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