Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/sound/pci/emu10k1/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 93 kB image not shown  

Quelle  emufx.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *                   James Courtier-Dutton <James@superbug.co.uk>
 *                   Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
 *                   Creative Labs, Inc.
 *
 *  Routines for effect processor FX8010
 */


#include <linux/pci.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
#include <linux/nospec.h>

#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/emu10k1.h>

#if 0  /* for testing purposes - digital out -> capture */
#define EMU10K1_CAPTURE_DIGITAL_OUT
#endif
#if 0  /* for testing purposes - set S/PDIF to AC3 output */
#define EMU10K1_SET_AC3_IEC958
#endif
#if 0  /* for testing purposes - feed the front signal to Center/LFE outputs */
#define EMU10K1_CENTER_LFE_FROM_FRONT
#endif

static bool high_res_gpr_volume;
module_param(high_res_gpr_volume, bool, 0444);
MODULE_PARM_DESC(high_res_gpr_volume, "GPR mixer controls use 31-bit range.");

/*
 *  Tables
 */


// Playback channel labels; corresponds with the public FXBUS_* defines.
// Unlike the tables below, this is not determined by the hardware.
const char * const snd_emu10k1_fxbus[32] = {
 /* 0x00 */ "PCM Left",
 /* 0x01 */ "PCM Right",
 /* 0x02 */ "PCM Rear Left",
 /* 0x03 */ "PCM Rear Right",
 /* 0x04 */ "MIDI Left",
 /* 0x05 */ "MIDI Right",
 /* 0x06 */ "PCM Center",
 /* 0x07 */ "PCM LFE",
 /* 0x08 */ "PCM Front Left",
 /* 0x09 */ "PCM Front Right",
 /* 0x0a */ NULL,
 /* 0x0b */ NULL,
 /* 0x0c */ "MIDI Reverb",
 /* 0x0d */ "MIDI Chorus",
 /* 0x0e */ "PCM Side Left",
 /* 0x0f */ "PCM Side Right",
 /* 0x10 */ NULL,
 /* 0x11 */ NULL,
 /* 0x12 */ NULL,
 /* 0x13 */ NULL,
 /* 0x14 */ "Passthrough Left",
 /* 0x15 */ "Passthrough Right",
 /* 0x16 */ NULL,
 /* 0x17 */ NULL,
 /* 0x18 */ NULL,
 /* 0x19 */ NULL,
 /* 0x1a */ NULL,
 /* 0x1b */ NULL,
 /* 0x1c */ NULL,
 /* 0x1d */ NULL,
 /* 0x1e */ NULL,
 /* 0x1f */ NULL
};

// Physical inputs; corresponds with the public EXTIN_* defines.
const char * const snd_emu10k1_sblive_ins[16] = {
 /* 0x00 */ "AC97 Left",
 /* 0x01 */ "AC97 Right",
 /* 0x02 */ "TTL IEC958 Left",
 /* 0x03 */ "TTL IEC958 Right",
 /* 0x04 */ "Zoom Video Left",
 /* 0x05 */ "Zoom Video Right",
 /* 0x06 */ "Optical IEC958 Left",
 /* 0x07 */ "Optical IEC958 Right",
 /* 0x08 */ "Line/Mic 1 Left",
 /* 0x09 */ "Line/Mic 1 Right",
 /* 0x0a */ "Coaxial IEC958 Left",
 /* 0x0b */ "Coaxial IEC958 Right",
 /* 0x0c */ "Line/Mic 2 Left",
 /* 0x0d */ "Line/Mic 2 Right",
 /* 0x0e */ NULL,
 /* 0x0f */ NULL
};

// Physical inputs; corresponds with the public A_EXTIN_* defines.
const char * const snd_emu10k1_audigy_ins[16] = {
 /* 0x00 */ "AC97 Left",
 /* 0x01 */ "AC97 Right",
 /* 0x02 */ "Audigy CD Left",
 /* 0x03 */ "Audigy CD Right",
 /* 0x04 */ "Optical IEC958 Left",
 /* 0x05 */ "Optical IEC958 Right",
 /* 0x06 */ NULL,
 /* 0x07 */ NULL,
 /* 0x08 */ "Line/Mic 2 Left",
 /* 0x09 */ "Line/Mic 2 Right",
 /* 0x0a */ "SPDIF Left",
 /* 0x0b */ "SPDIF Right",
 /* 0x0c */ "Aux2 Left",
 /* 0x0d */ "Aux2 Right",
 /* 0x0e */ NULL,
 /* 0x0f */ NULL
};

// Physical outputs; corresponds with the public EXTOUT_* defines.
const char * const snd_emu10k1_sblive_outs[32] = {
 /* 0x00 */ "AC97 Left",
 /* 0x01 */ "AC97 Right",
 /* 0x02 */ "Optical IEC958 Left",
 /* 0x03 */ "Optical IEC958 Right",
 /* 0x04 */ "Center",
 /* 0x05 */ "LFE",
 /* 0x06 */ "Headphone Left",
 /* 0x07 */ "Headphone Right",
 /* 0x08 */ "Surround Left",
 /* 0x09 */ "Surround Right",
 /* 0x0a */ "PCM Capture Left",
 /* 0x0b */ "PCM Capture Right",
 /* 0x0c */ "MIC Capture",
 /* 0x0d */ "AC97 Surround Left",
 /* 0x0e */ "AC97 Surround Right",
 /* 0x0f */ NULL,
 // This is actually the FXBUS2 range; SB Live! 5.1 only.
 /* 0x10 */ NULL,
 /* 0x11 */ "Analog Center",
 /* 0x12 */ "Analog LFE",
 /* 0x13 */ NULL,
 /* 0x14 */ NULL,
 /* 0x15 */ NULL,
 /* 0x16 */ NULL,
 /* 0x17 */ NULL,
 /* 0x18 */ NULL,
 /* 0x19 */ NULL,
 /* 0x1a */ NULL,
 /* 0x1b */ NULL,
 /* 0x1c */ NULL,
 /* 0x1d */ NULL,
 /* 0x1e */ NULL,
 /* 0x1f */ NULL,
};

// Physical outputs; corresponds with the public A_EXTOUT_* defines.
const char * const snd_emu10k1_audigy_outs[32] = {
 /* 0x00 */ "Digital Front Left",
 /* 0x01 */ "Digital Front Right",
 /* 0x02 */ "Digital Center",
 /* 0x03 */ "Digital LEF",
 /* 0x04 */ "Headphone Left",
 /* 0x05 */ "Headphone Right",
 /* 0x06 */ "Digital Rear Left",
 /* 0x07 */ "Digital Rear Right",
 /* 0x08 */ "Front Left",
 /* 0x09 */ "Front Right",
 /* 0x0a */ "Center",
 /* 0x0b */ "LFE",
 /* 0x0c */ NULL,
 /* 0x0d */ NULL,
 /* 0x0e */ "Rear Left",
 /* 0x0f */ "Rear Right",
 /* 0x10 */ "AC97 Front Left",
 /* 0x11 */ "AC97 Front Right",
 /* 0x12 */ "ADC Capture Left",
 /* 0x13 */ "ADC Capture Right",
 /* 0x14 */ NULL,
 /* 0x15 */ NULL,
 /* 0x16 */ NULL,
 /* 0x17 */ NULL,
 /* 0x18 */ NULL,
 /* 0x19 */ NULL,
 /* 0x1a */ NULL,
 /* 0x1b */ NULL,
 /* 0x1c */ NULL,
 /* 0x1d */ NULL,
 /* 0x1e */ NULL,
 /* 0x1f */ NULL,
};

// On the SB Live! 5.1, FXBUS2[1] and FXBUS2[2] are occupied by EXTOUT_ACENTER
// and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
//
// Since only 14 of the 16 EXTINs are used, this is not a big problem.
// We route AC97 to FX capture 14 and 15, SPDIF_CD to FX capture 0 and 3,
// and the rest of the EXTINs to the corresponding FX capture channel.
// Multitrack recorders will still see the center/LFE output signal
// on the second and third "input" channel.
const s8 snd_emu10k1_sblive51_fxbus2_map[16] = {
 2, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1
};

static const u32 bass_table[41][5] = {
 { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
 { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
 { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
 { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
 { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
 { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
 { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
 { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
 { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
 { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
 { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
 { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
 { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
 { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
 { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
 { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
 { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
 { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
 { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
 { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
 { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee },
 { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
 { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
 { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
 { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
 { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
 { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
 { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
 { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
 { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
 { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
 { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
 { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
 { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
 { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
 { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
 { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
 { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
 { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
 { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
 { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
};

static const u32 treble_table[41][5] = {
 { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
 { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
 { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
 { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
 { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
 { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
 { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
 { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
 { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
 { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
 { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
 { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
 { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
 { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
 { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
 { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
 { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
 { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
 { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
 { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
 { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
 { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
 { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
 { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
 { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
 { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
 { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
 { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
 { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
 { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
 { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
 { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
 { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
 { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
 { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
 { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
 { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
 { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
 { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
 { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
 { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
};

/* dB gain = (float) 20 * log10( float(db_table_value) / 0x8000000 ) */
static const u32 db_table[101] = {
 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
 0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1,
 0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0,
 0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9,
 0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb,
 0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005,
 0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d,
 0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd,
 0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8,
 0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481,
 0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333,
 0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d,
 0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6,
 0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d,
 0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf,
 0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038,
 0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a,
 0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea,
 0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272,
 0x7fffffff,
};

/* EMU10k1/EMU10k2 DSP control db gain */
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);

/* EMU10K1 bass/treble db gain */
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);

static const u32 onoff_table[2] = {
 0x00000000, 0x00000001
};

/*
 *   controls
 */


static int snd_emu10k1_gpr_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
 struct snd_emu10k1_fx8010_ctl *ctl =
  (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;

 if (ctl->min == 0 && ctl->max == 1)
  uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 else
  uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 uinfo->count = ctl->vcount;
 uinfo->value.integer.min = ctl->min;
 uinfo->value.integer.max = ctl->max;
 return 0;
}

static int snd_emu10k1_gpr_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
 struct snd_emu10k1_fx8010_ctl *ctl =
  (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
 unsigned int i;
 
 for (i = 0; i < ctl->vcount; i++)
  ucontrol->value.integer.value[i] = ctl->value[i];
 return 0;
}

static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
 struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
 struct snd_emu10k1_fx8010_ctl *ctl =
  (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
 int nval, val;
 unsigned int i, j;
 int change = 0;
 
 for (i = 0; i < ctl->vcount; i++) {
  nval = ucontrol->value.integer.value[i];
  if (nval < ctl->min)
   nval = ctl->min;
  if (nval > ctl->max)
   nval = ctl->max;
  if (nval != ctl->value[i])
   change = 1;
  val = ctl->value[i] = nval;
  switch (ctl->translation) {
  case EMU10K1_GPR_TRANSLATION_NONE:
   snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
   break;
  case EMU10K1_GPR_TRANSLATION_NEGATE:
   snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, ~val);
   break;
  case EMU10K1_GPR_TRANSLATION_TABLE100:
   snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
   break;
  case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
   snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0,
           val == 100 ? 0x80000000 : -(int)db_table[val]);
   break;
  case EMU10K1_GPR_TRANSLATION_BASS:
   if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
    change = -EIO;
    goto __error;
   }
   for (j = 0; j < 5; j++)
    snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]);
   break;
  case EMU10K1_GPR_TRANSLATION_TREBLE:
   if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
    change = -EIO;
    goto __error;
   }
   for (j = 0; j < 5; j++)
    snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]);
   break;
  case EMU10K1_GPR_TRANSLATION_ONOFF:
   snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]);
   break;
  }
 }
      __error:
 return change;
}

/*
 *   Interrupt handler
 */


static void snd_emu10k1_fx8010_interrupt(struct snd_emu10k1 *emu)
{
 struct snd_emu10k1_fx8010_irq *irq, *nirq;

 irq = emu->fx8010.irq_handlers;
 while (irq) {
  nirq = irq->next; /* irq ptr can be removed from list */
  if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) {
   if (irq->handler)
    irq->handler(emu, irq->private_data);
   snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1);
  }
  irq = nirq;
 }
}

int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
         snd_fx8010_irq_handler_t *handler,
         unsigned char gpr_running,
         void *private_data,
         struct snd_emu10k1_fx8010_irq *irq)
{
 unsigned long flags;
 
 irq->handler = handler;
 irq->gpr_running = gpr_running;
 irq->private_data = private_data;
 irq->next = NULL;
 spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
 if (emu->fx8010.irq_handlers == NULL) {
  emu->fx8010.irq_handlers = irq;
  emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
  snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE);
 } else {
  irq->next = emu->fx8010.irq_handlers;
  emu->fx8010.irq_handlers = irq;
 }
 spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
 return 0;
}

int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
           struct snd_emu10k1_fx8010_irq *irq)
{
 struct snd_emu10k1_fx8010_irq *tmp;
 unsigned long flags;
 
 spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
 tmp = emu->fx8010.irq_handlers;
 if (tmp == irq) {
  emu->fx8010.irq_handlers = tmp->next;
  if (emu->fx8010.irq_handlers == NULL) {
   snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
   emu->dsp_interrupt = NULL;
  }
 } else {
  while (tmp && tmp->next != irq)
   tmp = tmp->next;
  if (tmp)
   tmp->next = tmp->next->next;
 }
 spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
 return 0;
}

/*************************************************************************
 * EMU10K1 effect manager
 *************************************************************************/


static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,
     unsigned int *ptr,
     u32 op, u32 r, u32 a, u32 x, u32 y)
{
 u_int32_t *code;
 if (snd_BUG_ON(*ptr >= 512))
  return;
 code = icode->code + (*ptr) * 2;
 set_bit(*ptr, icode->code_valid);
 code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
 code[1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
 (*ptr)++;
}

#define OP(icode, ptr, op, r, a, x, y) \
 snd_emu10k1_write_op(icode, ptr, op, r, a, x, y)

static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,
     unsigned int *ptr,
     u32 op, u32 r, u32 a, u32 x, u32 y)
{
 u_int32_t *code;
 if (snd_BUG_ON(*ptr >= 1024))
  return;
 code = icode->code + (*ptr) * 2;
 set_bit(*ptr, icode->code_valid);
 code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
 code[1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
 (*ptr)++;
}

#define A_OP(icode, ptr, op, r, a, x, y) \
 snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y)

static void snd_emu10k1_efx_write(struct snd_emu10k1 *emu, unsigned int pc, unsigned int data)
{
 pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
 snd_emu10k1_ptr_write(emu, pc, 0, data);
}

unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc)
{
 pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
 return snd_emu10k1_ptr_read(emu, pc, 0);
}

static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
    struct snd_emu10k1_fx8010_code *icode,
    bool in_kernel)
{
 int gpr;
 u32 val;

 for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
  if (!test_bit(gpr, icode->gpr_valid))
   continue;
  if (in_kernel)
   val = icode->gpr_map[gpr];
  else if (get_user(val, (__user u32 *)&icode->gpr_map[gpr]))
   return -EFAULT;
  snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val);
 }
 return 0;
}

static int snd_emu10k1_gpr_peek(struct snd_emu10k1 *emu,
    struct snd_emu10k1_fx8010_code *icode)
{
 int gpr;
 u32 val;

 for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
  set_bit(gpr, icode->gpr_valid);
  val = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
  if (put_user(val, (__user u32 *)&icode->gpr_map[gpr]))
   return -EFAULT;
 }
 return 0;
}

static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
     struct snd_emu10k1_fx8010_code *icode,
     bool in_kernel)
{
 int tram;
 u32 addr, val;

 for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
  if (!test_bit(tram, icode->tram_valid))
   continue;
  if (in_kernel) {
   val = icode->tram_data_map[tram];
   addr = icode->tram_addr_map[tram];
  } else {
   if (get_user(val, (__user __u32 *)&icode->tram_data_map[tram]) ||
       get_user(addr, (__user __u32 *)&icode->tram_addr_map[tram]))
    return -EFAULT;
  }
  snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val);
  if (!emu->audigy) {
   snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr);
  } else {
   snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr << 12);
   snd_emu10k1_ptr_write(emu, A_TANKMEMCTLREGBASE + tram, 0, addr >> 20);
  }
 }
 return 0;
}

static int snd_emu10k1_tram_peek(struct snd_emu10k1 *emu,
     struct snd_emu10k1_fx8010_code *icode)
{
 int tram;
 u32 val, addr;

 memset(icode->tram_valid, 0, sizeof(icode->tram_valid));
 for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
  set_bit(tram, icode->tram_valid);
  val = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0);
  if (!emu->audigy) {
   addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0);
  } else {
   addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0) >> 12;
   addr |= snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + tram, 0) << 20;
  }
  if (put_user(val, (__user u32 *)&icode->tram_data_map[tram]) ||
      put_user(addr, (__user u32 *)&icode->tram_addr_map[tram]))
   return -EFAULT;
 }
 return 0;
}

static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu,
     struct snd_emu10k1_fx8010_code *icode,
     bool in_kernel)
{
 u32 pc, lo, hi;

 for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
  if (!test_bit(pc / 2, icode->code_valid))
   continue;
  if (in_kernel) {
   lo = icode->code[pc + 0];
   hi = icode->code[pc + 1];
  } else {
   if (get_user(lo, (__user u32 *)&icode->code[pc + 0]) ||
       get_user(hi, (__user u32 *)&icode->code[pc + 1]))
    return -EFAULT;
  }
  snd_emu10k1_efx_write(emu, pc + 0, lo);
  snd_emu10k1_efx_write(emu, pc + 1, hi);
 }
 return 0;
}

static int snd_emu10k1_code_peek(struct snd_emu10k1 *emu,
     struct snd_emu10k1_fx8010_code *icode)
{
 u32 pc;

 memset(icode->code_valid, 0, sizeof(icode->code_valid));
 for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
  set_bit(pc / 2, icode->code_valid);
  if (put_user(snd_emu10k1_efx_read(emu, pc + 0),
        (__user u32 *)&icode->code[pc + 0]))
   return -EFAULT;
  if (put_user(snd_emu10k1_efx_read(emu, pc + 1),
        (__user u32 *)&icode->code[pc + 1]))
   return -EFAULT;
 }
 return 0;
}

static struct snd_emu10k1_fx8010_ctl *
snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu,
    struct emu10k1_ctl_elem_id *_id)
{
 struct snd_ctl_elem_id *id = (struct snd_ctl_elem_id *)_id;
 struct snd_emu10k1_fx8010_ctl *ctl;
 struct snd_kcontrol *kcontrol;

 list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
  kcontrol = ctl->kcontrol;
  if (kcontrol->id.iface == id->iface &&
      kcontrol->id.index == id->index &&
      !strcmp(kcontrol->id.name, id->name))
   return ctl;
 }
 return NULL;
}

#define MAX_TLV_SIZE 256

static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
{
 unsigned int data[2];
 unsigned int *tlv;

 if (!_tlv)
  return NULL;
 if (in_kernel)
  memcpy(data, (__force void *)_tlv, sizeof(data));
 else if (copy_from_user(data, _tlv, sizeof(data)))
  return NULL;
 if (data[1] >= MAX_TLV_SIZE)
  return NULL;
 tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL);
 if (!tlv)
  return NULL;
 memcpy(tlv, data, sizeof(data));
 if (in_kernel) {
  memcpy(tlv + 2, (__force void *)(_tlv + 2),  data[1]);
 } else if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
  kfree(tlv);
  return NULL;
 }
 return tlv;
}

static int copy_gctl(struct snd_emu10k1 *emu,
       struct snd_emu10k1_fx8010_control_gpr *dst,
       struct snd_emu10k1_fx8010_control_gpr *src,
       int idx, bool in_kernel)
{
 struct snd_emu10k1_fx8010_control_gpr __user *_src;
 struct snd_emu10k1_fx8010_control_old_gpr *octl;
 struct snd_emu10k1_fx8010_control_old_gpr __user *_octl;

 _src = (struct snd_emu10k1_fx8010_control_gpr __user *)src;
 if (emu->support_tlv) {
  if (in_kernel)
   *dst = src[idx];
  else if (copy_from_user(dst, &_src[idx], sizeof(*src)))
   return -EFAULT;
  return 0;
 }

 octl = (struct snd_emu10k1_fx8010_control_old_gpr *)src;
 _octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)octl;
 if (in_kernel)
  memcpy(dst, &octl[idx], sizeof(*octl));
 else if (copy_from_user(dst, &_octl[idx], sizeof(*octl)))
  return -EFAULT;
 dst->tlv = NULL;
 return 0;
}

static int copy_gctl_to_user(struct snd_emu10k1 *emu,
       struct snd_emu10k1_fx8010_control_gpr *dst,
       struct snd_emu10k1_fx8010_control_gpr *src,
       int idx)
{
 struct snd_emu10k1_fx8010_control_gpr __user *_dst;
 struct snd_emu10k1_fx8010_control_old_gpr __user *octl;

 _dst = (struct snd_emu10k1_fx8010_control_gpr __user *)dst;
 if (emu->support_tlv)
  return copy_to_user(&_dst[idx], src, sizeof(*src));
 
 octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)dst;
 return copy_to_user(&octl[idx], src, sizeof(*octl));
}

static int copy_ctl_elem_id(const struct emu10k1_ctl_elem_id *list, int i,
       struct emu10k1_ctl_elem_id *ret, bool in_kernel)
{
 struct emu10k1_ctl_elem_id __user *_id =
  (struct emu10k1_ctl_elem_id __user *)&list[i];

 if (in_kernel)
  *ret = list[i];
 else if (copy_from_user(ret, _id, sizeof(*ret)))
  return -EFAULT;
 return 0;
}

static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
           struct snd_emu10k1_fx8010_code *icode,
           bool in_kernel)
{
 unsigned int i;
 struct emu10k1_ctl_elem_id id;
 struct snd_emu10k1_fx8010_control_gpr *gctl;
 struct snd_ctl_elem_id *gctl_id;
 int err;
 
 for (i = 0; i < icode->gpr_del_control_count; i++) {
  err = copy_ctl_elem_id(icode->gpr_del_controls, i, &id,
           in_kernel);
  if (err < 0)
   return err;
  if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
   return -ENOENT;
 }
 gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
 if (! gctl)
  return -ENOMEM;
 err = 0;
 for (i = 0; i < icode->gpr_add_control_count; i++) {
  if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
         in_kernel)) {
   err = -EFAULT;
   goto __error;
  }
  if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
   continue;
  gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
  if (snd_ctl_find_id(emu->card, gctl_id)) {
   err = -EEXIST;
   goto __error;
  }
  if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
      gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
   err = -EINVAL;
   goto __error;
  }
  switch (gctl->translation) {
  case EMU10K1_GPR_TRANSLATION_NONE:
  case EMU10K1_GPR_TRANSLATION_NEGATE:
   break;
  case EMU10K1_GPR_TRANSLATION_TABLE100:
  case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
   if (gctl->min != 0 || gctl->max != 100) {
    err = -EINVAL;
    goto __error;
   }
   break;
  case EMU10K1_GPR_TRANSLATION_BASS:
  case EMU10K1_GPR_TRANSLATION_TREBLE:
   if (gctl->min != 0 || gctl->max != 40) {
    err = -EINVAL;
    goto __error;
   }
   break;
  case EMU10K1_GPR_TRANSLATION_ONOFF:
   if (gctl->min != 0 || gctl->max != 1) {
    err = -EINVAL;
    goto __error;
   }
   break;
  default:
   err = -EINVAL;
   goto __error;
  }
 }
 for (i = 0; i < icode->gpr_list_control_count; i++) {
       /* FIXME: we need to check the WRITE access */
  if (copy_gctl(emu, gctl, icode->gpr_list_controls, i,
         in_kernel)) {
   err = -EFAULT;
   goto __error;
  }
 }
 __error:
 kfree(gctl);
 return err;
}

static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
{
 struct snd_emu10k1_fx8010_ctl *ctl;
 
 ctl = (struct snd_emu10k1_fx8010_ctl *) kctl->private_value;
 kctl->private_value = 0;
 list_del(&ctl->list);
 kfree(ctl);
 kfree(kctl->tlv.p);
}

static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
        struct snd_emu10k1_fx8010_code *icode,
        bool in_kernel)
{
 unsigned int i, j;
 struct snd_emu10k1_fx8010_control_gpr *gctl;
 struct snd_ctl_elem_id *gctl_id;
 struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
 struct snd_kcontrol_new knew;
 struct snd_kcontrol *kctl;
 struct snd_ctl_elem_value *val;
 int err = 0;

 val = kmalloc(sizeof(*val), GFP_KERNEL);
 gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
 nctl = kmalloc(sizeof(*nctl), GFP_KERNEL);
 if (!val || !gctl || !nctl) {
  err = -ENOMEM;
  goto __error;
 }

 for (i = 0; i < icode->gpr_add_control_count; i++) {
  if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
         in_kernel)) {
   err = -EFAULT;
   goto __error;
  }
  gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
  if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
      gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
   err = -EINVAL;
   goto __error;
  }
  if (!*gctl_id->name) {
   err = -EINVAL;
   goto __error;
  }
  ctl = snd_emu10k1_look_for_ctl(emu, &gctl->id);
  memset(&knew, 0, sizeof(knew));
  knew.iface = gctl_id->iface;
  knew.name = gctl_id->name;
  knew.index = gctl_id->index;
  knew.device = gctl_id->device;
  knew.subdevice = gctl_id->subdevice;
  knew.info = snd_emu10k1_gpr_ctl_info;
  knew.tlv.p = copy_tlv((const unsigned int __user *)gctl->tlv, in_kernel);
  if (knew.tlv.p)
   knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
    SNDRV_CTL_ELEM_ACCESS_TLV_READ;
  knew.get = snd_emu10k1_gpr_ctl_get;
  knew.put = snd_emu10k1_gpr_ctl_put;
  memset(nctl, 0, sizeof(*nctl));
  nctl->vcount = gctl->vcount;
  nctl->count = gctl->count;
  for (j = 0; j < 32; j++) {
   nctl->gpr[j] = gctl->gpr[j];
   nctl->value[j] = ~gctl->value[j]; /* inverted, we want to write new value in gpr_ctl_put() */
   val->value.integer.value[j] = gctl->value[j];
  }
  nctl->min = gctl->min;
  nctl->max = gctl->max;
  nctl->translation = gctl->translation;
  if (ctl == NULL) {
   ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
   if (ctl == NULL) {
    err = -ENOMEM;
    kfree(knew.tlv.p);
    goto __error;
   }
   knew.private_value = (unsigned long)ctl;
   *ctl = *nctl;
   kctl = snd_ctl_new1(&knew, emu);
   err = snd_ctl_add(emu->card, kctl);
   if (err < 0) {
    kfree(ctl);
    kfree(knew.tlv.p);
    goto __error;
   }
   kctl->private_free = snd_emu10k1_ctl_private_free;
   ctl->kcontrol = kctl;
   list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl);
  } else {
   /* overwrite */
   nctl->list = ctl->list;
   nctl->kcontrol = ctl->kcontrol;
   *ctl = *nctl;
   snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
                             SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id);
  }
  snd_emu10k1_gpr_ctl_put(ctl->kcontrol, val);
 }
      __error:
 kfree(nctl);
 kfree(gctl);
 kfree(val);
 return err;
}

static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
        struct snd_emu10k1_fx8010_code *icode,
        bool in_kernel)
{
 unsigned int i;
 struct emu10k1_ctl_elem_id id;
 struct snd_emu10k1_fx8010_ctl *ctl;
 struct snd_card *card = emu->card;
 int err;
 
 for (i = 0; i < icode->gpr_del_control_count; i++) {
  err = copy_ctl_elem_id(icode->gpr_del_controls, i, &id,
           in_kernel);
  if (err < 0)
   return err;
  ctl = snd_emu10k1_look_for_ctl(emu, &id);
  if (ctl)
   snd_ctl_remove(card, ctl->kcontrol);
 }
 return 0;
}

static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
         struct snd_emu10k1_fx8010_code *icode)
{
 unsigned int i = 0, j;
 unsigned int total = 0;
 struct snd_emu10k1_fx8010_control_gpr *gctl;
 struct snd_emu10k1_fx8010_ctl *ctl;
 struct snd_ctl_elem_id *id;

 gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
 if (! gctl)
  return -ENOMEM;

 list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
  total++;
  if (icode->gpr_list_controls &&
      i < icode->gpr_list_control_count) {
   memset(gctl, 0, sizeof(*gctl));
   id = &ctl->kcontrol->id;
   gctl->id.iface = (__force int)id->iface;
   strscpy(gctl->id.name, id->name, sizeof(gctl->id.name));
   gctl->id.index = id->index;
   gctl->id.device = id->device;
   gctl->id.subdevice = id->subdevice;
   gctl->vcount = ctl->vcount;
   gctl->count = ctl->count;
   for (j = 0; j < 32; j++) {
    gctl->gpr[j] = ctl->gpr[j];
    gctl->value[j] = ctl->value[j];
   }
   gctl->min = ctl->min;
   gctl->max = ctl->max;
   gctl->translation = ctl->translation;
   if (copy_gctl_to_user(emu, icode->gpr_list_controls,
           gctl, i)) {
    kfree(gctl);
    return -EFAULT;
   }
   i++;
  }
 }
 icode->gpr_list_control_total = total;
 kfree(gctl);
 return 0;
}

static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
      struct snd_emu10k1_fx8010_code *icode,
      bool in_kernel)
{
 int err = 0;

 mutex_lock(&emu->fx8010.lock);
 err = snd_emu10k1_verify_controls(emu, icode, in_kernel);
 if (err < 0)
  goto __error;
 strscpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
 /* stop FX processor - this may be dangerous, but it's better to miss
   some samples than generate wrong ones - [jk] */

 if (emu->audigy)
  snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
 else
  snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
 /* ok, do the main job */
 err = snd_emu10k1_del_controls(emu, icode, in_kernel);
 if (err < 0)
  goto __error;
 err = snd_emu10k1_gpr_poke(emu, icode, in_kernel);
 if (err < 0)
  goto __error;
 err = snd_emu10k1_tram_poke(emu, icode, in_kernel);
 if (err < 0)
  goto __error;
 err = snd_emu10k1_code_poke(emu, icode, in_kernel);
 if (err < 0)
  goto __error;
 err = snd_emu10k1_add_controls(emu, icode, in_kernel);
 if (err < 0)
  goto __error;
 /* start FX processor when the DSP code is updated */
 if (emu->audigy)
  snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
 else
  snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
      __error:
 mutex_unlock(&emu->fx8010.lock);
 return err;
}

static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
      struct snd_emu10k1_fx8010_code *icode)
{
 int err;

 mutex_lock(&emu->fx8010.lock);
 strscpy(icode->name, emu->fx8010.name, sizeof(icode->name));
 /* ok, do the main job */
 err = snd_emu10k1_gpr_peek(emu, icode);
 if (err >= 0)
  err = snd_emu10k1_tram_peek(emu, icode);
 if (err >= 0)
  err = snd_emu10k1_code_peek(emu, icode);
 if (err >= 0)
  err = snd_emu10k1_list_controls(emu, icode);
 mutex_unlock(&emu->fx8010.lock);
 return err;
}

static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
     struct snd_emu10k1_fx8010_pcm_rec *ipcm)
{
 unsigned int i;
 int err = 0;
 struct snd_emu10k1_fx8010_pcm *pcm;

 if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
  return -EINVAL;
 ipcm->substream = array_index_nospec(ipcm->substream,
          EMU10K1_FX8010_PCM_COUNT);
 if (ipcm->channels > 32)
  return -EINVAL;
 pcm = &emu->fx8010.pcm[ipcm->substream];
 mutex_lock(&emu->fx8010.lock);
 spin_lock_irq(&emu->reg_lock);
 if (pcm->opened) {
  err = -EBUSY;
  goto __error;
 }
 if (ipcm->channels == 0) { /* remove */
  pcm->valid = 0;
 } else {
  /* FIXME: we need to add universal code to the PCM transfer routine */
  if (ipcm->channels != 2) {
   err = -EINVAL;
   goto __error;
  }
  pcm->valid = 1;
  pcm->opened = 0;
  pcm->channels = ipcm->channels;
  pcm->tram_start = ipcm->tram_start;
  pcm->buffer_size = ipcm->buffer_size;
  pcm->gpr_size = ipcm->gpr_size;
  pcm->gpr_count = ipcm->gpr_count;
  pcm->gpr_tmpcount = ipcm->gpr_tmpcount;
  pcm->gpr_ptr = ipcm->gpr_ptr;
  pcm->gpr_trigger = ipcm->gpr_trigger;
  pcm->gpr_running = ipcm->gpr_running;
  for (i = 0; i < pcm->channels; i++)
   pcm->etram[i] = ipcm->etram[i];
 }
      __error:
 spin_unlock_irq(&emu->reg_lock);
 mutex_unlock(&emu->fx8010.lock);
 return err;
}

static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
     struct snd_emu10k1_fx8010_pcm_rec *ipcm)
{
 unsigned int i;
 int err = 0;
 struct snd_emu10k1_fx8010_pcm *pcm;

 if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
  return -EINVAL;
 ipcm->substream = array_index_nospec(ipcm->substream,
          EMU10K1_FX8010_PCM_COUNT);
 pcm = &emu->fx8010.pcm[ipcm->substream];
 mutex_lock(&emu->fx8010.lock);
 spin_lock_irq(&emu->reg_lock);
 ipcm->channels = pcm->channels;
 ipcm->tram_start = pcm->tram_start;
 ipcm->buffer_size = pcm->buffer_size;
 ipcm->gpr_size = pcm->gpr_size;
 ipcm->gpr_ptr = pcm->gpr_ptr;
 ipcm->gpr_count = pcm->gpr_count;
 ipcm->gpr_tmpcount = pcm->gpr_tmpcount;
 ipcm->gpr_trigger = pcm->gpr_trigger;
 ipcm->gpr_running = pcm->gpr_running;
 for (i = 0; i < pcm->channels; i++)
  ipcm->etram[i] = pcm->etram[i];
 ipcm->res1 = ipcm->res2 = 0;
 ipcm->pad = 0;
 spin_unlock_irq(&emu->reg_lock);
 mutex_unlock(&emu->fx8010.lock);
 return err;
}

#define SND_EMU10K1_GPR_CONTROLS 44
#define SND_EMU10K1_INPUTS  12
#define SND_EMU10K1_PLAYBACK_CHANNELS 8
#define SND_EMU10K1_CAPTURE_CHANNELS 4

#define HR_VAL(v) ((v) * 0x80000000LL / 100 - 1)

static void
snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl,
          const char *name, int gpr, int defval, int defval_hr)
{
 ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
 strscpy(ctl->id.name, name);
 ctl->vcount = ctl->count = 1;
 if (high_res_gpr_volume) {
  ctl->min = -1;
  ctl->max = 0x7fffffff;
  ctl->tlv = snd_emu10k1_db_linear;
  ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
  defval = defval_hr;
 } else {
  ctl->min = 0;
  ctl->max = 100;
  ctl->tlv = snd_emu10k1_db_scale1;
  ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
 }
 ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
}
#define snd_emu10k1_init_mono_control(ctl, name, gpr, defval) \
 snd_emu10k1_init_mono_control2(ctl, name, gpr, defval, HR_VAL(defval))

static void
snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl,
     const char *name, int gpr, int defval, int defval_hr)
{
 ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
 strscpy(ctl->id.name, name);
 ctl->vcount = ctl->count = 2;
 if (high_res_gpr_volume) {
  ctl->min = -1;
  ctl->max = 0x7fffffff;
  ctl->tlv = snd_emu10k1_db_linear;
  ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
  defval = defval_hr;
 } else {
  ctl->min = 0;
  ctl->max = 100;
  ctl->tlv = snd_emu10k1_db_scale1;
  ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
 }
 ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
 ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
}
#define snd_emu10k1_init_stereo_control(ctl, name, gpr, defval) \
 snd_emu10k1_init_stereo_control2(ctl, name, gpr, defval, HR_VAL(defval))

static void
snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
        const char *name, int gpr, int defval)
{
 ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
 strscpy(ctl->id.name, name);
 ctl->vcount = ctl->count = 1;
 ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
 ctl->min = 0;
 ctl->max = 1;
 ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
}

static void
snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
          const char *name, int gpr, int defval)
{
 ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
 strscpy(ctl->id.name, name);
 ctl->vcount = ctl->count = 2;
 ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
 ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
 ctl->min = 0;
 ctl->max = 1;
 ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
}

/*
 * Used for emu1010 - conversion from 32-bit capture inputs from the FPGA
 * to 2 x 16-bit registers in Audigy - their values are read via DMA.
 * Conversion is performed by Audigy DSP instructions of FX8010.
 */

static void snd_emu10k1_audigy_dsp_convert_32_to_2x16(
    struct snd_emu10k1_fx8010_code *icode,
    u32 *ptr, int tmp, int bit_shifter16,
    int reg_in, int reg_out)
{
 // This leaves the low word in place, which is fine,
 // as the low bits are completely ignored subsequently.
 // reg_out[1] = reg_in
 A_OP(icode, ptr, iACC3, reg_out + 1, reg_in, A_C_00000000, A_C_00000000);
 // It is fine to read reg_in multiple times.
 // tmp = reg_in << 15
 A_OP(icode, ptr, iMACINT1, A_GPR(tmp), A_C_00000000, reg_in, A_GPR(bit_shifter16));
 // Left-shift once more. This is a separate step, as the
 // signed multiplication would clobber the MSB.
 // reg_out[0] = tmp + ((tmp << 31) >> 31)
 A_OP(icode, ptr, iMAC3, reg_out, A_GPR(tmp), A_GPR(tmp), A_C_80000000);
}

#define ENUM_GPR(name, size) name, name ## _dummy = name + (size) - 1

/*
 * initial DSP configuration for Audigy
 */


static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
{
 int err, z, nctl;
 enum {
  ENUM_GPR(playback, SND_EMU10K1_PLAYBACK_CHANNELS),
  ENUM_GPR(stereo_mix, 2),
  ENUM_GPR(capture, 2),
  ENUM_GPR(bit_shifter16, 1),
  // The fixed allocation of these breaks the pattern, but why not.
  // Splitting these into left/right is questionable, as it will break
  // down for center/lfe. But it works for stereo/quadro, so whatever.
  ENUM_GPR(bass_gpr, 2 * 5),  // two sides, five coefficients
  ENUM_GPR(treble_gpr, 2 * 5),
  ENUM_GPR(bass_tmp, SND_EMU10K1_PLAYBACK_CHANNELS * 4),  // four delay stages
  ENUM_GPR(treble_tmp, SND_EMU10K1_PLAYBACK_CHANNELS * 4),
  ENUM_GPR(tmp, 3),
  num_static_gprs
 };
 int gpr = num_static_gprs;
 u32 ptr, ptr_skip;
 struct snd_emu10k1_fx8010_code *icode = NULL;
 struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
 u32 *gpr_map;

 err = -ENOMEM;
 icode = kzalloc(sizeof(*icode), GFP_KERNEL);
 if (!icode)
  return err;

 icode->gpr_map = kcalloc(512 + 256 + 256 + 2 * 1024,
     sizeof(u_int32_t), GFP_KERNEL);
 if (!icode->gpr_map)
  goto __err_gpr;
 controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
      sizeof(*controls), GFP_KERNEL);
 if (!controls)
  goto __err_ctrls;

 gpr_map = icode->gpr_map;

 icode->tram_data_map = icode->gpr_map + 512;
 icode->tram_addr_map = icode->tram_data_map + 256;
 icode->code = icode->tram_addr_map + 256;

 /* clear free GPRs */
 memset(icode->gpr_valid, 0xff, 512 / 8);
  
 /* clear TRAM data & address lines */
 memset(icode->tram_valid, 0xff, 256 / 8);

 strscpy(icode->name, "Audigy DSP code for ALSA");
 ptr = 0;
 nctl = 0;
 gpr_map[bit_shifter16] = 0x00008000;

#if 1
 /* PCM front Playback Volume (independent from stereo mix)
 * playback = -gpr * FXBUS_PCM_LEFT_FRONT >> 31
 * where gpr contains negated attenuation from corresponding mixer control
 * (snd_emu10k1_init_stereo_control)
 */

 A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
 gpr += 2;

 /* PCM Surround Playback (independent from stereo mix) */
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
 gpr += 2;
 
 /* PCM Side Playback (independent from stereo mix) */
 if (emu->card_capabilities->spk71) {
  A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
  A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
  snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
  gpr += 2;
 }

 /* PCM Center Playback (independent from stereo mix) */
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
 snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
 gpr++;

 /* PCM LFE Playback (independent from stereo mix) */
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
 snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
 gpr++;
 
 /*
 * Stereo Mix
 */

 /* Wave (PCM) Playback Volume (will be renamed later) */
 A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
 A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
 gpr += 2;

 /* Synth Playback */
 A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
 A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
 gpr += 2;

 /* Wave (PCM) Capture */
 A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
 A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
 gpr += 2;

 /* Synth Capture */
 A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
 A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
 gpr += 2;

 // We need to double the volume, as we configure the voices for half volume,
 // which is necessary for bit-identical reproduction.
 { static_assert(stereo_mix == playback + SND_EMU10K1_PLAYBACK_CHANNELS); }
 for (z = 0; z < SND_EMU10K1_PLAYBACK_CHANNELS + 2; z++)
  A_OP(icode, &ptr, iACC3, A_GPR(playback + z), A_GPR(playback + z), A_GPR(playback + z), A_C_00000000);

 /*
 * inputs
 */

#define A_ADD_VOLUME_IN(var,vol,input) \
 A_OP(icode, &ptr, iMAC1, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))

 if (emu->card_capabilities->emu_model) {
  /* EMU1010 DSP 0 and DSP 1 Capture */
  // The 24 MSB hold the actual value. We implicitly discard the 16 LSB.
  if (emu->card_capabilities->ca0108_chip) {
   // For unclear reasons, the EMU32IN cannot be the Y operand!
   A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A3_EMU32IN(0x0), A_GPR(gpr));
   // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
   // need to be delayed as well; we use an auxiliary register for that.
   A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
   A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A3_EMU32IN(0x1), A_C_00000000, A_C_00000000);
  } else {
   A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_P16VIN(0x0), A_GPR(gpr));
   // A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
   // need to be delayed as well; we use an auxiliary register for that.
   A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
   A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
  }
  snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
  gpr_map[gpr + 2] = 0x00000000;
  gpr += 3;
 } else {
  if (emu->card_capabilities->ac97_chip) {
   /* AC'97 Playback Volume - used only for mic (renamed later) */
   A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
   A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
   snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
   gpr += 2;
   /* AC'97 Capture Volume - used only for mic */
   A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
   A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
   snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
   gpr += 2;

   /* mic capture buffer */
   A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), A_C_40000000, A_EXTIN(A_EXTIN_AC97_R));
  }

  /* Audigy CD Playback Volume */
  A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
  A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++],
      emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
      gpr, 0);
  gpr += 2;
  /* Audigy CD Capture Volume */
  A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
  A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++],
      emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
      gpr, 0);
  gpr += 2;

  /* Optical SPDIF Playback Volume */
  A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
  A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
  gpr += 2;
  /* Optical SPDIF Capture Volume */
  A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
  A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
  gpr += 2;

  /* Line2 Playback Volume */
  A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
  A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++],
      emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
      gpr, 0);
  gpr += 2;
  /* Line2 Capture Volume */
  A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
  A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++],
      emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
      gpr, 0);
  gpr += 2;

  /* Philips ADC Playback Volume */
  A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
  A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
  gpr += 2;
  /* Philips ADC Capture Volume */
  A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
  A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
  gpr += 2;

  /* Aux2 Playback Volume */
  A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
  A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++],
      emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
      gpr, 0);
  gpr += 2;
  /* Aux2 Capture Volume */
  A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
  A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
  snd_emu10k1_init_stereo_control(&controls[nctl++],
      emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
      gpr, 0);
  gpr += 2;
 }
 
 /* Stereo Mix Front Playback Volume */
 A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
 gpr += 2;
 
 /* Stereo Mix Surround Playback */
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
 snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
 gpr += 2;

 /* Stereo Mix Center Playback */
 /* Center = sub = Left/2 + Right/2 */
 A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), A_C_40000000, A_GPR(stereo_mix+1));
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
 snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
 gpr++;

 /* Stereo Mix LFE Playback */
 A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
 snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
 gpr++;
 
 if (emu->card_capabilities->spk71) {
  /* Stereo Mix Side Playback */
  A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
  A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
  snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
  gpr += 2;
 }

 /*
 * outputs
 */

#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src))
#define A_PUT_STEREO_OUTPUT(out1,out2,src) \
 {A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);}

#define _A_SWITCH(icode, ptr, dst, src, sw) \
 A_OP((icode), ptr, iMACINT0, dst, A_C_00000000, src, sw);
#define A_SWITCH(icode, ptr, dst, src, sw) \
  _A_SWITCH(icode, ptr, A_GPR(dst), A_GPR(src), A_GPR(sw))
#define _A_SWITCH_NEG(icode, ptr, dst, src) \
 A_OP((icode), ptr, iANDXOR, dst, src, A_C_00000001, A_C_00000001);
#define A_SWITCH_NEG(icode, ptr, dst, src) \
  _A_SWITCH_NEG(icode, ptr, A_GPR(dst), A_GPR(src))


 /*
 *  Process tone control
 */

 ctl = &controls[nctl + 0];
 ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
 strscpy(ctl->id.name, "Tone Control - Bass");
 ctl->vcount = 2;
 ctl->count = 10;
 ctl->min = 0;
 ctl->max = 40;
 ctl->value[0] = ctl->value[1] = 20;
 ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
 ctl = &controls[nctl + 1];
 ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
 strscpy(ctl->id.name, "Tone Control - Treble");
 ctl->vcount = 2;
 ctl->count = 10;
 ctl->min = 0;
 ctl->max = 40;
 ctl->value[0] = ctl->value[1] = 20;
 ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
 for (z = 0; z < 5; z++) {
  int j;
  for (j = 0; j < 2; j++) {
   controls[nctl + 0].gpr[z * 2 + j] = bass_gpr + z * 2 + j;
   controls[nctl + 1].gpr[z * 2 + j] = treble_gpr + z * 2 + j;
  }
 }
 nctl += 2;

 A_OP(icode, &ptr, iACC3, A_C_00000000, A_GPR(gpr), A_C_00000000, A_C_00000000);
 snd_emu10k1_init_mono_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
 gpr++;
 A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_CC_REG_ZERO, A_GPR(gpr));
 ptr_skip = ptr;
 for (z = 0; z < 4; z++) {  /* front/rear/center-lfe/side */
  int j, k, l, d;
  for (j = 0; j < 2; j++) { /* left/right */
   k = bass_tmp + (z * 8) + (j * 4);
   l = treble_tmp + (z * 8) + (j * 4);
   d = playback + z * 2 + j;

   A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(bass_gpr + 0 + j));
   A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(bass_gpr + 4 + j));
   A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(bass_gpr + 2 + j));
   A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(bass_gpr + 8 + j));
   A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(bass_gpr + 6 + j));
   A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000);

   A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(treble_gpr + 0 + j));
   A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(treble_gpr + 4 + j));
   A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(treble_gpr + 2 + j));
   A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(treble_gpr + 8 + j));
   A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(treble_gpr + 6 + j));
   A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010);

   A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000);

   if (z == 2) /* center */
    break;
  }
 }
 gpr_map[gpr++] = ptr - ptr_skip;

 /* Master volume (will be renamed later) */
 for (z = 0; z < 8; z++)
  A_OP(icode, &ptr, iMAC1, A_GPR(playback+z), A_C_00000000, A_GPR(gpr), A_GPR(playback+z));
 snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
 gpr++;

 if (emu->card_capabilities->emu_model) {
  /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
  dev_info(emu->card->dev, "EMU outputs on\n");
  for (z = 0; z < 8; z++) {
   if (emu->card_capabilities->ca0108_chip) {
    A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + z), A_C_00000000, A_C_00000000);
   } else {
    A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + z), A_C_00000000, A_C_00000000);
   }
  }
 } else {
  /* analog speakers */
  A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback);
  A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2);
  A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4);
  A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5);
  if (emu->card_capabilities->spk71)
   A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6);

  /* headphone */
  A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback);

  /* IEC958 Optical Raw Playback Switch */
  gpr_map[gpr++] = 0;
  gpr_map[gpr++] = 0x1008;
  gpr_map[gpr++] = 0xffff0000;
  for (z = 0; z < 2; z++) {
   A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
   A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
   A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
   A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
   A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
   A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
   A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
   if ((z==1) && (emu->card_capabilities->spdif_bug)) {
    /* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
    dev_info(emu->card->dev,
      "Installing spdif_bug patch: %s\n",
      emu->card_capabilities->name);
    A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
    A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
   } else {
    A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
   }
  }
  snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
  gpr += 2;

  A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2);
  A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4);
  A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5);
 }

 /* ADC buffer */
#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
 A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback);
#else
 A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
 A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
#endif

 if (emu->card_capabilities->emu_model) {
  /* Capture 16 channels of S32_LE sound. */
  if (emu->card_capabilities->ca0108_chip) {
   dev_info(emu->card->dev, "EMU2 inputs on\n");
   /* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */

   snd_emu10k1_audigy_dsp_convert_32_to_2x16(
    icode, &ptr, tmp, bit_shifter16, A3_EMU32IN(0), A_FXBUS2(0));
   // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
   // need to be delayed as well; we use an auxiliary register for that.
   for (z = 1; z < 0x10; z++) {
    snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, 
         bit_shifter16,
         A_GPR(gpr),
         A_FXBUS2(z*2) );
    A_OP(icode, &ptr, iACC3, A_GPR(gpr), A3_EMU32IN(z), A_C_00000000, A_C_00000000);
    gpr_map[gpr++] = 0x00000000;
   }
  } else {
   dev_info(emu->card->dev, "EMU inputs on\n");
   /* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */

   /*
dev_dbg(emu->card->dev, "emufx.c: gpr=0x%x, tmp=0x%x\n",
       gpr, tmp);
*/

   snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
   /* A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
 * will need to also be delayed; we use an auxiliary register for that. */

   for (z = 1; z < 0x10; z++) {
    snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr), A_FXBUS2(z * 2) );
    A_OP(icode, &ptr, iACC3, A_GPR(gpr), A_P16VIN(z), A_C_00000000, A_C_00000000);
    gpr_map[gpr++] = 0x00000000;
   }
  }

#if 0
  for (z = 4; z < 8; z++) {
   A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
  }
  for (z = 0xc; z < 0x10; z++) {
   A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
  }
#endif
 } else {
  /* EFX capture - capture the 16 EXTINs */
  /* Capture 16 channels of S16_LE sound */
  for (z = 0; z < 16; z++) {
   A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z));
  }
 }
 
#endif /* JCD test */
 /*
 * ok, set up done..
 */


 if (gpr > 512) {
  snd_BUG();
  err = -EIO;
  goto __err;
 }

 /* clear remaining instruction memory */
 while (ptr < 0x400)
  A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);

 icode->gpr_add_control_count = nctl;
 icode->gpr_add_controls = controls;
 emu->support_tlv = 1; /* support TLV */
 err = snd_emu10k1_icode_poke(emu, icode, true);
 emu->support_tlv = 0; /* clear again */

__err:
 kfree(controls);
__err_ctrls:
 kfree(icode->gpr_map);
__err_gpr:
 kfree(icode);
 return err;
}


/*
 * initial DSP configuration for Emu10k1
 */


/* Volumes are in the [-2^31, 0] range, zero being mute. */
static void _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
 OP(icode, ptr, iMAC1, dst, C_00000000, src, vol);
}
static void _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
 OP(icode, ptr, iMAC1, dst, dst, src, vol);
}

#define VOLUME(icode, ptr, dst, src, vol) \
  _volume(icode, ptr, GPR(dst), GPR(src), GPR(vol))
#define VOLUME_IN(icode, ptr, dst, src, vol) \
  _volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
#define VOLUME_ADD(icode, ptr, dst, src, vol) \
  _volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol))
#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
  _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
#define VOLUME_OUT(icode, ptr, dst, src, vol) \
  _volume(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
#define _SWITCH(icode, ptr, dst, src, sw) \
 OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
#define SWITCH(icode, ptr, dst, src, sw) \
  _SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw))
#define SWITCH_IN(icode, ptr, dst, src, sw) \
  _SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw))
#define _SWITCH_NEG(icode, ptr, dst, src) \
 OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001);
#define SWITCH_NEG(icode, ptr, dst, src) \
  _SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))


static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
{
 int err, i, z, gpr, tmp, playback, capture;
 u32 ptr, ptr_skip;
 struct snd_emu10k1_fx8010_code *icode;
 struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL;
 struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
 u32 *gpr_map;

 err = -ENOMEM;
 icode = kzalloc(sizeof(*icode), GFP_KERNEL);
 if (!icode)
  return err;

 icode->gpr_map = kcalloc(256 + 160 + 160 + 2 * 512,
     sizeof(u_int32_t), GFP_KERNEL);
 if (!icode->gpr_map)
  goto __err_gpr;

 controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
      sizeof(struct snd_emu10k1_fx8010_control_gpr),
      GFP_KERNEL);
 if (!controls)
  goto __err_ctrls;

 ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
 if (!ipcm)
  goto __err_ipcm;

 gpr_map = icode->gpr_map;

 icode->tram_data_map = icode->gpr_map + 256;
 icode->tram_addr_map = icode->tram_data_map + 160;
 icode->code = icode->tram_addr_map + 160;
 
 /* clear free GPRs */
 memset(icode->gpr_valid, 0xff, 256 / 8);

 /* clear TRAM data & address lines */
 memset(icode->tram_valid, 0xff, 160 / 8);

 strscpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
 ptr = 0; i = 0;
 /* we have 12 inputs */
 playback = SND_EMU10K1_INPUTS;
 /* we have 6 playback channels and tone control doubles */
 capture = playback + SND_EMU10K1_PLAYBACK_CHANNELS;
 gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
 tmp = 0x88; /* we need 4 temporary GPR */
 /* from 0x8c to 0xff is the area for tone control */

 /*
 *  Process FX Buses
 */

 OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */
 OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */
 OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000008);
 OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000008);

 /* Raw S/PDIF PCM */
 ipcm->substream = 0;
 ipcm->channels = 2;
 ipcm->tram_start = 0;
 ipcm->buffer_size = (64 * 1024) / 2;
 ipcm->gpr_size = gpr++;
 ipcm->gpr_ptr = gpr++;
 ipcm->gpr_count = gpr++;
 ipcm->gpr_tmpcount = gpr++;
 ipcm->gpr_trigger = gpr++;
 ipcm->gpr_running = gpr++;
 ipcm->etram[0] = 0;
 ipcm->etram[1] = 1;

 gpr_map[gpr + 0] = 0xfffff000;
 gpr_map[gpr + 1] = 0xffff0000;
 gpr_map[gpr + 2] = 0x70000000;
 gpr_map[gpr + 3] = 0x00000007;
 gpr_map[gpr + 4] = 0x001f << 11;
 gpr_map[gpr + 5] = 0x001c << 11;
 gpr_map[gpr + 6] = (0x22  - 0x01) - 1; /* skip at 01 to 22 */
 gpr_map[gpr + 7] = (0x22  - 0x06) - 1; /* skip at 06 to 22 */
 gpr_map[gpr + 8] = 0x2000000 + (2<<11);
 gpr_map[gpr + 9] = 0x4000000 + (2<<11);
 gpr_map[gpr + 10] = 1<<11;
 gpr_map[gpr + 11] = (0x24 - 0x0a) - 1; /* skip at 0a to 24 */
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=95 G=94

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.