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