]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * The driver for the Yamaha's DS1/DS1E cards | |
c1017a4c | 4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
5 | */ |
6 | ||
1da177e4 LT |
7 | #include <linux/init.h> |
8 | #include <linux/pci.h> | |
9 | #include <linux/time.h> | |
65a77217 | 10 | #include <linux/module.h> |
1da177e4 | 11 | #include <sound/core.h> |
81fcb170 | 12 | #include "ymfpci.h" |
1da177e4 LT |
13 | #include <sound/mpu401.h> |
14 | #include <sound/opl3.h> | |
15 | #include <sound/initval.h> | |
16 | ||
c1017a4c | 17 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
22fb2a70 | 18 | MODULE_DESCRIPTION("Yamaha DS-1 PCI"); |
1da177e4 LT |
19 | MODULE_LICENSE("GPL"); |
20 | MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF724}," | |
21 | "{Yamaha,YMF724F}," | |
22 | "{Yamaha,YMF740}," | |
23 | "{Yamaha,YMF740C}," | |
24 | "{Yamaha,YMF744}," | |
25 | "{Yamaha,YMF754}}"); | |
26 | ||
27 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | |
28 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | |
a67ff6a5 | 29 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ |
1da177e4 LT |
30 | static long fm_port[SNDRV_CARDS]; |
31 | static long mpu_port[SNDRV_CARDS]; | |
32 | #ifdef SUPPORT_JOYSTICK | |
33 | static long joystick_port[SNDRV_CARDS]; | |
34 | #endif | |
a67ff6a5 | 35 | static bool rear_switch[SNDRV_CARDS]; |
1da177e4 LT |
36 | |
37 | module_param_array(index, int, NULL, 0444); | |
22fb2a70 | 38 | MODULE_PARM_DESC(index, "Index value for the Yamaha DS-1 PCI soundcard."); |
1da177e4 | 39 | module_param_array(id, charp, NULL, 0444); |
22fb2a70 | 40 | MODULE_PARM_DESC(id, "ID string for the Yamaha DS-1 PCI soundcard."); |
1da177e4 | 41 | module_param_array(enable, bool, NULL, 0444); |
22fb2a70 | 42 | MODULE_PARM_DESC(enable, "Enable Yamaha DS-1 soundcard."); |
6192c41f | 43 | module_param_hw_array(mpu_port, long, ioport, NULL, 0444); |
1da177e4 | 44 | MODULE_PARM_DESC(mpu_port, "MPU-401 Port."); |
6192c41f | 45 | module_param_hw_array(fm_port, long, ioport, NULL, 0444); |
1da177e4 LT |
46 | MODULE_PARM_DESC(fm_port, "FM OPL-3 Port."); |
47 | #ifdef SUPPORT_JOYSTICK | |
6192c41f | 48 | module_param_hw_array(joystick_port, long, ioport, NULL, 0444); |
1da177e4 LT |
49 | MODULE_PARM_DESC(joystick_port, "Joystick port address"); |
50 | #endif | |
51 | module_param_array(rear_switch, bool, NULL, 0444); | |
52 | MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch"); | |
53 | ||
9baa3c34 | 54 | static const struct pci_device_id snd_ymfpci_ids[] = { |
28d27aae JP |
55 | { PCI_VDEVICE(YAMAHA, 0x0004), 0, }, /* YMF724 */ |
56 | { PCI_VDEVICE(YAMAHA, 0x000d), 0, }, /* YMF724F */ | |
57 | { PCI_VDEVICE(YAMAHA, 0x000a), 0, }, /* YMF740 */ | |
58 | { PCI_VDEVICE(YAMAHA, 0x000c), 0, }, /* YMF740C */ | |
59 | { PCI_VDEVICE(YAMAHA, 0x0010), 0, }, /* YMF744 */ | |
60 | { PCI_VDEVICE(YAMAHA, 0x0012), 0, }, /* YMF754 */ | |
1da177e4 LT |
61 | { 0, } |
62 | }; | |
63 | ||
64 | MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids); | |
65 | ||
66 | #ifdef SUPPORT_JOYSTICK | |
e23e7a14 BP |
67 | static int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev, |
68 | int legacy_ctrl, int legacy_ctrl2) | |
1da177e4 LT |
69 | { |
70 | struct gameport *gp; | |
71 | struct resource *r = NULL; | |
72 | int io_port = joystick_port[dev]; | |
73 | ||
74 | if (!io_port) | |
75 | return -ENODEV; | |
76 | ||
77 | if (chip->pci->device >= 0x0010) { /* YMF 744/754 */ | |
78 | ||
79 | if (io_port == 1) { | |
80 | /* auto-detect */ | |
81 | if (!(io_port = pci_resource_start(chip->pci, 2))) | |
82 | return -ENODEV; | |
83 | } | |
84 | } else { | |
85 | if (io_port == 1) { | |
86 | /* auto-detect */ | |
87 | for (io_port = 0x201; io_port <= 0x205; io_port++) { | |
88 | if (io_port == 0x203) | |
89 | continue; | |
90 | if ((r = request_region(io_port, 1, "YMFPCI gameport")) != NULL) | |
91 | break; | |
92 | } | |
93 | if (!r) { | |
6436bcf6 TI |
94 | dev_err(chip->card->dev, |
95 | "no gameport ports available\n"); | |
1da177e4 LT |
96 | return -EBUSY; |
97 | } | |
98 | } | |
99 | switch (io_port) { | |
100 | case 0x201: legacy_ctrl2 |= 0 << 6; break; | |
101 | case 0x202: legacy_ctrl2 |= 1 << 6; break; | |
102 | case 0x204: legacy_ctrl2 |= 2 << 6; break; | |
103 | case 0x205: legacy_ctrl2 |= 3 << 6; break; | |
104 | default: | |
6436bcf6 TI |
105 | dev_err(chip->card->dev, |
106 | "invalid joystick port %#x", io_port); | |
1da177e4 LT |
107 | return -EINVAL; |
108 | } | |
109 | } | |
110 | ||
111 | if (!r && !(r = request_region(io_port, 1, "YMFPCI gameport"))) { | |
6436bcf6 TI |
112 | dev_err(chip->card->dev, |
113 | "joystick port %#x is in use.\n", io_port); | |
1da177e4 LT |
114 | return -EBUSY; |
115 | } | |
116 | ||
117 | chip->gameport = gp = gameport_allocate_port(); | |
118 | if (!gp) { | |
6436bcf6 TI |
119 | dev_err(chip->card->dev, |
120 | "cannot allocate memory for gameport\n"); | |
b1d5776d | 121 | release_and_free_resource(r); |
1da177e4 LT |
122 | return -ENOMEM; |
123 | } | |
124 | ||
125 | ||
126 | gameport_set_name(gp, "Yamaha YMF Gameport"); | |
127 | gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); | |
128 | gameport_set_dev_parent(gp, &chip->pci->dev); | |
129 | gp->io = io_port; | |
130 | gameport_set_port_data(gp, r); | |
131 | ||
132 | if (chip->pci->device >= 0x0010) /* YMF 744/754 */ | |
133 | pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, io_port); | |
134 | ||
135 | pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, legacy_ctrl | YMFPCI_LEGACY_JPEN); | |
136 | pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); | |
137 | ||
138 | gameport_register_port(chip->gameport); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
208a1b4c | 143 | void snd_ymfpci_free_gameport(struct snd_ymfpci *chip) |
1da177e4 LT |
144 | { |
145 | if (chip->gameport) { | |
146 | struct resource *r = gameport_get_port_data(chip->gameport); | |
147 | ||
148 | gameport_unregister_port(chip->gameport); | |
149 | chip->gameport = NULL; | |
150 | ||
b1d5776d | 151 | release_and_free_resource(r); |
1da177e4 LT |
152 | } |
153 | } | |
154 | #else | |
208a1b4c TI |
155 | static inline int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev, int l, int l2) { return -ENOSYS; } |
156 | void snd_ymfpci_free_gameport(struct snd_ymfpci *chip) { } | |
1da177e4 LT |
157 | #endif /* SUPPORT_JOYSTICK */ |
158 | ||
e23e7a14 BP |
159 | static int snd_card_ymfpci_probe(struct pci_dev *pci, |
160 | const struct pci_device_id *pci_id) | |
1da177e4 LT |
161 | { |
162 | static int dev; | |
208a1b4c | 163 | struct snd_card *card; |
1da177e4 LT |
164 | struct resource *fm_res = NULL; |
165 | struct resource *mpu_res = NULL; | |
208a1b4c TI |
166 | struct snd_ymfpci *chip; |
167 | struct snd_opl3 *opl3; | |
22fb2a70 | 168 | const char *str, *model; |
1da177e4 LT |
169 | int err; |
170 | u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl; | |
171 | ||
172 | if (dev >= SNDRV_CARDS) | |
173 | return -ENODEV; | |
174 | if (!enable[dev]) { | |
175 | dev++; | |
176 | return -ENOENT; | |
177 | } | |
178 | ||
60c5772b TI |
179 | err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, |
180 | 0, &card); | |
e58de7ba TI |
181 | if (err < 0) |
182 | return err; | |
1da177e4 LT |
183 | |
184 | switch (pci_id->device) { | |
22fb2a70 CL |
185 | case 0x0004: str = "YMF724"; model = "DS-1"; break; |
186 | case 0x000d: str = "YMF724F"; model = "DS-1"; break; | |
187 | case 0x000a: str = "YMF740"; model = "DS-1L"; break; | |
188 | case 0x000c: str = "YMF740C"; model = "DS-1L"; break; | |
189 | case 0x0010: str = "YMF744"; model = "DS-1S"; break; | |
190 | case 0x0012: str = "YMF754"; model = "DS-1E"; break; | |
191 | default: model = str = "???"; break; | |
1da177e4 LT |
192 | } |
193 | ||
194 | legacy_ctrl = 0; | |
195 | legacy_ctrl2 = 0x0800; /* SBEN = 0, SMOD = 01, LAD = 0 */ | |
196 | ||
197 | if (pci_id->device >= 0x0010) { /* YMF 744/754 */ | |
198 | if (fm_port[dev] == 1) { | |
199 | /* auto-detect */ | |
200 | fm_port[dev] = pci_resource_start(pci, 1); | |
201 | } | |
202 | if (fm_port[dev] > 0 && | |
203 | (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) { | |
204 | legacy_ctrl |= YMFPCI_LEGACY_FMEN; | |
205 | pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]); | |
206 | } | |
207 | if (mpu_port[dev] == 1) { | |
208 | /* auto-detect */ | |
209 | mpu_port[dev] = pci_resource_start(pci, 1) + 0x20; | |
210 | } | |
211 | if (mpu_port[dev] > 0 && | |
212 | (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) { | |
213 | legacy_ctrl |= YMFPCI_LEGACY_MEN; | |
214 | pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]); | |
215 | } | |
216 | } else { | |
217 | switch (fm_port[dev]) { | |
218 | case 0x388: legacy_ctrl2 |= 0; break; | |
219 | case 0x398: legacy_ctrl2 |= 1; break; | |
220 | case 0x3a0: legacy_ctrl2 |= 2; break; | |
221 | case 0x3a8: legacy_ctrl2 |= 3; break; | |
222 | default: fm_port[dev] = 0; break; | |
223 | } | |
224 | if (fm_port[dev] > 0 && | |
225 | (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) { | |
226 | legacy_ctrl |= YMFPCI_LEGACY_FMEN; | |
227 | } else { | |
228 | legacy_ctrl2 &= ~YMFPCI_LEGACY2_FMIO; | |
229 | fm_port[dev] = 0; | |
230 | } | |
231 | switch (mpu_port[dev]) { | |
232 | case 0x330: legacy_ctrl2 |= 0 << 4; break; | |
233 | case 0x300: legacy_ctrl2 |= 1 << 4; break; | |
234 | case 0x332: legacy_ctrl2 |= 2 << 4; break; | |
235 | case 0x334: legacy_ctrl2 |= 3 << 4; break; | |
236 | default: mpu_port[dev] = 0; break; | |
237 | } | |
238 | if (mpu_port[dev] > 0 && | |
239 | (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) { | |
240 | legacy_ctrl |= YMFPCI_LEGACY_MEN; | |
241 | } else { | |
242 | legacy_ctrl2 &= ~YMFPCI_LEGACY2_MPUIO; | |
243 | mpu_port[dev] = 0; | |
244 | } | |
245 | } | |
246 | if (mpu_res) { | |
247 | legacy_ctrl |= YMFPCI_LEGACY_MIEN; | |
248 | legacy_ctrl2 |= YMFPCI_LEGACY2_IMOD; | |
249 | } | |
250 | pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl); | |
251 | pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); | |
252 | pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); | |
253 | if ((err = snd_ymfpci_create(card, pci, | |
254 | old_legacy_ctrl, | |
255 | &chip)) < 0) { | |
b1d5776d TI |
256 | release_and_free_resource(mpu_res); |
257 | release_and_free_resource(fm_res); | |
1f5ca8ec | 258 | goto free_card; |
1da177e4 LT |
259 | } |
260 | chip->fm_res = fm_res; | |
261 | chip->mpu_res = mpu_res; | |
ded46235 TI |
262 | card->private_data = chip; |
263 | ||
1da177e4 | 264 | strcpy(card->driver, str); |
22fb2a70 | 265 | sprintf(card->shortname, "Yamaha %s (%s)", model, str); |
1da177e4 LT |
266 | sprintf(card->longname, "%s at 0x%lx, irq %i", |
267 | card->shortname, | |
268 | chip->reg_area_phys, | |
269 | chip->irq); | |
1f5ca8ec ME |
270 | err = snd_ymfpci_pcm(chip, 0); |
271 | if (err < 0) | |
272 | goto free_card; | |
273 | ||
274 | err = snd_ymfpci_pcm_spdif(chip, 1); | |
275 | if (err < 0) | |
276 | goto free_card; | |
277 | ||
4d20bb1d | 278 | err = snd_ymfpci_mixer(chip, rear_switch[dev]); |
1f5ca8ec ME |
279 | if (err < 0) |
280 | goto free_card; | |
281 | ||
4d20bb1d | 282 | if (chip->ac97->ext_id & AC97_EI_SDAC) { |
38c47181 | 283 | err = snd_ymfpci_pcm_4ch(chip, 2); |
1f5ca8ec ME |
284 | if (err < 0) |
285 | goto free_card; | |
286 | ||
38c47181 | 287 | err = snd_ymfpci_pcm2(chip, 3); |
1f5ca8ec ME |
288 | if (err < 0) |
289 | goto free_card; | |
1da177e4 | 290 | } |
1f5ca8ec ME |
291 | err = snd_ymfpci_timer(chip, 0); |
292 | if (err < 0) | |
293 | goto free_card; | |
294 | ||
1da177e4 LT |
295 | if (chip->mpu_res) { |
296 | if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, | |
302e4c2f | 297 | mpu_port[dev], |
dba8b469 CL |
298 | MPU401_INFO_INTEGRATED | |
299 | MPU401_INFO_IRQ_HOOK, | |
300 | -1, &chip->rawmidi)) < 0) { | |
6436bcf6 TI |
301 | dev_warn(card->dev, |
302 | "cannot initialize MPU401 at 0x%lx, skipping...\n", | |
303 | mpu_port[dev]); | |
1da177e4 LT |
304 | legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */ |
305 | pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); | |
306 | } | |
307 | } | |
308 | if (chip->fm_res) { | |
309 | if ((err = snd_opl3_create(card, | |
310 | fm_port[dev], | |
311 | fm_port[dev] + 2, | |
312 | OPL3_HW_OPL3, 1, &opl3)) < 0) { | |
6436bcf6 TI |
313 | dev_warn(card->dev, |
314 | "cannot initialize FM OPL3 at 0x%lx, skipping...\n", | |
315 | fm_port[dev]); | |
1da177e4 LT |
316 | legacy_ctrl &= ~YMFPCI_LEGACY_FMEN; |
317 | pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); | |
318 | } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | |
6436bcf6 | 319 | dev_err(card->dev, "cannot create opl3 hwdep\n"); |
1f5ca8ec | 320 | goto free_card; |
1da177e4 LT |
321 | } |
322 | } | |
323 | ||
324 | snd_ymfpci_create_gameport(chip, dev, legacy_ctrl, legacy_ctrl2); | |
325 | ||
1f5ca8ec ME |
326 | err = snd_card_register(card); |
327 | if (err < 0) | |
328 | goto free_card; | |
329 | ||
1da177e4 LT |
330 | pci_set_drvdata(pci, card); |
331 | dev++; | |
332 | return 0; | |
1f5ca8ec ME |
333 | |
334 | free_card: | |
335 | snd_card_free(card); | |
336 | return err; | |
1da177e4 LT |
337 | } |
338 | ||
e23e7a14 | 339 | static void snd_card_ymfpci_remove(struct pci_dev *pci) |
1da177e4 LT |
340 | { |
341 | snd_card_free(pci_get_drvdata(pci)); | |
1da177e4 LT |
342 | } |
343 | ||
e9f66d9b | 344 | static struct pci_driver ymfpci_driver = { |
3733e424 | 345 | .name = KBUILD_MODNAME, |
1da177e4 LT |
346 | .id_table = snd_ymfpci_ids, |
347 | .probe = snd_card_ymfpci_probe, | |
e23e7a14 | 348 | .remove = snd_card_ymfpci_remove, |
c7561cd8 | 349 | #ifdef CONFIG_PM_SLEEP |
68cb2b55 TI |
350 | .driver = { |
351 | .pm = &snd_ymfpci_pm, | |
352 | }, | |
ded46235 | 353 | #endif |
1da177e4 LT |
354 | }; |
355 | ||
e9f66d9b | 356 | module_pci_driver(ymfpci_driver); |