Merge branch 'topic/maya44' into for-linus

* topic/maya44:
  ALSA: ice1724 - Add ESI Maya44 support
  ALSA: ice1724 - Allow spec driver to create own routing controls
  ALSA: ice1724 - Add PCI postint to reset sequence
  ALSA: ice1724 - Clean up definitions of DMA records
  ALSA: ice1724 - Check error in set_rate function
This commit is contained in:
Takashi Iwai 2009-06-10 07:26:36 +02:00
commit ed8cc176c9
7 changed files with 1025 additions and 40 deletions

View file

@ -944,6 +944,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
* Onkyo SE-90PCI
* Onkyo SE-200PCI
* ESI Juli@
* ESI Maya44
* Hercules Fortissimo IV
* EGO-SYS WaveTerminal 192M
@ -952,7 +953,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
prodigy71xt, prodigy71hifi, prodigyhd2, prodigy192,
juli, aureon51, aureon71, universe, ap192, k8x800,
phase22, phase28, ms300, av710, se200pci, se90pci,
fortissimo4, sn25p, WT192M
fortissimo4, sn25p, WT192M, maya44
This module supports multiple cards and autoprobe.

View file

@ -0,0 +1,163 @@
NOTE: The following is the original document of Rainer's patch that the
current maya44 code based on. Some contents might be obsoleted, but I
keep here as reference -- tiwai
----------------------------------------------------------------
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

View file

@ -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

View file

@ -335,6 +335,7 @@ struct snd_ice1712 {
unsigned int force_rdma1:1; /* VT1720/4 - RDMA1 as non-spdif */
unsigned int midi_output:1; /* VT1720/4: MIDI output triggered */
unsigned int midi_input:1; /* VT1720/4: MIDI input triggered */
unsigned int own_routing:1; /* VT1720/4: use own routing ctls */
unsigned int num_total_dacs; /* total DACs */
unsigned int num_total_adcs; /* total ADCs */
unsigned int cur_rate; /* current rate */
@ -458,10 +459,17 @@ static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
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);

View file

@ -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
@ -626,7 +628,7 @@ static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
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;
@ -634,17 +636,18 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
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);
@ -652,7 +655,7 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
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;
@ -674,13 +677,15 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
}
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);
@ -715,7 +720,11 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
}
}
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));
}
@ -848,20 +857,39 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
#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 = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@ -1077,20 +1105,6 @@ static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
* 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)
{
@ -1963,7 +1977,7 @@ static inline int digital_route_shift(int idx)
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;
@ -1982,7 +1996,8 @@ static int get_route_val(struct snd_ice1712 *ice, int shift)
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;
@ -2010,7 +2025,7 @@ static int snd_vt1724_pro_route_analog_get(struct snd_kcontrol *kcontrol,
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;
}
@ -2019,8 +2034,9 @@ static int snd_vt1724_pro_route_analog_put(struct snd_kcontrol *kcontrol,
{
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,
@ -2029,7 +2045,7 @@ static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
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;
}
@ -2038,11 +2054,13 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
{
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));
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 = {
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,
@ -2109,6 +2127,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
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,
@ -2246,8 +2265,10 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
{
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
msleep(10);
outb(0, ICEREG1724(ice, CONTROL));
inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
msleep(10);
}
@ -2277,9 +2298,12 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
if (snd_BUG_ON(!ice->pcm))
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->own_routing) {
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)
@ -2326,7 +2350,7 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
if (ice->num_total_dacs > 0) {
if (!ice->own_routing && 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)

779
sound/pci/ice1712/maya44.c Normal file
View file

@ -0,0 +1,779 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for ESI Maya44 cards
*
* Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
* Based on the patches by 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 <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "maya44.h"
/* WM8776 register indexes */
#define WM8776_REG_HEADPHONE_L 0x00
#define WM8776_REG_HEADPHONE_R 0x01
#define WM8776_REG_HEADPHONE_MASTER 0x02
#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
#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 { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS };
enum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES };
struct snd_wm8776 {
unsigned char addr;
unsigned short regs[WM8776_NUM_REGS];
unsigned char volumes[WM_NUM_VOLS][2];
unsigned int switch_bits;
};
struct snd_maya44 {
struct snd_ice1712 *ice;
struct snd_wm8776 wm[2];
struct mutex mutex;
};
/* write the given register and save the data to the cache */
static void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
unsigned char reg, unsigned short val)
{
/*
* WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB
* of the address field
*/
snd_vt1724_write_i2c(ice, wm->addr,
(reg << 1) | ((val >> 8) & 1),
val & 0xff);
wm->regs[reg] = val;
}
/*
* update the given register with and/or mask and save the data to the cache
*/
static int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
unsigned char reg,
unsigned short mask, unsigned short val)
{
val |= wm->regs[reg] & ~mask;
if (val != wm->regs[reg]) {
wm8776_write(ice, wm, reg, val);
return 1;
}
return 0;
}
/*
* WM8776 volume controls
*/
struct maya_vol_info {
unsigned int maxval; /* volume range: 0..maxval */
unsigned char regs[2]; /* left and right registers */
unsigned short mask; /* value mask */
unsigned short offset; /* zero-value offset */
unsigned short mute; /* mute bit */
unsigned short update; /* update bits */
unsigned char mux_bits[2]; /* extra bits for ADC mute */
};
static struct maya_vol_info vol_info[WM_NUM_VOLS] = {
[WM_VOL_HP] = {
.maxval = 80,
.regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
.mask = 0x7f,
.offset = 0x30,
.mute = 0x00,
.update = 0x180, /* update and zero-cross enable */
},
[WM_VOL_DAC] = {
.maxval = 255,
.regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R },
.mask = 0xff,
.offset = 0x01,
.mute = 0x00,
.update = 0x100, /* zero-cross enable */
},
[WM_VOL_ADC] = {
.maxval = 91,
.regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R },
.mask = 0xff,
.offset = 0xa5,
.mute = 0xa5,
.update = 0x100, /* update */
.mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */
},
};
/*
* dB tables
*/
/* headphone output: mute, -73..+6db (1db step) */
static const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1);
/* DAC output: mute, -127..0db (0.5db step) */
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1);
/* ADC gain: mute, -21..+24db (0.5db step) */
static const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1);
static int maya_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
unsigned int idx = kcontrol->private_value;
struct maya_vol_info *vol = &vol_info[idx];
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = vol->maxval;
return 0;
}
static int maya_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
struct snd_wm8776 *wm =
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = kcontrol->private_value;
mutex_lock(&chip->mutex);
ucontrol->value.integer.value[0] = wm->volumes[idx][0];
ucontrol->value.integer.value[1] = wm->volumes[idx][1];
mutex_unlock(&chip->mutex);
return 0;
}
static int maya_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
struct snd_wm8776 *wm =
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = kcontrol->private_value;
struct maya_vol_info *vol = &vol_info[idx];
unsigned int val, data;
int ch, changed = 0;
mutex_lock(&chip->mutex);
for (ch = 0; ch < 2; ch++) {
val = ucontrol->value.integer.value[ch];
if (val > vol->maxval)
val = vol->maxval;
if (val == wm->volumes[idx][ch])
continue;
if (!val)
data = vol->mute;
else
data = (val - 1) + vol->offset;
data |= vol->update;
changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch],
vol->mask | vol->update, data);
if (vol->mux_bits[ch])
wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX,
vol->mux_bits[ch],
val ? 0 : vol->mux_bits[ch]);
wm->volumes[idx][ch] = val;
}
mutex_unlock(&chip->mutex);
return changed;
}
/*
* WM8776 switch controls
*/
#define COMPOSE_SW_VAL(idx, reg, mask) ((idx) | ((reg) << 8) | ((mask) << 16))
#define GET_SW_VAL_IDX(val) ((val) & 0xff)
#define GET_SW_VAL_REG(val) (((val) >> 8) & 0xff)
#define GET_SW_VAL_MASK(val) (((val) >> 16) & 0xff)
#define maya_sw_info snd_ctl_boolean_mono_info
static int maya_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
struct snd_wm8776 *wm =
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1;
return 0;
}
static int maya_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
struct snd_wm8776 *wm =
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
unsigned int mask, val;
int changed;
mutex_lock(&chip->mutex);
mask = 1 << idx;
wm->switch_bits &= ~mask;
val = ucontrol->value.integer.value[0];
if (val)
wm->switch_bits |= mask;
mask = GET_SW_VAL_MASK(kcontrol->private_value);
changed = wm8776_write_bits(chip->ice, wm,
GET_SW_VAL_REG(kcontrol->private_value),
mask, val ? mask : 0);
mutex_unlock(&chip->mutex);
return changed;
}
/*
* GPIO pins (known ones for maya44)
*/
#define GPIO_PHANTOM_OFF 2
#define GPIO_MIC_RELAY 4
#define GPIO_SPDIF_IN_INV 5
#define GPIO_MUST_BE_0 7
/*
* GPIO switch controls
*/
#define COMPOSE_GPIO_VAL(shift, inv) ((shift) | ((inv) << 8))
#define GET_GPIO_VAL_SHIFT(val) ((val) & 0xff)
#define GET_GPIO_VAL_INV(val) (((val) >> 8) & 1)
static int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask,
unsigned int bits)
{
unsigned int data;
data = snd_ice1712_gpio_read(ice);
if ((data & mask) == bits)
return 0;
snd_ice1712_gpio_write(ice, (data & ~mask) | bits);
return 1;
}
#define maya_gpio_sw_info snd_ctl_boolean_mono_info
static int maya_gpio_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
unsigned int val;
val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1;
if (GET_GPIO_VAL_INV(kcontrol->private_value))
val = !val;
ucontrol->value.integer.value[0] = val;
return 0;
}
static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
unsigned int val, mask;
int changed;
mutex_lock(&chip->mutex);
mask = 1 << shift;
val = ucontrol->value.integer.value[0];
if (GET_GPIO_VAL_INV(kcontrol->private_value))
val = !val;
val = val ? mask : 0;
changed = maya_set_gpio_bits(chip->ice, mask, val);
mutex_unlock(&chip->mutex);
return changed;
}
/*
* capture source selection
*/
/* known working input slots (0-4) */
#define MAYA_LINE_IN 1 /* in-2 */
#define MAYA_MIC_IN 4 /* in-5 */
static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
{
wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX,
0x1f, 1 << line);
}
static int maya_rec_src_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Line", "Mic" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = ARRAY_SIZE(texts);
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;
}
static int maya_rec_src_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
int sel;
if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY))
sel = 1;
else
sel = 0;
ucontrol->value.enumerated.item[0] = sel;
return 0;
}
static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
int sel = ucontrol->value.enumerated.item[0];
int changed;
mutex_lock(&chip->mutex);
changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY,
sel ? GPIO_MIC_RELAY : 0);
wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
mutex_unlock(&chip->mutex);
return changed;
}
/*
* Maya44 routing switch settings have different meanings than the standard
* ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c).
*/
static int maya_pb_route_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 = ARRAY_SIZE(texts);
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;
}
static int maya_pb_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 maya_pb_route_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
ucontrol->value.enumerated.item[0] =
snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx));
return 0;
}
static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
return snd_ice1724_put_route_val(chip->ice,
ucontrol->value.enumerated.item[0],
maya_pb_route_shift(idx));
}
/*
* controls to be added
*/
static struct snd_kcontrol_new maya_controls[] __devinitdata = {
{
.name = "Crossmix Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = maya_vol_info,
.get = maya_vol_get,
.put = maya_vol_put,
.tlv = { .p = db_scale_hp },
.private_value = WM_VOL_HP,
.count = 2,
},
{
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = maya_vol_info,
.get = maya_vol_get,
.put = maya_vol_put,
.tlv = { .p = db_scale_dac },
.private_value = WM_VOL_DAC,
.count = 2,
},
{
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = maya_vol_info,
.get = maya_vol_get,
.put = maya_vol_put,
.tlv = { .p = db_scale_adc },
.private_value = WM_VOL_ADC,
.count = 2,
},
{
.name = "PCM Playback Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = maya_sw_info,
.get = maya_sw_get,
.put = maya_sw_put,
.private_value = COMPOSE_SW_VAL(WM_SW_DAC,
WM8776_REG_OUTPUT_MUX, 0x01),
.count = 2,
},
{
.name = "Bypass Playback Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = maya_sw_info,
.get = maya_sw_get,
.put = maya_sw_put,
.private_value = COMPOSE_SW_VAL(WM_SW_BYPASS,
WM8776_REG_OUTPUT_MUX, 0x04),
.count = 2,
},
{
.name = "Capture Source",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = maya_rec_src_info,
.get = maya_rec_src_get,
.put = maya_rec_src_put,
},
{
.name = "Mic Phantom Power Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = maya_gpio_sw_info,
.get = maya_gpio_sw_get,
.put = maya_gpio_sw_put,
.private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1),
},
{
.name = "SPDIF Capture Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = maya_gpio_sw_info,
.get = maya_gpio_sw_get,
.put = maya_gpio_sw_put,
.private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1),
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
.info = maya_pb_route_info,
.get = maya_pb_route_get,
.put = maya_pb_route_put,
.count = 4, /* FIXME: do controls 5-9 have any meaning? */
},
};
static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(maya_controls); i++) {
err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i],
ice->spec));
if (err < 0)
return err;
}
return 0;
}
/*
* initialize a wm8776 chip
*/
static void __devinit wm8776_init(struct snd_ice1712 *ice,
struct snd_wm8776 *wm, unsigned int addr)
{
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;
wm->addr = addr;
/* enable DAC output; mute bypass, aux & all inputs */
wm->switch_bits = (1 << WM_SW_DAC);
ptr = inits_wm8776;
while (*ptr != 0xff) {
reg = *ptr++;
data = *ptr++;
wm8776_write(ice, wm, reg, data);
}
}
/*
* 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 snd_maya44 *chip = ice->spec;
unsigned int ratio, adc_ratio, val;
int 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;
}
/*
* 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.
*/
adc_ratio = ratio;
if (adc_ratio < WM8776_CLOCK_RATIO_256FS)
adc_ratio = WM8776_CLOCK_RATIO_256FS;
val = adc_ratio;
if (adc_ratio == WM8776_CLOCK_RATIO_256FS)
val |= 8;
val |= ratio << 4;
mutex_lock(&chip->mutex);
for (i = 0; i < 2; i++)
wm8776_write_bits(ice, &chip->wm[i],
WM8776_REG_MASTER_MODE_CONTROL,
0x180, val);
mutex_unlock(&chip->mutex);
}
/*
* supported sample rates (to override the default one)
*/
static unsigned int rates[] = {
32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
};
/* playback rates: 32..192 kHz */
static struct snd_pcm_hw_constraint_list dac_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0
};
/*
* chip addresses on I2C bus
*/
static unsigned char wm8776_addr[2] __devinitdata = {
0x34, 0x36, /* codec 0 & 1 */
};
/*
* initialize the chip
*/
static int __devinit maya44_init(struct snd_ice1712 *ice)
{
int i;
struct snd_maya44 *chip;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
mutex_init(&chip->mutex);
chip->ice = ice;
ice->spec = chip;
/* initialise codecs */
ice->num_total_dacs = 4;
ice->num_total_adcs = 4;
ice->akm_codecs = 0;
for (i = 0; i < 2; i++) {
wm8776_init(ice, &chip->wm[i], wm8776_addr[i]);
wm8776_select_input(chip, i, MAYA_LINE_IN);
}
/* set card specific rates */
ice->hw_rates = &dac_rates;
/* 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;
/* have an own routing control */
ice->own_routing = 1;
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] = (1 << GPIO_PHANTOM_OFF) |
(1 << 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 */
};

View file

@ -0,0 +1,10 @@
#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[];
#endif /* __SOUND_MAYA44_H */