linux/sound/pci/cs5535audio/cs5535audio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Driver for audio on multifunction CS5535/6 companion device
   4 * Copyright (C) Jaya Kumar
   5 *
   6 * Based on Jaroslav Kysela and Takashi Iwai's examples.
   7 * This work was sponsored by CIS(M) Sdn Bhd.
   8 */
   9
  10#include <linux/delay.h>
  11#include <linux/interrupt.h>
  12#include <linux/init.h>
  13#include <linux/pci.h>
  14#include <linux/slab.h>
  15#include <linux/module.h>
  16#include <linux/io.h>
  17#include <sound/core.h>
  18#include <sound/control.h>
  19#include <sound/pcm.h>
  20#include <sound/rawmidi.h>
  21#include <sound/ac97_codec.h>
  22#include <sound/initval.h>
  23#include <sound/asoundef.h>
  24#include "cs5535audio.h"
  25
  26#define DRIVER_NAME "cs5535audio"
  27
  28static char *ac97_quirk;
  29module_param(ac97_quirk, charp, 0444);
  30MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
  31
  32static const struct ac97_quirk ac97_quirks[] = {
  33#if 0 /* Not yet confirmed if all 5536 boards are HP only */
  34        {
  35                .subvendor = PCI_VENDOR_ID_AMD, 
  36                .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO, 
  37                .name = "AMD RDK",     
  38                .type = AC97_TUNE_HP_ONLY
  39        },
  40#endif
  41        {}
  42};
  43
  44static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
  45static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
  46static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
  47
  48module_param_array(index, int, NULL, 0444);
  49MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME);
  50module_param_array(id, charp, NULL, 0444);
  51MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
  52module_param_array(enable, bool, NULL, 0444);
  53MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
  54
  55static const struct pci_device_id snd_cs5535audio_ids[] = {
  56        { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
  57        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
  58        {}
  59};
  60
  61MODULE_DEVICE_TABLE(pci, snd_cs5535audio_ids);
  62
  63static void wait_till_cmd_acked(struct cs5535audio *cs5535au, unsigned long timeout)
  64{
  65        unsigned int tmp;
  66        do {
  67                tmp = cs_readl(cs5535au, ACC_CODEC_CNTL);
  68                if (!(tmp & CMD_NEW))
  69                        break;
  70                udelay(1);
  71        } while (--timeout);
  72        if (!timeout)
  73                dev_err(cs5535au->card->dev,
  74                        "Failure writing to cs5535 codec\n");
  75}
  76
  77static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
  78                                                 unsigned short reg)
  79{
  80        unsigned int regdata;
  81        unsigned int timeout;
  82        unsigned int val;
  83
  84        regdata = ((unsigned int) reg) << 24;
  85        regdata |= ACC_CODEC_CNTL_RD_CMD;
  86        regdata |= CMD_NEW;
  87
  88        cs_writel(cs5535au, ACC_CODEC_CNTL, regdata);
  89        wait_till_cmd_acked(cs5535au, 50);
  90
  91        timeout = 50;
  92        do {
  93                val = cs_readl(cs5535au, ACC_CODEC_STATUS);
  94                if ((val & STS_NEW) && reg == (val >> 24))
  95                        break;
  96                udelay(1);
  97        } while (--timeout);
  98        if (!timeout)
  99                dev_err(cs5535au->card->dev,
 100                        "Failure reading codec reg 0x%x, Last value=0x%x\n",
 101                        reg, val);
 102
 103        return (unsigned short) val;
 104}
 105
 106static void snd_cs5535audio_codec_write(struct cs5535audio *cs5535au,
 107                                        unsigned short reg, unsigned short val)
 108{
 109        unsigned int regdata;
 110
 111        regdata = ((unsigned int) reg) << 24;
 112        regdata |= val;
 113        regdata &= CMD_MASK;
 114        regdata |= CMD_NEW;
 115        regdata &= ACC_CODEC_CNTL_WR_CMD;
 116
 117        cs_writel(cs5535au, ACC_CODEC_CNTL, regdata);
 118        wait_till_cmd_acked(cs5535au, 50);
 119}
 120
 121static void snd_cs5535audio_ac97_codec_write(struct snd_ac97 *ac97,
 122                                             unsigned short reg, unsigned short val)
 123{
 124        struct cs5535audio *cs5535au = ac97->private_data;
 125        snd_cs5535audio_codec_write(cs5535au, reg, val);
 126}
 127
 128static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
 129                                                      unsigned short reg)
 130{
 131        struct cs5535audio *cs5535au = ac97->private_data;
 132        return snd_cs5535audio_codec_read(cs5535au, reg);
 133}
 134
 135static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 136{
 137        struct snd_card *card = cs5535au->card;
 138        struct snd_ac97_bus *pbus;
 139        struct snd_ac97_template ac97;
 140        int err;
 141        static const struct snd_ac97_bus_ops ops = {
 142                .write = snd_cs5535audio_ac97_codec_write,
 143                .read = snd_cs5535audio_ac97_codec_read,
 144        };
 145
 146        err = snd_ac97_bus(card, 0, &ops, NULL, &pbus);
 147        if (err < 0)
 148                return err;
 149
 150        memset(&ac97, 0, sizeof(ac97));
 151        ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
 152                        | AC97_SCAP_POWER_SAVE;
 153        ac97.private_data = cs5535au;
 154        ac97.pci = cs5535au->pci;
 155
 156        /* set any OLPC-specific scaps */
 157        olpc_prequirks(card, &ac97);
 158
 159        err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97);
 160        if (err < 0) {
 161                dev_err(card->dev, "mixer failed\n");
 162                return err;
 163        }
 164
 165        snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
 166
 167        err = olpc_quirks(card, cs5535au->ac97);
 168        if (err < 0) {
 169                dev_err(card->dev, "olpc quirks failed\n");
 170                return err;
 171        }
 172
 173        return 0;
 174}
 175
 176static void process_bm0_irq(struct cs5535audio *cs5535au)
 177{
 178        u8 bm_stat;
 179        spin_lock(&cs5535au->reg_lock);
 180        bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
 181        spin_unlock(&cs5535au->reg_lock);
 182        if (bm_stat & EOP) {
 183                snd_pcm_period_elapsed(cs5535au->playback_substream);
 184        } else {
 185                dev_err(cs5535au->card->dev,
 186                        "unexpected bm0 irq src, bm_stat=%x\n",
 187                        bm_stat);
 188        }
 189}
 190
 191static void process_bm1_irq(struct cs5535audio *cs5535au)
 192{
 193        u8 bm_stat;
 194        spin_lock(&cs5535au->reg_lock);
 195        bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
 196        spin_unlock(&cs5535au->reg_lock);
 197        if (bm_stat & EOP)
 198                snd_pcm_period_elapsed(cs5535au->capture_substream);
 199}
 200
 201static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
 202{
 203        u16 acc_irq_stat;
 204        unsigned char count;
 205        struct cs5535audio *cs5535au = dev_id;
 206
 207        if (cs5535au == NULL)
 208                return IRQ_NONE;
 209
 210        acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS);
 211
 212        if (!acc_irq_stat)
 213                return IRQ_NONE;
 214        for (count = 0; count < 4; count++) {
 215                if (acc_irq_stat & (1 << count)) {
 216                        switch (count) {
 217                        case IRQ_STS:
 218                                cs_readl(cs5535au, ACC_GPIO_STATUS);
 219                                break;
 220                        case WU_IRQ_STS:
 221                                cs_readl(cs5535au, ACC_GPIO_STATUS);
 222                                break;
 223                        case BM0_IRQ_STS:
 224                                process_bm0_irq(cs5535au);
 225                                break;
 226                        case BM1_IRQ_STS:
 227                                process_bm1_irq(cs5535au);
 228                                break;
 229                        default:
 230                                dev_err(cs5535au->card->dev,
 231                                        "Unexpected irq src: 0x%x\n",
 232                                        acc_irq_stat);
 233                                break;
 234                        }
 235                }
 236        }
 237        return IRQ_HANDLED;
 238}
 239
 240static void snd_cs5535audio_free(struct snd_card *card)
 241{
 242        olpc_quirks_cleanup();
 243}
 244
 245static int snd_cs5535audio_create(struct snd_card *card,
 246                                  struct pci_dev *pci)
 247{
 248        struct cs5535audio *cs5535au = card->private_data;
 249        int err;
 250
 251        err = pcim_enable_device(pci);
 252        if (err < 0)
 253                return err;
 254
 255        if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
 256                dev_warn(card->dev, "unable to get 32bit dma\n");
 257                return -ENXIO;
 258        }
 259
 260        spin_lock_init(&cs5535au->reg_lock);
 261        cs5535au->card = card;
 262        cs5535au->pci = pci;
 263        cs5535au->irq = -1;
 264
 265        err = pci_request_regions(pci, "CS5535 Audio");
 266        if (err < 0)
 267                return err;
 268
 269        cs5535au->port = pci_resource_start(pci, 0);
 270
 271        if (devm_request_irq(&pci->dev, pci->irq, snd_cs5535audio_interrupt,
 272                             IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
 273                dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
 274                return -EBUSY;
 275        }
 276
 277        cs5535au->irq = pci->irq;
 278        card->sync_irq = cs5535au->irq;
 279        pci_set_master(pci);
 280
 281        return 0;
 282}
 283
 284static int snd_cs5535audio_probe(struct pci_dev *pci,
 285                                 const struct pci_device_id *pci_id)
 286{
 287        static int dev;
 288        struct snd_card *card;
 289        struct cs5535audio *cs5535au;
 290        int err;
 291
 292        if (dev >= SNDRV_CARDS)
 293                return -ENODEV;
 294        if (!enable[dev]) {
 295                dev++;
 296                return -ENOENT;
 297        }
 298
 299        err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
 300                                sizeof(*cs5535au), &card);
 301        if (err < 0)
 302                return err;
 303        cs5535au = card->private_data;
 304        card->private_free = snd_cs5535audio_free;
 305
 306        err = snd_cs5535audio_create(card, pci);
 307        if (err < 0)
 308                return err;
 309
 310        err = snd_cs5535audio_mixer(cs5535au);
 311        if (err < 0)
 312                return err;
 313
 314        err = snd_cs5535audio_pcm(cs5535au);
 315        if (err < 0)
 316                return err;
 317
 318        strcpy(card->driver, DRIVER_NAME);
 319
 320        strcpy(card->shortname, "CS5535 Audio");
 321        sprintf(card->longname, "%s %s at 0x%lx, irq %i",
 322                card->shortname, card->driver,
 323                cs5535au->port, cs5535au->irq);
 324
 325        err = snd_card_register(card);
 326        if (err < 0)
 327                return err;
 328
 329        pci_set_drvdata(pci, card);
 330        dev++;
 331        return 0;
 332}
 333
 334static struct pci_driver cs5535audio_driver = {
 335        .name = KBUILD_MODNAME,
 336        .id_table = snd_cs5535audio_ids,
 337        .probe = snd_cs5535audio_probe,
 338#ifdef CONFIG_PM_SLEEP
 339        .driver = {
 340                .pm = &snd_cs5535audio_pm,
 341        },
 342#endif
 343};
 344
 345module_pci_driver(cs5535audio_driver);
 346
 347MODULE_AUTHOR("Jaya Kumar");
 348MODULE_LICENSE("GPL");
 349MODULE_DESCRIPTION("CS5535 Audio");
 350