Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Rainer Zimmermann <mail@lightshed.de>
To: Takashi Iwai <tiwai@suse.de>
Cc: ALSA Development Mailing List <alsa-devel@alsa-project.org>
Subject: Re: Patch - ESI Maya44 driver
Date: Fri, 28 Mar 2008 22:07:53 +0100	[thread overview]
Message-ID: <47ED5E29.4040400@lightshed.de> (raw)
In-Reply-To: <s5heja3158m.wl%tiwai@suse.de>

[-- Attachment #1: Type: text/plain, Size: 1662 bytes --]


Hi Takashi,

sorry about this. here's the fixed patch, identical to the last except for the 
coding style fixes.

-Rainer


Takashi Iwai wrote:
> At Fri, 21 Mar 2008 12:27:49 +0100,
> Rainer Zimmermann wrote:
>> Hi Takashi,
>>
>> here's the maya44 patch.
>>
>> This is a patch against the current alsa-kernel tree.
> 
> Thanks.  This looks better than the previous version.
> 
>> I did add 2 definitions to ice1712.h (snd_ice1724_get_route_val and 
>> snd_ice1724_put_route_val), which allowed me to move some of the maya44 specific 
>> code to maya44.c .
> 
> We could do it in a bit cleaer way, but I think it's OK as is.
> 
>> There are still a few maya44 special cases in ice1724.c, because maya44 uses the 
>> "con" channel and routing switches differently. This is not really clean style I 
>> think, but I would suggest sorting this out later, as it would again require 
>> larger changes to ice1724.c and testing with other hardware.
> 
> Agreed.  This kind of clean up can be done later.
> 
>> Hope everything is ok with you, for now.
>>
>> This patch does *not* limit the sampling rate to 96kHz for capture, as discussed 
>> before.
> 
> The patch looks apparently not compliant to the kernel coding style.
> Could you fix at least the issues reported by checkpatch.pl (the
> latest one)?
> 
> 
> thanks,
> 
> Takashi
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 


-- 
Lightshed IT Services
Löfflerstr. 27, 22765 Hamburg, Germany * mail@lightshed.de * www.lightshed.de

[-- Attachment #2: maya44_05.diff --]
[-- Type: text/x-patch, Size: 55951 bytes --]

diff -r 0d5f43585ca7 Documentation/README.maya44
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Documentation/README.maya44	Fri Mar 28 21:47:56 2008 +0100
@@ -0,0 +1,158 @@
+
+STATE OF DEVELOPMENT:
+
+This driver is being developed on the initiative of Piotr Makowski (oponek@gmail.com) and financed by Lars Bergmann.
+Development is carried out by Rainer Zimmermann (mail@lightshed.de).
+
+ESI provided a sample Maya44 card for the development work.
+
+However, unfortunately it has turned out difficult to get detailed programming information, so I (Rainer Zimmermann) had to find out some card-specific information by experiment and conjecture. Some information (in particular, several GPIO bits) is still missing.
+
+This is the first testing version of the Maya44 driver released to the alsa-devel mailing list (Feb 5, 2008).
+
+
+The following functions work, as tested by Rainer Zimmermann and Piotr Makowski:
+
+- playback and capture at all sampling rates
+- input/output level
+- crossmixing
+- line/mic switch
+- phantom power switch
+- analogue monitor a.k.a bypass
+
+
+The following functions *should* work, but are not fully tested:
+
+- Channel 3+4 analogue - S/PDIF input switching
+- S/PDIF output
+- all inputs/outputs on the M/IO/DIO extension card
+- internal/external clock selection
+
+
+*In particular, we would appreciate testing of these functions by anyone who has access to an M/IO/DIO extension card.*
+
+
+Things that do not seem to work:
+
+- The level meters ("multi track") in 'alsamixer' do not seem to react to signals in (if this is a bug, it would probably be in the existing ICE1724 code).
+
+- Ardour 2.1 seems to work only via JACK, not using ALSA directly or via OSS. This still needs to be tracked down.
+
+
+DRIVER DETAILS:
+
+the following files were added:
+
+pci/ice1724/maya44.c        - Maya44 specific code
+pci/ice1724/maya44.h
+pci/ice1724/ice1724.patch
+pci/ice1724/ice1724.h.patch - PROPOSED patch to ice1724.h (see SAMPLING RATES)
+i2c/other/wm8776.c  - low-level access routines for Wolfson WM8776 codecs 
+include/wm8776.h
+
+
+Note that the wm8776.c code is meant to be card-independent and does not actually register the codec with the ALSA infrastructure.
+This is done in maya44.c, mainly because some of the WM8776 controls are used in Maya44-specific ways, and should be named appropriately.
+
+
+the following files were created in pci/ice1724, simply #including the corresponding file from the alsa-kernel tree:
+
+wtm.h
+vt1720_mobo.h
+revo.h
+prodigy192.h
+pontis.h
+phase.h
+maya44.h
+juli.h
+aureon.h
+amp.h
+envy24ht.h
+se.h
+prodigy_hifi.h
+
+
+*I hope this is the correct way to do things.*
+
+
+SAMPLING RATES:
+
+The Maya44 card (or more exactly, the Wolfson WM8776 codecs) allow a maximum sampling rate of 192 kHz for playback and 92 kHz for capture.
+
+As the ICE1724 chip only allows one global sampling rate, this is handled as follows:
+
+* setting the sampling rate on any open PCM device on the maya44 card will always set the *global* sampling rate for all playback and capture channels.
+
+* In the current state of the driver, setting rates of up to 192 kHz is permitted even for capture devices.
+
+*AVOID CAPTURING AT RATES ABOVE 96kHz*, even though it may appear to work. The codec cannot actually capture at such rates, meaning poor quality.
+
+
+I propose some additional code for limiting the sampling rate when setting on a capture pcm device. However because of the global sampling rate, this logic would be somewhat problematic.
+
+The proposed code (currently deactivated) is in ice1712.h.patch, ice1724.c and maya44.c (in pci/ice1712).
+
+
+SOUND DEVICES:
+
+PCM devices correspond to inputs/outputs as follows (assuming Maya44 is card #0):
+
+hw:0,0 input - stereo, analog input 1+2
+hw:0,0 output - stereo, analog output 1+2
+hw:0,1 input - stereo, analog input 3+4 OR S/PDIF input
+hw:0,1 output - stereo, analog output 3+4 (and SPDIF out)
+
+
+NAMING OF MIXER CONTROLS:
+
+(for more information about the signal flow, please refer to the block diagram on p.24 of the ESI Maya44 manual, or in the ESI windows software).
+
+
+PCM: (digital) output level for channel 1+2
+PCM 1: same for channel 3+4
+
+Mic Phantom+48V: switch for +48V phantom power for electrostatic microphones on input 1/2.
+    Make sure this is not turned on while any other source is connected to input 1/2.
+    It might damage the source and/or the maya44 card.
+
+Mic/Line input: if switch is is on, input jack 1/2 is microphone input (mono), otherwise line input (stereo).
+
+Bypass: analogue bypass from ADC input to output for channel 1+2. Same as "Monitor" in the windows driver.
+Bypass 1: same for channel 3+4.
+
+Crossmix: cross-mixer from channels 1+2 to channels 3+4
+Crossmix 1: cross-mixer from channels 3+4 to channels 1+2
+
+IEC958 Output: switch for S/PDIF output.
+    This is not supported by the ESI windows driver.
+    S/PDIF should output the same signal as channel 3+4. [untested!]
+
+
+Digitial output selectors:
+
+    These switches allow a direct digital routing from the ADCs to the DACs.
+    Each switch determines where the digital input data to one of the DACs comes from.
+    They are not supported by the ESI windows driver.
+    For normal operation, they should all be set to "PCM out".
+
+H/W: Output source channel 1
+H/W 1: Output source channel 2
+H/W 2: Output source channel 3
+H/W 3: Output source channel 4
+
+H/W 4 ... H/W 9: unknown function, left in to enable testing.
+    Possibly some of these control S/PDIF output(s).
+    If these turn out to be unused, they will go away in later driver versions.
+
+Selectable values for each of the digital output selectors are:
+   "PCM out" -> DAC output of the corresponding channel (default setting)
+   "Input 1"...
+   "Input 4" -> direct routing from ADC output of the selected input channel
+
+
+--------
+
+Feb 14, 2008
+Rainer Zimmermann
+mail@lightshed.de
+
diff -r 0d5f43585ca7 i2c/other/Makefile
--- a/i2c/other/Makefile	Sat Mar 22 10:26:05 2008 +0100
+++ b/i2c/other/Makefile	Fri Mar 28 21:47:56 2008 +0100
@@ -8,9 +8,10 @@ snd-ak4xxx-adda-objs := ak4xxx-adda.o
 snd-ak4xxx-adda-objs := ak4xxx-adda.o
 snd-pt2258-objs := pt2258.o
 snd-tea575x-tuner-objs := tea575x-tuner.o
+snd-wm8776-objs := wm8776.o
 
 # Module Dependency
 obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
 obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o snd-wm8776.o
 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff -r 0d5f43585ca7 i2c/other/wm8776.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i2c/other/wm8776.c	Fri Mar 28 21:47:56 2008 +0100
@@ -0,0 +1,410 @@
+/*
+ *   Low-level routines for Wolfson WM8776 codec
+ *
+ *	Copyright (c) 2007 Rainer Zimmermann <mail@lightshed.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+ * not implemented:
+ * ADC highpass,
+ * AGC/limiter functions,
+ * DAC output phase+output control
+ * de-emphasis mode
+ * etc...
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/wm8776.h>
+
+MODULE_AUTHOR("Rainer Zimmermann <mail@lightshed.de>");
+MODULE_DESCRIPTION("Routines for control of WM8776 codecs");
+MODULE_LICENSE("GPL");
+
+/*
+ * If defined, ADC attenuation down to -103dB will be allowed, using the
+ * WM8776's digital attenuator. IMO this is rather useless because it will
+ * not prevent ADC overdrive, and digital attenuation can always be done in
+ *  software.
+ * If not defined, ADC level runs from -21 to +24dB, using the analogue PGA.
+ *
+ * NOTE: this driver does not currently support input mute. Since input level
+ * applies also to the analogue bypass besides the ADC, muting should be
+ * done using MUTERA/MUTELA in R21, rather than the digital mute using
+ * R14/R15.
+ */
+#undef WM8776_ALLOW_ADC_DIGITAL_ATTEN
+
+/*
+ * dB tables
+ */
+/* headphone output: mute, -73..+6db (1db step) */
+static const DECLARE_TLV_DB_SCALE(tlv_scale_headphone, -7400, 100, 1);
+/* DAC output: mute, -127..0db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(tlv_scale_dac, -12750, 50, 1);
+
+#ifdef WM8776_ALLOW_ADC_DIGITAL_ATTEN
+#define ADC_REG_LOW 0x01
+/* ADC gain: mute, -103..+24db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(tlv_scale_adcgain, -10300, 50, 0);
+#else
+#define ADC_REG_LOW 0xa5
+/* ADC gain: mute, -21..+24db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(tlv_scale_adcgain, -2100, 50, 0);
+#endif
+
+static struct snd_wm8776_volume_info volumeinfo[WM8776_NUM_VOLUMES] =
+{
+	[WM8776_VOL_HEADPHONE_L] = {
+		.nlvl = 80,
+		.tlv_scale = tlv_scale_headphone,
+		.reg_idx = WM8776_REG_HEADPHONE_L,
+		.reg_mask = 0x7f,
+		.reg_low = 0x30,
+		.reg_mute = 0x00
+	},
+	[WM8776_VOL_HEADPHONE_R] = {
+		.nlvl = 80,
+		.tlv_scale = tlv_scale_headphone,
+		.reg_idx = WM8776_REG_HEADPHONE_R,
+		.reg_mask = 0x7f,
+		.reg_low = 0x30,
+		.reg_mute = 0x00
+	},
+	[WM8776_VOL_DAC_L] = {
+		.nlvl = 255,
+		.tlv_scale = tlv_scale_dac,
+		.reg_idx = WM8776_REG_DAC_ATTEN_L,
+		.reg_mask = 0xff,
+		.reg_low = 0x01,
+		.reg_mute = 0x00
+	},
+	[WM8776_VOL_DAC_R] = {
+		.nlvl = 255,
+		.tlv_scale = tlv_scale_dac,
+		.reg_idx = WM8776_REG_DAC_ATTEN_R,
+		.reg_mask = 0xff,
+		.reg_low = 0x01,
+		.reg_mute = 0x00
+	},
+	[WM8776_VOL_ADC_GAIN_L] = {
+		.nlvl = 91,
+		.tlv_scale = tlv_scale_adcgain,
+		.reg_idx = WM8776_REG_ADC_ATTEN_L,
+		.reg_mask = 0xff,
+		.reg_low = ADC_REG_LOW,
+		.reg_mute = ADC_REG_LOW
+	},
+	[WM8776_VOL_ADC_GAIN_R] = {
+		.nlvl = 91,
+		.tlv_scale = tlv_scale_adcgain,
+		.reg_idx = WM8776_REG_ADC_ATTEN_R,
+		.reg_mask = 0xff,
+		.reg_low = ADC_REG_LOW,
+		.reg_mute = ADC_REG_LOW
+	}
+};
+
+static struct snd_wm8776_switch_info switchinfo[WM8776_NUM_SWITCHES] =
+{
+	[WM8776_SW_OUT_DAC] = {
+		.n_states = 2,
+		.reg_idx = 0x16,
+		.reg_inv = 0,
+		.reg_mask = 0x01
+	},
+	[WM8776_SW_OUT_AUX] = {
+		.n_states = 2,
+		.reg_idx = 0x16,
+		.reg_inv = 0,
+		.reg_mask = 0x02
+	},
+	[WM8776_SW_OUT_BYPASS] = {
+		.n_states = 2,
+		.reg_idx = 0x16,
+		.reg_inv = 0,
+		.reg_mask = 0x04
+	},
+	[WM8776_SW_IN_1] = {
+		.n_states = 2,
+		.reg_idx = 0x15,
+		.reg_inv = 0,
+		.reg_mask = 0x01
+	},
+	[WM8776_SW_IN_2] = {
+		.n_states = 2,
+		.reg_idx = 0x15,
+		.reg_inv = 0,
+		.reg_mask = 0x02
+	},
+	[WM8776_SW_IN_3] = {
+		.n_states = 2,
+		.reg_idx = 0x15,
+		.reg_inv = 0,
+		.reg_mask = 0x04
+	},
+	[WM8776_SW_IN_4] = {
+		.n_states = 2,
+		.reg_idx = 0x15,
+		.reg_inv = 0,
+		.reg_mask = 0x08
+	},
+	[WM8776_SW_IN_5] = {
+		.n_states = 2,
+		.reg_idx = 0x15,
+		.reg_inv = 0,
+		.reg_mask = 0x10
+	}
+};
+
+/* write the given register and save the data to the cache */
+void snd_wm8776_reg_write(struct snd_wm8776 *wm, unsigned char reg,
+						unsigned short val)
+{
+/*	printk(KERN_DEBUG "wm8776: wm8776_reg_write\n"); */
+
+	mutex_lock(&wm->wm_mutex);
+	wm->ops.write(wm->private_data, wm->chip, reg, val);
+	wm->regs[reg] = val;
+	mutex_unlock(&wm->wm_mutex);
+}
+EXPORT_SYMBOL(snd_wm8776_reg_write);
+
+/*
+ * update the given register with and/or mask and save the data to the cache
+ */
+void snd_wm8776_reg_mask(struct snd_wm8776 *wm, unsigned char reg,
+			unsigned short and_mask, unsigned short or_mask)
+{
+	unsigned short val = (wm->regs[reg] & and_mask) | or_mask;
+	if (val != wm->regs[reg]) {
+		mutex_lock(&wm->wm_mutex);
+		wm->ops.write(wm->private_data, wm->chip, reg, val);
+		wm->regs[reg] = val;
+		mutex_unlock(&wm->wm_mutex);
+	}
+}
+
+/*
+ * change a volume setting on a WM8776 codec.
+ * index is one of the WM8776_VOL_XXX constants defined in wm8776.h.
+ * value runs from 1 to nlvl specified in volume_info, or 0 for mute.
+ * returns 1 if changed, 0 otherwise
+ */
+int snd_wm8776_set_volume(struct snd_wm8776 *wm, int index, int value)
+{
+	struct snd_wm8776_volume_info *vi = &volumeinfo[index];
+	unsigned short flags;
+
+	if (wm->volumes[index] == value)
+		return 0;
+
+	wm->volumes[index] = value;
+
+	/*
+	 * HEADPHONE (index<2): UPDATE and zero cross enable (0x180),
+	 * ADC GAIN: zero cross enable (0x100),
+	 * DAC ATTEN: UPDATE (0x100)
+	 */
+	flags = (index < 2) ? 0x180 : 0x100;
+	snd_wm8776_reg_write(wm, vi->reg_idx,
+		((value == 0) ? vi->reg_mute : vi->reg_low+value-1) | flags);
+
+	/* handle ADC mute */
+	if (index == WM8776_VOL_ADC_GAIN_L)
+		snd_wm8776_reg_mask(wm, WM8776_REG_ADC_MUX, ~0x80,
+						(value == 0) ? 0x80 : 0);
+
+	if (index == WM8776_VOL_ADC_GAIN_R)
+		snd_wm8776_reg_mask(wm, WM8776_REG_ADC_MUX, ~0x40,
+						(value == 0) ? 0x40 : 0);
+
+	return 1;
+}
+EXPORT_SYMBOL(snd_wm8776_set_volume);
+
+int snd_wm8776_get_volume(struct snd_wm8776 *wm, int index)
+{
+	return wm->volumes[index];
+}
+EXPORT_SYMBOL(snd_wm8776_get_volume);
+
+const struct snd_wm8776_volume_info *snd_wm8776_get_volume_info(int index)
+{
+	return volumeinfo+index;
+}
+EXPORT_SYMBOL(snd_wm8776_get_volume_info);
+
+/* return 1 if changed, 0 otherwise */
+int snd_wm8776_set_switch(struct snd_wm8776 *wm, int index, int value)
+{
+	struct snd_wm8776_switch_info *vi = &switchinfo[index];
+	unsigned int mask = 1<<index;
+	unsigned int sw = (wm->switches & ~mask) | (value?mask:0);
+	if (sw == wm->switches)
+		return 0;
+	wm->switches = sw;
+	if (vi->reg_inv)
+		value = !value;
+	snd_wm8776_reg_mask(wm, vi->reg_idx, ~vi->reg_mask,
+						value?vi->reg_mask:0);
+	return 1;
+}
+EXPORT_SYMBOL(snd_wm8776_set_switch);
+
+int snd_wm8776_get_switch(struct snd_wm8776 *wm, int index)
+{
+	return (wm->switches & (1<<index)) != 0;
+}
+EXPORT_SYMBOL(snd_wm8776_get_switch);
+
+const struct snd_wm8776_switch_info *snd_wm8776_get_switch_info(int index)
+{
+	return switchinfo+index;
+}
+EXPORT_SYMBOL(snd_wm8776_get_switch_info);
+
+/*
+ * this currently sets the same rate for ADC and DAC, but limits ADC rate
+ * to 256X (96kHz). For 256X mode (96kHz), this sets ADC oversampling to 64x,
+ * as recommended by WM8776 datasheet. Setting the rate is not really
+ * necessary in slave mode.
+ */
+void snd_wm8776_set_rate(struct snd_wm8776 *wm, unsigned char clock_ratio)
+{
+	unsigned char adc_ratio = clock_ratio;
+	if (adc_ratio < WM8776_CLOCK_RATIO_256FS)
+		adc_ratio = WM8776_CLOCK_RATIO_256FS;
+
+	snd_wm8776_reg_mask(wm, WM8776_REG_MASTER_MODE_CONTROL,
+		0x180,
+		adc_ratio | ((adc_ratio == WM8776_CLOCK_RATIO_256FS)?8:0)
+				| (clock_ratio<<4));
+}
+EXPORT_SYMBOL(snd_wm8776_set_rate);
+
+
+/*
+ * reset a WM8776 codec
+ * @state: 1 = reset codec only, 0 = reset and restore the registers
+ *
+ * assert the reset operation and restores the register values to the chips.
+ */
+void snd_wm8776_reset(struct snd_wm8776 *wm, int state)
+{
+	unsigned char reg;
+
+	snd_wm8776_reg_write(wm, WM8776_REG_RESET, 0);  /* reset */
+
+	if (state) {
+		/* mute DAC */
+		snd_wm8776_reg_write(wm, WM8776_REG_DAC_MUTE, 1);
+	} else {
+		/* update registers from stored values */
+
+		/* restore attenuation levels, with update bit (0x100) set */
+		for (reg = 0; reg < 6; reg++)
+			snd_wm8776_reg_mask(wm, reg, 0xff, 0x100);
+
+		/* restore all other registers except reset register (0x17) */
+		for (reg = 6; reg < 23; reg++)
+			snd_wm8776_reg_mask(wm, reg, 0x1ff, 0);
+	}
+}
+EXPORT_SYMBOL(snd_wm8776_reset);
+
+/*
+ * initialize a wm8776 chip
+ */
+void snd_wm8776_init(struct snd_wm8776 *wm, struct snd_card *card,
+				void *private_data, int chip,
+				const struct snd_wm8776_ops *ops)
+{
+	static const unsigned short inits_wm8776[] = {
+		0x02, 0x100, /* R2: headphone L+R muted + update */
+		0x05, 0x100, /* R5: DAC output L+R muted + update */
+		0x06, 0x000, /* R6: DAC output phase normal */
+		0x07, 0x091, /* R7: DAC enable zero cross detection,
+				normal output */
+		0x08, 0x000, /* R8: DAC soft mute off */
+		0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */
+		0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */
+		0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit,
+				highpass filter enabled */
+		0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */
+		0x0d, 0x000, /* R13: all power up */
+		0x0e, 0x100, /* R14: ADC left muted,
+				enable zero cross detection */
+		0x0f, 0x100, /* R15: ADC right muted,
+				enable zero cross detection */
+			     /* R16: ALC...*/
+		0x11, 0x000, /* R17: disable ALC */
+			     /* R18: ALC...*/
+			     /* R19: noise gate...*/
+		0x15, 0x000, /* R21: ADC input mux init, mute all inputs */
+		0x16, 0x001, /* R22: output mux, select DAC */
+		0xff, 0xff
+	};
+
+	const unsigned short *ptr;
+	unsigned char reg;
+	unsigned short data;
+
+	mutex_init(&wm->wm_mutex);
+	wm->card = card;
+	wm->chip = chip;
+	wm->private_data = private_data;
+	memcpy(&wm->ops, ops, sizeof(wm->ops));
+
+	memset(wm->regs, 0, sizeof(wm->regs));
+	memset(wm->volumes, 0, sizeof(wm->volumes));
+
+	/* enable DAC output; mute bypass, aux & all inputs */
+	wm->switches = (1<<WM8776_SW_OUT_DAC);
+
+	ptr = inits_wm8776;
+	while (*ptr != 0xff) {
+		reg = *ptr++;
+		data = *ptr++;
+		snd_wm8776_reg_write(wm, reg, data);
+	}
+	printk(KERN_DEBUG "wm8776: init done, wm=%p, priv=%p, chip %i\n",
+				wm, wm->private_data, chip);
+}
+EXPORT_SYMBOL(snd_wm8776_init);
+
+
+static int __init alsa_wm8776_module_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_wm8776_module_exit(void)
+{
+}
+
+
+module_init(alsa_wm8776_module_init)
+module_exit(alsa_wm8776_module_exit)
+
+
diff -r 0d5f43585ca7 include/wm8776.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/wm8776.h	Fri Mar 28 21:47:56 2008 +0100
@@ -0,0 +1,149 @@
+#ifndef __SOUND_WM8776_H
+#define __SOUND_WM8776_H
+
+/*
+ *   Low-level routines for Wolfson WM8776 codec
+ *
+ *	Copyright (c) 2007 Rainer Zimmermann <mail@lightshed.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+struct snd_wm8776;
+
+/* register indexes */
+#define WM8776_REG_HEADPHONE_L 0x00
+#define WM8776_REG_HEADPHONE_R 0x01
+#define WM8776_REG_HEADPHONE_MASTER 0x02
+/* digital attenuation */
+#define WM8776_REG_DAC_ATTEN_L 0x03
+#define WM8776_REG_DAC_ATTEN_R 0x04
+#define WM8776_REG_DAC_ATTEN_MASTER 0x05
+#define WM8776_REG_DAC_PHASE 0x06
+#define WM8776_REG_DAC_CONTROL 0x07
+#define WM8776_REG_DAC_MUTE 0x08
+#define WM8776_REG_DAC_DEEMPH 0x09
+#define WM8776_REG_DAC_IF_CONTROL 0x0a
+#define WM8776_REG_ADC_IF_CONTROL 0x0b
+#define WM8776_REG_MASTER_MODE_CONTROL 0x0c
+#define WM8776_REG_POWERDOWN 0x0d
+/* analogue input pga + digital attenuation */
+#define WM8776_REG_ADC_ATTEN_L 0x0e
+#define WM8776_REG_ADC_ATTEN_R 0x0f
+#define WM8776_REG_ADC_ALC1 0x10
+#define WM8776_REG_ADC_ALC2 0x11
+#define WM8776_REG_ADC_ALC3 0x12
+#define WM8776_REG_ADC_NOISE_GATE 0x13
+#define WM8776_REG_ADC_LIMITER 0x14
+#define WM8776_REG_ADC_MUX 0x15
+#define WM8776_REG_OUTPUT_MUX 0x16
+#define WM8776_REG_RESET 0x17
+
+#define WM8776_NUM_REGS 0x18
+
+
+/* clock ratio identifiers for snd_wm8776_set_rate() */
+#define WM8776_CLOCK_RATIO_128FS 0
+#define WM8776_CLOCK_RATIO_192FS 1
+#define WM8776_CLOCK_RATIO_256FS 2
+#define WM8776_CLOCK_RATIO_384FS 3
+#define WM8776_CLOCK_RATIO_512FS 4
+#define WM8776_CLOCK_RATIO_768FS 5
+
+
+enum {
+	WM8776_VOL_HEADPHONE_L = 0,
+	WM8776_VOL_HEADPHONE_R,
+	WM8776_VOL_DAC_L,
+	WM8776_VOL_DAC_R,
+	WM8776_VOL_ADC_GAIN_L,
+	WM8776_VOL_ADC_GAIN_R,
+
+	WM8776_NUM_VOLUMES
+};
+
+enum {
+	WM8776_SW_OUT_DAC = 0,
+	WM8776_SW_OUT_AUX,
+	WM8776_SW_OUT_BYPASS,
+	WM8776_SW_IN_1,
+	WM8776_SW_IN_2,
+	WM8776_SW_IN_3,
+	WM8776_SW_IN_4,
+	WM8776_SW_IN_5,
+
+	WM8776_NUM_SWITCHES
+};
+
+
+struct snd_wm8776_ops {
+	/* currently only 1 op... */
+	void (*write)(void *private_data, int chip, unsigned char reg,
+		      unsigned short val);
+};
+
+struct snd_wm8776 {
+	struct snd_card *card;
+	unsigned char chip;
+	void *private_data;
+	struct snd_wm8776_ops ops;
+	unsigned short regs[WM8776_NUM_REGS];
+	unsigned char volumes[WM8776_NUM_VOLUMES];
+	unsigned int switches:WM8776_NUM_SWITCHES; /* bit field */
+	struct mutex wm_mutex; /* mutex for WM8776 register access */
+};
+
+struct snd_wm8776_volume_info {
+	/* number of levels (not counting mute) */
+	unsigned char nlvl;
+	/* declaration of dB scale (using DECLARE_TLV_DB_SCALE) */
+	const unsigned int *tlv_scale;
+
+	/*private data*/
+	unsigned char reg_idx;
+	unsigned short reg_mask;
+	unsigned short reg_low;   /* register value for lowest level */
+	unsigned short reg_mute;  /* register value for mute */
+};
+
+struct snd_wm8776_switch_info {
+	unsigned char n_states;
+
+	/*private data*/
+	unsigned char reg_idx;
+	unsigned char reg_inv;
+	unsigned short reg_mask;
+};
+
+void snd_wm8776_reset(struct snd_wm8776 *wm, int state);
+void snd_wm8776_init(struct snd_wm8776 *wm, struct snd_card *card,
+			void *private_data, int chip,
+			const struct snd_wm8776_ops *ops);
+
+void snd_wm8776_reg_write(struct snd_wm8776 *wm, unsigned char reg,
+			unsigned short val);
+#define snd_wm8776_reg_get(wm, reg) ((wm)->regs[(reg)])
+
+int snd_wm8776_set_volume(struct snd_wm8776 *wm, int index, int value);
+int snd_wm8776_get_volume(struct snd_wm8776 *wm, int index);
+const struct snd_wm8776_volume_info *snd_wm8776_get_volume_info(int index);
+int snd_wm8776_set_switch(struct snd_wm8776 *wm, int index, int value);
+int snd_wm8776_get_switch(struct snd_wm8776 *wm, int index);
+const struct snd_wm8776_switch_info *snd_wm8776_get_switch_info(int index);
+void snd_wm8776_set_rate(struct snd_wm8776 *wm, unsigned char clock_ratio);
+
+
+#endif /* __SOUND_WM8776_H */
diff -r 0d5f43585ca7 pci/ice1712/Makefile
--- a/pci/ice1712/Makefile	Sat Mar 22 10:26:05 2008 +0100
+++ b/pci/ice1712/Makefile	Fri Mar 28 21:47:56 2008 +0100
@@ -5,7 +5,7 @@
 
 snd-ice17xx-ak4xxx-objs := ak4xxx.o
 snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff -r 0d5f43585ca7 pci/ice1712/ice1712.h
--- a/pci/ice1712/ice1712.h	Sat Mar 22 10:26:05 2008 +0100
+++ b/pci/ice1712/ice1712.h	Fri Mar 28 21:47:56 2008 +0100
@@ -452,10 +452,17 @@ static inline int snd_ice1712_gpio_read_
 	return  (snd_ice1712_gpio_read(ice) & mask);
 }
 
+/* route access functions */
+int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift);
+int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
+								int shift);
+
 int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
 
-int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template,
-			     const struct snd_ak4xxx_private *priv, struct snd_ice1712 *ice);
+int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak,
+			     const struct snd_akm4xxx *template,
+			     const struct snd_ak4xxx_private *priv,
+			     struct snd_ice1712 *ice);
 void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice);
 int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);
 
diff -r 0d5f43585ca7 pci/ice1712/ice1724.c
--- a/pci/ice1712/ice1724.c	Sat Mar 22 10:26:05 2008 +0100
+++ b/pci/ice1712/ice1724.c	Fri Mar 28 21:47:56 2008 +0100
@@ -49,6 +49,7 @@
 #include "prodigy192.h"
 #include "prodigy_hifi.h"
 #include "juli.h"
+#include "maya44.h"
 #include "phase.h"
 #include "wtm.h"
 #include "se.h"
@@ -65,6 +66,7 @@ MODULE_SUPPORTED_DEVICE("{"
 	       PRODIGY192_DEVICE_DESC
 	       PRODIGY_HIFI_DEVICE_DESC
 	       JULI_DEVICE_DESC
+	       MAYA44_DEVICE_DESC
 	       PHASE_DEVICE_DESC
 	       WTM_DEVICE_DESC
 	       SE_DEVICE_DESC
@@ -439,7 +441,7 @@ static unsigned char stdclock_set_mclk(s
 	return 0;
 }
 
-static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
+static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
 				    int force)
 {
 	unsigned long flags;
@@ -447,17 +449,18 @@ static void snd_vt1724_set_pro_rate(stru
 	unsigned int i, old_rate;
 
 	if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
-		return;
+		return -EINVAL;
+
 	spin_lock_irqsave(&ice->reg_lock, flags);
 	if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
 	    (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
 		/* running? we cannot change the rate now... */
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return;
+		return -EBUSY;
 	}
 	if (!force && is_pro_rate_locked(ice)) {
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return;
+		return (rate == ice->cur_rate) ? 0 : -EBUSY;
 	}
 
 	old_rate = ice->get_rate(ice);
@@ -465,7 +468,7 @@ static void snd_vt1724_set_pro_rate(stru
 		ice->set_rate(ice, rate);
 	else if (rate == ice->cur_rate) {
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return;
+		return 0;
 	}
 
 	ice->cur_rate = rate;
@@ -487,13 +490,15 @@ static void snd_vt1724_set_pro_rate(stru
 	}
 	if (ice->spdif.ops.setup_rate)
 		ice->spdif.ops.setup_rate(ice, rate);
+
+	return 0;
 }
 
 static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
 				    struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
-	int i, chs;
+	int i, chs, err;
 
 	chs = params_channels(hw_params);
 	mutex_lock(&ice->open_mutex);
@@ -528,7 +533,11 @@ static int snd_vt1724_pcm_hw_params(stru
 		}
 	}
 	mutex_unlock(&ice->open_mutex);
-	snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+
+	err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+	if (err < 0)
+		return err;
+
 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
@@ -654,19 +663,39 @@ static snd_pcm_uframes_t snd_vt1724_pcm_
 #endif
 }
 
-static const struct vt1724_pcm_reg vt1724_playback_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_pdma0_reg = {
 	.addr = VT1724_MT_PLAYBACK_ADDR,
 	.size = VT1724_MT_PLAYBACK_SIZE,
 	.count = VT1724_MT_PLAYBACK_COUNT,
 	.start = VT1724_PDMA0_START,
 };
 
-static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_pdma4_reg = {
+	.addr = VT1724_MT_PDMA4_ADDR,
+	.size = VT1724_MT_PDMA4_SIZE,
+	.count = VT1724_MT_PDMA4_COUNT,
+	.start = VT1724_PDMA4_START,
+};
+
+static const struct vt1724_pcm_reg vt1724_rdma0_reg = {
 	.addr = VT1724_MT_CAPTURE_ADDR,
 	.size = VT1724_MT_CAPTURE_SIZE,
 	.count = VT1724_MT_CAPTURE_COUNT,
 	.start = VT1724_RDMA0_START,
 };
+
+static const struct vt1724_pcm_reg vt1724_rdma1_reg = {
+	.addr = VT1724_MT_RDMA1_ADDR,
+	.size = VT1724_MT_RDMA1_SIZE,
+	.count = VT1724_MT_RDMA1_COUNT,
+	.start = VT1724_RDMA1_START,
+};
+
+#define vt1724_playback_pro_reg vt1724_pdma0_reg
+#define vt1724_playback_spdif_reg vt1724_pdma4_reg
+#define vt1724_capture_pro_reg vt1724_rdma0_reg
+#define vt1724_capture_spdif_reg vt1724_rdma1_reg
+
 
 static const struct snd_pcm_hardware snd_vt1724_playback_pro =
 {
@@ -801,10 +830,12 @@ static int snd_vt1724_capture_pro_open(s
 
 	runtime->private_data = (void *)&vt1724_capture_pro_reg;
 	ice->capture_pro_substream = substream;
+
 	runtime->hw = snd_vt1724_2ch_stereo;
 	snd_pcm_set_sync(substream);
 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 	set_rate_constraints(ice, substream);
+
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 				   VT1724_BUFFER_ALIGN);
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
@@ -830,6 +861,7 @@ static int snd_vt1724_capture_pro_close(
 	if (PRO_RATE_RESET)
 		snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
 	ice->capture_pro_substream = NULL;
+
 	return 0;
 }
 
@@ -884,20 +916,6 @@ static int __devinit snd_vt1724_pcm_prof
 /*
  * SPDIF PCM
  */
-
-static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
-	.addr = VT1724_MT_PDMA4_ADDR,
-	.size = VT1724_MT_PDMA4_SIZE,
-	.count = VT1724_MT_PDMA4_COUNT,
-	.start = VT1724_PDMA4_START,
-};
-
-static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
-	.addr = VT1724_MT_RDMA1_ADDR,
-	.size = VT1724_MT_RDMA1_SIZE,
-	.count = VT1724_MT_RDMA1_COUNT,
-	.start = VT1724_RDMA1_START,
-};
 
 /* update spdif control bits; call with reg_lock */
 static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
@@ -1065,7 +1083,7 @@ static int __devinit snd_vt1724_pcm_spdi
 	if (ice->force_pdma4 || ice->force_rdma1)
 		name = "ICE1724 Secondary";
 	else
-		name = "IEC1724 IEC958";
+		name = "ICE1724 IEC958";
 	err = snd_pcm_new(ice->card, name, device, play, capt, &pcm);
 	if (err < 0)
 		return err;
@@ -1769,7 +1787,7 @@ static inline int digital_route_shift(in
 	return idx * 3;
 }
 
-static int get_route_val(struct snd_ice1712 *ice, int shift)
+int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift)
 {
 	unsigned long val;
 	unsigned char eitem;
@@ -1788,7 +1806,8 @@ static int get_route_val(struct snd_ice1
 	return eitem;
 }
 
-static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift)
+int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
+								int shift)
 {
 	unsigned int old_val, nval;
 	int change;
@@ -1816,7 +1835,7 @@ static int snd_vt1724_pro_route_analog_g
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	ucontrol->value.enumerated.item[0] =
-		get_route_val(ice, analog_route_shift(idx));
+		snd_ice1724_get_route_val(ice, analog_route_shift(idx));
 	return 0;
 }
 
@@ -1825,8 +1844,9 @@ static int snd_vt1724_pro_route_analog_p
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	return put_route_val(ice, ucontrol->value.enumerated.item[0],
-			     analog_route_shift(idx));
+	return snd_ice1724_put_route_val(ice,
+					 ucontrol->value.enumerated.item[0],
+					 analog_route_shift(idx));
 }
 
 static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
@@ -1835,7 +1855,7 @@ static int snd_vt1724_pro_route_spdif_ge
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	ucontrol->value.enumerated.item[0] =
-		get_route_val(ice, digital_route_shift(idx));
+		snd_ice1724_get_route_val(ice, digital_route_shift(idx));
 	return 0;
 }
 
@@ -1844,11 +1864,13 @@ static int snd_vt1724_pro_route_spdif_pu
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	return put_route_val(ice, ucontrol->value.enumerated.item[0],
-			     digital_route_shift(idx));
-}
-
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
+	return snd_ice1724_put_route_val(ice,
+					 ucontrol->value.enumerated.item[0],
+					 digital_route_shift(idx));
+}
+
+static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
+{
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "H/W Playback Route",
 	.info = snd_vt1724_pro_route_info,
@@ -1915,6 +1937,7 @@ static struct snd_ice1712_card_info *car
 	snd_vt1724_prodigy_hifi_cards,
 	snd_vt1724_prodigy192_cards,
 	snd_vt1724_juli_cards,
+	snd_vt1724_maya44_cards,
 	snd_vt1724_phase_cards,
 	snd_vt1724_wtm_cards,
 	snd_vt1724_se_cards,
@@ -2048,9 +2071,12 @@ static int __devinit snd_vt1724_chip_ini
 static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
 {
 	outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
+	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
 	udelay(200);
 	outb(0, ICEREG1724(ice, CONTROL));
+	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
 	udelay(200);
+
 	outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
 	outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
 	outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES));
@@ -2074,9 +2100,12 @@ static int __devinit snd_vt1724_spdif_bu
 
 	snd_assert(ice->pcm != NULL, return -EIO);
 
-	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
-	if (err < 0)
-		return err;
+	if (ice->eeprom.subvendor != VT1724_SUBDEVICE_MAYA44) {
+		err = snd_ctl_add(ice->card,
+			snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
+		if (err < 0)
+			return err;
+	}
 
 	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
 	if (err < 0)
@@ -2123,7 +2152,12 @@ static int __devinit snd_vt1724_build_co
 	if (err < 0)
 		return err;
 
-	if (ice->num_total_dacs > 0) {
+	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_MAYA44) {
+		err = snd_ctl_add(ice->card,
+			snd_ctl_new1(&snd_vt1724_mixer_pro_maya44_route, ice));
+		if (err < 0)
+			return err;
+	} else if (ice->num_total_dacs > 0) {
 		struct snd_kcontrol_new tmp = snd_vt1724_mixer_pro_analog_route;
 		tmp.count = ice->num_total_dacs;
 		if (ice->vt1720 && tmp.count > 2)
@@ -2390,6 +2424,7 @@ static int __devinit snd_vt1724_probe(st
 	}
 	pci_set_drvdata(pci, card);
 	dev++;
+
 	return 0;
 }
 
diff -r 0d5f43585ca7 pci/ice1712/maya44.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pci/ice1712/maya44.c	Fri Mar 28 21:47:56 2008 +0100
@@ -0,0 +1,761 @@
+#include "adriver.h"
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for ESI Maya44 cards
+ *
+ *	Copyright (c) 2007 Rainer Zimmermann <mail@lightshed.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/wm8776.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "maya44.h"
+
+/*
+ * If defined, allow separate lists of allowable rates for ADC/DAC.
+ * Actually, ADC & DAC rates are linked, so setting invalid
+ * ADC rates (>96 kHz) cannot be prevented in all cases.
+ * NOTE: this requires changes in ice1712.h (see ice1712.h.patch)
+ * and enabling the corresponding code in ice1724.c
+ */
+#undef SEPARATE_ADC_RATES
+
+struct maya44_spec {
+	struct snd_wm8776 *wm[2];
+	unsigned int miodio:1;
+};
+
+/*
+ * chip addresses on I2C bus
+ */
+#define WM8776_0_ADDR		0x34		/* Codec 0 */
+#define WM8776_1_ADDR		0x36		/* Codec 1 */
+
+/*
+ * GPIO pins (known ones for maya44)
+ */
+#define GPIO_PHANTOM_OFF	(1<<2)
+#define GPIO_MIC_RELAY		(1<<4)
+#define GPIO_SPDIF_IN_INV	(1<<5)  /* CHECK! */
+#define GPIO_MUST_BE_0		(1<<7)
+
+
+/*
+ * WM8776 codecs section
+ */
+
+static void maya44_wm_reg_write(void *private_data, int chip,
+			   unsigned char addr, unsigned short data)
+{
+	struct snd_ice1712 *ice = (struct snd_ice1712 *)private_data;
+
+#if 0
+	snd_printd(KERN_DEBUG
+		   "maya44: maya44_wm_reg_write, ice=%p, chip=%i, addr=%i, "
+		   "data=%i\n",
+		   ice, chip, (int)addr, (int)data);
+#endif
+
+	snd_assert(chip == 0 || chip == 1, return);
+
+/*
+ * WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB
+ * of the address field
+ */
+	snd_vt1724_write_i2c(ice,
+			     chip ? WM8776_1_ADDR : WM8776_0_ADDR,
+			     (addr<<1) | ((data>>8) & 1),
+			     data&0xff
+			     );
+}
+
+/*
+ * change the rate on the WM8776 codecs.
+ * this assumes that the VT17xx's rate is changed by the calling function.
+ * NOTE: even though the WM8776's are running in slave mode and rate
+ * selection is automatic, we need to call snd_wm8776_set_rate() here
+ * to make sure some flags are set correctly.
+ */
+static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+	struct maya44_spec *maya44_spec = ice->spec;
+	int ratio, i;
+
+	switch (rate) {
+	case 192000:
+		ratio = WM8776_CLOCK_RATIO_128FS;
+		break;
+	case 176400:
+		ratio = WM8776_CLOCK_RATIO_128FS;
+		break;
+	case 96000:
+		ratio = WM8776_CLOCK_RATIO_256FS;
+		break;
+	case 88200:
+		ratio = WM8776_CLOCK_RATIO_384FS;
+		break;
+	case 48000:
+		ratio = WM8776_CLOCK_RATIO_512FS;
+		break;
+	case 44100:
+		ratio = WM8776_CLOCK_RATIO_512FS;
+		break;
+	case 32000:
+		ratio = WM8776_CLOCK_RATIO_768FS;
+		break;
+	case 0:
+		/* no hint - S/PDIF input is master, simply return */
+		return;
+	default:
+		snd_BUG();
+		return;
+	}
+
+	snd_printd(KERN_DEBUG "maya44: set rate %i, ratio=%d\n", rate, ratio);
+
+	for (i = 0; i < 2; i++)
+		snd_wm8776_set_rate(maya44_spec->wm[i], ratio);
+}
+
+static int maya44_wm_dev_free(struct snd_device *device)
+{
+	snd_printd(KERN_DEBUG
+		   "maya44: maya44_wm_dev_free, dev=%p, device_data=%p\n",
+		   device, device->device_data);
+	kfree(device->device_data);
+	return 0;
+}
+
+static struct wm_vol_control {
+	char *name;
+	int index[2];
+} wm_vol_controls[] =
+{
+	{ .name = "Crossmix Playback Volume",
+	  .index = { WM8776_VOL_HEADPHONE_L, WM8776_VOL_HEADPHONE_R } },
+	{ .name = "PCM Playback Volume",
+	  .index = { WM8776_VOL_DAC_L, WM8776_VOL_DAC_R } },
+	{ .name = "Line Capture Volume",
+	  .index = { WM8776_VOL_ADC_GAIN_L, WM8776_VOL_ADC_GAIN_R } },
+	{} /* terminator */
+};
+
+/* definition of "1 per codec chip" switches */
+
+static struct wm_switch_control {
+	char *name;
+	int index;
+} wm_switch_controls[] =
+{
+	{ .name = "PCM Playback Switch",	.index = WM8776_SW_OUT_DAC },
+/* Aux is not used & set to OFF in wm8776.c by default */
+/*	{ .name = "Aux Playback Switch",	.index = WM8776_SW_OUT_AUX },*/
+	{ .name = "Bypass Playback Switch", 	.index = WM8776_SW_OUT_BYPASS },
+#ifdef _TESTMODE_
+	{ .name = "Line Capture Switch 1",  	.index = WM8776_SW_IN_1 },
+	{ .name = "Line Capture Switch 2",  	.index = WM8776_SW_IN_2 },
+	{ .name = "Line Capture Switch 3",  	.index = WM8776_SW_IN_3	},
+	{ .name = "Line Capture Switch 4",  	.index = WM8776_SW_IN_4	},
+	{ .name = "Line Capture Switch 5",  	.index = WM8776_SW_IN_5	},
+#endif
+	{} /* terminator */
+};
+
+#define WM_MAKE_PRIV(chip, idx) (((chip)<<8)|(idx))
+#define WM_GET_CHIP(priv) ((priv)>>8)
+#define WM_GET_IDX(priv) ((priv)&0xff)
+
+#define TLV_HAS_MUTE(tlv) (((tlv)[3]&0x10000) != 0)
+
+static int wm_vol_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	const struct snd_wm8776_volume_info *vi =
+		snd_wm8776_get_volume_info(WM_GET_IDX(kcontrol->private_value));
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: wm_vol_info, prv=%i\n",
+		   (int)kcontrol->private_value);
+#endif
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2; /* 2 channels per codec */
+	uinfo->value.integer.min = TLV_HAS_MUTE(vi->tlv_scale)?0:1;
+	uinfo->value.integer.max = vi->nlvl;
+	return 0;
+}
+
+static int wm_vol_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	struct maya44_spec *maya44_spec = ice->spec;
+	struct snd_wm8776 *wm;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: wm_vol_get, ice=%p, prv=%i\n",
+		   ice, (int)kcontrol->private_value);
+#endif
+
+	/* get L+R volumes from one WM8776 codec */
+	wm = maya44_spec->wm[WM_GET_CHIP(kcontrol->private_value)];
+	/* left channel */
+	ucontrol->value.integer.value[0] =
+		snd_wm8776_get_volume(wm,
+				      WM_GET_IDX(kcontrol->private_value));
+	/* right channel */
+	ucontrol->value.integer.value[1] =
+		snd_wm8776_get_volume(wm,
+				      WM_GET_IDX(kcontrol->private_value)+1);
+
+	return 0;
+}
+
+static int wm_vol_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	struct maya44_spec *maya44_spec = ice->spec;
+	struct snd_wm8776 *wm;
+	int changed = 0;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: wm_vol_put, ice=%p, prv=%i, val=%i,%i\n",
+		   ice, (int)kcontrol->private_value,
+		   (int)ucontrol->value.integer.value[0],
+		   (int)ucontrol->value.integer.value[1]);
+#endif
+
+	/* put L+R volumes for one WM8776 codec */
+	wm = maya44_spec->wm[WM_GET_CHIP(kcontrol->private_value)];
+	/* left channel */
+	changed = snd_wm8776_set_volume(wm,
+					WM_GET_IDX(kcontrol->private_value),
+					ucontrol->value.integer.value[0]);
+	/* right channel */
+	changed |= snd_wm8776_set_volume(wm,
+					 WM_GET_IDX(kcontrol->private_value)+1,
+					 ucontrol->value.integer.value[1]);
+
+	return changed;
+}
+
+#if 0
+static int wm_switch_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2; /* switches are always one per codec */
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+#endif
+
+static int wm_switch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	struct maya44_spec *maya44_spec = ice->spec;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: wm_switch_get, ice=%p, prv=%i\n",
+		   ice, (int)kcontrol->private_value);
+#endif
+
+	/* get switch from one WM8776 codec */
+	ucontrol->value.integer.value[0] =
+		snd_wm8776_get_switch(
+			maya44_spec->wm[WM_GET_CHIP(kcontrol->private_value)],
+			WM_GET_IDX(kcontrol->private_value));
+
+	return 0;
+}
+
+static int wm_switch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	struct maya44_spec *maya44_spec = ice->spec;
+	int changed = 0;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: wm_switch_put, ice=%p, prv=%i\n",
+		   ice, (int)kcontrol->private_value);
+#endif
+
+	/* put switch for one WM8776 codec */
+	changed = snd_wm8776_set_switch(
+			maya44_spec->wm[WM_GET_CHIP(kcontrol->private_value)],
+			WM_GET_IDX(kcontrol->private_value),
+			ucontrol->value.integer.value[0]);
+
+	return changed;
+}
+
+#ifdef _TESTMODE_
+
+static int gpio_switch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int data, mask = 1<<kcontrol->private_value;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: gpio_switch_get, ice=%p, prv=%i\n",
+		   ice, (int)kcontrol->private_value);
+#endif
+
+	data = ice->gpio.get_data(ice);
+	ucontrol->value.integer.value[0] = ((data & mask) != 0);
+	return 0;
+}
+
+static int gpio_switch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int data, mask = 1<<kcontrol->private_value;
+	unsigned int bit = ucontrol->value.integer.value[0]?mask:0;
+	int changed = 0;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: gpio_switch_put, ice=%p, prv=%i\n",
+		   ice, (int)kcontrol->private_value);
+#endif
+
+	mutex_lock(&ice->gpio_mutex);
+	data = ice->gpio.get_data(ice);
+	if ((data&mask) != bit) {
+		ice->gpio.set_data(ice, (data & ~mask)|bit);
+		changed = 1;
+	}
+	mutex_unlock(&ice->gpio_mutex);
+	return changed;
+}
+
+#endif /* _TESTMODE_ */
+
+enum { SW_MIC = 0, SW_PHANTOM, SW_SPDIF_IN };
+
+static const char *other_switch_names[] = {
+	[SW_MIC] = "Mic/Line Input Switch",
+	[SW_PHANTOM] = "Mic Phantom+48V Switch",
+/*
+ * the next one should probably be called "IEC958 Capture Switch", but
+ * then alsamixer wouldn't handle it properly.
+ */
+	[SW_SPDIF_IN] = "S/PDIF Capture Switch",
+	NULL /* terminator */
+};
+
+static unsigned char sw_other[] = {
+	[SW_MIC] = 0,
+	[SW_PHANTOM] = 0,
+	[SW_SPDIF_IN] = 0
+};
+
+static int set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask,
+			 unsigned int bits)
+{
+	unsigned int data;
+	unsigned char changed = 0;
+	mutex_lock(&ice->gpio_mutex);
+	data = ice->gpio.get_data(ice);
+	if ((data&mask) != bits) {
+		ice->gpio.set_data(ice, (data & ~mask)|bits);
+		changed = 1;
+	}
+	mutex_unlock(&ice->gpio_mutex);
+	return changed;
+}
+
+static int other_switch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+#if 0
+	snd_printd(KERN_DEBUG "maya44: other_switch_get, prv=%i\n",
+		   (int)kcontrol->private_value);
+#endif
+
+	ucontrol->value.integer.value[0] =
+				sw_other[(int)kcontrol->private_value];
+	return 0;
+}
+
+static int other_switch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	struct maya44_spec *maya44_spec = ice->spec;
+	int j;
+	int set = ucontrol->value.integer.value[0];
+	int idx = kcontrol->private_value;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: other_switch_put, ice=%p, prv=%i\n",
+		   ice, (int)kcontrol->private_value);
+#endif
+
+	if (set == sw_other[idx])
+		return 0; /* not changed */
+
+	switch (kcontrol->private_value) {
+	case SW_PHANTOM:
+		set_gpio_bits(ice, GPIO_PHANTOM_OFF,
+					set ? 0 : GPIO_PHANTOM_OFF);
+		break;
+	case SW_MIC:
+		set_gpio_bits(ice, GPIO_MIC_RELAY,
+					set ? GPIO_MIC_RELAY : 0);
+		/* line input = IN2, mic input = IN4 */
+		for (j = WM8776_SW_IN_1; j <= WM8776_SW_IN_5; j++)
+			snd_wm8776_set_switch(
+				maya44_spec->wm[0],
+				j,
+				(j == (set?WM8776_SW_IN_4:WM8776_SW_IN_2)));
+		break;
+	case SW_SPDIF_IN:
+		set_gpio_bits(ice, GPIO_SPDIF_IN_INV,
+						set ? 0 : GPIO_SPDIF_IN_INV);
+		break;
+	default:
+		snd_BUG();
+		return 0;
+	}
+
+	sw_other[idx] = set;
+	return 1;
+}
+
+
+/*
+ * Maya44 routing switch settings have different meanings than the standard
+ * ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c).
+ */
+int snd_vt1724_pro_route_maya44_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"PCM Out", /* 0 */
+		"Input 1", "Input 2", "Input 3", "Input 4"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item =
+			uinfo->value.enumerated.items - 1;
+
+	strcpy(uinfo->value.enumerated.name,
+					texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+int maya44_route_shift(int idx)
+{
+	static const unsigned char shift[10] =
+				{ 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 };
+	return shift[idx%10];
+}
+
+static int snd_vt1724_pro_route_maya44_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	ucontrol->value.enumerated.item[0] =
+		snd_ice1724_get_route_val(ice, maya44_route_shift(idx));
+	return 0;
+}
+
+static int snd_vt1724_pro_route_maya44_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	return snd_ice1724_put_route_val(ice,
+					 ucontrol->value.enumerated.item[0],
+					 maya44_route_shift(idx));
+}
+
+
+struct snd_kcontrol_new snd_vt1724_mixer_pro_maya44_route __devinitdata =
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "H/W Playback Route",
+	.info = snd_vt1724_pro_route_maya44_info,
+	.get = snd_vt1724_pro_route_maya44_get,
+	.put = snd_vt1724_pro_route_maya44_put,
+#if 1
+	.count = 10  /* XXX check whether controls 5-9 have any meaning */
+#else
+	.count = 4
+#endif
+};
+
+static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
+{
+	int err, i;
+	struct snd_kcontrol_new newc;
+	struct wm_vol_control *pv;
+	struct wm_switch_control *ps;
+
+#if 0
+	snd_printd(KERN_DEBUG "maya44: maya44_add_controls, ice=%p\n", ice);
+#endif
+
+	/* add volume controls for both WM8776 codecs */
+
+	memset(&newc, 0, sizeof(newc));
+	newc.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	newc.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+					SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+	newc.info = wm_vol_info;
+	newc.get = wm_vol_get;
+	newc.put = wm_vol_put;
+	newc.count = 1;
+
+	for (pv = wm_vol_controls; pv->name != NULL; pv++) {
+		newc.name = pv->name;
+		newc.tlv.p =
+			snd_wm8776_get_volume_info(pv->index[0])->tlv_scale;
+
+		for (newc.index = 0; newc.index < 2; newc.index++) {
+			newc.private_value =
+				WM_MAKE_PRIV(newc.index, pv->index[0]);
+#if 0
+			snd_printd(KERN_DEBUG
+				"maya44: add control '%s' idx %i, pval=%i\n",
+				pv->name, (int)newc.index,
+				(int)newc.private_value);
+#endif
+
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&newc, ice));
+#if 0
+			snd_printd(KERN_DEBUG "maya44: err=%i\n", err);
+#endif
+			if (err < 0)
+				return err;
+		}
+	}
+
+	/* add switch controls for both WM8776 codecs */
+
+	memset(&newc, 0, sizeof(newc));
+	newc.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	newc.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	newc.info = /*wm_switch_info*/ snd_ctl_boolean_mono_info;
+	newc.get = wm_switch_get;
+	newc.put = wm_switch_put;
+	newc.count = 1;
+
+	for (ps = wm_switch_controls; ps->name != NULL; ps++) {
+		newc.name = ps->name;
+		for (newc.index = 0; newc.index < 2; newc.index++) {
+			newc.private_value =
+					WM_MAKE_PRIV(newc.index, ps->index);
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&newc, ice));
+			if (err < 0)
+				return err;
+		}
+	}
+
+#ifdef _TESTMODE_
+	/* add GPIO controls */
+
+	memset(&newc, 0, sizeof(newc));
+	newc.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	newc.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	newc.info = snd_ctl_boolean_mono_info; /* simple switch */
+	newc.get = gpio_switch_get;
+	newc.put = gpio_switch_put;
+	newc.count = 1;
+
+	{
+		char gpname[40];
+		int i;
+		for (i = 0; i < 24; i++) {
+			sprintf(gpname, "GPIO bit %02i", i);
+			newc.name = gpname;
+			newc.private_value = i;
+
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&newc, ice));
+			if (err < 0)
+				return err;
+		}
+	}
+#endif
+
+	/* add other controls */
+
+	memset(&newc, 0, sizeof(newc));
+	newc.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	newc.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	newc.info = snd_ctl_boolean_mono_info; /* simple switch */
+	newc.get = other_switch_get;
+	newc.put = other_switch_put;
+	newc.count = 1;
+
+	for (i = 0; (newc.name = (char *)other_switch_names[i]) != NULL; i++) {
+		newc.private_value = i;
+#if 0
+		snd_printd(KERN_DEBUG "maya44: add control '%s', i=%i\n",
+			   newc.name, i);
+#endif
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&newc, ice));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ * initialize the chip
+ */
+static int __devinit maya44_init(struct snd_ice1712 *ice)
+{
+	int err, i, j;
+	struct maya44_spec *maya44_spec;
+	static unsigned int rates[] = {
+		32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
+	};
+#ifdef SEPARATE_ADC_RATES
+	/* record rates: 32..96 kHz */
+	static struct snd_pcm_hw_constraint_list adc_rates = {
+		.count = ARRAY_SIZE(rates)-2,
+		.list = rates,
+		.mask = 0
+	};
+#endif
+	/* playback rates: 32..192 kHz */
+	static struct snd_pcm_hw_constraint_list dac_rates = {
+		.count = ARRAY_SIZE(rates),
+		.list = rates,
+		.mask = 0
+	};
+
+	static struct snd_wm8776_ops _wm_ops = { .write = maya44_wm_reg_write };
+	static struct snd_device_ops _dev_ops = {
+		.dev_free = maya44_wm_dev_free,
+		/* .dev_register=..., */
+		/* .dev_disconnect=... */
+	};
+
+	snd_printd(KERN_DEBUG "maya44: maya44_init start\n");
+
+	maya44_spec = kzalloc(sizeof(*maya44_spec), GFP_KERNEL);
+	if (!maya44_spec)
+		return -ENOMEM;
+	ice->spec = maya44_spec;
+
+	/* initialise codecs */
+	ice->num_total_dacs = 4;
+	ice->num_total_adcs = 4;
+	ice->akm_codecs = 0;
+
+	for (i = 0; i < 2; i++) {
+		struct snd_wm8776 *wm;
+		wm = maya44_spec->wm[i] =
+			kzalloc(sizeof(struct snd_wm8776), GFP_KERNEL);
+		if (!wm)
+			return -ENOMEM;
+		err = snd_device_new(ice->card, SNDRV_DEV_LOWLEVEL,
+							wm, &_dev_ops);
+		if (err < 0) {
+			kfree(wm);
+			return err;
+		}
+		snd_wm8776_init(wm, ice->card, ice, i, &_wm_ops);
+		snd_printd(KERN_DEBUG "maya44: wm8776_init %i done\n", i);
+
+		/* Maya44 line inputs are seen on IN2 */
+		for (j = WM8776_SW_IN_1; j <= WM8776_SW_IN_5; j++)
+			snd_wm8776_set_switch(wm, j, (j == WM8776_SW_IN_2));
+	}
+
+/*
+ * set card specific rates.
+ */
+#ifdef SEPARATE_ADC_RATES
+	ice->hw_rates = &dac_rates;
+	ice->hw_rates_adc = &adc_rates;
+#else
+	ice->hw_rates = &dac_rates;
+#endif
+
+	/* register change rate notifier */
+	ice->gpio.set_pro_rate = set_rate;
+
+	/* RDMA1 (2nd input channel) is used for ADC by default */
+	ice->force_rdma1 = 1;
+
+	snd_printd(KERN_DEBUG "maya44: maya44_init finished\n");
+
+	return 0;
+}
+
+
+/*
+ * Maya44 boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char maya44_eeprom[] __devinitdata = {
+	[ICE_EEP2_SYSCONF]     = 0x45,
+		/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
+	[ICE_EEP2_ACLINK]      = 0x80,
+		/* I2S */
+	[ICE_EEP2_I2S]         = 0xf8,
+		/* vol, 96k, 24bit, 192k */
+	[ICE_EEP2_SPDIF]       = 0xc3,
+		/* enable spdif out, spdif out supp, spdif-in, ext spdif out */
+	[ICE_EEP2_GPIO_DIR]    = 0xff,
+	[ICE_EEP2_GPIO_DIR1]   = 0xff,
+	[ICE_EEP2_GPIO_DIR2]   = 0xff,
+	[ICE_EEP2_GPIO_MASK]   = 0/*0x9f*/,
+	[ICE_EEP2_GPIO_MASK1]  = 0/*0xff*/,
+	[ICE_EEP2_GPIO_MASK2]  = 0/*0x7f*/,
+	[ICE_EEP2_GPIO_STATE]  = GPIO_PHANTOM_OFF | GPIO_SPDIF_IN_INV,
+	[ICE_EEP2_GPIO_STATE1] = 0x00,
+	[ICE_EEP2_GPIO_STATE2] = 0x00,
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = {
+	{
+		.subvendor = VT1724_SUBDEVICE_MAYA44,
+		.name = "ESI Maya44",
+		.model = "maya44",
+		.chip_init = maya44_init,
+		.build_controls = maya44_add_controls,
+		.eeprom_size = sizeof(maya44_eeprom),
+		.eeprom_data = maya44_eeprom,
+	},
+	{ } /* terminator */
+};
diff -r 0d5f43585ca7 pci/ice1712/maya44.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pci/ice1712/maya44.h	Fri Mar 28 21:47:56 2008 +0100
@@ -0,0 +1,16 @@
+#ifndef __SOUND_MAYA44_H
+#define __SOUND_MAYA44_H
+
+#define MAYA44_DEVICE_DESC		"{ESI,Maya44},"
+
+#define VT1724_SUBDEVICE_MAYA44		0x34315441	/* Maya44 */
+
+extern struct snd_ice1712_card_info  snd_vt1724_maya44_cards[];
+
+int snd_vt1724_pro_route_maya44_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo);
+int maya44_route_shift(int idx);
+
+extern struct snd_kcontrol_new snd_vt1724_mixer_pro_maya44_route;
+
+#endif	/* __SOUND_MAYA44_H */

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

  reply	other threads:[~2008-03-28 21:07 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-03-21 11:27 Patch - ESI Maya44 driver Rainer Zimmermann
2008-03-22  9:24 ` Takashi Iwai
2008-03-28 21:07   ` Rainer Zimmermann [this message]
2008-04-05  8:07     ` Piotr Zaryk
2008-04-14 10:43     ` Takashi Iwai
2008-05-09  7:22       ` Piotr Zaryk
2008-05-09 10:51         ` Takashi Iwai
  -- strict thread matches above, loose matches on Subject: below --
2008-07-19 17:02 midragius
2008-07-21  9:27 ` midragius
2008-07-27  8:01   ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=47ED5E29.4040400@lightshed.de \
    --to=mail@lightshed.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox