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