1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for generic ESS AudioDrive ESx688 soundcards |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
5 | */ |
6 | |
7 | #include <linux/init.h> |
8 | #include <linux/err.h> |
9 | #include <linux/isa.h> |
10 | #include <linux/isapnp.h> |
11 | #include <linux/time.h> |
12 | #include <linux/wait.h> |
13 | #include <linux/module.h> |
14 | #include <asm/dma.h> |
15 | #include <sound/core.h> |
16 | #include <sound/es1688.h> |
17 | #include <sound/mpu401.h> |
18 | #include <sound/opl3.h> |
19 | #define SNDRV_LEGACY_FIND_FREE_IRQ |
20 | #define SNDRV_LEGACY_FIND_FREE_DMA |
21 | #include <sound/initval.h> |
22 | |
23 | #define CRD_NAME "Generic ESS ES1688/ES688 AudioDrive" |
24 | #define DEV_NAME "es1688" |
25 | |
26 | MODULE_DESCRIPTION(CRD_NAME); |
27 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>" ); |
28 | MODULE_LICENSE("GPL" ); |
29 | MODULE_ALIAS("snd_es968" ); |
30 | |
31 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ |
32 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ |
33 | #ifdef CONFIG_PNP |
34 | static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; |
35 | #endif |
36 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ |
37 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ |
38 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */ |
39 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; |
40 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ |
41 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ |
42 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ |
43 | |
44 | module_param_array(index, int, NULL, 0444); |
45 | MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard." ); |
46 | module_param_array(id, charp, NULL, 0444); |
47 | MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard." ); |
48 | module_param_array(enable, bool, NULL, 0444); |
49 | #ifdef CONFIG_PNP |
50 | module_param_array(isapnp, bool, NULL, 0444); |
51 | MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard." ); |
52 | #endif |
53 | MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard." ); |
54 | module_param_hw_array(port, long, ioport, NULL, 0444); |
55 | MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver." ); |
56 | module_param_hw_array(mpu_port, long, ioport, NULL, 0444); |
57 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver." ); |
58 | module_param_hw_array(irq, int, irq, NULL, 0444); |
59 | module_param_hw_array(fm_port, long, ioport, NULL, 0444); |
60 | MODULE_PARM_DESC(fm_port, "FM port # for ES1688 driver." ); |
61 | MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver." ); |
62 | module_param_hw_array(mpu_irq, int, irq, NULL, 0444); |
63 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver." ); |
64 | module_param_hw_array(dma8, int, dma, NULL, 0444); |
65 | MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver." ); |
66 | |
67 | #ifdef CONFIG_PNP |
68 | #define is_isapnp_selected(dev) isapnp[dev] |
69 | #else |
70 | #define is_isapnp_selected(dev) 0 |
71 | #endif |
72 | |
73 | static int snd_es1688_match(struct device *dev, unsigned int n) |
74 | { |
75 | return enable[n] && !is_isapnp_selected(n); |
76 | } |
77 | |
78 | static int snd_es1688_legacy_create(struct snd_card *card, |
79 | struct device *dev, unsigned int n) |
80 | { |
81 | struct snd_es1688 *chip = card->private_data; |
82 | static const long possible_ports[] = {0x220, 0x240, 0x260}; |
83 | static const int possible_irqs[] = {5, 9, 10, 7, -1}; |
84 | static const int possible_dmas[] = {1, 3, 0, -1}; |
85 | |
86 | int i, error; |
87 | |
88 | if (irq[n] == SNDRV_AUTO_IRQ) { |
89 | irq[n] = snd_legacy_find_free_irq(irq_table: possible_irqs); |
90 | if (irq[n] < 0) { |
91 | dev_err(dev, "unable to find a free IRQ\n" ); |
92 | return -EBUSY; |
93 | } |
94 | } |
95 | if (dma8[n] == SNDRV_AUTO_DMA) { |
96 | dma8[n] = snd_legacy_find_free_dma(dma_table: possible_dmas); |
97 | if (dma8[n] < 0) { |
98 | dev_err(dev, "unable to find a free DMA\n" ); |
99 | return -EBUSY; |
100 | } |
101 | } |
102 | |
103 | if (port[n] != SNDRV_AUTO_PORT) |
104 | return snd_es1688_create(card, chip, port: port[n], mpu_port: mpu_port[n], |
105 | irq: irq[n], mpu_irq: mpu_irq[n], dma8: dma8[n], ES1688_HW_AUTO); |
106 | |
107 | i = 0; |
108 | do { |
109 | port[n] = possible_ports[i]; |
110 | error = snd_es1688_create(card, chip, port: port[n], mpu_port: mpu_port[n], |
111 | irq: irq[n], mpu_irq: mpu_irq[n], dma8: dma8[n], ES1688_HW_AUTO); |
112 | } while (error < 0 && ++i < ARRAY_SIZE(possible_ports)); |
113 | |
114 | return error; |
115 | } |
116 | |
117 | static int snd_es1688_probe(struct snd_card *card, unsigned int n) |
118 | { |
119 | struct snd_es1688 *chip = card->private_data; |
120 | struct snd_opl3 *opl3; |
121 | int error; |
122 | |
123 | error = snd_es1688_pcm(card, chip, device: 0); |
124 | if (error < 0) |
125 | return error; |
126 | |
127 | error = snd_es1688_mixer(card, chip); |
128 | if (error < 0) |
129 | return error; |
130 | |
131 | strscpy(card->driver, "ES1688" , sizeof(card->driver)); |
132 | strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); |
133 | scnprintf(buf: card->longname, size: sizeof(card->longname), |
134 | fmt: "%s at 0x%lx, irq %i, dma %i" , chip->pcm->name, chip->port, |
135 | chip->irq, chip->dma8); |
136 | |
137 | if (fm_port[n] == SNDRV_AUTO_PORT) |
138 | fm_port[n] = port[n]; /* share the same port */ |
139 | |
140 | if (fm_port[n] > 0) { |
141 | if (snd_opl3_create(card, l_port: fm_port[n], r_port: fm_port[n] + 2, |
142 | OPL3_HW_OPL3, integrated: 0, opl3: &opl3) < 0) |
143 | dev_warn(card->dev, |
144 | "opl3 not detected at 0x%lx\n" , fm_port[n]); |
145 | else { |
146 | error = snd_opl3_hwdep_new(opl3, device: 0, seq_device: 1, NULL); |
147 | if (error < 0) |
148 | return error; |
149 | } |
150 | } |
151 | |
152 | if (mpu_irq[n] >= 0 && mpu_irq[n] != SNDRV_AUTO_IRQ && |
153 | chip->mpu_port > 0) { |
154 | error = snd_mpu401_uart_new(card, device: 0, MPU401_HW_ES1688, |
155 | port: chip->mpu_port, info_flags: 0, |
156 | irq: mpu_irq[n], NULL); |
157 | if (error < 0) |
158 | return error; |
159 | } |
160 | |
161 | return snd_card_register(card); |
162 | } |
163 | |
164 | static int snd_es1688_isa_probe(struct device *dev, unsigned int n) |
165 | { |
166 | struct snd_card *card; |
167 | int error; |
168 | |
169 | error = snd_devm_card_new(parent: dev, idx: index[n], xid: id[n], THIS_MODULE, |
170 | extra_size: sizeof(struct snd_es1688), card_ret: &card); |
171 | if (error < 0) |
172 | return error; |
173 | |
174 | error = snd_es1688_legacy_create(card, dev, n); |
175 | if (error < 0) |
176 | return error; |
177 | |
178 | error = snd_es1688_probe(card, n); |
179 | if (error < 0) |
180 | return error; |
181 | |
182 | dev_set_drvdata(dev, data: card); |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static struct isa_driver snd_es1688_driver = { |
188 | .match = snd_es1688_match, |
189 | .probe = snd_es1688_isa_probe, |
190 | #if 0 /* FIXME */ |
191 | .suspend = snd_es1688_suspend, |
192 | .resume = snd_es1688_resume, |
193 | #endif |
194 | .driver = { |
195 | .name = DEV_NAME |
196 | } |
197 | }; |
198 | |
199 | static int snd_es968_pnp_is_probed; |
200 | |
201 | #ifdef CONFIG_PNP |
202 | static int snd_card_es968_pnp(struct snd_card *card, unsigned int n, |
203 | struct pnp_card_link *pcard, |
204 | const struct pnp_card_device_id *pid) |
205 | { |
206 | struct snd_es1688 *chip = card->private_data; |
207 | struct pnp_dev *pdev; |
208 | int error; |
209 | |
210 | pdev = pnp_request_card_device(clink: pcard, id: pid->devs[0].id, NULL); |
211 | if (pdev == NULL) |
212 | return -ENODEV; |
213 | |
214 | error = pnp_activate_dev(dev: pdev); |
215 | if (error < 0) { |
216 | snd_printk(KERN_ERR "ES968 pnp configure failure\n" ); |
217 | return error; |
218 | } |
219 | port[n] = pnp_port_start(dev: pdev, bar: 0); |
220 | dma8[n] = pnp_dma(dev: pdev, bar: 0); |
221 | irq[n] = pnp_irq(dev: pdev, bar: 0); |
222 | |
223 | return snd_es1688_create(card, chip, port: port[n], mpu_port: mpu_port[n], irq: irq[n], |
224 | mpu_irq: mpu_irq[n], dma8: dma8[n], ES1688_HW_AUTO); |
225 | } |
226 | |
227 | static int snd_es968_pnp_detect(struct pnp_card_link *pcard, |
228 | const struct pnp_card_device_id *pid) |
229 | { |
230 | struct snd_card *card; |
231 | static unsigned int dev; |
232 | int error; |
233 | |
234 | if (snd_es968_pnp_is_probed) |
235 | return -EBUSY; |
236 | for ( ; dev < SNDRV_CARDS; dev++) { |
237 | if (enable[dev] && isapnp[dev]) |
238 | break; |
239 | } |
240 | if (dev == SNDRV_CARDS) |
241 | return -ENODEV; |
242 | |
243 | error = snd_devm_card_new(parent: &pcard->card->dev, |
244 | idx: index[dev], xid: id[dev], THIS_MODULE, |
245 | extra_size: sizeof(struct snd_es1688), card_ret: &card); |
246 | if (error < 0) |
247 | return error; |
248 | |
249 | error = snd_card_es968_pnp(card, n: dev, pcard, pid); |
250 | if (error < 0) |
251 | return error; |
252 | error = snd_es1688_probe(card, n: dev); |
253 | if (error < 0) |
254 | return error; |
255 | pnp_set_card_drvdata(pcard, data: card); |
256 | snd_es968_pnp_is_probed = 1; |
257 | return 0; |
258 | } |
259 | |
260 | static void snd_es968_pnp_remove(struct pnp_card_link *pcard) |
261 | { |
262 | snd_es968_pnp_is_probed = 0; |
263 | } |
264 | |
265 | #ifdef CONFIG_PM |
266 | static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, |
267 | pm_message_t state) |
268 | { |
269 | struct snd_card *card = pnp_get_card_drvdata(pcard); |
270 | |
271 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); |
272 | return 0; |
273 | } |
274 | |
275 | static int snd_es968_pnp_resume(struct pnp_card_link *pcard) |
276 | { |
277 | struct snd_card *card = pnp_get_card_drvdata(pcard); |
278 | struct snd_es1688 *chip = card->private_data; |
279 | |
280 | snd_es1688_reset(chip); |
281 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); |
282 | return 0; |
283 | } |
284 | #endif |
285 | |
286 | static const struct pnp_card_device_id snd_es968_pnpids[] = { |
287 | { .id = "ESS0968" , .devs = { { "@@@0968" }, } }, |
288 | { .id = "ESS0968" , .devs = { { "ESS0968" }, } }, |
289 | { .id = "" , } /* end */ |
290 | }; |
291 | |
292 | MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids); |
293 | |
294 | static struct pnp_card_driver es968_pnpc_driver = { |
295 | .flags = PNP_DRIVER_RES_DISABLE, |
296 | .name = DEV_NAME " PnP" , |
297 | .id_table = snd_es968_pnpids, |
298 | .probe = snd_es968_pnp_detect, |
299 | .remove = snd_es968_pnp_remove, |
300 | #ifdef CONFIG_PM |
301 | .suspend = snd_es968_pnp_suspend, |
302 | .resume = snd_es968_pnp_resume, |
303 | #endif |
304 | }; |
305 | #endif |
306 | |
307 | static int __init alsa_card_es1688_init(void) |
308 | { |
309 | #ifdef CONFIG_PNP |
310 | pnp_register_card_driver(drv: &es968_pnpc_driver); |
311 | if (snd_es968_pnp_is_probed) |
312 | return 0; |
313 | pnp_unregister_card_driver(drv: &es968_pnpc_driver); |
314 | #endif |
315 | return isa_register_driver(&snd_es1688_driver, SNDRV_CARDS); |
316 | } |
317 | |
318 | static void __exit alsa_card_es1688_exit(void) |
319 | { |
320 | if (!snd_es968_pnp_is_probed) { |
321 | isa_unregister_driver(&snd_es1688_driver); |
322 | return; |
323 | } |
324 | #ifdef CONFIG_PNP |
325 | pnp_unregister_card_driver(drv: &es968_pnpc_driver); |
326 | #endif |
327 | } |
328 | |
329 | module_init(alsa_card_es1688_init); |
330 | module_exit(alsa_card_es1688_exit); |
331 | |