Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Driver for generic MPU-401 boards (UART mode only) | |
c1017a4c | 4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4 | 5 | * Copyright (c) 2004 by Castet Matthieu <castet.matthieu@free.fr> |
1da177e4 LT |
6 | */ |
7 | ||
1da177e4 LT |
8 | #include <linux/init.h> |
9 | #include <linux/pnp.h> | |
b3fe9512 TI |
10 | #include <linux/err.h> |
11 | #include <linux/platform_device.h> | |
65a77217 | 12 | #include <linux/module.h> |
1da177e4 LT |
13 | #include <sound/core.h> |
14 | #include <sound/mpu401.h> | |
15 | #include <sound/initval.h> | |
16 | ||
c1017a4c | 17 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
1da177e4 LT |
18 | MODULE_DESCRIPTION("MPU-401 UART"); |
19 | MODULE_LICENSE("GPL"); | |
20 | ||
21 | static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* exclude the first card */ | |
22 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | |
a67ff6a5 | 23 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ |
1da177e4 | 24 | #ifdef CONFIG_PNP |
a67ff6a5 | 25 | static bool pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
1da177e4 LT |
26 | #endif |
27 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ | |
28 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ | |
a67ff6a5 | 29 | static bool uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
1da177e4 LT |
30 | |
31 | module_param_array(index, int, NULL, 0444); | |
32 | MODULE_PARM_DESC(index, "Index value for MPU-401 device."); | |
33 | module_param_array(id, charp, NULL, 0444); | |
34 | MODULE_PARM_DESC(id, "ID string for MPU-401 device."); | |
35 | module_param_array(enable, bool, NULL, 0444); | |
36 | MODULE_PARM_DESC(enable, "Enable MPU-401 device."); | |
37 | #ifdef CONFIG_PNP | |
38 | module_param_array(pnp, bool, NULL, 0444); | |
39 | MODULE_PARM_DESC(pnp, "PnP detection for MPU-401 device."); | |
40 | #endif | |
b11ce420 | 41 | module_param_hw_array(port, long, ioport, NULL, 0444); |
1da177e4 | 42 | MODULE_PARM_DESC(port, "Port # for MPU-401 device."); |
b11ce420 | 43 | module_param_hw_array(irq, int, irq, NULL, 0444); |
1da177e4 | 44 | MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); |
8f7ba051 TI |
45 | module_param_array(uart_enter, bool, NULL, 0444); |
46 | MODULE_PARM_DESC(uart_enter, "Issue UART_ENTER command at open."); | |
1da177e4 | 47 | |
f7a9275d | 48 | static struct platform_device *platform_devices[SNDRV_CARDS]; |
f301ae6a BH |
49 | static int pnp_registered; |
50 | static unsigned int snd_mpu401_devices; | |
1da177e4 | 51 | |
5872f3f6 TI |
52 | static int snd_mpu401_create(struct device *devptr, int dev, |
53 | struct snd_card **rcard) | |
1da177e4 | 54 | { |
e1fad17b | 55 | struct snd_card *card; |
1da177e4 LT |
56 | int err; |
57 | ||
c1099fcb CL |
58 | if (!uart_enter[dev]) |
59 | snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n"); | |
60 | ||
1da177e4 | 61 | *rcard = NULL; |
5872f3f6 TI |
62 | err = snd_card_new(devptr, index[dev], id[dev], THIS_MODULE, |
63 | 0, &card); | |
bd7dd77c TI |
64 | if (err < 0) |
65 | return err; | |
1da177e4 LT |
66 | strcpy(card->driver, "MPU-401 UART"); |
67 | strcpy(card->shortname, card->driver); | |
68 | sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]); | |
69 | if (irq[dev] >= 0) { | |
70 | sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]); | |
71 | } else { | |
72 | strcat(card->longname, "polled"); | |
73 | } | |
74 | ||
c1099fcb | 75 | err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0, |
dba8b469 | 76 | irq[dev], NULL); |
8f7ba051 | 77 | if (err < 0) { |
1da177e4 | 78 | printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]); |
16dab54b | 79 | goto _err; |
1da177e4 | 80 | } |
16dab54b | 81 | |
1da177e4 LT |
82 | *rcard = card; |
83 | return 0; | |
16dab54b TI |
84 | |
85 | _err: | |
86 | snd_card_free(card); | |
87 | return err; | |
1da177e4 LT |
88 | } |
89 | ||
fbbb01a1 | 90 | static int snd_mpu401_probe(struct platform_device *devptr) |
1da177e4 | 91 | { |
b3fe9512 TI |
92 | int dev = devptr->id; |
93 | int err; | |
94 | struct snd_card *card; | |
95 | ||
1da177e4 LT |
96 | if (port[dev] == SNDRV_AUTO_PORT) { |
97 | snd_printk(KERN_ERR "specify port\n"); | |
98 | return -EINVAL; | |
99 | } | |
100 | if (irq[dev] == SNDRV_AUTO_IRQ) { | |
101 | snd_printk(KERN_ERR "specify or disable IRQ\n"); | |
102 | return -EINVAL; | |
103 | } | |
5872f3f6 | 104 | err = snd_mpu401_create(&devptr->dev, dev, &card); |
b3fe9512 TI |
105 | if (err < 0) |
106 | return err; | |
b3fe9512 TI |
107 | if ((err = snd_card_register(card)) < 0) { |
108 | snd_card_free(card); | |
109 | return err; | |
110 | } | |
111 | platform_set_drvdata(devptr, card); | |
112 | return 0; | |
1da177e4 LT |
113 | } |
114 | ||
fbbb01a1 | 115 | static int snd_mpu401_remove(struct platform_device *devptr) |
b3fe9512 TI |
116 | { |
117 | snd_card_free(platform_get_drvdata(devptr)); | |
b3fe9512 TI |
118 | return 0; |
119 | } | |
120 | ||
121 | #define SND_MPU401_DRIVER "snd_mpu401" | |
122 | ||
123 | static struct platform_driver snd_mpu401_driver = { | |
124 | .probe = snd_mpu401_probe, | |
fbbb01a1 | 125 | .remove = snd_mpu401_remove, |
b3fe9512 | 126 | .driver = { |
8bf01d8a | 127 | .name = SND_MPU401_DRIVER, |
b3fe9512 TI |
128 | }, |
129 | }; | |
130 | ||
131 | ||
1da177e4 LT |
132 | #ifdef CONFIG_PNP |
133 | ||
134 | #define IO_EXTENT 2 | |
135 | ||
1491c68a | 136 | static const struct pnp_device_id snd_mpu401_pnpids[] = { |
1da177e4 LT |
137 | { .id = "PNPb006" }, |
138 | { .id = "" } | |
139 | }; | |
140 | ||
141 | MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids); | |
142 | ||
fbbb01a1 BP |
143 | static int snd_mpu401_pnp(int dev, struct pnp_dev *device, |
144 | const struct pnp_device_id *id) | |
1da177e4 LT |
145 | { |
146 | if (!pnp_port_valid(device, 0) || | |
147 | pnp_port_flags(device, 0) & IORESOURCE_DISABLED) { | |
148 | snd_printk(KERN_ERR "no PnP port\n"); | |
149 | return -ENODEV; | |
150 | } | |
151 | if (pnp_port_len(device, 0) < IO_EXTENT) { | |
aa0a2ddc GKH |
152 | snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n", |
153 | (unsigned long long)pnp_port_len(device, 0), | |
154 | IO_EXTENT); | |
1da177e4 LT |
155 | return -ENODEV; |
156 | } | |
157 | port[dev] = pnp_port_start(device, 0); | |
158 | ||
159 | if (!pnp_irq_valid(device, 0) || | |
160 | pnp_irq_flags(device, 0) & IORESOURCE_DISABLED) { | |
161 | snd_printk(KERN_WARNING "no PnP irq, using polling\n"); | |
162 | irq[dev] = -1; | |
163 | } else { | |
164 | irq[dev] = pnp_irq(device, 0); | |
165 | } | |
166 | return 0; | |
167 | } | |
168 | ||
fbbb01a1 BP |
169 | static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev, |
170 | const struct pnp_device_id *id) | |
1da177e4 LT |
171 | { |
172 | static int dev; | |
e1fad17b | 173 | struct snd_card *card; |
1da177e4 LT |
174 | int err; |
175 | ||
176 | for ( ; dev < SNDRV_CARDS; ++dev) { | |
177 | if (!enable[dev] || !pnp[dev]) | |
178 | continue; | |
179 | err = snd_mpu401_pnp(dev, pnp_dev, id); | |
180 | if (err < 0) | |
181 | return err; | |
5872f3f6 | 182 | err = snd_mpu401_create(&pnp_dev->dev, dev, &card); |
1da177e4 LT |
183 | if (err < 0) |
184 | return err; | |
b3fe9512 TI |
185 | if ((err = snd_card_register(card)) < 0) { |
186 | snd_card_free(card); | |
187 | return err; | |
188 | } | |
1da177e4 | 189 | pnp_set_drvdata(pnp_dev, card); |
f301ae6a | 190 | snd_mpu401_devices++; |
1da177e4 LT |
191 | ++dev; |
192 | return 0; | |
193 | } | |
194 | return -ENODEV; | |
195 | } | |
196 | ||
fbbb01a1 | 197 | static void snd_mpu401_pnp_remove(struct pnp_dev *dev) |
1da177e4 | 198 | { |
e1fad17b | 199 | struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev); |
1da177e4 LT |
200 | |
201 | snd_card_disconnect(card); | |
2b29b13c | 202 | snd_card_free_when_closed(card); |
1da177e4 LT |
203 | } |
204 | ||
205 | static struct pnp_driver snd_mpu401_pnp_driver = { | |
206 | .name = "mpu401", | |
207 | .id_table = snd_mpu401_pnpids, | |
208 | .probe = snd_mpu401_pnp_probe, | |
fbbb01a1 | 209 | .remove = snd_mpu401_pnp_remove, |
1da177e4 LT |
210 | }; |
211 | #else | |
212 | static struct pnp_driver snd_mpu401_pnp_driver; | |
213 | #endif | |
214 | ||
c12aad6e | 215 | static void snd_mpu401_unregister_all(void) |
f7a9275d CL |
216 | { |
217 | int i; | |
218 | ||
219 | if (pnp_registered) | |
220 | pnp_unregister_driver(&snd_mpu401_pnp_driver); | |
221 | for (i = 0; i < ARRAY_SIZE(platform_devices); ++i) | |
222 | platform_device_unregister(platform_devices[i]); | |
223 | platform_driver_unregister(&snd_mpu401_driver); | |
224 | } | |
225 | ||
1da177e4 LT |
226 | static int __init alsa_card_mpu401_init(void) |
227 | { | |
f301ae6a | 228 | int i, err; |
1da177e4 | 229 | |
b3fe9512 TI |
230 | if ((err = platform_driver_register(&snd_mpu401_driver)) < 0) |
231 | return err; | |
232 | ||
8278ca8f | 233 | for (i = 0; i < SNDRV_CARDS; i++) { |
b3fe9512 | 234 | struct platform_device *device; |
8278ca8f TI |
235 | if (! enable[i]) |
236 | continue; | |
1da177e4 | 237 | #ifdef CONFIG_PNP |
b3fe9512 | 238 | if (pnp[i]) |
1da177e4 LT |
239 | continue; |
240 | #endif | |
b3fe9512 TI |
241 | device = platform_device_register_simple(SND_MPU401_DRIVER, |
242 | i, NULL, 0); | |
a182ee98 RH |
243 | if (IS_ERR(device)) |
244 | continue; | |
7152447d RH |
245 | if (!platform_get_drvdata(device)) { |
246 | platform_device_unregister(device); | |
247 | continue; | |
248 | } | |
f7a9275d | 249 | platform_devices[i] = device; |
f301ae6a | 250 | snd_mpu401_devices++; |
1da177e4 | 251 | } |
f301ae6a BH |
252 | err = pnp_register_driver(&snd_mpu401_pnp_driver); |
253 | if (!err) | |
1da177e4 | 254 | pnp_registered = 1; |
1da177e4 | 255 | |
f301ae6a | 256 | if (!snd_mpu401_devices) { |
1da177e4 LT |
257 | #ifdef MODULE |
258 | printk(KERN_ERR "MPU-401 device not found or device busy\n"); | |
259 | #endif | |
a182ee98 RH |
260 | snd_mpu401_unregister_all(); |
261 | return -ENODEV; | |
1da177e4 LT |
262 | } |
263 | return 0; | |
264 | } | |
265 | ||
266 | static void __exit alsa_card_mpu401_exit(void) | |
267 | { | |
f7a9275d | 268 | snd_mpu401_unregister_all(); |
1da177e4 LT |
269 | } |
270 | ||
271 | module_init(alsa_card_mpu401_init) | |
272 | module_exit(alsa_card_mpu401_exit) |