From a019e7b4158a88f15b24e90e6d28cc615e558a9e Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Feb 2017 11:28:36 +0800 Subject: [PATCH 01/59] 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 Acked-by: Robin Gong [ Aisheng: split out imx-pcm.h changes ] Signed-off-by: Dong Aisheng --- .../bindings/sound/fsl,rpmsg-i2s.txt | 20 ++ sound/soc/fsl/Kconfig | 9 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/fsl_rpmsg_i2s.c | 251 ++++++++++++++ sound/soc/fsl/fsl_rpmsg_i2s.h | 306 ++++++++++++++++++ 5 files changed, 588 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt create mode 100644 sound/soc/fsl/fsl_rpmsg_i2s.c create mode 100644 sound/soc/fsl/fsl_rpmsg_i2s.h diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt new file mode 100644 index 000000000000..f38cf9d90500 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt @@ -0,0 +1,20 @@ +Freescale rpmsg i2s interface. + +The rpmsg i2s is based on RPMSG that used communicating with M4 core, +which provides a synchronous audio interface that supports fullduplex +serial interfaces with frame synchronization such as I2S. + +Required properties: + + - compatible : Compatible list, contains "fsl,imx7ulp-rpmsg-i2s". + + - fsl,audioindex : This is an index indicating the audio device index in + the M4 side. + +Example: +rpmsg_i2s: rpmsg-i2s { + compatible = "fsl,imx7ulp-rpmsg-i2s"; + /* the audio device index in m4 domain */ + fsl,audioindex = <0> ; + status = "okay"; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index aa99c008a925..824ec0cc50ff 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -74,6 +74,15 @@ config SND_SOC_FSL_MICFIL Say Y if you want to add Pulse Density Modulation microphone interface (MICFIL) support for NXP. +config SND_SOC_FSL_RPMSG_I2S + tristate "I2S base on the RPMSG support" + depends on RPMSG + help + Say Y if you want to add rpmsg i2s support for the Freescale CPUs. + which is depends on the rpmsg. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + config SND_SOC_FSL_UTILS tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index c0dd04422fe9..027ab6dfd4e1 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -25,6 +25,7 @@ snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o +snd-soc-fsl-rpmsg-i2s-objs := fsl_rpmsg_i2s.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o @@ -34,6 +35,7 @@ obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o +obj-$(CONFIG_SND_SOC_FSL_RPMSG_I2S) += snd-soc-fsl-rpmsg-i2s.o # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 "); +MODULE_ALIAS("platform:fsl-rpmsg_i2s"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h new file mode 100644 index 000000000000..7439f8a81923 --- /dev/null +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -0,0 +1,306 @@ +/* + * 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 version 2 as + * published by the Free Software Foundation. + * + ****************************************************************************** + * communication stack of audio with rpmsg + ****************************************************************************** + * Packet structure: + * A SRTM message consists of a 10 bytes header followed by 0~N bytes of data + * + * +---------------+-------------------------------+ + * | | Content | + * +---------------+-------------------------------+ + * | Byte Offset | 7 6 5 4 3 2 1 0 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 0 | Category | + * +---------------+---+---+---+---+---+---+---+---+ + * | 1 ~ 2 | Version | + * +---------------+---+---+---+---+---+---+---+---+ + * | 3 | Type | + * +---------------+---+---+---+---+---+---+---+---+ + * | 4 | Command | + * +---------------+---+---+---+---+---+---+---+---+ + * | 5 | Reserved0 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 6 | Reserved1 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 7 | Reserved2 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 8 | Reserved3 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 9 | Reserved4 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 10 | DATA 0 | + * +---------------+---+---+---+---+---+---+---+---+ + * : : : : : : : : : : : : : + * +---------------+---+---+---+---+---+---+---+---+ + * | N + 10 - 1 | DATA N-1 | + * +---------------+---+---+---+---+---+---+---+---+ + * + * +----------+------------+------------------------------------------------+ + * | Field | Byte | | + * +----------+------------+------------------------------------------------+ + * | Category | 0 | The destination category. | + * +----------+------------+------------------------------------------------+ + * | Version | 1 ~ 2 | The category version of the sender of the | + * | | | packet. | + * | | | The first byte represent the major version of | + * | | | the packet.The second byte represent the minor | + * | | | version of the packet. | + * +----------+------------+------------------------------------------------+ + * | Type | 3 | The message type of current message packet. | + * +----------+------------+------------------------------------------------+ + * | Command | 4 | The command byte sent to remote processor/SoC. | + * +----------+------------+------------------------------------------------+ + * | Reserved | 5 ~ 9 | Reserved field for future extension. | + * +----------+------------+------------------------------------------------+ + * | Data | N | The data payload of the message packet. | + * +----------+------------+------------------------------------------------+ + * + * Audio control: + * SRTM Audio Control Category Request Command Table: + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | Category | Version | Type | Command | Data | Function | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x00 | Data[0]: Audio Device Index | Open an Audio TX Instance. | + * | | | | | Data[1]: format | | + * | | | | | Data[2]: channels | | + * | | | | | Data[3-6]: samplerate | | + * | | | | | Data[7-10]: buffer_addr | | + * | | | | | Data[11-14]: buffer_size | | + * | | | | | Data[15-18]: period_size | | + * | | | | | Data[19-22]: buffer_tail | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x01 | Data[0]: Audio Device Index | Start an Audio TX Instance. | + * | | | | | Same as above command | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x02 | Data[0]: Audio Device Index | Pause an Audio TX Instance. | + * | | | | | Same as above command | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x03 | Data[0]: Audio Device Index | Resume an Audio TX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x04 | Data[0]: Audio Device Index | Terminate an Audio TX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x05 | Data[0]: Audio Device Index | Close an Audio TX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x06 | Data[0]: Audio Device Index | Set Parameters for an Audio TX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x07 | Data[0]: Audio Device Index | Set Audio TX Buffer. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x08 | Data[0]: Audio Device Index | Open an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x09 | Data[0]: Audio Device Index | Start an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x0A | Data[0]: Audio Device Index | Pause an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x0B | Data[0]: Audio Device Index | Resume an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x0C | Data[0]: Audio Device Index | Terminate an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x0D | Data[0]: Audio Device Index | Close an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x0E | Data[0]: Audio Device Index | Set Parameters for an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x0F | Data[0]: Audio Device Index | Set Audio RX Buffer. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * Note 1: See for available value of + * Sample Format; + * Note 2: See for available value of Channels; + * Note 3: Sample Rate of Set Parameters for an Audio TX Instance + * Command and Set Parameters for an Audio RX Instance Command is + * in little-endian format. + * + * SRTM Audio Control Category Response Command Table: + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | Category | Version | Type | Command | Data | Function | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x00 | Data[0]: Audio Device Index | Reply for Open an Audio TX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x01 | Data[0]: Audio Device Index | Reply for Start an Audio TX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x02 | Data[0]: Audio Device Index | Reply for Pause an Audio TX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x03 | Data[0]: Audio Device Index | Reply for Resume an Audio TX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x04 | Data[0]: Audio Device Index | Reply for Terminate an Audio TX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x05 | Data[0]: Audio Device Index | Reply for Close an Audio TX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x06 | Data[0]: Audio Device Index | Reply for Set Parameters for an Audio | + * | | | | | Data[1]: Return code | TX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x07 | Data[0]: Audio Device Index | Reply for Set Audio TX Buffer. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x08 | Data[0]: Audio Device Index | Reply for Open an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x09 | Data[0]: Audio Device Index | Reply for Start an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0A | Data[0]: Audio Device Index | Reply for Pause an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0B | Data[0]: Audio Device Index | Reply for Resume an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0C | Data[0]: Audio Device Index | Reply for Terminate an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0D | Data[0]: Audio Device Index | Reply for Close an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0E | Data[0]: Audio Device Index | Reply for Set Parameters for an Audio | + * | | | | | Data[1]: Return code | RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0F | Data[0]: Audio Device Index | Reply for Set Audio RX Buffer. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * + * SRTM Audio Control Category Notification Command Table: + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | Category | Version | Type | Command | Data | Function | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x02 | 0x00 | Data[0]: Audio Device Index | Notify one TX period is finished. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x02 | 0x01 | Data[0]: Audio Device Index | Notify one RX period is finished. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * + * List of Sample Format: + * +--------------------+----------------------------------------------+ + * | Sample Format | Description | + * +--------------------+----------------------------------------------+ + * | 0x0 | S16_LE | + * +--------------------+----------------------------------------------+ + * | 0x1 | S24_LE | + * +--------------------+----------------------------------------------+ + * + * List of Audio Channels + * +--------------------+----------------------------------------------+ + * | Audio Channel | Description | + * +--------------------+----------------------------------------------+ + * | 0x0 | Left Channel | + * +--------------------+----------------------------------------------+ + * | 0x1 | Right Channel | + * +--------------------+----------------------------------------------+ + * | 0x2 | Left & Right Channel | + * +--------------------+----------------------------------------------+ + * + */ + +#ifndef __FSL_RPMSG_I2S_H +#define __FSL_RPMSG_I2S_H + +#include +#include +#include +#include + +#define RPMSG_TIMEOUT 1000 + +#define I2S_TX_OPEN 0x0 +#define I2S_TX_START 0x1 +#define I2S_TX_PAUSE 0x2 +#define I2S_TX_RESTART 0x3 +#define I2S_TX_TERMINATE 0x4 +#define I2S_TX_CLOSE 0x5 +#define I2S_TX_HW_PARAM 0x6 +#define I2S_TX_BUFFER 0x7 +#define I2S_TX_SUSPEND 0x8 +#define I2S_TX_RESUME 0x9 + +#define I2S_RX_OPEN 0xA +#define I2S_RX_START 0xB +#define I2S_RX_PAUSE 0xC +#define I2S_RX_RESTART 0xD +#define I2S_RX_TERMINATE 0xE +#define I2S_RX_CLOSE 0xF +#define I2S_RX_HW_PARAM 0x10 +#define I2S_RX_BUFFER 0x11 +#define I2S_RX_SUSPEND 0x12 +#define I2S_RX_RESUME 0x13 +#define WORK_MAX_NUM 0x14 + +#define I2S_TX_PERIOD_DONE 0x0 +#define I2S_RX_PERIOD_DONE 0x1 + +#define I2S_TYPE_A 0x0 +#define I2S_TYPE_B 0x1 +#define I2S_TYPE_C 0x2 + +#define I2S_RESP_NONE 0x0 +#define I2S_RESP_NOT_ALLOWED 0x1 +#define I2S_RESP_SUCCESS 0x2 +#define I2S_RESP_FAILED 0x3 + +struct i2s_param_s { + unsigned char audioindex; + unsigned char format; + unsigned char channels; + unsigned int rate; + unsigned int buffer_addr; + unsigned int buffer_size; + unsigned int period_size; + unsigned int buffer_tail; +} __packed; + +struct i2s_param_r { + unsigned char audioindex; + unsigned char resp; +} __packed; + +/* struct of send message */ +struct i2s_rpmsg_s { + struct imx_rpmsg_head header; + struct i2s_param_s param; +}; + +/* struct of received message */ +struct i2s_rpmsg_r { + struct imx_rpmsg_head header; + struct i2s_param_r param; +}; + +struct work_of_rpmsg { + struct i2s_info *i2s_info; + /* sent msg for each work */ + struct i2s_rpmsg_s msg; + struct work_struct work; +}; + +typedef void (*dma_callback)(void *arg); +struct i2s_info { + struct rpmsg_device *rpdev; + struct device *dev; + struct completion cmd_complete; + /* received msg */ + struct i2s_rpmsg_r recv_msg; + /* backup sent msg */ + struct i2s_rpmsg_s send_msg[2]; + + struct workqueue_struct *rpmsg_wq; + struct work_of_rpmsg work_list[WORK_MAX_NUM]; + int num_period[2]; + void *callback_param[2]; + int (*send_message)(struct i2s_rpmsg_s *msg, struct i2s_info *info); + dma_callback callback[2]; + spinlock_t lock[2]; + struct mutex tx_lock; +}; + +struct fsl_rpmsg_i2s { + struct platform_device *pdev; + struct i2s_info i2s_info; +}; + +#endif /* __FSL_RPMSG_I2S_H */ From 118fc234d59e4defee4743293af0279e4c44f744 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Feb 2017 11:28:26 +0800 Subject: [PATCH 02/59] MLK-13904-2: ASoC: fsl: add audio platform driver base on rpmsg Add platform driver, each step like set hw param, trigger start trigger stop, and so on, will call the rpmsg api. Signed-off-by: Shengjiu Wang Acked-by: Robin Gong [Aisheng: clean for a new base ] Signed-off-by: Dong Aisheng --- sound/soc/fsl/Kconfig | 5 + sound/soc/fsl/Makefile | 1 + sound/soc/fsl/imx-pcm-rpmsg.c | 514 ++++++++++++++++++++++++++++++++++ 3 files changed, 520 insertions(+) create mode 100644 sound/soc/fsl/imx-pcm-rpmsg.c diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 824ec0cc50ff..320c6a442af8 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -90,6 +90,11 @@ config SND_SOC_IMX_PCM_DMA tristate select SND_SOC_GENERIC_DMAENGINE_PCM +config SND_SOC_IMX_PCM_RPMSG + tristate + depends on RPMSG + select SND_SOC_GENERIC_DMAENGINE_PCM + config SND_SOC_IMX_AUDMUX tristate "Digital Audio Mux module support" help diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 027ab6dfd4e1..70699370926d 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o +obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c new file mode 100644 index 000000000000..25369cc0fade --- /dev/null +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -0,0 +1,514 @@ +/* + * imx-rpmsg-platform.c -- ALSA Soc Audio Layer + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-pcm.h" +#include "fsl_rpmsg_i2s.h" + +struct i2s_info *i2s_info_g; + +static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .buffer_bytes_max = IMX_SAI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65535, /* Limited by SDMA engine */ + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + + rpmsg->param.rate = params_rate(params); + if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) + rpmsg->param.format = 0; + else + rpmsg->param.format = 1; + + if (params_channels(params) == 1) + rpmsg->param.channels = 0; + else + rpmsg->param.channels = 2; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->header.cmd = I2S_TX_HW_PARAM; + else + rpmsg->header.cmd = I2S_RX_HW_PARAM; + + i2s_info->send_message(rpmsg, i2s_info); + + return 0; +} + +static int imx_rpmsg_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +} + +static snd_pcm_uframes_t imx_rpmsg_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct dma_tx_state state; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + unsigned int buf_size; + unsigned int pos = 0; + unsigned long flags; + + spin_lock_irqsave(&i2s_info->lock[substream->stream], flags); + state.residue = (i2s_info->num_period[substream->stream] - + rpmsg->param.buffer_tail) + * rpmsg->param.period_size; + spin_unlock_irqrestore(&i2s_info->lock[substream->stream], flags); + + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + + return bytes_to_frames(substream->runtime, pos); +} + +static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct dmaengine_pcm_runtime_data *prtd; + + snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->header.cmd = I2S_TX_OPEN; + else + rpmsg->header.cmd = I2S_RX_OPEN; + i2s_info->send_message(rpmsg, i2s_info); + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + substream->runtime->private_data = prtd; + + return ret; +} + +static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct dmaengine_pcm_runtime_data *prtd = + substream->runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->header.cmd = I2S_TX_CLOSE; + else + rpmsg->header.cmd = I2S_RX_CLOSE; + i2s_info->send_message(rpmsg, i2s_info); + + kfree(prtd); + + return ret; +} + +static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static void imx_rpmsg_pcm_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct dmaengine_pcm_runtime_data *prtd + = substream->runtime->private_data; + + prtd->pos += snd_pcm_lib_period_bytes(substream); + if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) + prtd->pos = 0; + + snd_pcm_period_elapsed(substream); +} + +static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + u8 cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cmd = I2S_TX_BUFFER; + else + cmd = I2S_RX_BUFFER; + + rpmsg->header.cmd = cmd; + rpmsg->param.buffer_addr = substream->runtime->dma_addr; + rpmsg->param.buffer_size = snd_pcm_lib_buffer_bytes(substream); + rpmsg->param.period_size = snd_pcm_lib_period_bytes(substream); + rpmsg->param.buffer_tail = 0; + + i2s_info->num_period[substream->stream] = + rpmsg->param.buffer_size/rpmsg->param.period_size; + + memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + + i2s_info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete; + i2s_info->callback_param[substream->stream] = substream; + return 0; +} + +static void imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + u8 cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cmd = I2S_TX_START; + else + cmd = I2S_RX_START; + + rpmsg->header.cmd = cmd; + memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); +} + +static int imx_rpmsg_resume(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + u8 cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cmd = I2S_TX_RESTART; + else + cmd = I2S_RX_RESTART; + + rpmsg->header.cmd = cmd; + memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + + return 0; +} + +static int imx_rpmsg_pause(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + u8 cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cmd = I2S_TX_PAUSE; + else + cmd = I2S_RX_PAUSE; + + rpmsg->header.cmd = cmd; + memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + + return 0; +} + +static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + u8 cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cmd = I2S_TX_TERMINATE; + else + cmd = I2S_RX_TERMINATE; + + rpmsg->header.cmd = cmd; + memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + + return 0; +} + +int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = imx_rpmsg_pcm_prepare_and_submit(substream); + if (ret) + return ret; + imx_rpmsg_async_issue_pending(substream); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + imx_rpmsg_resume(substream); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (runtime->info & SNDRV_PCM_INFO_PAUSE) + imx_rpmsg_pause(substream); + else + imx_rpmsg_terminate_all(substream); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + imx_rpmsg_pause(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + imx_rpmsg_terminate_all(substream); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct snd_pcm_ops imx_rpmsg_pcm_ops = { + .open = imx_rpmsg_pcm_open, + .close = imx_rpmsg_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = imx_rpmsg_pcm_hw_params, + .hw_free = imx_rpmsg_pcm_hw_free, + .trigger = imx_rpmsg_pcm_trigger, + .pointer = imx_rpmsg_pcm_pointer, + .mmap = imx_rpmsg_pcm_mmap, +}; + +static int imx_rpmsg_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, + int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = imx_rpmsg_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static void imx_rpmsg_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = SNDRV_PCM_STREAM_PLAYBACK; + stream < SNDRV_PCM_STREAM_LAST; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static int imx_rpmsg_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + +out: + /* free preallocated buffers in case of error */ + if (ret) + imx_rpmsg_pcm_free_dma_buffers(pcm); + + return ret; +} + +static struct snd_soc_platform_driver imx_rpmsg_soc_platform = { + .ops = &imx_rpmsg_pcm_ops, + .pcm_new = imx_rpmsg_pcm_new, + .pcm_free = imx_rpmsg_pcm_free_dma_buffers, +}; + +int imx_rpmsg_platform_register(struct device *dev) +{ + + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + + i2s_info_g = &rpmsg_i2s->i2s_info; + + return devm_snd_soc_register_platform(dev, &imx_rpmsg_soc_platform); +} +EXPORT_SYMBOL_GPL(imx_rpmsg_platform_register); + +static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + struct i2s_rpmsg_r *msg = (struct i2s_rpmsg_r *)data; + struct i2s_rpmsg_s *rpmsg; + unsigned long flags; + + dev_dbg(&rpdev->dev, "get from%d: cmd:%d.\n", src, msg->header.cmd); + + memcpy(&i2s_info_g->recv_msg, msg, sizeof(struct i2s_rpmsg_r)); + + if (msg->header.type == I2S_TYPE_C) { + if (msg->header.cmd == I2S_TX_PERIOD_DONE) { + spin_lock_irqsave(&i2s_info_g->lock[0], flags); + rpmsg = &i2s_info_g->send_msg[0]; + rpmsg->param.buffer_tail++; + rpmsg->param.buffer_tail %= i2s_info_g->num_period[0]; + spin_unlock_irqrestore(&i2s_info_g->lock[0], flags); + i2s_info_g->callback[0](i2s_info_g->callback_param[0]); + } else if (msg->header.cmd == I2S_RX_PERIOD_DONE) { + spin_lock_irqsave(&i2s_info_g->lock[1], flags); + rpmsg = &i2s_info_g->send_msg[1]; + rpmsg->param.buffer_tail++; + rpmsg->param.buffer_tail %= i2s_info_g->num_period[1]; + spin_unlock_irqrestore(&i2s_info_g->lock[1], flags); + i2s_info_g->callback[1](i2s_info_g->callback_param[1]); + } + } + + if (msg->header.type == I2S_TYPE_B) + complete(&i2s_info_g->cmd_complete); + + return 0; +} + +static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) +{ + i2s_info_g->rpdev = rpdev; + + init_completion(&i2s_info_g->cmd_complete); + + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + return 0; +} + +static void i2s_rpmsg_remove(struct rpmsg_device *rpdev) +{ + dev_info(&rpdev->dev, "i2s rpmsg driver is removed\n"); +} + +static struct rpmsg_device_id i2s_rpmsg_id_table[] = { + { .name = "rpmsg-audio-channel" }, + { }, +}; + +static struct rpmsg_driver i2s_rpmsg_driver = { + .drv.name = "i2s_rpmsg", + .drv.owner = THIS_MODULE, + .id_table = i2s_rpmsg_id_table, + .probe = i2s_rpmsg_probe, + .callback = i2s_rpmsg_cb, + .remove = i2s_rpmsg_remove, +}; + +static int __init i2s_rpmsg_init(void) +{ + return register_rpmsg_driver(&i2s_rpmsg_driver); +} + +static void __exit i2s_rpmsg_exit(void) +{ + unregister_rpmsg_driver(&i2s_rpmsg_driver); +} +module_init(i2s_rpmsg_init); +module_exit(i2s_rpmsg_exit); + +MODULE_LICENSE("GPL"); From 6e155db640433a60c101d41ffb7a5a36780457cf Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Feb 2017 11:23:45 +0800 Subject: [PATCH 03/59] MLK-13904-3: ASoC: fsl: add audio machine driver base on rpmsg Add machine driver, which is using the dummy codec. Signed-off-by: Shengjiu Wang Acked-by: Robin Gong [ Aisheng: clean for a new base ] Signed-off-by: Dong Aisheng --- .../bindings/sound/imx-audio-rpmsg.txt | 13 ++ sound/soc/fsl/Kconfig | 11 ++ sound/soc/fsl/Makefile | 2 + sound/soc/fsl/imx-rpmsg.c | 113 ++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-rpmsg.txt create mode 100644 sound/soc/fsl/imx-rpmsg.c diff --git a/Documentation/devicetree/bindings/sound/imx-audio-rpmsg.txt b/Documentation/devicetree/bindings/sound/imx-audio-rpmsg.txt new file mode 100644 index 000000000000..3f015974ffeb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-rpmsg.txt @@ -0,0 +1,13 @@ +Freescale i.MX audio complex with rpmsg devices + +Required properties: +- compatible : "fsl,imx-audio-rpmsg" +- model : The user-visible name of this sound complex +- cpu-dai : The phandle of the i.MX rpmsg i2s device. + +Example: +sound-rpmsg { + compatible = "fsl,imx-audio-rpmsg"; + model = "rpmsg-audio"; + cpu-dai = <&rpmsg_i2s>; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 320c6a442af8..50713a6ddd11 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -263,6 +263,17 @@ config SND_SOC_EUKREA_TLV320 Enable I2S based access to the TLV320AIC23B codec attached to the SSI interface +config SND_SOC_IMX_RPMSG + tristate "SoC Audio support for i.MX boards with rpmsg" + depends on OF && I2C && INPUT + select SND_SOC_IMX_PCM_RPMSG + select SND_SOC_FSL_RPMSG_I2S + help + SoC Audio support for i.MX boards with rpmsg. + There should be rpmsg devices defined in other core + Say Y if you want to add support for SoC audio on an i.MX board with + a rpmsg devices. + config SND_SOC_IMX_ES8328 tristate "SoC Audio support for i.MX boards with the ES8328 codec" depends on OF && (I2C || SPI) diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 70699370926d..5d2a301c898c 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -66,6 +66,7 @@ snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-spdif-objs := imx-spdif.o snd-soc-imx-mc13783-objs := imx-mc13783.o snd-soc-imx-audmix-objs := imx-audmix.o +snd-soc-imx-rpmsg-objs := imx-rpmsg.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o @@ -73,6 +74,7 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o +obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c new file mode 100644 index 000000000000..72b2e01212d9 --- /dev/null +++ b/sound/soc/fsl/imx-rpmsg.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 NXP + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsl_rpmsg_i2s.h" + +struct imx_rpmsg_data { + struct snd_soc_dai_link dai[1]; + struct snd_soc_card card; +}; + +static int imx_rpmsg_probe(struct platform_device *pdev) +{ + struct device_node *cpu_np; + struct platform_device *cpu_pdev; + struct imx_rpmsg_data *data; + int ret; + + cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + cpu_pdev = of_find_device_by_node(cpu_np); + if (!cpu_pdev) { + dev_err(&pdev->dev, "failed to find rpmsg platform device\n"); + ret = -EINVAL; + goto fail; + } + + data->dai[0].name = "rpmsg hifi"; + data->dai[0].stream_name = "rpmsg hifi"; + data->dai[0].codec_dai_name = "snd-soc-dummy-dai"; + data->dai[0].codec_name = "snd-soc-dummy"; + data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); + data->dai[0].platform_of_node = cpu_np; + data->dai[0].playback_only = false; + data->dai[0].capture_only = false; + data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + data->card.num_links = 1; + data->card.dai_link = data->dai; + + data->card.dev = &pdev->dev; + data->card.owner = THIS_MODULE; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto fail; + + platform_set_drvdata(pdev, &data->card); + snd_soc_card_set_drvdata(&data->card, data); + ret = devm_snd_soc_register_card(&pdev->dev, &data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto fail; + } + +fail: + if (cpu_np) + of_node_put(cpu_np); + return ret; +} + +static const struct of_device_id imx_rpmsg_dt_ids[] = { + { .compatible = "fsl,imx-audio-rpmsg", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_rpmsg_dt_ids); + +static struct platform_driver imx_rpmsg_driver = { + .driver = { + .name = "imx-audio-rpmsg", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = imx_rpmsg_dt_ids, + }, + .probe = imx_rpmsg_probe, +}; +module_platform_driver(imx_rpmsg_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale i.MX rpmsg audio ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-rpmsg"); From 92dd31be5bc5b9e43f3cead5a769ff4035217106 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 16 Feb 2017 11:51:06 +0800 Subject: [PATCH 04/59] MLK-13980: ASoC: fsl_rpmsg_i2s: fix wrong cmd sent to M4 in suspend This typo issue will cause that wrong cmd send to M4 side. Fixes: 3e13a631aee0 ("MLK-13904-1: ASoC: fsl: add audio cpu dai driver base on rpmsg") Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 2768c65155db..c53807f88586 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -200,7 +200,7 @@ static int fsl_rpmsg_i2s_suspend(struct device *dev) rpmsg_tx->header.cmd = I2S_TX_SUSPEND; i2s_send_message(rpmsg_tx, i2s_info); - rpmsg_tx->header.cmd = I2S_RX_SUSPEND; + rpmsg_rx->header.cmd = I2S_RX_SUSPEND; i2s_send_message(rpmsg_rx, i2s_info); return 0; @@ -219,7 +219,7 @@ static int fsl_rpmsg_i2s_resume(struct device *dev) rpmsg_tx->header.cmd = I2S_TX_RESUME; i2s_send_message(rpmsg_tx, i2s_info); - rpmsg_tx->header.cmd = I2S_RX_RESUME; + rpmsg_rx->header.cmd = I2S_RX_RESUME; i2s_send_message(rpmsg_rx, i2s_info); return 0; From aa0cc6695a1be86576ed678e577e13f03a60d269 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 17 Feb 2017 10:11:35 +0800 Subject: [PATCH 05/59] MLK-13991: ASoC: fsl_rpmsg_i2s: remove unsupported rate The M4 audio driver only support 8k/16k/32k/44k/48kHz sample rate. so remove other rate in supported list. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index c53807f88586..172c279c7f94 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -26,7 +26,9 @@ #include "fsl_rpmsg_i2s.h" #include "imx-pcm.h" -#define FSL_RPMSG_I2S_RATES SNDRV_PCM_RATE_8000_96000 +#define FSL_RPMSG_I2S_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\ + SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\ + SNDRV_PCM_RATE_48000) #define FSL_RPMSG_I2S_FORMATS SNDRV_PCM_FMTBIT_S16_LE static int i2s_send_message(struct i2s_rpmsg_s *msg, From c5ffd857b55b9ae46ca0cbd7e0dbd85a3a2733b4 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 17 Feb 2017 10:09:31 +0800 Subject: [PATCH 06/59] MLK-13992: ASoC: fsl_rpmsg_i2s: add flush workqueue some cmd is sent by workqueue, others are sent by call send message function directly, for workqueue may have delay, so there is occasion that cmd is not sent in order. Add flush_workqueue before the CLOSE and SUSPEND to make sure previous cmd is finished in that time. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 1 + sound/soc/fsl/imx-pcm-rpmsg.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 172c279c7f94..d8f8da8dbf23 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -196,6 +196,7 @@ static int fsl_rpmsg_i2s_suspend(struct device *dev) struct i2s_rpmsg_s *rpmsg_tx; struct i2s_rpmsg_s *rpmsg_rx; + flush_workqueue(i2s_info->rpmsg_wq); rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 25369cc0fade..ace3bd77c8c2 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -149,6 +149,8 @@ static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) rpmsg->header.cmd = I2S_TX_CLOSE; else rpmsg->header.cmd = I2S_RX_CLOSE; + + flush_workqueue(i2s_info->rpmsg_wq); i2s_info->send_message(rpmsg, i2s_info); kfree(prtd); From 45aa9064e240a0174754aed88a0f9c6dd83fe1a4 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 20 Feb 2017 15:44:40 +0800 Subject: [PATCH 07/59] MLK-14007: ASoC: fsl_rpmsg_i2s: remove mono for M4 don't support it Currently the M4 audio driver don't support mono channel, so remove it. After mono channel is supported in M4 os, this commit should be reverted. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index d8f8da8dbf23..10c917ef66fd 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -71,14 +71,14 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, static struct snd_soc_dai_driver fsl_rpmsg_i2s_dai = { .playback = { .stream_name = "CPU-Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = FSL_RPMSG_I2S_RATES, .formats = FSL_RPMSG_I2S_FORMATS, }, .capture = { .stream_name = "CPU-Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = FSL_RPMSG_I2S_RATES, .formats = FSL_RPMSG_I2S_FORMATS, From c040763b306ba030c4213e9a927038e9e217b885 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 23 Feb 2017 15:54:20 +0800 Subject: [PATCH 08/59] MLK-14254: ASoC: imx_pcm_rpmsg: fix cmd dropped by work queue The test case is to playback a bitstream, then repeat ctrl+z and fg, several times later, the playback is failed to continue. The reason is if the work is pending in work queue, send second time of this work, the second work is dropped by work queue. so use one work for one cmd is not fit for audio case. use a work loop for audio cmd to fix this issue. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.h | 1 + sound/soc/fsl/imx-pcm-rpmsg.c | 65 +++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index 7439f8a81923..0fcc80de953f 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -290,6 +290,7 @@ struct i2s_info { struct workqueue_struct *rpmsg_wq; struct work_of_rpmsg work_list[WORK_MAX_NUM]; + int work_index; int num_period[2]; void *callback_param[2]; int (*send_message)(struct i2s_rpmsg_s *msg, struct i2s_info *info); diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index ace3bd77c8c2..2ece2cfaa728 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -189,14 +189,13 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - u8 cmd; + u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cmd = I2S_TX_BUFFER; + rpmsg->header.cmd = I2S_TX_BUFFER; else - cmd = I2S_RX_BUFFER; + rpmsg->header.cmd = I2S_RX_BUFFER; - rpmsg->header.cmd = cmd; rpmsg->param.buffer_addr = substream->runtime->dma_addr; rpmsg->param.buffer_size = snd_pcm_lib_buffer_bytes(substream); rpmsg->param.period_size = snd_pcm_lib_period_bytes(substream); @@ -205,9 +204,11 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) i2s_info->num_period[substream->stream] = rpmsg->param.buffer_size/rpmsg->param.period_size; - memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; i2s_info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete; i2s_info->callback_param[substream->stream] = substream; @@ -221,17 +222,18 @@ static void imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - u8 cmd; + u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cmd = I2S_TX_START; + rpmsg->header.cmd = I2S_TX_START; else - cmd = I2S_RX_START; + rpmsg->header.cmd = I2S_RX_START; - rpmsg->header.cmd = cmd; - memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; } static int imx_rpmsg_resume(struct snd_pcm_substream *substream) @@ -241,17 +243,18 @@ static int imx_rpmsg_resume(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - u8 cmd; + u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cmd = I2S_TX_RESTART; + rpmsg->header.cmd = I2S_TX_RESTART; else - cmd = I2S_RX_RESTART; + rpmsg->header.cmd = I2S_RX_RESTART; - rpmsg->header.cmd = cmd; - memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; return 0; } @@ -263,17 +266,18 @@ static int imx_rpmsg_pause(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - u8 cmd; + u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cmd = I2S_TX_PAUSE; + rpmsg->header.cmd = I2S_TX_PAUSE; else - cmd = I2S_RX_PAUSE; + rpmsg->header.cmd = I2S_RX_PAUSE; - rpmsg->header.cmd = cmd; - memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; return 0; } @@ -285,17 +289,18 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - u8 cmd; + u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cmd = I2S_TX_TERMINATE; + rpmsg->header.cmd = I2S_TX_TERMINATE; else - cmd = I2S_RX_TERMINATE; + rpmsg->header.cmd = I2S_RX_TERMINATE; - rpmsg->header.cmd = cmd; - memcpy(&i2s_info->work_list[cmd].msg, rpmsg, + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[cmd].work); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; return 0; } From 5d8c7ea7ce3e6f36d26e5125f5e8e13948c2fb83 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 24 Feb 2017 11:53:46 +0800 Subject: [PATCH 09/59] MLK-14258: ASoC: imx-pcm-rpmsg: sync sample rate with tx and rx In suspend and resume, the M4 side will reset hw parameter for tx and rx, when only tx is working, the parameter of rx is a old value, which will cause the parameter is not sync with tx and rx. currently the M4 audio can only work in sync mode, so set both parameter in same time. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.h | 7 ++++++ sound/soc/fsl/imx-pcm-rpmsg.c | 43 +++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index 0fcc80de953f..259cb632630f 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -243,6 +243,13 @@ #define I2S_RESP_SUCCESS 0x2 #define I2S_RESP_FAILED 0x3 +#define RPMSG_S16_LE 0x0 +#define RPMSG_S24_LE 0x1 + +#define RPMSG_CH_LEFT 0x0 +#define RPMSG_CH_RIGHT 0x1 +#define RPMSG_CH_STEREO 0x2 + struct i2s_param_s { unsigned char audioindex; unsigned char format; diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 2ece2cfaa728..71ef7692c290 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -33,7 +33,7 @@ static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = IMX_SAI_DMABUF_SIZE, - .period_bytes_min = 128, + .period_bytes_min = 512, .period_bytes_max = 65535, /* Limited by SDMA engine */ .periods_min = 2, .periods_max = 255, @@ -49,28 +49,37 @@ static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg_s *rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; + struct i2s_rpmsg_s *rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; - rpmsg->param.rate = params_rate(params); - if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) - rpmsg->param.format = 0; - else - rpmsg->param.format = 1; + rpmsg_tx->param.rate = params_rate(params); + rpmsg_rx->param.rate = params_rate(params); + if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) { + rpmsg_tx->param.format = RPMSG_S16_LE; + rpmsg_rx->param.format = RPMSG_S16_LE; + } else { + rpmsg_tx->param.format = RPMSG_S24_LE; + rpmsg_rx->param.format = RPMSG_S24_LE; + } - if (params_channels(params) == 1) - rpmsg->param.channels = 0; - else - rpmsg->param.channels = 2; + if (params_channels(params) == 1) { + rpmsg_tx->param.channels = RPMSG_CH_LEFT; + rpmsg_rx->param.channels = RPMSG_CH_LEFT; + } else { + rpmsg_tx->param.channels = RPMSG_CH_STEREO; + rpmsg_rx->param.channels = RPMSG_CH_STEREO; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_HW_PARAM; - else - rpmsg->header.cmd = I2S_RX_HW_PARAM; - - i2s_info->send_message(rpmsg, i2s_info); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rpmsg_tx->header.cmd = I2S_TX_HW_PARAM; + i2s_info->send_message(rpmsg_tx, i2s_info); + } else { + rpmsg_rx->header.cmd = I2S_RX_HW_PARAM; + i2s_info->send_message(rpmsg_rx, i2s_info); + } return 0; } From ce2f05d271f4cbaf77fed45dac0069b44dc58d96 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 6 Mar 2017 17:32:29 +0800 Subject: [PATCH 10/59] MLK-14372: ASoC: fsl_rpmsg_i2s: remove unsupported sample rate RTOS for M4 core in imx7ulp don't support 32kHz and 44kHz Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 10c917ef66fd..5a66e9097e43 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -27,7 +27,6 @@ #include "imx-pcm.h" #define FSL_RPMSG_I2S_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\ - SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\ SNDRV_PCM_RATE_48000) #define FSL_RPMSG_I2S_FORMATS SNDRV_PCM_FMTBIT_S16_LE From 14e0dcede5381bcde8308f9b28cf0896107b3c6f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 30 Mar 2017 20:14:18 +0800 Subject: [PATCH 11/59] MLK-14582: ASoC: imx-pcm-rpmsg: fix audio noise issue with pulseaudio The pulse audio will set a wrong buffer size which is not the integral multiple of period size, it will cause the DMA can't work correctly in M4 side. The reason is that we always need to add a constraint for SNDRV_PCM_HW_PARAM_PERIODS, which make it integer. Signed-off-by: Shengjiu Wang (cherry picked from commit 54a10a6c2130a69aca4c1923dac3a15137911146) --- sound/soc/fsl/imx-pcm-rpmsg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 71ef7692c290..e7d7244d7405 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -140,6 +140,11 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) substream->runtime->private_data = prtd; + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + return ret; } From 7fadff23ce8b7a88af8cc326bc7a2c91bef69788 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 May 2017 10:09:55 +0300 Subject: [PATCH 12/59] MLK-14528: ASoC: sdma: Update period/segment max bytes (part 2) Commit 665ced16cf044 ("MLK-10050 dma: imx-sdma: add support for sdma memory copy") enforces maximum SDMA buffer descriptor length at 65532, but doesn't update period_bytes_max or max_segment size in DMA drivers. Thus, resulting in the following bug: $ arecord -Dhw:0,0 -r 192000 -f S20_3LE -c 1 -d 10 audio192k20b1c.wav imx-sdma: SDMA channel 5: maximum period size exceeded: 65534 > 65532 Signed-off-by: Daniel Baluta [ Aisheng: split asrc change ] Signed-off-by: Dong Aisheng --- sound/soc/fsl/imx-pcm-rpmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index e7d7244d7405..45b71fdbb38d 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -34,7 +34,7 @@ static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = IMX_SAI_DMABUF_SIZE, .period_bytes_min = 512, - .period_bytes_max = 65535, /* Limited by SDMA engine */ + .period_bytes_max = 65532, /* Limited by SDMA engine */ .periods_min = 2, .periods_max = 255, .fifo_size = 0, From 214ddc0e96c7629e041a9cb10a111996a193761e Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 31 May 2017 11:04:52 +0800 Subject: [PATCH 13/59] MLK-14989: ASoC: fsl_rpmsg_i2s: enable pm_qos for audio with "echo 1 > /sys/class/graphics/fb0/blank", and there is no usb connected on board, the system may enter low power mode, then audio playback will be failed. use pm_qos to prevent A7 core enter low power mode during audio playback and recording. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 6 ++++++ sound/soc/fsl/fsl_rpmsg_i2s.h | 1 + 2 files changed, 7 insertions(+) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 5a66e9097e43..3cfa3e9e50e2 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -178,11 +178,17 @@ static int fsl_rpmsg_i2s_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int fsl_rpmsg_i2s_runtime_resume(struct device *dev) { + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + + pm_qos_add_request(&rpmsg_i2s->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0); return 0; } static int fsl_rpmsg_i2s_runtime_suspend(struct device *dev) { + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + + pm_qos_remove_request(&rpmsg_i2s->pm_qos_req); return 0; } #endif diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index 259cb632630f..be951e91df29 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -309,6 +309,7 @@ struct i2s_info { struct fsl_rpmsg_i2s { struct platform_device *pdev; struct i2s_info i2s_info; + struct pm_qos_request pm_qos_req; }; #endif /* __FSL_RPMSG_I2S_H */ From ccb2d1a9942c5317fe0bf969d3aa0621af22fc7d Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Tue, 12 Dec 2017 14:07:34 +0200 Subject: [PATCH 14/59] MLK-17220: ASoC: fsl_rpmsg_i2s: unlock mutex on error Mutex must be unlocked when i2s_send_message method fails. Signed-off-by: Viorel Suman --- sound/soc/fsl/fsl_rpmsg_i2s.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 3cfa3e9e50e2..95cbe455392b 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -35,7 +35,6 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, { int err; - mutex_lock(&info->tx_lock); if (!info->rpdev) { dev_dbg(info->dev, "rpmsg channel not ready, m4 image ready?\n"); return -EINVAL; @@ -43,11 +42,14 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, dev_dbg(&info->rpdev->dev, "send cmd %d\n", msg->header.cmd); + mutex_lock(&info->tx_lock); + 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); + mutex_unlock(&info->tx_lock); return err; } @@ -57,6 +59,7 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, if (!err) { dev_err(&info->rpdev->dev, "rpmsg_send cmd %d timeout!\n", msg->header.cmd); + mutex_unlock(&info->tx_lock); return -ETIMEDOUT; } From 53039092deebd9a22686c1ae5a02e0290652a677 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Thu, 14 Dec 2017 14:03:51 +0200 Subject: [PATCH 15/59] MLK-17220: ASoC: fsl_rpmsg_i2s: restore original lock context Restore original lock context and unlock the mutex in case if info->rpdev is uninitialized. Signed-off-by: Viorel Suman --- sound/soc/fsl/fsl_rpmsg_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 95cbe455392b..60b872f091a1 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -35,15 +35,15 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, { int err; + mutex_lock(&info->tx_lock); if (!info->rpdev) { dev_dbg(info->dev, "rpmsg channel not ready, m4 image ready?\n"); + mutex_unlock(&info->tx_lock); return -EINVAL; } dev_dbg(&info->rpdev->dev, "send cmd %d\n", msg->header.cmd); - mutex_lock(&info->tx_lock); - reinit_completion(&info->cmd_complete); err = rpmsg_send(info->rpdev->ept, (void *)msg, sizeof(struct i2s_rpmsg_s)); From c98b293c40824f4de4430ed40ee42ceb79e11f4d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 6 Mar 2018 12:45:22 +0800 Subject: [PATCH 16/59] MLK-17156-3: ASoC: fs_rpmsg_i2s: update the protocol for i2c message rpmsg provide command for A7 side to set the codec value and get codec value by i2c. In this case, the A7 can control the codec. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta --- sound/soc/fsl/fsl_rpmsg_i2s.c | 8 ++- sound/soc/fsl/fsl_rpmsg_i2s.h | 129 ++++++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 22 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 60b872f091a1..ec3560d3c60c 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -37,7 +37,7 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, mutex_lock(&info->tx_lock); if (!info->rpdev) { - dev_dbg(info->dev, "rpmsg channel not ready, m4 image ready?\n"); + dev_err(info->dev, "rpmsg channel not ready, m4 image ready?\n"); mutex_unlock(&info->tx_lock); return -EINVAL; } @@ -63,6 +63,9 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, return -ETIMEDOUT; } + if (msg->header.cmd == GET_CODEC_VALUE) + msg->param.buffer_size = info->recv_msg.param.reg_data; + dev_dbg(&info->rpdev->dev, "cmd:%d, resp %d\n", msg->header.cmd, info->recv_msg.param.resp); mutex_unlock(&info->tx_lock); @@ -146,7 +149,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) i2s_info->work_list[i].i2s_info = i2s_info; } - for (i = 0; i < 2; i++) { + for (i = 0; i < RPMSG_AUDIO_NUM; 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; @@ -155,6 +158,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) } mutex_init(&i2s_info->tx_lock); + mutex_init(&i2s_info->i2c_lock); platform_set_drvdata(pdev, rpmsg_i2s); pm_runtime_enable(&pdev->dev); diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index be951e91df29..cbb984e07999 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -88,24 +88,75 @@ * | 0x03 | 0x0100 | 0x00 | 0x05 | Data[0]: Audio Device Index | Close an Audio TX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ * | 0x03 | 0x0100 | 0x00 | 0x06 | Data[0]: Audio Device Index | Set Parameters for an Audio TX Instance. | + * | | | | | Data[1]: format | | + * | | | | | Data[2]: channels | | + * | | | | | Data[3-6]: samplerate | | + * | | | | | Data[7-22]: reserved | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ * | 0x03 | 0x0100 | 0x00 | 0x07 | Data[0]: Audio Device Index | Set Audio TX Buffer. | + * | | | | | Data[1-6]: reserved | | + * | | | | | Data[7-10]: buffer_addr | | + * | | | | | Data[11-14]: buffer_size | | + * | | | | | Data[15-18]: period_size | | + * | | | | | Data[19-22]: buffer_tail | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x08 | Data[0]: Audio Device Index | Open an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x08 | Data[0]: Audio Device Index | Suspend an Audio TX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x09 | Data[0]: Audio Device Index | Start an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x09 | Data[0]: Audio Device Index | Resume an Audio TX Instance. | + * | | | | | Data[1]: format | | + * | | | | | Data[2]: channels | | + * | | | | | Data[3-6]: samplerate | | + * | | | | | Data[7-10]: buffer_addr | | + * | | | | | Data[11-14]: buffer_size | | + * | | | | | Data[15-18]: period_size | | + * | | | | | Data[19-22]: buffer_tail | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x0A | Data[0]: Audio Device Index | Pause an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x0A | Data[0]: Audio Device Index | Open an Audio RX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x0B | Data[0]: Audio Device Index | Resume an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x0B | Data[0]: Audio Device Index | Start an Audio RX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x0C | Data[0]: Audio Device Index | Terminate an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x0C | Data[0]: Audio Device Index | Pause an Audio RX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x0D | Data[0]: Audio Device Index | Close an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x0D | Data[0]: Audio Device Index | Resume an Audio RX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x0E | Data[0]: Audio Device Index | Set Parameters for an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x00 | 0x0E | Data[0]: Audio Device Index | Terminate an Audio RX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x00 | 0x0F | Data[0]: Audio Device Index | Set Audio RX Buffer. | + * | 0x03 | 0x0100 | 0x00 | 0x0F | Data[0]: Audio Device Index | Close an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x10 | Data[0]: Audio Device Index | Set Parameters for an Audio RX Instance. | + * | | | | | Data[1]: format | | + * | | | | | Data[2]: channels | | + * | | | | | Data[3-6]: samplerate | | + * | | | | | Data[7-22]: reserved | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x11 | Data[0]: Audio Device Index | Set Audio RX Buffer. | + * | | | | | Data[1-6]: reserved | | + * | | | | | Data[7-10]: buffer_addr | | + * | | | | | Data[11-14]: buffer_size | | + * | | | | | Data[15-18]: period_size | | + * | | | | | Data[19-22]: buffer_tail | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x12 | Data[0]: Audio Device Index | Suspend an Audio RX Instance. | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x13 | Data[0]: Audio Device Index | Resume an Audio RX Instance. | + * | | | | | Data[1]: format | | + * | | | | | Data[2]: channels | | + * | | | | | Data[3-6]: samplerate | | + * | | | | | Data[7-10]: buffer_addr | | + * | | | | | Data[11-14]: buffer_size | | + * | | | | | Data[15-18]: period_size | | + * | | | | | Data[19-22]: buffer_tail | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x14 | Data[0]: Audio Device Index | Set register value to codec. | + * | | | | | Data[1-6]: reserved | | + * | | | | | Data[7-10]: register | | + * | | | | | Data[11-14]: value | | + * | | | | | Data[15-22]: reserved | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x00 | 0x15 | Data[0]: Audio Device Index | Get register value from codec. | + * | | | | | Data[1-6]: reserved | | + * | | | | | Data[7-10]: register | | + * | | | | | Data[11-22]: reserved | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ * Note 1: See for available value of * Sample Format; @@ -142,30 +193,52 @@ * | 0x03 | 0x0100 | 0x01 | 0x07 | Data[0]: Audio Device Index | Reply for Set Audio TX Buffer. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x08 | Data[0]: Audio Device Index | Reply for Open an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x01 | 0x08 | Data[0]: Audio Device Index | Reply for Suspend an Audio TX Instance. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x09 | Data[0]: Audio Device Index | Reply for Start an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x01 | 0x09 | Data[0]: Audio Device Index | Reply for Resume an Audio TX Instance. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x0A | Data[0]: Audio Device Index | Reply for Pause an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x01 | 0x0A | Data[0]: Audio Device Index | Reply for Open an Audio RX Instance. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x0B | Data[0]: Audio Device Index | Reply for Resume an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x01 | 0x0B | Data[0]: Audio Device Index | Reply for Start an Audio RX Instance. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x0C | Data[0]: Audio Device Index | Reply for Terminate an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x01 | 0x0C | Data[0]: Audio Device Index | Reply for Pause an Audio RX Instance. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x0D | Data[0]: Audio Device Index | Reply for Close an Audio RX Instance. | + * | 0x03 | 0x0100 | 0x01 | 0x0D | Data[0]: Audio Device Index | Reply for Resume an Audio RX Instance. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x0E | Data[0]: Audio Device Index | Reply for Set Parameters for an Audio | + * | 0x03 | 0x0100 | 0x01 | 0x0E | Data[0]: Audio Device Index | Reply for Terminate an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x0F | Data[0]: Audio Device Index | Reply for Close an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x10 | Data[0]: Audio Device Index | Reply for Set Parameters for an Audio | * | | | | | Data[1]: Return code | RX Instance. | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ - * | 0x03 | 0x0100 | 0x01 | 0x0F | Data[0]: Audio Device Index | Reply for Set Audio RX Buffer. | + * | 0x03 | 0x0100 | 0x01 | 0x11 | Data[0]: Audio Device Index | Reply for Set Audio RX Buffer. | * | | | | | Data[1]: Return code | | * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x12 | Data[0]: Audio Device Index | Reply for Supend an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x13 | Data[0]: Audio Device Index | Reply for Resume an Audio RX Instance. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x14 | Data[0]: Audio Device Index | Reply for Set codec register value. | + * | | | | | Data[1]: Return code | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ + * | 0x03 | 0x0100 | 0x01 | 0x15 | Data[0]: Audio Device Index | Reply for Get codec register value. | + * | | | | | Data[1]: Return code | | + * | | | | | Data[2-6]: reserved | | + * | | | | | Data[7-10]: register | | + * | | | | | Data[11-14]: value | | + * | | | | | Data[15-22]: reserved | | + * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ * * SRTM Audio Control Category Notification Command Table: * +------------+---------+------+---------+-------------------------------+------------------------------------------------+ @@ -229,7 +302,9 @@ #define I2S_RX_BUFFER 0x11 #define I2S_RX_SUSPEND 0x12 #define I2S_RX_RESUME 0x13 -#define WORK_MAX_NUM 0x14 +#define SET_CODEC_VALUE 0x14 +#define GET_CODEC_VALUE 0x15 +#define WORK_MAX_NUM 0x16 #define I2S_TX_PERIOD_DONE 0x0 #define I2S_RX_PERIOD_DONE 0x1 @@ -255,8 +330,8 @@ struct i2s_param_s { unsigned char format; unsigned char channels; unsigned int rate; - unsigned int buffer_addr; - unsigned int buffer_size; + unsigned int buffer_addr; /* Register for SET_CODEC_VALUE*/ + unsigned int buffer_size; /* register value for SET_CODEC_VALUE*/ unsigned int period_size; unsigned int buffer_tail; } __packed; @@ -264,6 +339,10 @@ struct i2s_param_s { struct i2s_param_r { unsigned char audioindex; unsigned char resp; + unsigned char reserved1[5]; + unsigned int reg_addr; + unsigned int reg_data; + unsigned char reserved2[8]; } __packed; /* struct of send message */ @@ -285,6 +364,13 @@ struct work_of_rpmsg { struct work_struct work; }; +enum { + RPMSG_AUDIO_TX = 0, + RPMSG_AUDIO_RX = 1, + RPMSG_AUDIO_I2C = 2, + RPMSG_AUDIO_NUM = 3, +}; + typedef void (*dma_callback)(void *arg); struct i2s_info { struct rpmsg_device *rpdev; @@ -293,7 +379,7 @@ struct i2s_info { /* received msg */ struct i2s_rpmsg_r recv_msg; /* backup sent msg */ - struct i2s_rpmsg_s send_msg[2]; + struct i2s_rpmsg_s send_msg[RPMSG_AUDIO_NUM]; struct workqueue_struct *rpmsg_wq; struct work_of_rpmsg work_list[WORK_MAX_NUM]; @@ -304,6 +390,7 @@ struct i2s_info { dma_callback callback[2]; spinlock_t lock[2]; struct mutex tx_lock; + struct mutex i2c_lock; }; struct fsl_rpmsg_i2s { @@ -312,4 +399,6 @@ struct fsl_rpmsg_i2s { struct pm_qos_request pm_qos_req; }; +#define RPMSG_CODEC_DRV_NAME "rpmsg-audio-codec" + #endif /* __FSL_RPMSG_I2S_H */ From d41276f382dc93f53206705bcf65cbd180cfba13 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 7 Mar 2018 11:14:29 +0800 Subject: [PATCH 17/59] MLK-17156-4: ASoC: rpmsg_wm8960: add rpmsg_wm8960 codec This codec is accessed by rpmsg. As the wm8960 is controlled mainly by M4, so we only add volume in this rpmsg_wm8960 codec. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rpmsg_wm8960.c | 178 ++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 sound/soc/codecs/rpmsg_wm8960.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 229cc89f8c5a..9854cfd50d63 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -257,6 +257,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) + select SND_SOC_RPMSG_WM8960 help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -1436,6 +1437,9 @@ config SND_SOC_ZX_AUD96P22 depends on I2C select REGMAP_I2C +config SND_SOC_RPMSG_WM8960 + tristate + # Amp config SND_SOC_LM4857 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c498373dcc5f..72594796dedf 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -274,6 +274,7 @@ snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o +snd-soc-rpmsg-wm8960-objs := rpmsg_wm8960.o # Amp snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o @@ -557,6 +558,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o +obj-$(CONFIG_SND_SOC_RPMSG_WM8960) += snd-soc-rpmsg-wm8960.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rpmsg_wm8960.c b/sound/soc/codecs/rpmsg_wm8960.c new file mode 100644 index 000000000000..37a716d10a2b --- /dev/null +++ b/sound/soc/codecs/rpmsg_wm8960.c @@ -0,0 +1,178 @@ +/* + * + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include "../fsl/fsl_rpmsg_i2s.h" + +#define WM8960_LINVOL 0x0 +#define WM8960_RINVOL 0x1 +#define WM8960_LOUT1 0x2 +#define WM8960_ROUT1 0x3 +#define WM8960_LDAC 0xa +#define WM8960_RDAC 0xb +#define WM8960_LADC 0x15 +#define WM8960_RADC 0x16 +#define WM8960_LOUT2 0x28 +#define WM8960_ROUT2 0x29 + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); + +static struct snd_kcontrol_new rpmsg_wm8960_ctrls[] = { +SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, + 0, 63, 0, inpga_tlv), +SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, + 0, 255, 0, dac_tlv), +SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, + 0, 255, 0, adc_tlv), +}; + +#define RPMSG_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define RPMSG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver rpmsg_wm8960_codec_dai = { + .name = "rpmsg-wm8960-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = RPMSG_RATES, + .formats = RPMSG_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = RPMSG_RATES, + .formats = RPMSG_FORMATS, + }, +}; + +static unsigned int rpmsg_wm8960_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_codec_get_drvdata(codec); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[RPMSG_AUDIO_I2C]; + int err, reg_val; + + mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.buffer_addr = reg; + rpmsg->header.cmd = GET_CODEC_VALUE; + err = i2s_info->send_message(rpmsg, i2s_info); + reg_val = rpmsg->param.buffer_size; + mutex_unlock(&i2s_info->i2c_lock); + if (err) + return 0; + + return reg_val; +} + +static int rpmsg_wm8960_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_codec_get_drvdata(codec); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[RPMSG_AUDIO_I2C]; + int err; + + mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.buffer_addr = reg; + rpmsg->param.buffer_size = val; + rpmsg->header.cmd = SET_CODEC_VALUE; + err = i2s_info->send_message(rpmsg, i2s_info); + mutex_unlock(&i2s_info->i2c_lock); + if (err) + return err; + + return 0; +} + +static int rpmsg_wm8960_probe(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); + + return 0; +} + +static struct snd_soc_codec_driver rpmsg_wm8960_codec = { + .probe = rpmsg_wm8960_probe, + .read = rpmsg_wm8960_read, + .write = rpmsg_wm8960_write, + .component_driver = { + .controls = rpmsg_wm8960_ctrls, + .num_controls = ARRAY_SIZE(rpmsg_wm8960_ctrls), + }, +}; + +static int rpmsg_wm8960_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(pdev->dev.parent); + int ret; + + ret = snd_soc_register_codec(dev, + &rpmsg_wm8960_codec, + &rpmsg_wm8960_codec_dai, + 1); + if (ret) { + dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n", + __func__, ret); + return ret; + } + + dev_set_drvdata(dev, rpmsg_i2s); + + return 0; +} + +static int rpmsg_wm8960_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver rpmsg_wm8960_codec_driver = { + .driver = { + .name = RPMSG_CODEC_DRV_NAME, + }, + .probe = rpmsg_wm8960_codec_probe, + .remove = rpmsg_wm8960_codec_remove, +}; + +module_platform_driver(rpmsg_wm8960_codec_driver); + +MODULE_DESCRIPTION("rpmsg wm8960 Codec Driver"); +MODULE_LICENSE("GPL"); From f823c83ef989e3c470115ff8a6c92df9be2a4d4f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 7 Mar 2018 11:15:23 +0800 Subject: [PATCH 18/59] MLK-17156-5: ASoC: imx-pcm-rpmsg: register rpmsg codec register rpmsg codec after the rpmsg-audio-channel is ready. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta --- sound/soc/fsl/imx-pcm-rpmsg.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 45b71fdbb38d..67b44a60cbfe 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -446,7 +446,6 @@ static struct snd_soc_platform_driver imx_rpmsg_soc_platform = { int imx_rpmsg_platform_register(struct device *dev) { - struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); i2s_info_g = &rpmsg_i2s->i2s_info; @@ -469,14 +468,14 @@ static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, if (msg->header.type == I2S_TYPE_C) { if (msg->header.cmd == I2S_TX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[0], flags); - rpmsg = &i2s_info_g->send_msg[0]; + rpmsg = &i2s_info_g->send_msg[RPMSG_AUDIO_TX]; rpmsg->param.buffer_tail++; rpmsg->param.buffer_tail %= i2s_info_g->num_period[0]; spin_unlock_irqrestore(&i2s_info_g->lock[0], flags); i2s_info_g->callback[0](i2s_info_g->callback_param[0]); } else if (msg->header.cmd == I2S_RX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[1], flags); - rpmsg = &i2s_info_g->send_msg[1]; + rpmsg = &i2s_info_g->send_msg[RPMSG_AUDIO_RX]; rpmsg->param.buffer_tail++; rpmsg->param.buffer_tail %= i2s_info_g->num_period[1]; spin_unlock_irqrestore(&i2s_info_g->lock[1], flags); @@ -492,12 +491,28 @@ static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) { + struct platform_device *codec_pdev; + struct fsl_rpmsg_i2s *rpmsg_i2s = NULL; + int ret; + i2s_info_g->rpdev = rpdev; init_completion(&i2s_info_g->cmd_complete); dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", rpdev->src, rpdev->dst); + + rpmsg_i2s = container_of(i2s_info_g, struct fsl_rpmsg_i2s, i2s_info); + + codec_pdev = platform_device_register_data(&rpmsg_i2s->pdev->dev, + RPMSG_CODEC_DRV_NAME, PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(codec_pdev)) { + dev_err(&rpdev->dev, "failed to register rpmsg audio codec\n"); + ret = PTR_ERR(codec_pdev); + return ret; + } + return 0; } From 35c675bc633467825bc68eb58d6b47121df498db Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 6 Mar 2018 12:46:18 +0800 Subject: [PATCH 19/59] MLK-17156-6: ASoC: imx-pcm-rpmsg: fix get codec data failed Receive message is only used when the type is B. originally we copy the receive message to revg_msg all the time, when the message type is C, which will overide the revg_msg, which cause the get codec data command return wrong value. Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta --- sound/soc/fsl/imx-pcm-rpmsg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 67b44a60cbfe..fc48451083bc 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -463,8 +463,6 @@ static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, dev_dbg(&rpdev->dev, "get from%d: cmd:%d.\n", src, msg->header.cmd); - memcpy(&i2s_info_g->recv_msg, msg, sizeof(struct i2s_rpmsg_r)); - if (msg->header.type == I2S_TYPE_C) { if (msg->header.cmd == I2S_TX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[0], flags); @@ -483,8 +481,10 @@ static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, } } - if (msg->header.type == I2S_TYPE_B) + if (msg->header.type == I2S_TYPE_B) { + memcpy(&i2s_info_g->recv_msg, msg, sizeof(struct i2s_rpmsg_r)); complete(&i2s_info_g->cmd_complete); + } return 0; } From b9156dc3c1beb7862c4108834e9e0f69918a8cae Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 7 Mar 2018 11:14:53 +0800 Subject: [PATCH 20/59] MLK-17156-7: ASoC: imx-rpmsg: use rpmsg codec instead the dummy use the rpmsg_wm8960 codec instead of the dummy codec Signed-off-by: Shengjiu Wang Reviewed-by: Daniel Baluta --- sound/soc/fsl/Kconfig | 1 + sound/soc/fsl/imx-rpmsg.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 50713a6ddd11..744f313c83d7 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -268,6 +268,7 @@ config SND_SOC_IMX_RPMSG depends on OF && I2C && INPUT select SND_SOC_IMX_PCM_RPMSG select SND_SOC_FSL_RPMSG_I2S + select SND_SOC_RPMSG_WM8960 help SoC Audio support for i.MX boards with rpmsg. There should be rpmsg devices defined in other core diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 72b2e01212d9..6887e69df220 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -58,8 +58,8 @@ static int imx_rpmsg_probe(struct platform_device *pdev) data->dai[0].name = "rpmsg hifi"; data->dai[0].stream_name = "rpmsg hifi"; - data->dai[0].codec_dai_name = "snd-soc-dummy-dai"; - data->dai[0].codec_name = "snd-soc-dummy"; + data->dai[0].codec_dai_name = "rpmsg-wm8960-hifi"; + data->dai[0].codec_name = "rpmsg-audio-codec"; data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); data->dai[0].platform_of_node = cpu_np; data->dai[0].playback_only = false; From ce72f6da781e551d349100164c03cc398be38888 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 11 Apr 2018 14:42:05 +0800 Subject: [PATCH 21/59] MLK-17979: ASoC: rpmsg_wm8960: add mixer control for ADC input The microphone only connect to left input, when record stereo channel data, the right channel is mute. Add 'ADC Data Output Select' mixer control that user can select the wanted configure. The default setting is 'Left Data = Left ADC; Right Data = Left ADC'. Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_wm8960.c | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/sound/soc/codecs/rpmsg_wm8960.c b/sound/soc/codecs/rpmsg_wm8960.c index 37a716d10a2b..f473a2cfbe3f 100644 --- a/sound/soc/codecs/rpmsg_wm8960.c +++ b/sound/soc/codecs/rpmsg_wm8960.c @@ -24,13 +24,48 @@ #define WM8960_RINVOL 0x1 #define WM8960_LOUT1 0x2 #define WM8960_ROUT1 0x3 +#define WM8960_DACCTL1 0x5 +#define WM8960_DACCTL2 0x6 #define WM8960_LDAC 0xa #define WM8960_RDAC 0xb + +#define WM8960_3D 0x10 +#define WM8960_ALC1 0x11 +#define WM8960_ALC2 0x12 +#define WM8960_ALC3 0x13 #define WM8960_LADC 0x15 #define WM8960_RADC 0x16 +#define WM8960_ADDCTL1 0x17 +#define WM8960_ADDCTL2 0x18 #define WM8960_LOUT2 0x28 #define WM8960_ROUT2 0x29 +/* enumerated controls */ +static const char * const wm8960_polarity[] = {"No Inversion", "Left Inverted", + "Right Inverted", "Stereo Inversion"}; +static const char * const wm8960_3d_upper_cutoff[] = {"High", "Low"}; +static const char * const wm8960_3d_lower_cutoff[] = {"Low", "High"}; +static const char * const wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; +static const char * const wm8960_alcmode[] = {"ALC", "Limiter"}; +static const char * const wm8960_adc_data_output_sel[] = { + "Left Data = Left ADC; Right Data = Right ADC", + "Left Data = Left ADC; Right Data = Left ADC", + "Left Data = Right ADC; Right Data = Right ADC", + "Left Data = Right ADC; Right Data = Left ADC", +}; +static const char * const wm8960_dmonomix[] = {"Stereo", "Mono"}; + +static const struct soc_enum wm8960_enum[] = { + SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), + SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), + SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), + SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff), + SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc), + SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), + SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel), + SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix), +}; + static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); @@ -47,6 +82,7 @@ SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 0, 127, 0, out_tlv), SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, 0, 255, 0, adc_tlv), +SOC_ENUM("ADC Data Output Select", wm8960_enum[6]), }; #define RPMSG_RATES (SNDRV_PCM_RATE_8000_48000 |\ @@ -124,6 +160,8 @@ static int rpmsg_wm8960_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); + snd_soc_update_bits(codec, WM8960_ADDCTL1, 0xC, 0x4); + return 0; } From 093cb51e6f5378402280a4d0d9179d72edc1a68f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 25 Jul 2018 13:42:50 +0800 Subject: [PATCH 22/59] MLK-19042-1: ASoC: fsl_rpmsg_i2s: support low power audio Add two new message command I2S_TX_POINTER and I2S_RX_POINTER, which are used to get the hw pointer in m4 side. For in low power audio mode, m4 won't send notification every period, the notification only be sent when hw pointer reach end of buffer, so we need these command to get the position of hw pointer, user can use it to calculate the timestamp. Restructure send message and recv message together for i2s_rpmsg, that every send message has a recv message. so the i2s_send_message can store the recv message indepedently. one reason is that the receive message is async withe send message. The low power audio is disabled in default, user need to enabled it by add "fsl,enable-lpa" in dts. Signed-off-by: Shengjiu Wang Reviewed-by: Viorel Suman (cherry picked from commit 753e7b819609ad4791e32069a124d4411c720947) --- .../bindings/sound/fsl,rpmsg-i2s.txt | 1 + sound/soc/fsl/fsl_rpmsg_i2s.c | 115 ++++++++++++------ sound/soc/fsl/fsl_rpmsg_i2s.h | 49 +++++--- 3 files changed, 116 insertions(+), 49 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt index f38cf9d90500..fd62b0d444a8 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt @@ -7,6 +7,7 @@ serial interfaces with frame synchronization such as I2S. Required properties: - compatible : Compatible list, contains "fsl,imx7ulp-rpmsg-i2s". + "fsl,imx8mq-rpmsg-i2s" - fsl,audioindex : This is an index indicating the audio device index in the M4 side. diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index ec3560d3c60c..4d004a3f41ae 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -30,10 +30,10 @@ SNDRV_PCM_RATE_48000) #define FSL_RPMSG_I2S_FORMATS SNDRV_PCM_FMTBIT_S16_LE -static int i2s_send_message(struct i2s_rpmsg_s *msg, +static int i2s_send_message(struct i2s_rpmsg *msg, struct i2s_info *info) { - int err; + int err = 0; mutex_lock(&info->tx_lock); if (!info->rpdev) { @@ -42,10 +42,12 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, return -EINVAL; } - dev_dbg(&info->rpdev->dev, "send cmd %d\n", msg->header.cmd); + dev_dbg(&info->rpdev->dev, "send cmd %d\n", msg->send_msg.header.cmd); - reinit_completion(&info->cmd_complete); - err = rpmsg_send(info->rpdev->ept, (void *)msg, + if (!(msg->send_msg.header.type == I2S_TYPE_C)) + reinit_completion(&info->cmd_complete); + + err = rpmsg_send(info->rpdev->ept, (void *)&msg->send_msg, sizeof(struct i2s_rpmsg_s)); if (err) { dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err); @@ -53,20 +55,25 @@ static int i2s_send_message(struct i2s_rpmsg_s *msg, return err; } - /* wait response from rpmsg */ - err = wait_for_completion_timeout(&info->cmd_complete, + if (!(msg->send_msg.header.type == I2S_TYPE_C)) { + /* 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); - mutex_unlock(&info->tx_lock); - return -ETIMEDOUT; + if (!err) { + dev_err(&info->rpdev->dev, + "rpmsg_send cmd %d timeout!\n", + msg->send_msg.header.cmd); + mutex_unlock(&info->tx_lock); + return -ETIMEDOUT; + } + memcpy(&msg->recv_msg, &info->recv_msg, + sizeof(struct i2s_rpmsg_r)); + memcpy(&info->rpmsg[msg->recv_msg.header.cmd].recv_msg, + &msg->recv_msg, sizeof(struct i2s_rpmsg_r)); } - if (msg->header.cmd == GET_CODEC_VALUE) - msg->param.buffer_size = info->recv_msg.param.reg_data; - - dev_dbg(&info->rpdev->dev, "cmd:%d, resp %d\n", msg->header.cmd, + dev_dbg(&info->rpdev->dev, "cmd:%d, resp %d\n", + msg->send_msg.header.cmd, info->recv_msg.param.resp); mutex_unlock(&info->tx_lock); @@ -99,6 +106,7 @@ static const struct snd_soc_component_driver fsl_component = { static const struct of_device_id fsl_rpmsg_i2s_ids[] = { { .compatible = "fsl,imx7ulp-rpmsg-i2s"}, + { .compatible = "fsl,imx8mq-rpmsg-i2s"}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_i2s_ids); @@ -149,17 +157,56 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) i2s_info->work_list[i].i2s_info = i2s_info; } - for (i = 0; i < RPMSG_AUDIO_NUM; 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; + for (i = 0; i < I2S_CMD_MAX_NUM ; i++) { + i2s_info->rpmsg[i].send_msg.header.cate = IMX_RPMSG_AUDIO; + i2s_info->rpmsg[i].send_msg.header.major = IMX_RMPSG_MAJOR; + i2s_info->rpmsg[i].send_msg.header.minor = IMX_RMPSG_MINOR; + i2s_info->rpmsg[i].send_msg.header.type = I2S_TYPE_A; + i2s_info->rpmsg[i].send_msg.param.audioindex = audioindex; } mutex_init(&i2s_info->tx_lock); mutex_init(&i2s_info->i2c_lock); + if (of_device_is_compatible(pdev->dev.of_node, + "fsl,imx7ulp-rpmsg-i2s")) { + rpmsg_i2s->codec = 1; + rpmsg_i2s->version = 1; + rpmsg_i2s->rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000; + rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE; + fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates; + fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats; + fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates; + fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats; + } + + if (of_device_is_compatible(pdev->dev.of_node, + "fsl,imx8mq-rpmsg-i2s")) { + rpmsg_i2s->codec = 0; + rpmsg_i2s->version = 2; + rpmsg_i2s->rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000; + rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_3LE; + fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates; + fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats; + fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates; + fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats; + } + + if (of_property_read_bool(pdev->dev.of_node, "fsl,enable-lpa")) + rpmsg_i2s->enable_lpa = 1; + + if (of_property_read_u32(np, "fsl,dma-buffer-size", + &i2s_info->prealloc_buffer_size)) + i2s_info->prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE; + platform_set_drvdata(pdev, rpmsg_i2s); pm_runtime_enable(&pdev->dev); @@ -205,17 +252,17 @@ 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; + struct i2s_rpmsg *rpmsg_tx; + struct i2s_rpmsg *rpmsg_rx; flush_workqueue(i2s_info->rpmsg_wq); - rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; - rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; + rpmsg_tx = &i2s_info->rpmsg[I2S_TX_SUSPEND]; + rpmsg_rx = &i2s_info->rpmsg[I2S_RX_SUSPEND]; - rpmsg_tx->header.cmd = I2S_TX_SUSPEND; + rpmsg_tx->send_msg.header.cmd = I2S_TX_SUSPEND; i2s_send_message(rpmsg_tx, i2s_info); - rpmsg_rx->header.cmd = I2S_RX_SUSPEND; + rpmsg_rx->send_msg.header.cmd = I2S_RX_SUSPEND; i2s_send_message(rpmsg_rx, i2s_info); return 0; @@ -225,16 +272,16 @@ 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; + struct i2s_rpmsg *rpmsg_tx; + struct i2s_rpmsg *rpmsg_rx; - rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; - rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; + rpmsg_tx = &i2s_info->rpmsg[I2S_TX_RESUME]; + rpmsg_rx = &i2s_info->rpmsg[I2S_RX_RESUME]; - rpmsg_tx->header.cmd = I2S_TX_RESUME; + rpmsg_tx->send_msg.header.cmd = I2S_TX_RESUME; i2s_send_message(rpmsg_tx, i2s_info); - rpmsg_rx->header.cmd = I2S_RX_RESUME; + rpmsg_rx->send_msg.header.cmd = I2S_RX_RESUME; i2s_send_message(rpmsg_rx, i2s_info); return 0; diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index cbb984e07999..9c5105c0ecf6 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -304,11 +304,20 @@ #define I2S_RX_RESUME 0x13 #define SET_CODEC_VALUE 0x14 #define GET_CODEC_VALUE 0x15 -#define WORK_MAX_NUM 0x16 +#define I2S_TX_POINTER 0x16 +#define I2S_RX_POINTER 0x17 + +#define I2S_TYPE_A_NUM 0x18 + +#define WORK_MAX_NUM 0x18 #define I2S_TX_PERIOD_DONE 0x0 #define I2S_RX_PERIOD_DONE 0x1 +#define I2S_TYPE_C_NUM 0x2 + +#define I2S_CMD_MAX_NUM (I2S_TYPE_A_NUM + I2S_TYPE_C_NUM) + #define I2S_TYPE_A 0x0 #define I2S_TYPE_B 0x1 #define I2S_TYPE_C 0x2 @@ -320,6 +329,7 @@ #define RPMSG_S16_LE 0x0 #define RPMSG_S24_LE 0x1 +#define RPMSG_S32_LE 0x2 #define RPMSG_CH_LEFT 0x0 #define RPMSG_CH_RIGHT 0x1 @@ -339,10 +349,12 @@ struct i2s_param_s { struct i2s_param_r { unsigned char audioindex; unsigned char resp; - unsigned char reserved1[5]; + unsigned char reserved1[1]; + unsigned int buffer_offset; /* the consumed offset of buffer*/ unsigned int reg_addr; unsigned int reg_data; - unsigned char reserved2[8]; + unsigned char reserved2[4]; + unsigned int buffer_tail; } __packed; /* struct of send message */ @@ -357,46 +369,53 @@ struct i2s_rpmsg_r { struct i2s_param_r param; }; +struct i2s_rpmsg { + struct i2s_rpmsg_s send_msg; + struct i2s_rpmsg_r recv_msg; +}; + struct work_of_rpmsg { struct i2s_info *i2s_info; /* sent msg for each work */ - struct i2s_rpmsg_s msg; + struct i2s_rpmsg msg; struct work_struct work; }; -enum { - RPMSG_AUDIO_TX = 0, - RPMSG_AUDIO_RX = 1, - RPMSG_AUDIO_I2C = 2, - RPMSG_AUDIO_NUM = 3, -}; - typedef void (*dma_callback)(void *arg); struct i2s_info { struct rpmsg_device *rpdev; struct device *dev; struct completion cmd_complete; - /* received msg */ + + /* received msg (global) */ struct i2s_rpmsg_r recv_msg; - /* backup sent msg */ - struct i2s_rpmsg_s send_msg[RPMSG_AUDIO_NUM]; + + struct i2s_rpmsg rpmsg[I2S_CMD_MAX_NUM]; struct workqueue_struct *rpmsg_wq; struct work_of_rpmsg work_list[WORK_MAX_NUM]; int work_index; int num_period[2]; void *callback_param[2]; - int (*send_message)(struct i2s_rpmsg_s *msg, struct i2s_info *info); + int (*send_message)(struct i2s_rpmsg *msg, struct i2s_info *info); dma_callback callback[2]; spinlock_t lock[2]; struct mutex tx_lock; struct mutex i2c_lock; + struct timer_list stream_timer[2]; + int prealloc_buffer_size; }; struct fsl_rpmsg_i2s { struct platform_device *pdev; struct i2s_info i2s_info; struct pm_qos_request pm_qos_req; + int codec; + int force_lpa; + int version; + int rates; + u64 formats; + int enable_lpa; }; #define RPMSG_CODEC_DRV_NAME "rpmsg-audio-codec" From c17407c2ddbea76a5c0e0d3a60feb0c98d159d71 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 25 Jul 2018 13:43:05 +0800 Subject: [PATCH 23/59] MLK-19042-2: ASoC: imx-pcm-rpmsg: support low power audio Add ack function, which is to info M4 side how many data has been writen to buffer. Add timer, which is to get the position of hw pointer in m4. Signed-off-by: Shengjiu Wang Reviewed-by: Viorel Suman (cherry picked from commit 0fd349fbab28d97f8c5501ec635bff053e3b1470) --- sound/soc/fsl/imx-pcm-rpmsg.c | 421 ++++++++++++++++++++++++++-------- 1 file changed, 320 insertions(+), 101 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index fc48451083bc..03f184a458ec 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -25,18 +25,19 @@ struct i2s_info *i2s_info_g; -static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { +static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = IMX_SAI_DMABUF_SIZE, .period_bytes_min = 512, .period_bytes_max = 65532, /* Limited by SDMA engine */ .periods_min = 2, - .periods_max = 255, + .periods_max = 6000, .fifo_size = 0, }; @@ -49,37 +50,36 @@ static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; - struct i2s_rpmsg_s *rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; + struct i2s_rpmsg *rpmsg; - rpmsg_tx->param.rate = params_rate(params); - rpmsg_rx->param.rate = params_rate(params); - if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) { - rpmsg_tx->param.format = RPMSG_S16_LE; - rpmsg_rx->param.format = RPMSG_S16_LE; - } else { - rpmsg_tx->param.format = RPMSG_S24_LE; - rpmsg_rx->param.format = RPMSG_S24_LE; - } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_HW_PARAM]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_HW_PARAM]; - if (params_channels(params) == 1) { - rpmsg_tx->param.channels = RPMSG_CH_LEFT; - rpmsg_rx->param.channels = RPMSG_CH_LEFT; - } else { - rpmsg_tx->param.channels = RPMSG_CH_STEREO; - rpmsg_rx->param.channels = RPMSG_CH_STEREO; - } + rpmsg->send_msg.param.rate = params_rate(params); + + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + rpmsg->send_msg.param.format = RPMSG_S16_LE; + else if (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE) + rpmsg->send_msg.param.format = RPMSG_S24_LE; + else + rpmsg->send_msg.param.format = RPMSG_S32_LE; + + if (params_channels(params) == 1) + rpmsg->send_msg.param.channels = RPMSG_CH_LEFT; + else + rpmsg->send_msg.param.channels = RPMSG_CH_STEREO; snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - rpmsg_tx->header.cmd = I2S_TX_HW_PARAM; - i2s_info->send_message(rpmsg_tx, i2s_info); - } else { - rpmsg_rx->header.cmd = I2S_RX_HW_PARAM; - i2s_info->send_message(rpmsg_rx, i2s_info); - } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_HW_PARAM; + else + rpmsg->send_msg.header.cmd = I2S_RX_HW_PARAM; + + i2s_info->send_message(rpmsg, i2s_info); return 0; } @@ -93,29 +93,62 @@ static int imx_rpmsg_pcm_hw_free(struct snd_pcm_substream *substream) static snd_pcm_uframes_t imx_rpmsg_pcm_pointer( struct snd_pcm_substream *substream) { - struct dma_tx_state state; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; - unsigned int buf_size; unsigned int pos = 0; - unsigned long flags; + struct i2s_rpmsg *rpmsg; + int buffer_tail = 0; - spin_lock_irqsave(&i2s_info->lock[substream->stream], flags); - state.residue = (i2s_info->num_period[substream->stream] - - rpmsg->param.buffer_tail) - * rpmsg->param.period_size; - spin_unlock_irqrestore(&i2s_info->lock[substream->stream], flags); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; - buf_size = snd_pcm_lib_buffer_bytes(substream); - if (state.residue > 0 && state.residue <= buf_size) - pos = buf_size - state.residue; + buffer_tail = rpmsg->recv_msg.param.buffer_offset / + snd_pcm_lib_period_bytes(substream); + pos = buffer_tail * snd_pcm_lib_period_bytes(substream); return bytes_to_frames(substream->runtime, pos); } +static void imx_rpmsg_timer_callback(unsigned long data) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg *rpmsg; + u8 index = i2s_info->work_index; + int time_msec; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_POINTER; + else + rpmsg->send_msg.header.cmd = I2S_RX_POINTER; + + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; + + if (rpmsg_i2s->force_lpa) { + time_msec = min(500, + (int)(runtime->period_size*1000/runtime->rate)); + mod_timer(&i2s_info->stream_timer[substream->stream], + jiffies + msecs_to_jiffies(time_msec)); + } +} + static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) { int ret = 0; @@ -123,15 +156,39 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; struct dmaengine_pcm_runtime_data *prtd; + int cmd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_OPEN]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_OPEN]; + + imx_rpmsg_pcm_hardware.buffer_bytes_max = + i2s_info->prealloc_buffer_size; + imx_rpmsg_pcm_hardware.period_bytes_max = + imx_rpmsg_pcm_hardware.buffer_bytes_max / 2; snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_OPEN; + rpmsg->send_msg.header.cmd = I2S_TX_OPEN; else - rpmsg->header.cmd = I2S_RX_OPEN; + rpmsg->send_msg.header.cmd = I2S_RX_OPEN; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + cmd = I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM; + i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; + i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0; + i2s_info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0; + } else { + cmd = I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM; + i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; + i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0; + i2s_info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0; + } + i2s_info->send_message(rpmsg, i2s_info); prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); @@ -145,6 +202,11 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) return ret; + + /*create thread*/ + setup_timer(&i2s_info->stream_timer[substream->stream], + imx_rpmsg_timer_callback, (unsigned long)substream); + return ret; } @@ -155,23 +217,51 @@ static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_CLOSE; + rpmsg = &i2s_info->rpmsg[I2S_TX_CLOSE]; else - rpmsg->header.cmd = I2S_RX_CLOSE; + rpmsg = &i2s_info->rpmsg[I2S_RX_CLOSE]; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_CLOSE; + else + rpmsg->send_msg.header.cmd = I2S_RX_CLOSE; flush_workqueue(i2s_info->rpmsg_wq); i2s_info->send_message(rpmsg, i2s_info); + del_timer(&i2s_info->stream_timer[substream->stream]); + kfree(prtd); return ret; } +static int imx_rpmsg_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + + /* NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts + * four condition to determine the lpa is enabled. + */ + if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) && + (substream->f_flags & O_NONBLOCK) && + rpmsg_i2s->version == 2 && + rpmsg_i2s->enable_lpa) + rpmsg_i2s->force_lpa = 1; + else + rpmsg_i2s->force_lpa = 0; + + return 0; +} + static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { @@ -186,12 +276,23 @@ static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, static void imx_rpmsg_pcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; - struct dmaengine_pcm_runtime_data *prtd - = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg *rpmsg, *rpmsg2; - prtd->pos += snd_pcm_lib_period_bytes(substream); - if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) - prtd->pos = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + rpmsg2 = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; + } else { + rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; + rpmsg2 = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; + } + + rpmsg->recv_msg.param.buffer_offset = + rpmsg2->recv_msg.param.buffer_tail + * snd_pcm_lib_period_bytes(substream); snd_pcm_period_elapsed(substream); } @@ -202,24 +303,31 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_BUFFER; + rpmsg = &i2s_info->rpmsg[I2S_TX_BUFFER]; else - rpmsg->header.cmd = I2S_RX_BUFFER; + rpmsg = &i2s_info->rpmsg[I2S_RX_BUFFER]; - rpmsg->param.buffer_addr = substream->runtime->dma_addr; - rpmsg->param.buffer_size = snd_pcm_lib_buffer_bytes(substream); - rpmsg->param.period_size = snd_pcm_lib_period_bytes(substream); - rpmsg->param.buffer_tail = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_BUFFER; + else + rpmsg->send_msg.header.cmd = I2S_RX_BUFFER; + + rpmsg->send_msg.param.buffer_addr = substream->runtime->dma_addr; + rpmsg->send_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream); + rpmsg->send_msg.param.period_size = snd_pcm_lib_period_bytes(substream); + rpmsg->send_msg.param.buffer_tail = 0; i2s_info->num_period[substream->stream] = - rpmsg->param.buffer_size/rpmsg->param.period_size; + rpmsg->send_msg.param.buffer_size / + rpmsg->send_msg.param.period_size; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; @@ -231,41 +339,60 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) static void imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; + int time_msec; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_START; + rpmsg = &i2s_info->rpmsg[I2S_TX_START]; else - rpmsg->header.cmd = I2S_RX_START; + rpmsg = &i2s_info->rpmsg[I2S_RX_START]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_START; + else + rpmsg->send_msg.header.cmd = I2S_RX_START; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; + + if (rpmsg_i2s->force_lpa) { + time_msec = min(500, + (int)(runtime->period_size*1000/runtime->rate)); + mod_timer(&i2s_info->stream_timer[substream->stream], + jiffies + msecs_to_jiffies(time_msec)); + } } -static int imx_rpmsg_resume(struct snd_pcm_substream *substream) +static int imx_rpmsg_restart(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_RESTART; + rpmsg = &i2s_info->rpmsg[I2S_TX_RESTART]; else - rpmsg->header.cmd = I2S_RX_RESTART; + rpmsg = &i2s_info->rpmsg[I2S_RX_RESTART]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_RESTART; + else + rpmsg->send_msg.header.cmd = I2S_RX_RESTART; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; @@ -279,20 +406,24 @@ static int imx_rpmsg_pause(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_PAUSE; + rpmsg = &i2s_info->rpmsg[I2S_TX_PAUSE]; else - rpmsg->header.cmd = I2S_RX_PAUSE; + rpmsg = &i2s_info->rpmsg[I2S_RX_PAUSE]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_PAUSE; + else + rpmsg->send_msg.header.cmd = I2S_RX_PAUSE; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; - return 0; } @@ -302,26 +433,35 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[substream->stream]; + struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->header.cmd = I2S_TX_TERMINATE; + rpmsg = &i2s_info->rpmsg[I2S_TX_TERMINATE]; else - rpmsg->header.cmd = I2S_RX_TERMINATE; + rpmsg = &i2s_info->rpmsg[I2S_RX_TERMINATE]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_TERMINATE; + else + rpmsg->send_msg.header.cmd = I2S_RX_TERMINATE; memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); + sizeof(struct i2s_rpmsg_s)); queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; + del_timer(&i2s_info->stream_timer[substream->stream]); return 0; } int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); int ret; switch (cmd) { @@ -332,14 +472,18 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) imx_rpmsg_async_issue_pending(substream); break; case SNDRV_PCM_TRIGGER_RESUME: + if (rpmsg_i2s->force_lpa) + break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - imx_rpmsg_resume(substream); + imx_rpmsg_restart(substream); break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (runtime->info & SNDRV_PCM_INFO_PAUSE) - imx_rpmsg_pause(substream); - else - imx_rpmsg_terminate_all(substream); + if (!rpmsg_i2s->force_lpa) { + if (runtime->info & SNDRV_PCM_INFO_PAUSE) + imx_rpmsg_pause(substream); + else + imx_rpmsg_terminate_all(substream); + } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: imx_rpmsg_pause(substream); @@ -354,6 +498,50 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } +int imx_rpmsg_pcm_ack(struct snd_pcm_substream *substream) +{ + /*send the hw_avail size through rpmsg*/ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg *rpmsg; + u8 index = i2s_info->work_index; + int buffer_tail = 0; + + if (!rpmsg_i2s->force_lpa) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; + else + rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rpmsg->send_msg.header.cmd = I2S_TX_PERIOD_DONE; + else + rpmsg->send_msg.header.cmd = I2S_RX_PERIOD_DONE; + + rpmsg->send_msg.header.type = I2S_TYPE_C; + + buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) % + snd_pcm_lib_buffer_bytes(substream)); + buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream); + + if (buffer_tail != rpmsg->send_msg.param.buffer_tail) { + rpmsg->send_msg.param.buffer_tail = buffer_tail; + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, + &i2s_info->work_list[index].work); + i2s_info->work_index++; + i2s_info->work_index %= WORK_MAX_NUM; + } + + return 0; +} + static struct snd_pcm_ops imx_rpmsg_pcm_ops = { .open = imx_rpmsg_pcm_open, .close = imx_rpmsg_pcm_close, @@ -363,14 +551,15 @@ static struct snd_pcm_ops imx_rpmsg_pcm_ops = { .trigger = imx_rpmsg_pcm_trigger, .pointer = imx_rpmsg_pcm_pointer, .mmap = imx_rpmsg_pcm_mmap, + .ack = imx_rpmsg_pcm_ack, + .prepare = imx_rpmsg_pcm_prepare, }; static int imx_rpmsg_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) + int stream, int size) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = imx_rpmsg_pcm_hardware.buffer_bytes_max; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -410,6 +599,9 @@ static int imx_rpmsg_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; int ret; ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); @@ -418,14 +610,16 @@ static int imx_rpmsg_pcm_new(struct snd_soc_pcm_runtime *rtd) if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, + i2s_info->prealloc_buffer_size); if (ret) goto out; } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); + SNDRV_PCM_STREAM_CAPTURE, + i2s_info->prealloc_buffer_size); if (ret) goto out; } @@ -458,24 +652,41 @@ static int i2s_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) { struct i2s_rpmsg_r *msg = (struct i2s_rpmsg_r *)data; - struct i2s_rpmsg_s *rpmsg; + struct i2s_rpmsg *rpmsg; unsigned long flags; - dev_dbg(&rpdev->dev, "get from%d: cmd:%d.\n", src, msg->header.cmd); + dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n", + src, msg->header.cmd, msg->param.resp); if (msg->header.type == I2S_TYPE_C) { if (msg->header.cmd == I2S_TX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[0], flags); - rpmsg = &i2s_info_g->send_msg[RPMSG_AUDIO_TX]; - rpmsg->param.buffer_tail++; - rpmsg->param.buffer_tail %= i2s_info_g->num_period[0]; + rpmsg = &i2s_info_g->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; + + if (msg->header.major == 1 && msg->header.minor == 2) + rpmsg->recv_msg.param.buffer_tail = + msg->param.buffer_tail; + else + rpmsg->recv_msg.param.buffer_tail++; + + rpmsg->recv_msg.param.buffer_tail %= + i2s_info_g->num_period[0]; + spin_unlock_irqrestore(&i2s_info_g->lock[0], flags); i2s_info_g->callback[0](i2s_info_g->callback_param[0]); + } else if (msg->header.cmd == I2S_RX_PERIOD_DONE) { spin_lock_irqsave(&i2s_info_g->lock[1], flags); - rpmsg = &i2s_info_g->send_msg[RPMSG_AUDIO_RX]; - rpmsg->param.buffer_tail++; - rpmsg->param.buffer_tail %= i2s_info_g->num_period[1]; + rpmsg = &i2s_info_g->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; + + if (msg->header.major == 1 && msg->header.minor == 2) + rpmsg->recv_msg.param.buffer_tail = + msg->param.buffer_tail; + else + rpmsg->recv_msg.param.buffer_tail++; + + rpmsg->recv_msg.param.buffer_tail %= + i2s_info_g->num_period[1]; spin_unlock_irqrestore(&i2s_info_g->lock[1], flags); i2s_info_g->callback[1](i2s_info_g->callback_param[1]); } @@ -495,6 +706,9 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) struct fsl_rpmsg_i2s *rpmsg_i2s = NULL; int ret; + if (!i2s_info_g) + return 0; + i2s_info_g->rpdev = rpdev; init_completion(&i2s_info_g->cmd_complete); @@ -504,13 +718,18 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) rpmsg_i2s = container_of(i2s_info_g, struct fsl_rpmsg_i2s, i2s_info); - codec_pdev = platform_device_register_data(&rpmsg_i2s->pdev->dev, - RPMSG_CODEC_DRV_NAME, PLATFORM_DEVID_NONE, - NULL, 0); - if (IS_ERR(codec_pdev)) { - dev_err(&rpdev->dev, "failed to register rpmsg audio codec\n"); - ret = PTR_ERR(codec_pdev); - return ret; + if (rpmsg_i2s->codec) { + codec_pdev = platform_device_register_data( + &rpmsg_i2s->pdev->dev, + RPMSG_CODEC_DRV_NAME, + PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(codec_pdev)) { + dev_err(&rpdev->dev, + "failed to register rpmsg audio codec\n"); + ret = PTR_ERR(codec_pdev); + return ret; + } } return 0; From 354ef27ff1ca07fe0d242f428c6dccb0afb6aca7 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 25 Jul 2018 13:43:17 +0800 Subject: [PATCH 24/59] MLK-19042-3: ASoC: rpmsg_wm8960: changes for receive message Update the read and write function for the send_message function changes Signed-off-by: Shengjiu Wang Reviewed-by: Viorel Suman (cherry picked from commit 4610826f39ee4124588b818e5589c25448aa3dba) --- sound/soc/codecs/rpmsg_wm8960.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rpmsg_wm8960.c b/sound/soc/codecs/rpmsg_wm8960.c index f473a2cfbe3f..15132321018d 100644 --- a/sound/soc/codecs/rpmsg_wm8960.c +++ b/sound/soc/codecs/rpmsg_wm8960.c @@ -113,14 +113,14 @@ static unsigned int rpmsg_wm8960_read(struct snd_soc_codec *codec, unsigned int { struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_codec_get_drvdata(codec); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[RPMSG_AUDIO_I2C]; + struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[GET_CODEC_VALUE].send_msg; int err, reg_val; mutex_lock(&i2s_info->i2c_lock); rpmsg->param.buffer_addr = reg; rpmsg->header.cmd = GET_CODEC_VALUE; - err = i2s_info->send_message(rpmsg, i2s_info); - reg_val = rpmsg->param.buffer_size; + err = i2s_info->send_message(&i2s_info->rpmsg[GET_CODEC_VALUE], i2s_info); + reg_val = i2s_info->rpmsg[GET_CODEC_VALUE].recv_msg.param.reg_data; mutex_unlock(&i2s_info->i2c_lock); if (err) return 0; @@ -132,14 +132,14 @@ static int rpmsg_wm8960_write(struct snd_soc_codec *codec, unsigned int reg, uns { struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_codec_get_drvdata(codec); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg_s *rpmsg = &i2s_info->send_msg[RPMSG_AUDIO_I2C]; + struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[SET_CODEC_VALUE].send_msg; int err; mutex_lock(&i2s_info->i2c_lock); rpmsg->param.buffer_addr = reg; rpmsg->param.buffer_size = val; rpmsg->header.cmd = SET_CODEC_VALUE; - err = i2s_info->send_message(rpmsg, i2s_info); + err = i2s_info->send_message(&i2s_info->rpmsg[SET_CODEC_VALUE], i2s_info); mutex_unlock(&i2s_info->i2c_lock); if (err) return err; From 95f35f84fe5e90425c433e580b8f7a942b6d297b Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 25 Jul 2018 13:43:31 +0800 Subject: [PATCH 25/59] MLK-19042-4: ASoC: imx-rpmsg: dummy codec support in imx8mm In imx8mm, there is no controls for wm8524, so we use the dummy codec instead. Signed-off-by: Shengjiu Wang Reviewed-by: Viorel Suman (cherry picked from commit 43bcd3a8e9f4c1b2af9974f2082a34beacfba4a1) --- sound/soc/fsl/imx-rpmsg.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 6887e69df220..844bdaceb472 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -34,6 +34,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev) struct device_node *cpu_np; struct platform_device *cpu_pdev; struct imx_rpmsg_data *data; + struct fsl_rpmsg_i2s *rpmsg_i2s; int ret; cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); @@ -56,10 +57,17 @@ static int imx_rpmsg_probe(struct platform_device *pdev) goto fail; } + rpmsg_i2s = platform_get_drvdata(cpu_pdev); + data->dai[0].name = "rpmsg hifi"; data->dai[0].stream_name = "rpmsg hifi"; - data->dai[0].codec_dai_name = "rpmsg-wm8960-hifi"; - data->dai[0].codec_name = "rpmsg-audio-codec"; + if (rpmsg_i2s->codec) { + data->dai[0].codec_dai_name = "rpmsg-wm8960-hifi"; + data->dai[0].codec_name = "rpmsg-audio-codec"; + } else { + data->dai[0].codec_dai_name = "snd-soc-dummy-dai"; + data->dai[0].codec_name = "snd-soc-dummy"; + } data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); data->dai[0].platform_of_node = cpu_np; data->dai[0].playback_only = false; From 610f643cd35763a6e144075bc4a8f0a42e19a7f8 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 27 Jul 2018 17:50:56 +0800 Subject: [PATCH 26/59] MLK-19067-1: ASoC: imx-rpmsg: differentiate input and output direction Some platform the rpmsg device only support playback or record. So Add a property to differentiate them. Signed-off-by: Shengjiu Wang (cherry picked from commit aa8b9304d207e5f00cca8a41772c130dd4c6944b) --- sound/soc/fsl/imx-rpmsg.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 844bdaceb472..a5bb68d7afb4 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -70,14 +70,26 @@ static int imx_rpmsg_probe(struct platform_device *pdev) } data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); data->dai[0].platform_of_node = cpu_np; - data->dai[0].playback_only = false; - data->dai[0].capture_only = false; + data->dai[0].playback_only = true; + data->dai[0].capture_only = true; data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; data->card.num_links = 1; data->card.dai_link = data->dai; + if (of_property_read_bool(pdev->dev.of_node, "rpmsg-out")) + data->dai[0].capture_only = false; + + if (of_property_read_bool(pdev->dev.of_node, "rpmsg-in")) + data->dai[0].playback_only = false; + + if (data->dai[0].playback_only && data->dai[0].capture_only) { + dev_err(&pdev->dev, "no enabled rpmsg DAI link\n"); + ret = -EINVAL; + goto fail; + } + data->card.dev = &pdev->dev; data->card.owner = THIS_MODULE; ret = snd_soc_of_parse_card_name(&data->card, "model"); From 6b270ced20e40ffa32c46e5fa475138c5c5b01fd Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 13:34:33 +0800 Subject: [PATCH 27/59] MLK-19155: ASoC: fsl_rpmsg: fix the volume is low for S24_LE The format send to M4 through rpmsg is wrong, that make the driver treat the data as S32_LE, it looks like data is right shift 8 bit. Signed-off-by: Shengjiu Wang (cherry picked from commit 508550b70e80339d3d49594ffc23946dd80b0c82) --- sound/soc/fsl/fsl_rpmsg_i2s.c | 3 +-- sound/soc/fsl/imx-pcm-rpmsg.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 4d004a3f41ae..202906cd3c98 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -192,8 +192,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) SNDRV_PCM_RATE_192000; rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE | - SNDRV_PCM_FMTBIT_S24_3LE; + SNDRV_PCM_FMTBIT_S32_LE; fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates; fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats; fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates; diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 03f184a458ec..4caf8326b738 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -61,7 +61,7 @@ static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream, if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) rpmsg->send_msg.param.format = RPMSG_S16_LE; - else if (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE) + else if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE) rpmsg->send_msg.param.format = RPMSG_S24_LE; else rpmsg->send_msg.param.format = RPMSG_S32_LE; From 69776826fdc4b45fa56b532ae13d17ff570cdfec Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 15:02:55 +0800 Subject: [PATCH 28/59] MLK-19168-1: ASoC: rpmsg_wm8960: port all function from wm8960 The difference of rpmsg_wm8960 and wm8960 driver is previous one will send command through rpmsg, others are same. Signed-off-by: Shengjiu Wang (cherry picked from commit 094521755ae806a572fd841455371b23408c36d1) --- sound/soc/codecs/rpmsg_wm8960.c | 1464 +++++++++++++++++++++++++++++-- 1 file changed, 1395 insertions(+), 69 deletions(-) diff --git a/sound/soc/codecs/rpmsg_wm8960.c b/sound/soc/codecs/rpmsg_wm8960.c index 15132321018d..644a1eea13ab 100644 --- a/sound/soc/codecs/rpmsg_wm8960.c +++ b/sound/soc/codecs/rpmsg_wm8960.c @@ -1,6 +1,9 @@ /* + * Copyright 2018 NXP * - * Copyright 2017 NXP + * Copyright 2007-11 Wolfson Microelectronics, plc + * + * Author: Liam Girdwood * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -11,34 +14,137 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ +#include #include #include +#include #include #include #include #include #include +#include #include "../fsl/fsl_rpmsg_i2s.h" +#include "wm8960.h" -#define WM8960_LINVOL 0x0 -#define WM8960_RINVOL 0x1 -#define WM8960_LOUT1 0x2 -#define WM8960_ROUT1 0x3 -#define WM8960_DACCTL1 0x5 -#define WM8960_DACCTL2 0x6 -#define WM8960_LDAC 0xa -#define WM8960_RDAC 0xb +/* R25 - Power 1 */ +#define WM8960_VMID_MASK 0x180 +#define WM8960_VREF 0x40 -#define WM8960_3D 0x10 -#define WM8960_ALC1 0x11 -#define WM8960_ALC2 0x12 -#define WM8960_ALC3 0x13 -#define WM8960_LADC 0x15 -#define WM8960_RADC 0x16 -#define WM8960_ADDCTL1 0x17 -#define WM8960_ADDCTL2 0x18 -#define WM8960_LOUT2 0x28 -#define WM8960_ROUT2 0x29 +/* R26 - Power 2 */ +#define WM8960_PWR2_LOUT1 0x40 +#define WM8960_PWR2_ROUT1 0x20 +#define WM8960_PWR2_OUT3 0x02 + +/* R28 - Anti-pop 1 */ +#define WM8960_POBCTRL 0x80 +#define WM8960_BUFDCOPEN 0x10 +#define WM8960_BUFIOEN 0x08 +#define WM8960_SOFT_ST 0x04 +#define WM8960_HPSTBY 0x01 + +/* R29 - Anti-pop 2 */ +#define WM8960_DISOP 0x40 +#define WM8960_DRES_MASK 0x30 + +static bool is_pll_freq_available(unsigned int source, unsigned int target); +static int wm8960_set_pll(struct snd_soc_codec *codec, + unsigned int freq_in, unsigned int freq_out); +/* + * wm8960 register cache + * We can't read the WM8960 register space when we are + * using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8960_reg_defaults[] = { + { 0x0, 0x00a7 }, + { 0x1, 0x00a7 }, + { 0x2, 0x0000 }, + { 0x3, 0x0000 }, + { 0x4, 0x0000 }, + { 0x5, 0x0008 }, + { 0x6, 0x0000 }, + { 0x7, 0x000a }, + { 0x8, 0x01c0 }, + { 0x9, 0x0000 }, + { 0xa, 0x00ff }, + { 0xb, 0x00ff }, + + { 0x10, 0x0000 }, + { 0x11, 0x007b }, + { 0x12, 0x0100 }, + { 0x13, 0x0032 }, + { 0x14, 0x0000 }, + { 0x15, 0x00c3 }, + { 0x16, 0x00c3 }, + { 0x17, 0x01c0 }, + { 0x18, 0x0000 }, + { 0x19, 0x0000 }, + { 0x1a, 0x0000 }, + { 0x1b, 0x0000 }, + { 0x1c, 0x0000 }, + { 0x1d, 0x0000 }, + + { 0x20, 0x0100 }, + { 0x21, 0x0100 }, + { 0x22, 0x0050 }, + + { 0x25, 0x0050 }, + { 0x26, 0x0000 }, + { 0x27, 0x0000 }, + { 0x28, 0x0000 }, + { 0x29, 0x0000 }, + { 0x2a, 0x0040 }, + { 0x2b, 0x0000 }, + { 0x2c, 0x0000 }, + { 0x2d, 0x0050 }, + { 0x2e, 0x0050 }, + { 0x2f, 0x0000 }, + { 0x30, 0x0002 }, + { 0x31, 0x0037 }, + + { 0x33, 0x0080 }, + { 0x34, 0x0008 }, + { 0x35, 0x0031 }, + { 0x36, 0x0026 }, + { 0x37, 0x00e9 }, +}; + +struct rpmsg_wm8960_priv { + struct clk *mclk; + struct regmap *regmap; + int (*set_bias_level)(struct snd_soc_codec *, + enum snd_soc_bias_level level); + struct snd_soc_dapm_widget *lout1; + struct snd_soc_dapm_widget *rout1; + struct snd_soc_dapm_widget *out3; + bool deemph; + int lrclk; + int bclk; + int sysclk; + int clk_id; + int freq_in; + bool is_stream_in_use[2]; + struct wm8960_data pdata; + struct fsl_rpmsg_i2s *rpmsg_i2s; + int audioindex; +}; + +static bool wm8960_volatile(struct device *dev, unsigned int reg) +{ + struct rpmsg_wm8960_priv *wm8960 = dev_get_drvdata(dev); + + if (!wm8960->mclk) + return true; + + switch (reg) { + case WM8960_RESET: + return true; + default: + return false; + } +} + +#define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) /* enumerated controls */ static const char * const wm8960_polarity[] = {"No Inversion", "Left Inverted", @@ -66,134 +172,1353 @@ static const struct soc_enum wm8960_enum[] = { SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix), }; +static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; + +static int wm8960_set_deemph(struct snd_soc_codec *codec) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8960->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i] - wm8960->lrclk) < + abs(deemph_settings[best] - wm8960->lrclk)) + best = i; + } + + val = best << 1; + } else { + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d\n", val); + + return snd_soc_update_bits(codec, WM8960_DACCTL1, + 0x6, val); +} + +static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wm8960->deemph; + return 0; +} + +static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + unsigned int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + wm8960->deemph = deemph; + + return wm8960_set_deemph(codec); +} + static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(micboost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0), + 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0), +); -static struct snd_kcontrol_new rpmsg_wm8960_ctrls[] = { +static const struct snd_kcontrol_new wm8960_snd_controls[] = { SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, - 0, 63, 0, inpga_tlv), + 0, 63, 0, inpga_tlv), +SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, + 6, 1, 0), +SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, + 7, 1, 1), + +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", + WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", + WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", + WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", + WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", + WM8960_RINPATH, 4, 3, 0, micboost_tlv), +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume", + WM8960_LINPATH, 4, 3, 0, micboost_tlv), + SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, - 0, 255, 0, dac_tlv), + 0, 255, 0, dac_tlv), + SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, + 7, 1, 0), + SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, + 7, 1, 0), +SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), +SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), + +SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), +SOC_ENUM("ADC Polarity", wm8960_enum[0]), +SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), + +SOC_ENUM("DAC Polarity", wm8960_enum[1]), +SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + wm8960_get_deemph, wm8960_put_deemph), + +SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), +SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), +SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), +SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), + +SOC_ENUM("ALC Function", wm8960_enum[4]), +SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), +SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), +SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), +SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), +SOC_ENUM("ALC Mode", wm8960_enum[5]), +SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), + +SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), +SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), + SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, 0, 255, 0, adc_tlv), + +SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", + WM8960_BYPASS1, 4, 7, 1, bypass_tlv), +SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", + WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), +SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", + WM8960_BYPASS2, 4, 7, 1, bypass_tlv), +SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", + WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), + SOC_ENUM("ADC Data Output Select", wm8960_enum[6]), +SOC_ENUM("DAC Mono Mix", wm8960_enum[7]), }; -#define RPMSG_RATES (SNDRV_PCM_RATE_8000_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) +static const struct snd_kcontrol_new wm8960_lin_boost[] = { +SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), +SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_lin[] = { +SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_rin_boost[] = { +SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), +SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_rin[] = { +SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { +SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), +SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_routput_mixer[] = { +SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), +SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_mono_out[] = { +SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("LINPUT1"), +SND_SOC_DAPM_INPUT("RINPUT1"), +SND_SOC_DAPM_INPUT("LINPUT2"), +SND_SOC_DAPM_INPUT("RINPUT2"), +SND_SOC_DAPM_INPUT("LINPUT3"), +SND_SOC_DAPM_INPUT("RINPUT3"), + +SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, + wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), +SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, + wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), + +SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, + wm8960_lin, ARRAY_SIZE(wm8960_lin)), +SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, + wm8960_rin, ARRAY_SIZE(wm8960_rin)), + +SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0), +SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER1, 2, 0), + +SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), +SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, + &wm8960_loutput_mixer[0], + ARRAY_SIZE(wm8960_loutput_mixer)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, + &wm8960_routput_mixer[0], + ARRAY_SIZE(wm8960_routput_mixer)), + +SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("SPK_LP"), +SND_SOC_DAPM_OUTPUT("SPK_LN"), +SND_SOC_DAPM_OUTPUT("HP_L"), +SND_SOC_DAPM_OUTPUT("HP_R"), +SND_SOC_DAPM_OUTPUT("SPK_RP"), +SND_SOC_DAPM_OUTPUT("SPK_RN"), +SND_SOC_DAPM_OUTPUT("OUT3"), +}; + +static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { +SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, + &wm8960_mono_out[0], + ARRAY_SIZE(wm8960_mono_out)), +}; + +/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ +static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { +SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route audio_paths[] = { + { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, + { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, + { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, + + { "Left Input Mixer", "Boost Switch", "Left Boost Mixer" }, + { "Left Input Mixer", "Boost Switch", "LINPUT1" }, /* Really Boost Switch */ + { "Left Input Mixer", NULL, "LINPUT2" }, + { "Left Input Mixer", NULL, "LINPUT3" }, + + { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, + { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, + { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, + + { "Right Input Mixer", "Boost Switch", "Right Boost Mixer" }, + { "Right Input Mixer", "Boost Switch", "RINPUT1" }, /* Really Boost Switch */ + { "Right Input Mixer", NULL, "RINPUT2" }, + { "Right Input Mixer", NULL, "RINPUT3" }, + + { "Left ADC", NULL, "Left Input Mixer" }, + { "Right ADC", NULL, "Right Input Mixer" }, + + { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, + { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer" }, + { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, + + { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, + { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" }, + { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, + + { "LOUT1 PGA", NULL, "Left Output Mixer" }, + { "ROUT1 PGA", NULL, "Right Output Mixer" }, + + { "HP_L", NULL, "LOUT1 PGA" }, + { "HP_R", NULL, "ROUT1 PGA" }, + + { "Left Speaker PGA", NULL, "Left Output Mixer" }, + { "Right Speaker PGA", NULL, "Right Output Mixer" }, + + { "Left Speaker Output", NULL, "Left Speaker PGA" }, + { "Right Speaker Output", NULL, "Right Speaker PGA" }, + + { "SPK_LN", NULL, "Left Speaker Output" }, + { "SPK_LP", NULL, "Left Speaker Output" }, + { "SPK_RN", NULL, "Right Speaker Output" }, + { "SPK_RP", NULL, "Right Speaker Output" }, +}; + +static const struct snd_soc_dapm_route audio_paths_out3[] = { + { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, + { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, + + { "OUT3", NULL, "Mono Output Mixer", } +}; + +static const struct snd_soc_dapm_route audio_paths_capless[] = { + { "HP_L", NULL, "OUT3 VMID" }, + { "HP_R", NULL, "OUT3 VMID" }, + + { "OUT3 VMID", NULL, "Left Output Mixer" }, + { "OUT3 VMID", NULL, "Right Output Mixer" }, +}; + +static int wm8960_add_widgets(struct snd_soc_codec *codec) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_data *pdata = &wm8960->pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dapm_widget *w; + + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, + ARRAY_SIZE(wm8960_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); + + /* In capless mode OUT3 is used to provide VMID for the + * headphone outputs, otherwise it is used as a mono mixer. + */ + if (pdata && pdata->capless) { + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, + ARRAY_SIZE(wm8960_dapm_widgets_capless)); + + snd_soc_dapm_add_routes(dapm, audio_paths_capless, + ARRAY_SIZE(audio_paths_capless)); + } else { + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, + ARRAY_SIZE(wm8960_dapm_widgets_out3)); + + snd_soc_dapm_add_routes(dapm, audio_paths_out3, + ARRAY_SIZE(audio_paths_out3)); + } + + /* We need to power up the headphone output stage out of + * sequence for capless mode. To save scanning the widget + * list each time to find the desired power state do so now + * and save the result. + */ + list_for_each_entry(w, &codec->component.card->widgets, list) { + if (w->dapm != dapm) + continue; + if (strcmp(w->name, "LOUT1 PGA") == 0) + wm8960->lout1 = w; + if (strcmp(w->name, "ROUT1 PGA") == 0) + wm8960->rout1 = w; + if (strcmp(w->name, "OUT3 VMID") == 0) + wm8960->out3 = w; + } + + return 0; +} + +static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface |= 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + /* set iface */ + snd_soc_write(codec, WM8960_IFACE1, iface); + return 0; +} + +static struct { + int rate; + unsigned int val; +} alc_rates[] = { + { 48000, 0 }, + { 44100, 0 }, + { 32000, 1 }, + { 22050, 2 }, + { 24000, 2 }, + { 16000, 3 }, + { 11025, 4 }, + { 12000, 4 }, + { 8000, 5 }, +}; + +/* -1 for reserved value */ +static const int sysclk_divs[] = { 1, -1, 2, -1 }; + +/* Multiply 256 for internal 256 div */ +static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; + +/* Multiply 10 to eliminate decimials */ +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, + 120, 160, 220, 240, 320, 320, 320 +}; + +/** + * wm8960_configure_sysclk - checks if there is a sysclk frequency available + * The sysclk must be chosen such that: + * - sysclk = MCLK / sysclk_divs + * - lrclk = sysclk / dac_divs + * - 10 * bclk = sysclk / bclk_divs + * + * @wm8960_priv: wm8960 codec private data + * @mclk: MCLK used to derive sysclk + * @sysclk_idx: sysclk_divs index for found sysclk + * @dac_idx: dac_divs index for found lrclk + * @bclk_idx: bclk_divs index for found bclk + * + * Returns: + * -1, in case no sysclk frequency available found + * >=0, in case we could derive bclk and lrclk from sysclk using + * (@sysclk_idx, @dac_idx, @bclk_idx) dividers + */ +static +int wm8960_configure_sysclk(struct rpmsg_wm8960_priv *wm8960, int mclk, + int *sysclk_idx, int *dac_idx, int *bclk_idx) +{ + int sysclk, bclk, lrclk; + int i, j, k; + int diff; + + /* marker for no match */ + *bclk_idx = -1; + + bclk = wm8960->bclk; + lrclk = wm8960->lrclk; + + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk = mclk / sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + if (sysclk != dac_divs[j] * lrclk) + continue; + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { + diff = sysclk - bclk * bclk_divs[k] / 10; + if (diff == 0) { + *sysclk_idx = i; + *dac_idx = j; + *bclk_idx = k; + break; + } + } + if (k != ARRAY_SIZE(bclk_divs)) + break; + } + if (j != ARRAY_SIZE(dac_divs)) + break; + } + return *bclk_idx; +} + +/** + * wm8960_configure_pll - checks if there is a PLL out frequency available + * The PLL out frequency must be chosen such that: + * - sysclk = lrclk * dac_divs + * - freq_out = sysclk * sysclk_divs + * - 10 * sysclk = bclk * bclk_divs + * + * @codec: codec structure + * @freq_in: input frequency used to derive freq out via PLL + * @sysclk_idx: sysclk_divs index for found sysclk + * @dac_idx: dac_divs index for found lrclk + * @bclk_idx: bclk_divs index for found bclk + * + * Returns: + * -1, in case no PLL frequency out available was found + * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using + * (@sysclk_idx, @dac_idx, @bclk_idx) dividers + */ +static +int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, + int *sysclk_idx, int *dac_idx, int *bclk_idx) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + int sysclk, bclk, lrclk, freq_out; + int diff, best_freq_out = 0; + int i, j, k; + + bclk = wm8960->bclk; + lrclk = wm8960->lrclk; + + *bclk_idx = *dac_idx = *sysclk_idx = -1; + + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + sysclk = lrclk * dac_divs[j]; + freq_out = sysclk * sysclk_divs[i]; + + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { + if (!is_pll_freq_available(freq_in, freq_out)) + continue; + + diff = sysclk - bclk * bclk_divs[k] / 10; + if (diff == 0) { + *sysclk_idx = i; + *dac_idx = j; + *bclk_idx = k; + best_freq_out = freq_out; + break; + } + } + if (k != ARRAY_SIZE(bclk_divs)) + break; + } + if (j != ARRAY_SIZE(dac_divs)) + break; + } + + if (*bclk_idx != -1) + wm8960_set_pll(codec, freq_in, best_freq_out); + + return *bclk_idx; +} +static int wm8960_configure_clocking(struct snd_soc_codec *codec) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + int freq_out, freq_in; + u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); + int i, j, k; + int ret; + + if (!(iface1 & (1<<6))) { + dev_dbg(codec->dev, + "Codec is slave mode, no need to configure clock\n"); + return 0; + } + + if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { + dev_err(codec->dev, "No MCLK configured\n"); + return -EINVAL; + } + + freq_in = wm8960->freq_in; + /* + * If it's sysclk auto mode, check if the MCLK can provide sysclk or + * not. If MCLK can provide sysclk, using MCLK to provide sysclk + * directly. Otherwise, auto select a available pll out frequency + * and set PLL. + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { + /* disable the PLL and using MCLK to provide sysclk */ + wm8960_set_pll(codec, 0, 0); + freq_out = freq_in; + } else if (wm8960->sysclk) { + freq_out = wm8960->sysclk; + } else { + dev_err(codec->dev, "No SYSCLK configured\n"); + return -EINVAL; + } + + if (wm8960->clk_id != WM8960_SYSCLK_PLL) { + ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k); + if (ret >= 0) { + goto configure_clock; + } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; + } + } + + ret = wm8960_configure_pll(codec, freq_in, &i, &j, &k); + if (ret < 0) { + dev_err(codec->dev, "failed to configure clock via PLL\n"); + return -EINVAL; + } + +configure_clock: + /* configure sysclk clock */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); + + /* configure frame clock */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); + + /* configure bit clock */ + snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); + + return 0; +} + +static int wm8960_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int i; + + wm8960->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + wm8960->bclk *= 2; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + case 32: + /* right justify mode does not support 32 word length */ + if ((iface & 0x3) != 0) { + iface |= 0x000c; + break; + } + default: + dev_err(codec->dev, "unsupported width %d\n", + params_width(params)); + return -EINVAL; + } + + wm8960->lrclk = params_rate(params); + /* Update filters for the new rate */ + if (tx) { + wm8960_set_deemph(codec); + } else { + for (i = 0; i < ARRAY_SIZE(alc_rates); i++) + if (alc_rates[i].rate == params_rate(params)) + snd_soc_update_bits(codec, + WM8960_ADDCTL3, 0x7, + alc_rates[i].val); + } + + /* set iface */ + snd_soc_write(codec, WM8960_IFACE1, iface); + + wm8960->is_stream_in_use[tx] = true; + + if (!wm8960->is_stream_in_use[!tx]) + return wm8960_configure_clocking(codec); + + return 0; +} + +static int wm8960_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + wm8960->is_stream_in_use[tx] = false; + + return 0; +} + +static int wm8960_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) + snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); + else + snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); + return 0; +} + +static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 pm2 = snd_soc_read(codec, WM8960_POWER2); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + switch (snd_soc_codec_get_bias_level(codec)) { + case SND_SOC_BIAS_STANDBY: + if (!IS_ERR(wm8960->mclk)) { + ret = clk_prepare_enable(wm8960->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable MCLK: %d\n", + ret); + return ret; + } + } + + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + + /* Set VMID to 2x50k */ + snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); + break; + + case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(codec, 0, 0); + + if (!IS_ERR(wm8960->mclk)) + clk_disable_unprepare(wm8960->mclk); + break; + + default: + break; + } + + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + regcache_sync(wm8960->regmap); + + /* Enable anti-pop features */ + snd_soc_write(codec, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN | WM8960_BUFIOEN); + + /* Enable & ramp VMID at 2x50k */ + snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); + msleep(100); + + /* Enable VREF */ + snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, + WM8960_VREF); + + /* Disable anti-pop features */ + snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); + } + + /* Set VMID to 2x250k */ + snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); + break; + + case SND_SOC_BIAS_OFF: + /* Enable anti-pop features */ + snd_soc_write(codec, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN | WM8960_BUFIOEN); + + /* Disable VMID and VREF, let them discharge */ + snd_soc_write(codec, WM8960_POWER1, 0); + msleep(600); + break; + } + + return 0; +} + +static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 pm2 = snd_soc_read(codec, WM8960_POWER2); + int reg, ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + switch (snd_soc_codec_get_bias_level(codec)) { + case SND_SOC_BIAS_STANDBY: + /* Enable anti pop mode */ + snd_soc_update_bits(codec, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN); + + /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ + reg = 0; + if (wm8960->lout1 && wm8960->lout1->power) + reg |= WM8960_PWR2_LOUT1; + if (wm8960->rout1 && wm8960->rout1->power) + reg |= WM8960_PWR2_ROUT1; + if (wm8960->out3 && wm8960->out3->power) + reg |= WM8960_PWR2_OUT3; + snd_soc_update_bits(codec, WM8960_POWER2, + WM8960_PWR2_LOUT1 | + WM8960_PWR2_ROUT1 | + WM8960_PWR2_OUT3, reg); + + /* Enable VMID at 2*50k */ + snd_soc_update_bits(codec, WM8960_POWER1, + WM8960_VMID_MASK, 0x80); + + /* Ramp */ + msleep(100); + + /* Enable VREF */ + snd_soc_update_bits(codec, WM8960_POWER1, + WM8960_VREF, WM8960_VREF); + + msleep(100); + + if (!IS_ERR(wm8960->mclk)) { + ret = clk_prepare_enable(wm8960->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable MCLK: %d\n", + ret); + return ret; + } + } + + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + + break; + + case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(codec, 0, 0); + + if (!IS_ERR(wm8960->mclk)) + clk_disable_unprepare(wm8960->mclk); + + /* Enable anti-pop mode */ + snd_soc_update_bits(codec, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN); + + /* Disable VMID and VREF */ + snd_soc_update_bits(codec, WM8960_POWER1, + WM8960_VREF | WM8960_VMID_MASK, 0); + break; + + case SND_SOC_BIAS_OFF: + regcache_sync(wm8960->regmap); + break; + default: + break; + } + break; + + case SND_SOC_BIAS_STANDBY: + switch (snd_soc_codec_get_bias_level(codec)) { + case SND_SOC_BIAS_PREPARE: + /* Disable HP discharge */ + snd_soc_update_bits(codec, WM8960_APOP2, + WM8960_DISOP | WM8960_DRES_MASK, + 0); + + /* Disable anti-pop features */ + snd_soc_update_bits(codec, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN); + break; + + default: + break; + } + break; + + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + +/* PLL divisors */ +struct _pll_div { + u32 pre_div:1; + u32 n:4; + u32 k:24; +}; + +static bool is_pll_freq_available(unsigned int source, unsigned int target) +{ + unsigned int Ndiv; + + if (source == 0 || target == 0) + return false; + + /* Scale up target to PLL operating frequency */ + target *= 4; + Ndiv = target / source; + + if ((Ndiv < 6) || (Ndiv > 12)) + return false; + + return true; +} + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later + */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) + +static int pll_factors(unsigned int source, unsigned int target, + struct _pll_div *pll_div) +{ + unsigned long long Kpart; + unsigned int K, Ndiv, Nmod; + + pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); + + /* Scale up target to PLL operating frequency */ + target *= 4; + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->pre_div = 1; + Ndiv = target / source; + } else + pll_div->pre_div = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) { + pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); + return -EINVAL; + } + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; + + pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", + pll_div->n, pll_div->k, pll_div->pre_div); + + return 0; +} + +static int wm8960_set_pll(struct snd_soc_codec *codec, + unsigned int freq_in, unsigned int freq_out) +{ + u16 reg; + static struct _pll_div pll_div; + int ret; + + if (freq_in && freq_out) { + ret = pll_factors(freq_in, freq_out, &pll_div); + if (ret != 0) + return ret; + } + + /* Disable the PLL: even if we are changing the frequency the + * PLL needs to be disabled while we do so. + */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); + snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); + + if (!freq_in || !freq_out) + return 0; + + reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; + reg |= pll_div.pre_div << 4; + reg |= pll_div.n; + + if (pll_div.k) { + reg |= 0x20; + + snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); + snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); + snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); + } + snd_soc_write(codec, WM8960_PLL1, reg); + + /* Turn it on */ + snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); + msleep(250); + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); + + return 0; +} + +static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + wm8960->freq_in = freq_in; + + if (pll_id == WM8960_SYSCLK_AUTO) + return 0; + + if (is_pll_freq_available(freq_in, freq_out)) + return -EINVAL; + + return wm8960_set_pll(codec, freq_in, freq_out); +} + +static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + + switch (div_id) { + case WM8960_SYSCLKDIV: + reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; + snd_soc_write(codec, WM8960_CLOCK1, reg | div); + break; + case WM8960_DACDIV: + reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; + snd_soc_write(codec, WM8960_CLOCK1, reg | div); + break; + case WM8960_OPCLKDIV: + reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; + snd_soc_write(codec, WM8960_PLL1, reg | div); + break; + case WM8960_DCLKDIV: + reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; + snd_soc_write(codec, WM8960_CLOCK2, reg | div); + break; + case WM8960_TOCLKSEL: + reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; + snd_soc_write(codec, WM8960_ADDCTL1, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8960_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + return wm8960->set_bias_level(codec, level); +} + +static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case WM8960_SYSCLK_MCLK: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_MCLK); + break; + case WM8960_SYSCLK_PLL: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_PLL); + break; + case WM8960_SYSCLK_AUTO: + break; + default: + return -EINVAL; + } + + wm8960->sysclk = freq; + wm8960->clk_id = clk_id; + + return 0; +} + +#define RPMSG_RATES SNDRV_PCM_RATE_8000_48000 #define RPMSG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops rpmsg_wm8960_dai_ops = { + .hw_params = wm8960_hw_params, + .hw_free = wm8960_hw_free, + .digital_mute = wm8960_mute, + .set_fmt = wm8960_set_dai_fmt, + .set_clkdiv = wm8960_set_dai_clkdiv, + .set_pll = wm8960_set_dai_pll, + .set_sysclk = wm8960_set_dai_sysclk, +}; + static struct snd_soc_dai_driver rpmsg_wm8960_codec_dai = { .name = "rpmsg-wm8960-hifi", .playback = { .stream_name = "Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RPMSG_RATES, .formats = RPMSG_FORMATS, }, .capture = { .stream_name = "Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RPMSG_RATES, .formats = RPMSG_FORMATS, }, + .ops = &rpmsg_wm8960_dai_ops, + .symmetric_rates = 1, }; -static unsigned int rpmsg_wm8960_read(struct snd_soc_codec *codec, unsigned int reg) +static int rpmsg_wm8960_probe(struct snd_soc_codec *codec) { - struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_data *pdata = &wm8960->pdata; + + if (pdata->capless) + wm8960->set_bias_level = wm8960_set_bias_level_capless; + else + wm8960->set_bias_level = wm8960_set_bias_level_out3; + + snd_soc_add_codec_controls(codec, wm8960_snd_controls, + ARRAY_SIZE(wm8960_snd_controls)); + wm8960_add_widgets(codec); + + return 0; +} + +static int rpmsg_wm8960_read(void *context, unsigned int reg, unsigned int *val) +{ + struct rpmsg_wm8960_priv *wm8960 = context; + struct fsl_rpmsg_i2s *rpmsg_i2s = wm8960->rpmsg_i2s; struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[GET_CODEC_VALUE].send_msg; int err, reg_val; mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.audioindex = wm8960->audioindex; rpmsg->param.buffer_addr = reg; rpmsg->header.cmd = GET_CODEC_VALUE; err = i2s_info->send_message(&i2s_info->rpmsg[GET_CODEC_VALUE], i2s_info); reg_val = i2s_info->rpmsg[GET_CODEC_VALUE].recv_msg.param.reg_data; mutex_unlock(&i2s_info->i2c_lock); if (err) - return 0; + return -EIO; - return reg_val; + *val = reg_val; + return 0; } -static int rpmsg_wm8960_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) +static int rpmsg_wm8960_write(void *context, unsigned int reg, unsigned int val) { - struct fsl_rpmsg_i2s *rpmsg_i2s = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = context; + struct fsl_rpmsg_i2s *rpmsg_i2s = wm8960->rpmsg_i2s; struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[SET_CODEC_VALUE].send_msg; int err; mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.audioindex = wm8960->audioindex; rpmsg->param.buffer_addr = reg; rpmsg->param.buffer_size = val; rpmsg->header.cmd = SET_CODEC_VALUE; err = i2s_info->send_message(&i2s_info->rpmsg[SET_CODEC_VALUE], i2s_info); mutex_unlock(&i2s_info->i2c_lock); if (err) - return err; - - return 0; -} - -static int rpmsg_wm8960_probe(struct snd_soc_codec *codec) -{ - snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); - snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); - - snd_soc_update_bits(codec, WM8960_ADDCTL1, 0xC, 0x4); + return -EIO; return 0; } static struct snd_soc_codec_driver rpmsg_wm8960_codec = { .probe = rpmsg_wm8960_probe, - .read = rpmsg_wm8960_read, - .write = rpmsg_wm8960_write, - .component_driver = { - .controls = rpmsg_wm8960_ctrls, - .num_controls = ARRAY_SIZE(rpmsg_wm8960_ctrls), - }, + .set_bias_level = wm8960_set_bias_level, + .suspend_bias_off = true, +}; + +static const struct regmap_config rpmsg_wm8960_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8960_PLL4, + + .reg_defaults = wm8960_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = wm8960_volatile, + .reg_read = rpmsg_wm8960_read, + .reg_write = rpmsg_wm8960_write, +}; + +#ifdef CONFIG_PM +static int wm8960_runtime_resume(struct device *dev) +{ + struct rpmsg_wm8960_priv *wm8960 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(wm8960->mclk); + if (ret) { + dev_err(dev, "Failed to enable MCLK: %d\n", ret); + return ret; + } + return 0; +} + +static int wm8960_runtime_suspend(struct device *dev) +{ + struct rpmsg_wm8960_priv *wm8960 = dev_get_drvdata(dev); + + clk_disable_unprepare(wm8960->mclk); + + return 0; +} +#endif + +static const struct dev_pm_ops wm8960_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(wm8960_runtime_suspend, wm8960_runtime_resume, NULL) }; static int rpmsg_wm8960_codec_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(pdev->dev.parent); + struct fsl_rpmsg_codec *pdata = pdev->dev.platform_data; + struct rpmsg_wm8960_priv *wm8960; int ret; + int repeat_reset = 10; - ret = snd_soc_register_codec(dev, - &rpmsg_wm8960_codec, - &rpmsg_wm8960_codec_dai, - 1); - if (ret) { - dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n", - __func__, ret); - return ret; + wm8960 = devm_kzalloc(&pdev->dev, sizeof(struct rpmsg_wm8960_priv), + GFP_KERNEL); + if (wm8960 == NULL) + return -ENOMEM; + + wm8960->rpmsg_i2s = rpmsg_i2s; + + wm8960->mclk = devm_clk_get(pdev->dev.parent, "mclk"); + if (IS_ERR(wm8960->mclk)) { + if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + wm8960->mclk = NULL; } - dev_set_drvdata(dev, rpmsg_i2s); + dev_set_drvdata(&pdev->dev, wm8960); - return 0; + wm8960->regmap = devm_regmap_init(&pdev->dev, NULL, wm8960, &rpmsg_wm8960_regmap); + if (IS_ERR(wm8960->regmap)) + return PTR_ERR(wm8960->regmap); + + if (pdata) { + wm8960->pdata.shared_lrclk = pdata->shared_lrclk; + wm8960->pdata.capless = pdata->capless; + wm8960->audioindex = pdata->audioindex; + } + + if (wm8960->mclk) { + do { + ret = wm8960_reset(wm8960->regmap); + repeat_reset--; + } while (repeat_reset > 0 && ret != 0); + + if (ret != 0) { + dev_err(&pdev->dev, "Failed to issue reset\n"); + return ret; + } + + if (wm8960->pdata.shared_lrclk) { + ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, + 0x4, 0x4); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to enable LRCM: %d\n", + ret); + return ret; + } + } + } + + /* Latch the update bits */ + regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); + + pm_runtime_enable(&pdev->dev); + + if (!wm8960->mclk) + rpmsg_wm8960_codec_dai.ops = NULL; + + ret = snd_soc_register_codec(&pdev->dev, + &rpmsg_wm8960_codec, &rpmsg_wm8960_codec_dai, 1); + + return ret; } static int rpmsg_wm8960_codec_remove(struct platform_device *pdev) @@ -204,7 +1529,8 @@ static int rpmsg_wm8960_codec_remove(struct platform_device *pdev) static struct platform_driver rpmsg_wm8960_codec_driver = { .driver = { - .name = RPMSG_CODEC_DRV_NAME, + .name = RPMSG_CODEC_DRV_NAME_WM8960, + .pm = &wm8960_pm, }, .probe = rpmsg_wm8960_codec_probe, .remove = rpmsg_wm8960_codec_remove, From dba422ef60db96dc15807ccae525b4bc05e13aec Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 15:03:32 +0800 Subject: [PATCH 29/59] MLK-19168-2: ASoC: rpmsg_cs42xx8: support cs42xx8 over rpmsg The difference of rpmsg_cs42xx8 and cs42xx8 driver is previous one will send command through rpmsg, others are same. Signed-off-by: Shengjiu Wang (cherry picked from commit dda40ff395bb1d86fc75920bb2bc2ea99099ed4b) --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 3 + sound/soc/codecs/rpmsg_cs42xx8.c | 745 +++++++++++++++++++++++++++++++ sound/soc/codecs/rpmsg_cs42xx8.h | 232 ++++++++++ 4 files changed, 984 insertions(+) create mode 100644 sound/soc/codecs/rpmsg_cs42xx8.c create mode 100644 sound/soc/codecs/rpmsg_cs42xx8.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9854cfd50d63..11229eaaa87a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -258,6 +258,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_RPMSG_WM8960 + select SND_SOC_RPMSG_CS42XX8 help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -1440,6 +1441,9 @@ config SND_SOC_ZX_AUD96P22 config SND_SOC_RPMSG_WM8960 tristate +config SND_SOC_RPMSG_CS42XX8 + tristate + # Amp config SND_SOC_LM4857 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 72594796dedf..1a609c141714 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -275,6 +275,8 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o snd-soc-rpmsg-wm8960-objs := rpmsg_wm8960.o +snd-soc-rpmsg-cs42xx8-objs := rpmsg_cs42xx8.o + # Amp snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o @@ -559,6 +561,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o obj-$(CONFIG_SND_SOC_RPMSG_WM8960) += snd-soc-rpmsg-wm8960.o +obj-$(CONFIG_SND_SOC_RPMSG_CS42XX8) += snd-soc-rpmsg-cs42xx8.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rpmsg_cs42xx8.c b/sound/soc/codecs/rpmsg_cs42xx8.c new file mode 100644 index 000000000000..dfc520b925d5 --- /dev/null +++ b/sound/soc/codecs/rpmsg_cs42xx8.c @@ -0,0 +1,745 @@ +/* + * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpmsg_cs42xx8.h" +#include "../fsl/fsl_rpmsg_i2s.h" + +#define CS42XX8_NUM_SUPPLIES 4 +static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = { + "VA", + "VD", + "VLS", + "VLC", +}; + +#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* codec private data */ +struct rpmsg_cs42xx8_priv { + struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES]; + struct cs42xx8_driver_data *drvdata; + struct regmap *regmap; + struct clk *clk; + + bool slave_mode; + unsigned long sysclk; + u32 tx_channels; + int rate[2]; + int reset_gpio; + int audioindex; + struct fsl_rpmsg_i2s *rpmsg_i2s; +}; + +/* -127.5dB to 0dB with step of 0.5dB */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +/* -64dB to 24dB with step of 0.5dB */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0); + +static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" }; +static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross", + "Soft Ramp", "Soft Ramp on Zero Cross" }; + +static const struct soc_enum adc1_single_enum = + SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single); +static const struct soc_enum adc2_single_enum = + SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single); +static const struct soc_enum adc3_single_enum = + SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single); +static const struct soc_enum dac_szc_enum = + SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc); +static const struct soc_enum adc_szc_enum = + SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc); + +static const struct snd_kcontrol_new cs42xx8_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1, + CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3, + CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5, + CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7, + CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1, + CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv), + SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3, + CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv), + SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0), + SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0), + SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0), + SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0), + SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0), + SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0), + SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1), + SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0), + SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum), + SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum), + SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0), + SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum), + SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0), + SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0), + SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0), + SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum), +}; + +static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5, + CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv), + SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0), + SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum), +}; + +static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1), + SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1), + + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + SND_SOC_DAPM_OUTPUT("AOUT3L"), + SND_SOC_DAPM_OUTPUT("AOUT3R"), + SND_SOC_DAPM_OUTPUT("AOUT4L"), + SND_SOC_DAPM_OUTPUT("AOUT4R"), + + SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1), + SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1), + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + +}; + +static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = { + SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1), + + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R"), +}; + +static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = { + /* Playback */ + { "AOUT1L", NULL, "DAC1" }, + { "AOUT1R", NULL, "DAC1" }, + + { "AOUT2L", NULL, "DAC2" }, + { "AOUT2R", NULL, "DAC2" }, + + { "AOUT3L", NULL, "DAC3" }, + { "AOUT3R", NULL, "DAC3" }, + + { "AOUT4L", NULL, "DAC4" }, + { "AOUT4R", NULL, "DAC4" }, + + /* Capture */ + { "ADC1", NULL, "AIN1L" }, + { "ADC1", NULL, "AIN1R" }, + + { "ADC2", NULL, "AIN2L" }, + { "ADC2", NULL, "AIN2R" }, +}; + +static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = { + /* Capture */ + { "ADC3", NULL, "AIN3L" }, + { "ADC3", NULL, "AIN3R" }, +}; + +struct cs42xx8_ratios { + unsigned int mfreq; + unsigned int min_mclk; + unsigned int max_mclk; + unsigned int ratio[3]; +}; + +static const struct cs42xx8_ratios cs42xx8_ratios[] = { + { 0, 1029000, 12800000, {256, 128, 64} }, + { 2, 1536000, 19200000, {384, 192, 96} }, + { 4, 2048000, 25600000, {512, 256, 128} }, + { 6, 3072000, 38400000, {768, 384, 192} }, + { 8, 4096000, 51200000, {1024, 512, 256} }, +}; + +static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + + cs42xx8->sysclk = freq; + + return 0; +} + +static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + u32 val; + + /* Set DAI format */ + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ; + break; + case SND_SOC_DAIFMT_I2S: + val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM; + break; + default: + dev_err(codec->dev, "unsupported dai format\n"); + return -EINVAL; + } + + regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF, + CS42XX8_INTF_DAC_DIF_MASK | + CS42XX8_INTF_ADC_DIF_MASK, val); + + /* Set master/slave audio interface */ + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs42xx8->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs42xx8->slave_mode = false; + break; + default: + dev_err(codec->dev, "unsupported master/slave mode\n"); + return -EINVAL; + } + + return 0; +} + +static int cs42xx8_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 rate = params_rate(params); + u32 ratio_tx, ratio_rx; + u32 rate_tx, rate_rx; + u32 fm_tx, fm_rx; + u32 i, fm, val, mask; + + if (tx) + cs42xx8->tx_channels = params_channels(params); + + rate_tx = tx ? rate : cs42xx8->rate[0]; + rate_rx = tx ? cs42xx8->rate[1] : rate; + + ratio_tx = rate_tx > 0 ? cs42xx8->sysclk / rate_tx : 0; + ratio_rx = rate_rx > 0 ? cs42xx8->sysclk / rate_rx : 0; + + if (cs42xx8->slave_mode) { + fm_rx = CS42XX8_FM_AUTO; + fm_tx = CS42XX8_FM_AUTO; + } else { + if (rate_tx < 50000) + fm_tx = CS42XX8_FM_SINGLE; + else if (rate_tx > 50000 && rate_tx < 100000) + fm_tx = CS42XX8_FM_DOUBLE; + else if (rate_tx > 100000 && rate_tx < 200000) + fm_tx = CS42XX8_FM_QUAD; + else { + dev_err(codec->dev, "unsupported sample rate or rate combine\n"); + return -EINVAL; + } + + if (rate_rx < 50000) + fm_rx = CS42XX8_FM_SINGLE; + else if (rate_rx > 50000 && rate_rx < 100000) + fm_rx = CS42XX8_FM_DOUBLE; + else if (rate_rx > 100000 && rate_rx < 200000) + fm_rx = CS42XX8_FM_QUAD; + else { + dev_err(codec->dev, "unsupported sample rate or rate combine\n"); + return -EINVAL; + } + } + + fm = tx ? fm_tx : fm_rx; + + if (fm == CS42XX8_FM_AUTO) { + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { + if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_tx || + cs42xx8_ratios[i].ratio[1] == ratio_tx || + cs42xx8_ratios[i].ratio[2] == ratio_tx) : true) && + (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_rx || + cs42xx8_ratios[i].ratio[1] == ratio_rx || + cs42xx8_ratios[i].ratio[2] == ratio_rx) : true) && + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk) + break; + } + } else { + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { + if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[fm_tx] == ratio_tx) : true) && + (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[fm_rx] == ratio_rx) : true) && + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk) + break; + } + } + + if (i == ARRAY_SIZE(cs42xx8_ratios)) { + dev_err(codec->dev, "unsupported sysclk ratio\n"); + return -EINVAL; + } + + cs42xx8->rate[substream->stream] = rate; + + mask = CS42XX8_FUNCMOD_MFREQ_MASK; + val = cs42xx8_ratios[i].mfreq; + + regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, + CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, + CS42XX8_FUNCMOD_xC_FM(tx, fm) | val); + + return 0; +} + +static int cs42xx8_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + cs42xx8->rate[substream->stream] = 0; + + regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, + CS42XX8_FUNCMOD_xC_FM_MASK(tx), + CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO)); + return 0; +} + +static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + u8 dac_unmute = cs42xx8->tx_channels ? + ~((0x1 << cs42xx8->tx_channels) - 1) : 0; + + regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, + mute ? CS42XX8_DACMUTE_ALL : dac_unmute); + + return 0; +} + +static const struct snd_soc_dai_ops cs42xx8_dai_ops = { + .set_fmt = cs42xx8_set_dai_fmt, + .set_sysclk = cs42xx8_set_dai_sysclk, + .hw_params = cs42xx8_hw_params, + .hw_free = cs42xx8_hw_free, + .digital_mute = cs42xx8_digital_mute, +}; + +static struct snd_soc_dai_driver cs42xx8_dai = { + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = CS42XX8_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = CS42XX8_FORMATS, + }, + .ops = &cs42xx8_dai_ops, +}; + +static const struct reg_default cs42xx8_reg[] = { + { 0x02, 0x00 }, /* Power Control */ + { 0x03, 0xF0 }, /* Functional Mode */ + { 0x04, 0x46 }, /* Interface Formats */ + { 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */ + { 0x06, 0x10 }, /* Transition Control */ + { 0x07, 0x00 }, /* DAC Channel Mute */ + { 0x08, 0x00 }, /* Volume Control AOUT1 */ + { 0x09, 0x00 }, /* Volume Control AOUT2 */ + { 0x0a, 0x00 }, /* Volume Control AOUT3 */ + { 0x0b, 0x00 }, /* Volume Control AOUT4 */ + { 0x0c, 0x00 }, /* Volume Control AOUT5 */ + { 0x0d, 0x00 }, /* Volume Control AOUT6 */ + { 0x0e, 0x00 }, /* Volume Control AOUT7 */ + { 0x0f, 0x00 }, /* Volume Control AOUT8 */ + { 0x10, 0x00 }, /* DAC Channel Invert */ + { 0x11, 0x00 }, /* Volume Control AIN1 */ + { 0x12, 0x00 }, /* Volume Control AIN2 */ + { 0x13, 0x00 }, /* Volume Control AIN3 */ + { 0x14, 0x00 }, /* Volume Control AIN4 */ + { 0x15, 0x00 }, /* Volume Control AIN5 */ + { 0x16, 0x00 }, /* Volume Control AIN6 */ + { 0x17, 0x00 }, /* ADC Channel Invert */ + { 0x18, 0x00 }, /* Status Control */ + { 0x1a, 0x00 }, /* Status Mask */ + { 0x1b, 0x00 }, /* MUTEC Pin Control */ +}; + +static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42XX8_STATUS: + return true; + default: + return false; + } +} + +static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42XX8_CHIPID: + case CS42XX8_STATUS: + return false; + default: + return true; + } +} + +static int rpmsg_cs42xx8_read(void *context, unsigned int reg, unsigned int *val) +{ + struct rpmsg_cs42xx8_priv *cs42xx8 = context; + struct fsl_rpmsg_i2s *rpmsg_i2s = cs42xx8->rpmsg_i2s; + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[GET_CODEC_VALUE].send_msg; + int err, reg_val; + + mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.audioindex = cs42xx8->audioindex; + rpmsg->param.buffer_addr = reg; + rpmsg->header.cmd = GET_CODEC_VALUE; + err = i2s_info->send_message(&i2s_info->rpmsg[GET_CODEC_VALUE], i2s_info); + reg_val = i2s_info->rpmsg[GET_CODEC_VALUE].recv_msg.param.reg_data; + mutex_unlock(&i2s_info->i2c_lock); + if (err) + return -EIO; + + *val = reg_val; + return 0; +} + +static int rpmsg_cs42xx8_write(void *context, unsigned int reg, unsigned int val) +{ + struct rpmsg_cs42xx8_priv *cs42xx8 = context; + struct fsl_rpmsg_i2s *rpmsg_i2s = cs42xx8->rpmsg_i2s; + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[SET_CODEC_VALUE].send_msg; + int err; + + mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.audioindex = cs42xx8->audioindex; + rpmsg->param.buffer_addr = reg; + rpmsg->param.buffer_size = val; + rpmsg->header.cmd = SET_CODEC_VALUE; + err = i2s_info->send_message(&i2s_info->rpmsg[SET_CODEC_VALUE], i2s_info); + mutex_unlock(&i2s_info->i2c_lock); + if (err) + return -EIO; + + return 0; +} + +static struct regmap_config rpmsg_cs42xx8_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42XX8_LASTREG, + .reg_defaults = cs42xx8_reg, + .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg), + .volatile_reg = cs42xx8_volatile_register, + .writeable_reg = cs42xx8_writeable_register, + .cache_type = REGCACHE_RBTREE, + + .reg_read = rpmsg_cs42xx8_read, + .reg_write = rpmsg_cs42xx8_write, +}; + +static int cs42xx8_codec_probe(struct snd_soc_codec *codec) +{ + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + switch (cs42xx8->drvdata->num_adcs) { + case 3: + snd_soc_add_codec_controls(codec, cs42xx8_adc3_snd_controls, + ARRAY_SIZE(cs42xx8_adc3_snd_controls)); + snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets, + ARRAY_SIZE(cs42xx8_adc3_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes, + ARRAY_SIZE(cs42xx8_adc3_dapm_routes)); + break; + default: + break; + } + + /* Mute all DAC channels */ + regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL); + regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL, + CS42XX8_PWRCTL_PDN_MASK, 0); + return 0; +} + +static const struct snd_soc_codec_driver cs42xx8_driver = { + .probe = cs42xx8_codec_probe, + .idle_bias_off = true, + + .component_driver = { + .controls = cs42xx8_snd_controls, + .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), + .dapm_widgets = cs42xx8_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), + .dapm_routes = cs42xx8_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), + }, +}; + +static int rpmsg_cs42xx8_codec_probe(struct platform_device *pdev) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(pdev->dev.parent); + struct fsl_rpmsg_codec *pdata = pdev->dev.platform_data; + struct rpmsg_cs42xx8_priv *cs42xx8; + struct device *dev = &pdev->dev; + int ret, val, i; + + cs42xx8 = devm_kzalloc(&pdev->dev, sizeof(*cs42xx8), GFP_KERNEL); + if (cs42xx8 == NULL) + return -ENOMEM; + + cs42xx8->regmap = devm_regmap_init(&pdev->dev, NULL, + cs42xx8, &rpmsg_cs42xx8_regmap_config); + if (IS_ERR(cs42xx8->regmap)) + return PTR_ERR(cs42xx8->regmap); + + dev_set_drvdata(&pdev->dev, cs42xx8); + + cs42xx8->drvdata = devm_kzalloc(&pdev->dev, + sizeof(struct cs42xx8_driver_data), GFP_KERNEL); + if (!cs42xx8->drvdata) + return -ENOMEM; + + cs42xx8->rpmsg_i2s = rpmsg_i2s; + + memcpy(cs42xx8->drvdata->name, pdata->name, 32); + cs42xx8->drvdata->num_adcs = pdata->num_adcs; + cs42xx8->audioindex = pdata->audioindex; + cs42xx8->reset_gpio = of_get_named_gpio(pdev->dev.parent->of_node, + "reset-gpio", 0); + if (gpio_is_valid(cs42xx8->reset_gpio)) { + ret = devm_gpio_request_one(dev, cs42xx8->reset_gpio, + GPIOF_OUT_INIT_LOW, "cs42xx8 reset"); + if (ret) { + dev_err(dev, "unable to get reset gpio\n"); + return ret; + } + gpio_set_value_cansleep(cs42xx8->reset_gpio, 1); + } + + cs42xx8->clk = devm_clk_get(pdev->dev.parent, "mclk"); + if (IS_ERR(cs42xx8->clk)) { + dev_err(dev, "failed to get the clock: %ld\n", + PTR_ERR(cs42xx8->clk)); + return -EINVAL; + } + + cs42xx8->sysclk = clk_get_rate(cs42xx8->clk); + + for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++) + cs42xx8->supplies[i].supply = cs42xx8_supply_names[i]; + + ret = devm_regulator_bulk_get(pdev->dev.parent, + ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies); + if (ret) { + dev_err(dev, "failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + /* Make sure hardware reset done */ + usleep_range(5000, 10000); + + /* + * We haven't marked the chip revision as volatile due to + * sharing a register with the right input volume; explicitly + * bypass the cache to read it. + */ + regcache_cache_bypass(cs42xx8->regmap, true); + + /* Validate the chip ID */ + ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val); + if (ret < 0) { + dev_err(dev, "failed to get device ID, ret = %d", ret); + goto err_enable; + } + + /* The top four bits of the chip ID should be 0000 */ + if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) { + dev_err(dev, "unmatched chip ID: %d\n", + (val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4); + ret = -EINVAL; + goto err_enable; + } + + dev_info(dev, "found device, revision %X\n", + val & CS42XX8_CHIPID_REV_ID_MASK); + + regcache_cache_bypass(cs42xx8->regmap, false); + + cs42xx8_dai.name = cs42xx8->drvdata->name; + + /* Each adc supports stereo input */ + cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2; + + pm_runtime_enable(dev); + pm_request_idle(dev); + + ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1); + if (ret) { + dev_err(dev, "failed to register codec:%d\n", ret); + goto err_enable; + } + + regcache_cache_only(cs42xx8->regmap, true); + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + + return ret; +} + +#ifdef CONFIG_PM +static int cs42xx8_runtime_resume(struct device *dev) +{ + struct rpmsg_cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(cs42xx8->clk); + if (ret) { + dev_err(dev, "failed to enable mclk: %d\n", ret); + return ret; + } + + if (gpio_is_valid(cs42xx8->reset_gpio)) { + gpio_set_value_cansleep(cs42xx8->reset_gpio, 0); + gpio_set_value_cansleep(cs42xx8->reset_gpio, 1); + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + goto err_clk; + } + + regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL, + CS42XX8_PWRCTL_PDN_MASK, 1); + /* Make sure hardware reset done */ + usleep_range(5000, 10000); + + regmap_update_bits(cs42xx8->regmap, CS42XX8_PWRCTL, + CS42XX8_PWRCTL_PDN_MASK, 0); + + regcache_cache_only(cs42xx8->regmap, false); + + ret = regcache_sync(cs42xx8->regmap); + if (ret) { + dev_err(dev, "failed to sync regmap: %d\n", ret); + goto err_bulk; + } + + return 0; + +err_bulk: + regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); +err_clk: + clk_disable_unprepare(cs42xx8->clk); + + return ret; +} + +static int cs42xx8_runtime_suspend(struct device *dev) +{ + struct rpmsg_cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); + + regcache_cache_only(cs42xx8->regmap, true); + + regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + + clk_disable_unprepare(cs42xx8->clk); + + return 0; +} +#endif + +const struct dev_pm_ops rpmsg_cs42xx8_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) +}; + +static int rpmsg_cs42xx8_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver rpmsg_cs42xx8_codec_driver = { + .driver = { + .name = RPMSG_CODEC_DRV_NAME_CS42888, + .pm = &rpmsg_cs42xx8_pm, + }, + .probe = rpmsg_cs42xx8_codec_probe, + .remove = rpmsg_cs42xx8_codec_remove, +}; + +module_platform_driver(rpmsg_cs42xx8_codec_driver); + +MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rpmsg_cs42xx8.h b/sound/soc/codecs/rpmsg_cs42xx8.h new file mode 100644 index 000000000000..682295272f49 --- /dev/null +++ b/sound/soc/codecs/rpmsg_cs42xx8.h @@ -0,0 +1,232 @@ +/* + * cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _RPMSG_CS42XX8_H +#define _RPMSG_CS42XX8_H + +struct cs42xx8_driver_data { + char name[32]; + int num_adcs; +}; + +/* CS42888 register map */ +#define CS42XX8_CHIPID 0x01 /* Chip ID */ +#define CS42XX8_PWRCTL 0x02 /* Power Control */ +#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */ +#define CS42XX8_INTF 0x04 /* Interface Formats */ +#define CS42XX8_ADCCTL 0x05 /* ADC Control */ +#define CS42XX8_TXCTL 0x06 /* Transition Control */ +#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */ +#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */ +#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */ +#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */ +#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */ +#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */ +#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */ +#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */ +#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */ +#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */ +#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */ +#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */ +#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */ +#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */ +#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */ +#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */ +#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */ +#define CS42XX8_STATUSCTL 0x18 /* Status Control */ +#define CS42XX8_STATUS 0x19 /* Status */ +#define CS42XX8_STATUSM 0x1A /* Status Mask */ +#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */ + +#define CS42XX8_FIRSTREG CS42XX8_CHIPID +#define CS42XX8_LASTREG CS42XX8_MUTEC +#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1) +#define CS42XX8_I2C_INCR 0x80 + +/* Chip I.D. and Revision Register (Address 01h) */ +#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0 +#define CS42XX8_CHIPID_REV_ID_MASK 0x0F + +/* Power Control (Address 02h) */ +#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7 +#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6 +#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5 +#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4 +#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3 +#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2 +#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1 +#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_SHIFT 0 +#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT) +#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT) + +/* Functional Mode (Address 03h) */ +#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6 +#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2 +#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT) +#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT) +#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4 +#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2 +#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT) +#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT) +#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK) +#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v)) +#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1 +#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3 +#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT) +#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) + +#define CS42XX8_FM_SINGLE 0 +#define CS42XX8_FM_DOUBLE 1 +#define CS42XX8_FM_QUAD 2 +#define CS42XX8_FM_AUTO 3 + +/* Interface Formats (Address 04h) */ +#define CS42XX8_INTF_FREEZE_SHIFT 7 +#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT) +#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT) +#define CS42XX8_INTF_AUX_DIF_SHIFT 6 +#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT) +#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_SHIFT 3 +#define CS42XX8_INTF_DAC_DIF_WIDTH 3 +#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_SHIFT 0 +#define CS42XX8_INTF_ADC_DIF_WIDTH 3 +#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT) + +/* ADC Control & DAC De-Emphasis (Address 05h) */ +#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7 +#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT) +#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT) +#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5 +#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT) +#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT) +#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4 +#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3 +#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2 +#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1 +#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT) +#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT) +#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0 +#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT) +#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT) + +/* Transition Control (Address 06h) */ +#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7 +#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5 +#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2 +#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_AMUTE_SHIFT 4 +#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT) +#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT) +#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3 +#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT) +#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT) +#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2 +#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0 +#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT) + +/* DAC Channel Mute (Address 07h) */ +#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n) +#define CS42XX8_DACMUTE_ALL 0xff + +/* Status Control (Address 18h)*/ +#define CS42XX8_STATUSCTL_INI_SHIFT 2 +#define CS42XX8_STATUSCTL_INI_WIDTH 2 +#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT) +#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT) +#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT) +#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT) + +/* Status (Address 19h)*/ +#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4 +#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT) +#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3 +#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT) +#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2 +#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT) +#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1 +#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT) +#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0 +#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT) + +/* Status Mask (Address 1Ah) */ +#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4 +#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT) +#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3 +#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT) +#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2 +#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT) +#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1 +#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT) +#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0 +#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT) + +/* MUTEC Pin Control (Address 1Bh) */ +#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1 +#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT) +#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT) +#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT) +#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0 +#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT) +#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT) +#endif /* _CS42XX8_H */ From 997f8ece23d921bc954c33873618081b54d635b1 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 15:06:58 +0800 Subject: [PATCH 30/59] MLK-19168-3: ASoC: imx-pcm-rpmsg: register rpmsg wm8960 and cs42xx8 codec register rpmsg wm8960 and cs42xx8 codec Signed-off-by: Shengjiu Wang (cherry picked from commit c49f8d20c6fd4479ad45d76290bb5c57d4800d9e) --- sound/soc/fsl/imx-pcm-rpmsg.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 4caf8326b738..d84723498641 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -704,6 +704,7 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) { struct platform_device *codec_pdev; struct fsl_rpmsg_i2s *rpmsg_i2s = NULL; + struct fsl_rpmsg_codec rpmsg_codec[2]; int ret; if (!i2s_info_g) @@ -718,12 +719,15 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) rpmsg_i2s = container_of(i2s_info_g, struct fsl_rpmsg_i2s, i2s_info); - if (rpmsg_i2s->codec) { + if (rpmsg_i2s->codec_wm8960) { + rpmsg_codec[0].audioindex = rpmsg_i2s->codec_wm8960 >> 16; + rpmsg_codec[0].shared_lrclk = true; + rpmsg_codec[0].capless = false; codec_pdev = platform_device_register_data( &rpmsg_i2s->pdev->dev, - RPMSG_CODEC_DRV_NAME, + RPMSG_CODEC_DRV_NAME_WM8960, PLATFORM_DEVID_NONE, - NULL, 0); + &rpmsg_codec[0], sizeof(struct fsl_rpmsg_codec)); if (IS_ERR(codec_pdev)) { dev_err(&rpdev->dev, "failed to register rpmsg audio codec\n"); @@ -732,6 +736,23 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) } } + if (rpmsg_i2s->codec_cs42888) { + rpmsg_codec[1].audioindex = rpmsg_i2s->codec_cs42888 >> 16; + strcpy(rpmsg_codec[1].name, "cs42888"); + rpmsg_codec[1].num_adcs = 2; + + codec_pdev = platform_device_register_data( + &rpmsg_i2s->pdev->dev, + RPMSG_CODEC_DRV_NAME_CS42888, + PLATFORM_DEVID_NONE, + &rpmsg_codec[1], sizeof(struct fsl_rpmsg_codec)); + if (IS_ERR(codec_pdev)) { + dev_err(&rpdev->dev, + "failed to register rpmsg audio codec\n"); + ret = PTR_ERR(codec_pdev); + return ret; + } + } return 0; } From 08cf70aa239c9741b90a184b32bc9adac7e5099a Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 15:07:43 +0800 Subject: [PATCH 31/59] MLK-19168-4: ASoC: fsl_rpmsg_i2s: support more codec support more codecs, codec is specified by compatible string Signed-off-by: Shengjiu Wang (cherry picked from commit 7c92a75fcf83ec0aa3fe6773e4cb5f5e88a1ff09) --- .../bindings/sound/fsl,rpmsg-i2s.txt | 2 +- sound/soc/fsl/fsl_rpmsg_i2s.c | 12 ++++++++++-- sound/soc/fsl/fsl_rpmsg_i2s.h | 19 +++++++++++++++++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt index fd62b0d444a8..5f8c6cbbe5da 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt @@ -7,7 +7,7 @@ serial interfaces with frame synchronization such as I2S. Required properties: - compatible : Compatible list, contains "fsl,imx7ulp-rpmsg-i2s". - "fsl,imx8mq-rpmsg-i2s" + "fsl,imx8mq-rpmsg-i2s", "fsl,imx8qxp-rpmsg-i2s" - fsl,audioindex : This is an index indicating the audio device index in the M4 side. diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 202906cd3c98..b7ace88f2d93 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -107,6 +107,7 @@ static const struct snd_soc_component_driver fsl_component = { static const struct of_device_id fsl_rpmsg_i2s_ids[] = { { .compatible = "fsl,imx7ulp-rpmsg-i2s"}, { .compatible = "fsl,imx8mq-rpmsg-i2s"}, + { .compatible = "fsl,imx8qxp-rpmsg-i2s"}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_i2s_ids); @@ -170,7 +171,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx7ulp-rpmsg-i2s")) { - rpmsg_i2s->codec = 1; + rpmsg_i2s->codec_wm8960 = 1; rpmsg_i2s->version = 1; rpmsg_i2s->rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | @@ -182,9 +183,16 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats; } + if (of_device_is_compatible(pdev->dev.of_node, + "fsl,imx8qxp-rpmsg-i2s")) { + rpmsg_i2s->codec_wm8960 = 1 + (1 << 16); + rpmsg_i2s->version = 1; + rpmsg_i2s->codec_cs42888 = 1 + (2 << 16); + } + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx8mq-rpmsg-i2s")) { - rpmsg_i2s->codec = 0; + rpmsg_i2s->codec_wm8960 = 0; rpmsg_i2s->version = 2; rpmsg_i2s->rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index 9c5105c0ecf6..e52c861c4d43 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -410,7 +410,8 @@ struct fsl_rpmsg_i2s { struct platform_device *pdev; struct i2s_info i2s_info; struct pm_qos_request pm_qos_req; - int codec; + int codec_wm8960; + int codec_cs42888; int force_lpa; int version; int rates; @@ -418,6 +419,20 @@ struct fsl_rpmsg_i2s { int enable_lpa; }; -#define RPMSG_CODEC_DRV_NAME "rpmsg-audio-codec" +#define RPMSG_CODEC_DRV_NAME_WM8960 "rpmsg-audio-codec-wm8960" +#define RPMSG_CODEC_DRV_NAME_CS42888 "rpmsg-audio-codec-cs42888" + +struct fsl_rpmsg_codec { + int audioindex; + + /*property for wm8960*/ + bool capless; + bool shared_lrclk; + + /*property for cs42xx8*/ + + char name[32]; + int num_adcs; +}; #endif /* __FSL_RPMSG_I2S_H */ From 6414b94c423ffe997019db58fd3d80102e885626 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 16:00:17 +0800 Subject: [PATCH 32/59] MLK-19168-6: ASoC: imx-rpmsg: update the rpmsg codec name update the rpmsg codec name Signed-off-by: Shengjiu Wang (cherry picked from commit 6130b919df97b127fafe319f0c2ff601ebc6bed2) --- sound/soc/fsl/imx-rpmsg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index a5bb68d7afb4..7648e88bbced 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -61,9 +61,9 @@ static int imx_rpmsg_probe(struct platform_device *pdev) data->dai[0].name = "rpmsg hifi"; data->dai[0].stream_name = "rpmsg hifi"; - if (rpmsg_i2s->codec) { + if (rpmsg_i2s->codec_wm8960) { data->dai[0].codec_dai_name = "rpmsg-wm8960-hifi"; - data->dai[0].codec_name = "rpmsg-audio-codec"; + data->dai[0].codec_name = "rpmsg-audio-codec-wm8960"; } else { data->dai[0].codec_dai_name = "snd-soc-dummy-dai"; data->dai[0].codec_name = "snd-soc-dummy"; From 713549b367b590ea304ddc0925bf6ff7fa7fa27c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 13 Sep 2018 13:07:47 +0800 Subject: [PATCH 33/59] MLK-19565-1: ASoC: fsl_rpmsg_i2s: support rpmsg on imx8qm On imx8qm mek, the cs42888 is connected with i2c in cm41 domain, but wm8960 is connected with i2c1, which is not in m4 domain. So we only need to eable rpmsg for cs42888. Signed-off-by: Shengjiu Wang (cherry picked from commit 9d2368aef40e4d107e4deee1a2c7e191c1afe644) --- Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt | 1 + sound/soc/fsl/fsl_rpmsg_i2s.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt index 5f8c6cbbe5da..27de48eb2519 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg-i2s.txt @@ -8,6 +8,7 @@ Required properties: - compatible : Compatible list, contains "fsl,imx7ulp-rpmsg-i2s". "fsl,imx8mq-rpmsg-i2s", "fsl,imx8qxp-rpmsg-i2s" + "fsl,imx8qm-rpmsg-i2s" - fsl,audioindex : This is an index indicating the audio device index in the M4 side. diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index b7ace88f2d93..2d45f513ee3f 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -108,6 +108,7 @@ static const struct of_device_id fsl_rpmsg_i2s_ids[] = { { .compatible = "fsl,imx7ulp-rpmsg-i2s"}, { .compatible = "fsl,imx8mq-rpmsg-i2s"}, { .compatible = "fsl,imx8qxp-rpmsg-i2s"}, + { .compatible = "fsl,imx8qm-rpmsg-i2s"}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_i2s_ids); @@ -190,6 +191,13 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) rpmsg_i2s->codec_cs42888 = 1 + (2 << 16); } + if (of_device_is_compatible(pdev->dev.of_node, + "fsl,imx8qm-rpmsg-i2s")) { + rpmsg_i2s->codec_wm8960 = 0; + rpmsg_i2s->version = 1; + rpmsg_i2s->codec_cs42888 = 1 + (0 << 16); + } + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx8mq-rpmsg-i2s")) { rpmsg_i2s->codec_wm8960 = 0; From d895d7a234063a760c96dd7363e75e6ef3256d17 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 14 Sep 2018 13:09:40 +0800 Subject: [PATCH 34/59] MLK-19581-1: ASoC: rpmsg_ak4497: support ak4497 over rpmsg The difference of rpmsg_ak4497 and ak4497 driver is first one will send command through rpmsg, second one send command through i2c. Signed-off-by: Shengjiu Wang (cherry picked from commit 337f70c4ea5278db28abe1e6eaefbf2d0082aec5) --- sound/soc/codecs/Kconfig | 3 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rpmsg_ak4497.c | 1135 +++++++++++++++++++++++++++++++ 3 files changed, 1140 insertions(+) create mode 100644 sound/soc/codecs/rpmsg_ak4497.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 11229eaaa87a..a5c467d1c9d1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -259,6 +259,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_RPMSG_WM8960 select SND_SOC_RPMSG_CS42XX8 + select SND_SOC_RPMSG_AK4497 help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -1444,6 +1445,8 @@ config SND_SOC_RPMSG_WM8960 config SND_SOC_RPMSG_CS42XX8 tristate +config SND_SOC_RPMSG_AK4497 + tristate # Amp config SND_SOC_LM4857 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1a609c141714..c06200646c9f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -276,6 +276,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o snd-soc-rpmsg-wm8960-objs := rpmsg_wm8960.o snd-soc-rpmsg-cs42xx8-objs := rpmsg_cs42xx8.o +snd-soc-rpmsg-ak4497-objs := rpmsg_ak4497.o # Amp snd-soc-max9877-objs := max9877.o @@ -562,6 +563,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o obj-$(CONFIG_SND_SOC_RPMSG_WM8960) += snd-soc-rpmsg-wm8960.o obj-$(CONFIG_SND_SOC_RPMSG_CS42XX8) += snd-soc-rpmsg-cs42xx8.o +obj-$(CONFIG_SND_SOC_RPMSG_AK4497) += snd-soc-rpmsg-ak4497.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rpmsg_ak4497.c b/sound/soc/codecs/rpmsg_ak4497.c new file mode 100644 index 000000000000..80b8362eec93 --- /dev/null +++ b/sound/soc/codecs/rpmsg_ak4497.c @@ -0,0 +1,1135 @@ +/* + * ak4497.c -- audio driver for AK4497 + * + * Copyright (C) 2016 Asahi Kasei Microdevices Corporation + * Copyright (C) 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../fsl/fsl_rpmsg_i2s.h" + +#include "ak4497.h" + +//#define AK4497_DEBUG //used at debug mode +#define AK4497_NUM_SUPPLIES 2 +static const char *ak4497_supply_names[AK4497_NUM_SUPPLIES] = { + "DVDD", + "AVDD", +}; + +/* AK4497 Codec Private Data */ +struct rpmsg_ak4497_priv { + struct regmap *regmap; + int fs1; /* Sampling Frequency */ + int nBickFreq; /* 0: 48fs for 24bit, 1: 64fs or more for 32bit */ + int nTdmSds; + int pdn_gpio; + int mute_gpio; + int fmt; + struct regulator_bulk_data supplies[AK4497_NUM_SUPPLIES]; + struct fsl_rpmsg_i2s *rpmsg_i2s; + int audioindex; + struct platform_device *pdev; +}; + +/* ak4497 register cache & default register settings */ +static const struct reg_default ak4497_reg[] = { + { AK4497_00_CONTROL1, 0x0C}, + { AK4497_01_CONTROL2, 0x22}, + { AK4497_02_CONTROL3, 0x00}, + { AK4497_03_LCHATT, 0xFF}, + { AK4497_04_RCHATT, 0xFF}, + { AK4497_05_CONTROL4, 0x00}, + { AK4497_06_DSD1, 0x00}, + { AK4497_07_CONTROL5, 0x00}, + { AK4497_08_SOUNDCONTROL, 0x00}, + { AK4497_09_DSD2, 0x00}, + { AK4497_0A_CONTROL7, 0x04}, + { AK4497_0B_CONTROL8, 0x00}, + { AK4497_0C_RESERVED, 0x00}, + { AK4497_0D_RESERVED, 0x00}, + { AK4497_0E_RESERVED, 0x00}, + { AK4497_0F_RESERVED, 0x00}, + { AK4497_10_RESERVED, 0x00}, + { AK4497_11_RESERVED, 0x00}, + { AK4497_12_RESERVED, 0x00}, + { AK4497_13_RESERVED, 0x00}, + { AK4497_14_RESERVED, 0x00}, + { AK4497_15_DFSREAD, 0x00}, +}; + +/* Volume control: + * from -127 to 0 dB in 0.5 dB steps (mute instead of -127.5 dB) + */ +static DECLARE_TLV_DB_SCALE(latt_tlv, -12750, 50, 0); +static DECLARE_TLV_DB_SCALE(ratt_tlv, -12750, 50, 0); + +static const char * const ak4497_ecs_select_texts[] = {"768kHz", "384kHz"}; + +static const char * const ak4497_dem_select_texts[] = { + "44.1kHz", "OFF", "48kHz", "32kHz"}; +static const char * const ak4497_dzfm_select_texts[] = { + "Separated", "ANDed"}; + +static const char * const ak4497_sellr_select_texts[] = { + "Rch", "Lch"}; +static const char * const ak4497_dckb_select_texts[] = { + "Falling", "Rising"}; +static const char * const ak4497_dcks_select_texts[] = { + "512fs", "768fs"}; + +static const char * const ak4497_dsdd_select_texts[] = { + "Normal", "Volume Bypass"}; + +static const char * const ak4497_sc_select_texts[] = { + "Setting 1", "Setting 2", "Setting 3"}; +static const char * const ak4497_dsdf_select_texts[] = { + "50kHz", "150kHz"}; +static const char * const ak4497_dsd_input_path_select[] = { + "16_17_19pin", "3_4_5pin"}; +static const char * const ak4497_ats_select_texts[] = { + "4080/fs", "2040/fs", "510/fs", "255/fs"}; + +static const struct soc_enum ak4497_dac_enum[] = { + SOC_ENUM_SINGLE(AK4497_00_CONTROL1, 5, + ARRAY_SIZE(ak4497_ecs_select_texts), + ak4497_ecs_select_texts), + SOC_ENUM_SINGLE(AK4497_01_CONTROL2, 1, + ARRAY_SIZE(ak4497_dem_select_texts), + ak4497_dem_select_texts), + SOC_ENUM_SINGLE(AK4497_01_CONTROL2, 6, + ARRAY_SIZE(ak4497_dzfm_select_texts), + ak4497_dzfm_select_texts), + SOC_ENUM_SINGLE(AK4497_02_CONTROL3, 1, + ARRAY_SIZE(ak4497_sellr_select_texts), + ak4497_sellr_select_texts), + SOC_ENUM_SINGLE(AK4497_02_CONTROL3, 4, + ARRAY_SIZE(ak4497_dckb_select_texts), + ak4497_dckb_select_texts), + SOC_ENUM_SINGLE(AK4497_02_CONTROL3, 5, + ARRAY_SIZE(ak4497_dcks_select_texts), + ak4497_dcks_select_texts), + SOC_ENUM_SINGLE(AK4497_06_DSD1, 1, + ARRAY_SIZE(ak4497_dsdd_select_texts), + ak4497_dsdd_select_texts), + SOC_ENUM_SINGLE(AK4497_08_SOUNDCONTROL, 0, + ARRAY_SIZE(ak4497_sc_select_texts), + ak4497_sc_select_texts), + SOC_ENUM_SINGLE(AK4497_09_DSD2, 1, + ARRAY_SIZE(ak4497_dsdf_select_texts), + ak4497_dsdf_select_texts), + SOC_ENUM_SINGLE(AK4497_09_DSD2, 2, + ARRAY_SIZE(ak4497_dsd_input_path_select), + ak4497_dsd_input_path_select), + SOC_ENUM_SINGLE(AK4497_0B_CONTROL8, 6, + ARRAY_SIZE(ak4497_ats_select_texts), + ak4497_ats_select_texts), +}; + +static const char * const ak4497_dsdsel_select_texts[] = { + "64fs", "128fs", "256fs", "512fs"}; +static const char * const ak4497_bickfreq_select[] = {"48fs", "64fs"}; + +static const char * const ak4497_tdm_sds_select[] = { + "L1R1", "TDM128_L1R1", "TDM128_L2R2", + "TDM256_L1R1", "TDM256_L2R2", "TDM256_L3R3", "TDM256_L4R4", + "TDM512_L1R1", "TDM512_L2R2", "TDM512_L3R3", "TDM512_L4R4", + "TDM512_L5R5", "TDM512_L6R6", "TDM512_L7R7", "TDM512_L8R8", +}; + +static const char * const ak4497_adfs_select[] = { + "Normal Speed Mode", "Double Speed Mode", "Quad Speed Mode", + "Quad Speed Mode", "Oct Speed Mode", "Hex Speed Mode", "Oct Speed Mode", + "Hex Speed Mode" +}; + +static const struct soc_enum ak4497_dac_enum2[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_dsdsel_select_texts), + ak4497_dsdsel_select_texts), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_bickfreq_select), + ak4497_bickfreq_select), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_tdm_sds_select), + ak4497_tdm_sds_select), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_adfs_select), + ak4497_adfs_select) +}; + +static int ak4497_read(struct snd_soc_codec *codec, unsigned int reg, + unsigned int *val) +{ + int ret; + + ret = snd_soc_component_read(&codec->component, reg, val); + if (ret < 0) + dev_err(codec->dev, "Register %u read failed, ret=%d.\n", reg, ret); + + return ret; +} + +static int ak4497_get_dsdsel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + unsigned int dsdsel0, dsdsel1; + + ak4497_read(codec, AK4497_06_DSD1, &dsdsel0); + dsdsel0 &= AK4497_DSDSEL0; + + ak4497_read(codec, AK4497_09_DSD2, &dsdsel1); + dsdsel1 &= AK4497_DSDSEL1; + + ucontrol->value.enumerated.item[0] = ((dsdsel1 << 1) | dsdsel0); + + return 0; +} + +static int ak4497_set_dsdsel(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + unsigned int dsdsel = ucontrol->value.enumerated.item[0]; + + switch (dsdsel) { + case 0: /* 2.8224MHz */ + snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x00); + snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x00); + break; + case 1: /* 5.6448MHz */ + snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x01); + snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x00); + break; + case 2: /* 11.2896MHz */ + snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x00); + snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x01); + break; + case 3: /* 22.5792MHz */ + snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x01); + snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x01); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ak4497_get_bickfs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = ak4497->nBickFreq; + + return 0; +} + +static int ak4497_set_bickfs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + + ak4497->nBickFreq = ucontrol->value.enumerated.item[0]; + + return 0; +} + +static int ak4497_get_tdmsds(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = ak4497->nTdmSds; + + return 0; +} + +static int ak4497_set_tdmsds(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + int regA, regB; + + ak4497->nTdmSds = ucontrol->value.enumerated.item[0]; + + if (ak4497->nTdmSds == 0) + regB = 0; /* SDS0 bit = 0 */ + else + regB = (1 & (ak4497->nTdmSds - 1)); /* SDS0 bit = 1 */ + + switch (ak4497->nTdmSds) { + case 0: + regA = 0; /* Normal */ + break; + case 1: + case 2: + regA = 4; /* TDM128 TDM1-0bits = 1 */ + break; + case 3: + case 4: + regA = 8; /* TDM128 TDM1-0bits = 2 */ + break; + case 5: + case 6: + regA = 9; /* TDM128 TDM1-0bits = 2 */ + break; + case 7: + case 8: + regA = 0xC; /* TDM128 TDM1-0bits = 3 */ + break; + case 9: + case 10: + regA = 0xD; /* TDM128 TDM1-0bits = 3 */ + break; + case 11: + case 12: + regA = 0xE; /* TDM128 TDM1-0bits = 3 */ + break; + case 13: + case 14: + regA = 0xF; /* TDM128 TDM1-0bits = 3 */ + break; + default: + regA = 0; + regB = 0; + break; + } + + regA <<= 4; + regB <<= 4; + + snd_soc_update_bits(codec, AK4497_0A_CONTROL7, 0xF0, regA); + snd_soc_update_bits(codec, AK4497_0B_CONTROL8, 0x10, regB); + + return 0; +} + +static int ak4497_get_adfs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + unsigned int nADFSbit; + + ak4497_read(codec, AK4497_15_DFSREAD, &nADFSbit); + nADFSbit &= 0x7; + + ucontrol->value.enumerated.item[0] = nADFSbit; + + return 0; +} + +static int ak4497_set_adfs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("AK4497 : ADFS is read only\n"); + + return 0; +} + +static const char * const gain_control_texts[] = { + "2.8_2.8Vpp", "2.8_2.5Vpp", "2.5_2.5Vpp", "3.75_3.75Vpp", "3.75_2.5Vpp" +}; + +static const unsigned int gain_control_values[] = { + 0, 1, 2, 4, 5 +}; + +static const struct soc_enum ak4497_gain_control_enum = + SOC_VALUE_ENUM_SINGLE(AK4497_07_CONTROL5, 1, 7, + ARRAY_SIZE(gain_control_texts), + gain_control_texts, + gain_control_values); + +#ifdef AK4497_DEBUG + +static const char * const test_reg_select[] = { + "read AK4497 Reg 00:0B", + "read AK4497 Reg 15" +}; + +static const struct soc_enum ak4497_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(test_reg_select), test_reg_select), +}; + +static int nTestRegNo; + +static int get_test_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* Get the current output routing */ + ucontrol->value.enumerated.item[0] = nTestRegNo; + + return 0; +} + +static int set_test_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 currMode = ucontrol->value.enumerated.item[0]; + int i, regs, rege; + unsigned int value; + + nTestRegNo = currMode; + + if (nTestRegNo == 0) { + regs = 0x00; + rege = 0x0B; + } else { + regs = 0x15; + rege = 0x15; + } + + for (i = regs; i <= rege; i++) { + ak4497_read(codec, i, &value); + pr_debug("***AK4497 Addr,Reg=(%x, %x)\n", i, value); + } + + return 0; +} +#endif + +static const struct snd_kcontrol_new ak4497_snd_controls[] = { + SOC_SINGLE_TLV("AK4497 Lch Digital Volume", + AK4497_03_LCHATT, 0, 0xFF, 0, latt_tlv), + SOC_SINGLE_TLV("AK4497 Rch Digital Volume", + AK4497_04_RCHATT, 0, 0xFF, 0, ratt_tlv), + + SOC_ENUM("AK4497 EX DF I/F clock", ak4497_dac_enum[0]), + SOC_ENUM("AK4497 De-emphasis Response", ak4497_dac_enum[1]), + SOC_ENUM("AK4497 Data Zero Detect Mode", ak4497_dac_enum[2]), + SOC_ENUM("AK4497 Data Selection at Mono Mode", ak4497_dac_enum[3]), + + SOC_ENUM("AK4497 Polarity of DCLK", ak4497_dac_enum[4]), + SOC_ENUM("AK4497 DCKL Frequency", ak4497_dac_enum[5]), + + SOC_ENUM("AK4497 DDSD Play Back Path", ak4497_dac_enum[6]), + SOC_ENUM("AK4497 Sound control", ak4497_dac_enum[7]), + SOC_ENUM("AK4497 Cut Off of DSD Filter", ak4497_dac_enum[8]), + + SOC_ENUM_EXT("AK4497 DSD Data Stream", ak4497_dac_enum2[0], + ak4497_get_dsdsel, ak4497_set_dsdsel), + SOC_ENUM_EXT("AK4497 BICK Frequency Select", ak4497_dac_enum2[1], + ak4497_get_bickfs, ak4497_set_bickfs), + SOC_ENUM_EXT("AK4497 TDM Data Select", ak4497_dac_enum2[2], + ak4497_get_tdmsds, ak4497_set_tdmsds), + + SOC_SINGLE("AK4497 External Digital Filter", AK4497_00_CONTROL1, + 6, 1, 0), + SOC_SINGLE("AK4497 MCLK Frequency Auto Setting", AK4497_00_CONTROL1, + 7, 1, 0), + SOC_SINGLE("AK4497 MCLK FS Auto Detect", AK4497_00_CONTROL1, 4, 1, 0), + + SOC_SINGLE("AK4497 Soft Mute Control", AK4497_01_CONTROL2, 0, 1, 0), + SOC_SINGLE("AK4497 Short delay filter", AK4497_01_CONTROL2, 5, 1, 0), + SOC_SINGLE("AK4497 Data Zero Detect Enable", AK4497_01_CONTROL2, + 7, 1, 0), + SOC_SINGLE("AK4497 Slow Roll-off Filter", AK4497_02_CONTROL3, 0, 1, 0), + SOC_SINGLE("AK4497 Invering Enable of DZF", AK4497_02_CONTROL3, + 4, 1, 0), + SOC_SINGLE("AK4497 Mono Mode", AK4497_02_CONTROL3, 3, 1, 0), + SOC_SINGLE("AK4497 Super Slow Roll-off Filter", AK4497_05_CONTROL4, + 0, 1, 0), + SOC_SINGLE("AK4497 AOUTR Phase Inverting", AK4497_05_CONTROL4, + 6, 1, 0), + SOC_SINGLE("AK4497 AOUTL Phase Inverting", AK4497_05_CONTROL4, + 7, 1, 0), + SOC_SINGLE("AK4497 DSD Mute Release", AK4497_06_DSD1, 3, 1, 0), + SOC_SINGLE("AK4497 DSD Mute Control Hold", AK4497_06_DSD1, 4, 1, 0), + SOC_SINGLE("AK4497 DSDR is detected", AK4497_06_DSD1, 5, 1, 0), + SOC_SINGLE("AK4497 DSDL is detected", AK4497_06_DSD1, 6, 1, 0), + SOC_SINGLE("AK4497 DSD Data Mute", AK4497_06_DSD1, 7, 1, 0), + SOC_SINGLE("AK4497 Synchronization Control", AK4497_07_CONTROL5, + 0, 1, 0), + + SOC_ENUM("AK4497 Output Level", ak4497_gain_control_enum), + SOC_SINGLE("AK4497 High Sonud Quality Mode", AK4497_08_SOUNDCONTROL, + 2, 1, 0), + SOC_SINGLE("AK4497 Heavy Load Mode", AK4497_08_SOUNDCONTROL, 3, 1, 0), + SOC_ENUM("AK4497 DSD Data Input Pin", ak4497_dac_enum[9]), + SOC_SINGLE("AK4497 Daisy Chain", AK4497_0B_CONTROL8, 1, 1, 0), + SOC_ENUM("AK4497 ATT Transit Time", ak4497_dac_enum[10]), + + SOC_ENUM_EXT("AK4497 Read FS Auto Detect Mode", ak4497_dac_enum2[3], + ak4497_get_adfs, ak4497_set_adfs), + +#ifdef AK4497_DEBUG + SOC_ENUM_EXT("Reg Read", ak4497_enum[0], get_test_reg, set_test_reg), +#endif + +}; + +static const char * const ak4497_dac_enable_texts[] = {"Off", "On"}; + +static SOC_ENUM_SINGLE_VIRT_DECL(ak4497_dac_enable_enum, + ak4497_dac_enable_texts); + +static const struct snd_kcontrol_new ak4497_dac_enable_control = + SOC_DAPM_ENUM("DAC Switch", ak4497_dac_enable_enum); + +/* ak4497 dapm widgets */ +static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4497_0A_CONTROL7, 2, 0), + + SND_SOC_DAPM_MUX("AK4497 DAC Enable", SND_SOC_NOPM, + 0, 0, &ak4497_dac_enable_control), + + SND_SOC_DAPM_OUTPUT("AK4497 AOUT"), + +}; + +static const struct snd_soc_dapm_route ak4497_intercon[] = { + {"AK4497 DAC", NULL, "AK4497 SDTI"}, + {"AK4497 DAC Enable", "On", "AK4497 DAC"}, + {"AK4497 AOUT", NULL, "AK4497 DAC Enable"}, +}; + +static int ak4497_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + snd_pcm_format_t pcm_format = params_format(params); + + unsigned int dfs, dfs2, dsdsel0, dsdsel1, format; + int nfs1; + bool is_dsd = false; + int dsd_bclk; + + if (pcm_format == SNDRV_PCM_FORMAT_DSD_U8 || + pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE || + pcm_format == SNDRV_PCM_FORMAT_DSD_U16_BE || + pcm_format == SNDRV_PCM_FORMAT_DSD_U32_LE || + pcm_format == SNDRV_PCM_FORMAT_DSD_U32_BE) + is_dsd = true; + + nfs1 = params_rate(params); + ak4497->fs1 = nfs1; + + ak4497_read(codec, AK4497_01_CONTROL2, &dfs); + dfs &= ~AK4497_DFS; + + ak4497_read(codec, AK4497_05_CONTROL4, &dfs2); + dfs2 &= ~AK4497_DFS2; + + ak4497_read(codec, AK4497_06_DSD1, &dsdsel0); + dsdsel0 &= ~AK4497_DSDSEL0; + + ak4497_read(codec, AK4497_09_DSD2, &dsdsel1); + dsdsel1 &= ~AK4497_DSDSEL1; + + if (!is_dsd) { + switch (nfs1) { + case 8000: + case 11025: + case 16000: + case 22050: + case 32000: + case 44100: + case 48000: + dfs |= AK4497_DFS_48KHZ; + dfs2 |= AK4497_DFS2_48KHZ; + break; + case 88200: + case 96000: + dfs |= AK4497_DFS_96KHZ; + dfs2 |= AK4497_DFS2_48KHZ; + break; + case 176400: + case 192000: + dfs |= AK4497_DFS_192KHZ; + dfs2 |= AK4497_DFS2_48KHZ; + break; + case 352800: + case 384000: + dfs |= AK4497_DFS_384KHZ; + dfs2 |= AK4497_DFS2_384KHZ; + break; + case 705600: + case 768000: + dfs |= AK4497_DFS_768KHZ; + dfs2 |= AK4497_DFS2_384KHZ; + break; + default: + return -EINVAL; + } + } else { + dsd_bclk = params_rate(params) * + params_physical_width(params); + + switch (dsd_bclk) { + case 2822400: + dsdsel0 |= AK4497_DSDSEL0_2MHZ; + dsdsel1 |= AK4497_DSDSEL1_2MHZ; + break; + case 5644800: + dsdsel0 |= AK4497_DSDSEL0_5MHZ; + dsdsel1 |= AK4497_DSDSEL1_5MHZ; + break; + case 11289600: + dsdsel0 |= AK4497_DSDSEL0_11MHZ; + dsdsel1 |= AK4497_DSDSEL1_11MHZ; + break; + case 22579200: + dsdsel0 |= AK4497_DSDSEL0_22MHZ; + dsdsel1 |= AK4497_DSDSEL1_22MHZ; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, AK4497_06_DSD1, dsdsel0); + snd_soc_write(codec, AK4497_09_DSD2, dsdsel1); + } + + snd_soc_write(codec, AK4497_01_CONTROL2, dfs); + snd_soc_write(codec, AK4497_05_CONTROL4, dfs2); + + ak4497_read(codec, AK4497_00_CONTROL1, &format); + format &= ~AK4497_DIF; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + if (ak4497->fmt == SND_SOC_DAIFMT_I2S) + format |= AK4497_DIF_24BIT_I2S; + else + format |= AK4497_DIF_16BIT_LSB; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + if (ak4497->fmt == SND_SOC_DAIFMT_I2S) + format |= AK4497_DIF_32BIT_I2S; + else if (ak4497->fmt == SND_SOC_DAIFMT_LEFT_J) + format |= AK4497_DIF_32BIT_MSB; + else if (ak4497->fmt == SND_SOC_DAIFMT_RIGHT_J) + format |= AK4497_DIF_32BIT_LSB; + else + return -EINVAL; + break; + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, AK4497_00_CONTROL1, format); + + return 0; +} + +static int ak4497_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + return 0; +} + +static int ak4497_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + unsigned int format, format2; + + /* set master/slave audio interface */ + ak4497_read(codec, AK4497_00_CONTROL1, &format); + format &= ~AK4497_DIF; + + ak4497_read(codec, AK4497_02_CONTROL3, &format2); + format2 &= ~AK4497_DIF_DSD; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ak4497->fmt = SND_SOC_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ak4497->fmt = SND_SOC_DAIFMT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ak4497->fmt = SND_SOC_DAIFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_PDM: + format2 |= AK4497_DIF_DSD_MODE; + break; + default: + return -EINVAL; + } + + /* set format */ + snd_soc_write(codec, AK4497_00_CONTROL1, format); + snd_soc_write(codec, AK4497_02_CONTROL3, format2); + + return 0; +} + +static bool ak4497_volatile(struct device *dev, unsigned int reg) +{ + int ret; + +#ifdef AK4497_DEBUG + ret = 1; +#else + switch (reg) { + case AK4497_15_DFSREAD: + ret = 1; + break; + default: + ret = 0; + break; + } +#endif + return ret; +} + +static int ak4497_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + /* RSTN bit = 1 */ + snd_soc_update_bits(codec, AK4497_00_CONTROL1, 0x01, 0x01); + break; + case SND_SOC_BIAS_OFF: + /* RSTN bit = 0 */ + snd_soc_update_bits(codec, AK4497_00_CONTROL1, 0x01, 0x00); + break; + } + + return 0; +} + +static int ak4497_set_dai_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + int nfs, ndt; + + nfs = ak4497->fs1; + + if (mute) { /* SMUTE: 1 , MUTE */ + snd_soc_update_bits(codec, AK4497_01_CONTROL2, 0x01, 0x01); + ndt = 7424000 / nfs; + mdelay(ndt); + + /* External Mute ON */ + if (gpio_is_valid(ak4497->mute_gpio)) + gpio_set_value_cansleep(ak4497->mute_gpio, 1); + } else { /* SMUTE: 0, NORMAL operation */ + + /* External Mute OFF */ + if (gpio_is_valid(ak4497->mute_gpio)) + gpio_set_value_cansleep(ak4497->mute_gpio, 0); + snd_soc_update_bits(codec, AK4497_01_CONTROL2, 0x01, 0x00); + } + + return 0; +} + +#define AK4497_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define AK4497_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_DSD_U8 |\ + SNDRV_PCM_FMTBIT_DSD_U16_LE |\ + SNDRV_PCM_FMTBIT_DSD_U32_LE) + +static const unsigned int ak4497_rates[] = { + 8000, 11025, 16000, 22050, + 32000, 44100, 48000, 88200, + 96000, 176400, 192000, 352800, + 384000, 705600, 768000, 1411200, + 2822400, +}; + +static const struct snd_pcm_hw_constraint_list ak4497_rate_constraints = { + .count = ARRAY_SIZE(ak4497_rates), + .list = ak4497_rates, +}; + +static int ak4497_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { + int ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &ak4497_rate_constraints); + + return ret; +} + +static struct snd_soc_dai_ops ak4497_dai_ops = { + .startup = ak4497_startup, + .hw_params = ak4497_hw_params, + .set_sysclk = ak4497_set_dai_sysclk, + .set_fmt = ak4497_set_dai_fmt, + .digital_mute = ak4497_set_dai_mute, +}; + +struct snd_soc_dai_driver rpmsg_ak4497_dai[] = { + { + .name = "rpmsg-ak4497-aif", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK4497_FORMATS, + }, + .ops = &ak4497_dai_ops, + }, +}; + +static int ak4497_init_reg(struct snd_soc_codec *codec) +{ + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + /* External Mute ON */ + if (gpio_is_valid(ak4497->mute_gpio)) + gpio_set_value_cansleep(ak4497->mute_gpio, 1); + + if (gpio_is_valid(ak4497->pdn_gpio)) { + gpio_set_value_cansleep(ak4497->pdn_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(ak4497->pdn_gpio, 1); + usleep_range(1000, 2000); + } + + /* ak4497_set_bias_level(codec, SND_SOC_BIAS_STANDBY); */ + + /* SYNCE bit = 1 */ + ret = snd_soc_update_bits(codec, AK4497_07_CONTROL5, 0x01, 0x01); + if (ret) + return ret; + + /* HLOAD bit = 1, SC2 bit = 1 */ + ret = snd_soc_update_bits(codec, AK4497_08_SOUNDCONTROL, 0x0F, 0x0C); + if (ret) + return ret; + + return ret; +} + +static int ak4497_parse_dt(struct rpmsg_ak4497_priv *ak4497) +{ + struct device *dev; + struct device_node *np; + + dev = &(ak4497->pdev->dev); + np = dev->of_node; + + ak4497->pdn_gpio = -1; + ak4497->mute_gpio = -1; + + if (!np) + return 0; + + ak4497->pdn_gpio = of_get_named_gpio(np, "ak4497,pdn-gpio", 0); + if (ak4497->pdn_gpio < 0) + ak4497->pdn_gpio = -1; + + if (!gpio_is_valid(ak4497->pdn_gpio)) { + dev_err(dev, "ak4497 pdn pin(%u) is invalid\n", + ak4497->pdn_gpio); + ak4497->pdn_gpio = -1; + } + + ak4497->mute_gpio = of_get_named_gpio(np, "ak4497,mute-gpio", 0); + if (ak4497->mute_gpio < 0) + ak4497->mute_gpio = -1; + + if (!gpio_is_valid(ak4497->mute_gpio)) { + dev_err(dev, "ak4497 mute_gpio(%u) is invalid\n", + ak4497->mute_gpio); + ak4497->mute_gpio = -1; + } + + return 0; +} + +static int ak4497_probe(struct snd_soc_codec *codec) +{ + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + ret = ak4497_parse_dt(ak4497); + if (ret) + return ret; + + if (gpio_is_valid(ak4497->pdn_gpio)) { + ret = gpio_request(ak4497->pdn_gpio, "ak4497 pdn"); + if (ret) + return ret; + gpio_direction_output(ak4497->pdn_gpio, 0); + } + if (gpio_is_valid(ak4497->mute_gpio)) { + ret = gpio_request(ak4497->mute_gpio, "ak4497 mute"); + if (ret) + return ret; + gpio_direction_output(ak4497->mute_gpio, 0); + } + + ret = ak4497_init_reg(codec); + if (ret) + return ret; + + ak4497->fs1 = 48000; + ak4497->nBickFreq = 1; + ak4497->nTdmSds = 0; + + return ret; +} + +static int ak4497_remove(struct snd_soc_codec *codec) +{ + struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + + ak4497_set_bias_level(codec, SND_SOC_BIAS_OFF); + if (gpio_is_valid(ak4497->pdn_gpio)) { + gpio_set_value_cansleep(ak4497->pdn_gpio, 0); + gpio_free(ak4497->pdn_gpio); + } + if (gpio_is_valid(ak4497->mute_gpio)) + gpio_free(ak4497->mute_gpio); + + return 0; +} + +#ifdef CONFIG_PM +static int ak4497_runtime_suspend(struct device *dev) +{ + struct rpmsg_ak4497_priv *ak4497 = dev_get_drvdata(dev); + + regcache_cache_only(ak4497->regmap, true); + + if (gpio_is_valid(ak4497->pdn_gpio)) { + gpio_set_value_cansleep(ak4497->pdn_gpio, 0); + usleep_range(1000, 2000); + } + + if (gpio_is_valid(ak4497->mute_gpio)) + gpio_free(ak4497->mute_gpio); + + return 0; +} + +static int ak4497_runtime_resume(struct device *dev) +{ + struct rpmsg_ak4497_priv *ak4497 = dev_get_drvdata(dev); + + /* External Mute ON */ + if (gpio_is_valid(ak4497->mute_gpio)) + gpio_set_value_cansleep(ak4497->mute_gpio, 1); + + if (gpio_is_valid(ak4497->pdn_gpio)) { + gpio_set_value_cansleep(ak4497->pdn_gpio, 1); + usleep_range(1000, 2000); + } + + regcache_cache_only(ak4497->regmap, false); + regcache_mark_dirty(ak4497->regmap); + + return regcache_sync(ak4497->regmap); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops ak4497_pm = { + SET_RUNTIME_PM_OPS(ak4497_runtime_suspend, ak4497_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + +struct snd_soc_codec_driver rpmsg_codec_dev_ak4497 = { + .probe = ak4497_probe, + .remove = ak4497_remove, + + .idle_bias_off = true, + .set_bias_level = ak4497_set_bias_level, + + .component_driver = { + .controls = ak4497_snd_controls, + .num_controls = ARRAY_SIZE(ak4497_snd_controls), + .dapm_widgets = ak4497_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), + .dapm_routes = ak4497_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), + }, +}; + +static int rpmsg_ak4497_read(void *context, unsigned int reg, unsigned int *val) +{ + struct rpmsg_ak4497_priv *ak4497 = context; + struct fsl_rpmsg_i2s *rpmsg_i2s = ak4497->rpmsg_i2s; + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[GET_CODEC_VALUE].send_msg; + int err, reg_val; + + mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.audioindex = ak4497->audioindex; + rpmsg->param.buffer_addr = reg; + rpmsg->header.cmd = GET_CODEC_VALUE; + err = i2s_info->send_message(&i2s_info->rpmsg[GET_CODEC_VALUE], i2s_info); + reg_val = i2s_info->rpmsg[GET_CODEC_VALUE].recv_msg.param.reg_data; + mutex_unlock(&i2s_info->i2c_lock); + if (err) + return -EIO; + + *val = reg_val; + return 0; +} + +static int rpmsg_ak4497_write(void *context, unsigned int reg, unsigned int val) +{ + struct rpmsg_ak4497_priv *ak4497 = context; + struct fsl_rpmsg_i2s *rpmsg_i2s = ak4497->rpmsg_i2s; + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg = &i2s_info->rpmsg[SET_CODEC_VALUE].send_msg; + int err; + + mutex_lock(&i2s_info->i2c_lock); + rpmsg->param.audioindex = ak4497->audioindex; + rpmsg->param.buffer_addr = reg; + rpmsg->param.buffer_size = val; + rpmsg->header.cmd = SET_CODEC_VALUE; + err = i2s_info->send_message(&i2s_info->rpmsg[SET_CODEC_VALUE], i2s_info); + mutex_unlock(&i2s_info->i2c_lock); + if (err) + return -EIO; + + return 0; +} + +static const struct regmap_config rpmsg_ak4497_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4497_MAX_REGISTERS, + .volatile_reg = ak4497_volatile, + + .reg_defaults = ak4497_reg, + .num_reg_defaults = ARRAY_SIZE(ak4497_reg), + .cache_type = REGCACHE_RBTREE, + + .reg_read = rpmsg_ak4497_read, + .reg_write = rpmsg_ak4497_write, +}; + +static int rpmsg_ak4497_codec_probe(struct platform_device *pdev) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(pdev->dev.parent); + struct fsl_rpmsg_codec *pdata = pdev->dev.platform_data; + struct rpmsg_ak4497_priv *ak4497; + int ret = 0; + int i; + + ak4497 = devm_kzalloc(&pdev->dev, + sizeof(struct rpmsg_ak4497_priv), GFP_KERNEL); + if (ak4497 == NULL) + return -ENOMEM; + + ak4497->rpmsg_i2s = rpmsg_i2s; + ak4497->pdev = pdev; + + ak4497->regmap = devm_regmap_init(&pdev->dev, NULL, ak4497, &rpmsg_ak4497_regmap); + if (IS_ERR(ak4497->regmap)) + return PTR_ERR(ak4497->regmap); + + if (pdata) + ak4497->audioindex = pdata->audioindex; + + dev_set_drvdata(&pdev->dev, ak4497); + + for (i = 0; i < ARRAY_SIZE(ak4497->supplies); i++) + ak4497->supplies[i].supply = ak4497_supply_names[i]; + + ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(ak4497->supplies), + ak4497->supplies); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4497->supplies), + ak4497->supplies); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&pdev->dev, &rpmsg_codec_dev_ak4497, + &rpmsg_ak4497_dai[0], ARRAY_SIZE(rpmsg_ak4497_dai)); + if (ret < 0) + return ret; + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int rpmsg_ak4497_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver rpmsg_ak4497_codec_driver = { + .driver = { + .name = RPMSG_CODEC_DRV_NAME_AK4497, + .pm = &ak4497_pm, + }, + .probe = rpmsg_ak4497_codec_probe, + .remove = rpmsg_ak4497_codec_remove, +}; + +module_platform_driver(rpmsg_ak4497_codec_driver); + +MODULE_AUTHOR("Junichi Wakasugi "); +MODULE_AUTHOR("Daniel Baluta "); +MODULE_DESCRIPTION("ASoC ak4497 codec driver"); +MODULE_LICENSE("GPL"); From 1744cad4ec1d9ca6a0da2fcfa8dbb23d3f43c430 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 14 Sep 2018 13:10:05 +0800 Subject: [PATCH 35/59] MLK-19581-2: ASoC: imx-rpmsg: support rpmsg_ak4497 For ak4497, the dai fmt should be SND_SOC_DAIFMT_CBS_CFS. Signed-off-by: Shengjiu Wang (cherry picked from commit ecd184c46e2916d2bcdde8db9b2281b89e8b0189) --- sound/soc/fsl/Kconfig | 1 + sound/soc/fsl/imx-rpmsg.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 744f313c83d7..39fc56e5c359 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -269,6 +269,7 @@ config SND_SOC_IMX_RPMSG select SND_SOC_IMX_PCM_RPMSG select SND_SOC_FSL_RPMSG_I2S select SND_SOC_RPMSG_WM8960 + select SND_SOC_RPMSG_AK4497 help SoC Audio support for i.MX boards with rpmsg. There should be rpmsg devices defined in other core diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 7648e88bbced..8bbe044e29ba 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -61,20 +61,32 @@ static int imx_rpmsg_probe(struct platform_device *pdev) data->dai[0].name = "rpmsg hifi"; data->dai[0].stream_name = "rpmsg hifi"; + data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + if (rpmsg_i2s->codec_wm8960) { data->dai[0].codec_dai_name = "rpmsg-wm8960-hifi"; data->dai[0].codec_name = "rpmsg-audio-codec-wm8960"; - } else { + } + + if (rpmsg_i2s->codec_dummy) { data->dai[0].codec_dai_name = "snd-soc-dummy-dai"; data->dai[0].codec_name = "snd-soc-dummy"; } + + if (rpmsg_i2s->codec_ak4497) { + data->dai[0].codec_dai_name = "rpmsg-ak4497-aif"; + data->dai[0].codec_name = "rpmsg-audio-codec-ak4497"; + data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + } + data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); data->dai[0].platform_of_node = cpu_np; data->dai[0].playback_only = true; data->dai[0].capture_only = true; - data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; data->card.num_links = 1; data->card.dai_link = data->dai; From ace16c55c8bfeb4ca6f1f1164e48ae3b4d82ea46 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 14 Sep 2018 13:15:27 +0800 Subject: [PATCH 36/59] MLK-19581-3: ASoC: fsl_rpmsg_i2s: support multipul rate and DSD format The Ak4497 support large range rate and DSD format, so increase the supported scope of cpu dai. Signed-off-by: Shengjiu Wang (cherry picked from commit 29155a9161bfb9985918ed9130aebafc2293c734) --- sound/soc/fsl/fsl_rpmsg_i2s.c | 44 ++++++++++++++++++++++++++++------- sound/soc/fsl/fsl_rpmsg_i2s.h | 6 +++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 2d45f513ee3f..901e35c52b17 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -80,24 +80,50 @@ static int i2s_send_message(struct i2s_rpmsg *msg, return 0; } +static const unsigned int fsl_rpmsg_rates[] = { + 32000, 48000, 96000, 88200, 176400, 192000, + 352800, 384000, 705600, 768000, 1411200, 2822400, +}; + +static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = { + .count = ARRAY_SIZE(fsl_rpmsg_rates), + .list = fsl_rpmsg_rates, +}; + +static int fsl_rpmsg_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + int ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &fsl_rpmsg_rate_constraints); + + return ret; +} + +static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = { + .startup = fsl_rpmsg_startup, +}; + static struct snd_soc_dai_driver fsl_rpmsg_i2s_dai = { .playback = { .stream_name = "CPU-Playback", .channels_min = 2, .channels_max = 2, - .rates = FSL_RPMSG_I2S_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_RPMSG_I2S_FORMATS, }, .capture = { .stream_name = "CPU-Capture", .channels_min = 2, .channels_max = 2, - .rates = FSL_RPMSG_I2S_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_RPMSG_I2S_FORMATS, }, .symmetric_rates = 1, .symmetric_channels = 1, .symmetric_samplebits = 1, + .ops = &fsl_rpmsg_dai_ops, }; static const struct snd_soc_component_driver fsl_component = { @@ -200,15 +226,17 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx8mq-rpmsg-i2s")) { - rpmsg_i2s->codec_wm8960 = 0; + rpmsg_i2s->codec_dummy = 0; + rpmsg_i2s->codec_ak4497 = 1; rpmsg_i2s->version = 2; - rpmsg_i2s->rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000; + rpmsg_i2s->rates = SNDRV_PCM_RATE_KNOT; rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE; + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_DSD_U8 | + SNDRV_PCM_FMTBIT_DSD_U16_LE | + SNDRV_PCM_FMTBIT_DSD_U32_LE; + fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates; fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats; fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates; diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index e52c861c4d43..36d6c5f51360 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -330,6 +330,9 @@ #define RPMSG_S16_LE 0x0 #define RPMSG_S24_LE 0x1 #define RPMSG_S32_LE 0x2 +#define RPMSG_DSD_U16_LE 0x3 +#define RPMSG_DSD_U24_LE 0x4 +#define RPMSG_DSD_U32_LE 0x5 #define RPMSG_CH_LEFT 0x0 #define RPMSG_CH_RIGHT 0x1 @@ -410,8 +413,10 @@ struct fsl_rpmsg_i2s { struct platform_device *pdev; struct i2s_info i2s_info; struct pm_qos_request pm_qos_req; + int codec_dummy; int codec_wm8960; int codec_cs42888; + int codec_ak4497; int force_lpa; int version; int rates; @@ -421,6 +426,7 @@ struct fsl_rpmsg_i2s { #define RPMSG_CODEC_DRV_NAME_WM8960 "rpmsg-audio-codec-wm8960" #define RPMSG_CODEC_DRV_NAME_CS42888 "rpmsg-audio-codec-cs42888" +#define RPMSG_CODEC_DRV_NAME_AK4497 "rpmsg-audio-codec-ak4497" struct fsl_rpmsg_codec { int audioindex; From 6ef9128b309dbf826a174e415a0052bb0d6ec153 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 14 Sep 2018 13:16:15 +0800 Subject: [PATCH 37/59] MLK-19581-4: ASoC: imx-pcm-rpmsg: support rpmsg_ak4497 register the codec when needed. Signed-off-by: Shengjiu Wang (cherry picked from commit 241b6b3275924f3dc63be26d1442b55b80ac53ef) --- sound/soc/fsl/imx-pcm-rpmsg.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index d84723498641..0c2e7e392b79 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -63,6 +63,10 @@ static int imx_rpmsg_pcm_hw_params(struct snd_pcm_substream *substream, rpmsg->send_msg.param.format = RPMSG_S16_LE; else if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE) rpmsg->send_msg.param.format = RPMSG_S24_LE; + else if (params_format(params) == SNDRV_PCM_FORMAT_DSD_U16_LE) + rpmsg->send_msg.param.format = SNDRV_PCM_FORMAT_DSD_U16_LE; + else if (params_format(params) == SNDRV_PCM_FORMAT_DSD_U32_LE) + rpmsg->send_msg.param.format = SNDRV_PCM_FORMAT_DSD_U32_LE; else rpmsg->send_msg.param.format = RPMSG_S32_LE; @@ -704,7 +708,7 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) { struct platform_device *codec_pdev; struct fsl_rpmsg_i2s *rpmsg_i2s = NULL; - struct fsl_rpmsg_codec rpmsg_codec[2]; + struct fsl_rpmsg_codec rpmsg_codec[3]; int ret; if (!i2s_info_g) @@ -753,6 +757,22 @@ static int i2s_rpmsg_probe(struct rpmsg_device *rpdev) return ret; } } + + if (rpmsg_i2s->codec_ak4497) { + rpmsg_codec[2].audioindex = rpmsg_i2s->codec_ak4497 >> 16; + codec_pdev = platform_device_register_data( + &rpmsg_i2s->pdev->dev, + RPMSG_CODEC_DRV_NAME_AK4497, + PLATFORM_DEVID_NONE, + &rpmsg_codec[2], sizeof(struct fsl_rpmsg_codec)); + if (IS_ERR(codec_pdev)) { + dev_err(&rpdev->dev, + "failed to register rpmsg audio codec\n"); + ret = PTR_ERR(codec_pdev); + return ret; + } + } + return 0; } From 4fd0f03d0b2d8d3206c6c26661ba553a8c0ac6cf Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 19 Sep 2018 16:17:58 +0800 Subject: [PATCH 38/59] MLK-19760: ASoC: imx-pcm-rpmsg: fix resume back quickly after resume With LPA mode, if the period size is small, the timer for query buffer pointer will be triggered immediately after suspend, the MU interrupt will resume the system quickly. This patch is to disable timer when suspend. Signed-off-by: Shengjiu Wang (cherry picked from commit 9c5e78cf50855bd73f2b5c3dc8bc48f8a0907b39) --- sound/soc/fsl/imx-pcm-rpmsg.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 0c2e7e392b79..a7e4818050b3 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -22,6 +22,7 @@ #include "imx-pcm.h" #include "fsl_rpmsg_i2s.h" +#include "../../core/pcm_local.h" struct i2s_info *i2s_info_g; @@ -297,8 +298,17 @@ static void imx_rpmsg_pcm_dma_complete(void *arg) rpmsg->recv_msg.param.buffer_offset = rpmsg2->recv_msg.param.buffer_tail * snd_pcm_lib_period_bytes(substream); - - snd_pcm_period_elapsed(substream); + /* + * With suspend state, which is not running state, M4 will trigger + * system resume with PERIOD_DONE command, at this moment, the + * snd_pcm_period_elapsed can't update the hw ptr. so call + * snd_pcm_update_hw_ptr directly for this special case. + * + */ + if (!snd_pcm_running(substream) && rpmsg_i2s->force_lpa) + snd_pcm_update_hw_ptr(substream); + else + snd_pcm_period_elapsed(substream); } static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) @@ -466,7 +476,9 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; int ret; + int time_msec; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -476,8 +488,13 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) imx_rpmsg_async_issue_pending(substream); break; case SNDRV_PCM_TRIGGER_RESUME: - if (rpmsg_i2s->force_lpa) + if (rpmsg_i2s->force_lpa) { + time_msec = min(500, + (int)(runtime->period_size*1000/runtime->rate)); + mod_timer(&i2s_info->stream_timer[substream->stream], + jiffies + msecs_to_jiffies(time_msec)); break; + } case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: imx_rpmsg_restart(substream); break; @@ -487,7 +504,8 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) imx_rpmsg_pause(substream); else imx_rpmsg_terminate_all(substream); - } + } else + del_timer(&i2s_info->stream_timer[substream->stream]); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: imx_rpmsg_pause(substream); From 66aea92f46c17f455d72e67ebadaf9768dbc77fe Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 31 Oct 2018 16:45:27 +0800 Subject: [PATCH 39/59] MLK-19750-3: ASoC: rpmsg_cs42xx8: enhance async mode for rpmsg_cs42xx8 with this patch,codec driver can support tx and rx in different master/slave mode, for example, tx in master mode, rx in slave mode Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_cs42xx8.c | 146 ++++++++++++++++--------------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/sound/soc/codecs/rpmsg_cs42xx8.c b/sound/soc/codecs/rpmsg_cs42xx8.c index dfc520b925d5..38c3b1a86a93 100644 --- a/sound/soc/codecs/rpmsg_cs42xx8.c +++ b/sound/soc/codecs/rpmsg_cs42xx8.c @@ -43,7 +43,7 @@ struct rpmsg_cs42xx8_priv { struct regmap *regmap; struct clk *clk; - bool slave_mode; + bool slave_mode[2]; unsigned long sysclk; u32 tx_channels; int rate[2]; @@ -226,17 +226,21 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, CS42XX8_INTF_DAC_DIF_MASK | CS42XX8_INTF_ADC_DIF_MASK, val); - /* Set master/slave audio interface */ - switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - cs42xx8->slave_mode = true; - break; - case SND_SOC_DAIFMT_CBM_CFM: - cs42xx8->slave_mode = false; - break; - default: - dev_err(codec->dev, "unsupported master/slave mode\n"); - return -EINVAL; + if (cs42xx8->slave_mode[0] == cs42xx8->slave_mode[1]) { + /* Set master/slave audio interface */ + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs42xx8->slave_mode[0] = true; + cs42xx8->slave_mode[1] = true; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs42xx8->slave_mode[0] = false; + cs42xx8->slave_mode[1] = false; + break; + default: + dev_err(codec->dev, "unsupported master/slave mode\n"); + return -EINVAL; + } } return 0; @@ -249,70 +253,62 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u32 rate = params_rate(params); - u32 ratio_tx, ratio_rx; - u32 rate_tx, rate_rx; - u32 fm_tx, fm_rx; - u32 i, fm, val, mask; + u32 ratio[2]; + u32 rate[2]; + u32 fm[2]; + u32 i, val, mask; + bool condition1, condition2; if (tx) cs42xx8->tx_channels = params_channels(params); - rate_tx = tx ? rate : cs42xx8->rate[0]; - rate_rx = tx ? cs42xx8->rate[1] : rate; + rate[tx] = params_rate(params); + rate[!tx] = cs42xx8->rate[!tx]; - ratio_tx = rate_tx > 0 ? cs42xx8->sysclk / rate_tx : 0; - ratio_rx = rate_rx > 0 ? cs42xx8->sysclk / rate_rx : 0; + ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0; + ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0; - if (cs42xx8->slave_mode) { - fm_rx = CS42XX8_FM_AUTO; - fm_tx = CS42XX8_FM_AUTO; - } else { - if (rate_tx < 50000) - fm_tx = CS42XX8_FM_SINGLE; - else if (rate_tx > 50000 && rate_tx < 100000) - fm_tx = CS42XX8_FM_DOUBLE; - else if (rate_tx > 100000 && rate_tx < 200000) - fm_tx = CS42XX8_FM_QUAD; - else { - dev_err(codec->dev, "unsupported sample rate or rate combine\n"); - return -EINVAL; - } - - if (rate_rx < 50000) - fm_rx = CS42XX8_FM_SINGLE; - else if (rate_rx > 50000 && rate_rx < 100000) - fm_rx = CS42XX8_FM_DOUBLE; - else if (rate_rx > 100000 && rate_rx < 200000) - fm_rx = CS42XX8_FM_QUAD; - else { - dev_err(codec->dev, "unsupported sample rate or rate combine\n"); - return -EINVAL; + for (i = 0; i < 2; i++) { + if (cs42xx8->slave_mode[i]) { + fm[i] = CS42XX8_FM_AUTO; + } else { + if (rate[i] < 50000) + fm[i] = CS42XX8_FM_SINGLE; + else if (rate[i] > 50000 && rate[i] < 100000) + fm[i] = CS42XX8_FM_DOUBLE; + else if (rate[i] > 100000 && rate[i] < 200000) + fm[i] = CS42XX8_FM_QUAD; + else { + dev_err(codec->dev, + "unsupported sample rate or rate combine\n"); + return -EINVAL; + } } } - fm = tx ? fm_tx : fm_rx; + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { + condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ? + (cs42xx8_ratios[i].ratio[0] == ratio[tx] || + cs42xx8_ratios[i].ratio[1] == ratio[tx] || + cs42xx8_ratios[i].ratio[2] == ratio[tx]) : + (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) && + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk; - if (fm == CS42XX8_FM_AUTO) { - for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { - if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_tx || - cs42xx8_ratios[i].ratio[1] == ratio_tx || - cs42xx8_ratios[i].ratio[2] == ratio_tx) : true) && - (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_rx || - cs42xx8_ratios[i].ratio[1] == ratio_rx || - cs42xx8_ratios[i].ratio[2] == ratio_rx) : true) && - cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && - cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk) - break; - } - } else { - for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { - if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[fm_tx] == ratio_tx) : true) && - (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[fm_rx] == ratio_rx) : true) && - cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && - cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk) - break; - } + if (ratio[tx] <= 0) + condition1 = true; + + condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ? + (cs42xx8_ratios[i].ratio[0] == ratio[!tx] || + cs42xx8_ratios[i].ratio[1] == ratio[!tx] || + cs42xx8_ratios[i].ratio[2] == ratio[!tx]) : + (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx])); + + if (ratio[!tx] <= 0) + condition2 = true; + + if (condition1 && condition2) + break; } if (i == ARRAY_SIZE(cs42xx8_ratios)) { @@ -320,14 +316,14 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - cs42xx8->rate[substream->stream] = rate; + cs42xx8->rate[tx] = params_rate(params); mask = CS42XX8_FUNCMOD_MFREQ_MASK; val = cs42xx8_ratios[i].mfreq; regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, - CS42XX8_FUNCMOD_xC_FM(tx, fm) | val); + CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val); return 0; } @@ -340,7 +336,7 @@ static int cs42xx8_hw_free(struct snd_pcm_substream *substream, struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - cs42xx8->rate[substream->stream] = 0; + cs42xx8->rate[tx] = 0; regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, CS42XX8_FUNCMOD_xC_FM_MASK(tx), @@ -582,6 +578,18 @@ static int rpmsg_cs42xx8_codec_probe(struct platform_device *pdev) cs42xx8->sysclk = clk_get_rate(cs42xx8->clk); + if (of_property_read_bool(pdev->dev.parent->of_node, "fsl,txm-rxs")) { + /* 0 -- rx, 1 -- tx */ + cs42xx8->slave_mode[0] = true; + cs42xx8->slave_mode[1] = false; + } + + if (of_property_read_bool(pdev->dev.parent->of_node, "fsl,txs-rxm")) { + /* 0 -- rx, 1 -- tx */ + cs42xx8->slave_mode[0] = false; + cs42xx8->slave_mode[1] = true; + } + for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++) cs42xx8->supplies[i].supply = cs42xx8_supply_names[i]; From aa4833ef354c38464e5e85d42645d3bd8f97c8f6 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 8 Nov 2018 15:07:13 +0800 Subject: [PATCH 40/59] MLK-20277-1: ASoC: imx-pcm-rpmsg: reset the period index at stop With the case that underrun happened, the aplay will trigger stop and start, if the period index is not reset at stop, the counter of period will be wrong Signed-off-by: Shengjiu Wang --- sound/soc/fsl/imx-pcm-rpmsg.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index a7e4818050b3..f492e009399d 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -449,6 +449,7 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; u8 index = i2s_info->work_index; + int cmd; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_TERMINATE]; @@ -466,6 +467,18 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) i2s_info->work_index++; i2s_info->work_index %= WORK_MAX_NUM; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + cmd = I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM; + i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; + i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0; + i2s_info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0; + } else { + cmd = I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM; + i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; + i2s_info->rpmsg[cmd].recv_msg.param.buffer_tail = 0; + i2s_info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0; + } + del_timer(&i2s_info->stream_timer[substream->stream]); return 0; } From 783c56a330e020e5d31add27034ed51a51605acd Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 8 Nov 2018 15:12:25 +0800 Subject: [PATCH 41/59] MLK-20277-2: ASoC: rpmsg_ak4497: remove digital_mute function For the LPA mode, when the system enter suspend, the M4 will continue to play the data, but for normal ALSA case, the digital mute should be called at suspend, so the codec will be mute, which conflict with the requirement of LPA. Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_ak4497.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/sound/soc/codecs/rpmsg_ak4497.c b/sound/soc/codecs/rpmsg_ak4497.c index 80b8362eec93..ffc6792afec1 100644 --- a/sound/soc/codecs/rpmsg_ak4497.c +++ b/sound/soc/codecs/rpmsg_ak4497.c @@ -739,33 +739,6 @@ static int ak4497_set_bias_level(struct snd_soc_codec *codec, return 0; } -static int ak4497_set_dai_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); - int nfs, ndt; - - nfs = ak4497->fs1; - - if (mute) { /* SMUTE: 1 , MUTE */ - snd_soc_update_bits(codec, AK4497_01_CONTROL2, 0x01, 0x01); - ndt = 7424000 / nfs; - mdelay(ndt); - - /* External Mute ON */ - if (gpio_is_valid(ak4497->mute_gpio)) - gpio_set_value_cansleep(ak4497->mute_gpio, 1); - } else { /* SMUTE: 0, NORMAL operation */ - - /* External Mute OFF */ - if (gpio_is_valid(ak4497->mute_gpio)) - gpio_set_value_cansleep(ak4497->mute_gpio, 0); - snd_soc_update_bits(codec, AK4497_01_CONTROL2, 0x01, 0x00); - } - - return 0; -} - #define AK4497_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ @@ -807,7 +780,6 @@ static struct snd_soc_dai_ops ak4497_dai_ops = { .hw_params = ak4497_hw_params, .set_sysclk = ak4497_set_dai_sysclk, .set_fmt = ak4497_set_dai_fmt, - .digital_mute = ak4497_set_dai_mute, }; struct snd_soc_dai_driver rpmsg_ak4497_dai[] = { From cabb2debdc7e290dc09d7c41b55cfe43336cce13 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 8 Nov 2018 15:15:42 +0800 Subject: [PATCH 42/59] MLK-20277-3: ASoC: imx-pcm-rpmsg: change the state of substream in resume In LPA mode, the system will be resumed by audio notification, when the period size is small, there will be occasion that when notification the underrun is happen, but the substream runtime state is not running so the aplay won't trigger stop first, then start. just only trigger the start, which don't comply with the convention. So in this patch, we change the substream runtime state to running, when the notification happened at resume. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/imx-pcm-rpmsg.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index f492e009399d..440f0777e95e 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -282,6 +282,7 @@ static void imx_rpmsg_pcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; @@ -301,14 +302,20 @@ static void imx_rpmsg_pcm_dma_complete(void *arg) /* * With suspend state, which is not running state, M4 will trigger * system resume with PERIOD_DONE command, at this moment, the - * snd_pcm_period_elapsed can't update the hw ptr. so call - * snd_pcm_update_hw_ptr directly for this special case. + * snd_pcm_period_elapsed can't update the hw ptr. so change the + * state to be running and update timer * */ - if (!snd_pcm_running(substream) && rpmsg_i2s->force_lpa) - snd_pcm_update_hw_ptr(substream); - else - snd_pcm_period_elapsed(substream); + if (!snd_pcm_running(substream) && rpmsg_i2s->force_lpa) { + int time_msec; + + substream->runtime->status->state = SNDRV_PCM_STATE_RUNNING; + time_msec = min(500, + (int)(runtime->period_size*1000/runtime->rate)); + mod_timer(&i2s_info->stream_timer[substream->stream], + jiffies + msecs_to_jiffies(time_msec)); + } + snd_pcm_period_elapsed(substream); } static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) @@ -491,7 +498,6 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; int ret; - int time_msec; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -501,13 +507,8 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) imx_rpmsg_async_issue_pending(substream); break; case SNDRV_PCM_TRIGGER_RESUME: - if (rpmsg_i2s->force_lpa) { - time_msec = min(500, - (int)(runtime->period_size*1000/runtime->rate)); - mod_timer(&i2s_info->stream_timer[substream->stream], - jiffies + msecs_to_jiffies(time_msec)); + if (rpmsg_i2s->force_lpa) break; - } case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: imx_rpmsg_restart(substream); break; From ce1d0f858c1430ab02078a9415e20ba82f932a03 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 9 Nov 2018 15:16:23 +0800 Subject: [PATCH 43/59] MLK-20247: ASoC: imx-rpmsg: fix error when m4 image is not loaded The reason is same as commit d4eb8ab26399 ("MLK-19854-1: ASoC: imx-cs42888: fix error when m4 image is not loaded") Signed-off-by: Shengjiu Wang --- sound/soc/fsl/imx-rpmsg.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 8bbe044e29ba..c782fb9de042 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -35,6 +35,8 @@ static int imx_rpmsg_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct imx_rpmsg_data *data; struct fsl_rpmsg_i2s *rpmsg_i2s; + struct snd_soc_dai_link_component dlc = { 0 }; + struct snd_soc_dai *codec_dai; int ret; cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); @@ -83,6 +85,12 @@ static int imx_rpmsg_probe(struct platform_device *pdev) SND_SOC_DAIFMT_CBS_CFS; } + dlc.name = data->dai[0].codec_name; + dlc.dai_name = data->dai[0].codec_dai_name; + codec_dai = snd_soc_find_dai(&dlc); + if (!codec_dai) + return -ENODEV; + data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); data->dai[0].platform_of_node = cpu_np; data->dai[0].playback_only = true; From f59723a50dd900bd600c2d341b0fcfc6eddb6763 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 14 Nov 2018 14:49:34 +0800 Subject: [PATCH 44/59] MLK-20357-1: ASoC: imx-rpmsg: add audio routing for wm8960 The rpmsg wm8960 codec driver is completed to support full function, not only the volume control. which cause an issue that there is no sound when recording, the reason is that the MIC Bias not enabled, and it should be enabled through audio routing. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/imx-rpmsg.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index c782fb9de042..47aeffa3d15f 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -29,6 +29,13 @@ struct imx_rpmsg_data { struct snd_soc_card card; }; +static const struct snd_soc_dapm_widget imx_wm8960_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("Main MIC", NULL), +}; + static int imx_rpmsg_probe(struct platform_device *pdev) { struct device_node *cpu_np; @@ -116,6 +123,17 @@ static int imx_rpmsg_probe(struct platform_device *pdev) if (ret) goto fail; + if (rpmsg_i2s->codec_wm8960) { + ret = snd_soc_of_parse_audio_routing(&data->card, + "audio-routing"); + if (ret) + goto fail; + + data->card.dapm_widgets = imx_wm8960_dapm_widgets; + data->card.num_dapm_widgets = + ARRAY_SIZE(imx_wm8960_dapm_widgets); + } + platform_set_drvdata(pdev, &data->card); snd_soc_card_set_drvdata(&data->card, data); ret = devm_snd_soc_register_card(&pdev->dev, &data->card); From 270158aeddf911af9bd7db1f9f9b94f8e0b16e7c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 21 Nov 2018 14:20:36 +0800 Subject: [PATCH 45/59] MLK-20434: ASoC: fsl_rpmsg_i2s: add more rates in constraint list add more rates in constraint list, Fixes commit ee959e2c9b18 ("MLK-19581-3: ASoC: fsl_rpmsg_i2s: support multipul rate and DSD format") Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 901e35c52b17..192802ae339b 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -81,6 +81,7 @@ static int i2s_send_message(struct i2s_rpmsg *msg, } static const unsigned int fsl_rpmsg_rates[] = { + 8000, 11025, 16000, 22050, 44100, 32000, 48000, 96000, 88200, 176400, 192000, 352800, 384000, 705600, 768000, 1411200, 2822400, }; From a6f50f7227354768f43bb21bd3ca3b66017e4590 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 27 Dec 2018 14:48:06 +0800 Subject: [PATCH 46/59] MLK-20661: ASoC: imx-pcm-rpmsg: remove the nonblock constraint we can call the snd_pcm_period_elapsed in timer's callback to achieve pseudo period wake up, so the nonblock constraint can be removed Signed-off-by: Shengjiu Wang --- sound/soc/fsl/imx-pcm-rpmsg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 440f0777e95e..e46b80a51411 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -152,6 +152,8 @@ static void imx_rpmsg_timer_callback(unsigned long data) mod_timer(&i2s_info->stream_timer[substream->stream], jiffies + msecs_to_jiffies(time_msec)); } + + snd_pcm_period_elapsed(substream); } static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) @@ -257,7 +259,6 @@ static int imx_rpmsg_pcm_prepare(struct snd_pcm_substream *substream) */ if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) && - (substream->f_flags & O_NONBLOCK) && rpmsg_i2s->version == 2 && rpmsg_i2s->enable_lpa) rpmsg_i2s->force_lpa = 1; From 0f97f661f23294002522119c90fcc85cf0757267 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 8 Aug 2018 15:05:48 +0800 Subject: [PATCH 47/59] MLK-19168-5: ASoC: imx-cs42888: support codec through rpmsg (part 2) support codec through rpmsg Signed-off-by: Shengjiu Wang (cherry picked from commit 8585d67e54c4c3607990a792718992de8be8fe58) [ Aisheng: split card imx-cs42888 changes ] Signed-off-by: Dong Aisheng --- sound/soc/fsl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 39fc56e5c359..4d3bd04381e4 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -270,6 +270,7 @@ config SND_SOC_IMX_RPMSG select SND_SOC_FSL_RPMSG_I2S select SND_SOC_RPMSG_WM8960 select SND_SOC_RPMSG_AK4497 + select SND_SOC_RPMSG_CS42XX8 help SoC Audio support for i.MX boards with rpmsg. There should be rpmsg devices defined in other core From 75b021881108ad2e4ae78355086e76ea6b7f4a76 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Mon, 22 Apr 2019 15:35:27 +0800 Subject: [PATCH 48/59] ASoC: fsl: fix SND_SOC_FSL_RPMSG_I2S dependency issue IMX RPMSG is still not ready. In file included from ../sound/soc/codecs/rpmsg_wm8960.c:27:0: ../sound/soc/codecs/../fsl/fsl_rpmsg_i2s.h:278:29: fatal error: linux/imx_rpmsg.h: No such file or directory #include ^ compilation terminated. ../scripts/Makefile.build:278: recipe for target 'sound/soc/codecs/rpmsg_wm8960.o' failed make[4]: *** [sound/soc/codecs/rpmsg_wm8960.o] Error 1 make[4]: *** Waiting for unfinished jobs.... AR arch/arm/mach-imx/built-in.a In file included from ../sound/soc/codecs/rpmsg_cs42xx8.c:25:0: ../sound/soc/codecs/../fsl/fsl_rpmsg_i2s.h:278:29: fatal error: linux/imx_rpmsg.h: No such file or directory #include Signed-off-by: Dong Aisheng --- sound/soc/fsl/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 4d3bd04381e4..00b5db8898d3 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -76,7 +76,7 @@ config SND_SOC_FSL_MICFIL config SND_SOC_FSL_RPMSG_I2S tristate "I2S base on the RPMSG support" - depends on RPMSG + depends on CONFIG_HAVE_IMX_RPMSG help Say Y if you want to add rpmsg i2s support for the Freescale CPUs. which is depends on the rpmsg. @@ -92,7 +92,7 @@ config SND_SOC_IMX_PCM_DMA config SND_SOC_IMX_PCM_RPMSG tristate - depends on RPMSG + depends on CONFIG_HAVE_IMX_RPMSG select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SOC_IMX_AUDMUX @@ -265,7 +265,7 @@ config SND_SOC_EUKREA_TLV320 config SND_SOC_IMX_RPMSG tristate "SoC Audio support for i.MX boards with rpmsg" - depends on OF && I2C && INPUT + depends on CONFIG_HAVE_IMX_RPMSG select SND_SOC_IMX_PCM_RPMSG select SND_SOC_FSL_RPMSG_I2S select SND_SOC_RPMSG_WM8960 From 49822e0e11381218216d052811719257b8851deb Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 11 Apr 2019 19:42:50 +0800 Subject: [PATCH 49/59] MLK-21440-2: ASoC: imx-rpmsg: Remove snd_soc_find_dai Previously we add snd_soc_find_dai to check if the codec is probed or not, but this bring kernel dump issue when CONFIG_LOCKDEP=y. [ 2.823379] WARNING: CPU: 2 PID: 1 at sound/soc/soc-core.c:1016 snd_soc_find_dai+0x144/0x150 [ 2.831827] Modules linked in: [ 2.834907] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 4.14.78-00007-g524e1b1f0b3f #18 [ 2.842748] Hardware name: FSL i.MX8MM EVK board (DT) [ 2.847811] task: ffff8000624f0000 task.stack: ffff000008078000 [ 2.853745] PC is at snd_soc_find_dai+0x144/0x150 [ 2.858462] LR is at snd_soc_find_dai+0x140/0x150 ... [ 3.469675] Call trace: [ 3.472135] Exception stack(0xffff00000807ba40 to 0xffff00000807bb80) [ 3.478590] ba40: 0000000000000000 00000000ffffffff 0000000000000000 0000000000000002 [ 3.4864333.596564] [] bus_add_driver+0x110/0x230 [ 3.602149] [] driver_register+0x68/0x100 [ 3.607735] [] __platform_driver_register+0x54/0x60 [ 3.614191] [] imx_rpmsg_driver_init+0x20/0x28 [ 3.620213] [] do_one_initcall+0x44/0x130 [ 3.625801] [] kernel_init_freeable+0x1e0/0x280 [ 3.631909] [] kernel_init+0x18/0x110 [ 3.637148] [] ret_from_fork+0x10/0x1c So we could't resolve the warning "snd_soc_register_card failed (-517)". Fixes: 75632b22a332 ("MLK-20247: ASoC: imx-rpmsg: fix error when m4 image is not loaded") Signed-off-by: Shengjiu Wang (cherry picked from commit bbc1868d1c3ec448301d76606e7199f74aa35581) (cherry picked from commit ebf3fc59b84503a8da40a8e54a92e6de60e01dce) --- sound/soc/fsl/imx-rpmsg.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 47aeffa3d15f..ad9e4c322de9 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -42,8 +42,6 @@ static int imx_rpmsg_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct imx_rpmsg_data *data; struct fsl_rpmsg_i2s *rpmsg_i2s; - struct snd_soc_dai_link_component dlc = { 0 }; - struct snd_soc_dai *codec_dai; int ret; cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); @@ -92,12 +90,6 @@ static int imx_rpmsg_probe(struct platform_device *pdev) SND_SOC_DAIFMT_CBS_CFS; } - dlc.name = data->dai[0].codec_name; - dlc.dai_name = data->dai[0].codec_dai_name; - codec_dai = snd_soc_find_dai(&dlc); - if (!codec_dai) - return -ENODEV; - data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); data->dai[0].platform_of_node = cpu_np; data->dai[0].playback_only = true; From b3b38fea22b1a929830ffb5576d1d4a1a53b0b00 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 11 Apr 2019 19:39:47 +0800 Subject: [PATCH 50/59] MLK-21440-1: ASoC: fsl_rpmsg_i2s: init spin lock With CONFIG_LOCKDEP=y, there will kernel dump [ 55.305563] INFO: trying to register non-static key. [ 55.310540] the code is fine but needs lockdep annotation. [ 55.316027] turning off the locking correctness validator. [ 55.321520] CPU: 0 PID: 32 Comm: kworker/0:1 Tainted: G W 4.14.78-00007-g524e1b1f0b3f #18 [ 55.330827] Hardware name: FSL i.MX8MM EVK board (DT) [ 55.335892] Workqueue: events rpmsg_work_handler [ 55.340515] Call trace: [ 55.342971] [] dump_backtrace+0x0/0x270 [ 55.348375] [] show_stack+0x24/0x30 [ 55.353433] [] dump_stack+0xb8/0xf0 [ 55.358490] [] register_lock_class+0x364/0x548 [ 55.364501] [] __lock_acquire+0x7c/0x18a8 [ 55.370077] [] lock_acquire+0xc8/0x290 [ 55.375394] [] _raw_spin_lock_irqsave+0x54/0x70 [ 55.381493] [] i2s_rpmsg_cb+0xe8/0x1b0 [ 55.386810] [] rpmsg_recv_done+0x144/0x210 [ 55.392476] [] vring_interrupt+0x44/0x78 [ 55.397966] [] imx_mu_rpmsg_callback+0x6c/0x80 [ 55.403977] [] notifier_call_chain+0x5c/0x98 [ 55.409813] [] __blocking_notifier_call_chain+0x58/0xa0 [ 55.416605] [] blocking_notifier_call_chain+0x3c/0x50 [ 55.423223] [] rpmsg_work_handler+0x88/0xe8 [ 55.428974] [] process_one_work+0x290/0x738 [ 55.434723] [] worker_thread+0x58/0x460 [ 55.440127] [] kthread+0x104/0x130 [ 55.445096] [] ret_from_fork+0x10/0x1c The reason is that the spin lock is not initilized. Signed-off-by: Shengjiu Wang (cherry picked from commit 0e98ac55f17f6505f92c515c3c04581039e21865) (cherry picked from commit 314ef30b1bc75971c3e59f5ab9919586495ca556) --- sound/soc/fsl/fsl_rpmsg_i2s.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 192802ae339b..c94212dfb48f 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -196,6 +196,8 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) mutex_init(&i2s_info->tx_lock); mutex_init(&i2s_info->i2c_lock); + spin_lock_init(&i2s_info->lock[0]); + spin_lock_init(&i2s_info->lock[1]); if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx7ulp-rpmsg-i2s")) { From 26e4c3d80f3ce07dbe5732009b4e6a46ec370966 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 7 Mar 2019 11:01:06 +0800 Subject: [PATCH 51/59] ASoC: fsl_rpmsg: Merge changes from imx_4.19.y 77be7d36a525 ("MLK-22400-1: ASoC: fsl_rpmsg_i2s: Add rpmsg i2s for imx8mn") 31d5dfa44c20 ("MLK-22340-5: ASoC: imx-pcm-rpmsg: enable ignore_suspend for LPA") c5c41138d5c8 ("MLK-22340-4: ASoC: fsl_rpmsg_i2s: add lock to protect the resource") adb4b596d2ba ("MLK-22340-3: ASoC: imx-pcm-rpmsg: refine the timer") a5e80bf012c3 ("MLK-22340-2: ASoC: imx-pcm-rpmsg: drop the cmd I2S_TX_POINTER") 404367d9316b ("MLK-21980: ASoC: imx-pcm-rpmsg: add debugfs_prefix for platform") 971faf095246 ("MLK-21450: ASoC: fsl_rpmsg: fix timer issue for updating to 4.19") edbbcdc07bf1 ("MLK-21461: ASoC: fsl_rpmsg_i2s: clear buffer pointer in i2s_send_message") f5f2f38018d8 ("MLK-21447: ASoC: fsl_rpmsg_i2s: underrun in m4 for msg delayed") d9410ace757f ("MLK-21307: ASoC: fsl_rpmsg_i2s: optimize the message sent to m4") 98633a4cd35b ("MLK-21440-1: ASoC: fsl_rpmsg_i2s: init spin lock") 7bf6d3131863 ("MLK-21107-1: ASoC: Fix timer wake up system after suspend") eb422681ffa4 ("MLK-20661: ASoC: imx-pcm-rpmsg: remove the nonblock constraint") e36957e97ca9 ("MLK-21002-1: ASoC: imx-pcm-rpmsg: fix data consumed faster with pause") bc828e61693f ("MLK-19565-1: ASoC: fsl_rpmsg_i2s: support rpmsg on imx8qm") e56bedf71ae4 ("MLK-21107-2: ASoC: Drop msg if msg queue is full") Signed-off-by: Shengjiu Wang (cherry picked from commit 31def064871336af7780d01a6ed8c5ed075b7e48) --- sound/soc/fsl/Kconfig | 6 +- sound/soc/fsl/fsl_rpmsg_i2s.c | 68 +++++++- sound/soc/fsl/fsl_rpmsg_i2s.h | 16 +- sound/soc/fsl/imx-pcm-rpmsg.c | 307 ++++++++++++++++++++-------------- sound/soc/fsl/imx-rpmsg.c | 28 +++- 5 files changed, 287 insertions(+), 138 deletions(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 00b5db8898d3..1409bd27d7cc 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -76,7 +76,7 @@ config SND_SOC_FSL_MICFIL config SND_SOC_FSL_RPMSG_I2S tristate "I2S base on the RPMSG support" - depends on CONFIG_HAVE_IMX_RPMSG + depends on HAVE_IMX_RPMSG help Say Y if you want to add rpmsg i2s support for the Freescale CPUs. which is depends on the rpmsg. @@ -92,7 +92,7 @@ config SND_SOC_IMX_PCM_DMA config SND_SOC_IMX_PCM_RPMSG tristate - depends on CONFIG_HAVE_IMX_RPMSG + depends on HAVE_IMX_RPMSG select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SOC_IMX_AUDMUX @@ -265,7 +265,7 @@ config SND_SOC_EUKREA_TLV320 config SND_SOC_IMX_RPMSG tristate "SoC Audio support for i.MX boards with rpmsg" - depends on CONFIG_HAVE_IMX_RPMSG + depends on HAVE_IMX_RPMSG select SND_SOC_IMX_PCM_RPMSG select SND_SOC_FSL_RPMSG_I2S select SND_SOC_RPMSG_WM8960 diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index c94212dfb48f..4ba151186681 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -70,6 +70,25 @@ static int i2s_send_message(struct i2s_rpmsg *msg, sizeof(struct i2s_rpmsg_r)); memcpy(&info->rpmsg[msg->recv_msg.header.cmd].recv_msg, &msg->recv_msg, sizeof(struct i2s_rpmsg_r)); + + /* + * Reset the buffer pointer to be zero, actully we have + * set the buffer pointer to be zero in imx_rpmsg_terminate_all + * But if there is timer task queued in queue, after it is + * executed the buffer pointer will be changed, so need to + * reset it again with TERMINATE command. + */ + + switch (msg->send_msg.header.cmd) { + case I2S_TX_TERMINATE: + info->rpmsg[I2S_TX_POINTER].recv_msg.param.buffer_offset = 0; + break; + case I2S_RX_TERMINATE: + info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0; + break; + default: + break; + } } dev_dbg(&info->rpdev->dev, "cmd:%d, resp %d\n", @@ -136,6 +155,7 @@ static const struct of_device_id fsl_rpmsg_i2s_ids[] = { { .compatible = "fsl,imx8mq-rpmsg-i2s"}, { .compatible = "fsl,imx8qxp-rpmsg-i2s"}, { .compatible = "fsl,imx8qm-rpmsg-i2s"}, + { .compatible = "fsl,imx8mn-rpmsg-i2s"}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_i2s_ids); @@ -144,11 +164,40 @@ static void rpmsg_i2s_work(struct work_struct *work) { struct work_of_rpmsg *work_of_rpmsg; struct i2s_info *i2s_info; + bool is_period_done = false; + unsigned long flags; + struct i2s_rpmsg msg; 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); + spin_lock_irqsave(&i2s_info->lock[0], flags); + if (i2s_info->period_done_msg_enabled[0]) { + memcpy(&msg, &i2s_info->period_done_msg[0], sizeof(struct i2s_rpmsg_s)); + i2s_info->period_done_msg_enabled[0] = false; + spin_unlock_irqrestore(&i2s_info->lock[0], flags); + + i2s_send_message(&msg, i2s_info); + } else + spin_unlock_irqrestore(&i2s_info->lock[0], flags); + + if (i2s_info->period_done_msg_enabled[1]) { + i2s_send_message(&i2s_info->period_done_msg[1], i2s_info); + i2s_info->period_done_msg_enabled[1] = false; + } + + if (work_of_rpmsg->msg.send_msg.header.type == I2S_TYPE_C && + (work_of_rpmsg->msg.send_msg.header.cmd == I2S_TX_PERIOD_DONE || + work_of_rpmsg->msg.send_msg.header.cmd == I2S_RX_PERIOD_DONE)) + is_period_done = true; + + if (!is_period_done) + i2s_send_message(&work_of_rpmsg->msg, i2s_info); + + spin_lock_irqsave(&i2s_info->wq_lock, flags); + i2s_info->work_read_index++; + i2s_info->work_read_index %= WORK_MAX_NUM; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); } static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) @@ -179,6 +228,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) return -ENOMEM; } + i2s_info->work_write_index = 1; i2s_info->send_message = i2s_send_message; for (i = 0; i < WORK_MAX_NUM; i++) { @@ -198,6 +248,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) mutex_init(&i2s_info->i2c_lock); spin_lock_init(&i2s_info->lock[0]); spin_lock_init(&i2s_info->lock[1]); + spin_lock_init(&i2s_info->wq_lock); if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx7ulp-rpmsg-i2s")) { @@ -246,6 +297,21 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats; } + if (of_device_is_compatible(pdev->dev.of_node, + "fsl,imx8mn-rpmsg-i2s")) { + rpmsg_i2s->codec_dummy = 1; + rpmsg_i2s->version = 2; + rpmsg_i2s->rates = SNDRV_PCM_RATE_KNOT; + rpmsg_i2s->formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE; + + fsl_rpmsg_i2s_dai.playback.rates = rpmsg_i2s->rates; + fsl_rpmsg_i2s_dai.playback.formats = rpmsg_i2s->formats; + fsl_rpmsg_i2s_dai.capture.rates = rpmsg_i2s->rates; + fsl_rpmsg_i2s_dai.capture.formats = rpmsg_i2s->formats; + } + if (of_property_read_bool(pdev->dev.of_node, "fsl,enable-lpa")) rpmsg_i2s->enable_lpa = 1; diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.h b/sound/soc/fsl/fsl_rpmsg_i2s.h index 36d6c5f51360..e42986ca14ff 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.h +++ b/sound/soc/fsl/fsl_rpmsg_i2s.h @@ -309,7 +309,7 @@ #define I2S_TYPE_A_NUM 0x18 -#define WORK_MAX_NUM 0x18 +#define WORK_MAX_NUM 0x30 #define I2S_TX_PERIOD_DONE 0x0 #define I2S_RX_PERIOD_DONE 0x1 @@ -384,6 +384,11 @@ struct work_of_rpmsg { struct work_struct work; }; +struct stream_timer { + struct timer_list timer; + struct snd_pcm_substream *substream; +}; + typedef void (*dma_callback)(void *arg); struct i2s_info { struct rpmsg_device *rpdev; @@ -394,18 +399,23 @@ struct i2s_info { struct i2s_rpmsg_r recv_msg; struct i2s_rpmsg rpmsg[I2S_CMD_MAX_NUM]; + struct i2s_rpmsg period_done_msg[2]; + bool period_done_msg_enabled[2]; struct workqueue_struct *rpmsg_wq; struct work_of_rpmsg work_list[WORK_MAX_NUM]; - int work_index; + int work_write_index; + int work_read_index; + int msg_drop_count[2]; int num_period[2]; void *callback_param[2]; int (*send_message)(struct i2s_rpmsg *msg, struct i2s_info *info); dma_callback callback[2]; spinlock_t lock[2]; + spinlock_t wq_lock; struct mutex tx_lock; struct mutex i2c_lock; - struct timer_list stream_timer[2]; + struct stream_timer stream_timer[2]; int prealloc_buffer_size; }; diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index e46b80a51411..f49205a6c41a 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -24,6 +24,8 @@ #include "fsl_rpmsg_i2s.h" #include "../../core/pcm_local.h" +#define DRV_NAME "imx_pcm_rpmsg" + struct i2s_info *i2s_info_g; static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { @@ -107,53 +109,49 @@ static snd_pcm_uframes_t imx_rpmsg_pcm_pointer( int buffer_tail = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; else - rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; + rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; - buffer_tail = rpmsg->recv_msg.param.buffer_offset / - snd_pcm_lib_period_bytes(substream); + buffer_tail = rpmsg->recv_msg.param.buffer_tail; pos = buffer_tail * snd_pcm_lib_period_bytes(substream); return bytes_to_frames(substream->runtime, pos); } -static void imx_rpmsg_timer_callback(unsigned long data) +static void imx_rpmsg_timer_callback(struct timer_list *t) { - struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; - struct snd_pcm_runtime *runtime = substream->runtime; + struct stream_timer *stream_timer = + from_timer(stream_timer, t, timer); + struct snd_pcm_substream *substream = stream_timer->substream; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; - int time_msec; + unsigned long flags; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; + rpmsg = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; else - rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; + rpmsg = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rpmsg->send_msg.header.cmd = I2S_TX_POINTER; + rpmsg->send_msg.header.cmd = I2S_TX_PERIOD_DONE; else - rpmsg->send_msg.header.cmd = I2S_RX_POINTER; + rpmsg->send_msg.header.cmd = I2S_RX_PERIOD_DONE; - memcpy(&i2s_info->work_list[index].msg, rpmsg, + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; - - if (rpmsg_i2s->force_lpa) { - time_msec = min(500, - (int)(runtime->period_size*1000/runtime->rate)); - mod_timer(&i2s_info->stream_timer[substream->stream], - jiffies + msecs_to_jiffies(time_msec)); - } - - snd_pcm_period_elapsed(substream); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + } else + i2s_info->msg_drop_count[substream->stream]++; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); } static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) @@ -209,10 +207,13 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) return ret; + i2s_info->msg_drop_count[substream->stream] = 0; /*create thread*/ - setup_timer(&i2s_info->stream_timer[substream->stream], - imx_rpmsg_timer_callback, (unsigned long)substream); + i2s_info->stream_timer[substream->stream].substream = substream; + + timer_setup(&i2s_info->stream_timer[substream->stream].timer, + imx_rpmsg_timer_callback, 0); return ret; } @@ -240,10 +241,16 @@ static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) flush_workqueue(i2s_info->rpmsg_wq); i2s_info->send_message(rpmsg, i2s_info); - del_timer(&i2s_info->stream_timer[substream->stream]); + del_timer(&i2s_info->stream_timer[substream->stream].timer); kfree(prtd); + rtd->dai_link->ignore_suspend = 0; + + if (i2s_info->msg_drop_count[substream->stream]) + dev_warn(rtd->dev, "Msg is dropped!, number is %d\n", + i2s_info->msg_drop_count[substream->stream]); + return ret; } @@ -260,9 +267,10 @@ static int imx_rpmsg_pcm_prepare(struct snd_pcm_substream *substream) if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) && rpmsg_i2s->version == 2 && - rpmsg_i2s->enable_lpa) + rpmsg_i2s->enable_lpa) { + rtd->dai_link->ignore_suspend = 1; rpmsg_i2s->force_lpa = 1; - else + } else rpmsg_i2s->force_lpa = 0; return 0; @@ -282,40 +290,7 @@ static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, static void imx_rpmsg_pcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); - struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - struct i2s_rpmsg *rpmsg, *rpmsg2; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - rpmsg = &i2s_info->rpmsg[I2S_TX_POINTER]; - rpmsg2 = &i2s_info->rpmsg[I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM]; - } else { - rpmsg = &i2s_info->rpmsg[I2S_RX_POINTER]; - rpmsg2 = &i2s_info->rpmsg[I2S_RX_PERIOD_DONE + I2S_TYPE_A_NUM]; - } - - rpmsg->recv_msg.param.buffer_offset = - rpmsg2->recv_msg.param.buffer_tail - * snd_pcm_lib_period_bytes(substream); - /* - * With suspend state, which is not running state, M4 will trigger - * system resume with PERIOD_DONE command, at this moment, the - * snd_pcm_period_elapsed can't update the hw ptr. so change the - * state to be running and update timer - * - */ - if (!snd_pcm_running(substream) && rpmsg_i2s->force_lpa) { - int time_msec; - - substream->runtime->status->state = SNDRV_PCM_STATE_RUNNING; - time_msec = min(500, - (int)(runtime->period_size*1000/runtime->rate)); - mod_timer(&i2s_info->stream_timer[substream->stream], - jiffies + msecs_to_jiffies(time_msec)); - } snd_pcm_period_elapsed(substream); } @@ -326,7 +301,7 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; + unsigned long flags; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_BUFFER]; @@ -348,27 +323,34 @@ static int imx_rpmsg_pcm_prepare_and_submit(struct snd_pcm_substream *substream) rpmsg->send_msg.param.buffer_size / rpmsg->send_msg.param.period_size; - memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; - i2s_info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete; i2s_info->callback_param[substream->stream] = substream; + + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + } else { + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + return -EPIPE; + } + return 0; } -static void imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) +static int imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; - int time_msec; + unsigned long flags; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_START]; @@ -380,18 +362,21 @@ static void imx_rpmsg_async_issue_pending(struct snd_pcm_substream *substream) else rpmsg->send_msg.header.cmd = I2S_RX_START; - memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; - - if (rpmsg_i2s->force_lpa) { - time_msec = min(500, - (int)(runtime->period_size*1000/runtime->rate)); - mod_timer(&i2s_info->stream_timer[substream->stream], - jiffies + msecs_to_jiffies(time_msec)); + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + } else { + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + return -EPIPE; } + + return 0; } static int imx_rpmsg_restart(struct snd_pcm_substream *substream) @@ -401,7 +386,7 @@ static int imx_rpmsg_restart(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; + unsigned long flags; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_RESTART]; @@ -413,12 +398,19 @@ static int imx_rpmsg_restart(struct snd_pcm_substream *substream) else rpmsg->send_msg.header.cmd = I2S_RX_RESTART; - memcpy(&i2s_info->work_list[index].msg, rpmsg, + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; - + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + } else { + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + return -EPIPE; + } return 0; } @@ -429,7 +421,7 @@ static int imx_rpmsg_pause(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; + unsigned long flags; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_PAUSE]; @@ -441,11 +433,19 @@ static int imx_rpmsg_pause(struct snd_pcm_substream *substream) else rpmsg->send_msg.header.cmd = I2S_RX_PAUSE; - memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + } else { + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + return -EPIPE; + } return 0; } @@ -456,8 +456,8 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; int cmd; + unsigned long flags; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_TERMINATE]; @@ -469,12 +469,6 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) else rpmsg->send_msg.header.cmd = I2S_RX_TERMINATE; - memcpy(&i2s_info->work_list[index].msg, rpmsg, - sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cmd = I2S_TX_PERIOD_DONE + I2S_TYPE_A_NUM; i2s_info->rpmsg[cmd].send_msg.param.buffer_tail = 0; @@ -487,7 +481,22 @@ static int imx_rpmsg_terminate_all(struct snd_pcm_substream *substream) i2s_info->rpmsg[I2S_RX_POINTER].recv_msg.param.buffer_offset = 0; } - del_timer(&i2s_info->stream_timer[substream->stream]); + del_timer(&i2s_info->stream_timer[substream->stream].timer); + + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, + sizeof(struct i2s_rpmsg_s)); + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + } else { + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + return -EPIPE; + } + return 0; } @@ -497,41 +506,43 @@ int imx_rpmsg_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); - struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; - int ret; + int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: ret = imx_rpmsg_pcm_prepare_and_submit(substream); if (ret) return ret; - imx_rpmsg_async_issue_pending(substream); + ret = imx_rpmsg_async_issue_pending(substream); break; case SNDRV_PCM_TRIGGER_RESUME: if (rpmsg_i2s->force_lpa) break; + /* fall through */ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - imx_rpmsg_restart(substream); + ret = imx_rpmsg_restart(substream); break; case SNDRV_PCM_TRIGGER_SUSPEND: if (!rpmsg_i2s->force_lpa) { if (runtime->info & SNDRV_PCM_INFO_PAUSE) - imx_rpmsg_pause(substream); + ret = imx_rpmsg_pause(substream); else - imx_rpmsg_terminate_all(substream); - } else - del_timer(&i2s_info->stream_timer[substream->stream]); + ret = imx_rpmsg_terminate_all(substream); + } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - imx_rpmsg_pause(substream); + ret = imx_rpmsg_pause(substream); break; case SNDRV_PCM_TRIGGER_STOP: - imx_rpmsg_terminate_all(substream); + ret = imx_rpmsg_terminate_all(substream); break; default: return -EINVAL; } + if (ret) + return ret; + return 0; } @@ -544,8 +555,10 @@ int imx_rpmsg_pcm_ack(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - u8 index = i2s_info->work_index; int buffer_tail = 0; + int writen_num = 0; + snd_pcm_sframes_t avail; + unsigned long flags; if (!rpmsg_i2s->force_lpa) return 0; @@ -567,13 +580,45 @@ int imx_rpmsg_pcm_ack(struct snd_pcm_substream *substream) buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream); if (buffer_tail != rpmsg->send_msg.param.buffer_tail) { + writen_num = buffer_tail - rpmsg->send_msg.param.buffer_tail; + if (writen_num < 0) + writen_num += runtime->periods; + rpmsg->send_msg.param.buffer_tail = buffer_tail; - memcpy(&i2s_info->work_list[index].msg, rpmsg, + + spin_lock_irqsave(&i2s_info->lock[substream->stream], flags); + memcpy(&i2s_info->period_done_msg[substream->stream], rpmsg, + sizeof(struct i2s_rpmsg_s)); + + i2s_info->period_done_msg_enabled[substream->stream] = true; + spin_unlock_irqrestore(&i2s_info->lock[substream->stream], flags); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_hw_avail(runtime); + else + avail = snd_pcm_capture_hw_avail(runtime); + + if ((avail - writen_num * runtime->period_size) <= runtime->period_size) { + spin_lock_irqsave(&i2s_info->wq_lock, flags); + if (i2s_info->work_write_index != i2s_info->work_read_index) { + int index = i2s_info->work_write_index; + memcpy(&i2s_info->work_list[index].msg, rpmsg, sizeof(struct i2s_rpmsg_s)); - queue_work(i2s_info->rpmsg_wq, + queue_work(i2s_info->rpmsg_wq, &i2s_info->work_list[index].work); - i2s_info->work_index++; - i2s_info->work_index %= WORK_MAX_NUM; + i2s_info->work_write_index++; + i2s_info->work_write_index %= WORK_MAX_NUM; + } else + i2s_info->msg_drop_count[substream->stream]++; + spin_unlock_irqrestore(&i2s_info->wq_lock, flags); + } else { + if (rpmsg_i2s->force_lpa && !timer_pending(&i2s_info->stream_timer[substream->stream].timer)) { + int time_msec; + time_msec = (int)(runtime->period_size*1000/runtime->rate); + mod_timer(&i2s_info->stream_timer[substream->stream].timer, + jiffies + msecs_to_jiffies(time_msec)); + } + } } return 0; @@ -669,7 +714,8 @@ out: return ret; } -static struct snd_soc_platform_driver imx_rpmsg_soc_platform = { +static struct snd_soc_component_driver imx_rpmsg_soc_component = { + .name = DRV_NAME, .ops = &imx_rpmsg_pcm_ops, .pcm_new = imx_rpmsg_pcm_new, .pcm_free = imx_rpmsg_pcm_free_dma_buffers, @@ -678,10 +724,25 @@ static struct snd_soc_platform_driver imx_rpmsg_soc_platform = { int imx_rpmsg_platform_register(struct device *dev) { struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + struct snd_soc_component *component; + int ret; i2s_info_g = &rpmsg_i2s->i2s_info; - return devm_snd_soc_register_platform(dev, &imx_rpmsg_soc_platform); + ret = devm_snd_soc_register_component(dev, &imx_rpmsg_soc_component, + NULL, 0); + if (ret) + return ret; + + component = snd_soc_lookup_component(dev, DRV_NAME); + if (!component) + return -EINVAL; + +#ifdef CONFIG_DEBUG_FS + component->debugfs_prefix = "dma"; +#endif + + return 0; } EXPORT_SYMBOL_GPL(imx_rpmsg_platform_register); diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index ad9e4c322de9..9db5f190bc4f 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -42,8 +42,13 @@ static int imx_rpmsg_probe(struct platform_device *pdev) struct platform_device *cpu_pdev; struct imx_rpmsg_data *data; struct fsl_rpmsg_i2s *rpmsg_i2s; + struct snd_soc_dai_link_component *dlc; int ret; + dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL); + if (!dlc) + return -ENOMEM; + cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); if (!cpu_np) { dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n"); @@ -66,6 +71,13 @@ static int imx_rpmsg_probe(struct platform_device *pdev) rpmsg_i2s = platform_get_drvdata(cpu_pdev); + data->dai[0].cpus = &dlc[0]; + data->dai[0].num_cpus = 1; + data->dai[0].platforms = &dlc[1]; + data->dai[0].num_platforms = 1; + data->dai[0].codecs = &dlc[2]; + data->dai[0].num_codecs = 1; + data->dai[0].name = "rpmsg hifi"; data->dai[0].stream_name = "rpmsg hifi"; data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | @@ -73,25 +85,25 @@ static int imx_rpmsg_probe(struct platform_device *pdev) SND_SOC_DAIFMT_CBM_CFM; if (rpmsg_i2s->codec_wm8960) { - data->dai[0].codec_dai_name = "rpmsg-wm8960-hifi"; - data->dai[0].codec_name = "rpmsg-audio-codec-wm8960"; + data->dai[0].codecs->dai_name = "rpmsg-wm8960-hifi"; + data->dai[0].codecs->name = "rpmsg-audio-codec-wm8960"; } if (rpmsg_i2s->codec_dummy) { - data->dai[0].codec_dai_name = "snd-soc-dummy-dai"; - data->dai[0].codec_name = "snd-soc-dummy"; + data->dai[0].codecs->dai_name = "snd-soc-dummy-dai"; + data->dai[0].codecs->name = "snd-soc-dummy"; } if (rpmsg_i2s->codec_ak4497) { - data->dai[0].codec_dai_name = "rpmsg-ak4497-aif"; - data->dai[0].codec_name = "rpmsg-audio-codec-ak4497"; + data->dai[0].codecs->dai_name = "rpmsg-ak4497-aif"; + data->dai[0].codecs->name = "rpmsg-audio-codec-ak4497"; data->dai[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; } - data->dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev); - data->dai[0].platform_of_node = cpu_np; + data->dai[0].cpus->dai_name = dev_name(&cpu_pdev->dev); + data->dai[0].platforms->of_node = cpu_np; data->dai[0].playback_only = true; data->dai[0].capture_only = true; data->card.num_links = 1; From d4215f595396a4b53c00da587d1f34a836bac77e Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 3 Sep 2019 14:55:54 +0800 Subject: [PATCH 52/59] ASoC: rpmsg_ak4497: replace codec to component replace codec to component Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_ak4497.c | 154 ++++++++++++++++---------------- sound/soc/codecs/rpmsg_ak4497.h | 90 +++++++++++++++++++ 2 files changed, 167 insertions(+), 77 deletions(-) create mode 100644 sound/soc/codecs/rpmsg_ak4497.h diff --git a/sound/soc/codecs/rpmsg_ak4497.c b/sound/soc/codecs/rpmsg_ak4497.c index ffc6792afec1..1837dcb3f789 100644 --- a/sound/soc/codecs/rpmsg_ak4497.c +++ b/sound/soc/codecs/rpmsg_ak4497.c @@ -30,7 +30,7 @@ #include #include "../fsl/fsl_rpmsg_i2s.h" -#include "ak4497.h" +#include "rpmsg_ak4497.h" //#define AK4497_DEBUG //used at debug mode #define AK4497_NUM_SUPPLIES 2 @@ -176,14 +176,14 @@ static const struct soc_enum ak4497_dac_enum2[] = { ak4497_adfs_select) }; -static int ak4497_read(struct snd_soc_codec *codec, unsigned int reg, +static int ak4497_read(struct snd_soc_component *component, unsigned int reg, unsigned int *val) { int ret; - ret = snd_soc_component_read(&codec->component, reg, val); + ret = snd_soc_component_read(component, reg, val); if (ret < 0) - dev_err(codec->dev, "Register %u read failed, ret=%d.\n", reg, ret); + dev_err(component->dev, "Register %u read failed, ret=%d.\n", reg, ret); return ret; } @@ -191,13 +191,13 @@ static int ak4497_read(struct snd_soc_codec *codec, unsigned int reg, static int ak4497_get_dsdsel(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); unsigned int dsdsel0, dsdsel1; - ak4497_read(codec, AK4497_06_DSD1, &dsdsel0); + ak4497_read(component, AK4497_06_DSD1, &dsdsel0); dsdsel0 &= AK4497_DSDSEL0; - ak4497_read(codec, AK4497_09_DSD2, &dsdsel1); + ak4497_read(component, AK4497_09_DSD2, &dsdsel1); dsdsel1 &= AK4497_DSDSEL1; ucontrol->value.enumerated.item[0] = ((dsdsel1 << 1) | dsdsel0); @@ -208,25 +208,25 @@ static int ak4497_get_dsdsel(struct snd_kcontrol *kcontrol, static int ak4497_set_dsdsel(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); unsigned int dsdsel = ucontrol->value.enumerated.item[0]; switch (dsdsel) { case 0: /* 2.8224MHz */ - snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x00); - snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x00); + snd_soc_component_update_bits(component, AK4497_06_DSD1, 0x01, 0x00); + snd_soc_component_update_bits(component, AK4497_09_DSD2, 0x01, 0x00); break; case 1: /* 5.6448MHz */ - snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x01); - snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x00); + snd_soc_component_update_bits(component, AK4497_06_DSD1, 0x01, 0x01); + snd_soc_component_update_bits(component, AK4497_09_DSD2, 0x01, 0x00); break; case 2: /* 11.2896MHz */ - snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x00); - snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x01); + snd_soc_component_update_bits(component, AK4497_06_DSD1, 0x01, 0x00); + snd_soc_component_update_bits(component, AK4497_09_DSD2, 0x01, 0x01); break; case 3: /* 22.5792MHz */ - snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x01); - snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x01); + snd_soc_component_update_bits(component, AK4497_06_DSD1, 0x01, 0x01); + snd_soc_component_update_bits(component, AK4497_09_DSD2, 0x01, 0x01); break; default: return -EINVAL; @@ -238,8 +238,8 @@ static int ak4497_set_dsdsel(struct snd_kcontrol *kcontrol, static int ak4497_get_bickfs(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); ucontrol->value.enumerated.item[0] = ak4497->nBickFreq; @@ -249,8 +249,8 @@ static int ak4497_get_bickfs(struct snd_kcontrol *kcontrol, static int ak4497_set_bickfs(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); ak4497->nBickFreq = ucontrol->value.enumerated.item[0]; @@ -260,8 +260,8 @@ static int ak4497_set_bickfs(struct snd_kcontrol *kcontrol, static int ak4497_get_tdmsds(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); ucontrol->value.enumerated.item[0] = ak4497->nTdmSds; @@ -271,8 +271,8 @@ static int ak4497_get_tdmsds(struct snd_kcontrol *kcontrol, static int ak4497_set_tdmsds(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); int regA, regB; ak4497->nTdmSds = ucontrol->value.enumerated.item[0]; @@ -323,8 +323,8 @@ static int ak4497_set_tdmsds(struct snd_kcontrol *kcontrol, regA <<= 4; regB <<= 4; - snd_soc_update_bits(codec, AK4497_0A_CONTROL7, 0xF0, regA); - snd_soc_update_bits(codec, AK4497_0B_CONTROL8, 0x10, regB); + snd_soc_component_update_bits(component, AK4497_0A_CONTROL7, 0xF0, regA); + snd_soc_component_update_bits(component, AK4497_0B_CONTROL8, 0x10, regB); return 0; } @@ -332,10 +332,10 @@ static int ak4497_set_tdmsds(struct snd_kcontrol *kcontrol, static int ak4497_get_adfs(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); unsigned int nADFSbit; - ak4497_read(codec, AK4497_15_DFSREAD, &nADFSbit); + ak4497_read(component, AK4497_15_DFSREAD, &nADFSbit); nADFSbit &= 0x7; ucontrol->value.enumerated.item[0] = nADFSbit; @@ -390,7 +390,7 @@ static int get_test_reg(struct snd_kcontrol *kcontrol, static int set_test_reg(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); u32 currMode = ucontrol->value.enumerated.item[0]; int i, regs, rege; unsigned int value; @@ -406,7 +406,7 @@ static int set_test_reg(struct snd_kcontrol *kcontrol, } for (i = regs; i <= rege; i++) { - ak4497_read(codec, i, &value); + ak4497_read(component, i, &value); pr_debug("***AK4497 Addr,Reg=(%x, %x)\n", i, value); } @@ -515,8 +515,8 @@ static int ak4497_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); snd_pcm_format_t pcm_format = params_format(params); unsigned int dfs, dfs2, dsdsel0, dsdsel1, format; @@ -534,16 +534,16 @@ static int ak4497_hw_params(struct snd_pcm_substream *substream, nfs1 = params_rate(params); ak4497->fs1 = nfs1; - ak4497_read(codec, AK4497_01_CONTROL2, &dfs); + ak4497_read(component, AK4497_01_CONTROL2, &dfs); dfs &= ~AK4497_DFS; - ak4497_read(codec, AK4497_05_CONTROL4, &dfs2); + ak4497_read(component, AK4497_05_CONTROL4, &dfs2); dfs2 &= ~AK4497_DFS2; - ak4497_read(codec, AK4497_06_DSD1, &dsdsel0); + ak4497_read(component, AK4497_06_DSD1, &dsdsel0); dsdsel0 &= ~AK4497_DSDSEL0; - ak4497_read(codec, AK4497_09_DSD2, &dsdsel1); + ak4497_read(component, AK4497_09_DSD2, &dsdsel1); dsdsel1 &= ~AK4497_DSDSEL1; if (!is_dsd) { @@ -606,14 +606,14 @@ static int ak4497_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_write(codec, AK4497_06_DSD1, dsdsel0); - snd_soc_write(codec, AK4497_09_DSD2, dsdsel1); + snd_soc_component_write(component, AK4497_06_DSD1, dsdsel0); + snd_soc_component_write(component, AK4497_09_DSD2, dsdsel1); } - snd_soc_write(codec, AK4497_01_CONTROL2, dfs); - snd_soc_write(codec, AK4497_05_CONTROL4, dfs2); + snd_soc_component_write(component, AK4497_01_CONTROL2, dfs); + snd_soc_component_write(component, AK4497_05_CONTROL4, dfs2); - ak4497_read(codec, AK4497_00_CONTROL1, &format); + ak4497_read(component, AK4497_00_CONTROL1, &format); format &= ~AK4497_DIF; switch (params_format(params)) { @@ -642,7 +642,7 @@ static int ak4497_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_write(codec, AK4497_00_CONTROL1, format); + snd_soc_component_write(component, AK4497_00_CONTROL1, format); return 0; } @@ -655,15 +655,15 @@ static int ak4497_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static int ak4497_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); unsigned int format, format2; /* set master/slave audio interface */ - ak4497_read(codec, AK4497_00_CONTROL1, &format); + ak4497_read(component, AK4497_00_CONTROL1, &format); format &= ~AK4497_DIF; - ak4497_read(codec, AK4497_02_CONTROL3, &format2); + ak4497_read(component, AK4497_02_CONTROL3, &format2); format2 &= ~AK4497_DIF_DSD; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -673,7 +673,7 @@ static int ak4497_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBM_CFS: default: - dev_err(codec->dev, "Clock mode unsupported"); + dev_err(component->dev, "Clock mode unsupported"); return -EINVAL; } @@ -695,8 +695,8 @@ static int ak4497_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } /* set format */ - snd_soc_write(codec, AK4497_00_CONTROL1, format); - snd_soc_write(codec, AK4497_02_CONTROL3, format2); + snd_soc_component_write(component, AK4497_00_CONTROL1, format); + snd_soc_component_write(component, AK4497_02_CONTROL3, format2); return 0; } @@ -720,7 +720,7 @@ static bool ak4497_volatile(struct device *dev, unsigned int reg) return ret; } -static int ak4497_set_bias_level(struct snd_soc_codec *codec, +static int ak4497_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { switch (level) { @@ -728,11 +728,11 @@ static int ak4497_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: /* RSTN bit = 1 */ - snd_soc_update_bits(codec, AK4497_00_CONTROL1, 0x01, 0x01); + snd_soc_component_update_bits(component, AK4497_00_CONTROL1, 0x01, 0x01); break; case SND_SOC_BIAS_OFF: /* RSTN bit = 0 */ - snd_soc_update_bits(codec, AK4497_00_CONTROL1, 0x01, 0x00); + snd_soc_component_update_bits(component, AK4497_00_CONTROL1, 0x01, 0x00); break; } @@ -766,7 +766,8 @@ static const struct snd_pcm_hw_constraint_list ak4497_rate_constraints = { }; static int ak4497_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) { + struct snd_soc_dai *dai) +{ int ret; ret = snd_pcm_hw_constraint_list(substream->runtime, 0, @@ -796,9 +797,9 @@ struct snd_soc_dai_driver rpmsg_ak4497_dai[] = { }, }; -static int ak4497_init_reg(struct snd_soc_codec *codec) +static int ak4497_init_reg(struct snd_soc_component *component) { - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); int ret = 0; /* External Mute ON */ @@ -815,12 +816,12 @@ static int ak4497_init_reg(struct snd_soc_codec *codec) /* ak4497_set_bias_level(codec, SND_SOC_BIAS_STANDBY); */ /* SYNCE bit = 1 */ - ret = snd_soc_update_bits(codec, AK4497_07_CONTROL5, 0x01, 0x01); + ret = snd_soc_component_update_bits(component, AK4497_07_CONTROL5, 0x01, 0x01); if (ret) return ret; /* HLOAD bit = 1, SC2 bit = 1 */ - ret = snd_soc_update_bits(codec, AK4497_08_SOUNDCONTROL, 0x0F, 0x0C); + ret = snd_soc_component_update_bits(component, AK4497_08_SOUNDCONTROL, 0x0F, 0x0C); if (ret) return ret; @@ -864,9 +865,9 @@ static int ak4497_parse_dt(struct rpmsg_ak4497_priv *ak4497) return 0; } -static int ak4497_probe(struct snd_soc_codec *codec) +static int ak4497_probe(struct snd_soc_component *component) { - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); int ret = 0; ret = ak4497_parse_dt(ak4497); @@ -886,7 +887,7 @@ static int ak4497_probe(struct snd_soc_codec *codec) gpio_direction_output(ak4497->mute_gpio, 0); } - ret = ak4497_init_reg(codec); + ret = ak4497_init_reg(component); if (ret) return ret; @@ -897,11 +898,11 @@ static int ak4497_probe(struct snd_soc_codec *codec) return ret; } -static int ak4497_remove(struct snd_soc_codec *codec) +static void ak4497_remove(struct snd_soc_component *component) { - struct rpmsg_ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_ak4497_priv *ak4497 = snd_soc_component_get_drvdata(component); - ak4497_set_bias_level(codec, SND_SOC_BIAS_OFF); + ak4497_set_bias_level(component, SND_SOC_BIAS_OFF); if (gpio_is_valid(ak4497->pdn_gpio)) { gpio_set_value_cansleep(ak4497->pdn_gpio, 0); gpio_free(ak4497->pdn_gpio); @@ -909,7 +910,6 @@ static int ak4497_remove(struct snd_soc_codec *codec) if (gpio_is_valid(ak4497->mute_gpio)) gpio_free(ak4497->mute_gpio); - return 0; } #ifdef CONFIG_PM @@ -955,21 +955,22 @@ static const struct dev_pm_ops ak4497_pm = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; -struct snd_soc_codec_driver rpmsg_codec_dev_ak4497 = { +struct snd_soc_component_driver rpmsg_codec_dev_ak4497 = { .probe = ak4497_probe, .remove = ak4497_remove, - .idle_bias_off = true, .set_bias_level = ak4497_set_bias_level, - - .component_driver = { - .controls = ak4497_snd_controls, - .num_controls = ARRAY_SIZE(ak4497_snd_controls), - .dapm_widgets = ak4497_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), - .dapm_routes = ak4497_intercon, - .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), - }, + .controls = ak4497_snd_controls, + .num_controls = ARRAY_SIZE(ak4497_snd_controls), + .dapm_widgets = ak4497_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), + .dapm_routes = ak4497_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, }; static int rpmsg_ak4497_read(void *context, unsigned int reg, unsigned int *val) @@ -1072,7 +1073,7 @@ static int rpmsg_ak4497_codec_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_register_codec(&pdev->dev, &rpmsg_codec_dev_ak4497, + ret = devm_snd_soc_register_component(&pdev->dev, &rpmsg_codec_dev_ak4497, &rpmsg_ak4497_dai[0], ARRAY_SIZE(rpmsg_ak4497_dai)); if (ret < 0) return ret; @@ -1084,7 +1085,6 @@ static int rpmsg_ak4497_codec_probe(struct platform_device *pdev) static int rpmsg_ak4497_codec_remove(struct platform_device *pdev) { - snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; diff --git a/sound/soc/codecs/rpmsg_ak4497.h b/sound/soc/codecs/rpmsg_ak4497.h new file mode 100644 index 000000000000..33644088cc94 --- /dev/null +++ b/sound/soc/codecs/rpmsg_ak4497.h @@ -0,0 +1,90 @@ +/* + * ak4497.h -- audio driver for ak4497 + * + * Copyright (C) 2016 Asahi Kasei Microdevices Corporation + * Copyright (C) 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. + * + */ +#ifndef _RPMSG_AK4497_H +#define _RPMSG_AK4497_H + +#define AK4497_00_CONTROL1 0x00 +#define AK4497_01_CONTROL2 0x01 +#define AK4497_02_CONTROL3 0x02 +#define AK4497_03_LCHATT 0x03 +#define AK4497_04_RCHATT 0x04 +#define AK4497_05_CONTROL4 0x05 +#define AK4497_06_DSD1 0x06 +#define AK4497_07_CONTROL5 0x07 +#define AK4497_08_SOUNDCONTROL 0x08 +#define AK4497_09_DSD2 0x09 +#define AK4497_0A_CONTROL7 0x0A +#define AK4497_0B_CONTROL8 0x0B +#define AK4497_0C_RESERVED 0x0C +#define AK4497_0D_RESERVED 0x0D +#define AK4497_0E_RESERVED 0x0E +#define AK4497_0F_RESERVED 0x0F +#define AK4497_10_RESERVED 0x10 +#define AK4497_11_RESERVED 0x11 +#define AK4497_12_RESERVED 0x12 +#define AK4497_13_RESERVED 0x13 +#define AK4497_14_RESERVED 0x14 +#define AK4497_15_DFSREAD 0x15 + + +#define AK4497_MAX_REGISTERS (AK4497_15_DFSREAD) + +/* Bitfield Definitions */ + +/* AK4497_00_CONTROL1 (0x00) Fields */ +#define AK4497_DIF 0x0E +#define AK4497_DIF_MSB_MODE (2 << 1) +#define AK4497_DIF_I2S_MODE (3 << 1) +#define AK4497_DIF_32BIT_MODE (4 << 1) + +#define AK4497_DIF_16BIT_LSB (0 << 1) +#define AK4497_DIF_20BIT_LSB (1 << 1) +#define AK4497_DIF_24BIT_MSB (2 << 1) +#define AK4497_DIF_24BIT_I2S (3 << 1) +#define AK4497_DIF_24BIT_LSB (4 << 1) +#define AK4497_DIF_32BIT_LSB (5 << 1) +#define AK4497_DIF_32BIT_MSB (6 << 1) +#define AK4497_DIF_32BIT_I2S (7 << 1) + +/* AK4497_02_CONTROL3 (0x02) Fields */ +#define AK4497_DIF_DSD 0x80 +#define AK4497_DIF_DSD_MODE (1 << 7) + + +/* AK4497_01_CONTROL2 (0x01) Fields */ +/* AK4497_05_CONTROL4 (0x05) Fields */ +#define AK4497_DFS 0x18 +#define AK4497_DFS_48KHZ (0x0 << 3) // 30kHz to 54kHz +#define AK4497_DFS_96KHZ (0x1 << 3) // 54kHz to 108kHz +#define AK4497_DFS_192KHZ (0x2 << 3) // 120kHz to 216kHz +#define AK4497_DFS_384KHZ (0x0 << 3) +#define AK4497_DFS_768KHZ (0x1 << 3) + +#define AK4497_DFS2 0x2 +#define AK4497_DFS2_48KHZ (0x0 << 1) // 30kHz to 216kHz +#define AK4497_DFS2_384KHZ (0x1 << 1) // 384kHz, 768kHz to 108kHz + + +#define AK4497_DSDSEL0 0x1 +#define AK4497_DSDSEL0_2MHZ 0x0 +#define AK4497_DSDSEL0_5MHZ 0x1 +#define AK4497_DSDSEL0_11MHZ 0x0 +#define AK4497_DSDSEL0_22MHZ 0x1 + +#define AK4497_DSDSEL1 0x1 +#define AK4497_DSDSEL1_2MHZ 0x0 +#define AK4497_DSDSEL1_5MHZ 0x0 +#define AK4497_DSDSEL1_11MHZ 0x1 +#define AK4497_DSDSEL1_22MHZ 0x1 + +#endif From b13fc2d9bbcbeb0c43d8cdde43b8113ca4319f06 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 3 Sep 2019 14:57:25 +0800 Subject: [PATCH 53/59] ASoC: rpmsg_cs42xx8: replace codec to component replace codec to component Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_cs42xx8.c | 69 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/sound/soc/codecs/rpmsg_cs42xx8.c b/sound/soc/codecs/rpmsg_cs42xx8.c index 38c3b1a86a93..02bdfb0cfd49 100644 --- a/sound/soc/codecs/rpmsg_cs42xx8.c +++ b/sound/soc/codecs/rpmsg_cs42xx8.c @@ -185,22 +185,22 @@ static const struct cs42xx8_ratios cs42xx8_ratios[] = { { 8, 4096000, 51200000, {1024, 512, 256} }, }; -static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, +static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = codec_dai->codec; - struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); cs42xx8->sysclk = freq; return 0; } -static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int cs42xx8_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) { - struct snd_soc_codec *codec = codec_dai->codec; - struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); u32 val; /* Set DAI format */ @@ -218,7 +218,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM; break; default: - dev_err(codec->dev, "unsupported dai format\n"); + dev_err(component->dev, "unsupported dai format\n"); return -EINVAL; } @@ -238,7 +238,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, cs42xx8->slave_mode[1] = false; break; default: - dev_err(codec->dev, "unsupported master/slave mode\n"); + dev_err(component->dev, "unsupported master/slave mode\n"); return -EINVAL; } } @@ -250,8 +250,8 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; u32 ratio[2]; u32 rate[2]; @@ -279,7 +279,7 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, else if (rate[i] > 100000 && rate[i] < 200000) fm[i] = CS42XX8_FM_QUAD; else { - dev_err(codec->dev, + dev_err(component->dev, "unsupported sample rate or rate combine\n"); return -EINVAL; } @@ -312,7 +312,7 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, } if (i == ARRAY_SIZE(cs42xx8_ratios)) { - dev_err(codec->dev, "unsupported sysclk ratio\n"); + dev_err(component->dev, "unsupported sysclk ratio\n"); return -EINVAL; } @@ -331,9 +331,8 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, static int cs42xx8_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; cs42xx8->rate[tx] = 0; @@ -346,8 +345,8 @@ static int cs42xx8_hw_free(struct snd_pcm_substream *substream, static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); u8 dac_unmute = cs42xx8->tx_channels ? ~((0x1 << cs42xx8->tx_channels) - 1) : 0; @@ -489,14 +488,14 @@ static struct regmap_config rpmsg_cs42xx8_regmap_config = { .reg_write = rpmsg_cs42xx8_write, }; -static int cs42xx8_codec_probe(struct snd_soc_codec *codec) +static int cs42xx8_codec_probe(struct snd_soc_component *component) { - struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct rpmsg_cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); switch (cs42xx8->drvdata->num_adcs) { case 3: - snd_soc_add_codec_controls(codec, cs42xx8_adc3_snd_controls, + snd_soc_add_component_controls(component, cs42xx8_adc3_snd_controls, ARRAY_SIZE(cs42xx8_adc3_snd_controls)); snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets, ARRAY_SIZE(cs42xx8_adc3_dapm_widgets)); @@ -514,18 +513,19 @@ static int cs42xx8_codec_probe(struct snd_soc_codec *codec) return 0; } -static const struct snd_soc_codec_driver cs42xx8_driver = { - .probe = cs42xx8_codec_probe, - .idle_bias_off = true, - - .component_driver = { - .controls = cs42xx8_snd_controls, - .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), - .dapm_widgets = cs42xx8_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), - .dapm_routes = cs42xx8_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), - }, +static const struct snd_soc_component_driver cs42xx8_driver = { + .probe = cs42xx8_codec_probe, + .controls = cs42xx8_snd_controls, + .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), + .dapm_widgets = cs42xx8_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), + .dapm_routes = cs42xx8_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, }; static int rpmsg_cs42xx8_codec_probe(struct platform_device *pdev) @@ -645,7 +645,7 @@ static int rpmsg_cs42xx8_codec_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_request_idle(dev); - ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1); + ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1); if (ret) { dev_err(dev, "failed to register codec:%d\n", ret); goto err_enable; @@ -733,7 +733,6 @@ const struct dev_pm_ops rpmsg_cs42xx8_pm = { static int rpmsg_cs42xx8_codec_remove(struct platform_device *pdev) { - snd_soc_unregister_codec(&pdev->dev); return 0; } From 9c2bac2c8564e587319fc639105697415bb5c26b Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 3 Sep 2019 14:57:49 +0800 Subject: [PATCH 54/59] ASoC: rpmsg_wm8960: replace codec to component replace codec to component Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_wm8960.c | 240 ++++++++++++++++---------------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/sound/soc/codecs/rpmsg_wm8960.c b/sound/soc/codecs/rpmsg_wm8960.c index 644a1eea13ab..d70e5a9b0606 100644 --- a/sound/soc/codecs/rpmsg_wm8960.c +++ b/sound/soc/codecs/rpmsg_wm8960.c @@ -48,7 +48,7 @@ #define WM8960_DRES_MASK 0x30 static bool is_pll_freq_available(unsigned int source, unsigned int target); -static int wm8960_set_pll(struct snd_soc_codec *codec, +static int wm8960_set_pll(struct snd_soc_component *component, unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache @@ -112,7 +112,7 @@ static const struct reg_default wm8960_reg_defaults[] = { struct rpmsg_wm8960_priv { struct clk *mclk; struct regmap *regmap; - int (*set_bias_level)(struct snd_soc_codec *, + int (*set_bias_level)(struct snd_soc_component *, enum snd_soc_bias_level level); struct snd_soc_dapm_widget *lout1; struct snd_soc_dapm_widget *rout1; @@ -174,9 +174,9 @@ static const struct soc_enum wm8960_enum[] = { static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; -static int wm8960_set_deemph(struct snd_soc_codec *codec) +static int wm8960_set_deemph(struct snd_soc_component *component) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); int val, i, best; /* If we're using deemphasis select the nearest available sample @@ -195,17 +195,17 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) val = 0; } - dev_dbg(codec->dev, "Set deemphasis %d\n", val); + dev_dbg(component->dev, "Set deemphasis %d\n", val); - return snd_soc_update_bits(codec, WM8960_DACCTL1, + return snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x6, val); } static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = wm8960->deemph; return 0; @@ -214,8 +214,8 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) @@ -223,7 +223,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, wm8960->deemph = deemph; - return wm8960_set_deemph(codec); + return wm8960_set_deemph(component); } static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); @@ -476,11 +476,11 @@ static const struct snd_soc_dapm_route audio_paths_capless[] = { { "OUT3 VMID", NULL, "Right Output Mixer" }, }; -static int wm8960_add_widgets(struct snd_soc_codec *codec) +static int wm8960_add_widgets(struct snd_soc_component *component) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); struct wm8960_data *pdata = &wm8960->pdata; - struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dapm_widget *w; snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, @@ -510,7 +510,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * list each time to find the desired power state do so now * and save the result. */ - list_for_each_entry(w, &codec->component.card->widgets, list) { + list_for_each_entry(w, &component->card->widgets, list) { if (w->dapm != dapm) continue; if (strcmp(w->name, "LOUT1 PGA") == 0) @@ -527,7 +527,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_component *component = codec_dai->component; u16 iface = 0; /* set master/slave audio interface */ @@ -579,7 +579,7 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, } /* set iface */ - snd_soc_write(codec, WM8960_IFACE1, iface); + snd_soc_component_write(component, WM8960_IFACE1, iface); return 0; } @@ -675,7 +675,7 @@ int wm8960_configure_sysclk(struct rpmsg_wm8960_priv *wm8960, int mclk, * - freq_out = sysclk * sysclk_divs * - 10 * sysclk = bclk * bclk_divs * - * @codec: codec structure + * @component: component structure * @freq_in: input frequency used to derive freq out via PLL * @sysclk_idx: sysclk_divs index for found sysclk * @dac_idx: dac_divs index for found lrclk @@ -687,10 +687,10 @@ int wm8960_configure_sysclk(struct rpmsg_wm8960_priv *wm8960, int mclk, * (@sysclk_idx, @dac_idx, @bclk_idx) dividers */ static -int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, +int wm8960_configure_pll(struct snd_soc_component *component, int freq_in, int *sysclk_idx, int *dac_idx, int *bclk_idx) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); int sysclk, bclk, lrclk, freq_out; int diff, best_freq_out = 0; int i, j, k; @@ -728,26 +728,26 @@ int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, } if (*bclk_idx != -1) - wm8960_set_pll(codec, freq_in, best_freq_out); + wm8960_set_pll(component, freq_in, best_freq_out); return *bclk_idx; } -static int wm8960_configure_clocking(struct snd_soc_codec *codec) +static int wm8960_configure_clocking(struct snd_soc_component *component) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); int freq_out, freq_in; - u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); + u16 iface1 = snd_soc_component_read32(component, WM8960_IFACE1); int i, j, k; int ret; if (!(iface1 & (1<<6))) { - dev_dbg(codec->dev, + dev_dbg(component->dev, "Codec is slave mode, no need to configure clock\n"); return 0; } if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { - dev_err(codec->dev, "No MCLK configured\n"); + dev_err(component->dev, "No MCLK configured\n"); return -EINVAL; } @@ -760,12 +760,12 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { /* disable the PLL and using MCLK to provide sysclk */ - wm8960_set_pll(codec, 0, 0); + wm8960_set_pll(component, 0, 0); freq_out = freq_in; } else if (wm8960->sysclk) { freq_out = wm8960->sysclk; } else { - dev_err(codec->dev, "No SYSCLK configured\n"); + dev_err(component->dev, "No SYSCLK configured\n"); return -EINVAL; } @@ -774,27 +774,27 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) if (ret >= 0) { goto configure_clock; } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { - dev_err(codec->dev, "failed to configure clock\n"); + dev_err(component->dev, "failed to configure clock\n"); return -EINVAL; } } - ret = wm8960_configure_pll(codec, freq_in, &i, &j, &k); + ret = wm8960_configure_pll(component, freq_in, &i, &j, &k); if (ret < 0) { - dev_err(codec->dev, "failed to configure clock via PLL\n"); + dev_err(component->dev, "failed to configure clock via PLL\n"); return -EINVAL; } configure_clock: /* configure sysclk clock */ - snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 3 << 1, i << 1); /* configure frame clock */ - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 3, j << 3); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ - snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); + snd_soc_component_update_bits(component, WM8960_CLOCK2, 0xf, k); return 0; } @@ -803,9 +803,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; + struct snd_soc_component *component = dai->component; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read32(component, WM8960_IFACE1) & 0xfff3; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; @@ -829,8 +829,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, iface |= 0x000c; break; } + /* fall through */ default: - dev_err(codec->dev, "unsupported width %d\n", + dev_err(component->dev, "unsupported width %d\n", params_width(params)); return -EINVAL; } @@ -838,22 +839,22 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ if (tx) { - wm8960_set_deemph(codec); + wm8960_set_deemph(component); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) if (alc_rates[i].rate == params_rate(params)) - snd_soc_update_bits(codec, + snd_soc_component_update_bits(component, WM8960_ADDCTL3, 0x7, alc_rates[i].val); } /* set iface */ - snd_soc_write(codec, WM8960_IFACE1, iface); + snd_soc_component_write(component, WM8960_IFACE1, iface); wm8960->is_stream_in_use[tx] = true; if (!wm8960->is_stream_in_use[!tx]) - return wm8960_configure_clocking(codec); + return wm8960_configure_clocking(component); return 0; } @@ -861,8 +862,8 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, static int wm8960_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; wm8960->is_stream_in_use[tx] = false; @@ -872,20 +873,20 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream, static int wm8960_mute(struct snd_soc_dai *dai, int mute) { - struct snd_soc_codec *codec = dai->codec; + struct snd_soc_component *component = dai->component; if (mute) - snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); + snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0x8); else - snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); + snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0); return 0; } -static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, +static int wm8960_set_bias_level_out3(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - u16 pm2 = snd_soc_read(codec, WM8960_POWER2); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 pm2 = snd_soc_component_read32(component, WM8960_POWER2); int ret; switch (level) { @@ -893,24 +894,24 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (snd_soc_codec_get_bias_level(codec)) { + switch (snd_soc_component_get_bias_level(component)) { case SND_SOC_BIAS_STANDBY: if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to enable MCLK: %d\n", ret); return ret; } } - ret = wm8960_configure_clocking(codec); + ret = wm8960_configure_clocking(component); if (ret) return ret; /* Set VMID to 2x50k */ - snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); + snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: @@ -919,7 +920,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) - wm8960_set_pll(codec, 0, 0); + wm8960_set_pll(component, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); @@ -932,38 +933,38 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { regcache_sync(wm8960->regmap); /* Enable anti-pop features */ - snd_soc_write(codec, WM8960_APOP1, + snd_soc_component_write(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); /* Enable & ramp VMID at 2x50k */ - snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); + snd_soc_component_update_bits(component, WM8960_POWER1, 0x80, 0x80); msleep(100); /* Enable VREF */ - snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF, WM8960_VREF); /* Disable anti-pop features */ - snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); + snd_soc_component_write(component, WM8960_APOP1, WM8960_BUFIOEN); } /* Set VMID to 2x250k */ - snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); + snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x100); break; case SND_SOC_BIAS_OFF: /* Enable anti-pop features */ - snd_soc_write(codec, WM8960_APOP1, + snd_soc_component_write(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); /* Disable VMID and VREF, let them discharge */ - snd_soc_write(codec, WM8960_POWER1, 0); + snd_soc_component_write(component, WM8960_POWER1, 0); msleep(600); break; } @@ -971,11 +972,11 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, return 0; } -static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, +static int wm8960_set_bias_level_capless(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - u16 pm2 = snd_soc_read(codec, WM8960_POWER2); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 pm2 = snd_soc_component_read32(component, WM8960_POWER2); int reg, ret; switch (level) { @@ -983,10 +984,10 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (snd_soc_codec_get_bias_level(codec)) { + switch (snd_soc_component_get_bias_level(component)) { case SND_SOC_BIAS_STANDBY: /* Enable anti pop mode */ - snd_soc_update_bits(codec, WM8960_APOP1, + snd_soc_component_update_bits(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | @@ -1000,20 +1001,20 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, reg |= WM8960_PWR2_ROUT1; if (wm8960->out3 && wm8960->out3->power) reg |= WM8960_PWR2_OUT3; - snd_soc_update_bits(codec, WM8960_POWER2, + snd_soc_component_update_bits(component, WM8960_POWER2, WM8960_PWR2_LOUT1 | WM8960_PWR2_ROUT1 | WM8960_PWR2_OUT3, reg); /* Enable VMID at 2*50k */ - snd_soc_update_bits(codec, WM8960_POWER1, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VMID_MASK, 0x80); /* Ramp */ msleep(100); /* Enable VREF */ - snd_soc_update_bits(codec, WM8960_POWER1, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF, WM8960_VREF); msleep(100); @@ -1021,14 +1022,14 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to enable MCLK: %d\n", ret); return ret; } } - ret = wm8960_configure_clocking(codec); + ret = wm8960_configure_clocking(component); if (ret) return ret; @@ -1040,20 +1041,20 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) - wm8960_set_pll(codec, 0, 0); + wm8960_set_pll(component, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); /* Enable anti-pop mode */ - snd_soc_update_bits(codec, WM8960_APOP1, + snd_soc_component_update_bits(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN); /* Disable VMID and VREF */ - snd_soc_update_bits(codec, WM8960_POWER1, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF | WM8960_VMID_MASK, 0); break; @@ -1066,15 +1067,15 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - switch (snd_soc_codec_get_bias_level(codec)) { + switch (snd_soc_component_get_bias_level(component)) { case SND_SOC_BIAS_PREPARE: /* Disable HP discharge */ - snd_soc_update_bits(codec, WM8960_APOP2, + snd_soc_component_update_bits(component, WM8960_APOP2, WM8960_DISOP | WM8960_DRES_MASK, 0); /* Disable anti-pop features */ - snd_soc_update_bits(codec, WM8960_APOP1, + snd_soc_component_update_bits(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | @@ -1169,7 +1170,7 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } -static int wm8960_set_pll(struct snd_soc_codec *codec, +static int wm8960_set_pll(struct snd_soc_component *component, unsigned int freq_in, unsigned int freq_out) { u16 reg; @@ -1185,29 +1186,29 @@ static int wm8960_set_pll(struct snd_soc_codec *codec, /* Disable the PLL: even if we are changing the frequency the * PLL needs to be disabled while we do so. */ - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); - snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0); + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0); if (!freq_in || !freq_out) return 0; - reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; + reg = snd_soc_component_read32(component, WM8960_PLL1) & ~0x3f; reg |= pll_div.pre_div << 4; reg |= pll_div.n; if (pll_div.k) { reg |= 0x20; - snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); - snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); - snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); + snd_soc_component_write(component, WM8960_PLL2, (pll_div.k >> 16) & 0xff); + snd_soc_component_write(component, WM8960_PLL3, (pll_div.k >> 8) & 0xff); + snd_soc_component_write(component, WM8960_PLL4, pll_div.k & 0xff); } - snd_soc_write(codec, WM8960_PLL1, reg); + snd_soc_component_write(component, WM8960_PLL1, reg); /* Turn it on */ - snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0x1); msleep(250); - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0x1); return 0; } @@ -1215,8 +1216,8 @@ static int wm8960_set_pll(struct snd_soc_codec *codec, static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - struct snd_soc_codec *codec = codec_dai->codec; - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = codec_dai->component; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); wm8960->freq_in = freq_in; @@ -1226,35 +1227,35 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (is_pll_freq_available(freq_in, freq_out)) return -EINVAL; - return wm8960_set_pll(codec, freq_in, freq_out); + return wm8960_set_pll(component, freq_in, freq_out); } static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_component *component = codec_dai->component; u16 reg; switch (div_id) { case WM8960_SYSCLKDIV: - reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; - snd_soc_write(codec, WM8960_CLOCK1, reg | div); + reg = snd_soc_component_read32(component, WM8960_CLOCK1) & 0x1f9; + snd_soc_component_write(component, WM8960_CLOCK1, reg | div); break; case WM8960_DACDIV: - reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; - snd_soc_write(codec, WM8960_CLOCK1, reg | div); + reg = snd_soc_component_read32(component, WM8960_CLOCK1) & 0x1c7; + snd_soc_component_write(component, WM8960_CLOCK1, reg | div); break; case WM8960_OPCLKDIV: - reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; - snd_soc_write(codec, WM8960_PLL1, reg | div); + reg = snd_soc_component_read32(component, WM8960_PLL1) & 0x03f; + snd_soc_component_write(component, WM8960_PLL1, reg | div); break; case WM8960_DCLKDIV: - reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; - snd_soc_write(codec, WM8960_CLOCK2, reg | div); + reg = snd_soc_component_read32(component, WM8960_CLOCK2) & 0x03f; + snd_soc_component_write(component, WM8960_CLOCK2, reg | div); break; case WM8960_TOCLKSEL: - reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; - snd_soc_write(codec, WM8960_ADDCTL1, reg | div); + reg = snd_soc_component_read32(component, WM8960_ADDCTL1) & 0x1fd; + snd_soc_component_write(component, WM8960_ADDCTL1, reg | div); break; default: return -EINVAL; @@ -1263,27 +1264,27 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } -static int wm8960_set_bias_level(struct snd_soc_codec *codec, +static int wm8960_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); - return wm8960->set_bias_level(codec, level); + return wm8960->set_bias_level(component, level); } static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = dai->codec; - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); switch (clk_id) { case WM8960_SYSCLK_MCLK: - snd_soc_update_bits(codec, WM8960_CLOCK1, + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_MCLK); break; case WM8960_SYSCLK_PLL: - snd_soc_update_bits(codec, WM8960_CLOCK1, + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; case WM8960_SYSCLK_AUTO: @@ -1333,9 +1334,9 @@ static struct snd_soc_dai_driver rpmsg_wm8960_codec_dai = { .symmetric_rates = 1, }; -static int rpmsg_wm8960_probe(struct snd_soc_codec *codec) +static int rpmsg_wm8960_probe(struct snd_soc_component *component) { - struct rpmsg_wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct rpmsg_wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); struct wm8960_data *pdata = &wm8960->pdata; if (pdata->capless) @@ -1343,9 +1344,9 @@ static int rpmsg_wm8960_probe(struct snd_soc_codec *codec) else wm8960->set_bias_level = wm8960_set_bias_level_out3; - snd_soc_add_codec_controls(codec, wm8960_snd_controls, + snd_soc_add_component_controls(component, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); - wm8960_add_widgets(codec); + wm8960_add_widgets(component); return 0; } @@ -1393,10 +1394,10 @@ static int rpmsg_wm8960_write(void *context, unsigned int reg, unsigned int val) return 0; } -static struct snd_soc_codec_driver rpmsg_wm8960_codec = { +static const struct snd_soc_component_driver rpmsg_wm8960_component = { .probe = rpmsg_wm8960_probe, .set_bias_level = wm8960_set_bias_level, - .suspend_bias_off = true, + .suspend_bias_off = 1, }; static const struct regmap_config rpmsg_wm8960_regmap = { @@ -1515,15 +1516,14 @@ static int rpmsg_wm8960_codec_probe(struct platform_device *pdev) if (!wm8960->mclk) rpmsg_wm8960_codec_dai.ops = NULL; - ret = snd_soc_register_codec(&pdev->dev, - &rpmsg_wm8960_codec, &rpmsg_wm8960_codec_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, + &rpmsg_wm8960_component, &rpmsg_wm8960_codec_dai, 1); return ret; } static int rpmsg_wm8960_codec_remove(struct platform_device *pdev) { - snd_soc_unregister_codec(&pdev->dev); return 0; } From bc157dae224b3a482b890acb54659456f6a47f15 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 5 Sep 2019 10:51:12 +0800 Subject: [PATCH 55/59] Revert "ASoC: soc-pcm: remove soc_rtdcom_ack()" This reverts commit 18bd7b5ef3a313a9af1314508bca1ed982c05f75. We need the soc_rtdcom_ack() for LPA. Signed-off-by: Shengjiu Wang --- sound/soc/soc-pcm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b600d3eaaf5c..fb0f540696b8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2818,6 +2818,26 @@ static void soc_pcm_private_free(struct snd_pcm *pcm) snd_soc_pcm_component_free(pcm); } +static int soc_rtdcom_ack(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->ack) + continue; + + /* FIXME. it returns 1st ask now */ + return component->driver->ops->ack(substream); + } + + return -EINVAL; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { @@ -2941,6 +2961,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (!ops) continue; + if (ops->ack) + rtd->ops.ack = soc_rtdcom_ack; if (ops->copy_user) rtd->ops.copy_user = snd_soc_pcm_component_copy_user; if (ops->page) From 07386f3836dff0fe673e95f5e146a143ccb6f7bd Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 6 Sep 2019 17:58:52 +0800 Subject: [PATCH 56/59] ASoC: rpmsg_ak4497: ignore suspend with DAPM When suspend, the widget "AK4497 DAC" is powered down then there is no sound output, which is not accepted by LPA definition. so ignore suspend with DAPM, that the widget will not be powered down Signed-off-by: Shengjiu Wang --- sound/soc/codecs/rpmsg_ak4497.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/rpmsg_ak4497.c b/sound/soc/codecs/rpmsg_ak4497.c index 1837dcb3f789..8f5657c57fbc 100644 --- a/sound/soc/codecs/rpmsg_ak4497.c +++ b/sound/soc/codecs/rpmsg_ak4497.c @@ -887,6 +887,9 @@ static int ak4497_probe(struct snd_soc_component *component) gpio_direction_output(ak4497->mute_gpio, 0); } + snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(component), "AK4497 AOUT"); + snd_soc_dapm_ignore_suspend(snd_soc_component_get_dapm(component), "Playback"); + ret = ak4497_init_reg(component); if (ret) return ret; From 6eb375e54d95c5a9fc4ef041de0d4fa14a45babb Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 4 Oct 2019 01:22:12 +0300 Subject: [PATCH 57/59] ASoC: imx-pcm-rpmsg: Fix writecombine/wc build error Signed-off-by: Leonard Crestez --- sound/soc/fsl/imx-pcm-rpmsg.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index f49205a6c41a..005c5442e9e9 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -281,10 +281,10 @@ static int imx_rpmsg_pcm_mmap(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); + return dma_mmap_wc(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); } static void imx_rpmsg_pcm_dma_complete(void *arg) @@ -646,8 +646,8 @@ static int imx_rpmsg_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); + buf->area = dma_alloc_wc(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); if (!buf->area) return -ENOMEM; @@ -671,8 +671,8 @@ static void imx_rpmsg_pcm_free_dma_buffers(struct snd_pcm *pcm) if (!buf->area) continue; - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); + dma_free_wc(pcm->card->dev, buf->bytes, + buf->area, buf->addr); buf->area = NULL; } } From 50b229c2d5a4ce5adcaa204442734d64575f9316 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 5 Nov 2019 15:18:13 +0800 Subject: [PATCH 58/59] ASoC: imx-pcm-rpmsg: Remove the prtd prtd is not needed by this driver. Signed-off-by: Shengjiu Wang --- sound/soc/fsl/imx-pcm-rpmsg.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 005c5442e9e9..8da1345ccd06 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -162,7 +162,6 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - struct dmaengine_pcm_runtime_data *prtd; int cmd; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -196,12 +195,6 @@ static int imx_rpmsg_pcm_open(struct snd_pcm_substream *substream) i2s_info->send_message(rpmsg, i2s_info); - prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); - if (!prtd) - return -ENOMEM; - - substream->runtime->private_data = prtd; - ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) @@ -226,8 +219,6 @@ static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(cpu_dai->dev); struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; struct i2s_rpmsg *rpmsg; - struct dmaengine_pcm_runtime_data *prtd = - substream->runtime->private_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rpmsg = &i2s_info->rpmsg[I2S_TX_CLOSE]; @@ -243,8 +234,6 @@ static int imx_rpmsg_pcm_close(struct snd_pcm_substream *substream) del_timer(&i2s_info->stream_timer[substream->stream].timer); - kfree(prtd); - rtd->dai_link->ignore_suspend = 0; if (i2s_info->msg_drop_count[substream->stream]) From dfb874e95e3aa46b43b8940b6e921cf753eda947 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 29 Nov 2019 12:03:04 +0800 Subject: [PATCH 59/59] LF-215: ASoC: fsl_rpmsg_i2s: Enable WQ_FREEZABLE for workqueue In the end of playback, when the suspend happen, there will be error in m4 side. RTM_SaiSdmaAdapter_SetParam: Tx in wrong state 2! SRTM_SaiSdmaAdapter_SetBuf: Tx in wrong state 2! SRTM_SaiSdmaAdapter_Start: Tx in wrong state 2! The reason is that the I2S_TX_TERMINATE happen in the middle of I2S_TX_SUSPEND and I2S_TX_RESUME, this sequence is not allowed. So we make the rpmsg message workqueue enter freeze in suspend to avoid such issue. that the command sequence will be I2S_TX_SUSPEND->I2S_TX_RESUME->I2S_TX_TERMINATE Signed-off-by: Shengjiu Wang --- sound/soc/fsl/fsl_rpmsg_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c index 4ba151186681..f392817dd3fc 100644 --- a/sound/soc/fsl/fsl_rpmsg_i2s.c +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -222,7 +222,7 @@ static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) audioindex = 0; /* Setup work queue */ - i2s_info->rpmsg_wq = create_singlethread_workqueue("rpmsg_i2s"); + i2s_info->rpmsg_wq = alloc_ordered_workqueue("rpmsg_i2s", WQ_HIGHPRI | WQ_UNBOUND | WQ_FREEZABLE); if (i2s_info->rpmsg_wq == NULL) { dev_err(&pdev->dev, "workqueue create failed\n"); return -ENOMEM;