Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | ********************************************************************** |
| 3 | * mixer.c - /dev/mixer interface for emu10k1 driver |
| 4 | * Copyright 1999, 2000 Creative Labs, Inc. |
| 5 | * |
| 6 | ********************************************************************** |
| 7 | * |
| 8 | * Date Author Summary of changes |
| 9 | * ---- ------ ------------------ |
| 10 | * October 20, 1999 Bertrand Lee base code release |
| 11 | * November 2, 1999 Alan Cox cleaned up stuff |
| 12 | * |
| 13 | ********************************************************************** |
| 14 | * |
| 15 | * This program is free software; you can redistribute it and/or |
| 16 | * modify it under the terms of the GNU General Public License as |
| 17 | * published by the Free Software Foundation; either version 2 of |
| 18 | * the License, or (at your option) any later version. |
| 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. |
| 24 | * |
| 25 | * You should have received a copy of the GNU General Public |
| 26 | * License along with this program; if not, write to the Free |
| 27 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, |
| 28 | * USA. |
| 29 | * |
| 30 | ********************************************************************** |
| 31 | */ |
| 32 | |
| 33 | #include <linux/module.h> |
| 34 | #include <asm/uaccess.h> |
| 35 | #include <linux/fs.h> |
| 36 | |
| 37 | #include "hwaccess.h" |
| 38 | #include "8010.h" |
| 39 | #include "recmgr.h" |
| 40 | |
| 41 | |
| 42 | static const u32 bass_table[41][5] = { |
| 43 | { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, |
| 44 | { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, |
| 45 | { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, |
| 46 | { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, |
| 47 | { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, |
| 48 | { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, |
| 49 | { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, |
| 50 | { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, |
| 51 | { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, |
| 52 | { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, |
| 53 | { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, |
| 54 | { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, |
| 55 | { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, |
| 56 | { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, |
| 57 | { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, |
| 58 | { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, |
| 59 | { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, |
| 60 | { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, |
| 61 | { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, |
| 62 | { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, |
| 63 | { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee }, |
| 64 | { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, |
| 65 | { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, |
| 66 | { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, |
| 67 | { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, |
| 68 | { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, |
| 69 | { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, |
| 70 | { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, |
| 71 | { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, |
| 72 | { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, |
| 73 | { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, |
| 74 | { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, |
| 75 | { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, |
| 76 | { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, |
| 77 | { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, |
| 78 | { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, |
| 79 | { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, |
| 80 | { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, |
| 81 | { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, |
| 82 | { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, |
| 83 | { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } |
| 84 | }; |
| 85 | |
| 86 | static const u32 treble_table[41][5] = { |
| 87 | { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, |
| 88 | { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, |
| 89 | { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, |
| 90 | { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, |
| 91 | { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, |
| 92 | { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, |
| 93 | { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, |
| 94 | { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, |
| 95 | { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, |
| 96 | { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, |
| 97 | { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, |
| 98 | { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, |
| 99 | { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, |
| 100 | { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, |
| 101 | { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, |
| 102 | { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, |
| 103 | { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, |
| 104 | { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, |
| 105 | { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, |
| 106 | { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, |
| 107 | { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, |
| 108 | { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, |
| 109 | { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, |
| 110 | { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, |
| 111 | { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, |
| 112 | { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, |
| 113 | { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, |
| 114 | { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, |
| 115 | { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, |
| 116 | { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, |
| 117 | { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, |
| 118 | { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, |
| 119 | { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, |
| 120 | { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, |
| 121 | { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, |
| 122 | { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, |
| 123 | { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, |
| 124 | { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, |
| 125 | { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, |
| 126 | { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, |
| 127 | { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } |
| 128 | }; |
| 129 | |
| 130 | |
| 131 | static void set_bass(struct emu10k1_card *card, int l, int r) |
| 132 | { |
| 133 | int i; |
| 134 | |
| 135 | l = (l * 40 + 50) / 100; |
| 136 | r = (r * 40 + 50) / 100; |
| 137 | |
| 138 | for (i = 0; i < 5; i++) |
| 139 | sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + card->mgr.ctrl_gpr[SOUND_MIXER_BASS][0] + i, 0, bass_table[l][i]); |
| 140 | } |
| 141 | |
| 142 | static void set_treble(struct emu10k1_card *card, int l, int r) |
| 143 | { |
| 144 | int i; |
| 145 | |
| 146 | l = (l * 40 + 50) / 100; |
| 147 | r = (r * 40 + 50) / 100; |
| 148 | |
| 149 | for (i = 0; i < 5; i++) |
| 150 | sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + card->mgr.ctrl_gpr[SOUND_MIXER_TREBLE][0] + i , 0, treble_table[l][i]); |
| 151 | } |
| 152 | |
| 153 | const char volume_params[SOUND_MIXER_NRDEVICES]= { |
| 154 | /* Used by the ac97 driver */ |
| 155 | [SOUND_MIXER_VOLUME] = VOL_6BIT, |
| 156 | [SOUND_MIXER_BASS] = VOL_4BIT, |
| 157 | [SOUND_MIXER_TREBLE] = VOL_4BIT, |
| 158 | [SOUND_MIXER_PCM] = VOL_5BIT, |
| 159 | [SOUND_MIXER_SPEAKER] = VOL_4BIT, |
| 160 | [SOUND_MIXER_LINE] = VOL_5BIT, |
| 161 | [SOUND_MIXER_MIC] = VOL_5BIT, |
| 162 | [SOUND_MIXER_CD] = VOL_5BIT, |
| 163 | [SOUND_MIXER_ALTPCM] = VOL_6BIT, |
| 164 | [SOUND_MIXER_IGAIN] = VOL_4BIT, |
| 165 | [SOUND_MIXER_LINE1] = VOL_5BIT, |
| 166 | [SOUND_MIXER_PHONEIN] = VOL_5BIT, |
| 167 | [SOUND_MIXER_PHONEOUT] = VOL_6BIT, |
| 168 | [SOUND_MIXER_VIDEO] = VOL_5BIT, |
| 169 | /* Not used by the ac97 driver */ |
| 170 | [SOUND_MIXER_SYNTH] = VOL_5BIT, |
| 171 | [SOUND_MIXER_IMIX] = VOL_5BIT, |
| 172 | [SOUND_MIXER_RECLEV] = VOL_5BIT, |
| 173 | [SOUND_MIXER_OGAIN] = VOL_5BIT, |
| 174 | [SOUND_MIXER_LINE2] = VOL_5BIT, |
| 175 | [SOUND_MIXER_LINE3] = VOL_5BIT, |
| 176 | [SOUND_MIXER_DIGITAL1] = VOL_5BIT, |
| 177 | [SOUND_MIXER_DIGITAL2] = VOL_5BIT, |
| 178 | [SOUND_MIXER_DIGITAL3] = VOL_5BIT, |
| 179 | [SOUND_MIXER_RADIO] = VOL_5BIT, |
| 180 | [SOUND_MIXER_MONITOR] = VOL_5BIT |
| 181 | }; |
| 182 | |
| 183 | /* Mixer file operations */ |
| 184 | static int emu10k1_private_mixer(struct emu10k1_card *card, unsigned int cmd, unsigned long arg) |
| 185 | { |
| 186 | struct mixer_private_ioctl *ctl; |
| 187 | struct dsp_patch *patch; |
| 188 | u32 size, page; |
| 189 | int addr, size_reg, i, ret; |
| 190 | unsigned int id, ch; |
| 191 | void __user *argp = (void __user *)arg; |
| 192 | |
| 193 | switch (cmd) { |
| 194 | |
| 195 | case SOUND_MIXER_PRIVATE3: |
| 196 | |
| 197 | ctl = (struct mixer_private_ioctl *) kmalloc(sizeof(struct mixer_private_ioctl), GFP_KERNEL); |
| 198 | if (ctl == NULL) |
| 199 | return -ENOMEM; |
| 200 | |
| 201 | if (copy_from_user(ctl, argp, sizeof(struct mixer_private_ioctl))) { |
| 202 | kfree(ctl); |
| 203 | return -EFAULT; |
| 204 | } |
| 205 | |
| 206 | ret = 0; |
| 207 | switch (ctl->cmd) { |
| 208 | #ifdef DBGEMU |
| 209 | case CMD_WRITEFN0: |
| 210 | emu10k1_writefn0_2(card, ctl->val[0], ctl->val[1], ctl->val[2]); |
| 211 | break; |
| 212 | #endif |
| 213 | case CMD_WRITEPTR: |
| 214 | #ifdef DBGEMU |
| 215 | if (ctl->val[1] >= 0x40 || ctl->val[0] >= 0x1000) { |
| 216 | #else |
| 217 | if (ctl->val[1] >= 0x40 || ctl->val[0] >= 0x1000 || ((ctl->val[0] < 0x100 ) && |
| 218 | //Any register allowed raw access goes here: |
| 219 | (ctl->val[0] != A_SPDIF_SAMPLERATE) && (ctl->val[0] != A_DBG) |
| 220 | ) |
| 221 | ) { |
| 222 | #endif |
| 223 | ret = -EINVAL; |
| 224 | break; |
| 225 | } |
| 226 | sblive_writeptr(card, ctl->val[0], ctl->val[1], ctl->val[2]); |
| 227 | break; |
| 228 | |
| 229 | case CMD_READFN0: |
| 230 | ctl->val[2] = emu10k1_readfn0(card, ctl->val[0]); |
| 231 | |
| 232 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 233 | ret = -EFAULT; |
| 234 | |
| 235 | break; |
| 236 | |
| 237 | case CMD_READPTR: |
| 238 | if (ctl->val[1] >= 0x40 || (ctl->val[0] & 0x7ff) > 0xff) { |
| 239 | ret = -EINVAL; |
| 240 | break; |
| 241 | } |
| 242 | |
| 243 | if ((ctl->val[0] & 0x7ff) > 0x3f) |
| 244 | ctl->val[1] = 0x00; |
| 245 | |
| 246 | ctl->val[2] = sblive_readptr(card, ctl->val[0], ctl->val[1]); |
| 247 | |
| 248 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 249 | ret = -EFAULT; |
| 250 | |
| 251 | break; |
| 252 | |
| 253 | case CMD_SETRECSRC: |
| 254 | switch (ctl->val[0]) { |
| 255 | case WAVERECORD_AC97: |
| 256 | if (card->is_aps) { |
| 257 | ret = -EINVAL; |
| 258 | break; |
| 259 | } |
| 260 | |
| 261 | card->wavein.recsrc = WAVERECORD_AC97; |
| 262 | break; |
| 263 | |
| 264 | case WAVERECORD_MIC: |
| 265 | card->wavein.recsrc = WAVERECORD_MIC; |
| 266 | break; |
| 267 | |
| 268 | case WAVERECORD_FX: |
| 269 | card->wavein.recsrc = WAVERECORD_FX; |
| 270 | card->wavein.fxwc = ctl->val[1] & 0xffff; |
| 271 | |
| 272 | if (!card->wavein.fxwc) |
| 273 | ret = -EINVAL; |
| 274 | |
| 275 | break; |
| 276 | |
| 277 | default: |
| 278 | ret = -EINVAL; |
| 279 | break; |
| 280 | } |
| 281 | break; |
| 282 | |
| 283 | case CMD_GETRECSRC: |
| 284 | ctl->val[0] = card->wavein.recsrc; |
| 285 | ctl->val[1] = card->wavein.fxwc; |
| 286 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 287 | ret = -EFAULT; |
| 288 | |
| 289 | break; |
| 290 | |
| 291 | case CMD_GETVOICEPARAM: |
| 292 | ctl->val[0] = card->waveout.send_routing[0]; |
| 293 | ctl->val[1] = card->waveout.send_dcba[0]; |
| 294 | |
| 295 | ctl->val[2] = card->waveout.send_routing[1]; |
| 296 | ctl->val[3] = card->waveout.send_dcba[1]; |
| 297 | |
| 298 | ctl->val[4] = card->waveout.send_routing[2]; |
| 299 | ctl->val[5] = card->waveout.send_dcba[2]; |
| 300 | |
| 301 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 302 | ret = -EFAULT; |
| 303 | |
| 304 | break; |
| 305 | |
| 306 | case CMD_SETVOICEPARAM: |
| 307 | card->waveout.send_routing[0] = ctl->val[0]; |
| 308 | card->waveout.send_dcba[0] = ctl->val[1]; |
| 309 | |
| 310 | card->waveout.send_routing[1] = ctl->val[2]; |
| 311 | card->waveout.send_dcba[1] = ctl->val[3]; |
| 312 | |
| 313 | card->waveout.send_routing[2] = ctl->val[4]; |
| 314 | card->waveout.send_dcba[2] = ctl->val[5]; |
| 315 | |
| 316 | break; |
| 317 | |
| 318 | case CMD_SETMCH_FX: |
| 319 | card->mchannel_fx = ctl->val[0] & 0x000f; |
| 320 | break; |
| 321 | |
| 322 | case CMD_GETPATCH: |
| 323 | if (ctl->val[0] == 0) { |
| 324 | if (copy_to_user(argp, &card->mgr.rpatch, sizeof(struct dsp_rpatch))) |
| 325 | ret = -EFAULT; |
| 326 | } else { |
| 327 | if ((ctl->val[0] - 1) / PATCHES_PER_PAGE >= card->mgr.current_pages) { |
| 328 | ret = -EINVAL; |
| 329 | break; |
| 330 | } |
| 331 | |
| 332 | if (copy_to_user(argp, PATCH(&card->mgr, ctl->val[0] - 1), sizeof(struct dsp_patch))) |
| 333 | ret = -EFAULT; |
| 334 | } |
| 335 | |
| 336 | break; |
| 337 | |
| 338 | case CMD_GETGPR: |
| 339 | id = ctl->val[0]; |
| 340 | |
| 341 | if (id > NUM_GPRS) { |
| 342 | ret = -EINVAL; |
| 343 | break; |
| 344 | } |
| 345 | |
| 346 | if (copy_to_user(argp, &card->mgr.gpr[id], sizeof(struct dsp_gpr))) |
| 347 | ret = -EFAULT; |
| 348 | |
| 349 | break; |
| 350 | |
| 351 | case CMD_GETCTLGPR: |
| 352 | addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, &((char *) ctl->val)[PATCH_NAME_SIZE]); |
| 353 | ctl->val[0] = sblive_readptr(card, addr, 0); |
| 354 | |
| 355 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 356 | ret = -EFAULT; |
| 357 | |
| 358 | break; |
| 359 | |
| 360 | case CMD_SETPATCH: |
| 361 | if (ctl->val[0] == 0) |
| 362 | memcpy(&card->mgr.rpatch, &ctl->val[1], sizeof(struct dsp_rpatch)); |
| 363 | else { |
| 364 | page = (ctl->val[0] - 1) / PATCHES_PER_PAGE; |
| 365 | if (page > MAX_PATCHES_PAGES) { |
| 366 | ret = -EINVAL; |
| 367 | break; |
| 368 | } |
| 369 | |
| 370 | if (page >= card->mgr.current_pages) { |
| 371 | for (i = card->mgr.current_pages; i < page + 1; i++) { |
| 372 | card->mgr.patch[i] = (void *)__get_free_page(GFP_KERNEL); |
| 373 | if(card->mgr.patch[i] == NULL) { |
| 374 | card->mgr.current_pages = i; |
| 375 | ret = -ENOMEM; |
| 376 | break; |
| 377 | } |
| 378 | memset(card->mgr.patch[i], 0, PAGE_SIZE); |
| 379 | } |
| 380 | card->mgr.current_pages = page + 1; |
| 381 | } |
| 382 | |
| 383 | patch = PATCH(&card->mgr, ctl->val[0] - 1); |
| 384 | |
| 385 | memcpy(patch, &ctl->val[1], sizeof(struct dsp_patch)); |
| 386 | |
| 387 | if (patch->code_size == 0) { |
| 388 | for(i = page + 1; i < card->mgr.current_pages; i++) |
| 389 | free_page((unsigned long) card->mgr.patch[i]); |
| 390 | |
| 391 | card->mgr.current_pages = page + 1; |
| 392 | } |
| 393 | } |
| 394 | break; |
| 395 | |
| 396 | case CMD_SETGPR: |
| 397 | if (ctl->val[0] > NUM_GPRS) { |
| 398 | ret = -EINVAL; |
| 399 | break; |
| 400 | } |
| 401 | |
| 402 | memcpy(&card->mgr.gpr[ctl->val[0]], &ctl->val[1], sizeof(struct dsp_gpr)); |
| 403 | break; |
| 404 | |
| 405 | case CMD_SETCTLGPR: |
| 406 | addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, (char *) ctl->val + PATCH_NAME_SIZE); |
| 407 | emu10k1_set_control_gpr(card, addr, *((s32 *)((char *) ctl->val + 2 * PATCH_NAME_SIZE)), 0); |
| 408 | break; |
| 409 | |
| 410 | case CMD_SETGPOUT: |
| 411 | if ( ((ctl->val[0] > 2) && (!card->is_audigy)) |
| 412 | || (ctl->val[0] > 15) || ctl->val[1] > 1) { |
| 413 | ret= -EINVAL; |
| 414 | break; |
| 415 | } |
| 416 | |
| 417 | if (card->is_audigy) |
| 418 | emu10k1_writefn0(card, (1 << 24) | ((ctl->val[0]) << 16) | A_IOCFG, ctl->val[1]); |
| 419 | else |
| 420 | emu10k1_writefn0(card, (1 << 24) | (((ctl->val[0]) + 10) << 16) | HCFG, ctl->val[1]); |
| 421 | break; |
| 422 | |
| 423 | case CMD_GETGPR2OSS: |
| 424 | id = ctl->val[0]; |
| 425 | ch = ctl->val[1]; |
| 426 | |
| 427 | if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { |
| 428 | ret = -EINVAL; |
| 429 | break; |
| 430 | } |
| 431 | |
| 432 | ctl->val[2] = card->mgr.ctrl_gpr[id][ch]; |
| 433 | |
| 434 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 435 | ret = -EFAULT; |
| 436 | |
| 437 | break; |
| 438 | |
| 439 | case CMD_SETGPR2OSS: |
| 440 | id = ctl->val[0]; |
| 441 | /* 0 == left, 1 == right */ |
| 442 | ch = ctl->val[1]; |
| 443 | addr = ctl->val[2]; |
| 444 | |
| 445 | if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { |
| 446 | ret = -EINVAL; |
| 447 | break; |
| 448 | } |
| 449 | |
| 450 | card->mgr.ctrl_gpr[id][ch] = addr; |
| 451 | |
| 452 | if (card->is_aps) |
| 453 | break; |
| 454 | |
| 455 | if (addr >= 0) { |
| 456 | unsigned int state = card->ac97->mixer_state[id]; |
| 457 | |
| 458 | if (ch == 1) { |
| 459 | state >>= 8; |
| 460 | card->ac97->stereo_mixers |= (1 << id); |
| 461 | } |
| 462 | |
| 463 | card->ac97->supported_mixers |= (1 << id); |
| 464 | |
| 465 | if (id == SOUND_MIXER_TREBLE) { |
| 466 | set_treble(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff); |
| 467 | } else if (id == SOUND_MIXER_BASS) { |
| 468 | set_bass(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff); |
| 469 | } else |
| 470 | emu10k1_set_volume_gpr(card, addr, state & 0xff, |
| 471 | volume_params[id]); |
| 472 | } else { |
| 473 | card->ac97->stereo_mixers &= ~(1 << id); |
| 474 | card->ac97->stereo_mixers |= card->ac97_stereo_mixers; |
| 475 | |
| 476 | if (ch == 0) { |
| 477 | card->ac97->supported_mixers &= ~(1 << id); |
| 478 | card->ac97->supported_mixers |= card->ac97_supported_mixers; |
| 479 | } |
| 480 | } |
| 481 | break; |
| 482 | |
| 483 | case CMD_SETPASSTHROUGH: |
| 484 | card->pt.selected = ctl->val[0] ? 1 : 0; |
| 485 | if (card->pt.state != PT_STATE_INACTIVE) |
| 486 | break; |
| 487 | |
| 488 | card->pt.spcs_to_use = ctl->val[0] & 0x07; |
| 489 | break; |
| 490 | |
| 491 | case CMD_PRIVATE3_VERSION: |
| 492 | ctl->val[0] = PRIVATE3_VERSION; //private3 version |
| 493 | ctl->val[1] = MAJOR_VER; //major driver version |
| 494 | ctl->val[2] = MINOR_VER; //minor driver version |
| 495 | ctl->val[3] = card->is_audigy; //1=card is audigy |
| 496 | |
| 497 | if (card->is_audigy) |
| 498 | ctl->val[4]=emu10k1_readfn0(card, 0x18); |
| 499 | |
| 500 | if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl))) |
| 501 | ret = -EFAULT; |
| 502 | break; |
| 503 | |
| 504 | case CMD_AC97_BOOST: |
| 505 | if (ctl->val[0]) |
| 506 | emu10k1_ac97_write(card->ac97, 0x18, 0x0); |
| 507 | else |
| 508 | emu10k1_ac97_write(card->ac97, 0x18, 0x0808); |
| 509 | break; |
| 510 | default: |
| 511 | ret = -EINVAL; |
| 512 | break; |
| 513 | } |
| 514 | |
| 515 | kfree(ctl); |
| 516 | return ret; |
| 517 | break; |
| 518 | |
| 519 | case SOUND_MIXER_PRIVATE4: |
| 520 | |
| 521 | if (copy_from_user(&size, argp, sizeof(size))) |
| 522 | return -EFAULT; |
| 523 | |
| 524 | DPD(2, "External tram size %#x\n", size); |
| 525 | |
| 526 | if (size > 0x1fffff) |
| 527 | return -EINVAL; |
| 528 | |
| 529 | size_reg = 0; |
| 530 | |
| 531 | if (size != 0) { |
| 532 | size = (size - 1) >> 14; |
| 533 | |
| 534 | while (size) { |
| 535 | size >>= 1; |
| 536 | size_reg++; |
| 537 | } |
| 538 | |
| 539 | size = 0x4000 << size_reg; |
| 540 | } |
| 541 | |
| 542 | DPD(2, "External tram size %#x %#x\n", size, size_reg); |
| 543 | |
| 544 | if (size != card->tankmem.size) { |
| 545 | if (card->tankmem.size > 0) { |
| 546 | emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1); |
| 547 | |
| 548 | sblive_writeptr_tag(card, 0, TCB, 0, TCBS, 0, TAGLIST_END); |
| 549 | |
| 550 | pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); |
| 551 | |
| 552 | card->tankmem.size = 0; |
| 553 | } |
| 554 | |
| 555 | if (size != 0) { |
| 556 | card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size, &card->tankmem.dma_handle); |
| 557 | if (card->tankmem.addr == NULL) |
| 558 | return -ENOMEM; |
| 559 | |
| 560 | card->tankmem.size = size; |
| 561 | |
| 562 | sblive_writeptr_tag(card, 0, TCB, (u32) card->tankmem.dma_handle, TCBS,(u32) size_reg, TAGLIST_END); |
| 563 | |
| 564 | emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0); |
| 565 | } |
| 566 | } |
| 567 | return 0; |
| 568 | break; |
| 569 | |
| 570 | default: |
| 571 | break; |
| 572 | } |
| 573 | |
| 574 | return -EINVAL; |
| 575 | } |
| 576 | |
| 577 | static int emu10k1_dsp_mixer(struct emu10k1_card *card, unsigned int oss_mixer, unsigned long arg) |
| 578 | { |
| 579 | unsigned int left, right; |
| 580 | int val; |
| 581 | int scale; |
| 582 | |
| 583 | card->ac97->modcnt++; |
| 584 | |
| 585 | if (get_user(val, (int __user *)arg)) |
| 586 | return -EFAULT; |
| 587 | |
| 588 | /* cleanse input a little */ |
| 589 | right = ((val >> 8) & 0xff); |
| 590 | left = (val & 0xff); |
| 591 | |
| 592 | if (right > 100) right = 100; |
| 593 | if (left > 100) left = 100; |
| 594 | |
| 595 | card->ac97->mixer_state[oss_mixer] = (right << 8) | left; |
| 596 | if (oss_mixer == SOUND_MIXER_TREBLE) { |
| 597 | set_treble(card, left, right); |
| 598 | return 0; |
| 599 | } if (oss_mixer == SOUND_MIXER_BASS) { |
| 600 | set_bass(card, left, right); |
| 601 | return 0; |
| 602 | } |
| 603 | |
| 604 | if (oss_mixer == SOUND_MIXER_VOLUME) |
| 605 | scale = 1 << card->ac97->bit_resolution; |
| 606 | else |
| 607 | scale = volume_params[oss_mixer]; |
| 608 | |
| 609 | emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, scale); |
| 610 | emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, scale); |
| 611 | |
| 612 | if (card->ac97_supported_mixers & (1 << oss_mixer)) |
| 613 | card->ac97->write_mixer(card->ac97, oss_mixer, left, right); |
| 614 | |
| 615 | return 0; |
| 616 | } |
| 617 | |
| 618 | static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) |
| 619 | { |
| 620 | int ret; |
| 621 | struct emu10k1_card *card = file->private_data; |
| 622 | unsigned int oss_mixer = _IOC_NR(cmd); |
| 623 | |
| 624 | ret = -EINVAL; |
| 625 | if (!card->is_aps) { |
| 626 | if (cmd == SOUND_MIXER_INFO) { |
| 627 | mixer_info info; |
| 628 | |
| 629 | strlcpy(info.id, card->ac97->name, sizeof(info.id)); |
| 630 | |
| 631 | if (card->is_audigy) |
| 632 | strlcpy(info.name, "Audigy - Emu10k1", sizeof(info.name)); |
| 633 | else |
| 634 | strlcpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name)); |
| 635 | |
| 636 | info.modify_counter = card->ac97->modcnt; |
| 637 | |
| 638 | if (copy_to_user((void __user *)arg, &info, sizeof(info))) |
| 639 | return -EFAULT; |
| 640 | |
| 641 | return 0; |
| 642 | } |
| 643 | |
| 644 | if ((_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES) |
| 645 | ret = emu10k1_dsp_mixer(card, oss_mixer, arg); |
| 646 | else |
| 647 | ret = card->ac97->mixer_ioctl(card->ac97, cmd, arg); |
| 648 | } |
| 649 | |
| 650 | if (ret < 0) |
| 651 | ret = emu10k1_private_mixer(card, cmd, arg); |
| 652 | |
| 653 | return ret; |
| 654 | } |
| 655 | |
| 656 | static int emu10k1_mixer_open(struct inode *inode, struct file *file) |
| 657 | { |
| 658 | int minor = iminor(inode); |
| 659 | struct emu10k1_card *card = NULL; |
| 660 | struct list_head *entry; |
| 661 | |
| 662 | DPF(4, "emu10k1_mixer_open()\n"); |
| 663 | |
| 664 | list_for_each(entry, &emu10k1_devs) { |
| 665 | card = list_entry(entry, struct emu10k1_card, list); |
| 666 | |
| 667 | if (card->ac97->dev_mixer == minor) |
| 668 | goto match; |
| 669 | } |
| 670 | |
| 671 | return -ENODEV; |
| 672 | |
| 673 | match: |
| 674 | file->private_data = card; |
| 675 | return 0; |
| 676 | } |
| 677 | |
| 678 | static int emu10k1_mixer_release(struct inode *inode, struct file *file) |
| 679 | { |
| 680 | DPF(4, "emu10k1_mixer_release()\n"); |
| 681 | return 0; |
| 682 | } |
| 683 | |
| 684 | struct file_operations emu10k1_mixer_fops = { |
| 685 | .owner = THIS_MODULE, |
| 686 | .llseek = no_llseek, |
| 687 | .ioctl = emu10k1_mixer_ioctl, |
| 688 | .open = emu10k1_mixer_open, |
| 689 | .release = emu10k1_mixer_release, |
| 690 | }; |