• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/sound/pci/cs5535audio/
1/*
2 * Driver for audio on multifunction CS5535/6 companion device
3 * Copyright (C) Jaya Kumar
4 *
5 * Based on Jaroslav Kysela and Takashi Iwai's examples.
6 * This work was sponsored by CIS(M) Sdn Bhd.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 *
22 */
23
24#include <linux/delay.h>
25#include <linux/interrupt.h>
26#include <linux/init.h>
27#include <linux/pci.h>
28#include <linux/slab.h>
29#include <linux/moduleparam.h>
30#include <asm/io.h>
31#include <sound/core.h>
32#include <sound/control.h>
33#include <sound/pcm.h>
34#include <sound/rawmidi.h>
35#include <sound/ac97_codec.h>
36#include <sound/initval.h>
37#include <sound/asoundef.h>
38#include "cs5535audio.h"
39
40#define DRIVER_NAME "cs5535audio"
41
42static char *ac97_quirk;
43module_param(ac97_quirk, charp, 0444);
44MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
45
46static struct ac97_quirk ac97_quirks[] __devinitdata = {
47	{}
48};
49
50static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
51static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
52static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
53
54module_param_array(index, int, NULL, 0444);
55MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME);
56module_param_array(id, charp, NULL, 0444);
57MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
58module_param_array(enable, bool, NULL, 0444);
59MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
60
61static DEFINE_PCI_DEVICE_TABLE(snd_cs5535audio_ids) = {
62	{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
63	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
64	{}
65};
66
67MODULE_DEVICE_TABLE(pci, snd_cs5535audio_ids);
68
69static void wait_till_cmd_acked(struct cs5535audio *cs5535au, unsigned long timeout)
70{
71	unsigned int tmp;
72	do {
73		tmp = cs_readl(cs5535au, ACC_CODEC_CNTL);
74		if (!(tmp & CMD_NEW))
75			break;
76		udelay(1);
77	} while (--timeout);
78	if (!timeout)
79		snd_printk(KERN_ERR "Failure writing to cs5535 codec\n");
80}
81
82static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
83						 unsigned short reg)
84{
85	unsigned int regdata;
86	unsigned int timeout;
87	unsigned int val;
88
89	regdata = ((unsigned int) reg) << 24;
90	regdata |= ACC_CODEC_CNTL_RD_CMD;
91	regdata |= CMD_NEW;
92
93	cs_writel(cs5535au, ACC_CODEC_CNTL, regdata);
94	wait_till_cmd_acked(cs5535au, 50);
95
96	timeout = 50;
97	do {
98		val = cs_readl(cs5535au, ACC_CODEC_STATUS);
99		if ((val & STS_NEW) && reg == (val >> 24))
100			break;
101		udelay(1);
102	} while (--timeout);
103	if (!timeout)
104		snd_printk(KERN_ERR "Failure reading codec reg 0x%x,"
105					"Last value=0x%x\n", reg, val);
106
107	return (unsigned short) val;
108}
109
110static 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
125static 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
132static 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
139static int __devinit 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 struct snd_ac97_bus_ops ops = {
146		.write = snd_cs5535audio_ac97_codec_write,
147		.read = snd_cs5535audio_ac97_codec_read,
148	};
149
150	if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
151		return err;
152
153	memset(&ac97, 0, sizeof(ac97));
154	ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
155			| AC97_SCAP_POWER_SAVE;
156	ac97.private_data = cs5535au;
157	ac97.pci = cs5535au->pci;
158
159	/* set any OLPC-specific scaps */
160	olpc_prequirks(card, &ac97);
161
162	if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
163		snd_printk(KERN_ERR "mixer failed\n");
164		return err;
165	}
166
167	snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
168
169	err = olpc_quirks(card, cs5535au->ac97);
170	if (err < 0) {
171		snd_printk(KERN_ERR "olpc quirks failed\n");
172		return err;
173	}
174
175	return 0;
176}
177
178static void process_bm0_irq(struct cs5535audio *cs5535au)
179{
180	u8 bm_stat;
181	spin_lock(&cs5535au->reg_lock);
182	bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
183	spin_unlock(&cs5535au->reg_lock);
184	if (bm_stat & EOP) {
185		struct cs5535audio_dma *dma;
186		dma = cs5535au->playback_substream->runtime->private_data;
187		snd_pcm_period_elapsed(cs5535au->playback_substream);
188	} else {
189		snd_printk(KERN_ERR "unexpected bm0 irq src, bm_stat=%x\n",
190					bm_stat);
191	}
192}
193
194static void process_bm1_irq(struct cs5535audio *cs5535au)
195{
196	u8 bm_stat;
197	spin_lock(&cs5535au->reg_lock);
198	bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
199	spin_unlock(&cs5535au->reg_lock);
200	if (bm_stat & EOP) {
201		struct cs5535audio_dma *dma;
202		dma = cs5535au->capture_substream->runtime->private_data;
203		snd_pcm_period_elapsed(cs5535au->capture_substream);
204	}
205}
206
207static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
208{
209	u16 acc_irq_stat;
210	unsigned char count;
211	struct cs5535audio *cs5535au = dev_id;
212
213	if (cs5535au == NULL)
214		return IRQ_NONE;
215
216	acc_irq_stat = cs_readw(cs5535au, ACC_IRQ_STATUS);
217
218	if (!acc_irq_stat)
219		return IRQ_NONE;
220	for (count = 0; count < 4; count++) {
221		if (acc_irq_stat & (1 << count)) {
222			switch (count) {
223			case IRQ_STS:
224				cs_readl(cs5535au, ACC_GPIO_STATUS);
225				break;
226			case WU_IRQ_STS:
227				cs_readl(cs5535au, ACC_GPIO_STATUS);
228				break;
229			case BM0_IRQ_STS:
230				process_bm0_irq(cs5535au);
231				break;
232			case BM1_IRQ_STS:
233				process_bm1_irq(cs5535au);
234				break;
235			default:
236				snd_printk(KERN_ERR "Unexpected irq src: "
237						"0x%x\n", acc_irq_stat);
238				break;
239			}
240		}
241	}
242	return IRQ_HANDLED;
243}
244
245static int snd_cs5535audio_free(struct cs5535audio *cs5535au)
246{
247	synchronize_irq(cs5535au->irq);
248	pci_set_power_state(cs5535au->pci, 3);
249
250	if (cs5535au->irq >= 0)
251		free_irq(cs5535au->irq, cs5535au);
252
253	pci_release_regions(cs5535au->pci);
254	pci_disable_device(cs5535au->pci);
255	kfree(cs5535au);
256	return 0;
257}
258
259static int snd_cs5535audio_dev_free(struct snd_device *device)
260{
261	struct cs5535audio *cs5535au = device->device_data;
262	return snd_cs5535audio_free(cs5535au);
263}
264
265static int __devinit snd_cs5535audio_create(struct snd_card *card,
266					    struct pci_dev *pci,
267					    struct cs5535audio **rcs5535au)
268{
269	struct cs5535audio *cs5535au;
270
271	int err;
272	static struct snd_device_ops ops = {
273		.dev_free =	snd_cs5535audio_dev_free,
274	};
275
276	*rcs5535au = NULL;
277	if ((err = pci_enable_device(pci)) < 0)
278		return err;
279
280	if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
281	    pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
282		printk(KERN_WARNING "unable to get 32bit dma\n");
283		err = -ENXIO;
284		goto pcifail;
285	}
286
287	cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL);
288	if (cs5535au == NULL) {
289		err = -ENOMEM;
290		goto pcifail;
291	}
292
293	spin_lock_init(&cs5535au->reg_lock);
294	cs5535au->card = card;
295	cs5535au->pci = pci;
296	cs5535au->irq = -1;
297
298	if ((err = pci_request_regions(pci, "CS5535 Audio")) < 0) {
299		kfree(cs5535au);
300		goto pcifail;
301	}
302
303	cs5535au->port = pci_resource_start(pci, 0);
304
305	if (request_irq(pci->irq, snd_cs5535audio_interrupt,
306			IRQF_SHARED, "CS5535 Audio", cs5535au)) {
307		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
308		err = -EBUSY;
309		goto sndfail;
310	}
311
312	cs5535au->irq = pci->irq;
313	pci_set_master(pci);
314
315	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
316				  cs5535au, &ops)) < 0)
317		goto sndfail;
318
319	snd_card_set_dev(card, &pci->dev);
320
321	*rcs5535au = cs5535au;
322	return 0;
323
324sndfail: /* leave the device alive, just kill the snd */
325	snd_cs5535audio_free(cs5535au);
326	return err;
327
328pcifail:
329	pci_disable_device(pci);
330	return err;
331}
332
333static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
334					   const struct pci_device_id *pci_id)
335{
336	static int dev;
337	struct snd_card *card;
338	struct cs5535audio *cs5535au;
339	int err;
340
341	if (dev >= SNDRV_CARDS)
342		return -ENODEV;
343	if (!enable[dev]) {
344		dev++;
345		return -ENOENT;
346	}
347
348	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
349	if (err < 0)
350		return err;
351
352	if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
353		goto probefail_out;
354
355	card->private_data = cs5535au;
356
357	if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
358		goto probefail_out;
359
360	if ((err = snd_cs5535audio_pcm(cs5535au)) < 0)
361		goto probefail_out;
362
363	strcpy(card->driver, DRIVER_NAME);
364
365	strcpy(card->shortname, "CS5535 Audio");
366	sprintf(card->longname, "%s %s at 0x%lx, irq %i",
367		card->shortname, card->driver,
368		cs5535au->port, cs5535au->irq);
369
370	if ((err = snd_card_register(card)) < 0)
371		goto probefail_out;
372
373	pci_set_drvdata(pci, card);
374	dev++;
375	return 0;
376
377probefail_out:
378	snd_card_free(card);
379	return err;
380}
381
382static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
383{
384	olpc_quirks_cleanup();
385	snd_card_free(pci_get_drvdata(pci));
386	pci_set_drvdata(pci, NULL);
387}
388
389static struct pci_driver driver = {
390	.name = DRIVER_NAME,
391	.id_table = snd_cs5535audio_ids,
392	.probe = snd_cs5535audio_probe,
393	.remove = __devexit_p(snd_cs5535audio_remove),
394#ifdef CONFIG_PM
395	.suspend = snd_cs5535audio_suspend,
396	.resume = snd_cs5535audio_resume,
397#endif
398};
399
400static int __init alsa_card_cs5535audio_init(void)
401{
402	return pci_register_driver(&driver);
403}
404
405static void __exit alsa_card_cs5535audio_exit(void)
406{
407	pci_unregister_driver(&driver);
408}
409
410module_init(alsa_card_cs5535audio_init)
411module_exit(alsa_card_cs5535audio_exit)
412
413MODULE_AUTHOR("Jaya Kumar");
414MODULE_LICENSE("GPL");
415MODULE_DESCRIPTION("CS5535 Audio");
416MODULE_SUPPORTED_DEVICE("CS5535 Audio");
417