Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Driver for Digigram VXpocket V2/440 soundcards | |
4 | * | |
5 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | |
1ac71e5a | 6 | |
1da177e4 LT |
7 | */ |
8 | ||
1da177e4 | 9 | |
1da177e4 | 10 | #include <linux/init.h> |
65a77217 | 11 | #include <linux/module.h> |
5a0e3ad6 | 12 | #include <linux/slab.h> |
1da177e4 | 13 | #include <sound/core.h> |
1da177e4 | 14 | #include "vxpocket.h" |
6d00a312 TI |
15 | #include <pcmcia/ciscode.h> |
16 | #include <pcmcia/cisreg.h> | |
1da177e4 | 17 | #include <sound/initval.h> |
1186ed8c | 18 | #include <sound/tlv.h> |
1da177e4 | 19 | |
1da177e4 | 20 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); |
6d00a312 | 21 | MODULE_DESCRIPTION("Digigram VXPocket"); |
1da177e4 | 22 | MODULE_LICENSE("GPL"); |
1da177e4 LT |
23 | |
24 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | |
25 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | |
a67ff6a5 | 26 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ |
1da177e4 LT |
27 | static int ibl[SNDRV_CARDS]; |
28 | ||
29 | module_param_array(index, int, NULL, 0444); | |
6d00a312 | 30 | MODULE_PARM_DESC(index, "Index value for VXPocket soundcard."); |
1da177e4 | 31 | module_param_array(id, charp, NULL, 0444); |
6d00a312 | 32 | MODULE_PARM_DESC(id, "ID string for VXPocket soundcard."); |
1da177e4 | 33 | module_param_array(enable, bool, NULL, 0444); |
6d00a312 | 34 | MODULE_PARM_DESC(enable, "Enable VXPocket soundcard."); |
1da177e4 | 35 | module_param_array(ibl, int, NULL, 0444); |
6d00a312 | 36 | MODULE_PARM_DESC(ibl, "Capture IBL size for VXPocket soundcard."); |
1da177e4 LT |
37 | |
38 | ||
39 | /* | |
40 | */ | |
41 | ||
6d00a312 | 42 | static unsigned int card_alloc; |
1da177e4 | 43 | |
1da177e4 | 44 | |
6d00a312 TI |
45 | /* |
46 | */ | |
fba395ee | 47 | static void vxpocket_release(struct pcmcia_device *link) |
6d00a312 | 48 | { |
db0a5214 | 49 | free_irq(link->irq, link->priv); |
fba395ee | 50 | pcmcia_disable_device(link); |
6d00a312 | 51 | } |
1da177e4 | 52 | |
6d00a312 TI |
53 | /* |
54 | * Hardware information | |
55 | */ | |
56 | ||
57 | /* VX-pocket V2 | |
58 | * | |
59 | * 1 DSP, 1 sync UER | |
60 | * 1 programmable clock (NIY) | |
61 | * 1 stereo analog input (line/micro) | |
62 | * 1 stereo analog output | |
63 | * Only output levels can be modified | |
64 | */ | |
65 | ||
0cb29ea0 | 66 | static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); |
1186ed8c | 67 | |
f8ae2d29 | 68 | static const struct snd_vx_hardware vxpocket_hw = { |
6d00a312 TI |
69 | .name = "VXPocket", |
70 | .type = VX_TYPE_VXPOCKET, | |
1da177e4 LT |
71 | |
72 | /* hardware specs */ | |
6d00a312 TI |
73 | .num_codecs = 1, |
74 | .num_ins = 1, | |
75 | .num_outs = 1, | |
1da177e4 | 76 | .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, |
1186ed8c | 77 | .output_level_db_scale = db_scale_old_vol, |
1da177e4 LT |
78 | }; |
79 | ||
6d00a312 TI |
80 | /* VX-pocket 440 |
81 | * | |
82 | * 1 DSP, 1 sync UER, 1 sync World Clock (NIY) | |
83 | * SMPTE (NIY) | |
84 | * 2 stereo analog input (line/micro) | |
85 | * 2 stereo analog output | |
86 | * Only output levels can be modified | |
87 | * UER, but only for the first two inputs and outputs. | |
88 | */ | |
1da177e4 | 89 | |
f8ae2d29 | 90 | static const struct snd_vx_hardware vxp440_hw = { |
6d00a312 TI |
91 | .name = "VXPocket440", |
92 | .type = VX_TYPE_VXP440, | |
93 | ||
94 | /* hardware specs */ | |
95 | .num_codecs = 2, | |
96 | .num_ins = 2, | |
97 | .num_outs = 2, | |
98 | .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, | |
1186ed8c | 99 | .output_level_db_scale = db_scale_old_vol, |
6d00a312 TI |
100 | }; |
101 | ||
102 | ||
103 | /* | |
104 | * create vxpocket instance | |
105 | */ | |
2fa51107 TI |
106 | static int snd_vxpocket_new(struct snd_card *card, int ibl, |
107 | struct pcmcia_device *link, | |
108 | struct snd_vxpocket **chip_ret) | |
6d00a312 | 109 | { |
af26367f | 110 | struct vx_core *chip; |
6d00a312 | 111 | struct snd_vxpocket *vxp; |
6d00a312 TI |
112 | |
113 | chip = snd_vx_create(card, &vxpocket_hw, &snd_vxpocket_ops, | |
af26367f | 114 | sizeof(struct snd_vxpocket) - sizeof(struct vx_core)); |
2fa51107 TI |
115 | if (!chip) |
116 | return -ENOMEM; | |
6d00a312 | 117 | |
6d00a312 TI |
118 | chip->ibl.size = ibl; |
119 | ||
2e0de6ea | 120 | vxp = to_vxpocket(chip); |
6d00a312 | 121 | |
fba395ee | 122 | vxp->p_dev = link; |
6d00a312 TI |
123 | link->priv = chip; |
124 | ||
90abdc3b DB |
125 | link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; |
126 | link->resource[0]->end = 16; | |
6d00a312 | 127 | |
1ac71e5a | 128 | link->config_flags |= CONF_ENABLE_IRQ; |
7feabb64 DB |
129 | link->config_index = 1; |
130 | link->config_regs = PRESENT_OPTION; | |
6d00a312 | 131 | |
2fa51107 TI |
132 | *chip_ret = vxp; |
133 | return 0; | |
6d00a312 TI |
134 | } |
135 | ||
136 | ||
137 | /** | |
138 | * snd_vxpocket_assign_resources - initialize the hardware and card instance. | |
2a9e8df0 | 139 | * @chip: VX core instance |
6d00a312 TI |
140 | * @port: i/o port for the card |
141 | * @irq: irq number for the card | |
142 | * | |
143 | * this function assigns the specified port and irq, boot the card, | |
144 | * create pcm and control instances, and initialize the rest hardware. | |
145 | * | |
146 | * returns 0 if successful, or a negative error code. | |
147 | */ | |
af26367f | 148 | static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq) |
6d00a312 TI |
149 | { |
150 | int err; | |
af26367f | 151 | struct snd_card *card = chip->card; |
2e0de6ea | 152 | struct snd_vxpocket *vxp = to_vxpocket(chip); |
6d00a312 TI |
153 | |
154 | snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq); | |
155 | vxp->port = port; | |
156 | ||
157 | sprintf(card->shortname, "Digigram %s", card->driver); | |
158 | sprintf(card->longname, "%s at 0x%x, irq %i", | |
159 | card->shortname, port, irq); | |
160 | ||
161 | chip->irq = irq; | |
0ca4cefa | 162 | card->sync_irq = chip->irq; |
6d00a312 | 163 | |
2073fa44 TI |
164 | err = snd_vx_setup_firmware(chip); |
165 | if (err < 0) | |
6d00a312 TI |
166 | return err; |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | ||
172 | /* | |
173 | * configuration callback | |
174 | */ | |
175 | ||
15b99ac1 | 176 | static int vxpocket_config(struct pcmcia_device *link) |
6d00a312 | 177 | { |
af26367f | 178 | struct vx_core *chip = link->priv; |
7c5af6ff | 179 | int ret; |
6d00a312 TI |
180 | |
181 | snd_printdd(KERN_DEBUG "vxpocket_config called\n"); | |
6d00a312 TI |
182 | |
183 | /* redefine hardware record according to the VERSION1 string */ | |
af2b3b50 | 184 | if (!strcmp(link->prod_id[1], "VX-POCKET")) { |
6d00a312 TI |
185 | snd_printdd("VX-pocket is detected\n"); |
186 | } else { | |
187 | snd_printdd("VX-pocket 440 is detected\n"); | |
188 | /* overwrite the hardware information */ | |
189 | chip->hw = &vxp440_hw; | |
190 | chip->type = vxp440_hw.type; | |
191 | strcpy(chip->card->driver, vxp440_hw.name); | |
192 | } | |
193 | ||
90abdc3b | 194 | ret = pcmcia_request_io(link); |
7c5af6ff | 195 | if (ret) |
db0a5214 | 196 | goto failed_preirq; |
7c5af6ff | 197 | |
db0a5214 TI |
198 | ret = request_threaded_irq(link->irq, snd_vx_irq_handler, |
199 | snd_vx_threaded_irq_handler, | |
200 | IRQF_SHARED, link->devname, link->priv); | |
7c5af6ff | 201 | if (ret) |
db0a5214 | 202 | goto failed_preirq; |
7c5af6ff | 203 | |
1ac71e5a | 204 | ret = pcmcia_enable_device(link); |
7c5af6ff DB |
205 | if (ret) |
206 | goto failed; | |
6d00a312 | 207 | |
dd2e5a15 | 208 | chip->dev = &link->dev; |
6d00a312 | 209 | |
9a017a91 DB |
210 | if (snd_vxpocket_assign_resources(chip, link->resource[0]->start, |
211 | link->irq) < 0) | |
6d00a312 TI |
212 | goto failed; |
213 | ||
79ca4f3f | 214 | return 0; |
6d00a312 | 215 | |
db0a5214 TI |
216 | failed: |
217 | free_irq(link->irq, link->priv); | |
218 | failed_preirq: | |
fba395ee | 219 | pcmcia_disable_device(link); |
15b99ac1 | 220 | return -ENODEV; |
6d00a312 TI |
221 | } |
222 | ||
efe3cd10 | 223 | #ifdef CONFIG_PM |
6d00a312 | 224 | |
fba395ee | 225 | static int vxp_suspend(struct pcmcia_device *link) |
6d00a312 | 226 | { |
af26367f | 227 | struct vx_core *chip = link->priv; |
6d00a312 | 228 | |
efe3cd10 | 229 | snd_printdd(KERN_DEBUG "SUSPEND\n"); |
efe3cd10 DB |
230 | if (chip) { |
231 | snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); | |
68cb2b55 | 232 | snd_vx_suspend(chip); |
efe3cd10 | 233 | } |
efe3cd10 DB |
234 | |
235 | return 0; | |
236 | } | |
237 | ||
fba395ee | 238 | static int vxp_resume(struct pcmcia_device *link) |
efe3cd10 | 239 | { |
efe3cd10 DB |
240 | struct vx_core *chip = link->priv; |
241 | ||
242 | snd_printdd(KERN_DEBUG "RESUME\n"); | |
9940ec36 | 243 | if (pcmcia_dev_present(link)) { |
efe3cd10 | 244 | //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; |
0ed1cad1 | 245 | if (chip) { |
efe3cd10 DB |
246 | snd_printdd(KERN_DEBUG "calling snd_vx_resume\n"); |
247 | snd_vx_resume(chip); | |
6d00a312 | 248 | } |
6d00a312 | 249 | } |
efe3cd10 DB |
250 | snd_printdd(KERN_DEBUG "resume done!\n"); |
251 | ||
6d00a312 TI |
252 | return 0; |
253 | } | |
1da177e4 | 254 | |
efe3cd10 DB |
255 | #endif |
256 | ||
1da177e4 LT |
257 | |
258 | /* | |
259 | */ | |
15b99ac1 | 260 | static int vxpocket_probe(struct pcmcia_device *p_dev) |
1da177e4 | 261 | { |
af26367f | 262 | struct snd_card *card; |
6d00a312 | 263 | struct snd_vxpocket *vxp; |
bd7dd77c | 264 | int i, err; |
6d00a312 TI |
265 | |
266 | /* find an empty slot from the card list */ | |
267 | for (i = 0; i < SNDRV_CARDS; i++) { | |
27fe0f4b | 268 | if (!(card_alloc & (1 << i))) |
6d00a312 TI |
269 | break; |
270 | } | |
271 | if (i >= SNDRV_CARDS) { | |
272 | snd_printk(KERN_ERR "vxpocket: too many cards found\n"); | |
efe3cd10 | 273 | return -EINVAL; |
6d00a312 TI |
274 | } |
275 | if (! enable[i]) | |
efe3cd10 | 276 | return -ENODEV; /* disabled explicitly */ |
6d00a312 TI |
277 | |
278 | /* ok, create a card instance */ | |
5815f555 TI |
279 | err = snd_card_new(&p_dev->dev, index[i], id[i], THIS_MODULE, |
280 | 0, &card); | |
bd7dd77c | 281 | if (err < 0) { |
6d00a312 | 282 | snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n"); |
bd7dd77c | 283 | return err; |
6d00a312 TI |
284 | } |
285 | ||
2fa51107 TI |
286 | err = snd_vxpocket_new(card, ibl[i], p_dev, &vxp); |
287 | if (err < 0) { | |
6d00a312 | 288 | snd_card_free(card); |
2fa51107 | 289 | return err; |
6d00a312 | 290 | } |
0ed1cad1 | 291 | card->private_data = vxp; |
6d00a312 | 292 | |
adf111e6 | 293 | vxp->index = i; |
6d00a312 TI |
294 | card_alloc |= 1 << i; |
295 | ||
fd238232 | 296 | vxp->p_dev = p_dev; |
efe3cd10 | 297 | |
15b99ac1 | 298 | return vxpocket_config(p_dev); |
1da177e4 LT |
299 | } |
300 | ||
fba395ee | 301 | static void vxpocket_detach(struct pcmcia_device *link) |
1da177e4 | 302 | { |
6d00a312 | 303 | struct snd_vxpocket *vxp; |
af26367f | 304 | struct vx_core *chip; |
6d00a312 TI |
305 | |
306 | if (! link) | |
307 | return; | |
308 | ||
309 | vxp = link->priv; | |
af26367f | 310 | chip = (struct vx_core *)vxp; |
6d00a312 TI |
311 | card_alloc &= ~(1 << vxp->index); |
312 | ||
6d00a312 TI |
313 | chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */ |
314 | snd_card_disconnect(chip->card); | |
315 | vxpocket_release(link); | |
2b29b13c | 316 | snd_card_free_when_closed(chip->card); |
1da177e4 LT |
317 | } |
318 | ||
319 | /* | |
320 | * Module entry points | |
321 | */ | |
322 | ||
4ef7e714 | 323 | static const struct pcmcia_device_id vxp_ids[] = { |
e9a07afd DB |
324 | PCMCIA_DEVICE_MANF_CARD(0x01f1, 0x0100), |
325 | PCMCIA_DEVICE_NULL | |
326 | }; | |
327 | MODULE_DEVICE_TABLE(pcmcia, vxp_ids); | |
328 | ||
1da177e4 LT |
329 | static struct pcmcia_driver vxp_cs_driver = { |
330 | .owner = THIS_MODULE, | |
2e9b981a | 331 | .name = "snd-vxpocket", |
15b99ac1 | 332 | .probe = vxpocket_probe, |
efe3cd10 | 333 | .remove = vxpocket_detach, |
e9a07afd | 334 | .id_table = vxp_ids, |
efe3cd10 DB |
335 | #ifdef CONFIG_PM |
336 | .suspend = vxp_suspend, | |
337 | .resume = vxp_resume, | |
338 | #endif | |
1da177e4 | 339 | }; |
b85c4a18 | 340 | module_pcmcia_driver(vxp_cs_driver); |