18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for audio on multifunction CS5535/6 companion device 48c2ecf20Sopenharmony_ci * Copyright (C) Jaya Kumar 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on Jaroslav Kysela and Takashi Iwai's examples. 78c2ecf20Sopenharmony_ci * This work was sponsored by CIS(M) Sdn Bhd. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <sound/core.h> 188c2ecf20Sopenharmony_ci#include <sound/control.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm.h> 208c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 218c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 228c2ecf20Sopenharmony_ci#include <sound/initval.h> 238c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 248c2ecf20Sopenharmony_ci#include "cs5535audio.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DRIVER_NAME "cs5535audio" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic char *ac97_quirk; 298c2ecf20Sopenharmony_cimodule_param(ac97_quirk, charp, 0444); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds."); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct ac97_quirk ac97_quirks[] = { 338c2ecf20Sopenharmony_ci#if 0 /* Not yet confirmed if all 5536 boards are HP only */ 348c2ecf20Sopenharmony_ci { 358c2ecf20Sopenharmony_ci .subvendor = PCI_VENDOR_ID_AMD, 368c2ecf20Sopenharmony_ci .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO, 378c2ecf20Sopenharmony_ci .name = "AMD RDK", 388c2ecf20Sopenharmony_ci .type = AC97_TUNE_HP_ONLY 398c2ecf20Sopenharmony_ci }, 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci {} 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 458c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 468c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " DRIVER_NAME); 508c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " DRIVER_NAME); 528c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " DRIVER_NAME); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_cs5535audio_ids[] = { 568c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) }, 578c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) }, 588c2ecf20Sopenharmony_ci {} 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_cs5535audio_ids); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void wait_till_cmd_acked(struct cs5535audio *cs5535au, unsigned long timeout) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci unsigned int tmp; 668c2ecf20Sopenharmony_ci do { 678c2ecf20Sopenharmony_ci tmp = cs_readl(cs5535au, ACC_CODEC_CNTL); 688c2ecf20Sopenharmony_ci if (!(tmp & CMD_NEW)) 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci udelay(1); 718c2ecf20Sopenharmony_ci } while (--timeout); 728c2ecf20Sopenharmony_ci if (!timeout) 738c2ecf20Sopenharmony_ci dev_err(cs5535au->card->dev, 748c2ecf20Sopenharmony_ci "Failure writing to cs5535 codec\n"); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au, 788c2ecf20Sopenharmony_ci unsigned short reg) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci unsigned int regdata; 818c2ecf20Sopenharmony_ci unsigned int timeout; 828c2ecf20Sopenharmony_ci unsigned int val; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci regdata = ((unsigned int) reg) << 24; 858c2ecf20Sopenharmony_ci regdata |= ACC_CODEC_CNTL_RD_CMD; 868c2ecf20Sopenharmony_ci regdata |= CMD_NEW; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci cs_writel(cs5535au, ACC_CODEC_CNTL, regdata); 898c2ecf20Sopenharmony_ci wait_till_cmd_acked(cs5535au, 50); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci timeout = 50; 928c2ecf20Sopenharmony_ci do { 938c2ecf20Sopenharmony_ci val = cs_readl(cs5535au, ACC_CODEC_STATUS); 948c2ecf20Sopenharmony_ci if ((val & STS_NEW) && reg == (val >> 24)) 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci udelay(1); 978c2ecf20Sopenharmony_ci } while (--timeout); 988c2ecf20Sopenharmony_ci if (!timeout) 998c2ecf20Sopenharmony_ci dev_err(cs5535au->card->dev, 1008c2ecf20Sopenharmony_ci "Failure reading codec reg 0x%x, Last value=0x%x\n", 1018c2ecf20Sopenharmony_ci reg, val); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return (unsigned short) val; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void snd_cs5535audio_codec_write(struct cs5535audio *cs5535au, 1078c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned int regdata; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci regdata = ((unsigned int) reg) << 24; 1128c2ecf20Sopenharmony_ci regdata |= val; 1138c2ecf20Sopenharmony_ci regdata &= CMD_MASK; 1148c2ecf20Sopenharmony_ci regdata |= CMD_NEW; 1158c2ecf20Sopenharmony_ci regdata &= ACC_CODEC_CNTL_WR_CMD; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci cs_writel(cs5535au, ACC_CODEC_CNTL, regdata); 1188c2ecf20Sopenharmony_ci wait_till_cmd_acked(cs5535au, 50); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void snd_cs5535audio_ac97_codec_write(struct snd_ac97 *ac97, 1228c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct cs5535audio *cs5535au = ac97->private_data; 1258c2ecf20Sopenharmony_ci snd_cs5535audio_codec_write(cs5535au, reg, val); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97, 1298c2ecf20Sopenharmony_ci unsigned short reg) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct cs5535audio *cs5535au = ac97->private_data; 1328c2ecf20Sopenharmony_ci return snd_cs5535audio_codec_read(cs5535au, reg); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int snd_cs5535audio_mixer(struct cs5535audio *cs5535au) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct snd_card *card = cs5535au->card; 1388c2ecf20Sopenharmony_ci struct snd_ac97_bus *pbus; 1398c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 1408c2ecf20Sopenharmony_ci int err; 1418c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 1428c2ecf20Sopenharmony_ci .write = snd_cs5535audio_ac97_codec_write, 1438c2ecf20Sopenharmony_ci .read = snd_cs5535audio_ac97_codec_read, 1448c2ecf20Sopenharmony_ci }; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0) 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 1508c2ecf20Sopenharmony_ci ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM 1518c2ecf20Sopenharmony_ci | AC97_SCAP_POWER_SAVE; 1528c2ecf20Sopenharmony_ci ac97.private_data = cs5535au; 1538c2ecf20Sopenharmony_ci ac97.pci = cs5535au->pci; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* set any OLPC-specific scaps */ 1568c2ecf20Sopenharmony_ci olpc_prequirks(card, &ac97); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) { 1598c2ecf20Sopenharmony_ci dev_err(card->dev, "mixer failed\n"); 1608c2ecf20Sopenharmony_ci return err; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci err = olpc_quirks(card, cs5535au->ac97); 1668c2ecf20Sopenharmony_ci if (err < 0) { 1678c2ecf20Sopenharmony_ci dev_err(card->dev, "olpc quirks failed\n"); 1688c2ecf20Sopenharmony_ci return err; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void process_bm0_irq(struct cs5535audio *cs5535au) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci u8 bm_stat; 1778c2ecf20Sopenharmony_ci spin_lock(&cs5535au->reg_lock); 1788c2ecf20Sopenharmony_ci bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS); 1798c2ecf20Sopenharmony_ci spin_unlock(&cs5535au->reg_lock); 1808c2ecf20Sopenharmony_ci if (bm_stat & EOP) { 1818c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cs5535au->playback_substream); 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci dev_err(cs5535au->card->dev, 1848c2ecf20Sopenharmony_ci "unexpected bm0 irq src, bm_stat=%x\n", 1858c2ecf20Sopenharmony_ci bm_stat); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void process_bm1_irq(struct cs5535audio *cs5535au) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci u8 bm_stat; 1928c2ecf20Sopenharmony_ci spin_lock(&cs5535au->reg_lock); 1938c2ecf20Sopenharmony_ci bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS); 1948c2ecf20Sopenharmony_ci spin_unlock(&cs5535au->reg_lock); 1958c2ecf20Sopenharmony_ci if (bm_stat & EOP) 1968c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cs5535au->capture_substream); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci u16 acc_irq_stat; 2028c2ecf20Sopenharmony_ci unsigned char count; 2038c2ecf20Sopenharmony_ci struct cs5535audio *cs5535au = dev_id; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (cs5535au == NULL) 2068c2ecf20Sopenharmony_ci return IRQ_NONE; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (!acc_irq_stat) 2118c2ecf20Sopenharmony_ci return IRQ_NONE; 2128c2ecf20Sopenharmony_ci for (count = 0; count < 4; count++) { 2138c2ecf20Sopenharmony_ci if (acc_irq_stat & (1 << count)) { 2148c2ecf20Sopenharmony_ci switch (count) { 2158c2ecf20Sopenharmony_ci case IRQ_STS: 2168c2ecf20Sopenharmony_ci cs_readl(cs5535au, ACC_GPIO_STATUS); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case WU_IRQ_STS: 2198c2ecf20Sopenharmony_ci cs_readl(cs5535au, ACC_GPIO_STATUS); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci case BM0_IRQ_STS: 2228c2ecf20Sopenharmony_ci process_bm0_irq(cs5535au); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci case BM1_IRQ_STS: 2258c2ecf20Sopenharmony_ci process_bm1_irq(cs5535au); 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci default: 2288c2ecf20Sopenharmony_ci dev_err(cs5535au->card->dev, 2298c2ecf20Sopenharmony_ci "Unexpected irq src: 0x%x\n", 2308c2ecf20Sopenharmony_ci acc_irq_stat); 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int snd_cs5535audio_free(struct cs5535audio *cs5535au) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci pci_set_power_state(cs5535au->pci, PCI_D3hot); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (cs5535au->irq >= 0) 2438c2ecf20Sopenharmony_ci free_irq(cs5535au->irq, cs5535au); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci pci_release_regions(cs5535au->pci); 2468c2ecf20Sopenharmony_ci pci_disable_device(cs5535au->pci); 2478c2ecf20Sopenharmony_ci kfree(cs5535au); 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int snd_cs5535audio_dev_free(struct snd_device *device) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct cs5535audio *cs5535au = device->device_data; 2548c2ecf20Sopenharmony_ci return snd_cs5535audio_free(cs5535au); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int snd_cs5535audio_create(struct snd_card *card, 2588c2ecf20Sopenharmony_ci struct pci_dev *pci, 2598c2ecf20Sopenharmony_ci struct cs5535audio **rcs5535au) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct cs5535audio *cs5535au; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci int err; 2648c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 2658c2ecf20Sopenharmony_ci .dev_free = snd_cs5535audio_dev_free, 2668c2ecf20Sopenharmony_ci }; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci *rcs5535au = NULL; 2698c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 2708c2ecf20Sopenharmony_ci return err; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 || 2738c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { 2748c2ecf20Sopenharmony_ci dev_warn(card->dev, "unable to get 32bit dma\n"); 2758c2ecf20Sopenharmony_ci err = -ENXIO; 2768c2ecf20Sopenharmony_ci goto pcifail; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL); 2808c2ecf20Sopenharmony_ci if (cs5535au == NULL) { 2818c2ecf20Sopenharmony_ci err = -ENOMEM; 2828c2ecf20Sopenharmony_ci goto pcifail; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci spin_lock_init(&cs5535au->reg_lock); 2868c2ecf20Sopenharmony_ci cs5535au->card = card; 2878c2ecf20Sopenharmony_ci cs5535au->pci = pci; 2888c2ecf20Sopenharmony_ci cs5535au->irq = -1; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, "CS5535 Audio")) < 0) { 2918c2ecf20Sopenharmony_ci kfree(cs5535au); 2928c2ecf20Sopenharmony_ci goto pcifail; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci cs5535au->port = pci_resource_start(pci, 0); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_cs5535audio_interrupt, 2988c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, cs5535au)) { 2998c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 3008c2ecf20Sopenharmony_ci err = -EBUSY; 3018c2ecf20Sopenharmony_ci goto sndfail; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci cs5535au->irq = pci->irq; 3058c2ecf20Sopenharmony_ci card->sync_irq = cs5535au->irq; 3068c2ecf20Sopenharmony_ci pci_set_master(pci); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, 3098c2ecf20Sopenharmony_ci cs5535au, &ops)) < 0) 3108c2ecf20Sopenharmony_ci goto sndfail; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci *rcs5535au = cs5535au; 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cisndfail: /* leave the device alive, just kill the snd */ 3168c2ecf20Sopenharmony_ci snd_cs5535audio_free(cs5535au); 3178c2ecf20Sopenharmony_ci return err; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cipcifail: 3208c2ecf20Sopenharmony_ci pci_disable_device(pci); 3218c2ecf20Sopenharmony_ci return err; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int snd_cs5535audio_probe(struct pci_dev *pci, 3258c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci static int dev; 3288c2ecf20Sopenharmony_ci struct snd_card *card; 3298c2ecf20Sopenharmony_ci struct cs5535audio *cs5535au; 3308c2ecf20Sopenharmony_ci int err; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 3338c2ecf20Sopenharmony_ci return -ENODEV; 3348c2ecf20Sopenharmony_ci if (!enable[dev]) { 3358c2ecf20Sopenharmony_ci dev++; 3368c2ecf20Sopenharmony_ci return -ENOENT; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 3408c2ecf20Sopenharmony_ci 0, &card); 3418c2ecf20Sopenharmony_ci if (err < 0) 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0) 3458c2ecf20Sopenharmony_ci goto probefail_out; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci card->private_data = cs5535au; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if ((err = snd_cs5535audio_mixer(cs5535au)) < 0) 3508c2ecf20Sopenharmony_ci goto probefail_out; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if ((err = snd_cs5535audio_pcm(cs5535au)) < 0) 3538c2ecf20Sopenharmony_ci goto probefail_out; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci strcpy(card->driver, DRIVER_NAME); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci strcpy(card->shortname, "CS5535 Audio"); 3588c2ecf20Sopenharmony_ci sprintf(card->longname, "%s %s at 0x%lx, irq %i", 3598c2ecf20Sopenharmony_ci card->shortname, card->driver, 3608c2ecf20Sopenharmony_ci cs5535au->port, cs5535au->irq); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) 3638c2ecf20Sopenharmony_ci goto probefail_out; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 3668c2ecf20Sopenharmony_ci dev++; 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ciprobefail_out: 3708c2ecf20Sopenharmony_ci snd_card_free(card); 3718c2ecf20Sopenharmony_ci return err; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void snd_cs5535audio_remove(struct pci_dev *pci) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci olpc_quirks_cleanup(); 3778c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic struct pci_driver cs5535audio_driver = { 3818c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 3828c2ecf20Sopenharmony_ci .id_table = snd_cs5535audio_ids, 3838c2ecf20Sopenharmony_ci .probe = snd_cs5535audio_probe, 3848c2ecf20Sopenharmony_ci .remove = snd_cs5535audio_remove, 3858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3868c2ecf20Sopenharmony_ci .driver = { 3878c2ecf20Sopenharmony_ci .pm = &snd_cs5535audio_pm, 3888c2ecf20Sopenharmony_ci }, 3898c2ecf20Sopenharmony_ci#endif 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cimodule_pci_driver(cs5535audio_driver); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 3958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CS5535 Audio"); 3978c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("CS5535 Audio"); 398