brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 440b8f9 Raw
356 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for audio on multifunction CS5535/6 companion device4 * Copyright (C) Jaya Kumar5 *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_ONLY39	},40#endif41	{}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	err = snd_ac97_bus(card, 0, &ops, NULL, &pbus);147	if (err < 0)148		return err;149 150	memset(&ac97, 0, sizeof(ac97));151	ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM152			| AC97_SCAP_POWER_SAVE;153	ac97.private_data = cs5535au;154	ac97.pci = cs5535au->pci;155 156	/* set any OLPC-specific scaps */157	olpc_prequirks(card, &ac97);158 159	err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97);160	if (err < 0) {161		dev_err(card->dev, "mixer failed\n");162		return err;163	}164 165	snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);166 167	err = olpc_quirks(card, cs5535au->ac97);168	if (err < 0) {169		dev_err(card->dev, "olpc quirks failed\n");170		return err;171	}172 173	return 0;174}175 176static void process_bm0_irq(struct cs5535audio *cs5535au)177{178	u8 bm_stat;179	spin_lock(&cs5535au->reg_lock);180	bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);181	spin_unlock(&cs5535au->reg_lock);182	if (bm_stat & EOP) {183		snd_pcm_period_elapsed(cs5535au->playback_substream);184	} else {185		dev_err(cs5535au->card->dev,186			"unexpected bm0 irq src, bm_stat=%x\n",187			bm_stat);188	}189}190 191static void process_bm1_irq(struct cs5535audio *cs5535au)192{193	u8 bm_stat;194	spin_lock(&cs5535au->reg_lock);195	bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);196	spin_unlock(&cs5535au->reg_lock);197	if (bm_stat & EOP)198		snd_pcm_period_elapsed(cs5535au->capture_substream);199}200 201static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)202{203	u16 acc_irq_stat;204	unsigned char count;205	struct cs5535audio *cs5535au = dev_id;206 207	if (cs5535au == NULL)208		return IRQ_NONE;209 210	acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS);211 212	if (!acc_irq_stat)213		return IRQ_NONE;214	for (count = 0; count < 4; count++) {215		if (acc_irq_stat & (1 << count)) {216			switch (count) {217			case IRQ_STS:218				cs_readl(cs5535au, ACC_GPIO_STATUS);219				break;220			case WU_IRQ_STS:221				cs_readl(cs5535au, ACC_GPIO_STATUS);222				break;223			case BM0_IRQ_STS:224				process_bm0_irq(cs5535au);225				break;226			case BM1_IRQ_STS:227				process_bm1_irq(cs5535au);228				break;229			default:230				dev_err(cs5535au->card->dev,231					"Unexpected irq src: 0x%x\n",232					acc_irq_stat);233				break;234			}235		}236	}237	return IRQ_HANDLED;238}239 240static void snd_cs5535audio_free(struct snd_card *card)241{242	olpc_quirks_cleanup();243}244 245static int snd_cs5535audio_create(struct snd_card *card,246				  struct pci_dev *pci)247{248	struct cs5535audio *cs5535au = card->private_data;249	int err;250 251	err = pcim_enable_device(pci);252	if (err < 0)253		return err;254 255	if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {256		dev_warn(card->dev, "unable to get 32bit dma\n");257		return -ENXIO;258	}259 260	spin_lock_init(&cs5535au->reg_lock);261	cs5535au->card = card;262	cs5535au->pci = pci;263	cs5535au->irq = -1;264 265	err = pci_request_regions(pci, "CS5535 Audio");266	if (err < 0)267		return err;268 269	cs5535au->port = pci_resource_start(pci, 0);270 271	if (devm_request_irq(&pci->dev, pci->irq, snd_cs5535audio_interrupt,272			     IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {273		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);274		return -EBUSY;275	}276 277	cs5535au->irq = pci->irq;278	card->sync_irq = cs5535au->irq;279	pci_set_master(pci);280 281	return 0;282}283 284static int __snd_cs5535audio_probe(struct pci_dev *pci,285				   const struct pci_device_id *pci_id)286{287	static int dev;288	struct snd_card *card;289	struct cs5535audio *cs5535au;290	int err;291 292	if (dev >= SNDRV_CARDS)293		return -ENODEV;294	if (!enable[dev]) {295		dev++;296		return -ENOENT;297	}298 299	err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,300				sizeof(*cs5535au), &card);301	if (err < 0)302		return err;303	cs5535au = card->private_data;304	card->private_free = snd_cs5535audio_free;305 306	err = snd_cs5535audio_create(card, pci);307	if (err < 0)308		return err;309 310	err = snd_cs5535audio_mixer(cs5535au);311	if (err < 0)312		return err;313 314	err = snd_cs5535audio_pcm(cs5535au);315	if (err < 0)316		return err;317 318	strcpy(card->driver, DRIVER_NAME);319 320	strcpy(card->shortname, "CS5535 Audio");321	sprintf(card->longname, "%s %s at 0x%lx, irq %i",322		card->shortname, card->driver,323		cs5535au->port, cs5535au->irq);324 325	err = snd_card_register(card);326	if (err < 0)327		return err;328 329	pci_set_drvdata(pci, card);330	dev++;331	return 0;332}333 334static int snd_cs5535audio_probe(struct pci_dev *pci,335				 const struct pci_device_id *pci_id)336{337	return snd_card_free_on_error(&pci->dev, __snd_cs5535audio_probe(pci, pci_id));338}339 340static struct pci_driver cs5535audio_driver = {341	.name = KBUILD_MODNAME,342	.id_table = snd_cs5535audio_ids,343	.probe = snd_cs5535audio_probe,344#ifdef CONFIG_PM_SLEEP345	.driver = {346		.pm = &snd_cs5535audio_pm,347	},348#endif349};350 351module_pci_driver(cs5535audio_driver);352 353MODULE_AUTHOR("Jaya Kumar");354MODULE_LICENSE("GPL");355MODULE_DESCRIPTION("CS5535 Audio");356