1 // SPDX-License-Identifier: GPL-2.0-or-later 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 2 /* 3 * Copyright (c) 1998-2002 by Paul Davis <pbd 3 * Copyright (c) 1998-2002 by Paul Davis <pbd@op.net> 4 */ 4 */ 5 5 6 #include <linux/io.h> 6 #include <linux/io.h> 7 #include <linux/init.h> 7 #include <linux/init.h> 8 #include <linux/time.h> 8 #include <linux/time.h> 9 #include <linux/wait.h> 9 #include <linux/wait.h> 10 #include <linux/slab.h> 10 #include <linux/slab.h> 11 #include <linux/module.h> 11 #include <linux/module.h> 12 #include <linux/firmware.h> 12 #include <linux/firmware.h> 13 #include <sound/core.h> 13 #include <sound/core.h> 14 #include <sound/snd_wavefront.h> 14 #include <sound/snd_wavefront.h> 15 #include <sound/initval.h> 15 #include <sound/initval.h> 16 16 17 /* Control bits for the Load Control Register 17 /* Control bits for the Load Control Register 18 */ 18 */ 19 19 20 #define FX_LSB_TRANSFER 0x01 /* transfer af 20 #define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ 21 #define FX_MSB_TRANSFER 0x02 /* transfer af 21 #define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ 22 #define FX_AUTO_INCR 0x04 /* auto-increm 22 #define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ 23 23 24 #define WAIT_IDLE 0xff 24 #define WAIT_IDLE 0xff 25 25 26 static int 26 static int 27 wavefront_fx_idle (snd_wavefront_t *dev) 27 wavefront_fx_idle (snd_wavefront_t *dev) 28 28 29 { 29 { 30 int i; 30 int i; 31 unsigned int x = 0x80; 31 unsigned int x = 0x80; 32 32 33 for (i = 0; i < 1000; i++) { 33 for (i = 0; i < 1000; i++) { 34 x = inb (dev->fx_status); 34 x = inb (dev->fx_status); 35 if ((x & 0x80) == 0) { 35 if ((x & 0x80) == 0) { 36 break; 36 break; 37 } 37 } 38 } 38 } 39 39 40 if (x & 0x80) { 40 if (x & 0x80) { 41 dev_err(dev->card->dev, "FX de 41 dev_err(dev->card->dev, "FX device never idle.\n"); 42 return 0; 42 return 0; 43 } 43 } 44 44 45 return (1); 45 return (1); 46 } 46 } 47 47 48 static void 48 static void 49 wavefront_fx_mute (snd_wavefront_t *dev, int o 49 wavefront_fx_mute (snd_wavefront_t *dev, int onoff) 50 50 51 { 51 { 52 if (!wavefront_fx_idle(dev)) { 52 if (!wavefront_fx_idle(dev)) { 53 return; 53 return; 54 } 54 } 55 55 56 outb (onoff ? 0x02 : 0x00, dev->fx_op) 56 outb (onoff ? 0x02 : 0x00, dev->fx_op); 57 } 57 } 58 58 59 static int 59 static int 60 wavefront_fx_memset (snd_wavefront_t *dev, 60 wavefront_fx_memset (snd_wavefront_t *dev, 61 int page, 61 int page, 62 int addr, 62 int addr, 63 int cnt, 63 int cnt, 64 unsigned short *data) 64 unsigned short *data) 65 { 65 { 66 if (page < 0 || page > 7) { 66 if (page < 0 || page > 7) { 67 dev_err(dev->card->dev, 67 dev_err(dev->card->dev, 68 "FX memset: page must 68 "FX memset: page must be >= 0 and <= 7\n"); 69 return -EINVAL; 69 return -EINVAL; 70 } 70 } 71 71 72 if (addr < 0 || addr > 0x7f) { 72 if (addr < 0 || addr > 0x7f) { 73 dev_err(dev->card->dev, 73 dev_err(dev->card->dev, 74 "FX memset: addr must 74 "FX memset: addr must be >= 0 and <= 7f\n"); 75 return -EINVAL; 75 return -EINVAL; 76 } 76 } 77 77 78 if (cnt == 1) { 78 if (cnt == 1) { 79 79 80 outb (FX_LSB_TRANSFER, dev->fx 80 outb (FX_LSB_TRANSFER, dev->fx_lcr); 81 outb (page, dev->fx_dsp_page); 81 outb (page, dev->fx_dsp_page); 82 outb (addr, dev->fx_dsp_addr); 82 outb (addr, dev->fx_dsp_addr); 83 outb ((data[0] >> 8), dev->fx_ 83 outb ((data[0] >> 8), dev->fx_dsp_msb); 84 outb ((data[0] & 0xff), dev->f 84 outb ((data[0] & 0xff), dev->fx_dsp_lsb); 85 85 86 dev_err(dev->card->dev, "FX: a 86 dev_err(dev->card->dev, "FX: addr %d:%x set to 0x%x\n", 87 page, addr, data[0]); 87 page, addr, data[0]); 88 88 89 } else { 89 } else { 90 int i; 90 int i; 91 91 92 outb (FX_AUTO_INCR|FX_LSB_TRAN 92 outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); 93 outb (page, dev->fx_dsp_page); 93 outb (page, dev->fx_dsp_page); 94 outb (addr, dev->fx_dsp_addr); 94 outb (addr, dev->fx_dsp_addr); 95 95 96 for (i = 0; i < cnt; i++) { 96 for (i = 0; i < cnt; i++) { 97 outb ((data[i] >> 8), 97 outb ((data[i] >> 8), dev->fx_dsp_msb); 98 outb ((data[i] & 0xff) 98 outb ((data[i] & 0xff), dev->fx_dsp_lsb); 99 if (!wavefront_fx_idle 99 if (!wavefront_fx_idle (dev)) { 100 break; 100 break; 101 } 101 } 102 } 102 } 103 103 104 if (i != cnt) { 104 if (i != cnt) { 105 dev_err(dev->card->dev 105 dev_err(dev->card->dev, 106 "FX memset (0x 106 "FX memset (0x%x, 0x%x, 0x%lx, %d) incomplete\n", 107 page, addr, (u 107 page, addr, (unsigned long) data, cnt); 108 return -EIO; 108 return -EIO; 109 } 109 } 110 } 110 } 111 111 112 return 0; 112 return 0; 113 } 113 } 114 114 115 int 115 int 116 snd_wavefront_fx_detect (snd_wavefront_t *dev) 116 snd_wavefront_fx_detect (snd_wavefront_t *dev) 117 117 118 { 118 { 119 /* This is a crude check, but its the 119 /* This is a crude check, but its the best one I have for now. 120 Certainly on the Maui and the Trope 120 Certainly on the Maui and the Tropez, wavefront_fx_idle() will 121 report "never idle", which suggests 121 report "never idle", which suggests that this test should 122 work OK. 122 work OK. 123 */ 123 */ 124 124 125 if (inb (dev->fx_status) & 0x80) { 125 if (inb (dev->fx_status) & 0x80) { 126 dev_err(dev->card->dev, "Hmm, 126 dev_err(dev->card->dev, "Hmm, probably a Maui or Tropez.\n"); 127 return -1; 127 return -1; 128 } 128 } 129 129 130 return 0; 130 return 0; 131 } 131 } 132 132 133 int 133 int 134 snd_wavefront_fx_open (struct snd_hwdep *hw, s 134 snd_wavefront_fx_open (struct snd_hwdep *hw, struct file *file) 135 135 136 { 136 { 137 if (!try_module_get(hw->card->module)) 137 if (!try_module_get(hw->card->module)) 138 return -EFAULT; 138 return -EFAULT; 139 file->private_data = hw; 139 file->private_data = hw; 140 return 0; 140 return 0; 141 } 141 } 142 142 143 int 143 int 144 snd_wavefront_fx_release (struct snd_hwdep *hw 144 snd_wavefront_fx_release (struct snd_hwdep *hw, struct file *file) 145 145 146 { 146 { 147 module_put(hw->card->module); 147 module_put(hw->card->module); 148 return 0; 148 return 0; 149 } 149 } 150 150 151 int 151 int 152 snd_wavefront_fx_ioctl (struct snd_hwdep *sdev 152 snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file, 153 unsigned int cmd, unsi 153 unsigned int cmd, unsigned long arg) 154 154 155 { 155 { 156 struct snd_card *card; 156 struct snd_card *card; 157 snd_wavefront_card_t *acard; 157 snd_wavefront_card_t *acard; 158 snd_wavefront_t *dev; 158 snd_wavefront_t *dev; 159 wavefront_fx_info r; 159 wavefront_fx_info r; 160 unsigned short *page_data = NULL; 160 unsigned short *page_data = NULL; 161 unsigned short *pd; 161 unsigned short *pd; 162 int err = 0; 162 int err = 0; 163 163 164 card = sdev->card; 164 card = sdev->card; 165 if (snd_BUG_ON(!card)) 165 if (snd_BUG_ON(!card)) 166 return -ENODEV; 166 return -ENODEV; 167 if (snd_BUG_ON(!card->private_data)) 167 if (snd_BUG_ON(!card->private_data)) 168 return -ENODEV; 168 return -ENODEV; 169 169 170 acard = card->private_data; 170 acard = card->private_data; 171 dev = &acard->wavefront; 171 dev = &acard->wavefront; 172 172 173 if (copy_from_user (&r, (void __user * 173 if (copy_from_user (&r, (void __user *)arg, sizeof (wavefront_fx_info))) 174 return -EFAULT; 174 return -EFAULT; 175 175 176 switch (r.request) { 176 switch (r.request) { 177 case WFFX_MUTE: 177 case WFFX_MUTE: 178 wavefront_fx_mute (dev, r.data 178 wavefront_fx_mute (dev, r.data[0]); 179 return -EIO; 179 return -EIO; 180 180 181 case WFFX_MEMSET: 181 case WFFX_MEMSET: 182 if (r.data[2] <= 0) { 182 if (r.data[2] <= 0) { 183 dev_err(dev->card->dev 183 dev_err(dev->card->dev, 184 "cannot write 184 "cannot write <= 0 bytes to FX\n"); 185 return -EIO; 185 return -EIO; 186 } else if (r.data[2] == 1) { 186 } else if (r.data[2] == 1) { 187 pd = (unsigned short * 187 pd = (unsigned short *) &r.data[3]; 188 } else { 188 } else { 189 if (r.data[2] > 256) { 189 if (r.data[2] > 256) { 190 dev_err(dev->c 190 dev_err(dev->card->dev, 191 "canno 191 "cannot write > 512 bytes to FX\n"); 192 return -EIO; 192 return -EIO; 193 } 193 } 194 page_data = memdup_arr 194 page_data = memdup_array_user((unsigned char __user *) 195 195 r.data[3], 196 196 r.data[2], sizeof(short)); 197 if (IS_ERR(page_data)) 197 if (IS_ERR(page_data)) 198 return PTR_ERR 198 return PTR_ERR(page_data); 199 pd = page_data; 199 pd = page_data; 200 } 200 } 201 201 202 err = wavefront_fx_memset (dev 202 err = wavefront_fx_memset (dev, 203 r.data[0], /* pag 203 r.data[0], /* page */ 204 r.data[1], /* add 204 r.data[1], /* addr */ 205 r.data[2], /* cnt 205 r.data[2], /* cnt */ 206 pd); 206 pd); 207 kfree(page_data); 207 kfree(page_data); 208 break; 208 break; 209 209 210 default: 210 default: 211 dev_err(dev->card->dev, "FX: i 211 dev_err(dev->card->dev, "FX: ioctl %d not yet supported\n", 212 r.request); 212 r.request); 213 return -ENOTTY; 213 return -ENOTTY; 214 } 214 } 215 return err; 215 return err; 216 } 216 } 217 217 218 /* YSS225 initialization. 218 /* YSS225 initialization. 219 219 220 This code was developed using DOSEMU. The T 220 This code was developed using DOSEMU. The Turtle Beach SETUPSND 221 utility was run with I/O tracing in DOSEMU 221 utility was run with I/O tracing in DOSEMU enabled, and a reconstruction 222 of the port I/O done, using the Yamaha faxb 222 of the port I/O done, using the Yamaha faxback document as a guide 223 to add more logic to the code. Its really p 223 to add more logic to the code. Its really pretty weird. 224 224 225 This is the approach of just dumping the wh 225 This is the approach of just dumping the whole I/O 226 sequence as a series of port/value pairs an 226 sequence as a series of port/value pairs and a simple loop 227 that outputs it. 227 that outputs it. 228 */ 228 */ 229 229 230 int 230 int 231 snd_wavefront_fx_start (snd_wavefront_t *dev) 231 snd_wavefront_fx_start (snd_wavefront_t *dev) 232 { 232 { 233 unsigned int i; 233 unsigned int i; 234 int err; 234 int err; 235 const struct firmware *firmware = NULL 235 const struct firmware *firmware = NULL; 236 236 237 if (dev->fx_initialized) 237 if (dev->fx_initialized) 238 return 0; 238 return 0; 239 239 240 err = request_firmware(&firmware, "yam 240 err = request_firmware(&firmware, "yamaha/yss225_registers.bin", 241 dev->card->dev) 241 dev->card->dev); 242 if (err < 0) { 242 if (err < 0) { 243 err = -1; 243 err = -1; 244 goto out; 244 goto out; 245 } 245 } 246 246 247 for (i = 0; i + 1 < firmware->size; i 247 for (i = 0; i + 1 < firmware->size; i += 2) { 248 if (firmware->data[i] >= 8 && 248 if (firmware->data[i] >= 8 && firmware->data[i] < 16) { 249 outb(firmware->data[i 249 outb(firmware->data[i + 1], 250 dev->base + firmw 250 dev->base + firmware->data[i]); 251 } else if (firmware->data[i] = 251 } else if (firmware->data[i] == WAIT_IDLE) { 252 if (!wavefront_fx_idle 252 if (!wavefront_fx_idle(dev)) { 253 err = -1; 253 err = -1; 254 goto out; 254 goto out; 255 } 255 } 256 } else { 256 } else { 257 dev_err(dev->card->dev 257 dev_err(dev->card->dev, 258 "invalid addre 258 "invalid address in register data\n"); 259 err = -1; 259 err = -1; 260 goto out; 260 goto out; 261 } 261 } 262 } 262 } 263 263 264 dev->fx_initialized = 1; 264 dev->fx_initialized = 1; 265 err = 0; 265 err = 0; 266 266 267 out: 267 out: 268 release_firmware(firmware); 268 release_firmware(firmware); 269 return err; 269 return err; 270 } 270 } 271 271 272 MODULE_FIRMWARE("yamaha/yss225_registers.bin") 272 MODULE_FIRMWARE("yamaha/yss225_registers.bin"); 273 273
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.