linux/sound/soc/xilinx/xlnx_spdif.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Xilinx ASoC SPDIF audio support
   4//
   5// Copyright (C) 2018 Xilinx, Inc.
   6//
   7// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
   8//
   9
  10#include <linux/clk.h>
  11#include <linux/io.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/of_platform.h>
  15#include <linux/platform_device.h>
  16#include <sound/pcm_params.h>
  17#include <sound/soc.h>
  18
  19#define XLNX_SPDIF_RATES \
  20        (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
  21        SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
  22        SNDRV_PCM_RATE_192000)
  23
  24#define XLNX_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
  25
  26#define XSPDIF_IRQ_STS_REG              0x20
  27#define XSPDIF_IRQ_STS_CH_STS_MASK      BIT(5)
  28#define XSPDIF_IRQ_ENABLE_REG           0x28
  29#define XSPDIF_SOFT_RESET_REG           0x40
  30#define XSPDIF_SOFT_RESET_VAL           0xA
  31#define XSPDIF_CONTROL_REG              0x44
  32#define XSPDIF_CONTROL_ENABLE_MASK      BIT(0)
  33#define XSPDIF_CONTROL_FIFO_FLUSH_MASK  BIT(1)
  34#define XSPDIF_CONTROL_CLK_CFG_MASK     GENMASK(5, 2)
  35#define XSPDIF_CONTROL_CLK_CFG_SHIFT    2
  36#define XSPDIF_CHAN_0_STS_REG           0x4C
  37#define XSPDIF_GLOBAL_IRQ_REG           0x1C
  38#define XSPDIF_GLOBAL_IRQ_ENABLE_MASK   BIT(31)
  39#define XSPDIF_CH_A_USER_DATA_REG_0     0x64
  40
  41#define XSPDIF_MAX_CHANNELS             2
  42#define XSPDIF_AES_SAMPLE_WIDTH         32
  43#define XSPDIF_CH_STS_UPDATE_TIMEOUT    40
  44
  45enum {
  46        CLK_DIV_BY_4,
  47        CLK_DIV_BY_8,
  48        CLK_DIV_BY_16,
  49        CLK_DIV_BY_24,
  50        CLK_DIV_BY_32,
  51        CLK_DIV_BY_48,
  52        CLK_DIV_BY_64,
  53};
  54
  55struct spdif_dev_data {
  56        wait_queue_head_t chsts_q;
  57        void __iomem *base;
  58        struct clk *axi_clk;
  59        struct clk *axis_clk;
  60        struct clk *aud_clk;
  61        u32 mode;
  62        unsigned long aclk;
  63        bool rx_chsts_updated;
  64};
  65
  66static irqreturn_t xlnx_spdifrx_irq_handler(int irq, void *arg)
  67{
  68        u32 val;
  69        struct spdif_dev_data *ctx = arg;
  70
  71        val = ioread32(ctx->base + XSPDIF_IRQ_STS_REG);
  72        if (val & XSPDIF_IRQ_STS_CH_STS_MASK) {
  73                writel(val & XSPDIF_IRQ_STS_CH_STS_MASK,
  74                       ctx->base + XSPDIF_IRQ_STS_REG);
  75                val = ioread32(ctx->base +
  76                               XSPDIF_IRQ_ENABLE_REG);
  77                writel(val & ~XSPDIF_IRQ_STS_CH_STS_MASK,
  78                       ctx->base + XSPDIF_IRQ_ENABLE_REG);
  79
  80                ctx->rx_chsts_updated = true;
  81                wake_up_interruptible(&ctx->chsts_q);
  82                return IRQ_HANDLED;
  83        }
  84
  85        return IRQ_NONE;
  86}
  87
  88static int xlnx_spdif_startup(struct snd_pcm_substream *substream,
  89                              struct snd_soc_dai *dai)
  90{
  91        u32 val;
  92        struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev);
  93
  94        val = ioread32(ctx->base + XSPDIF_CONTROL_REG);
  95        val |= XSPDIF_CONTROL_FIFO_FLUSH_MASK;
  96        writel(val, ctx->base + XSPDIF_CONTROL_REG);
  97
  98        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  99                writel(XSPDIF_IRQ_STS_CH_STS_MASK,
 100                       ctx->base + XSPDIF_IRQ_ENABLE_REG);
 101                writel(XSPDIF_GLOBAL_IRQ_ENABLE_MASK,
 102                       ctx->base + XSPDIF_GLOBAL_IRQ_REG);
 103        }
 104
 105        return 0;
 106}
 107
 108static void xlnx_spdif_shutdown(struct snd_pcm_substream *substream,
 109                                struct snd_soc_dai *dai)
 110{
 111        struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev);
 112
 113        writel(XSPDIF_SOFT_RESET_VAL, ctx->base + XSPDIF_SOFT_RESET_REG);
 114}
 115
 116static int xlnx_spdif_hw_params(struct snd_pcm_substream *substream,
 117                                struct snd_pcm_hw_params *params,
 118                                struct snd_soc_dai *dai)
 119{
 120        u32 val, clk_div, clk_cfg;
 121        struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev);
 122
 123        ctx->aclk = clk_get_rate(ctx->aud_clk);
 124        clk_div = DIV_ROUND_CLOSEST(ctx->aclk, XSPDIF_MAX_CHANNELS *
 125                                    XSPDIF_AES_SAMPLE_WIDTH *
 126                                    params_rate(params));
 127
 128        switch (clk_div) {
 129        case 4:
 130                clk_cfg = CLK_DIV_BY_4;
 131                break;
 132        case 8:
 133                clk_cfg = CLK_DIV_BY_8;
 134                break;
 135        case 16:
 136                clk_cfg = CLK_DIV_BY_16;
 137                break;
 138        case 24:
 139                clk_cfg = CLK_DIV_BY_24;
 140                break;
 141        case 32:
 142                clk_cfg = CLK_DIV_BY_32;
 143                break;
 144        case 48:
 145                clk_cfg = CLK_DIV_BY_48;
 146                break;
 147        case 64:
 148                clk_cfg = CLK_DIV_BY_64;
 149                break;
 150        default:
 151                return -EINVAL;
 152        }
 153
 154        val = ioread32(ctx->base + XSPDIF_CONTROL_REG);
 155        val &= ~XSPDIF_CONTROL_CLK_CFG_MASK;
 156        val |= clk_cfg << XSPDIF_CONTROL_CLK_CFG_SHIFT;
 157        writel(val, ctx->base + XSPDIF_CONTROL_REG);
 158
 159        return 0;
 160}
 161
 162static int rx_stream_detect(struct snd_soc_dai *dai)
 163{
 164        int err;
 165        struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev);
 166        unsigned long jiffies = msecs_to_jiffies(XSPDIF_CH_STS_UPDATE_TIMEOUT);
 167
 168        /* start capture only if stream is detected within 40ms timeout */
 169        err = wait_event_interruptible_timeout(ctx->chsts_q,
 170                                               ctx->rx_chsts_updated,
 171                                               jiffies);
 172        if (!err) {
 173                dev_err(dai->dev, "No streaming audio detected!\n");
 174                return -EINVAL;
 175        }
 176        ctx->rx_chsts_updated = false;
 177
 178        return 0;
 179}
 180
 181static int xlnx_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 182                              struct snd_soc_dai *dai)
 183{
 184        u32 val;
 185        int ret = 0;
 186        struct spdif_dev_data *ctx = dev_get_drvdata(dai->dev);
 187
 188        val = ioread32(ctx->base + XSPDIF_CONTROL_REG);
 189        switch (cmd) {
 190        case SNDRV_PCM_TRIGGER_START:
 191        case SNDRV_PCM_TRIGGER_RESUME:
 192        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 193                val |= XSPDIF_CONTROL_ENABLE_MASK;
 194                writel(val, ctx->base + XSPDIF_CONTROL_REG);
 195                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 196                        ret = rx_stream_detect(dai);
 197                break;
 198        case SNDRV_PCM_TRIGGER_STOP:
 199        case SNDRV_PCM_TRIGGER_SUSPEND:
 200        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 201                val &= ~XSPDIF_CONTROL_ENABLE_MASK;
 202                writel(val, ctx->base + XSPDIF_CONTROL_REG);
 203                break;
 204        default:
 205                ret = -EINVAL;
 206        }
 207
 208        return ret;
 209}
 210
 211static const struct snd_soc_dai_ops xlnx_spdif_dai_ops = {
 212        .startup = xlnx_spdif_startup,
 213        .shutdown = xlnx_spdif_shutdown,
 214        .trigger = xlnx_spdif_trigger,
 215        .hw_params = xlnx_spdif_hw_params,
 216};
 217
 218static struct snd_soc_dai_driver xlnx_spdif_tx_dai = {
 219        .name = "xlnx_spdif_tx",
 220        .playback = {
 221                .channels_min = 2,
 222                .channels_max = 2,
 223                .rates = XLNX_SPDIF_RATES,
 224                .formats = XLNX_SPDIF_FORMATS,
 225        },
 226        .ops = &xlnx_spdif_dai_ops,
 227};
 228
 229static struct snd_soc_dai_driver xlnx_spdif_rx_dai = {
 230        .name = "xlnx_spdif_rx",
 231        .capture = {
 232                .channels_min = 2,
 233                .channels_max = 2,
 234                .rates = XLNX_SPDIF_RATES,
 235                .formats = XLNX_SPDIF_FORMATS,
 236        },
 237        .ops = &xlnx_spdif_dai_ops,
 238};
 239
 240static const struct snd_soc_component_driver xlnx_spdif_component = {
 241        .name = "xlnx-spdif",
 242};
 243
 244static const struct of_device_id xlnx_spdif_of_match[] = {
 245        { .compatible = "xlnx,spdif-2.0", },
 246        {},
 247};
 248MODULE_DEVICE_TABLE(of, xlnx_spdif_of_match);
 249
 250static int xlnx_spdif_probe(struct platform_device *pdev)
 251{
 252        int ret;
 253        struct resource *res;
 254        struct snd_soc_dai_driver *dai_drv;
 255        struct spdif_dev_data *ctx;
 256
 257        struct device *dev = &pdev->dev;
 258        struct device_node *node = dev->of_node;
 259
 260        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
 261        if (!ctx)
 262                return -ENOMEM;
 263
 264        ctx->axi_clk = devm_clk_get(dev, "s_axi_aclk");
 265        if (IS_ERR(ctx->axi_clk)) {
 266                ret = PTR_ERR(ctx->axi_clk);
 267                dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret);
 268                return ret;
 269        }
 270        ret = clk_prepare_enable(ctx->axi_clk);
 271        if (ret) {
 272                dev_err(dev, "failed to enable s_axi_aclk(%d)\n", ret);
 273                return ret;
 274        }
 275
 276        ctx->base = devm_platform_ioremap_resource(pdev, 0);
 277        if (IS_ERR(ctx->base)) {
 278                ret = PTR_ERR(ctx->base);
 279                goto axi_clk_err;
 280        }
 281        ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode);
 282        if (ret < 0) {
 283                dev_err(dev, "cannot get SPDIF mode\n");
 284                goto axi_clk_err;
 285        }
 286        if (ctx->mode) {
 287                ctx->axis_clk = devm_clk_get(dev, "s_axis_aclk");
 288                if (IS_ERR(ctx->axis_clk)) {
 289                        ret = PTR_ERR(ctx->axis_clk);
 290                        dev_err(dev, "failed to get s_axis_aclk(%d)\n", ret);
 291                        goto axi_clk_err;
 292                }
 293                dai_drv = &xlnx_spdif_tx_dai;
 294        } else {
 295                ctx->axis_clk = devm_clk_get(dev, "m_axis_aclk");
 296                if (IS_ERR(ctx->axis_clk)) {
 297                        ret = PTR_ERR(ctx->axis_clk);
 298                        dev_err(dev, "failed to get m_axis_aclk(%d)\n", ret);
 299                        goto axi_clk_err;
 300                }
 301
 302                res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 303                if (!res) {
 304                        dev_err(dev, "No IRQ resource found\n");
 305                        ret = -ENODEV;
 306                        goto axi_clk_err;
 307                }
 308                ret = devm_request_irq(dev, res->start,
 309                                       xlnx_spdifrx_irq_handler,
 310                                       0, "XLNX_SPDIF_RX", ctx);
 311                if (ret) {
 312                        dev_err(dev, "spdif rx irq request failed\n");
 313                        ret = -ENODEV;
 314                        goto axi_clk_err;
 315                }
 316
 317                init_waitqueue_head(&ctx->chsts_q);
 318                dai_drv = &xlnx_spdif_rx_dai;
 319        }
 320
 321        ctx->aud_clk = devm_clk_get(dev, "aud_clk_i");
 322        if (IS_ERR(ctx->aud_clk)) {
 323                ret = PTR_ERR(ctx->aud_clk);
 324                dev_err(dev, "failed to get aud_aclk(%d)\n", ret);
 325                goto axi_clk_err;
 326        }
 327
 328        ret = clk_prepare_enable(ctx->axis_clk);
 329        if (ret) {
 330                dev_err(dev, "failed to enable axis_aclk(%d)\n", ret);
 331                goto axi_clk_err;
 332        }
 333
 334        ret = clk_prepare_enable(ctx->aud_clk);
 335        if (ret) {
 336                dev_err(dev, "failed to enable aud_aclk(%d)\n", ret);
 337                goto axis_clk_err;
 338        }
 339
 340        dev_set_drvdata(dev, ctx);
 341
 342        ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component,
 343                                              dai_drv, 1);
 344        if (ret) {
 345                dev_err(dev, "SPDIF component registration failed\n");
 346                goto clk_err;
 347        }
 348
 349        writel(XSPDIF_SOFT_RESET_VAL, ctx->base + XSPDIF_SOFT_RESET_REG);
 350        dev_info(dev, "%s DAI registered\n", dai_drv->name);
 351        return 0;
 352
 353clk_err:
 354        clk_disable_unprepare(ctx->aud_clk);
 355axis_clk_err:
 356        clk_disable_unprepare(ctx->axis_clk);
 357axi_clk_err:
 358        clk_disable_unprepare(ctx->axi_clk);
 359
 360        return ret;
 361}
 362
 363static int xlnx_spdif_remove(struct platform_device *pdev)
 364{
 365        struct spdif_dev_data *ctx = dev_get_drvdata(&pdev->dev);
 366
 367        clk_disable_unprepare(ctx->aud_clk);
 368        clk_disable_unprepare(ctx->axis_clk);
 369        clk_disable_unprepare(ctx->axi_clk);
 370        return 0;
 371}
 372
 373static struct platform_driver xlnx_spdif_driver = {
 374        .driver = {
 375                .name = "xlnx-spdif",
 376                .of_match_table = xlnx_spdif_of_match,
 377        },
 378        .probe = xlnx_spdif_probe,
 379        .remove = xlnx_spdif_remove,
 380};
 381module_platform_driver(xlnx_spdif_driver);
 382
 383MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>");
 384MODULE_DESCRIPTION("XILINX SPDIF driver");
 385MODULE_LICENSE("GPL v2");
 386