1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for generic MPU-401 boards (UART mode only) |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
5 | * Copyright (c) 2004 by Castet Matthieu <castet.matthieu@free.fr> |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/pnp.h> |
10 | #include <linux/err.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/module.h> |
13 | #include <sound/core.h> |
14 | #include <sound/mpu401.h> |
15 | #include <sound/initval.h> |
16 | |
17 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>" ); |
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 */ |
23 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ |
24 | #ifdef CONFIG_PNP |
25 | static bool pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
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 */ |
29 | static bool uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
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 |
41 | module_param_hw_array(port, long, ioport, NULL, 0444); |
42 | MODULE_PARM_DESC(port, "Port # for MPU-401 device." ); |
43 | module_param_hw_array(irq, int, irq, NULL, 0444); |
44 | MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device." ); |
45 | module_param_array(uart_enter, bool, NULL, 0444); |
46 | MODULE_PARM_DESC(uart_enter, "Issue UART_ENTER command at open." ); |
47 | |
48 | static struct platform_device *platform_devices[SNDRV_CARDS]; |
49 | static int pnp_registered; |
50 | static unsigned int snd_mpu401_devices; |
51 | |
52 | static int snd_mpu401_create(struct device *devptr, int dev, |
53 | struct snd_card **rcard) |
54 | { |
55 | struct snd_card *card; |
56 | int err; |
57 | |
58 | if (!uart_enter[dev]) |
59 | snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n" ); |
60 | |
61 | *rcard = NULL; |
62 | err = snd_devm_card_new(parent: devptr, idx: index[dev], xid: id[dev], THIS_MODULE, |
63 | extra_size: 0, card_ret: &card); |
64 | if (err < 0) |
65 | return err; |
66 | strcpy(p: card->driver, q: "MPU-401 UART" ); |
67 | strcpy(p: card->shortname, q: card->driver); |
68 | sprintf(buf: card->longname, fmt: "%s at %#lx, " , card->shortname, port[dev]); |
69 | if (irq[dev] >= 0) { |
70 | sprintf(buf: card->longname + strlen(card->longname), fmt: "irq %d" , irq[dev]); |
71 | } else { |
72 | strcat(p: card->longname, q: "polled" ); |
73 | } |
74 | |
75 | err = snd_mpu401_uart_new(card, device: 0, MPU401_HW_MPU401, port: port[dev], info_flags: 0, |
76 | irq: irq[dev], NULL); |
77 | if (err < 0) { |
78 | printk(KERN_ERR "MPU401 not detected at 0x%lx\n" , port[dev]); |
79 | return err; |
80 | } |
81 | |
82 | *rcard = card; |
83 | return 0; |
84 | } |
85 | |
86 | static int snd_mpu401_probe(struct platform_device *devptr) |
87 | { |
88 | int dev = devptr->id; |
89 | int err; |
90 | struct snd_card *card; |
91 | |
92 | if (port[dev] == SNDRV_AUTO_PORT) { |
93 | snd_printk(KERN_ERR "specify port\n" ); |
94 | return -EINVAL; |
95 | } |
96 | if (irq[dev] == SNDRV_AUTO_IRQ) { |
97 | snd_printk(KERN_ERR "specify or disable IRQ\n" ); |
98 | return -EINVAL; |
99 | } |
100 | err = snd_mpu401_create(devptr: &devptr->dev, dev, rcard: &card); |
101 | if (err < 0) |
102 | return err; |
103 | err = snd_card_register(card); |
104 | if (err < 0) |
105 | return err; |
106 | platform_set_drvdata(pdev: devptr, data: card); |
107 | return 0; |
108 | } |
109 | |
110 | #define SND_MPU401_DRIVER "snd_mpu401" |
111 | |
112 | static struct platform_driver snd_mpu401_driver = { |
113 | .probe = snd_mpu401_probe, |
114 | .driver = { |
115 | .name = SND_MPU401_DRIVER, |
116 | }, |
117 | }; |
118 | |
119 | |
120 | #ifdef CONFIG_PNP |
121 | |
122 | #define IO_EXTENT 2 |
123 | |
124 | static const struct pnp_device_id snd_mpu401_pnpids[] = { |
125 | { .id = "PNPb006" }, |
126 | { .id = "" } |
127 | }; |
128 | |
129 | MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids); |
130 | |
131 | static int snd_mpu401_pnp(int dev, struct pnp_dev *device, |
132 | const struct pnp_device_id *id) |
133 | { |
134 | if (!pnp_port_valid(dev: device, bar: 0) || |
135 | pnp_port_flags(dev: device, bar: 0) & IORESOURCE_DISABLED) { |
136 | snd_printk(KERN_ERR "no PnP port\n" ); |
137 | return -ENODEV; |
138 | } |
139 | if (pnp_port_len(dev: device, bar: 0) < IO_EXTENT) { |
140 | snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n" , |
141 | (unsigned long long)pnp_port_len(device, 0), |
142 | IO_EXTENT); |
143 | return -ENODEV; |
144 | } |
145 | port[dev] = pnp_port_start(dev: device, bar: 0); |
146 | |
147 | if (!pnp_irq_valid(dev: device, bar: 0) || |
148 | pnp_irq_flags(dev: device, bar: 0) & IORESOURCE_DISABLED) { |
149 | snd_printk(KERN_WARNING "no PnP irq, using polling\n" ); |
150 | irq[dev] = -1; |
151 | } else { |
152 | irq[dev] = pnp_irq(dev: device, bar: 0); |
153 | } |
154 | return 0; |
155 | } |
156 | |
157 | static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev, |
158 | const struct pnp_device_id *id) |
159 | { |
160 | static int dev; |
161 | struct snd_card *card; |
162 | int err; |
163 | |
164 | for ( ; dev < SNDRV_CARDS; ++dev) { |
165 | if (!enable[dev] || !pnp[dev]) |
166 | continue; |
167 | err = snd_mpu401_pnp(dev, device: pnp_dev, id); |
168 | if (err < 0) |
169 | return err; |
170 | err = snd_mpu401_create(devptr: &pnp_dev->dev, dev, rcard: &card); |
171 | if (err < 0) |
172 | return err; |
173 | err = snd_card_register(card); |
174 | if (err < 0) |
175 | return err; |
176 | pnp_set_drvdata(pdev: pnp_dev, data: card); |
177 | snd_mpu401_devices++; |
178 | ++dev; |
179 | return 0; |
180 | } |
181 | return -ENODEV; |
182 | } |
183 | |
184 | static struct pnp_driver snd_mpu401_pnp_driver = { |
185 | .name = "mpu401" , |
186 | .id_table = snd_mpu401_pnpids, |
187 | .probe = snd_mpu401_pnp_probe, |
188 | }; |
189 | #else |
190 | static struct pnp_driver snd_mpu401_pnp_driver; |
191 | #endif |
192 | |
193 | static void snd_mpu401_unregister_all(void) |
194 | { |
195 | int i; |
196 | |
197 | if (pnp_registered) |
198 | pnp_unregister_driver(drv: &snd_mpu401_pnp_driver); |
199 | for (i = 0; i < ARRAY_SIZE(platform_devices); ++i) |
200 | platform_device_unregister(platform_devices[i]); |
201 | platform_driver_unregister(&snd_mpu401_driver); |
202 | } |
203 | |
204 | static int __init alsa_card_mpu401_init(void) |
205 | { |
206 | int i, err; |
207 | |
208 | err = platform_driver_register(&snd_mpu401_driver); |
209 | if (err < 0) |
210 | return err; |
211 | |
212 | for (i = 0; i < SNDRV_CARDS; i++) { |
213 | struct platform_device *device; |
214 | if (! enable[i]) |
215 | continue; |
216 | #ifdef CONFIG_PNP |
217 | if (pnp[i]) |
218 | continue; |
219 | #endif |
220 | device = platform_device_register_simple(SND_MPU401_DRIVER, |
221 | id: i, NULL, num: 0); |
222 | if (IS_ERR(ptr: device)) |
223 | continue; |
224 | if (!platform_get_drvdata(pdev: device)) { |
225 | platform_device_unregister(device); |
226 | continue; |
227 | } |
228 | platform_devices[i] = device; |
229 | snd_mpu401_devices++; |
230 | } |
231 | err = pnp_register_driver(drv: &snd_mpu401_pnp_driver); |
232 | if (!err) |
233 | pnp_registered = 1; |
234 | |
235 | if (!snd_mpu401_devices) { |
236 | #ifdef MODULE |
237 | printk(KERN_ERR "MPU-401 device not found or device busy\n" ); |
238 | #endif |
239 | snd_mpu401_unregister_all(); |
240 | return -ENODEV; |
241 | } |
242 | return 0; |
243 | } |
244 | |
245 | static void __exit alsa_card_mpu401_exit(void) |
246 | { |
247 | snd_mpu401_unregister_all(); |
248 | } |
249 | |
250 | module_init(alsa_card_mpu401_init) |
251 | module_exit(alsa_card_mpu401_exit) |
252 | |