linux/sound/soc/fsl/fsl_aud2htx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2// Copyright 2020 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/regmap.h>
  13#include <linux/slab.h>
  14#include <linux/time.h>
  15#include <linux/pm_qos.h>
  16#include <sound/core.h>
  17#include <sound/dmaengine_pcm.h>
  18#include <sound/pcm_params.h>
  19#include <linux/dma-mapping.h>
  20
  21#include "fsl_aud2htx.h"
  22#include "imx-pcm.h"
  23
  24static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd,
  25                               struct snd_soc_dai *dai)
  26{
  27        struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai);
  28
  29        switch (cmd) {
  30        case SNDRV_PCM_TRIGGER_START:
  31        case SNDRV_PCM_TRIGGER_RESUME:
  32        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  33                regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
  34                                   AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN);
  35                regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
  36                                   AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE);
  37                break;
  38        case SNDRV_PCM_TRIGGER_SUSPEND:
  39        case SNDRV_PCM_TRIGGER_STOP:
  40        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  41                regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
  42                                   AUD2HTX_CTRE_DE, 0);
  43                regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL,
  44                                   AUD2HTX_CTRL_EN, 0);
  45                break;
  46        default:
  47                return -EINVAL;
  48        }
  49        return 0;
  50}
  51
  52static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = {
  53        .trigger        = fsl_aud2htx_trigger,
  54};
  55
  56static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai)
  57{
  58        struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev);
  59
  60        /* DMA request when number of entries < WTMK_LOW */
  61        regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
  62                           AUD2HTX_CTRE_DT_MASK, 0);
  63
  64        /* Disable interrupts*/
  65        regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK,
  66                           AUD2HTX_WM_HIGH_IRQ_MASK |
  67                           AUD2HTX_WM_LOW_IRQ_MASK |
  68                           AUD2HTX_OVF_MASK,
  69                           AUD2HTX_WM_HIGH_IRQ_MASK |
  70                           AUD2HTX_WM_LOW_IRQ_MASK |
  71                           AUD2HTX_OVF_MASK);
  72
  73        /* Configure watermark */
  74        regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
  75                           AUD2HTX_CTRE_WL_MASK,
  76                           AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT);
  77        regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT,
  78                           AUD2HTX_CTRE_WH_MASK,
  79                           AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT);
  80
  81        snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx,
  82                                  &aud2htx->dma_params_rx);
  83
  84        return 0;
  85}
  86
  87static struct snd_soc_dai_driver fsl_aud2htx_dai = {
  88        .probe = fsl_aud2htx_dai_probe,
  89        .playback = {
  90                .stream_name = "CPU-Playback",
  91                .channels_min = 1,
  92                .channels_max = 8,
  93                .rates = SNDRV_PCM_RATE_32000 |
  94                         SNDRV_PCM_RATE_44100 |
  95                         SNDRV_PCM_RATE_48000 |
  96                         SNDRV_PCM_RATE_88200 |
  97                         SNDRV_PCM_RATE_96000 |
  98                         SNDRV_PCM_RATE_176400 |
  99                         SNDRV_PCM_RATE_192000,
 100                .formats = FSL_AUD2HTX_FORMATS,
 101        },
 102        .ops = &fsl_aud2htx_dai_ops,
 103};
 104
 105static const struct snd_soc_component_driver fsl_aud2htx_component = {
 106        .name   = "fsl-aud2htx",
 107};
 108
 109static const struct reg_default fsl_aud2htx_reg_defaults[] = {
 110        {AUD2HTX_CTRL,          0x00000000},
 111        {AUD2HTX_CTRL_EXT,      0x00000000},
 112        {AUD2HTX_WR,            0x00000000},
 113        {AUD2HTX_STATUS,        0x00000000},
 114        {AUD2HTX_IRQ_NOMASK,    0x00000000},
 115        {AUD2HTX_IRQ_MASKED,    0x00000000},
 116        {AUD2HTX_IRQ_MASK,      0x00000000},
 117};
 118
 119static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg)
 120{
 121        switch (reg) {
 122        case AUD2HTX_CTRL:
 123        case AUD2HTX_CTRL_EXT:
 124        case AUD2HTX_STATUS:
 125        case AUD2HTX_IRQ_NOMASK:
 126        case AUD2HTX_IRQ_MASKED:
 127        case AUD2HTX_IRQ_MASK:
 128                return true;
 129        default:
 130                return false;
 131        }
 132}
 133
 134static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg)
 135{
 136        switch (reg) {
 137        case AUD2HTX_CTRL:
 138        case AUD2HTX_CTRL_EXT:
 139        case AUD2HTX_WR:
 140        case AUD2HTX_IRQ_NOMASK:
 141        case AUD2HTX_IRQ_MASKED:
 142        case AUD2HTX_IRQ_MASK:
 143                return true;
 144        default:
 145                return false;
 146        }
 147}
 148
 149static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg)
 150{
 151        switch (reg) {
 152        case AUD2HTX_STATUS:
 153        case AUD2HTX_IRQ_NOMASK:
 154        case AUD2HTX_IRQ_MASKED:
 155                return true;
 156        default:
 157                return false;
 158        }
 159}
 160
 161static const struct regmap_config fsl_aud2htx_regmap_config = {
 162        .reg_bits = 32,
 163        .reg_stride = 4,
 164        .val_bits = 32,
 165
 166        .max_register = AUD2HTX_IRQ_MASK,
 167        .reg_defaults = fsl_aud2htx_reg_defaults,
 168        .num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults),
 169        .readable_reg = fsl_aud2htx_readable_reg,
 170        .volatile_reg = fsl_aud2htx_volatile_reg,
 171        .writeable_reg = fsl_aud2htx_writeable_reg,
 172        .cache_type = REGCACHE_RBTREE,
 173};
 174
 175static const struct of_device_id fsl_aud2htx_dt_ids[] = {
 176        { .compatible = "fsl,imx8mp-aud2htx",},
 177        {}
 178};
 179MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids);
 180
 181static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id)
 182{
 183        return IRQ_HANDLED;
 184}
 185
 186static int fsl_aud2htx_probe(struct platform_device *pdev)
 187{
 188        struct fsl_aud2htx *aud2htx;
 189        struct resource *res;
 190        void __iomem *regs;
 191        int ret, irq;
 192
 193        aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL);
 194        if (!aud2htx)
 195                return -ENOMEM;
 196
 197        aud2htx->pdev = pdev;
 198
 199        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 200        regs = devm_ioremap_resource(&pdev->dev, res);
 201        if (IS_ERR(regs)) {
 202                dev_err(&pdev->dev, "failed ioremap\n");
 203                return PTR_ERR(regs);
 204        }
 205
 206        aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
 207                                                &fsl_aud2htx_regmap_config);
 208        if (IS_ERR(aud2htx->regmap)) {
 209                dev_err(&pdev->dev, "failed to init regmap");
 210                return PTR_ERR(aud2htx->regmap);
 211        }
 212
 213        irq = platform_get_irq(pdev, 0);
 214        if (irq < 0)
 215                return irq;
 216
 217        ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0,
 218                               dev_name(&pdev->dev), aud2htx);
 219        if (ret) {
 220                dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
 221                return ret;
 222        }
 223
 224        aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus");
 225        if (IS_ERR(aud2htx->bus_clk)) {
 226                dev_err(&pdev->dev, "failed to get mem clock\n");
 227                return PTR_ERR(aud2htx->bus_clk);
 228        }
 229
 230        aud2htx->dma_params_tx.chan_name = "tx";
 231        aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST;
 232        aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR;
 233
 234        platform_set_drvdata(pdev, aud2htx);
 235        pm_runtime_enable(&pdev->dev);
 236
 237        regcache_cache_only(aud2htx->regmap, true);
 238
 239        ret = devm_snd_soc_register_component(&pdev->dev,
 240                                              &fsl_aud2htx_component,
 241                                              &fsl_aud2htx_dai, 1);
 242        if (ret) {
 243                dev_err(&pdev->dev, "failed to register ASoC DAI\n");
 244                return ret;
 245        }
 246
 247        ret = imx_pcm_dma_init(pdev, IMX_DEFAULT_DMABUF_SIZE);
 248        if (ret)
 249                dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
 250
 251        return ret;
 252}
 253
 254static int fsl_aud2htx_remove(struct platform_device *pdev)
 255{
 256        pm_runtime_disable(&pdev->dev);
 257
 258        return 0;
 259}
 260
 261static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev)
 262{
 263        struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
 264
 265        regcache_cache_only(aud2htx->regmap, true);
 266        clk_disable_unprepare(aud2htx->bus_clk);
 267
 268        return 0;
 269}
 270
 271static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev)
 272{
 273        struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
 274        int ret;
 275
 276        ret = clk_prepare_enable(aud2htx->bus_clk);
 277        if (ret)
 278                return ret;
 279
 280        regcache_cache_only(aud2htx->regmap, false);
 281        regcache_mark_dirty(aud2htx->regmap);
 282        regcache_sync(aud2htx->regmap);
 283
 284        return 0;
 285}
 286
 287static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
 288        SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend,
 289                           fsl_aud2htx_runtime_resume,
 290                           NULL)
 291        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 292                                pm_runtime_force_resume)
 293};
 294
 295static struct platform_driver fsl_aud2htx_driver = {
 296        .probe = fsl_aud2htx_probe,
 297        .remove = fsl_aud2htx_remove,
 298        .driver = {
 299                .name = "fsl-aud2htx",
 300                .pm = &fsl_aud2htx_pm_ops,
 301                .of_match_table = fsl_aud2htx_dt_ids,
 302        },
 303};
 304module_platform_driver(fsl_aud2htx_driver);
 305
 306MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
 307MODULE_DESCRIPTION("NXP AUD2HTX driver");
 308MODULE_LICENSE("GPL v2");
 309