]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (c) 1998-2002 by Paul Davis <pbd@op.net> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | */ | |
18 | ||
1da177e4 LT |
19 | #include <asm/io.h> |
20 | #include <linux/init.h> | |
21 | #include <linux/time.h> | |
22 | #include <linux/wait.h> | |
226968c7 | 23 | #include <linux/firmware.h> |
1da177e4 LT |
24 | #include <sound/core.h> |
25 | #include <sound/snd_wavefront.h> | |
26 | #include <sound/initval.h> | |
27 | ||
28 | /* Control bits for the Load Control Register | |
29 | */ | |
30 | ||
31 | #define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ | |
32 | #define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ | |
33 | #define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ | |
34 | ||
59540fe8 CL |
35 | #define WAIT_IDLE 0xff |
36 | ||
8ad2da19 | 37 | #ifdef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL |
59540fe8 | 38 | #include "yss225.c" |
226968c7 CL |
39 | static const struct firmware yss225_registers_firmware = { |
40 | .data = (u8 *)yss225_registers, | |
41 | .size = sizeof yss225_registers | |
42 | }; | |
43 | #endif | |
1da177e4 LT |
44 | |
45 | static int | |
46 | wavefront_fx_idle (snd_wavefront_t *dev) | |
47 | ||
48 | { | |
49 | int i; | |
50 | unsigned int x = 0x80; | |
51 | ||
52 | for (i = 0; i < 1000; i++) { | |
53 | x = inb (dev->fx_status); | |
54 | if ((x & 0x80) == 0) { | |
55 | break; | |
56 | } | |
57 | } | |
58 | ||
59 | if (x & 0x80) { | |
60 | snd_printk ("FX device never idle.\n"); | |
61 | return 0; | |
62 | } | |
63 | ||
64 | return (1); | |
65 | } | |
66 | ||
67 | static void | |
68 | wavefront_fx_mute (snd_wavefront_t *dev, int onoff) | |
69 | ||
70 | { | |
71 | if (!wavefront_fx_idle(dev)) { | |
72 | return; | |
73 | } | |
74 | ||
75 | outb (onoff ? 0x02 : 0x00, dev->fx_op); | |
76 | } | |
77 | ||
78 | static int | |
79 | wavefront_fx_memset (snd_wavefront_t *dev, | |
80 | int page, | |
81 | int addr, | |
82 | int cnt, | |
83 | unsigned short *data) | |
84 | { | |
85 | if (page < 0 || page > 7) { | |
86 | snd_printk ("FX memset: " | |
87 | "page must be >= 0 and <= 7\n"); | |
88 | return -(EINVAL); | |
89 | } | |
90 | ||
91 | if (addr < 0 || addr > 0x7f) { | |
92 | snd_printk ("FX memset: " | |
93 | "addr must be >= 0 and <= 7f\n"); | |
94 | return -(EINVAL); | |
95 | } | |
96 | ||
97 | if (cnt == 1) { | |
98 | ||
99 | outb (FX_LSB_TRANSFER, dev->fx_lcr); | |
100 | outb (page, dev->fx_dsp_page); | |
101 | outb (addr, dev->fx_dsp_addr); | |
102 | outb ((data[0] >> 8), dev->fx_dsp_msb); | |
103 | outb ((data[0] & 0xff), dev->fx_dsp_lsb); | |
104 | ||
105 | snd_printk ("FX: addr %d:%x set to 0x%x\n", | |
106 | page, addr, data[0]); | |
107 | ||
108 | } else { | |
109 | int i; | |
110 | ||
111 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | |
112 | outb (page, dev->fx_dsp_page); | |
113 | outb (addr, dev->fx_dsp_addr); | |
114 | ||
115 | for (i = 0; i < cnt; i++) { | |
116 | outb ((data[i] >> 8), dev->fx_dsp_msb); | |
117 | outb ((data[i] & 0xff), dev->fx_dsp_lsb); | |
118 | if (!wavefront_fx_idle (dev)) { | |
119 | break; | |
120 | } | |
121 | } | |
122 | ||
123 | if (i != cnt) { | |
124 | snd_printk ("FX memset " | |
125 | "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", | |
126 | page, addr, (unsigned long) data, cnt); | |
127 | return -(EIO); | |
128 | } | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | int | |
135 | snd_wavefront_fx_detect (snd_wavefront_t *dev) | |
136 | ||
137 | { | |
138 | /* This is a crude check, but its the best one I have for now. | |
139 | Certainly on the Maui and the Tropez, wavefront_fx_idle() will | |
140 | report "never idle", which suggests that this test should | |
141 | work OK. | |
142 | */ | |
143 | ||
144 | if (inb (dev->fx_status) & 0x80) { | |
145 | snd_printk ("Hmm, probably a Maui or Tropez.\n"); | |
146 | return -1; | |
147 | } | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | int | |
542172f3 | 153 | snd_wavefront_fx_open (struct snd_hwdep *hw, struct file *file) |
1da177e4 LT |
154 | |
155 | { | |
156 | if (!try_module_get(hw->card->module)) | |
157 | return -EFAULT; | |
158 | file->private_data = hw; | |
159 | return 0; | |
160 | } | |
161 | ||
162 | int | |
542172f3 | 163 | snd_wavefront_fx_release (struct snd_hwdep *hw, struct file *file) |
1da177e4 LT |
164 | |
165 | { | |
166 | module_put(hw->card->module); | |
167 | return 0; | |
168 | } | |
169 | ||
170 | int | |
542172f3 | 171 | snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file, |
1da177e4 LT |
172 | unsigned int cmd, unsigned long arg) |
173 | ||
174 | { | |
542172f3 | 175 | struct snd_card *card; |
1da177e4 LT |
176 | snd_wavefront_card_t *acard; |
177 | snd_wavefront_t *dev; | |
178 | wavefront_fx_info r; | |
179 | unsigned short *page_data = NULL; | |
180 | unsigned short *pd; | |
181 | int err = 0; | |
182 | ||
1da177e4 | 183 | card = sdev->card; |
622207dc TI |
184 | if (snd_BUG_ON(!card)) |
185 | return -ENODEV; | |
186 | if (snd_BUG_ON(!card->private_data)) | |
187 | return -ENODEV; | |
1da177e4 LT |
188 | |
189 | acard = card->private_data; | |
190 | dev = &acard->wavefront; | |
191 | ||
192 | if (copy_from_user (&r, (void __user *)arg, sizeof (wavefront_fx_info))) | |
193 | return -EFAULT; | |
194 | ||
195 | switch (r.request) { | |
196 | case WFFX_MUTE: | |
197 | wavefront_fx_mute (dev, r.data[0]); | |
198 | return -EIO; | |
199 | ||
200 | case WFFX_MEMSET: | |
201 | if (r.data[2] <= 0) { | |
202 | snd_printk ("cannot write " | |
203 | "<= 0 bytes to FX\n"); | |
204 | return -EIO; | |
205 | } else if (r.data[2] == 1) { | |
206 | pd = (unsigned short *) &r.data[3]; | |
207 | } else { | |
208 | if (r.data[2] > 256) { | |
209 | snd_printk ("cannot write " | |
210 | "> 512 bytes to FX\n"); | |
211 | return -EIO; | |
212 | } | |
213 | page_data = kmalloc(r.data[2] * sizeof(short), GFP_KERNEL); | |
214 | if (!page_data) | |
215 | return -ENOMEM; | |
216 | if (copy_from_user (page_data, | |
217 | (unsigned char __user *) r.data[3], | |
218 | r.data[2] * sizeof(short))) { | |
219 | kfree(page_data); | |
220 | return -EFAULT; | |
221 | } | |
222 | pd = page_data; | |
223 | } | |
224 | ||
225 | err = wavefront_fx_memset (dev, | |
226 | r.data[0], /* page */ | |
227 | r.data[1], /* addr */ | |
228 | r.data[2], /* cnt */ | |
229 | pd); | |
230 | kfree(page_data); | |
231 | break; | |
232 | ||
233 | default: | |
234 | snd_printk ("FX: ioctl %d not yet supported\n", | |
235 | r.request); | |
236 | return -ENOTTY; | |
237 | } | |
238 | return err; | |
239 | } | |
240 | ||
241 | /* YSS225 initialization. | |
242 | ||
243 | This code was developed using DOSEMU. The Turtle Beach SETUPSND | |
244 | utility was run with I/O tracing in DOSEMU enabled, and a reconstruction | |
245 | of the port I/O done, using the Yamaha faxback document as a guide | |
246 | to add more logic to the code. Its really pretty weird. | |
247 | ||
59540fe8 | 248 | This is the approach of just dumping the whole I/O |
1da177e4 | 249 | sequence as a series of port/value pairs and a simple loop |
59540fe8 | 250 | that outputs it. |
1da177e4 LT |
251 | */ |
252 | ||
40e1a9c0 | 253 | int __devinit |
1da177e4 | 254 | snd_wavefront_fx_start (snd_wavefront_t *dev) |
1da177e4 | 255 | { |
59540fe8 | 256 | unsigned int i; |
226968c7 | 257 | int err; |
b7dd2b34 | 258 | const struct firmware *firmware = NULL; |
1da177e4 | 259 | |
59540fe8 CL |
260 | if (dev->fx_initialized) |
261 | return 0; | |
1da177e4 | 262 | |
b7dd2b34 TI |
263 | #ifdef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL |
264 | firmware = &yss225_registers_firmware; | |
265 | #else | |
226968c7 CL |
266 | err = request_firmware(&firmware, "yamaha/yss225_registers.bin", |
267 | dev->card->dev); | |
268 | if (err < 0) { | |
226968c7 CL |
269 | err = -1; |
270 | goto out; | |
226968c7 | 271 | } |
b7dd2b34 | 272 | #endif |
226968c7 CL |
273 | |
274 | for (i = 0; i + 1 < firmware->size; i += 2) { | |
275 | if (firmware->data[i] >= 8 && firmware->data[i] < 16) { | |
276 | outb(firmware->data[i + 1], | |
277 | dev->base + firmware->data[i]); | |
278 | } else if (firmware->data[i] == WAIT_IDLE) { | |
279 | if (!wavefront_fx_idle(dev)) { | |
280 | err = -1; | |
281 | goto out; | |
282 | } | |
59540fe8 CL |
283 | } else { |
284 | snd_printk(KERN_ERR "invalid address" | |
285 | " in register data\n"); | |
226968c7 CL |
286 | err = -1; |
287 | goto out; | |
1da177e4 | 288 | } |
1da177e4 LT |
289 | } |
290 | ||
59540fe8 | 291 | dev->fx_initialized = 1; |
226968c7 CL |
292 | err = 0; |
293 | ||
294 | out: | |
b7dd2b34 TI |
295 | #ifndef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL |
296 | release_firmware(firmware); | |
226968c7 | 297 | #endif |
226968c7 | 298 | return err; |
1da177e4 | 299 | } |
7e0af29d CL |
300 | |
301 | #ifndef CONFIG_SND_WAVEFRONT_FIRMWARE_IN_KERNEL | |
302 | MODULE_FIRMWARE("yamaha/yss225_registers.bin"); | |
303 | #endif |