cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fsl_rpmsg.c (8346B)


      1// SPDX-License-Identifier: GPL-2.0+
      2// Copyright 2018-2021 NXP
      3
      4#include <linux/clk.h>
      5#include <linux/clk-provider.h>
      6#include <linux/delay.h>
      7#include <linux/dmaengine.h>
      8#include <linux/module.h>
      9#include <linux/of_device.h>
     10#include <linux/of_address.h>
     11#include <linux/pm_runtime.h>
     12#include <linux/rpmsg.h>
     13#include <linux/slab.h>
     14#include <sound/core.h>
     15#include <sound/dmaengine_pcm.h>
     16#include <sound/pcm_params.h>
     17
     18#include "fsl_rpmsg.h"
     19#include "imx-pcm.h"
     20
     21#define FSL_RPMSG_RATES        (SNDRV_PCM_RATE_8000 | \
     22				SNDRV_PCM_RATE_16000 | \
     23				SNDRV_PCM_RATE_48000)
     24#define FSL_RPMSG_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
     25
     26/* 192kHz/32bit/2ch/60s size is 0x574e00 */
     27#define LPA_LARGE_BUFFER_SIZE  (0x6000000)
     28
     29static const unsigned int fsl_rpmsg_rates[] = {
     30	8000, 11025, 16000, 22050, 44100,
     31	32000, 48000, 96000, 88200, 176400, 192000,
     32	352800, 384000, 705600, 768000, 1411200, 2822400,
     33};
     34
     35static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = {
     36	.count = ARRAY_SIZE(fsl_rpmsg_rates),
     37	.list = fsl_rpmsg_rates,
     38};
     39
     40static int fsl_rpmsg_hw_params(struct snd_pcm_substream *substream,
     41			       struct snd_pcm_hw_params *params,
     42			       struct snd_soc_dai *dai)
     43{
     44	struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
     45	struct clk *p = rpmsg->mclk, *pll = NULL, *npll = NULL;
     46	u64 rate = params_rate(params);
     47	int ret = 0;
     48
     49	/* Get current pll parent */
     50	while (p && rpmsg->pll8k && rpmsg->pll11k) {
     51		struct clk *pp = clk_get_parent(p);
     52
     53		if (clk_is_match(pp, rpmsg->pll8k) ||
     54		    clk_is_match(pp, rpmsg->pll11k)) {
     55			pll = pp;
     56			break;
     57		}
     58		p = pp;
     59	}
     60
     61	/* Switch to another pll parent if needed. */
     62	if (pll) {
     63		npll = (do_div(rate, 8000) ? rpmsg->pll11k : rpmsg->pll8k);
     64		if (!clk_is_match(pll, npll)) {
     65			ret = clk_set_parent(p, npll);
     66			if (ret < 0)
     67				dev_warn(dai->dev, "failed to set parent %s: %d\n",
     68					 __clk_get_name(npll), ret);
     69		}
     70	}
     71
     72	if (!(rpmsg->mclk_streams & BIT(substream->stream))) {
     73		ret = clk_prepare_enable(rpmsg->mclk);
     74		if (ret) {
     75			dev_err(dai->dev, "failed to enable mclk: %d\n", ret);
     76			return ret;
     77		}
     78
     79		rpmsg->mclk_streams |= BIT(substream->stream);
     80	}
     81
     82	return ret;
     83}
     84
     85static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
     86			     struct snd_soc_dai *dai)
     87{
     88	struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
     89
     90	if (rpmsg->mclk_streams & BIT(substream->stream)) {
     91		clk_disable_unprepare(rpmsg->mclk);
     92		rpmsg->mclk_streams &= ~BIT(substream->stream);
     93	}
     94
     95	return 0;
     96}
     97
     98static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
     99			     struct snd_soc_dai *cpu_dai)
    100{
    101	int ret;
    102
    103	ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
    104					 SNDRV_PCM_HW_PARAM_RATE,
    105					 &fsl_rpmsg_rate_constraints);
    106
    107	return ret;
    108}
    109
    110static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
    111	.startup	= fsl_rpmsg_startup,
    112	.hw_params      = fsl_rpmsg_hw_params,
    113	.hw_free        = fsl_rpmsg_hw_free,
    114};
    115
    116static struct snd_soc_dai_driver fsl_rpmsg_dai = {
    117	.playback = {
    118		.stream_name = "CPU-Playback",
    119		.channels_min = 2,
    120		.channels_max = 2,
    121		.rates = SNDRV_PCM_RATE_KNOT,
    122		.formats = FSL_RPMSG_FORMATS,
    123	},
    124	.capture = {
    125		.stream_name = "CPU-Capture",
    126		.channels_min = 2,
    127		.channels_max = 2,
    128		.rates = SNDRV_PCM_RATE_KNOT,
    129		.formats = FSL_RPMSG_FORMATS,
    130	},
    131	.symmetric_rate        = 1,
    132	.symmetric_channels    = 1,
    133	.symmetric_sample_bits = 1,
    134	.ops = &fsl_rpmsg_dai_ops,
    135};
    136
    137static const struct snd_soc_component_driver fsl_component = {
    138	.name           = "fsl-rpmsg",
    139};
    140
    141static const struct fsl_rpmsg_soc_data imx7ulp_data = {
    142	.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
    143		 SNDRV_PCM_RATE_48000,
    144	.formats = SNDRV_PCM_FMTBIT_S16_LE,
    145};
    146
    147static const struct fsl_rpmsg_soc_data imx8mm_data = {
    148	.rates = SNDRV_PCM_RATE_KNOT,
    149	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
    150		   SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U8 |
    151		   SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE,
    152};
    153
    154static const struct fsl_rpmsg_soc_data imx8mn_data = {
    155	.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
    156		 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
    157		 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
    158		 SNDRV_PCM_RATE_192000,
    159	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
    160		   SNDRV_PCM_FMTBIT_S32_LE,
    161};
    162
    163static const struct fsl_rpmsg_soc_data imx8mp_data = {
    164	.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
    165		 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
    166		 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
    167		 SNDRV_PCM_RATE_192000,
    168	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
    169		   SNDRV_PCM_FMTBIT_S32_LE,
    170};
    171
    172static const struct of_device_id fsl_rpmsg_ids[] = {
    173	{ .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data},
    174	{ .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data},
    175	{ .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data},
    176	{ .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data},
    177	{ .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data},
    178	{ /* sentinel */ }
    179};
    180MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
    181
    182static int fsl_rpmsg_probe(struct platform_device *pdev)
    183{
    184	struct device_node *np = pdev->dev.of_node;
    185	struct fsl_rpmsg *rpmsg;
    186	int ret;
    187
    188	rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
    189	if (!rpmsg)
    190		return -ENOMEM;
    191
    192	rpmsg->soc_data = of_device_get_match_data(&pdev->dev);
    193
    194	fsl_rpmsg_dai.playback.rates = rpmsg->soc_data->rates;
    195	fsl_rpmsg_dai.capture.rates = rpmsg->soc_data->rates;
    196	fsl_rpmsg_dai.playback.formats = rpmsg->soc_data->formats;
    197	fsl_rpmsg_dai.capture.formats = rpmsg->soc_data->formats;
    198
    199	if (of_property_read_bool(np, "fsl,enable-lpa")) {
    200		rpmsg->enable_lpa = 1;
    201		rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE;
    202	} else {
    203		rpmsg->buffer_size = IMX_DEFAULT_DMABUF_SIZE;
    204	}
    205
    206	/* Get the optional clocks */
    207	rpmsg->ipg = devm_clk_get_optional(&pdev->dev, "ipg");
    208	if (IS_ERR(rpmsg->ipg))
    209		return PTR_ERR(rpmsg->ipg);
    210
    211	rpmsg->mclk = devm_clk_get_optional(&pdev->dev, "mclk");
    212	if (IS_ERR(rpmsg->mclk))
    213		return PTR_ERR(rpmsg->mclk);
    214
    215	rpmsg->dma = devm_clk_get_optional(&pdev->dev, "dma");
    216	if (IS_ERR(rpmsg->dma))
    217		return PTR_ERR(rpmsg->dma);
    218
    219	rpmsg->pll8k = devm_clk_get_optional(&pdev->dev, "pll8k");
    220	if (IS_ERR(rpmsg->pll8k))
    221		return PTR_ERR(rpmsg->pll8k);
    222
    223	rpmsg->pll11k = devm_clk_get_optional(&pdev->dev, "pll11k");
    224	if (IS_ERR(rpmsg->pll11k))
    225		return PTR_ERR(rpmsg->pll11k);
    226
    227	platform_set_drvdata(pdev, rpmsg);
    228	pm_runtime_enable(&pdev->dev);
    229
    230	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
    231					      &fsl_rpmsg_dai, 1);
    232	if (ret)
    233		return ret;
    234
    235	rpmsg->card_pdev = platform_device_register_data(&pdev->dev,
    236							 "imx-audio-rpmsg",
    237							 PLATFORM_DEVID_NONE,
    238							 NULL,
    239							 0);
    240	if (IS_ERR(rpmsg->card_pdev)) {
    241		dev_err(&pdev->dev, "failed to register rpmsg card\n");
    242		ret = PTR_ERR(rpmsg->card_pdev);
    243		return ret;
    244	}
    245
    246	return 0;
    247}
    248
    249static int fsl_rpmsg_remove(struct platform_device *pdev)
    250{
    251	struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);
    252
    253	if (rpmsg->card_pdev)
    254		platform_device_unregister(rpmsg->card_pdev);
    255
    256	return 0;
    257}
    258
    259#ifdef CONFIG_PM
    260static int fsl_rpmsg_runtime_resume(struct device *dev)
    261{
    262	struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
    263	int ret;
    264
    265	ret = clk_prepare_enable(rpmsg->ipg);
    266	if (ret) {
    267		dev_err(dev, "failed to enable ipg clock: %d\n", ret);
    268		goto ipg_err;
    269	}
    270
    271	ret = clk_prepare_enable(rpmsg->dma);
    272	if (ret) {
    273		dev_err(dev, "Failed to enable dma clock %d\n", ret);
    274		goto dma_err;
    275	}
    276
    277	return 0;
    278
    279dma_err:
    280	clk_disable_unprepare(rpmsg->ipg);
    281ipg_err:
    282	return ret;
    283}
    284
    285static int fsl_rpmsg_runtime_suspend(struct device *dev)
    286{
    287	struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
    288
    289	clk_disable_unprepare(rpmsg->dma);
    290	clk_disable_unprepare(rpmsg->ipg);
    291
    292	return 0;
    293}
    294#endif
    295
    296static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
    297	SET_RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend,
    298			   fsl_rpmsg_runtime_resume,
    299			   NULL)
    300};
    301
    302static struct platform_driver fsl_rpmsg_driver = {
    303	.probe  = fsl_rpmsg_probe,
    304	.remove = fsl_rpmsg_remove,
    305	.driver = {
    306		.name = "fsl_rpmsg",
    307		.pm = &fsl_rpmsg_pm_ops,
    308		.of_match_table = fsl_rpmsg_ids,
    309	},
    310};
    311module_platform_driver(fsl_rpmsg_driver);
    312
    313MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
    314MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
    315MODULE_ALIAS("platform:fsl_rpmsg");
    316MODULE_LICENSE("GPL");