linux/sound/soc/sunxi/sun4i-spdif.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * ALSA SoC SPDIF Audio Layer
   4 *
   5 * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it>
   6 * Copyright 2015 Marcus Cooper <codekipper@gmail.com>
   7 *
   8 * Based on the Allwinner SDK driver, released under the GPL.
   9 */
  10
  11#include <linux/clk.h>
  12#include <linux/delay.h>
  13#include <linux/device.h>
  14#include <linux/kernel.h>
  15#include <linux/init.h>
  16#include <linux/regmap.h>
  17#include <linux/of_address.h>
  18#include <linux/of_device.h>
  19#include <linux/ioport.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm_runtime.h>
  23#include <linux/reset.h>
  24#include <sound/dmaengine_pcm.h>
  25#include <sound/pcm_params.h>
  26#include <sound/soc.h>
  27
  28#define SUN4I_SPDIF_CTL         (0x00)
  29        #define SUN4I_SPDIF_CTL_MCLKDIV(v)              ((v) << 4) /* v even */
  30        #define SUN4I_SPDIF_CTL_MCLKOUTEN               BIT(2)
  31        #define SUN4I_SPDIF_CTL_GEN                     BIT(1)
  32        #define SUN4I_SPDIF_CTL_RESET                   BIT(0)
  33
  34#define SUN4I_SPDIF_TXCFG       (0x04)
  35        #define SUN4I_SPDIF_TXCFG_SINGLEMOD             BIT(31)
  36        #define SUN4I_SPDIF_TXCFG_ASS                   BIT(17)
  37        #define SUN4I_SPDIF_TXCFG_NONAUDIO              BIT(16)
  38        #define SUN4I_SPDIF_TXCFG_TXRATIO(v)            ((v) << 4)
  39        #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK          GENMASK(8, 4)
  40        #define SUN4I_SPDIF_TXCFG_FMTRVD                GENMASK(3, 2)
  41        #define SUN4I_SPDIF_TXCFG_FMT16BIT              (0 << 2)
  42        #define SUN4I_SPDIF_TXCFG_FMT20BIT              (1 << 2)
  43        #define SUN4I_SPDIF_TXCFG_FMT24BIT              (2 << 2)
  44        #define SUN4I_SPDIF_TXCFG_CHSTMODE              BIT(1)
  45        #define SUN4I_SPDIF_TXCFG_TXEN                  BIT(0)
  46
  47#define SUN4I_SPDIF_RXCFG       (0x08)
  48        #define SUN4I_SPDIF_RXCFG_LOCKFLAG              BIT(4)
  49        #define SUN4I_SPDIF_RXCFG_CHSTSRC               BIT(3)
  50        #define SUN4I_SPDIF_RXCFG_CHSTCP                BIT(1)
  51        #define SUN4I_SPDIF_RXCFG_RXEN                  BIT(0)
  52
  53#define SUN4I_SPDIF_TXFIFO      (0x0C)
  54
  55#define SUN4I_SPDIF_RXFIFO      (0x10)
  56
  57#define SUN4I_SPDIF_FCTL        (0x14)
  58        #define SUN4I_SPDIF_FCTL_FIFOSRC                BIT(31)
  59        #define SUN4I_SPDIF_FCTL_FTX                    BIT(17)
  60        #define SUN4I_SPDIF_FCTL_FRX                    BIT(16)
  61        #define SUN4I_SPDIF_FCTL_TXTL(v)                ((v) << 8)
  62        #define SUN4I_SPDIF_FCTL_TXTL_MASK              GENMASK(12, 8)
  63        #define SUN4I_SPDIF_FCTL_RXTL(v)                ((v) << 3)
  64        #define SUN4I_SPDIF_FCTL_RXTL_MASK              GENMASK(7, 3)
  65        #define SUN4I_SPDIF_FCTL_TXIM                   BIT(2)
  66        #define SUN4I_SPDIF_FCTL_RXOM(v)                ((v) << 0)
  67        #define SUN4I_SPDIF_FCTL_RXOM_MASK              GENMASK(1, 0)
  68
  69#define SUN50I_H6_SPDIF_FCTL (0x14)
  70        #define SUN50I_H6_SPDIF_FCTL_HUB_EN             BIT(31)
  71        #define SUN50I_H6_SPDIF_FCTL_FTX                BIT(30)
  72        #define SUN50I_H6_SPDIF_FCTL_FRX                BIT(29)
  73        #define SUN50I_H6_SPDIF_FCTL_TXTL(v)            ((v) << 12)
  74        #define SUN50I_H6_SPDIF_FCTL_TXTL_MASK          GENMASK(19, 12)
  75        #define SUN50I_H6_SPDIF_FCTL_RXTL(v)            ((v) << 4)
  76        #define SUN50I_H6_SPDIF_FCTL_RXTL_MASK          GENMASK(10, 4)
  77        #define SUN50I_H6_SPDIF_FCTL_TXIM               BIT(2)
  78        #define SUN50I_H6_SPDIF_FCTL_RXOM(v)            ((v) << 0)
  79        #define SUN50I_H6_SPDIF_FCTL_RXOM_MASK          GENMASK(1, 0)
  80
  81#define SUN4I_SPDIF_FSTA        (0x18)
  82        #define SUN4I_SPDIF_FSTA_TXE                    BIT(14)
  83        #define SUN4I_SPDIF_FSTA_TXECNTSHT              (8)
  84        #define SUN4I_SPDIF_FSTA_RXA                    BIT(6)
  85        #define SUN4I_SPDIF_FSTA_RXACNTSHT              (0)
  86
  87#define SUN4I_SPDIF_INT         (0x1C)
  88        #define SUN4I_SPDIF_INT_RXLOCKEN                BIT(18)
  89        #define SUN4I_SPDIF_INT_RXUNLOCKEN              BIT(17)
  90        #define SUN4I_SPDIF_INT_RXPARERREN              BIT(16)
  91        #define SUN4I_SPDIF_INT_TXDRQEN                 BIT(7)
  92        #define SUN4I_SPDIF_INT_TXUIEN                  BIT(6)
  93        #define SUN4I_SPDIF_INT_TXOIEN                  BIT(5)
  94        #define SUN4I_SPDIF_INT_TXEIEN                  BIT(4)
  95        #define SUN4I_SPDIF_INT_RXDRQEN                 BIT(2)
  96        #define SUN4I_SPDIF_INT_RXOIEN                  BIT(1)
  97        #define SUN4I_SPDIF_INT_RXAIEN                  BIT(0)
  98
  99#define SUN4I_SPDIF_ISTA        (0x20)
 100        #define SUN4I_SPDIF_ISTA_RXLOCKSTA              BIT(18)
 101        #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA            BIT(17)
 102        #define SUN4I_SPDIF_ISTA_RXPARERRSTA            BIT(16)
 103        #define SUN4I_SPDIF_ISTA_TXUSTA                 BIT(6)
 104        #define SUN4I_SPDIF_ISTA_TXOSTA                 BIT(5)
 105        #define SUN4I_SPDIF_ISTA_TXESTA                 BIT(4)
 106        #define SUN4I_SPDIF_ISTA_RXOSTA                 BIT(1)
 107        #define SUN4I_SPDIF_ISTA_RXASTA                 BIT(0)
 108
 109#define SUN8I_SPDIF_TXFIFO      (0x20)
 110
 111#define SUN4I_SPDIF_TXCNT       (0x24)
 112
 113#define SUN4I_SPDIF_RXCNT       (0x28)
 114
 115#define SUN4I_SPDIF_TXCHSTA0    (0x2C)
 116        #define SUN4I_SPDIF_TXCHSTA0_CLK(v)             ((v) << 28)
 117        #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v)         ((v) << 24)
 118        #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK       GENMASK(27, 24)
 119        #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v)           ((v) << 20)
 120        #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK         GENMASK(23, 20)
 121        #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v)          ((v) << 16)
 122        #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v)         ((v) << 8)
 123        #define SUN4I_SPDIF_TXCHSTA0_MODE(v)            ((v) << 6)
 124        #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v)        ((v) << 3)
 125        #define SUN4I_SPDIF_TXCHSTA0_CP                 BIT(2)
 126        #define SUN4I_SPDIF_TXCHSTA0_AUDIO              BIT(1)
 127        #define SUN4I_SPDIF_TXCHSTA0_PRO                BIT(0)
 128
 129#define SUN4I_SPDIF_TXCHSTA1    (0x30)
 130        #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v)           ((v) << 8)
 131        #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v)      ((v) << 4)
 132        #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK    GENMASK(7, 4)
 133        #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v)      ((v) << 1)
 134        #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN         BIT(0)
 135
 136#define SUN4I_SPDIF_RXCHSTA0    (0x34)
 137        #define SUN4I_SPDIF_RXCHSTA0_CLK(v)             ((v) << 28)
 138        #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v)         ((v) << 24)
 139        #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v)           ((v) << 20)
 140        #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v)          ((v) << 16)
 141        #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v)         ((v) << 8)
 142        #define SUN4I_SPDIF_RXCHSTA0_MODE(v)            ((v) << 6)
 143        #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v)        ((v) << 3)
 144        #define SUN4I_SPDIF_RXCHSTA0_CP                 BIT(2)
 145        #define SUN4I_SPDIF_RXCHSTA0_AUDIO              BIT(1)
 146        #define SUN4I_SPDIF_RXCHSTA0_PRO                BIT(0)
 147
 148#define SUN4I_SPDIF_RXCHSTA1    (0x38)
 149        #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v)           ((v) << 8)
 150        #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v)      ((v) << 4)
 151        #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v)      ((v) << 1)
 152        #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN         BIT(0)
 153
 154/* Defines for Sampling Frequency */
 155#define SUN4I_SPDIF_SAMFREQ_44_1KHZ             0x0
 156#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED       0x1
 157#define SUN4I_SPDIF_SAMFREQ_48KHZ               0x2
 158#define SUN4I_SPDIF_SAMFREQ_32KHZ               0x3
 159#define SUN4I_SPDIF_SAMFREQ_22_05KHZ            0x4
 160#define SUN4I_SPDIF_SAMFREQ_24KHZ               0x6
 161#define SUN4I_SPDIF_SAMFREQ_88_2KHZ             0x8
 162#define SUN4I_SPDIF_SAMFREQ_76_8KHZ             0x9
 163#define SUN4I_SPDIF_SAMFREQ_96KHZ               0xa
 164#define SUN4I_SPDIF_SAMFREQ_176_4KHZ            0xc
 165#define SUN4I_SPDIF_SAMFREQ_192KHZ              0xe
 166
 167/**
 168 * struct sun4i_spdif_quirks - Differences between SoC variants.
 169 *
 170 * @reg_dac_txdata: TX FIFO offset for DMA config.
 171 * @has_reset: SoC needs reset deasserted.
 172 * @val_fctl_ftx: TX FIFO flush bitmask.
 173 */
 174struct sun4i_spdif_quirks {
 175        unsigned int reg_dac_txdata;
 176        bool has_reset;
 177        unsigned int val_fctl_ftx;
 178};
 179
 180struct sun4i_spdif_dev {
 181        struct platform_device *pdev;
 182        struct clk *spdif_clk;
 183        struct clk *apb_clk;
 184        struct reset_control *rst;
 185        struct snd_soc_dai_driver cpu_dai_drv;
 186        struct regmap *regmap;
 187        struct snd_dmaengine_dai_dma_data dma_params_tx;
 188        const struct sun4i_spdif_quirks *quirks;
 189};
 190
 191static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
 192{
 193        const struct sun4i_spdif_quirks *quirks = host->quirks;
 194
 195        /* soft reset SPDIF */
 196        regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET);
 197
 198        /* flush TX FIFO */
 199        regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
 200                           quirks->val_fctl_ftx, quirks->val_fctl_ftx);
 201
 202        /* clear TX counter */
 203        regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0);
 204}
 205
 206static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream,
 207                                struct sun4i_spdif_dev *host)
 208{
 209        if (substream->runtime->channels == 1)
 210                regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
 211                                   SUN4I_SPDIF_TXCFG_SINGLEMOD,
 212                                   SUN4I_SPDIF_TXCFG_SINGLEMOD);
 213
 214        /* SPDIF TX ENABLE */
 215        regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
 216                           SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN);
 217
 218        /* DRQ ENABLE */
 219        regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
 220                           SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN);
 221
 222        /* Global enable */
 223        regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
 224                           SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN);
 225}
 226
 227static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream,
 228                                 struct sun4i_spdif_dev *host)
 229{
 230        /* SPDIF TX DISABLE */
 231        regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
 232                           SUN4I_SPDIF_TXCFG_TXEN, 0);
 233
 234        /* DRQ DISABLE */
 235        regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
 236                           SUN4I_SPDIF_INT_TXDRQEN, 0);
 237
 238        /* Global disable */
 239        regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
 240                           SUN4I_SPDIF_CTL_GEN, 0);
 241}
 242
 243static int sun4i_spdif_startup(struct snd_pcm_substream *substream,
 244                               struct snd_soc_dai *cpu_dai)
 245{
 246        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 247        struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
 248
 249        if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 250                return -EINVAL;
 251
 252        sun4i_spdif_configure(host);
 253
 254        return 0;
 255}
 256
 257static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream,
 258                                 struct snd_pcm_hw_params *params,
 259                                 struct snd_soc_dai *cpu_dai)
 260{
 261        int ret = 0;
 262        int fmt;
 263        unsigned long rate = params_rate(params);
 264        u32 mclk_div = 0;
 265        unsigned int mclk = 0;
 266        u32 reg_val;
 267        struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
 268        struct platform_device *pdev = host->pdev;
 269
 270        /* Add the PCM and raw data select interface */
 271        switch (params_channels(params)) {
 272        case 1: /* PCM mode */
 273        case 2:
 274                fmt = 0;
 275                break;
 276        case 4: /* raw data mode */
 277                fmt = SUN4I_SPDIF_TXCFG_NONAUDIO;
 278                break;
 279        default:
 280                return -EINVAL;
 281        }
 282
 283        switch (params_format(params)) {
 284        case SNDRV_PCM_FORMAT_S16_LE:
 285                fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT;
 286                break;
 287        case SNDRV_PCM_FORMAT_S20_3LE:
 288                fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT;
 289                break;
 290        case SNDRV_PCM_FORMAT_S24_LE:
 291                fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT;
 292                break;
 293        default:
 294                return -EINVAL;
 295        }
 296
 297        switch (rate) {
 298        case 22050:
 299        case 44100:
 300        case 88200:
 301        case 176400:
 302                mclk = 22579200;
 303                break;
 304        case 24000:
 305        case 32000:
 306        case 48000:
 307        case 96000:
 308        case 192000:
 309                mclk = 24576000;
 310                break;
 311        default:
 312                return -EINVAL;
 313        }
 314
 315        ret = clk_set_rate(host->spdif_clk, mclk);
 316        if (ret < 0) {
 317                dev_err(&pdev->dev,
 318                        "Setting SPDIF clock rate for %d Hz failed!\n", mclk);
 319                return ret;
 320        }
 321
 322        regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
 323                           SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM);
 324
 325        switch (rate) {
 326        case 22050:
 327        case 24000:
 328                mclk_div = 8;
 329                break;
 330        case 32000:
 331                mclk_div = 6;
 332                break;
 333        case 44100:
 334        case 48000:
 335                mclk_div = 4;
 336                break;
 337        case 88200:
 338        case 96000:
 339                mclk_div = 2;
 340                break;
 341        case 176400:
 342        case 192000:
 343                mclk_div = 1;
 344                break;
 345        default:
 346                return -EINVAL;
 347        }
 348
 349        reg_val = 0;
 350        reg_val |= SUN4I_SPDIF_TXCFG_ASS;
 351        reg_val |= fmt; /* set non audio and bit depth */
 352        reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE;
 353        reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1);
 354        regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val);
 355
 356        return 0;
 357}
 358
 359static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 360                               struct snd_soc_dai *dai)
 361{
 362        int ret = 0;
 363        struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
 364
 365        if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 366                return -EINVAL;
 367
 368        switch (cmd) {
 369        case SNDRV_PCM_TRIGGER_START:
 370        case SNDRV_PCM_TRIGGER_RESUME:
 371        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 372                sun4i_snd_txctrl_on(substream, host);
 373                break;
 374
 375        case SNDRV_PCM_TRIGGER_STOP:
 376        case SNDRV_PCM_TRIGGER_SUSPEND:
 377        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 378                sun4i_snd_txctrl_off(substream, host);
 379                break;
 380
 381        default:
 382                ret = -EINVAL;
 383                break;
 384        }
 385        return ret;
 386}
 387
 388static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai)
 389{
 390        struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
 391
 392        snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL);
 393        return 0;
 394}
 395
 396static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = {
 397        .startup        = sun4i_spdif_startup,
 398        .trigger        = sun4i_spdif_trigger,
 399        .hw_params      = sun4i_spdif_hw_params,
 400};
 401
 402static const struct regmap_config sun4i_spdif_regmap_config = {
 403        .reg_bits = 32,
 404        .reg_stride = 4,
 405        .val_bits = 32,
 406        .max_register = SUN4I_SPDIF_RXCHSTA1,
 407};
 408
 409#define SUN4I_RATES     SNDRV_PCM_RATE_8000_192000
 410
 411#define SUN4I_FORMATS   (SNDRV_PCM_FORMAT_S16_LE | \
 412                                SNDRV_PCM_FORMAT_S20_3LE | \
 413                                SNDRV_PCM_FORMAT_S24_LE)
 414
 415static struct snd_soc_dai_driver sun4i_spdif_dai = {
 416        .playback = {
 417                .channels_min = 1,
 418                .channels_max = 2,
 419                .rates = SUN4I_RATES,
 420                .formats = SUN4I_FORMATS,
 421        },
 422        .probe = sun4i_spdif_soc_dai_probe,
 423        .ops = &sun4i_spdif_dai_ops,
 424        .name = "spdif",
 425};
 426
 427static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = {
 428        .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
 429        .val_fctl_ftx   = SUN4I_SPDIF_FCTL_FTX,
 430};
 431
 432static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = {
 433        .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
 434        .val_fctl_ftx   = SUN4I_SPDIF_FCTL_FTX,
 435        .has_reset      = true,
 436};
 437
 438static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = {
 439        .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
 440        .val_fctl_ftx   = SUN4I_SPDIF_FCTL_FTX,
 441        .has_reset      = true,
 442};
 443
 444static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = {
 445        .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
 446        .val_fctl_ftx   = SUN50I_H6_SPDIF_FCTL_FTX,
 447        .has_reset      = true,
 448};
 449
 450static const struct of_device_id sun4i_spdif_of_match[] = {
 451        {
 452                .compatible = "allwinner,sun4i-a10-spdif",
 453                .data = &sun4i_a10_spdif_quirks,
 454        },
 455        {
 456                .compatible = "allwinner,sun6i-a31-spdif",
 457                .data = &sun6i_a31_spdif_quirks,
 458        },
 459        {
 460                .compatible = "allwinner,sun8i-h3-spdif",
 461                .data = &sun8i_h3_spdif_quirks,
 462        },
 463        {
 464                .compatible = "allwinner,sun50i-h6-spdif",
 465                .data = &sun50i_h6_spdif_quirks,
 466        },
 467        { /* sentinel */ }
 468};
 469MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
 470
 471static const struct snd_soc_component_driver sun4i_spdif_component = {
 472        .name           = "sun4i-spdif",
 473};
 474
 475static int sun4i_spdif_runtime_suspend(struct device *dev)
 476{
 477        struct sun4i_spdif_dev *host  = dev_get_drvdata(dev);
 478
 479        clk_disable_unprepare(host->spdif_clk);
 480        clk_disable_unprepare(host->apb_clk);
 481
 482        return 0;
 483}
 484
 485static int sun4i_spdif_runtime_resume(struct device *dev)
 486{
 487        struct sun4i_spdif_dev *host  = dev_get_drvdata(dev);
 488        int ret;
 489
 490        ret = clk_prepare_enable(host->spdif_clk);
 491        if (ret)
 492                return ret;
 493        ret = clk_prepare_enable(host->apb_clk);
 494        if (ret)
 495                clk_disable_unprepare(host->spdif_clk);
 496
 497        return ret;
 498}
 499
 500static int sun4i_spdif_probe(struct platform_device *pdev)
 501{
 502        struct sun4i_spdif_dev *host;
 503        struct resource *res;
 504        const struct sun4i_spdif_quirks *quirks;
 505        int ret;
 506        void __iomem *base;
 507
 508        dev_dbg(&pdev->dev, "Entered %s\n", __func__);
 509
 510        host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
 511        if (!host)
 512                return -ENOMEM;
 513
 514        host->pdev = pdev;
 515
 516        /* Initialize this copy of the CPU DAI driver structure */
 517        memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));
 518        host->cpu_dai_drv.name = dev_name(&pdev->dev);
 519
 520        /* Get the addresses */
 521        base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 522        if (IS_ERR(base))
 523                return PTR_ERR(base);
 524
 525        quirks = of_device_get_match_data(&pdev->dev);
 526        if (quirks == NULL) {
 527                dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
 528                return -ENODEV;
 529        }
 530        host->quirks = quirks;
 531
 532        host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
 533                                                &sun4i_spdif_regmap_config);
 534
 535        /* Clocks */
 536        host->apb_clk = devm_clk_get(&pdev->dev, "apb");
 537        if (IS_ERR(host->apb_clk)) {
 538                dev_err(&pdev->dev, "failed to get a apb clock.\n");
 539                return PTR_ERR(host->apb_clk);
 540        }
 541
 542        host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
 543        if (IS_ERR(host->spdif_clk)) {
 544                dev_err(&pdev->dev, "failed to get a spdif clock.\n");
 545                return PTR_ERR(host->spdif_clk);
 546        }
 547
 548        host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata;
 549        host->dma_params_tx.maxburst = 8;
 550        host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 551
 552        platform_set_drvdata(pdev, host);
 553
 554        if (quirks->has_reset) {
 555                host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
 556                                                                      NULL);
 557                if (PTR_ERR(host->rst) == -EPROBE_DEFER) {
 558                        ret = -EPROBE_DEFER;
 559                        dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
 560                        return ret;
 561                }
 562                if (!IS_ERR(host->rst))
 563                        reset_control_deassert(host->rst);
 564        }
 565
 566        ret = devm_snd_soc_register_component(&pdev->dev,
 567                                &sun4i_spdif_component, &sun4i_spdif_dai, 1);
 568        if (ret)
 569                return ret;
 570
 571        pm_runtime_enable(&pdev->dev);
 572        if (!pm_runtime_enabled(&pdev->dev)) {
 573                ret = sun4i_spdif_runtime_resume(&pdev->dev);
 574                if (ret)
 575                        goto err_unregister;
 576        }
 577
 578        ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 579        if (ret)
 580                goto err_suspend;
 581        return 0;
 582err_suspend:
 583        if (!pm_runtime_status_suspended(&pdev->dev))
 584                sun4i_spdif_runtime_suspend(&pdev->dev);
 585err_unregister:
 586        pm_runtime_disable(&pdev->dev);
 587        return ret;
 588}
 589
 590static int sun4i_spdif_remove(struct platform_device *pdev)
 591{
 592        pm_runtime_disable(&pdev->dev);
 593        if (!pm_runtime_status_suspended(&pdev->dev))
 594                sun4i_spdif_runtime_suspend(&pdev->dev);
 595
 596        return 0;
 597}
 598
 599static const struct dev_pm_ops sun4i_spdif_pm = {
 600        SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend,
 601                           sun4i_spdif_runtime_resume, NULL)
 602};
 603
 604static struct platform_driver sun4i_spdif_driver = {
 605        .driver         = {
 606                .name   = "sun4i-spdif",
 607                .of_match_table = of_match_ptr(sun4i_spdif_of_match),
 608                .pm     = &sun4i_spdif_pm,
 609        },
 610        .probe          = sun4i_spdif_probe,
 611        .remove         = sun4i_spdif_remove,
 612};
 613
 614module_platform_driver(sun4i_spdif_driver);
 615
 616MODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>");
 617MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
 618MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface");
 619MODULE_LICENSE("GPL");
 620MODULE_ALIAS("platform:sun4i-spdif");
 621