linux/sound/soc/nuc900/nuc900-ac97.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2009-2010 Nuvoton technology corporation.
   4 *
   5 * Wan ZongShun <mcuos.com@gmail.com>
   6 */
   7
   8#include <linux/init.h>
   9#include <linux/module.h>
  10#include <linux/slab.h>
  11#include <linux/device.h>
  12#include <linux/delay.h>
  13#include <linux/mutex.h>
  14#include <linux/suspend.h>
  15#include <sound/core.h>
  16#include <sound/pcm.h>
  17#include <sound/initval.h>
  18#include <sound/soc.h>
  19#include <linux/clk.h>
  20
  21#include <mach/mfp.h>
  22
  23#include "nuc900-audio.h"
  24
  25static DEFINE_MUTEX(ac97_mutex);
  26struct nuc900_audio *nuc900_ac97_data;
  27EXPORT_SYMBOL_GPL(nuc900_ac97_data);
  28
  29static int nuc900_checkready(void)
  30{
  31        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
  32
  33        if (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS0) & CODEC_READY))
  34                return -EPERM;
  35
  36        return 0;
  37}
  38
  39/* AC97 controller reads codec register */
  40static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97,
  41                                        unsigned short reg)
  42{
  43        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
  44        unsigned long timeout = 0x10000, val;
  45
  46        mutex_lock(&ac97_mutex);
  47
  48        val = nuc900_checkready();
  49        if (val) {
  50                dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
  51                goto out;
  52        }
  53
  54        /* set the R_WB bit and write register index */
  55        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, R_WB | reg);
  56
  57        /* set the valid frame bit and valid slots */
  58        val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
  59        val |= (VALID_FRAME | SLOT1_VALID);
  60        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
  61
  62        udelay(100);
  63
  64        /* polling the AC_R_FINISH */
  65        while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH)
  66                                                                && --timeout)
  67                mdelay(1);
  68
  69        if (!timeout) {
  70                dev_err(nuc900_audio->dev, "AC97 read register time out !\n");
  71                val = -EPERM;
  72                goto out;
  73        }
  74
  75        val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0) ;
  76        val &= ~SLOT1_VALID;
  77        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
  78
  79        if (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS1) >> 2 != reg) {
  80                dev_err(nuc900_audio->dev,
  81                                "R_INDEX of REG_ACTL_ACIS1 not match!\n");
  82        }
  83
  84        udelay(100);
  85        val = (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS2) & 0xFFFF);
  86
  87out:
  88        mutex_unlock(&ac97_mutex);
  89        return val;
  90}
  91
  92/* AC97 controller writes to codec register */
  93static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
  94                                unsigned short val)
  95{
  96        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
  97        unsigned long tmp, timeout = 0x10000;
  98
  99        mutex_lock(&ac97_mutex);
 100
 101        tmp = nuc900_checkready();
 102        if (tmp)
 103                dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
 104
 105        /* clear the R_WB bit and write register index */
 106        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, reg);
 107
 108        /* write register value */
 109        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS2, val);
 110
 111        /* set the valid frame bit and valid slots */
 112        tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
 113        tmp |= SLOT1_VALID | SLOT2_VALID | VALID_FRAME;
 114        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
 115
 116        udelay(100);
 117
 118        /* polling the AC_W_FINISH */
 119        while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH)
 120                                                                && --timeout)
 121                mdelay(1);
 122
 123        if (!timeout)
 124                dev_err(nuc900_audio->dev, "AC97 write register time out !\n");
 125
 126        tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
 127        tmp &= ~(SLOT1_VALID | SLOT2_VALID);
 128        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
 129
 130        mutex_unlock(&ac97_mutex);
 131
 132}
 133
 134static void nuc900_ac97_warm_reset(struct snd_ac97 *ac97)
 135{
 136        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
 137        unsigned long val;
 138
 139        mutex_lock(&ac97_mutex);
 140
 141        /* warm reset AC 97 */
 142        val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
 143        val |= AC_W_RES;
 144        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
 145
 146        udelay(100);
 147
 148        val = nuc900_checkready();
 149        if (val)
 150                dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
 151
 152        mutex_unlock(&ac97_mutex);
 153}
 154
 155static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97)
 156{
 157        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
 158        unsigned long val;
 159
 160        mutex_lock(&ac97_mutex);
 161
 162        /* reset Audio Controller */
 163        val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 164        val |= ACTL_RESET_BIT;
 165        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 166
 167        val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 168        val &= (~ACTL_RESET_BIT);
 169        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 170
 171        /* reset AC-link interface */
 172
 173        val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 174        val |= AC_RESET;
 175        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 176
 177        val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 178        val &= ~AC_RESET;
 179        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 180
 181        /* cold reset AC 97 */
 182        val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
 183        val |= AC_C_RES;
 184        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
 185
 186        val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
 187        val &= (~AC_C_RES);
 188        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
 189
 190        udelay(100);
 191
 192        mutex_unlock(&ac97_mutex);
 193
 194}
 195
 196/* AC97 controller operations */
 197static struct snd_ac97_bus_ops nuc900_ac97_ops = {
 198        .read           = nuc900_ac97_read,
 199        .write          = nuc900_ac97_write,
 200        .reset          = nuc900_ac97_cold_reset,
 201        .warm_reset     = nuc900_ac97_warm_reset,
 202};
 203
 204static int nuc900_ac97_trigger(struct snd_pcm_substream *substream,
 205                                int cmd, struct snd_soc_dai *dai)
 206{
 207        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
 208        int ret;
 209        unsigned long val, tmp;
 210
 211        ret = 0;
 212
 213        switch (cmd) {
 214        case SNDRV_PCM_TRIGGER_START:
 215        case SNDRV_PCM_TRIGGER_RESUME:
 216                val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 217                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 218                        tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
 219                        tmp |= (SLOT3_VALID | SLOT4_VALID | VALID_FRAME);
 220                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
 221
 222                        tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
 223                        tmp |= (P_DMA_END_IRQ | P_DMA_MIDDLE_IRQ);
 224                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, tmp);
 225                        val |= AC_PLAY;
 226                } else {
 227                        tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
 228                        tmp |= (R_DMA_END_IRQ | R_DMA_MIDDLE_IRQ);
 229
 230                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, tmp);
 231                        val |= AC_RECORD;
 232                }
 233
 234                AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 235
 236                break;
 237        case SNDRV_PCM_TRIGGER_STOP:
 238        case SNDRV_PCM_TRIGGER_SUSPEND:
 239                val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 240                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 241                        tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
 242                        tmp &= ~(SLOT3_VALID | SLOT4_VALID);
 243                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
 244
 245                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, RESET_PRSR);
 246                        val &= ~AC_PLAY;
 247                } else {
 248                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, RESET_PRSR);
 249                        val &= ~AC_RECORD;
 250                }
 251
 252                AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 253
 254                break;
 255        default:
 256                ret = -EINVAL;
 257        }
 258
 259        return ret;
 260}
 261
 262static int nuc900_ac97_probe(struct snd_soc_dai *dai)
 263{
 264        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
 265        unsigned long val;
 266
 267        mutex_lock(&ac97_mutex);
 268
 269        /* enable unit clock */
 270        clk_enable(nuc900_audio->clk);
 271
 272        /* enable audio controller and AC-link interface */
 273        val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 274        val |= (IIS_AC_PIN_SEL | ACLINK_EN);
 275        AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
 276
 277        mutex_unlock(&ac97_mutex);
 278
 279        return 0;
 280}
 281
 282static int nuc900_ac97_remove(struct snd_soc_dai *dai)
 283{
 284        struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
 285
 286        clk_disable(nuc900_audio->clk);
 287        return 0;
 288}
 289
 290static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
 291        .trigger        = nuc900_ac97_trigger,
 292};
 293
 294static struct snd_soc_dai_driver nuc900_ac97_dai = {
 295        .probe                  = nuc900_ac97_probe,
 296        .remove                 = nuc900_ac97_remove,
 297        .bus_control            = true,
 298        .playback = {
 299                .rates          = SNDRV_PCM_RATE_8000_48000,
 300                .formats        = SNDRV_PCM_FMTBIT_S16_LE,
 301                .channels_min   = 1,
 302                .channels_max   = 2,
 303        },
 304        .capture = {
 305                .rates          = SNDRV_PCM_RATE_8000_48000,
 306                .formats        = SNDRV_PCM_FMTBIT_S16_LE,
 307                .channels_min   = 1,
 308                .channels_max   = 2,
 309        },
 310        .ops = &nuc900_ac97_dai_ops,
 311};
 312
 313static const struct snd_soc_component_driver nuc900_ac97_component = {
 314        .name           = "nuc900-ac97",
 315};
 316
 317static int nuc900_ac97_drvprobe(struct platform_device *pdev)
 318{
 319        struct nuc900_audio *nuc900_audio;
 320        int ret;
 321
 322        if (nuc900_ac97_data)
 323                return -EBUSY;
 324
 325        nuc900_audio = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_audio),
 326                                    GFP_KERNEL);
 327        if (!nuc900_audio)
 328                return -ENOMEM;
 329
 330        spin_lock_init(&nuc900_audio->lock);
 331
 332        nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 333        nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev,
 334                                                   nuc900_audio->res);
 335        if (IS_ERR(nuc900_audio->mmio))
 336                return PTR_ERR(nuc900_audio->mmio);
 337
 338        nuc900_audio->clk = devm_clk_get(&pdev->dev, NULL);
 339        if (IS_ERR(nuc900_audio->clk)) {
 340                ret = PTR_ERR(nuc900_audio->clk);
 341                goto out;
 342        }
 343
 344        ret = platform_get_irq(pdev, 0);
 345        if (ret < 0)
 346                goto out;
 347        nuc900_audio->irq_num = ret;
 348
 349        nuc900_ac97_data = nuc900_audio;
 350
 351        ret = snd_soc_set_ac97_ops(&nuc900_ac97_ops);
 352        if (ret)
 353                goto out;
 354
 355        ret = devm_snd_soc_register_component(&pdev->dev, &nuc900_ac97_component,
 356                                         &nuc900_ac97_dai, 1);
 357        if (ret)
 358                goto out;
 359
 360        /* enbale ac97 multifunction pin */
 361        mfp_set_groupg(nuc900_audio->dev, NULL);
 362
 363        return 0;
 364
 365out:
 366        snd_soc_set_ac97_ops(NULL);
 367        return ret;
 368}
 369
 370static int nuc900_ac97_drvremove(struct platform_device *pdev)
 371{
 372        nuc900_ac97_data = NULL;
 373        snd_soc_set_ac97_ops(NULL);
 374
 375        return 0;
 376}
 377
 378static struct platform_driver nuc900_ac97_driver = {
 379        .driver = {
 380                .name   = "nuc900-ac97",
 381        },
 382        .probe          = nuc900_ac97_drvprobe,
 383        .remove         = nuc900_ac97_drvremove,
 384};
 385
 386module_platform_driver(nuc900_ac97_driver);
 387
 388MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 389MODULE_DESCRIPTION("NUC900 AC97 SoC driver!");
 390MODULE_LICENSE("GPL");
 391MODULE_ALIAS("platform:nuc900-ac97");
 392