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        if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
 147                return err;
 148
 149        memset(&ac97, 0, sizeof(ac97));
 150        ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
 151                        | AC97_SCAP_POWER_SAVE;
 152        ac97.private_data = cs5535au;
 153        ac97.pci = cs5535au->pci;
 154
 155        /* set any OLPC-specific scaps */
 156        olpc_prequirks(card, &ac97);
 157
 158        if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
 159                dev_err(card->dev, "mixer failed\n");
 160                return err;
 161        }
 162
 163        snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
 164
 165        err = olpc_quirks(card, cs5535au->ac97);
 166        if (err < 0) {
 167                dev_err(card->dev, "olpc quirks failed\n");
 168                return err;
 169        }
 170
 171        return 0;
 172}
 173
 174static void process_bm0_irq(struct cs5535audio *cs5535au)
 175{
 176        u8 bm_stat;
 177        spin_lock(&cs5535au->reg_lock);
 178        bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
 179        spin_unlock(&cs5535au->reg_lock);
 180        if (bm_stat & EOP) {
 181                snd_pcm_period_elapsed(cs5535au->playback_substream);
 182        } else {
 183                dev_err(cs5535au->card->dev,
 184                        "unexpected bm0 irq src, bm_stat=%x\n",
 185                        bm_stat);
 186        }
 187}
 188
 189static void process_bm1_irq(struct cs5535audio *cs5535au)
 190{
 191        u8 bm_stat;
 192        spin_lock(&cs5535au->reg_lock);
 193        bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
 194        spin_unlock(&cs5535au->reg_lock);
 195        if (bm_stat & EOP)
 196                snd_pcm_period_elapsed(cs5535au->capture_substream);
 197}
 198
 199static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
 200{
 201        u16 acc_irq_stat;
 202        unsigned char count;
 203        struct cs5535audio *cs5535au = dev_id;
 204
 205        if (cs5535au == NULL)
 206                return IRQ_NONE;
 207
 208        acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS);
 209
 210        if (!acc_irq_stat)
 211                return IRQ_NONE;
 212        for (count = 0; count < 4; count++) {
 213                if (acc_irq_stat & (1 << count)) {
 214                        switch (count) {
 215                        case IRQ_STS:
 216                                cs_readl(cs5535au, ACC_GPIO_STATUS);
 217                                break;
 218                        case WU_IRQ_STS:
 219                                cs_readl(cs5535au, ACC_GPIO_STATUS);
 220                                break;
 221                        case BM0_IRQ_STS:
 222                                process_bm0_irq(cs5535au);
 223                                break;
 224                        case BM1_IRQ_STS:
 225                                process_bm1_irq(cs5535au);
 226                                break;
 227                        default:
 228                                dev_err(cs5535au->card->dev,
 229                                        "Unexpected irq src: 0x%x\n",
 230                                        acc_irq_stat);
 231                                break;
 232                        }
 233                }
 234        }
 235        return IRQ_HANDLED;
 236}
 237
 238static int snd_cs5535audio_free(struct cs5535audio *cs5535au)
 239{
 240        pci_set_power_state(cs5535au->pci, PCI_D3hot);
 241
 242        if (cs5535au->irq >= 0)
 243                free_irq(cs5535au->irq, cs5535au);
 244
 245        pci_release_regions(cs5535au->pci);
 246        pci_disable_device(cs5535au->pci);
 247        kfree(cs5535au);
 248        return 0;
 249}
 250
 251static int snd_cs5535audio_dev_free(struct snd_device *device)
 252{
 253        struct cs5535audio *cs5535au = device->device_data;
 254        return snd_cs5535audio_free(cs5535au);
 255}
 256
 257static int snd_cs5535audio_create(struct snd_card *card,
 258                                  struct pci_dev *pci,
 259                                  struct cs5535audio **rcs5535au)
 260{
 261        struct cs5535audio *cs5535au;
 262
 263        int err;
 264        static const struct snd_device_ops ops = {
 265                .dev_free =     snd_cs5535audio_dev_free,
 266        };
 267
 268        *rcs5535au = NULL;
 269        if ((err = pci_enable_device(pci)) < 0)
 270                return err;
 271
 272        if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 ||
 273            dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) {
 274                dev_warn(card->dev, "unable to get 32bit dma\n");
 275                err = -ENXIO;
 276                goto pcifail;
 277        }
 278
 279        cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL);
 280        if (cs5535au == NULL) {
 281                err = -ENOMEM;
 282                goto pcifail;
 283        }
 284
 285        spin_lock_init(&cs5535au->reg_lock);
 286        cs5535au->card = card;
 287        cs5535au->pci = pci;
 288        cs5535au->irq = -1;
 289
 290        if ((err = pci_request_regions(pci, "CS5535 Audio")) < 0) {
 291                kfree(cs5535au);
 292                goto pcifail;
 293        }
 294
 295        cs5535au->port = pci_resource_start(pci, 0);
 296
 297        if (request_irq(pci->irq, snd_cs5535audio_interrupt,
 298                        IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
 299                dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
 300                err = -EBUSY;
 301                goto sndfail;
 302        }
 303
 304        cs5535au->irq = pci->irq;
 305        card->sync_irq = cs5535au->irq;
 306        pci_set_master(pci);
 307
 308        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
 309                                  cs5535au, &ops)) < 0)
 310                goto sndfail;
 311
 312        *rcs5535au = cs5535au;
 313        return 0;
 314
 315sndfail: /* leave the device alive, just kill the snd */
 316        snd_cs5535audio_free(cs5535au);
 317        return err;
 318
 319pcifail:
 320        pci_disable_device(pci);
 321        return err;
 322}
 323
 324static int snd_cs5535audio_probe(struct pci_dev *pci,
 325                                 const struct pci_device_id *pci_id)
 326{
 327        static int dev;
 328        struct snd_card *card;
 329        struct cs5535audio *cs5535au;
 330        int err;
 331
 332        if (dev >= SNDRV_CARDS)
 333                return -ENODEV;
 334        if (!enable[dev]) {
 335                dev++;
 336                return -ENOENT;
 337        }
 338
 339        err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
 340                           0, &card);
 341        if (err < 0)
 342                return err;
 343
 344        if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
 345                goto probefail_out;
 346
 347        card->private_data = cs5535au;
 348
 349        if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
 350                goto probefail_out;
 351
 352        if ((err = snd_cs5535audio_pcm(cs5535au)) < 0)
 353                goto probefail_out;
 354
 355        strcpy(card->driver, DRIVER_NAME);
 356
 357        strcpy(card->shortname, "CS5535 Audio");
 358        sprintf(card->longname, "%s %s at 0x%lx, irq %i",
 359                card->shortname, card->driver,
 360                cs5535au->port, cs5535au->irq);
 361
 362        if ((err = snd_card_register(card)) < 0)
 363                goto probefail_out;
 364
 365        pci_set_drvdata(pci, card);
 366        dev++;
 367        return 0;
 368
 369probefail_out:
 370        snd_card_free(card);
 371        return err;
 372}
 373
 374static void snd_cs5535audio_remove(struct pci_dev *pci)
 375{
 376        olpc_quirks_cleanup();
 377        snd_card_free(pci_get_drvdata(pci));
 378}
 379
 380static struct pci_driver cs5535audio_driver = {
 381        .name = KBUILD_MODNAME,
 382        .id_table = snd_cs5535audio_ids,
 383        .probe = snd_cs5535audio_probe,
 384        .remove = snd_cs5535audio_remove,
 385#ifdef CONFIG_PM_SLEEP
 386        .driver = {
 387                .pm = &snd_cs5535audio_pm,
 388        },
 389#endif
 390};
 391
 392module_pci_driver(cs5535audio_driver);
 393
 394MODULE_AUTHOR("Jaya Kumar");
 395MODULE_LICENSE("GPL");
 396MODULE_DESCRIPTION("CS5535 Audio");
 397MODULE_SUPPORTED_DEVICE("CS5535 Audio");
 398