File Index
Files and directories
Sign in
FILE

cs5535audio.c

File preview with syntax highlighting.

linux-6.19.14/sound/pci/cs5535audio/cs5535audio.c C
  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
 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_ONLY
 39	},
 40#endif
 41	{}
 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_MODEM
152			| 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
180	scoped_guard(spinlock, &cs5535au->reg_lock) {
181		bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
182	}
183	if (bm_stat & EOP) {
184		snd_pcm_period_elapsed(cs5535au->playback_substream);
185	} else {
186		dev_err(cs5535au->card->dev,
187			"unexpected bm0 irq src, bm_stat=%x\n",
188			bm_stat);
189	}
190}
191
192static void process_bm1_irq(struct cs5535audio *cs5535au)
193{
194	u8 bm_stat;
195
196	scoped_guard(spinlock, &cs5535au->reg_lock) {
197		bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
198	}
199	if (bm_stat & EOP)
200		snd_pcm_period_elapsed(cs5535au->capture_substream);
201}
202
203static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
204{
205	u16 acc_irq_stat;
206	unsigned char count;
207	struct cs5535audio *cs5535au = dev_id;
208
209	if (cs5535au == NULL)
210		return IRQ_NONE;
211
212	acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS);
213
214	if (!acc_irq_stat)
215		return IRQ_NONE;
216	for (count = 0; count < 4; count++) {
217		if (acc_irq_stat & (1 << count)) {
218			switch (count) {
219			case IRQ_STS:
220				cs_readl(cs5535au, ACC_GPIO_STATUS);
221				break;
222			case WU_IRQ_STS:
223				cs_readl(cs5535au, ACC_GPIO_STATUS);
224				break;
225			case BM0_IRQ_STS:
226				process_bm0_irq(cs5535au);
227				break;
228			case BM1_IRQ_STS:
229				process_bm1_irq(cs5535au);
230				break;
231			default:
232				dev_err(cs5535au->card->dev,
233					"Unexpected irq src: 0x%x\n",
234					acc_irq_stat);
235				break;
236			}
237		}
238	}
239	return IRQ_HANDLED;
240}
241
242static void snd_cs5535audio_free(struct snd_card *card)
243{
244	olpc_quirks_cleanup();
245}
246
247static int snd_cs5535audio_create(struct snd_card *card,
248				  struct pci_dev *pci)
249{
250	struct cs5535audio *cs5535au = card->private_data;
251	int err;
252
253	err = pcim_enable_device(pci);
254	if (err < 0)
255		return err;
256
257	if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
258		dev_warn(card->dev, "unable to get 32bit dma\n");
259		return -ENXIO;
260	}
261
262	spin_lock_init(&cs5535au->reg_lock);
263	cs5535au->card = card;
264	cs5535au->pci = pci;
265	cs5535au->irq = -1;
266
267	err = pcim_request_all_regions(pci, "CS5535 Audio");
268	if (err < 0)
269		return err;
270
271	cs5535au->port = pci_resource_start(pci, 0);
272
273	if (devm_request_irq(&pci->dev, pci->irq, snd_cs5535audio_interrupt,
274			     IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
275		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
276		return -EBUSY;
277	}
278
279	cs5535au->irq = pci->irq;
280	card->sync_irq = cs5535au->irq;
281	pci_set_master(pci);
282
283	return 0;
284}
285
286static int __snd_cs5535audio_probe(struct pci_dev *pci,
287				   const struct pci_device_id *pci_id)
288{
289	static int dev;
290	struct snd_card *card;
291	struct cs5535audio *cs5535au;
292	int err;
293
294	if (dev >= SNDRV_CARDS)
295		return -ENODEV;
296	if (!enable[dev]) {
297		dev++;
298		return -ENOENT;
299	}
300
301	err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
302				sizeof(*cs5535au), &card);
303	if (err < 0)
304		return err;
305	cs5535au = card->private_data;
306	card->private_free = snd_cs5535audio_free;
307
308	err = snd_cs5535audio_create(card, pci);
309	if (err < 0)
310		return err;
311
312	err = snd_cs5535audio_mixer(cs5535au);
313	if (err < 0)
314		return err;
315
316	err = snd_cs5535audio_pcm(cs5535au);
317	if (err < 0)
318		return err;
319
320	strscpy(card->driver, DRIVER_NAME);
321
322	strscpy(card->shortname, "CS5535 Audio");
323	sprintf(card->longname, "%s %s at 0x%lx, irq %i",
324		card->shortname, card->driver,
325		cs5535au->port, cs5535au->irq);
326
327	err = snd_card_register(card);
328	if (err < 0)
329		return err;
330
331	pci_set_drvdata(pci, card);
332	dev++;
333	return 0;
334}
335
336static int snd_cs5535audio_probe(struct pci_dev *pci,
337				 const struct pci_device_id *pci_id)
338{
339	return snd_card_free_on_error(&pci->dev, __snd_cs5535audio_probe(pci, pci_id));
340}
341
342static struct pci_driver cs5535audio_driver = {
343	.name = KBUILD_MODNAME,
344	.id_table = snd_cs5535audio_ids,
345	.probe = snd_cs5535audio_probe,
346#ifdef CONFIG_PM_SLEEP
347	.driver = {
348		.pm = &snd_cs5535audio_pm,
349	},
350#endif
351};
352
353module_pci_driver(cs5535audio_driver);
354
355MODULE_AUTHOR("Jaya Kumar");
356MODULE_LICENSE("GPL");
357MODULE_DESCRIPTION("CS5535 Audio");
~/ Read-only file preview.