summaryrefslogtreecommitdiffstats
path: root/drivers/sound/via82cxxx.c
blob: 79d87e2e00cf85807943c96a5596e63bccf1600a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/*
 * Support for VIA 82Cxxx Audio Codecs
 * Copyright 1999 Jeff Garzik <jgarzik@pobox.com>
 *
 * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2.
 * See the "COPYING" file distributed with this software for more info.
 *
 ********************************************************************
 *
 * TODO:
 *
 *	- Integrate AC'97 support, when AC'97 interface released
 *
 */
 
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>

#include <asm/io.h>

#include "sound_config.h"
#include "soundmodule.h"
#include "sb.h"

#ifndef SOUND_LOCK
#define SOUND_LOCK do {} while (0)
#define SOUND_LOCK_END do {} while (0)
#endif

#define MAX_CARDS	2

#define PFX		"via82cxxx: "

#define VIA_VERSION	"1.0.0"
#define VIA_CARD_NAME	"VIA 82Cxxx Audio driver " VIA_VERSION

#define VIA_FUNC_ENABLE		0x42
#define VIA_PNP_CONTROL		0x43

#define VIA_CR42_SB_ENABLE	0x01
#define VIA_CR42_MIDI_ENABLE	0x02
#define VIA_CR42_FM_ENABLE	0x04

#define via_probe_midi probe_uart401
#define via_attach_midi attach_uart401
#define via_unload_midi unload_uart401

static struct address_info	sb_data[MAX_CARDS];
static struct address_info	opl3_data[MAX_CARDS];
static unsigned			cards = 0;


static void __init via_attach_sb(struct address_info *hw_config)
{
	if(!sb_dsp_init(hw_config))
		hw_config->slots[0] = -1;
}


static int __init via_probe_sb(struct address_info *hw_config)
{
	if (check_region(hw_config->io_base, 16))
	{
		printk(KERN_DEBUG PFX "SBPro port 0x%x is already in use\n",
		       hw_config->io_base);
		return 0;
	}
	return sb_dsp_detect(hw_config, 0, 0);
}


static void __exit via_unload_sb(struct address_info *hw_config, int unload_mpu)
{
	if(hw_config->slots[0]!=-1)
		sb_dsp_unload(hw_config, unload_mpu);
}


static int __init via82cxxx_install (struct pci_dev *pcidev)
{
	int sb_io_base = 0;
	int sb_irq = 0;
	int sb_dma = 0;
	int midi_base = 0;
	u8 tmp8;
	
	memset (&sb_data[cards], 0, sizeof (struct address_info));
	memset (&opl3_data[cards], 0, sizeof (struct address_info));

	sb_data[cards].name = opl3_data[cards].name = VIA_CARD_NAME;
	opl3_data[cards].irq = -1;

	/* turn on features, if not already */
	pci_read_config_byte (pcidev, VIA_FUNC_ENABLE, &tmp8);
	tmp8 |= VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE |
		VIA_CR42_FM_ENABLE;
	pci_write_config_byte (pcidev, VIA_FUNC_ENABLE, tmp8);

	/* read legacy PNP info byte */
	pci_read_config_byte (pcidev, VIA_PNP_CONTROL, &tmp8);
	pci_write_config_byte (pcidev, VIA_PNP_CONTROL, tmp8);
	
	switch ((tmp8 >> 6) & 0x03) {
		case 0: sb_irq = 5; break;
		case 1: sb_irq = 7; break;
		case 2: sb_irq = 9; break;
		case 3: sb_irq = 10; break;
		default: /* do nothing */ break;
	}
	switch ((tmp8 >> 4) & 0x03) {
		case 0: sb_dma = 0; break;
		case 1: sb_dma = 1; break;
		case 2: sb_dma = 2; break;
		case 3: sb_dma = 3; break;
		default: /* do nothing */ break;
	}
	switch ((tmp8 >> 2) & 0x03) {
		case 0: midi_base = 0x300; break;
		case 1: midi_base = 0x310; break;
		case 2: midi_base = 0x320; break;
		case 3: midi_base = 0x330; break;
		default: /* do nothing */ break;
	}
	switch (tmp8 & 0x03) {
		case 0: sb_io_base = 0x220; break;
		case 1: sb_io_base = 0x240; break;
		case 2: sb_io_base = 0x260; break;
		case 3: sb_io_base = 0x280; break;
		default: /* do nothing */ break;
	}

	udelay(100);
	
	printk(KERN_INFO PFX "legacy "
	       "MIDI: 0x%X, SB: 0x%X / %d IRQ / %d DMA\n",
		midi_base, sb_io_base, sb_irq, sb_dma);
		
	sb_data[cards].card_subtype = MDL_SBPRO;
	sb_data[cards].io_base = sb_io_base;
	sb_data[cards].irq = sb_irq;
	sb_data[cards].dma = sb_dma;
	
	opl3_data[cards].io_base = midi_base;
	
	/* register legacy SoundBlaster Pro */
	if (!via_probe_sb (&sb_data[cards])) {
		printk (KERN_ERR PFX
			"SB probe @ 0x%X failed, aborting\n",
			sb_io_base);
		return -1;
	}
	via_attach_sb (&sb_data[cards]);

	/* register legacy MIDI */
	if (!via_probe_midi (&opl3_data[cards])) {
		printk (KERN_ERR PFX
			"MIDI probe @ 0x%X failed, aborting\n",
			midi_base);
		via_unload_sb (&sb_data[cards], 0);
		return -1;
	}
	via_attach_midi (&opl3_data[cards]);

	cards++;	
	return 0;
}


/*
 * 	This loop walks the PCI configuration database and finds where
 *	the sound cards are.
 */
 
static int __init probe_via82cxxx (void)
{
	struct pci_dev *pcidev = NULL;

	while ((pcidev = pci_find_device (PCI_VENDOR_ID_VIA,
					  PCI_DEVICE_ID_VIA_82C686_5,
					  pcidev)) != NULL) {

		  if (via82cxxx_install (pcidev) != 0) {
			  printk (KERN_ERR PFX "audio init failed\n");
			  return -1;
		  }

		  if (cards == MAX_CARDS) {
		  	  printk (KERN_DEBUG PFX "maximum number of cards reached\n");
			  break;
		  }
	}

	return 0;
}


/*
 *	This function is called when the user or kernel loads the 
 *	module into memory.
 */


static int __init init_via82cxxx_module(void)
{
	if (!pci_present ()) {
		printk (KERN_DEBUG PFX "PCI not present, exiting\n");
		return -ENODEV;
	}

	if (probe_via82cxxx() != 0) {
		printk(KERN_ERR PFX "probe failed, aborting\n");
		/* XXX unload cards registered so far, if any */
		return -ENODEV;
	}

	if (cards == 0) {
		printk(KERN_DEBUG PFX "No chips found, aborting\n");
		return -ENODEV;
	}

	printk (KERN_INFO PFX VIA_CARD_NAME " loaded\n");
	
	/*
	 *	Binds us to the sound subsystem	
	 */
	SOUND_LOCK;
	return 0;
}

/*
 *	This is called when it is removed. It will only be removed 
 *	when its use count is 0. For sound the SOUND_LOCK/SOUND_UNLOCK
 *	macros hide the entire work for this.
 */
 
static void __exit cleanup_via82cxxx_module(void)
{
	int i;
	
	for (i = 0; i < cards; i++)
		via_unload_sb (&sb_data[i], 1);

	/*
	 *	Final clean up with the sound layer
	 */
	SOUND_LOCK_END;
}

module_init(init_via82cxxx_module);
module_exit(cleanup_via82cxxx_module);