linux/sound/soc/fsl/fsl_rpmsg.c
<<
>>
Prefs
   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 of_device_id fsl_rpmsg_ids[] = {
 142        { .compatible = "fsl,imx7ulp-rpmsg-audio"},
 143        { .compatible = "fsl,imx8mm-rpmsg-audio"},
 144        { .compatible = "fsl,imx8mn-rpmsg-audio"},
 145        { .compatible = "fsl,imx8mp-rpmsg-audio"},
 146        { /* sentinel */ }
 147};
 148MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
 149
 150static int fsl_rpmsg_probe(struct platform_device *pdev)
 151{
 152        struct device_node *np = pdev->dev.of_node;
 153        struct fsl_rpmsg *rpmsg;
 154        int ret;
 155
 156        rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
 157        if (!rpmsg)
 158                return -ENOMEM;
 159
 160        if (of_property_read_bool(np, "fsl,enable-lpa")) {
 161                rpmsg->enable_lpa = 1;
 162                rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE;
 163        } else {
 164                rpmsg->buffer_size = IMX_DEFAULT_DMABUF_SIZE;
 165        }
 166
 167        /* Get the optional clocks */
 168        rpmsg->ipg = devm_clk_get(&pdev->dev, "ipg");
 169        if (IS_ERR(rpmsg->ipg))
 170                rpmsg->ipg = NULL;
 171
 172        rpmsg->mclk = devm_clk_get(&pdev->dev, "mclk");
 173        if (IS_ERR(rpmsg->mclk))
 174                rpmsg->mclk = NULL;
 175
 176        rpmsg->dma = devm_clk_get(&pdev->dev, "dma");
 177        if (IS_ERR(rpmsg->dma))
 178                rpmsg->dma = NULL;
 179
 180        rpmsg->pll8k = devm_clk_get(&pdev->dev, "pll8k");
 181        if (IS_ERR(rpmsg->pll8k))
 182                rpmsg->pll8k = NULL;
 183
 184        rpmsg->pll11k = devm_clk_get(&pdev->dev, "pll11k");
 185        if (IS_ERR(rpmsg->pll11k))
 186                rpmsg->pll11k = NULL;
 187
 188        platform_set_drvdata(pdev, rpmsg);
 189        pm_runtime_enable(&pdev->dev);
 190
 191        ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
 192                                              &fsl_rpmsg_dai, 1);
 193        if (ret)
 194                return ret;
 195
 196        rpmsg->card_pdev = platform_device_register_data(&pdev->dev,
 197                                                         "imx-audio-rpmsg",
 198                                                         PLATFORM_DEVID_NONE,
 199                                                         NULL,
 200                                                         0);
 201        if (IS_ERR(rpmsg->card_pdev)) {
 202                dev_err(&pdev->dev, "failed to register rpmsg card\n");
 203                ret = PTR_ERR(rpmsg->card_pdev);
 204                return ret;
 205        }
 206
 207        return 0;
 208}
 209
 210static int fsl_rpmsg_remove(struct platform_device *pdev)
 211{
 212        struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);
 213
 214        if (rpmsg->card_pdev)
 215                platform_device_unregister(rpmsg->card_pdev);
 216
 217        return 0;
 218}
 219
 220#ifdef CONFIG_PM
 221static int fsl_rpmsg_runtime_resume(struct device *dev)
 222{
 223        struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
 224        int ret;
 225
 226        ret = clk_prepare_enable(rpmsg->ipg);
 227        if (ret) {
 228                dev_err(dev, "failed to enable ipg clock: %d\n", ret);
 229                goto ipg_err;
 230        }
 231
 232        ret = clk_prepare_enable(rpmsg->dma);
 233        if (ret) {
 234                dev_err(dev, "Failed to enable dma clock %d\n", ret);
 235                goto dma_err;
 236        }
 237
 238        return 0;
 239
 240dma_err:
 241        clk_disable_unprepare(rpmsg->ipg);
 242ipg_err:
 243        return ret;
 244}
 245
 246static int fsl_rpmsg_runtime_suspend(struct device *dev)
 247{
 248        struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
 249
 250        clk_disable_unprepare(rpmsg->dma);
 251        clk_disable_unprepare(rpmsg->ipg);
 252
 253        return 0;
 254}
 255#endif
 256
 257static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
 258        SET_RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend,
 259                           fsl_rpmsg_runtime_resume,
 260                           NULL)
 261        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 262                                pm_runtime_force_resume)
 263};
 264
 265static struct platform_driver fsl_rpmsg_driver = {
 266        .probe  = fsl_rpmsg_probe,
 267        .remove = fsl_rpmsg_remove,
 268        .driver = {
 269                .name = "fsl_rpmsg",
 270                .pm = &fsl_rpmsg_pm_ops,
 271                .of_match_table = fsl_rpmsg_ids,
 272        },
 273};
 274module_platform_driver(fsl_rpmsg_driver);
 275
 276MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
 277MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
 278MODULE_ALIAS("platform:fsl_rpmsg");
 279MODULE_LICENSE("GPL");
 280