linux/sound/pci/cs5535audio/cs5535audio.c
<<
>>
Prefs
   1/*
   2 * Driver for audio on multifunction CS5535/6 companion device
   3 * Copyright (C) Jaya Kumar
   4 *
   5 * Based on Jaroslav Kysela and Takashi Iwai's examples.
   6 * This work was sponsored by CIS(M) Sdn Bhd.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  21 *
  22 */
  23
  24#include <linux/delay.h>
  25#include <linux/interrupt.h>
  26#include <linux/init.h>
  27#include <linux/pci.h>
  28#include <linux/slab.h>
  29#include <linux/module.h>
  30#include <asm/io.h>
  31#include <sound/core.h>
  32#include <sound/control.h>
  33#include <sound/pcm.h>
  34#include <sound/rawmidi.h>
  35#include <sound/ac97_codec.h>
  36#include <sound/initval.h>
  37#include <sound/asoundef.h>
  38#include "cs5535audio.h"
  39
  40#define DRIVER_NAME "cs5535audio"
  41
  42static char *ac97_quirk;
  43module_param(ac97_quirk, charp, 0444);
  44MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
  45
  46static struct ac97_quirk ac97_quirks[] = {
  47#if 0 /* Not yet confirmed if all 5536 boards are HP only */
  48        {
  49                .subvendor = PCI_VENDOR_ID_AMD, 
  50                .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO, 
  51                .name = "AMD RDK",     
  52                .type = AC97_TUNE_HP_ONLY
  53        },
  54#endif
  55        {}
  56};
  57
  58static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
  59static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
  60static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
  61
  62module_param_array(index, int, NULL, 0444);
  63MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME);
  64module_param_array(id, charp, NULL, 0444);
  65MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
  66module_param_array(enable, bool, NULL, 0444);
  67MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
  68
  69static DEFINE_PCI_DEVICE_TABLE(snd_cs5535audio_ids) = {
  70        { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
  71        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
  72        {}
  73};
  74
  75MODULE_DEVICE_TABLE(pci, snd_cs5535audio_ids);
  76
  77static void wait_till_cmd_acked(struct cs5535audio *cs5535au, unsigned long timeout)
  78{
  79        unsigned int tmp;
  80        do {
  81                tmp = cs_readl(cs5535au, ACC_CODEC_CNTL);
  82                if (!(tmp & CMD_NEW))
  83                        break;
  84                udelay(1);
  85        } while (--timeout);
  86        if (!timeout)
  87                snd_printk(KERN_ERR "Failure writing to cs5535 codec\n");
  88}
  89
  90static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
  91                                                 unsigned short reg)
  92{
  93        unsigned int regdata;
  94        unsigned int timeout;
  95        unsigned int val;
  96
  97        regdata = ((unsigned int) reg) << 24;
  98        regdata |= ACC_CODEC_CNTL_RD_CMD;
  99        regdata |= CMD_NEW;
 100
 101        cs_writel(cs5535au, ACC_CODEC_CNTL, regdata);
 102        wait_till_cmd_acked(cs5535au, 50);
 103
 104        timeout = 50;
 105        do {
 106                val = cs_readl(cs5535au, ACC_CODEC_STATUS);
 107                if ((val & STS_NEW) && reg == (val >> 24))
 108                        break;
 109                udelay(1);
 110        } while (--timeout);
 111        if (!timeout)
 112                snd_printk(KERN_ERR "Failure reading codec reg 0x%x,"
 113                                        "Last value=0x%x\n", reg, val);
 114
 115        return (unsigned short) val;
 116}
 117
 118static void snd_cs5535audio_codec_write(struct cs5535audio *cs5535au,
 119                                        unsigned short reg, unsigned short val)
 120{
 121        unsigned int regdata;
 122
 123        regdata = ((unsigned int) reg) << 24;
 124        regdata |= val;
 125        regdata &= CMD_MASK;
 126        regdata |= CMD_NEW;
 127        regdata &= ACC_CODEC_CNTL_WR_CMD;
 128
 129        cs_writel(cs5535au, ACC_CODEC_CNTL, regdata);
 130        wait_till_cmd_acked(cs5535au, 50);
 131}
 132
 133static void snd_cs5535audio_ac97_codec_write(struct snd_ac97 *ac97,
 134                                             unsigned short reg, unsigned short val)
 135{
 136        struct cs5535audio *cs5535au = ac97->private_data;
 137        snd_cs5535audio_codec_write(cs5535au, reg, val);
 138}
 139
 140static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
 141                                                      unsigned short reg)
 142{
 143        struct cs5535audio *cs5535au = ac97->private_data;
 144        return snd_cs5535audio_codec_read(cs5535au, reg);
 145}
 146
 147static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 148{
 149        struct snd_card *card = cs5535au->card;
 150        struct snd_ac97_bus *pbus;
 151        struct snd_ac97_template ac97;
 152        int err;
 153        static struct snd_ac97_bus_ops ops = {
 154                .write = snd_cs5535audio_ac97_codec_write,
 155                .read = snd_cs5535audio_ac97_codec_read,
 156        };
 157
 158        if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
 159                return err;
 160
 161        memset(&ac97, 0, sizeof(ac97));
 162        ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
 163                        | AC97_SCAP_POWER_SAVE;
 164        ac97.private_data = cs5535au;
 165        ac97.pci = cs5535au->pci;
 166
 167        /* set any OLPC-specific scaps */
 168        olpc_prequirks(card, &ac97);
 169
 170        if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
 171                snd_printk(KERN_ERR "mixer failed\n");
 172                return err;
 173        }
 174
 175        snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
 176
 177        err = olpc_quirks(card, cs5535au->ac97);
 178        if (err < 0) {
 179                snd_printk(KERN_ERR "olpc quirks failed\n");
 180                return err;
 181        }
 182
 183        return 0;
 184}
 185
 186static void process_bm0_irq(struct cs5535audio *cs5535au)
 187{
 188        u8 bm_stat;
 189        spin_lock(&cs5535au->reg_lock);
 190        bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
 191        spin_unlock(&cs5535au->reg_lock);
 192        if (bm_stat & EOP) {
 193                struct cs5535audio_dma *dma;
 194                dma = cs5535au->playback_substream->runtime->private_data;
 195                snd_pcm_period_elapsed(cs5535au->playback_substream);
 196        } else {
 197                snd_printk(KERN_ERR "unexpected bm0 irq src, bm_stat=%x\n",
 198                                        bm_stat);
 199        }
 200}
 201
 202static void process_bm1_irq(struct cs5535audio *cs5535au)
 203{
 204        u8 bm_stat;
 205        spin_lock(&cs5535au->reg_lock);
 206        bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
 207        spin_unlock(&cs5535au->reg_lock);
 208        if (bm_stat & EOP) {
 209                struct cs5535audio_dma *dma;
 210                dma = cs5535au->capture_substream->runtime->private_data;
 211                snd_pcm_period_elapsed(cs5535au->capture_substream);
 212        }
 213}
 214
 215static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
 216{
 217        u16 acc_irq_stat;
 218        unsigned char count;
 219        struct cs5535audio *cs5535au = dev_id;
 220
 221        if (cs5535au == NULL)
 222                return IRQ_NONE;
 223
 224        acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS);
 225
 226        if (!acc_irq_stat)
 227                return IRQ_NONE;
 228        for (count = 0; count < 4; count++) {
 229                if (acc_irq_stat & (1 << count)) {
 230                        switch (count) {
 231                        case IRQ_STS:
 232                                cs_readl(cs5535au, ACC_GPIO_STATUS);
 233                                break;
 234                        case WU_IRQ_STS:
 235                                cs_readl(cs5535au, ACC_GPIO_STATUS);
 236                                break;
 237                        case BM0_IRQ_STS:
 238                                process_bm0_irq(cs5535au);
 239                                break;
 240                        case BM1_IRQ_STS:
 241                                process_bm1_irq(cs5535au);
 242                                break;
 243                        default:
 244                                snd_printk(KERN_ERR "Unexpected irq src: "
 245                                                "0x%x\n", acc_irq_stat);
 246                                break;
 247                        }
 248                }
 249        }
 250        return IRQ_HANDLED;
 251}
 252
 253static int snd_cs5535audio_free(struct cs5535audio *cs5535au)
 254{
 255        synchronize_irq(cs5535au->irq);
 256        pci_set_power_state(cs5535au->pci, 3);
 257
 258        if (cs5535au->irq >= 0)
 259                free_irq(cs5535au->irq, cs5535au);
 260
 261        pci_release_regions(cs5535au->pci);
 262        pci_disable_device(cs5535au->pci);
 263        kfree(cs5535au);
 264        return 0;
 265}
 266
 267static int snd_cs5535audio_dev_free(struct snd_device *device)
 268{
 269        struct cs5535audio *cs5535au = device->device_data;
 270        return snd_cs5535audio_free(cs5535au);
 271}
 272
 273static int snd_cs5535audio_create(struct snd_card *card,
 274                                  struct pci_dev *pci,
 275                                  struct cs5535audio **rcs5535au)
 276{
 277        struct cs5535audio *cs5535au;
 278
 279        int err;
 280        static struct snd_device_ops ops = {
 281                .dev_free =     snd_cs5535audio_dev_free,
 282        };
 283
 284        *rcs5535au = NULL;
 285        if ((err = pci_enable_device(pci)) < 0)
 286                return err;
 287
 288        if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
 289            pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
 290                printk(KERN_WARNING "unable to get 32bit dma\n");
 291                err = -ENXIO;
 292                goto pcifail;
 293        }
 294
 295        cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL);
 296        if (cs5535au == NULL) {
 297                err = -ENOMEM;
 298                goto pcifail;
 299        }
 300
 301        spin_lock_init(&cs5535au->reg_lock);
 302        cs5535au->card = card;
 303        cs5535au->pci = pci;
 304        cs5535au->irq = -1;
 305
 306        if ((err = pci_request_regions(pci, "CS5535 Audio")) < 0) {
 307                kfree(cs5535au);
 308                goto pcifail;
 309        }
 310
 311        cs5535au->port = pci_resource_start(pci, 0);
 312
 313        if (request_irq(pci->irq, snd_cs5535audio_interrupt,
 314                        IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
 315                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
 316                err = -EBUSY;
 317                goto sndfail;
 318        }
 319
 320        cs5535au->irq = pci->irq;
 321        pci_set_master(pci);
 322
 323        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
 324                                  cs5535au, &ops)) < 0)
 325                goto sndfail;
 326
 327        snd_card_set_dev(card, &pci->dev);
 328
 329        *rcs5535au = cs5535au;
 330        return 0;
 331
 332sndfail: /* leave the device alive, just kill the snd */
 333        snd_cs5535audio_free(cs5535au);
 334        return err;
 335
 336pcifail:
 337        pci_disable_device(pci);
 338        return err;
 339}
 340
 341static int snd_cs5535audio_probe(struct pci_dev *pci,
 342                                 const struct pci_device_id *pci_id)
 343{
 344        static int dev;
 345        struct snd_card *card;
 346        struct cs5535audio *cs5535au;
 347        int err;
 348
 349        if (dev >= SNDRV_CARDS)
 350                return -ENODEV;
 351        if (!enable[dev]) {
 352                dev++;
 353                return -ENOENT;
 354        }
 355
 356        err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
 357        if (err < 0)
 358                return err;
 359
 360        if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
 361                goto probefail_out;
 362
 363        card->private_data = cs5535au;
 364
 365        if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
 366                goto probefail_out;
 367
 368        if ((err = snd_cs5535audio_pcm(cs5535au)) < 0)
 369                goto probefail_out;
 370
 371        strcpy(card->driver, DRIVER_NAME);
 372
 373        strcpy(card->shortname, "CS5535 Audio");
 374        sprintf(card->longname, "%s %s at 0x%lx, irq %i",
 375                card->shortname, card->driver,
 376                cs5535au->port, cs5535au->irq);
 377
 378        if ((err = snd_card_register(card)) < 0)
 379                goto probefail_out;
 380
 381        pci_set_drvdata(pci, card);
 382        dev++;
 383        return 0;
 384
 385probefail_out:
 386        snd_card_free(card);
 387        return err;
 388}
 389
 390static void snd_cs5535audio_remove(struct pci_dev *pci)
 391{
 392        olpc_quirks_cleanup();
 393        snd_card_free(pci_get_drvdata(pci));
 394        pci_set_drvdata(pci, NULL);
 395}
 396
 397static struct pci_driver cs5535audio_driver = {
 398        .name = KBUILD_MODNAME,
 399        .id_table = snd_cs5535audio_ids,
 400        .probe = snd_cs5535audio_probe,
 401        .remove = snd_cs5535audio_remove,
 402#ifdef CONFIG_PM_SLEEP
 403        .driver = {
 404                .pm = &snd_cs5535audio_pm,
 405        },
 406#endif
 407};
 408
 409module_pci_driver(cs5535audio_driver);
 410
 411MODULE_AUTHOR("Jaya Kumar");
 412MODULE_LICENSE("GPL");
 413MODULE_DESCRIPTION("CS5535 Audio");
 414MODULE_SUPPORTED_DEVICE("CS5535 Audio");
 415