Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,12 @@
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
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 maya44.o quartet.o psc724.o wm8766.o wm8776.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
obj-$(CONFIG_SND_ICE1724) += snd-ice1724.o snd-ice17xx-ak4xxx.o

196
sound/pci/ice1712/ak4xxx.c Normal file
View file

@ -0,0 +1,196 @@
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "ice1712.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface");
MODULE_LICENSE("GPL");
static void snd_ice1712_akm4xxx_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_save_gpio_status(ice);
}
static void snd_ice1712_akm4xxx_unlock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_restore_gpio_status(ice);
}
/*
* write AK4xxx register
*/
static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip,
unsigned char addr, unsigned char data)
{
unsigned int tmp;
int idx;
unsigned int addrdata;
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
if (snd_BUG_ON(chip < 0 || chip >= 4))
return;
tmp = snd_ice1712_gpio_read(ice);
tmp |= priv->add_flags;
tmp &= ~priv->mask_flags;
if (priv->cs_mask == priv->cs_addr) {
if (priv->cif) {
tmp |= priv->cs_mask; /* start without chip select */
} else {
tmp &= ~priv->cs_mask; /* chip select low */
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
} else {
/* doesn't handle cf=1 yet */
tmp &= ~priv->cs_mask;
tmp |= priv->cs_addr;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
/* build I2C address + data byte */
addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f);
addrdata = (addrdata << 8) | data;
for (idx = 15; idx >= 0; idx--) {
/* drop clock */
tmp &= ~priv->clk_mask;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
/* set data */
if (addrdata & (1 << idx))
tmp |= priv->data_mask;
else
tmp &= ~priv->data_mask;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
/* raise clock */
tmp |= priv->clk_mask;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
if (priv->cs_mask == priv->cs_addr) {
if (priv->cif) {
/* assert a cs pulse to trigger */
tmp &= ~priv->cs_mask;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
tmp |= priv->cs_mask; /* chip select high to trigger */
} else {
tmp &= ~priv->cs_mask;
tmp |= priv->cs_none; /* deselect address */
}
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
/*
* initialize the struct snd_akm4xxx record with the template
*/
int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *temp,
const struct snd_ak4xxx_private *_priv, struct snd_ice1712 *ice)
{
struct snd_ak4xxx_private *priv;
if (_priv != NULL) {
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
*priv = *_priv;
} else {
priv = NULL;
}
*ak = *temp;
ak->card = ice->card;
ak->private_value[0] = (unsigned long)priv;
ak->private_data[0] = ice;
if (ak->ops.lock == NULL)
ak->ops.lock = snd_ice1712_akm4xxx_lock;
if (ak->ops.unlock == NULL)
ak->ops.unlock = snd_ice1712_akm4xxx_unlock;
if (ak->ops.write == NULL)
ak->ops.write = snd_ice1712_akm4xxx_write;
snd_akm4xxx_init(ak);
return 0;
}
void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice)
{
unsigned int akidx;
if (ice->akm == NULL)
return;
for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
struct snd_akm4xxx *ak = &ice->akm[akidx];
kfree((void*)ak->private_value[0]);
}
kfree(ice->akm);
}
/*
* build AK4xxx controls
*/
int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice)
{
unsigned int akidx;
int err;
for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
struct snd_akm4xxx *ak = &ice->akm[akidx];
err = snd_akm4xxx_build_controls(ak);
if (err < 0)
return err;
}
return 0;
}
static int __init alsa_ice1712_akm4xxx_module_init(void)
{
return 0;
}
static void __exit alsa_ice1712_akm4xxx_module_exit(void)
{
}
module_init(alsa_ice1712_akm4xxx_module_init)
module_exit(alsa_ice1712_akm4xxx_module_exit)
EXPORT_SYMBOL(snd_ice1712_akm4xxx_init);
EXPORT_SYMBOL(snd_ice1712_akm4xxx_free);
EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls);

97
sound/pci/ice1712/amp.c Normal file
View file

@ -0,0 +1,97 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "amp.h"
static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
{
unsigned short cval;
cval = (reg << 9) | val;
snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
}
static int snd_vt1724_amp_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits[] = {
WM_ATTEN_L, 0x0000, /* 0 db */
WM_ATTEN_R, 0x0000, /* 0 db */
WM_DAC_CTRL, 0x0008, /* 24bit I2S */
WM_INT_CTRL, 0x0001, /* 24bit I2S */
};
unsigned int i;
/* only use basic functionality for now */
/* VT1616 6ch codec connected to PSDOUT0 using packed mode */
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
/* Chaintech AV-710 has another WM8728 codec connected to PSDOUT4
(shared with the SPDIF output). Mixer control for this codec
is not yet supported. */
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AV710) {
for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
wm_put(ice, wm_inits[i], wm_inits[i+1]);
}
return 0;
}
static int snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
{
if (ice->ac97)
/* we use pins 39 and 41 of the VT1616 for left and right
read outputs */
snd_ac97_write_cache(ice->ac97, 0x5a,
snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
return 0;
}
/* entry point */
struct snd_ice1712_card_info snd_vt1724_amp_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_AV710,
.name = "Chaintech AV-710",
.model = "av710",
.chip_init = snd_vt1724_amp_init,
.build_controls = snd_vt1724_amp_add_controls,
},
{
.subvendor = VT1724_SUBDEVICE_AUDIO2000,
.name = "AMP Ltd AUDIO2000",
.model = "amp2000",
.chip_init = snd_vt1724_amp_init,
.build_controls = snd_vt1724_amp_add_controls,
},
{ } /* terminator */
};

48
sound/pci/ice1712/amp.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef __SOUND_AMP_H
#define __SOUND_AMP_H
/*
* ALSA driver for VIA VT1724 (Envy24HT)
*
* Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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
*
*/
#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000},"\
"{Chaintech,AV-710},"
#if 0
#define VT1724_SUBDEVICE_AUDIO2000 0x12142417 /* Advanced Micro Peripherals Ltd AUDIO2000 */
#else
#define VT1724_SUBDEVICE_AUDIO2000 0x00030003 /* a dummy ID for AMP Audio2000 */
#endif
#define VT1724_SUBDEVICE_AV710 0x12142417 /* AV710 - the same ID with Audio2000! */
/* WM8728 on I2C for AV710 */
#define WM_DEV 0x36
#define WM_ATTEN_L 0x00
#define WM_ATTEN_R 0x01
#define WM_DAC_CTRL 0x02
#define WM_INT_CTRL 0x03
extern struct snd_ice1712_card_info snd_vt1724_amp_cards[];
#endif /* __SOUND_AMP_H */

2311
sound/pci/ice1712/aureon.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
#ifndef __SOUND_AUREON_H
#define __SOUND_AUREON_H
/*
* ALSA driver for VIA VT1724 (Envy24HT)
*
* Lowlevel functions for Terratec Aureon cards
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.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
*
*/
#define AUREON_DEVICE_DESC "{Terratec,Aureon 5.1 Sky},"\
"{Terratec,Aureon 7.1 Space},"\
"{Terratec,Aureon 7.1 Universe}," \
"{AudioTrak,Prodigy 7.1}," \
"{AudioTrak,Prodigy 7.1 LT},"\
"{AudioTrak,Prodigy 7.1 XT},"
#define VT1724_SUBDEVICE_AUREON51_SKY 0x3b154711 /* Aureon 5.1 Sky */
#define VT1724_SUBDEVICE_AUREON71_SPACE 0x3b154511 /* Aureon 7.1 Space */
#define VT1724_SUBDEVICE_AUREON71_UNIVERSE 0x3b155311 /* Aureon 7.1 Universe */
#define VT1724_SUBDEVICE_PRODIGY71 0x33495345 /* PRODIGY 7.1 */
#define VT1724_SUBDEVICE_PRODIGY71LT 0x32315441 /* PRODIGY 7.1 LT */
#define VT1724_SUBDEVICE_PRODIGY71XT 0x36315441 /* PRODIGY 7.1 XT*/
extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[];
/* GPIO bits */
#define AUREON_CS8415_CS (1 << 22)
#define AUREON_SPI_MISO (1 << 21)
#define AUREON_WM_RESET (1 << 20)
#define AUREON_SPI_CLK (1 << 19)
#define AUREON_SPI_MOSI (1 << 18)
#define AUREON_WM_RW (1 << 17)
#define AUREON_AC97_RESET (1 << 16)
#define AUREON_DIGITAL_SEL1 (1 << 15)
#define AUREON_HP_SEL (1 << 14)
#define AUREON_WM_CS (1 << 12)
#define AUREON_AC97_COMMIT (1 << 11)
#define AUREON_AC97_ADDR (1 << 10)
#define AUREON_AC97_DATA_LOW (1 << 9)
#define AUREON_AC97_DATA_HIGH (1 << 8)
#define AUREON_AC97_DATA_MASK 0xFF
#define PRODIGY_WM_CS (1 << 8)
#define PRODIGY_SPI_MOSI (1 << 10)
#define PRODIGY_SPI_CLK (1 << 9)
#define PRODIGY_HP_SEL (1 << 5)
#endif /* __SOUND_AUREON_H */

934
sound/pci/ice1712/delta.c Normal file
View file

@ -0,0 +1,934 @@
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for M-Audio Delta 1010, 1010E, 44, 66, 66E, Dio2496,
* Audiophile, Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/cs8427.h>
#include <sound/asoundef.h>
#include "ice1712.h"
#include "delta.h"
#define SND_CS8403
#include <sound/cs8403.h>
/*
* CS8427 via SPI mode (for Audiophile), emulated I2C
*/
/* send 8 bits */
static void ap_cs8427_write_byte(struct snd_ice1712 *ice, unsigned char data, unsigned char tmp)
{
int idx;
for (idx = 7; idx >= 0; idx--) {
tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK);
if (data & (1 << idx))
tmp |= ICE1712_DELTA_AP_DOUT;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(5);
tmp |= ICE1712_DELTA_AP_CCLK;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(5);
}
}
/* read 8 bits */
static unsigned char ap_cs8427_read_byte(struct snd_ice1712 *ice, unsigned char tmp)
{
unsigned char data = 0;
int idx;
for (idx = 7; idx >= 0; idx--) {
tmp &= ~ICE1712_DELTA_AP_CCLK;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(5);
if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN)
data |= 1 << idx;
tmp |= ICE1712_DELTA_AP_CCLK;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(5);
}
return data;
}
/* assert chip select */
static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
{
unsigned char tmp;
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
tmp &= ~ICE1712_DELTA_1010LT_CS;
tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427;
break;
case ICE1712_SUBDEVICE_AUDIOPHILE:
case ICE1712_SUBDEVICE_DELTA410:
tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
break;
case ICE1712_SUBDEVICE_DELTA66E:
tmp |= ICE1712_DELTA_66E_CCLK | ICE1712_DELTA_66E_CS_CHIP_A |
ICE1712_DELTA_66E_CS_CHIP_B;
tmp &= ~ICE1712_DELTA_66E_CS_CS8427;
break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
tmp &= ~ICE1712_VX442_CS_DIGITAL;
break;
}
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(5);
return tmp;
}
/* deassert chip select */
static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
{
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
tmp &= ~ICE1712_DELTA_1010LT_CS;
tmp |= ICE1712_DELTA_1010LT_CS_NONE;
break;
case ICE1712_SUBDEVICE_AUDIOPHILE:
case ICE1712_SUBDEVICE_DELTA410:
tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
break;
case ICE1712_SUBDEVICE_DELTA66E:
tmp |= ICE1712_DELTA_66E_CS_CS8427;
break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CS_DIGITAL;
break;
}
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
}
/* sequential write */
static int ap_cs8427_sendbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)
{
struct snd_ice1712 *ice = device->bus->private_data;
int res = count;
unsigned char tmp;
mutex_lock(&ice->gpio_mutex);
tmp = ap_cs8427_codec_select(ice);
ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
while (count-- > 0)
ap_cs8427_write_byte(ice, *bytes++, tmp);
ap_cs8427_codec_deassert(ice, tmp);
mutex_unlock(&ice->gpio_mutex);
return res;
}
/* sequential read */
static int ap_cs8427_readbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)
{
struct snd_ice1712 *ice = device->bus->private_data;
int res = count;
unsigned char tmp;
mutex_lock(&ice->gpio_mutex);
tmp = ap_cs8427_codec_select(ice);
ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
while (count-- > 0)
*bytes++ = ap_cs8427_read_byte(ice, tmp);
ap_cs8427_codec_deassert(ice, tmp);
mutex_unlock(&ice->gpio_mutex);
return res;
}
static int ap_cs8427_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
{
if (addr == 0x10)
return 1;
return -ENOENT;
}
static struct snd_i2c_ops ap_cs8427_i2c_ops = {
.sendbytes = ap_cs8427_sendbytes,
.readbytes = ap_cs8427_readbytes,
.probeaddr = ap_cs8427_probeaddr,
};
/*
*/
static void snd_ice1712_delta_cs8403_spdif_write(struct snd_ice1712 *ice, unsigned char bits)
{
unsigned char tmp, mask1, mask2;
int idx;
/* send byte to transmitter */
mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
mutex_lock(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
for (idx = 7; idx >= 0; idx--) {
tmp &= ~(mask1 | mask2);
if (bits & (1 << idx))
tmp |= mask2;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(100);
tmp |= mask1;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(100);
}
tmp &= ~mask1;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
mutex_unlock(&ice->gpio_mutex);
}
static void delta_spdif_default_get(struct snd_ice1712 *ice, struct snd_ctl_elem_value *ucontrol)
{
snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits);
}
static int delta_spdif_default_put(struct snd_ice1712 *ice, struct snd_ctl_elem_value *ucontrol)
{
unsigned int val;
int change;
val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
spin_lock_irq(&ice->reg_lock);
change = ice->spdif.cs8403_bits != val;
ice->spdif.cs8403_bits = val;
if (change && ice->playback_pro_substream == NULL) {
spin_unlock_irq(&ice->reg_lock);
snd_ice1712_delta_cs8403_spdif_write(ice, val);
} else {
spin_unlock_irq(&ice->reg_lock);
}
return change;
}
static void delta_spdif_stream_get(struct snd_ice1712 *ice, struct snd_ctl_elem_value *ucontrol)
{
snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits);
}
static int delta_spdif_stream_put(struct snd_ice1712 *ice, struct snd_ctl_elem_value *ucontrol)
{
unsigned int val;
int change;
val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
spin_lock_irq(&ice->reg_lock);
change = ice->spdif.cs8403_stream_bits != val;
ice->spdif.cs8403_stream_bits = val;
if (change && ice->playback_pro_substream != NULL) {
spin_unlock_irq(&ice->reg_lock);
snd_ice1712_delta_cs8403_spdif_write(ice, val);
} else {
spin_unlock_irq(&ice->reg_lock);
}
return change;
}
/*
* AK4524 on Delta 44 and 66 to choose the chip mask
*/
static void delta_ak4524_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_save_gpio_status(ice);
priv->cs_mask =
priv->cs_addr = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A :
ICE1712_DELTA_CODEC_CHIP_B;
}
/*
* AK4524 on Delta1010LT to choose the chip address
*/
static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_save_gpio_status(ice);
priv->cs_mask = ICE1712_DELTA_1010LT_CS;
priv->cs_addr = chip << 4;
}
/*
* AK4524 on Delta66 rev E to choose the chip address
*/
static void delta66e_ak4524_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_save_gpio_status(ice);
priv->cs_mask =
priv->cs_addr = chip == 0 ? ICE1712_DELTA_66E_CS_CHIP_A :
ICE1712_DELTA_66E_CS_CHIP_B;
}
/*
* AK4528 on VX442 to choose the chip mask
*/
static void vx442_ak4524_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_save_gpio_status(ice);
priv->cs_mask =
priv->cs_addr = chip == 0 ? ICE1712_VX442_CODEC_CHIP_A :
ICE1712_VX442_CODEC_CHIP_B;
}
/*
* change the DFS bit according rate for Delta1010
*/
static void delta_1010_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned char tmp, tmp2;
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
mutex_lock(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
tmp2 = tmp & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp2 |= ICE1712_DELTA_DFS;
if (tmp != tmp2)
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp2);
mutex_unlock(&ice->gpio_mutex);
}
/*
* change the rate of AK4524 on Delta 44/66, AP, 1010LT
*/
static void delta_ak4524_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
unsigned char tmp, tmp2;
struct snd_ice1712 *ice = ak->private_data[0];
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
/* check before reset ak4524 to avoid unnecessary clicks */
mutex_lock(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
mutex_unlock(&ice->gpio_mutex);
tmp2 = tmp & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp2 |= ICE1712_DELTA_DFS;
if (tmp == tmp2)
return;
/* do it again */
snd_akm4xxx_reset(ak, 1);
mutex_lock(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp |= ICE1712_DELTA_DFS;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
mutex_unlock(&ice->gpio_mutex);
snd_akm4xxx_reset(ak, 0);
}
/*
* change the rate of AK4524 on VX442
*/
static void vx442_ak4524_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
unsigned char val;
val = (rate > 48000) ? 0x65 : 0x60;
if (snd_akm4xxx_get(ak, 0, 0x02) != val ||
snd_akm4xxx_get(ak, 1, 0x02) != val) {
snd_akm4xxx_reset(ak, 1);
snd_akm4xxx_write(ak, 0, 0x02, val);
snd_akm4xxx_write(ak, 1, 0x02, val);
snd_akm4xxx_reset(ak, 0);
}
}
/*
* SPDIF ops for Delta 1010, Dio, 66
*/
/* open callback */
static void delta_open_spdif(struct snd_ice1712 *ice, struct snd_pcm_substream *substream)
{
ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits;
}
/* set up */
static void delta_setup_spdif(struct snd_ice1712 *ice, int rate)
{
unsigned long flags;
unsigned int tmp;
int change;
spin_lock_irqsave(&ice->reg_lock, flags);
tmp = ice->spdif.cs8403_stream_bits;
if (tmp & 0x01) /* consumer */
tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
switch (rate) {
case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
}
change = ice->spdif.cs8403_stream_bits != tmp;
ice->spdif.cs8403_stream_bits = tmp;
spin_unlock_irqrestore(&ice->reg_lock, flags);
if (change)
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
}
#define snd_ice1712_delta1010lt_wordclock_status_info \
snd_ctl_boolean_mono_info
static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
char reg = 0x10; /* CS8427 receiver error register */
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
dev_err(ice->card->dev,
"unable to send register 0x%x byte to CS8427\n", reg);
snd_i2c_readbytes(ice->cs8427, &reg, 1);
ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0;
return 0;
}
static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Word Clock Status",
.info = snd_ice1712_delta1010lt_wordclock_status_info,
.get = snd_ice1712_delta1010lt_wordclock_status_get,
};
/*
* initialize the chips on M-Audio cards
*/
static struct snd_akm4xxx akm_audiophile = {
.type = SND_AK4528,
.num_adcs = 2,
.num_dacs = 2,
.ops = {
.set_rate_val = delta_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_audiophile_priv = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
.clk_mask = ICE1712_DELTA_AP_CCLK,
.cs_mask = ICE1712_DELTA_AP_CS_CODEC,
.cs_addr = ICE1712_DELTA_AP_CS_CODEC,
.cs_none = 0,
.add_flags = ICE1712_DELTA_AP_CS_DIGITAL,
.mask_flags = 0,
};
static struct snd_akm4xxx akm_delta410 = {
.type = SND_AK4529,
.num_adcs = 2,
.num_dacs = 8,
.ops = {
.set_rate_val = delta_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_delta410_priv = {
.caddr = 0,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
.clk_mask = ICE1712_DELTA_AP_CCLK,
.cs_mask = ICE1712_DELTA_AP_CS_CODEC,
.cs_addr = ICE1712_DELTA_AP_CS_CODEC,
.cs_none = 0,
.add_flags = ICE1712_DELTA_AP_CS_DIGITAL,
.mask_flags = 0,
};
static struct snd_akm4xxx akm_delta1010lt = {
.type = SND_AK4524,
.num_adcs = 8,
.num_dacs = 8,
.ops = {
.lock = delta1010lt_ak4524_lock,
.set_rate_val = delta_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_delta1010lt_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_1010LT_DOUT,
.clk_mask = ICE1712_DELTA_1010LT_CCLK,
.cs_mask = 0,
.cs_addr = 0, /* set later */
.cs_none = ICE1712_DELTA_1010LT_CS_NONE,
.add_flags = 0,
.mask_flags = 0,
};
static struct snd_akm4xxx akm_delta66e = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
.ops = {
.lock = delta66e_ak4524_lock,
.set_rate_val = delta_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_delta66e_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_66E_DOUT,
.clk_mask = ICE1712_DELTA_66E_CCLK,
.cs_mask = 0,
.cs_addr = 0, /* set later */
.cs_none = 0,
.add_flags = 0,
.mask_flags = 0,
};
static struct snd_akm4xxx akm_delta44 = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
.ops = {
.lock = delta_ak4524_lock,
.set_rate_val = delta_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_delta44_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA,
.clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK,
.cs_mask = 0,
.cs_addr = 0, /* set later */
.cs_none = 0,
.add_flags = 0,
.mask_flags = 0,
};
static struct snd_akm4xxx akm_vx442 = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
.ops = {
.lock = vx442_ak4524_lock,
.set_rate_val = vx442_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_vx442_priv = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_VX442_DOUT,
.clk_mask = ICE1712_VX442_CCLK,
.cs_mask = 0,
.cs_addr = 0, /* set later */
.cs_none = 0,
.add_flags = 0,
.mask_flags = 0,
};
#ifdef CONFIG_PM_SLEEP
static int snd_ice1712_delta_resume(struct snd_ice1712 *ice)
{
unsigned char akm_img_bak[AK4XXX_IMAGE_SIZE];
unsigned char akm_vol_bak[AK4XXX_IMAGE_SIZE];
/* init spdif */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_AUDIOPHILE:
case ICE1712_SUBDEVICE_DELTA410:
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
snd_cs8427_init(ice->i2c, ice->cs8427);
break;
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_MEDIASTATION:
/* nothing */
break;
case ICE1712_SUBDEVICE_DELTADIO2496:
case ICE1712_SUBDEVICE_DELTA66:
/* Set spdif defaults */
snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits);
break;
}
/* init codec and restore registers */
if (ice->akm_codecs) {
memcpy(akm_img_bak, ice->akm->images, sizeof(akm_img_bak));
memcpy(akm_vol_bak, ice->akm->volumes, sizeof(akm_vol_bak));
snd_akm4xxx_init(ice->akm);
memcpy(ice->akm->images, akm_img_bak, sizeof(akm_img_bak));
memcpy(ice->akm->volumes, akm_vol_bak, sizeof(akm_vol_bak));
snd_akm4xxx_reset(ice->akm, 0);
}
return 0;
}
static int snd_ice1712_delta_suspend(struct snd_ice1712 *ice)
{
if (ice->akm_codecs) /* reset & mute codec */
snd_akm4xxx_reset(ice->akm, 1);
return 0;
}
#endif
static int snd_ice1712_delta_init(struct snd_ice1712 *ice)
{
int err;
struct snd_akm4xxx *ak;
unsigned char tmp;
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 &&
ice->eeprom.gpiodir == 0x7b)
ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA1010E;
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA66 &&
ice->eeprom.gpiodir == 0xfb)
ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA66E;
/* determine I2C, DACs and ADCs */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_AUDIOPHILE:
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
break;
case ICE1712_SUBDEVICE_DELTA410:
ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
break;
case ICE1712_SUBDEVICE_DELTA44:
case ICE1712_SUBDEVICE_DELTA66:
ice->num_total_dacs = ice->omni ? 8 : 4;
ice->num_total_adcs = ice->omni ? 8 : 4;
break;
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_MEDIASTATION:
case ICE1712_SUBDEVICE_EDIROLDA2496:
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
break;
case ICE1712_SUBDEVICE_DELTADIO2496:
ice->num_total_dacs = 4; /* two AK4324 codecs */
break;
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E: /* omni not supported yet */
ice->num_total_dacs = 4;
ice->num_total_adcs = 4;
break;
}
#ifdef CONFIG_PM_SLEEP
ice->pm_resume = snd_ice1712_delta_resume;
ice->pm_suspend = snd_ice1712_delta_suspend;
ice->pm_suspend_enabled = 1;
#endif
/* initialize the SPI clock to high */
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
tmp |= ICE1712_DELTA_AP_CCLK;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
udelay(5);
/* initialize spdif */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_AUDIOPHILE:
case ICE1712_SUBDEVICE_DELTA410:
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
dev_err(ice->card->dev, "unable to create I2C bus\n");
return err;
}
ice->i2c->private_data = ice;
ice->i2c->ops = &ap_cs8427_i2c_ops;
if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
return err;
break;
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_MEDIASTATION:
ice->gpio.set_pro_rate = delta_1010_set_rate_val;
break;
case ICE1712_SUBDEVICE_DELTADIO2496:
ice->gpio.set_pro_rate = delta_1010_set_rate_val;
/* fall thru */
case ICE1712_SUBDEVICE_DELTA66:
ice->spdif.ops.open = delta_open_spdif;
ice->spdif.ops.setup_rate = delta_setup_spdif;
ice->spdif.ops.default_get = delta_spdif_default_get;
ice->spdif.ops.default_put = delta_spdif_default_put;
ice->spdif.ops.stream_get = delta_spdif_stream_get;
ice->spdif.ops.stream_put = delta_spdif_stream_put;
/* Set spdif defaults */
snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits);
break;
}
/* no analog? */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTADIO2496:
case ICE1712_SUBDEVICE_MEDIASTATION:
return 0;
}
/* second stage of initialization, analog parts and others */
ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ak)
return -ENOMEM;
ice->akm_codecs = 1;
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_AUDIOPHILE:
err = snd_ice1712_akm4xxx_init(ak, &akm_audiophile, &akm_audiophile_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA410:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_EDIROLDA2496:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_DELTA44:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
break;
case ICE1712_SUBDEVICE_VX442:
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
break;
default:
snd_BUG();
return -EINVAL;
}
return err;
}
/*
* additional controls for M-Audio cards
*/
static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 0, 0);
static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
static struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
static struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
static int snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
{
int err;
/* 1010 and dio specific controls */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_MEDIASTATION:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice));
if (err < 0)
return err;
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice));
if (err < 0)
return err;
break;
case ICE1712_SUBDEVICE_DELTADIO2496:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice));
if (err < 0)
return err;
break;
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice));
if (err < 0)
return err;
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_status, ice));
if (err < 0)
return err;
break;
}
/* normal spdif controls */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_DELTADIO2496:
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_MEDIASTATION:
err = snd_ice1712_spdif_build_controls(ice);
if (err < 0)
return err;
break;
}
/* spdif status in */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010:
case ICE1712_SUBDEVICE_DELTADIO2496:
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_MEDIASTATION:
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice));
if (err < 0)
return err;
break;
}
/* ak4524 controls */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_AUDIOPHILE:
case ICE1712_SUBDEVICE_DELTA410:
case ICE1712_SUBDEVICE_DELTA44:
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
case ICE1712_SUBDEVICE_EDIROLDA2496:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
break;
}
return 0;
}
/* entry point */
struct snd_ice1712_card_info snd_ice1712_delta_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_DELTA1010,
.name = "M Audio Delta 1010",
.model = "delta1010",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
{
.subvendor = ICE1712_SUBDEVICE_DELTADIO2496,
.name = "M Audio Delta DiO 2496",
.model = "dio2496",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
.no_mpu401 = 1,
},
{
.subvendor = ICE1712_SUBDEVICE_DELTA66,
.name = "M Audio Delta 66",
.model = "delta66",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
.no_mpu401 = 1,
},
{
.subvendor = ICE1712_SUBDEVICE_DELTA44,
.name = "M Audio Delta 44",
.model = "delta44",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
.no_mpu401 = 1,
},
{
.subvendor = ICE1712_SUBDEVICE_AUDIOPHILE,
.name = "M Audio Audiophile 24/96",
.model = "audiophile",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
{
.subvendor = ICE1712_SUBDEVICE_DELTA410,
.name = "M Audio Delta 410",
.model = "delta410",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
{
.subvendor = ICE1712_SUBDEVICE_DELTA1010LT,
.name = "M Audio Delta 1010LT",
.model = "delta1010lt",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
{
.subvendor = ICE1712_SUBDEVICE_VX442,
.name = "Digigram VX442",
.model = "vx442",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
.no_mpu401 = 1,
},
{
.subvendor = ICE1712_SUBDEVICE_MEDIASTATION,
.name = "Lionstracs Mediastation",
.model = "mediastation",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
{
.subvendor = ICE1712_SUBDEVICE_EDIROLDA2496,
.name = "Edirol DA2496",
.model = "da2496",
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
{ } /* terminator */
};

166
sound/pci/ice1712/delta.h Normal file
View file

@ -0,0 +1,166 @@
#ifndef __SOUND_DELTA_H
#define __SOUND_DELTA_H
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
* Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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
*
*/
#define DELTA_DEVICE_DESC \
"{MidiMan M Audio,Delta 1010},"\
"{MidiMan M Audio,Delta 1010LT},"\
"{MidiMan M Audio,Delta DiO 2496},"\
"{MidiMan M Audio,Delta 66},"\
"{MidiMan M Audio,Delta 44},"\
"{MidiMan M Audio,Delta 410},"\
"{MidiMan M Audio,Audiophile 24/96},"\
"{Digigram,VX442},"\
"{Lionstracs,Mediastation},"\
"{Edirol,DA2496},"
#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6
#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6
#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6
#define ICE1712_SUBDEVICE_DELTA66 0x121432d6
#define ICE1712_SUBDEVICE_DELTA66E 0xff1432d6
#define ICE1712_SUBDEVICE_DELTA44 0x121433d6
#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6
#define ICE1712_SUBDEVICE_DELTA410 0x121438d6
#define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6
#define ICE1712_SUBDEVICE_VX442 0x12143cd6
#define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100
#define ICE1712_SUBDEVICE_EDIROLDA2496 0xce164010
/* entry point */
extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
/*
* MidiMan M-Audio Delta GPIO definitions
*/
/* MidiMan M-Audio Delta shared pins */
#define ICE1712_DELTA_DFS 0x01 /* fast/slow sample rate mode */
/* (>48kHz must be 1) */
#define ICE1712_DELTA_SPDIF_IN_STAT 0x02
/* S/PDIF input status */
/* 0 = valid signal is present */
/* all except Delta44 */
/* look to CS8414 datasheet */
#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04
/* S/PDIF output status clock */
/* (writing on rising edge - 0->1) */
/* all except Delta44 */
/* look to CS8404A datasheet */
#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08
/* S/PDIF output status data */
/* all except Delta44 */
/* look to CS8404A datasheet */
/* MidiMan M-Audio DeltaDiO */
/* 0x01 = DFS */
/* 0x02 = SPDIF_IN_STAT */
/* 0x04 = SPDIF_OUT_STAT_CLOCK */
/* 0x08 = SPDIF_OUT_STAT_DATA */
#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10
/* coaxial (0), optical (1) */
/* S/PDIF input select*/
/* MidiMan M-Audio Delta1010 */
/* 0x01 = DFS */
/* 0x02 = SPDIF_IN_STAT */
/* 0x04 = SPDIF_OUT_STAT_CLOCK */
/* 0x08 = SPDIF_OUT_STAT_DATA */
#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10
/* 1 - clock are taken from S/PDIF input */
/* 0 - clock are taken from Word Clock input */
/* affected SPMCLKIN pin of Envy24 */
#define ICE1712_DELTA_WORD_CLOCK_STATUS 0x20
/* 0 = valid word clock signal is present */
/* MidiMan M-Audio Delta66 */
/* 0x01 = DFS */
/* 0x02 = SPDIF_IN_STAT */
/* 0x04 = SPDIF_OUT_STAT_CLOCK */
/* 0x08 = SPDIF_OUT_STAT_DATA */
#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10
/* AKM4524 serial data */
#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20
/* AKM4524 serial clock */
/* (writing on rising edge - 0->1 */
#define ICE1712_DELTA_CODEC_CHIP_A 0x40
#define ICE1712_DELTA_CODEC_CHIP_B 0x80
/* 1 - select chip A or B */
/* MidiMan M-Audio Delta44 */
/* 0x01 = DFS */
/* 0x10 = CODEC_SERIAL_DATA */
/* 0x20 = CODEC_SERIAL_CLOCK */
/* 0x40 = CODEC_CHIP_A */
/* 0x80 = CODEC_CHIP_B */
/* MidiMan M-Audio Audiophile/Delta410 definitions */
/* thanks to Kristof Pelckmans <Kristof.Pelckmans@antwerpen.be> for Delta410 info */
/* 0x01 = DFS */
#define ICE1712_DELTA_AP_CCLK 0x02 /* SPI clock */
/* (clocking on rising edge - 0->1) */
#define ICE1712_DELTA_AP_DIN 0x04 /* data input */
#define ICE1712_DELTA_AP_DOUT 0x08 /* data output */
#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */
/* low signal = select */
#define ICE1712_DELTA_AP_CS_CODEC 0x20 /* AK4528 (audiophile), AK4529 (Delta410) chip select */
/* low signal = select */
/* MidiMan M-Audio Delta1010LT definitions */
/* thanks to Anders Johansson <ajh@watri.uwa.edu.au> */
/* 0x01 = DFS */
#define ICE1712_DELTA_1010LT_CCLK 0x02 /* SPI clock (AK4524 + CS8427) */
#define ICE1712_DELTA_1010LT_DIN 0x04 /* data input (CS8427) */
#define ICE1712_DELTA_1010LT_DOUT 0x08 /* data output (AK4524 + CS8427) */
#define ICE1712_DELTA_1010LT_CS 0x70 /* mask for CS address */
#define ICE1712_DELTA_1010LT_CS_CHIP_A 0x00 /* AK4524 #0 */
#define ICE1712_DELTA_1010LT_CS_CHIP_B 0x10 /* AK4524 #1 */
#define ICE1712_DELTA_1010LT_CS_CHIP_C 0x20 /* AK4524 #2 */
#define ICE1712_DELTA_1010LT_CS_CHIP_D 0x30 /* AK4524 #3 */
#define ICE1712_DELTA_1010LT_CS_CS8427 0x40 /* CS8427 */
#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */
#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
/* M-Audio Delta 66 rev. E definitions.
* Newer revisions of Delta 66 have CS8427 over SPI for
* S/PDIF transceiver instead of CS8404/CS8414. */
/* 0x01 = DFS */
#define ICE1712_DELTA_66E_CCLK 0x02 /* SPI clock */
#define ICE1712_DELTA_66E_DIN 0x04 /* data input */
#define ICE1712_DELTA_66E_DOUT 0x08 /* data output */
#define ICE1712_DELTA_66E_CS_CS8427 0x10 /* chip select, low = CS8427 */
#define ICE1712_DELTA_66E_CS_CHIP_A 0x20 /* AK4524 #0 */
#define ICE1712_DELTA_66E_CS_CHIP_B 0x40 /* AK4524 #1 */
/* Digigram VX442 definitions */
#define ICE1712_VX442_CCLK 0x02 /* SPI clock */
#define ICE1712_VX442_DIN 0x04 /* data input */
#define ICE1712_VX442_DOUT 0x08 /* data output */
#define ICE1712_VX442_CS_DIGITAL 0x10 /* chip select, low = CS8427 */
#define ICE1712_VX442_CODEC_CHIP_A 0x20 /* select chip A */
#define ICE1712_VX442_CODEC_CHIP_B 0x40 /* select chip B */
#endif /* __SOUND_DELTA_H */

View file

@ -0,0 +1,220 @@
#ifndef __SOUND_VT1724_H
#define __SOUND_VT1724_H
/*
* ALSA driver for ICEnsemble VT1724 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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 <sound/control.h>
#include <sound/ac97_codec.h>
#include <sound/rawmidi.h>
#include <sound/i2c.h>
#include <sound/pcm.h>
#include "ice1712.h"
enum {
ICE_EEP2_SYSCONF = 0, /* 06 */
ICE_EEP2_ACLINK, /* 07 */
ICE_EEP2_I2S, /* 08 */
ICE_EEP2_SPDIF, /* 09 */
ICE_EEP2_GPIO_DIR, /* 0a */
ICE_EEP2_GPIO_DIR1, /* 0b */
ICE_EEP2_GPIO_DIR2, /* 0c */
ICE_EEP2_GPIO_MASK, /* 0d */
ICE_EEP2_GPIO_MASK1, /* 0e */
ICE_EEP2_GPIO_MASK2, /* 0f */
ICE_EEP2_GPIO_STATE, /* 10 */
ICE_EEP2_GPIO_STATE1, /* 11 */
ICE_EEP2_GPIO_STATE2 /* 12 */
};
/*
* Direct registers
*/
#define ICEREG1724(ice, x) ((ice)->port + VT1724_REG_##x)
#define VT1724_REG_CONTROL 0x00 /* byte */
#define VT1724_RESET 0x80 /* reset whole chip */
#define VT1724_REG_IRQMASK 0x01 /* byte */
#define VT1724_IRQ_MPU_RX 0x80
#define VT1724_IRQ_MPU_TX 0x20
#define VT1724_IRQ_MTPCM 0x10
#define VT1724_REG_IRQSTAT 0x02 /* byte */
/* look to VT1724_IRQ_* */
#define VT1724_REG_SYS_CFG 0x04 /* byte - system configuration PCI60 on Envy24*/
#define VT1724_CFG_CLOCK 0xc0
#define VT1724_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */
#define VT1724_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */
#define VT1724_CFG_MPU401 0x20 /* MPU401 UARTs */
#define VT1724_CFG_ADC_MASK 0x0c /* one, two or one and S/PDIF, stereo ADCs */
#define VT1724_CFG_ADC_NONE 0x0c /* no ADCs */
#define VT1724_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */
#define VT1724_REG_AC97_CFG 0x05 /* byte */
#define VT1724_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */
#define VT1724_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */
#define VT1724_REG_I2S_FEATURES 0x06 /* byte */
#define VT1724_CFG_I2S_VOLUME 0x80 /* volume/mute capability */
#define VT1724_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */
#define VT1724_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */
#define VT1724_CFG_I2S_192KHZ 0x08 /* supports 192kHz sampling */
#define VT1724_CFG_I2S_OTHER 0x07 /* other I2S IDs */
#define VT1724_REG_SPDIF_CFG 0x07 /* byte */
#define VT1724_CFG_SPDIF_OUT_EN 0x80 /*Internal S/PDIF output is enabled*/
#define VT1724_CFG_SPDIF_OUT_INT 0x40 /*Internal S/PDIF output is implemented*/
#define VT1724_CFG_I2S_CHIPID 0x3c /* I2S chip ID */
#define VT1724_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */
#define VT1724_CFG_SPDIF_OUT 0x01 /* External S/PDIF output is present */
/*there is no consumer AC97 codec with the VT1724*/
//#define VT1724_REG_AC97_INDEX 0x08 /* byte */
//#define VT1724_REG_AC97_CMD 0x09 /* byte */
#define VT1724_REG_MPU_TXFIFO 0x0a /*byte ro. number of bytes in TX fifo*/
#define VT1724_REG_MPU_RXFIFO 0x0b /*byte ro. number of bytes in RX fifo*/
#define VT1724_REG_MPU_DATA 0x0c /* byte */
#define VT1724_REG_MPU_CTRL 0x0d /* byte */
#define VT1724_MPU_UART 0x01
#define VT1724_MPU_TX_EMPTY 0x02
#define VT1724_MPU_TX_FULL 0x04
#define VT1724_MPU_RX_EMPTY 0x08
#define VT1724_MPU_RX_FULL 0x10
#define VT1724_REG_MPU_FIFO_WM 0x0e /*byte set the high/low watermarks for RX/TX fifos*/
#define VT1724_MPU_RX_FIFO 0x20 //1=rx fifo watermark 0=tx fifo watermark
#define VT1724_MPU_FIFO_MASK 0x1f
#define VT1724_REG_I2C_DEV_ADDR 0x10 /* byte */
#define VT1724_I2C_WRITE 0x01 /* write direction */
#define VT1724_REG_I2C_BYTE_ADDR 0x11 /* byte */
#define VT1724_REG_I2C_DATA 0x12 /* byte */
#define VT1724_REG_I2C_CTRL 0x13 /* byte */
#define VT1724_I2C_EEPROM 0x80 /* 1 = EEPROM exists */
#define VT1724_I2C_BUSY 0x01 /* busy bit */
#define VT1724_REG_GPIO_DATA 0x14 /* word */
#define VT1724_REG_GPIO_WRITE_MASK 0x16 /* word */
#define VT1724_REG_GPIO_DIRECTION 0x18 /* dword? (3 bytes) 0=input 1=output.
bit3 - during reset used for Eeprom power-on strapping
if TESTEN# pin active, bit 2 always input*/
#define VT1724_REG_POWERDOWN 0x1c
#define VT1724_REG_GPIO_DATA_22 0x1e /* byte direction for GPIO 16:22 */
#define VT1724_REG_GPIO_WRITE_MASK_22 0x1f /* byte write mask for GPIO 16:22 */
/*
* Professional multi-track direct control registers
*/
#define ICEMT1724(ice, x) ((ice)->profi_port + VT1724_MT_##x)
#define VT1724_MT_IRQ 0x00 /* byte - interrupt mask */
#define VT1724_MULTI_PDMA4 0x80 /* SPDIF Out / PDMA4 */
#define VT1724_MULTI_PDMA3 0x40 /* PDMA3 */
#define VT1724_MULTI_PDMA2 0x20 /* PDMA2 */
#define VT1724_MULTI_PDMA1 0x10 /* PDMA1 */
#define VT1724_MULTI_FIFO_ERR 0x08 /* DMA FIFO underrun/overrun. */
#define VT1724_MULTI_RDMA1 0x04 /* RDMA1 (S/PDIF input) */
#define VT1724_MULTI_RDMA0 0x02 /* RMDA0 */
#define VT1724_MULTI_PDMA0 0x01 /* MC Interleave/PDMA0 */
#define VT1724_MT_RATE 0x01 /* byte - sampling rate select */
#define VT1724_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */
#define VT1724_MT_I2S_FORMAT 0x02 /* byte - I2S data format */
#define VT1724_MT_I2S_MCLK_128X 0x08
#define VT1724_MT_I2S_FORMAT_MASK 0x03
#define VT1724_MT_I2S_FORMAT_I2S 0x00
#define VT1724_MT_DMA_INT_MASK 0x03 /* byte -DMA Interrupt Mask */
/* lool to VT1724_MULTI_* */
#define VT1724_MT_AC97_INDEX 0x04 /* byte - AC'97 index */
#define VT1724_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */
#define VT1724_AC97_COLD 0x80 /* cold reset */
#define VT1724_AC97_WARM 0x40 /* warm reset */
#define VT1724_AC97_WRITE 0x20 /* W: write, R: write in progress */
#define VT1724_AC97_READ 0x10 /* W: read, R: read in progress */
#define VT1724_AC97_READY 0x08 /* codec ready status bit */
#define VT1724_AC97_ID_MASK 0x03 /* codec id mask */
#define VT1724_MT_AC97_DATA 0x06 /* word - AC'97 data */
#define VT1724_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */
#define VT1724_MT_PLAYBACK_SIZE 0x14 /* dword - playback size */
#define VT1724_MT_DMA_CONTROL 0x18 /* byte - control */
#define VT1724_PDMA4_START 0x80 /* SPDIF out / PDMA4 start */
#define VT1724_PDMA3_START 0x40 /* PDMA3 start */
#define VT1724_PDMA2_START 0x20 /* PDMA2 start */
#define VT1724_PDMA1_START 0x10 /* PDMA1 start */
#define VT1724_RDMA1_START 0x04 /* RDMA1 start */
#define VT1724_RDMA0_START 0x02 /* RMDA0 start */
#define VT1724_PDMA0_START 0x01 /* MC Interleave / PDMA0 start */
#define VT1724_MT_BURST 0x19 /* Interleaved playback DMA Active streams / PCI burst size */
#define VT1724_MT_DMA_FIFO_ERR 0x1a /*Global playback and record DMA FIFO Underrun/Overrun */
#define VT1724_PDMA4_UNDERRUN 0x80
#define VT1724_PDMA2_UNDERRUN 0x40
#define VT1724_PDMA3_UNDERRUN 0x20
#define VT1724_PDMA1_UNDERRUN 0x10
#define VT1724_RDMA1_UNDERRUN 0x04
#define VT1724_RDMA0_UNDERRUN 0x02
#define VT1724_PDMA0_UNDERRUN 0x01
#define VT1724_MT_DMA_PAUSE 0x1b /*Global playback and record DMA FIFO pause/resume */
#define VT1724_PDMA4_PAUSE 0x80
#define VT1724_PDMA3_PAUSE 0x40
#define VT1724_PDMA2_PAUSE 0x20
#define VT1724_PDMA1_PAUSE 0x10
#define VT1724_RDMA1_PAUSE 0x04
#define VT1724_RDMA0_PAUSE 0x02
#define VT1724_PDMA0_PAUSE 0x01
#define VT1724_MT_PLAYBACK_COUNT 0x1c /* word - playback count */
#define VT1724_MT_CAPTURE_ADDR 0x20 /* dword - capture address */
#define VT1724_MT_CAPTURE_SIZE 0x24 /* word - capture size */
#define VT1724_MT_CAPTURE_COUNT 0x26 /* word - capture count */
#define VT1724_MT_ROUTE_PLAYBACK 0x2c /* word */
#define VT1724_MT_RDMA1_ADDR 0x30 /* dword - RDMA1 capture address */
#define VT1724_MT_RDMA1_SIZE 0x34 /* word - RDMA1 capture size */
#define VT1724_MT_RDMA1_COUNT 0x36 /* word - RDMA1 capture count */
#define VT1724_MT_SPDIF_CTRL 0x3c /* word */
#define VT1724_MT_MONITOR_PEAKINDEX 0x3e /* byte */
#define VT1724_MT_MONITOR_PEAKDATA 0x3f /* byte */
/* concurrent stereo channels */
#define VT1724_MT_PDMA4_ADDR 0x40 /* dword */
#define VT1724_MT_PDMA4_SIZE 0x44 /* word */
#define VT1724_MT_PDMA4_COUNT 0x46 /* word */
#define VT1724_MT_PDMA3_ADDR 0x50 /* dword */
#define VT1724_MT_PDMA3_SIZE 0x54 /* word */
#define VT1724_MT_PDMA3_COUNT 0x56 /* word */
#define VT1724_MT_PDMA2_ADDR 0x60 /* dword */
#define VT1724_MT_PDMA2_SIZE 0x64 /* word */
#define VT1724_MT_PDMA2_COUNT 0x66 /* word */
#define VT1724_MT_PDMA1_ADDR 0x70 /* dword */
#define VT1724_MT_PDMA1_SIZE 0x74 /* word */
#define VT1724_MT_PDMA1_COUNT 0x76 /* word */
unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice, unsigned char dev, unsigned char addr);
void snd_vt1724_write_i2c(struct snd_ice1712 *ice, unsigned char dev, unsigned char addr, unsigned char data);
#endif /* __SOUND_VT1724_H */

1088
sound/pci/ice1712/ews.c Normal file

File diff suppressed because it is too large Load diff

86
sound/pci/ice1712/ews.h Normal file
View file

@ -0,0 +1,86 @@
#ifndef __SOUND_EWS_H
#define __SOUND_EWS_H
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 Takashi Iwai <tiwai@suse.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
*
*/
#define EWS_DEVICE_DESC \
"{TerraTec,EWX 24/96},"\
"{TerraTec,EWS 88MT},"\
"{TerraTec,EWS 88D},"\
"{TerraTec,DMX 6Fire},"\
"{TerraTec,Phase 88}," \
"{terrasoniq,TS 88},"
#define ICE1712_SUBDEVICE_EWX2496 0x3b153011
#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511
#define ICE1712_SUBDEVICE_EWS88MT_NEW 0x3b152511
#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11
#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811
#define ICE1712_SUBDEVICE_PHASE88 0x3b155111
#define ICE1712_SUBDEVICE_TS88 0x3b157c11
/* entry point */
extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
/* TerraTec EWX 24/96 configuration definitions */
#define ICE1712_EWX2496_AK4524_CS 0x01 /* AK4524 chip select; low = active */
#define ICE1712_EWX2496_AIN_SEL 0x02 /* input sensitivity switch; high = louder */
#define ICE1712_EWX2496_AOUT_SEL 0x04 /* output sensitivity switch; high = louder */
#define ICE1712_EWX2496_RW 0x08 /* read/write switch for i2c; high = write */
#define ICE1712_EWX2496_SERIAL_DATA 0x10 /* i2c & ak4524 data */
#define ICE1712_EWX2496_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */
#define ICE1712_EWX2496_TX2 0x40 /* MIDI2 (not used) */
#define ICE1712_EWX2496_RX2 0x80 /* MIDI2 (not used) */
/* TerraTec EWS 88MT/D configuration definitions */
/* RW, SDA snd SCLK are identical with EWX24/96 */
#define ICE1712_EWS88_CS8414_RATE 0x07 /* CS8414 sample rate: gpio 0-2 */
#define ICE1712_EWS88_RW 0x08 /* read/write switch for i2c; high = write */
#define ICE1712_EWS88_SERIAL_DATA 0x10 /* i2c & ak4524 data */
#define ICE1712_EWS88_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */
#define ICE1712_EWS88_TX2 0x40 /* MIDI2 (only on 88D) */
#define ICE1712_EWS88_RX2 0x80 /* MIDI2 (only on 88D) */
/* i2c address */
#define ICE1712_EWS88MT_CS8404_ADDR (0x40>>1)
#define ICE1712_EWS88MT_INPUT_ADDR (0x46>>1)
#define ICE1712_EWS88MT_OUTPUT_ADDR (0x48>>1)
#define ICE1712_EWS88MT_OUTPUT_SENSE 0x40 /* mask */
#define ICE1712_EWS88D_PCF_ADDR (0x40>>1)
/* TerraTec DMX 6Fire configuration definitions */
#define ICE1712_6FIRE_AK4524_CS_MASK 0x07 /* AK4524 chip select #1-#3 */
#define ICE1712_6FIRE_RW 0x08 /* read/write switch for i2c; high = write */
#define ICE1712_6FIRE_SERIAL_DATA 0x10 /* i2c & ak4524 data */
#define ICE1712_6FIRE_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */
#define ICE1712_6FIRE_TX2 0x40 /* MIDI2 */
#define ICE1712_6FIRE_RX2 0x80 /* MIDI2 */
#define ICE1712_6FIRE_PCF9554_ADDR (0x40>>1)
#define ICE1712_6FIRE_CS8427_ADDR (0x22)
#endif /* __SOUND_EWS_H */

View file

@ -0,0 +1,359 @@
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for Hoontech STDSP24
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include "ice1712.h"
#include "hoontech.h"
/* Hoontech-specific setting */
struct hoontech_spec {
unsigned char boxbits[4];
unsigned int config;
unsigned short boxconfig[4];
};
static void snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
{
byte |= ICE1712_STDSP24_CLOCK_BIT;
udelay(100);
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
byte &= ~ICE1712_STDSP24_CLOCK_BIT;
udelay(100);
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
byte |= ICE1712_STDSP24_CLOCK_BIT;
udelay(100);
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
}
static void snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
/* select box */
ICE1712_STDSP24_0_BOX(spec->boxbits, box);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
/* prepare for write */
if (chn == 3)
ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
udelay(100);
if (chn == 3) {
ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
} else {
switch (chn) {
case 0: ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break;
case 1: ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break;
case 2: ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break;
}
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
}
udelay(100);
ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
udelay(100);
ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
/* select box */
ICE1712_STDSP24_0_BOX(spec->boxbits, box);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
ICE1712_STDSP24_2_MIDI1(spec->boxbits, master);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
udelay(100);
ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
mdelay(10);
ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
mutex_unlock(&ice->gpio_mutex);
}
static void snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
mutex_unlock(&ice->gpio_mutex);
}
static int snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
{
struct hoontech_spec *spec;
int box, chn;
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0);
ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1);
ICE1712_STDSP24_0_BOX(spec->boxbits, 0);
ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0);
ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1);
ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1);
ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2);
ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1);
ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3);
ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1);
ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0);
ICE1712_STDSP24_3_MUTE(spec->boxbits, 1);
ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
/* let's go - activate only functions in first box */
spec->config = 0;
/* ICE1712_STDSP24_MUTE |
ICE1712_STDSP24_INSEL |
ICE1712_STDSP24_DAREAR; */
/* These boxconfigs have caused problems in the past.
* The code is not optimal, but should now enable a working config to
* be achieved.
* ** MIDI IN can only be configured on one box **
* ICE1712_STDSP24_BOX_MIDI1 needs to be set for that box.
* Tests on a ADAC2000 box suggest the box config flags do not
* work as would be expected, and the inputs are crossed.
* Setting ICE1712_STDSP24_BOX_MIDI1 and ICE1712_STDSP24_BOX_MIDI2
* on the same box connects MIDI-In to both 401 uarts; both outputs
* are then active on all boxes.
* The default config here sets up everything on the first box.
* Alan Horstmann 5.2.2008
*/
spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
ICE1712_STDSP24_BOX_CHN2 |
ICE1712_STDSP24_BOX_CHN3 |
ICE1712_STDSP24_BOX_CHN4 |
ICE1712_STDSP24_BOX_MIDI1 |
ICE1712_STDSP24_BOX_MIDI2;
spec->boxconfig[1] =
spec->boxconfig[2] =
spec->boxconfig[3] = 0;
snd_ice1712_stdsp24_darear(ice,
(spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
snd_ice1712_stdsp24_mute(ice,
(spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
snd_ice1712_stdsp24_insel(ice,
(spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
for (box = 0; box < 4; box++) {
if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
snd_ice1712_stdsp24_midi2(ice, 1);
for (chn = 0; chn < 4; chn++)
snd_ice1712_stdsp24_box_channel(ice, box, chn,
(spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1)
snd_ice1712_stdsp24_box_midi(ice, box, 1);
}
return 0;
}
/*
* AK4524 access
*/
/* start callback for STDSP24 with modified hardware */
static void stdsp24_ak4524_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ice1712 *ice = ak->private_data[0];
unsigned char tmp;
snd_ice1712_save_gpio_status(ice);
tmp = ICE1712_STDSP24_SERIAL_DATA |
ICE1712_STDSP24_SERIAL_CLOCK |
ICE1712_STDSP24_AK4524_CS;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
ice->gpio.direction | tmp);
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
}
static int snd_ice1712_value_init(struct snd_ice1712 *ice)
{
/* Hoontech STDSP24 with modified hardware */
static struct snd_akm4xxx akm_stdsp24_mv = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
.ops = {
.lock = stdsp24_ak4524_lock
}
};
static struct snd_ak4xxx_private akm_stdsp24_mv_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_STDSP24_SERIAL_DATA,
.clk_mask = ICE1712_STDSP24_SERIAL_CLOCK,
.cs_mask = ICE1712_STDSP24_AK4524_CS,
.cs_addr = ICE1712_STDSP24_AK4524_CS,
.cs_none = 0,
.add_flags = 0,
};
int err;
struct snd_akm4xxx *ak;
/* set the analog DACs */
ice->num_total_dacs = 2;
/* set the analog ADCs */
ice->num_total_adcs = 2;
/* analog section */
ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ak)
return -ENOMEM;
ice->akm_codecs = 1;
err = snd_ice1712_akm4xxx_init(ak, &akm_stdsp24_mv, &akm_stdsp24_mv_priv, ice);
if (err < 0)
return err;
/* ak4524 controls */
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
return 0;
}
static int snd_ice1712_ez8_init(struct snd_ice1712 *ice)
{
ice->gpio.write_mask = ice->eeprom.gpiomask;
ice->gpio.direction = ice->eeprom.gpiodir;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
return 0;
}
/* entry point */
struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_STDSP24,
.name = "Hoontech SoundTrack Audio DSP24",
.model = "dsp24",
.chip_init = snd_ice1712_hoontech_init,
.mpu401_1_name = "MIDI-1 Hoontech/STA DSP24",
.mpu401_2_name = "MIDI-2 Hoontech/STA DSP24",
},
{
.subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE, /* a dummy id */
.name = "Hoontech SoundTrack Audio DSP24 Value",
.model = "dsp24_value",
.chip_init = snd_ice1712_value_init,
},
{
.subvendor = ICE1712_SUBDEVICE_STDSP24_MEDIA7_1,
.name = "Hoontech STA DSP24 Media 7.1",
.model = "dsp24_71",
.chip_init = snd_ice1712_hoontech_init,
},
{
.subvendor = ICE1712_SUBDEVICE_EVENT_EZ8, /* a dummy id */
.name = "Event Electronics EZ8",
.model = "ez8",
.chip_init = snd_ice1712_ez8_init,
},
{ } /* terminator */
};

View file

@ -0,0 +1,77 @@
#ifndef __SOUND_HOONTECH_H
#define __SOUND_HOONTECH_H
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for Hoontech STDSP24
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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
*
*/
#define HOONTECH_DEVICE_DESC \
"{Hoontech,SoundTrack DSP 24}," \
"{Hoontech,SoundTrack DSP 24 Value}," \
"{Hoontech,SoundTrack DSP 24 Media 7.1}," \
"{Event Electronics,EZ8},"
#define ICE1712_SUBDEVICE_STDSP24 0x12141217 /* Hoontech SoundTrack Audio DSP 24 */
#define ICE1712_SUBDEVICE_STDSP24_VALUE 0x00010010 /* A dummy id for Hoontech SoundTrack Audio DSP 24 Value */
#define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */
#define ICE1712_SUBDEVICE_EVENT_EZ8 0x00010001 /* A dummy id for EZ8 */
extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
#define ICE1712_STDSP24_0_BOX(r, x) r[0] = ((r[0] & ~3) | ((x)&3))
#define ICE1712_STDSP24_0_DAREAR(r, x) r[0] = ((r[0] & ~4) | (((x)&1)<<2))
#define ICE1712_STDSP24_1_CHN1(r, x) r[1] = ((r[1] & ~1) | ((x)&1))
#define ICE1712_STDSP24_1_CHN2(r, x) r[1] = ((r[1] & ~2) | (((x)&1)<<1))
#define ICE1712_STDSP24_1_CHN3(r, x) r[1] = ((r[1] & ~4) | (((x)&1)<<2))
#define ICE1712_STDSP24_2_CHN4(r, x) r[2] = ((r[2] & ~1) | ((x)&1))
#define ICE1712_STDSP24_2_MIDIIN(r, x) r[2] = ((r[2] & ~2) | (((x)&1)<<1))
#define ICE1712_STDSP24_2_MIDI1(r, x) r[2] = ((r[2] & ~4) | (((x)&1)<<2))
#define ICE1712_STDSP24_3_MIDI2(r, x) r[3] = ((r[3] & ~1) | ((x)&1))
#define ICE1712_STDSP24_3_MUTE(r, x) r[3] = ((r[3] & ~2) | (((x)&1)<<1))
#define ICE1712_STDSP24_3_INSEL(r, x) r[3] = ((r[3] & ~4) | (((x)&1)<<2))
#define ICE1712_STDSP24_SET_ADDR(r, a) r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3))
#define ICE1712_STDSP24_CLOCK(r, a, c) r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5))
#define ICE1712_STDSP24_CLOCK_BIT (1<<5)
/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */
#define ICE1712_STDSP24_DAREAR (1<<0)
#define ICE1712_STDSP24_MUTE (1<<1)
#define ICE1712_STDSP24_INSEL (1<<2)
#define ICE1712_STDSP24_BOX_CHN1 (1<<0) /* input channel 1 */
#define ICE1712_STDSP24_BOX_CHN2 (1<<1) /* input channel 2 */
#define ICE1712_STDSP24_BOX_CHN3 (1<<2) /* input channel 3 */
#define ICE1712_STDSP24_BOX_CHN4 (1<<3) /* input channel 4 */
#define ICE1712_STDSP24_BOX_MIDI1 (1<<8)
#define ICE1712_STDSP24_BOX_MIDI2 (1<<9)
/* Hoontech SoundTrack Audio DSP 24 Value definitions for modified hardware */
#define ICE1712_STDSP24_AK4524_CS 0x03 /* AK4524 chip select; low = active */
#define ICE1712_STDSP24_SERIAL_DATA 0x0c /* ak4524 data */
#define ICE1712_STDSP24_SERIAL_CLOCK 0x30 /* ak4524 clock */
#endif /* __SOUND_HOONTECH_H */

2931
sound/pci/ice1712/ice1712.c Normal file

File diff suppressed because it is too large Load diff

538
sound/pci/ice1712/ice1712.h Normal file
View file

@ -0,0 +1,538 @@
#ifndef __SOUND_ICE1712_H
#define __SOUND_ICE1712_H
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
* 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/io.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
#include <sound/rawmidi.h>
#include <sound/i2c.h>
#include <sound/ak4xxx-adda.h>
#include <sound/ak4114.h>
#include <sound/pt2258.h>
#include <sound/pcm.h>
#include <sound/mpu401.h>
/*
* Direct registers
*/
#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
#define ICE1712_REG_CONTROL 0x00 /* byte */
#define ICE1712_RESET 0x80 /* soft reset whole chip */
#define ICE1712_SERR_ASSERT_DS_DMA 0x40 /* disabled SERR# assertion for the DS DMA Ch-C irq otherwise enabled */
#define ICE1712_DOS_VOL 0x10 /* DOS WT/FM volume control */
#define ICE1712_SERR_LEVEL 0x08 /* SERR# level otherwise edge */
#define ICE1712_SERR_ASSERT_SB 0x02 /* disabled SERR# assertion for SB irq otherwise enabled */
#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */
#define ICE1712_REG_IRQMASK 0x01 /* byte */
#define ICE1712_IRQ_MPU1 0x80 /* MIDI irq mask */
#define ICE1712_IRQ_TIMER 0x40 /* Timer mask */
#define ICE1712_IRQ_MPU2 0x20 /* Secondary MIDI irq mask */
#define ICE1712_IRQ_PROPCM 0x10 /* professional multi-track */
#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */
#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */
#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */
#define ICE1712_IRQ_CONPBK 0x01 /* consumer playback */
#define ICE1712_REG_IRQSTAT 0x02 /* byte */
/* look to ICE1712_IRQ_* */
#define ICE1712_REG_INDEX 0x03 /* byte - indirect CCIxx regs */
#define ICE1712_REG_DATA 0x04 /* byte - indirect CCIxx regs */
#define ICE1712_REG_NMI_STAT1 0x05 /* byte */
#define ICE1712_REG_NMI_DATA 0x06 /* byte */
#define ICE1712_REG_NMI_INDEX 0x07 /* byte */
#define ICE1712_REG_AC97_INDEX 0x08 /* byte */
#define ICE1712_REG_AC97_CMD 0x09 /* byte */
#define ICE1712_AC97_COLD 0x80 /* cold reset */
#define ICE1712_AC97_WARM 0x40 /* warm reset */
#define ICE1712_AC97_WRITE 0x20 /* W: write, R: write in progress */
#define ICE1712_AC97_READ 0x10 /* W: read, R: read in progress */
#define ICE1712_AC97_READY 0x08 /* codec ready status bit */
#define ICE1712_AC97_PBK_VSR 0x02 /* playback VSR */
#define ICE1712_AC97_CAP_VSR 0x01 /* capture VSR */
#define ICE1712_REG_AC97_DATA 0x0a /* word (little endian) */
#define ICE1712_REG_MPU1_CTRL 0x0c /* byte */
#define ICE1712_REG_MPU1_DATA 0x0d /* byte */
#define ICE1712_REG_I2C_DEV_ADDR 0x10 /* byte */
#define ICE1712_I2C_WRITE 0x01 /* write direction */
#define ICE1712_REG_I2C_BYTE_ADDR 0x11 /* byte */
#define ICE1712_REG_I2C_DATA 0x12 /* byte */
#define ICE1712_REG_I2C_CTRL 0x13 /* byte */
#define ICE1712_I2C_EEPROM 0x80 /* EEPROM exists */
#define ICE1712_I2C_BUSY 0x01 /* busy bit */
#define ICE1712_REG_CONCAP_ADDR 0x14 /* dword - consumer capture */
#define ICE1712_REG_CONCAP_COUNT 0x18 /* word - current/base count */
#define ICE1712_REG_SERR_SHADOW 0x1b /* byte */
#define ICE1712_REG_MPU2_CTRL 0x1c /* byte */
#define ICE1712_REG_MPU2_DATA 0x1d /* byte */
#define ICE1712_REG_TIMER 0x1e /* word */
/*
* Indirect registers
*/
#define ICE1712_IREG_PBK_COUNT_LO 0x00
#define ICE1712_IREG_PBK_COUNT_HI 0x01
#define ICE1712_IREG_PBK_CTRL 0x02
#define ICE1712_IREG_PBK_LEFT 0x03 /* left volume */
#define ICE1712_IREG_PBK_RIGHT 0x04 /* right volume */
#define ICE1712_IREG_PBK_SOFT 0x05 /* soft volume */
#define ICE1712_IREG_PBK_RATE_LO 0x06
#define ICE1712_IREG_PBK_RATE_MID 0x07
#define ICE1712_IREG_PBK_RATE_HI 0x08
#define ICE1712_IREG_CAP_COUNT_LO 0x10
#define ICE1712_IREG_CAP_COUNT_HI 0x11
#define ICE1712_IREG_CAP_CTRL 0x12
#define ICE1712_IREG_GPIO_DATA 0x20
#define ICE1712_IREG_GPIO_WRITE_MASK 0x21
#define ICE1712_IREG_GPIO_DIRECTION 0x22
#define ICE1712_IREG_CONSUMER_POWERDOWN 0x30
#define ICE1712_IREG_PRO_POWERDOWN 0x31
/*
* Consumer section direct DMA registers
*/
#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
#define ICE1712_DS_INTMASK 0x00 /* word - interrupt mask */
#define ICE1712_DS_INTSTAT 0x02 /* word - interrupt status */
#define ICE1712_DS_DATA 0x04 /* dword - channel data */
#define ICE1712_DS_INDEX 0x08 /* dword - channel index */
/*
* Consumer section channel registers
*/
#define ICE1712_DSC_ADDR0 0x00 /* dword - base address 0 */
#define ICE1712_DSC_COUNT0 0x01 /* word - count 0 */
#define ICE1712_DSC_ADDR1 0x02 /* dword - base address 1 */
#define ICE1712_DSC_COUNT1 0x03 /* word - count 1 */
#define ICE1712_DSC_CONTROL 0x04 /* byte - control & status */
#define ICE1712_BUFFER1 0x80 /* buffer1 is active */
#define ICE1712_BUFFER1_AUTO 0x40 /* buffer1 auto init */
#define ICE1712_BUFFER0_AUTO 0x20 /* buffer0 auto init */
#define ICE1712_FLUSH 0x10 /* flush FIFO */
#define ICE1712_STEREO 0x08 /* stereo */
#define ICE1712_16BIT 0x04 /* 16-bit data */
#define ICE1712_PAUSE 0x02 /* pause */
#define ICE1712_START 0x01 /* start */
#define ICE1712_DSC_RATE 0x05 /* dword - rate */
#define ICE1712_DSC_VOLUME 0x06 /* word - volume control */
/*
* Professional multi-track direct control registers
*/
#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x)
#define ICE1712_MT_IRQ 0x00 /* byte - interrupt mask */
#define ICE1712_MULTI_CAPTURE 0x80 /* capture IRQ */
#define ICE1712_MULTI_PLAYBACK 0x40 /* playback IRQ */
#define ICE1712_MULTI_CAPSTATUS 0x02 /* capture IRQ status */
#define ICE1712_MULTI_PBKSTATUS 0x01 /* playback IRQ status */
#define ICE1712_MT_RATE 0x01 /* byte - sampling rate select */
#define ICE1712_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */
#define ICE1712_MT_I2S_FORMAT 0x02 /* byte - I2S data format */
#define ICE1712_MT_AC97_INDEX 0x04 /* byte - AC'97 index */
#define ICE1712_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */
/* look to ICE1712_AC97_* */
#define ICE1712_MT_AC97_DATA 0x06 /* word - AC'97 data */
#define ICE1712_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */
#define ICE1712_MT_PLAYBACK_SIZE 0x14 /* word - playback size */
#define ICE1712_MT_PLAYBACK_COUNT 0x16 /* word - playback count */
#define ICE1712_MT_PLAYBACK_CONTROL 0x18 /* byte - control */
#define ICE1712_CAPTURE_START_SHADOW 0x04 /* capture start */
#define ICE1712_PLAYBACK_PAUSE 0x02 /* playback pause */
#define ICE1712_PLAYBACK_START 0x01 /* playback start */
#define ICE1712_MT_CAPTURE_ADDR 0x20 /* dword - capture address */
#define ICE1712_MT_CAPTURE_SIZE 0x24 /* word - capture size */
#define ICE1712_MT_CAPTURE_COUNT 0x26 /* word - capture count */
#define ICE1712_MT_CAPTURE_CONTROL 0x28 /* byte - control */
#define ICE1712_CAPTURE_START 0x01 /* capture start */
#define ICE1712_MT_ROUTE_PSDOUT03 0x30 /* word */
#define ICE1712_MT_ROUTE_SPDOUT 0x32 /* word */
#define ICE1712_MT_ROUTE_CAPTURE 0x34 /* dword */
#define ICE1712_MT_MONITOR_VOLUME 0x38 /* word */
#define ICE1712_MT_MONITOR_INDEX 0x3a /* byte */
#define ICE1712_MT_MONITOR_RATE 0x3b /* byte */
#define ICE1712_MT_MONITOR_ROUTECTRL 0x3c /* byte */
#define ICE1712_ROUTE_AC97 0x01 /* route digital mixer output to AC'97 */
#define ICE1712_MT_MONITOR_PEAKINDEX 0x3e /* byte */
#define ICE1712_MT_MONITOR_PEAKDATA 0x3f /* byte */
/*
* Codec configuration bits
*/
/* PCI[60] System Configuration */
#define ICE1712_CFG_CLOCK 0xc0
#define ICE1712_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */
#define ICE1712_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */
#define ICE1712_CFG_EXT 0x80 /* external clock */
#define ICE1712_CFG_2xMPU401 0x20 /* two MPU401 UARTs */
#define ICE1712_CFG_NO_CON_AC97 0x10 /* consumer AC'97 codec is not present */
#define ICE1712_CFG_ADC_MASK 0x0c /* one, two, three, four stereo ADCs */
#define ICE1712_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */
/* PCI[61] AC-Link Configuration */
#define ICE1712_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */
#define ICE1712_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */
/* PCI[62] I2S Features */
#define ICE1712_CFG_I2S_VOLUME 0x80 /* volume/mute capability */
#define ICE1712_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */
#define ICE1712_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */
#define ICE1712_CFG_I2S_OTHER 0x0f /* other I2S IDs */
/* PCI[63] S/PDIF Configuration */
#define ICE1712_CFG_I2S_CHIPID 0xfc /* I2S chip ID */
#define ICE1712_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */
#define ICE1712_CFG_SPDIF_OUT 0x01 /* S/PDIF output is present */
/*
* DMA mode values
* identical with DMA_XXX on i386 architecture.
*/
#define ICE1712_DMA_MODE_WRITE 0x48
#define ICE1712_DMA_AUTOINIT 0x10
/*
*
*/
struct snd_ice1712;
struct snd_ice1712_eeprom {
unsigned int subvendor; /* PCI[2c-2f] */
unsigned char size; /* size of EEPROM image in bytes */
unsigned char version; /* must be 1 (or 2 for vt1724) */
unsigned char data[32];
unsigned int gpiomask;
unsigned int gpiostate;
unsigned int gpiodir;
};
enum {
ICE_EEP1_CODEC = 0, /* 06 */
ICE_EEP1_ACLINK, /* 07 */
ICE_EEP1_I2SID, /* 08 */
ICE_EEP1_SPDIF, /* 09 */
ICE_EEP1_GPIO_MASK, /* 0a */
ICE_EEP1_GPIO_STATE, /* 0b */
ICE_EEP1_GPIO_DIR, /* 0c */
ICE_EEP1_AC97_MAIN_LO, /* 0d */
ICE_EEP1_AC97_MAIN_HI, /* 0e */
ICE_EEP1_AC97_PCM_LO, /* 0f */
ICE_EEP1_AC97_PCM_HI, /* 10 */
ICE_EEP1_AC97_REC_LO, /* 11 */
ICE_EEP1_AC97_REC_HI, /* 12 */
ICE_EEP1_AC97_RECSRC, /* 13 */
ICE_EEP1_DAC_ID, /* 14 */
ICE_EEP1_DAC_ID1,
ICE_EEP1_DAC_ID2,
ICE_EEP1_DAC_ID3,
ICE_EEP1_ADC_ID, /* 18 */
ICE_EEP1_ADC_ID1,
ICE_EEP1_ADC_ID2,
ICE_EEP1_ADC_ID3
};
#define ice_has_con_ac97(ice) (!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97))
struct snd_ak4xxx_private {
unsigned int cif:1; /* CIF mode */
unsigned char caddr; /* C0 and C1 bits */
unsigned int data_mask; /* DATA gpio bit */
unsigned int clk_mask; /* CLK gpio bit */
unsigned int cs_mask; /* bit mask for select/deselect address */
unsigned int cs_addr; /* bits to select address */
unsigned int cs_none; /* bits to deselect address */
unsigned int add_flags; /* additional bits at init */
unsigned int mask_flags; /* total mask bits */
struct snd_akm4xxx_ops {
void (*set_rate_val)(struct snd_akm4xxx *ak, unsigned int rate);
} ops;
};
struct snd_ice1712_spdif {
unsigned char cs8403_bits;
unsigned char cs8403_stream_bits;
struct snd_kcontrol *stream_ctl;
struct snd_ice1712_spdif_ops {
void (*open)(struct snd_ice1712 *, struct snd_pcm_substream *);
void (*setup_rate)(struct snd_ice1712 *, int rate);
void (*close)(struct snd_ice1712 *, struct snd_pcm_substream *);
void (*default_get)(struct snd_ice1712 *, struct snd_ctl_elem_value *ucontrol);
int (*default_put)(struct snd_ice1712 *, struct snd_ctl_elem_value *ucontrol);
void (*stream_get)(struct snd_ice1712 *, struct snd_ctl_elem_value *ucontrol);
int (*stream_put)(struct snd_ice1712 *, struct snd_ctl_elem_value *ucontrol);
} ops;
};
struct snd_ice1712_card_info;
struct snd_ice1712 {
unsigned long conp_dma_size;
unsigned long conc_dma_size;
unsigned long prop_dma_size;
unsigned long proc_dma_size;
int irq;
unsigned long port;
unsigned long ddma_port;
unsigned long dmapath_port;
unsigned long profi_port;
struct pci_dev *pci;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm *pcm_ds;
struct snd_pcm *pcm_pro;
struct snd_pcm_substream *playback_con_substream;
struct snd_pcm_substream *playback_con_substream_ds[6];
struct snd_pcm_substream *capture_con_substream;
struct snd_pcm_substream *playback_pro_substream;
struct snd_pcm_substream *capture_pro_substream;
unsigned int playback_pro_size;
unsigned int capture_pro_size;
unsigned int playback_con_virt_addr[6];
unsigned int playback_con_active_buf[6];
unsigned int capture_con_virt_addr;
unsigned int ac97_ext_id;
struct snd_ac97 *ac97;
struct snd_rawmidi *rmidi[2];
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;
struct snd_ice1712_eeprom eeprom;
struct snd_ice1712_card_info *card_info;
unsigned int pro_volumes[20];
unsigned int omni:1; /* Delta Omni I/O */
unsigned int dxr_enable:1; /* Terratec DXR enable for DMX6FIRE */
unsigned int vt1724:1;
unsigned int vt1720:1;
unsigned int has_spdif:1; /* VT1720/4 - has SPDIF I/O */
unsigned int force_pdma4:1; /* VT1720/4 - PDMA4 as non-spdif */
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 */
struct mutex open_mutex;
struct snd_pcm_substream *pcm_reserved[4];
struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */
unsigned int akm_codecs;
struct snd_akm4xxx *akm;
struct snd_ice1712_spdif spdif;
struct mutex i2c_mutex; /* I2C mutex for ICE1724 registers */
struct snd_i2c_bus *i2c; /* I2C bus */
struct snd_i2c_device *cs8427; /* CS8427 I2C device */
unsigned int cs8427_timeout; /* CS8427 reset timeout in HZ/100 */
struct ice1712_gpio {
unsigned int direction; /* current direction bits */
unsigned int write_mask; /* current mask bits */
unsigned int saved[2]; /* for ewx_i2c */
/* operators */
void (*set_mask)(struct snd_ice1712 *ice, unsigned int data);
unsigned int (*get_mask)(struct snd_ice1712 *ice);
void (*set_dir)(struct snd_ice1712 *ice, unsigned int data);
unsigned int (*get_dir)(struct snd_ice1712 *ice);
void (*set_data)(struct snd_ice1712 *ice, unsigned int data);
unsigned int (*get_data)(struct snd_ice1712 *ice);
/* misc operators - move to another place? */
void (*set_pro_rate)(struct snd_ice1712 *ice, unsigned int rate);
void (*i2s_mclk_changed)(struct snd_ice1712 *ice);
} gpio;
struct mutex gpio_mutex;
/* other board-specific data */
void *spec;
/* VT172x specific */
int pro_rate_default;
int (*is_spdif_master)(struct snd_ice1712 *ice);
unsigned int (*get_rate)(struct snd_ice1712 *ice);
void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
int (*set_spdif_clock)(struct snd_ice1712 *ice, int type);
int (*get_spdif_master_type)(struct snd_ice1712 *ice);
const char * const *ext_clock_names;
int ext_clock_count;
void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
#ifdef CONFIG_PM_SLEEP
int (*pm_suspend)(struct snd_ice1712 *);
int (*pm_resume)(struct snd_ice1712 *);
unsigned int pm_suspend_enabled:1;
unsigned int pm_saved_is_spdif_master:1;
unsigned int pm_saved_spdif_ctrl;
unsigned char pm_saved_spdif_cfg;
unsigned int pm_saved_route;
#endif
};
/*
* gpio access functions
*/
static inline void snd_ice1712_gpio_set_dir(struct snd_ice1712 *ice, unsigned int bits)
{
ice->gpio.set_dir(ice, bits);
}
static inline unsigned int snd_ice1712_gpio_get_dir(struct snd_ice1712 *ice)
{
return ice->gpio.get_dir(ice);
}
static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits)
{
ice->gpio.set_mask(ice, bits);
}
static inline void snd_ice1712_gpio_write(struct snd_ice1712 *ice, unsigned int val)
{
ice->gpio.set_data(ice, val);
}
static inline unsigned int snd_ice1712_gpio_read(struct snd_ice1712 *ice)
{
return ice->gpio.get_data(ice);
}
/*
* save and restore gpio status
* The access to gpio will be protected by mutex, so don't forget to
* restore!
*/
static inline void snd_ice1712_save_gpio_status(struct snd_ice1712 *ice)
{
mutex_lock(&ice->gpio_mutex);
ice->gpio.saved[0] = ice->gpio.direction;
ice->gpio.saved[1] = ice->gpio.write_mask;
}
static inline void snd_ice1712_restore_gpio_status(struct snd_ice1712 *ice)
{
ice->gpio.set_dir(ice, ice->gpio.saved[0]);
ice->gpio.set_mask(ice, ice->gpio.saved[1]);
ice->gpio.direction = ice->gpio.saved[0];
ice->gpio.write_mask = ice->gpio.saved[1];
mutex_unlock(&ice->gpio_mutex);
}
/* for bit controls */
#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \
{ .iface = xiface, .name = xname, .access = xaccess, .info = snd_ctl_boolean_mono_info, \
.get = snd_ice1712_gpio_get, .put = snd_ice1712_gpio_put, \
.private_value = mask | (invert << 24) }
int snd_ice1712_gpio_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
/*
* set gpio direction, write mask and data
*/
static inline void snd_ice1712_gpio_write_bits(struct snd_ice1712 *ice,
unsigned int mask, unsigned int bits)
{
unsigned val;
ice->gpio.direction |= mask;
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
val = snd_ice1712_gpio_read(ice);
val &= ~mask;
val |= mask & bits;
snd_ice1712_gpio_write(ice, val);
}
static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
unsigned int mask)
{
ice->gpio.direction &= ~mask;
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
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);
void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice);
int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);
int snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr);
static inline void snd_ice1712_write(struct snd_ice1712 *ice, u8 addr, u8 data)
{
outb(addr, ICEREG(ice, INDEX));
outb(data, ICEREG(ice, DATA));
}
static inline u8 snd_ice1712_read(struct snd_ice1712 *ice, u8 addr)
{
outb(addr, ICEREG(ice, INDEX));
return inb(ICEREG(ice, DATA));
}
/*
* entry pointer
*/
struct snd_ice1712_card_info {
unsigned int subvendor;
const char *name;
const char *model;
const char *driver;
int (*chip_init)(struct snd_ice1712 *);
void (*chip_exit)(struct snd_ice1712 *);
int (*build_controls)(struct snd_ice1712 *);
unsigned int no_mpu401:1;
unsigned int mpu401_1_info_flags;
unsigned int mpu401_2_info_flags;
const char *mpu401_1_name;
const char *mpu401_2_name;
const unsigned int eeprom_size;
const unsigned char *eeprom_data;
};
#endif /* __SOUND_ICE1712_H */

2910
sound/pci/ice1712/ice1724.c Normal file

File diff suppressed because it is too large Load diff

700
sound/pci/ice1712/juli.c Normal file
View file

@ -0,0 +1,700 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for ESI Juli@ cards
*
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
* 2008 Pavel Hofman <dustin@seznam.cz>
*
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "juli.h"
struct juli_spec {
struct ak4114 *ak4114;
unsigned int analog:1;
};
/*
* chip addresses on I2C bus
*/
#define AK4114_ADDR 0x20 /* S/PDIF receiver */
#define AK4358_ADDR 0x22 /* DAC */
/*
* Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is
* supplied by external clock provided by Xilinx array and MK73-1 PLL frequency
* multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx.
*
* The clock circuitry is supplied by the two ice1724 crystals. This
* arrangement allows to generate independent clock signal for AK4114's input
* rate detection circuit. As a result, Juli, unlike most other
* ice1724+ak4114-based cards, detects spdif input rate correctly.
* This fact is applied in the driver, allowing to modify PCM stream rate
* parameter according to the actual input rate.
*
* Juli uses the remaining three stereo-channels of its DAC to optionally
* monitor analog input, digital input, and digital output. The corresponding
* I2S signals are routed by Xilinx, controlled by GPIOs.
*
* The master mute is implemented using output muting transistors (GPIO) in
* combination with smuting the DAC.
*
* The card itself has no HW master volume control, implemented using the
* vmaster control.
*
* TODO:
* researching and fixing the input monitors
*/
/*
* GPIO pins
*/
#define GPIO_FREQ_MASK (3<<0)
#define GPIO_FREQ_32KHZ (0<<0)
#define GPIO_FREQ_44KHZ (1<<0)
#define GPIO_FREQ_48KHZ (2<<0)
#define GPIO_MULTI_MASK (3<<2)
#define GPIO_MULTI_4X (0<<2)
#define GPIO_MULTI_2X (1<<2)
#define GPIO_MULTI_1X (2<<2) /* also external */
#define GPIO_MULTI_HALF (3<<2)
#define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */
#define GPIO_CLOCK_MASK (1<<4)
#define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */
#define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */
#define GPIO_AK5385A_CKS0 (1<<8)
#define GPIO_AK5385A_DFS1 (1<<9)
#define GPIO_AK5385A_DFS0 (1<<10)
#define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */
#define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */
#define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */
#define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */
#define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */
#define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \
GPIO_CLOCK_MASK)
#define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \
GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1)
#define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
#define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \
GPIO_INTERNAL_CLOCK)
#define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \
GPIO_INTERNAL_CLOCK)
/*
* Initial setup of the conversion array GPIO <-> rate
*/
static unsigned int juli_rates[] = {
16000, 22050, 24000, 32000,
44100, 48000, 64000, 88200,
96000, 176400, 192000,
};
static unsigned int gpio_vals[] = {
GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000,
GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200,
GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000,
};
static struct snd_pcm_hw_constraint_list juli_rates_info = {
.count = ARRAY_SIZE(juli_rates),
.list = juli_rates,
.mask = 0,
};
static int get_gpio_val(int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(juli_rates); i++)
if (juli_rates[i] == rate)
return gpio_vals[i];
return 0;
}
static void juli_ak4114_write(void *private_data, unsigned char reg,
unsigned char val)
{
snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR,
reg, val);
}
static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
{
return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data,
AK4114_ADDR, reg);
}
/*
* If SPDIF capture and slaved to SPDIF-IN, setting runtime rate
* to the external rate
*/
static void juli_spdif_in_open(struct snd_ice1712 *ice,
struct snd_pcm_substream *substream)
{
struct juli_spec *spec = ice->spec;
struct snd_pcm_runtime *runtime = substream->runtime;
int rate;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
!ice->is_spdif_master(ice))
return;
rate = snd_ak4114_external_rate(spec->ak4114);
if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) {
runtime->hw.rate_min = rate;
runtime->hw.rate_max = rate;
}
}
/*
* AK4358 section
*/
static void juli_akm_lock(struct snd_akm4xxx *ak, int chip)
{
}
static void juli_akm_unlock(struct snd_akm4xxx *ak, int chip)
{
}
static void juli_akm_write(struct snd_akm4xxx *ak, int chip,
unsigned char addr, unsigned char data)
{
struct snd_ice1712 *ice = ak->private_data[0];
if (snd_BUG_ON(chip))
return;
snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data);
}
/*
* change the rate of envy24HT, AK4358, AK5385
*/
static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
unsigned char old, tmp, ak4358_dfs;
unsigned int ak5385_pins, old_gpio, new_gpio;
struct snd_ice1712 *ice = ak->private_data[0];
struct juli_spec *spec = ice->spec;
if (rate == 0) /* no hint - S/PDIF input is master or the new spdif
input rate undetected, simply return */
return;
/* adjust DFS on codecs */
if (rate > 96000) {
ak4358_dfs = 2;
ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0;
} else if (rate > 48000) {
ak4358_dfs = 1;
ak5385_pins = GPIO_AK5385A_DFS0;
} else {
ak4358_dfs = 0;
ak5385_pins = 0;
}
/* AK5385 first, since it requires cold reset affecting both codecs */
old_gpio = ice->gpio.get_data(ice);
new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins;
/* dev_dbg(ice->card->dev, "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
new_gpio); */
ice->gpio.set_data(ice, new_gpio);
/* cold reset */
old = inb(ICEMT1724(ice, AC97_CMD));
outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD));
udelay(1);
outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD));
/* AK4358 */
/* set new value, reset DFS */
tmp = snd_akm4xxx_get(ak, 0, 2);
snd_akm4xxx_reset(ak, 1);
tmp = snd_akm4xxx_get(ak, 0, 2);
tmp &= ~(0x03 << 4);
tmp |= ak4358_dfs << 4;
snd_akm4xxx_set(ak, 0, 2, tmp);
snd_akm4xxx_reset(ak, 0);
/* reinit ak4114 */
snd_ak4114_reinit(spec->ak4114);
}
#define AK_DAC(xname, xch) { .name = xname, .num_channels = xch }
#define PCM_VOLUME "PCM Playback Volume"
#define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume"
#define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume"
#define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume"
static const struct snd_akm4xxx_dac_channel juli_dac[] = {
AK_DAC(PCM_VOLUME, 2),
AK_DAC(MONITOR_AN_IN_VOLUME, 2),
AK_DAC(MONITOR_DIG_OUT_VOLUME, 2),
AK_DAC(MONITOR_DIG_IN_VOLUME, 2),
};
static struct snd_akm4xxx akm_juli_dac = {
.type = SND_AK4358,
.num_dacs = 8, /* DAC1 - analog out
DAC2 - analog in monitor
DAC3 - digital out monitor
DAC4 - digital in monitor
*/
.ops = {
.lock = juli_akm_lock,
.unlock = juli_akm_unlock,
.write = juli_akm_write,
.set_rate_val = juli_akm_set_rate_val
},
.dac_info = juli_dac,
};
#define juli_mute_info snd_ctl_boolean_mono_info
static int juli_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value;
if (kcontrol->private_value == GPIO_MUTE_CONTROL)
/* val 0 = signal on */
ucontrol->value.integer.value[0] = (val) ? 0 : 1;
else
/* val 1 = signal on */
ucontrol->value.integer.value[0] = (val) ? 1 : 0;
return 0;
}
static int juli_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int old_gpio, new_gpio;
old_gpio = ice->gpio.get_data(ice);
if (ucontrol->value.integer.value[0]) {
/* unmute */
if (kcontrol->private_value == GPIO_MUTE_CONTROL) {
/* 0 = signal on */
new_gpio = old_gpio & ~GPIO_MUTE_CONTROL;
/* un-smuting DAC */
snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01);
} else
/* 1 = signal on */
new_gpio = old_gpio |
(unsigned int) kcontrol->private_value;
} else {
/* mute */
if (kcontrol->private_value == GPIO_MUTE_CONTROL) {
/* 1 = signal off */
new_gpio = old_gpio | GPIO_MUTE_CONTROL;
/* smuting DAC */
snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03);
} else
/* 0 = signal off */
new_gpio = old_gpio &
~((unsigned int) kcontrol->private_value);
}
/* dev_dbg(ice->card->dev,
"JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, "
"new_gpio 0x%x\n",
(unsigned int)ucontrol->value.integer.value[0], old_gpio,
new_gpio); */
if (old_gpio != new_gpio) {
ice->gpio.set_data(ice, new_gpio);
return 1;
}
/* no change */
return 0;
}
static struct snd_kcontrol_new juli_mute_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = juli_mute_info,
.get = juli_mute_get,
.put = juli_mute_put,
.private_value = GPIO_MUTE_CONTROL,
},
/* Although the following functionality respects the succint NDA'd
* documentation from the card manufacturer, and the same way of
* operation is coded in OSS Juli driver, only Digital Out monitor
* seems to work. Surprisingly, Analog input monitor outputs Digital
* output data. The two are independent, as enabling both doubles
* volume of the monitor sound.
*
* Checking traces on the board suggests the functionality described
* by the manufacturer is correct - I2S from ADC and AK4114
* go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor
* inputs) are fed from Xilinx.
*
* I even checked traces on board and coded a support in driver for
* an alternative possibility - the unused I2S ICE output channels
* switched to HW-IN/SPDIF-IN and providing the monitoring signal to
* the DAC - to no avail. The I2S outputs seem to be unconnected.
*
* The windows driver supports the monitoring correctly.
*/
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Analog In Switch",
.info = juli_mute_info,
.get = juli_mute_get,
.put = juli_mute_put,
.private_value = GPIO_ANAIN_MONITOR,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Digital Out Switch",
.info = juli_mute_info,
.get = juli_mute_get,
.put = juli_mute_put,
.private_value = GPIO_DIGOUT_MONITOR,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Digital In Switch",
.info = juli_mute_info,
.get = juli_mute_get,
.put = juli_mute_put,
.private_value = GPIO_DIGIN_MONITOR,
},
};
static char *slave_vols[] = {
PCM_VOLUME,
MONITOR_AN_IN_VOLUME,
MONITOR_DIG_IN_VOLUME,
MONITOR_DIG_OUT_VOLUME,
NULL
};
static
DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
static struct snd_kcontrol *ctl_find(struct snd_card *card,
const char *name)
{
struct snd_ctl_elem_id sid;
memset(&sid, 0, sizeof(sid));
/* FIXME: strcpy is bad. */
strcpy(sid.name, name);
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_find_id(card, &sid);
}
static void add_slaves(struct snd_card *card,
struct snd_kcontrol *master,
char * const *list)
{
for (; *list; list++) {
struct snd_kcontrol *slave = ctl_find(card, *list);
/* dev_dbg(card->dev, "add_slaves - %s\n", *list); */
if (slave) {
/* dev_dbg(card->dev, "slave %s found\n", *list); */
snd_ctl_add_slave(master, slave);
}
}
}
static int juli_add_controls(struct snd_ice1712 *ice)
{
struct juli_spec *spec = ice->spec;
int err;
unsigned int i;
struct snd_kcontrol *vmaster;
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) {
err = snd_ctl_add(ice->card,
snd_ctl_new1(&juli_mute_controls[i], ice));
if (err < 0)
return err;
}
/* Create virtual master control */
vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
juli_master_db_scale);
if (!vmaster)
return -ENOMEM;
add_slaves(ice->card, vmaster, slave_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
/* only capture SPDIF over AK4114 */
err = snd_ak4114_build(spec->ak4114, NULL,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
return 0;
}
/*
* suspend/resume
* */
#ifdef CONFIG_PM_SLEEP
static int juli_resume(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
struct juli_spec *spec = ice->spec;
/* akm4358 un-reset, un-mute */
snd_akm4xxx_reset(ak, 0);
/* reinit ak4114 */
snd_ak4114_reinit(spec->ak4114);
return 0;
}
static int juli_suspend(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
/* akm4358 reset and soft-mute */
snd_akm4xxx_reset(ak, 1);
return 0;
}
#endif
/*
* initialize the chip
*/
static inline int juli_is_spdif_master(struct snd_ice1712 *ice)
{
return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1;
}
static unsigned int juli_get_rate(struct snd_ice1712 *ice)
{
int i;
unsigned char result;
result = ice->gpio.get_data(ice) & GPIO_RATE_MASK;
for (i = 0; i < ARRAY_SIZE(gpio_vals); i++)
if (gpio_vals[i] == result)
return juli_rates[i];
return 0;
}
/* setting new rate */
static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned int old, new;
unsigned char val;
old = ice->gpio.get_data(ice);
new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate);
/* dev_dbg(ice->card->dev, "JULI - set_rate: old %x, new %x\n",
old & GPIO_RATE_MASK,
new & GPIO_RATE_MASK); */
ice->gpio.set_data(ice, new);
/* switching to external clock - supplied by external circuits */
val = inb(ICEMT1724(ice, RATE));
outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
}
static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
unsigned int rate)
{
/* no change in master clock */
return 0;
}
/* setting clock to external - SPDIF */
static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type)
{
unsigned int old;
old = ice->gpio.get_data(ice);
/* external clock (= 0), multiply 1x, 48kHz */
ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
GPIO_FREQ_48KHZ);
return 0;
}
/* Called when ak4114 detects change in the input SPDIF stream */
static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,
unsigned char c1)
{
struct snd_ice1712 *ice = ak4114->change_callback_private;
int rate;
if (ice->is_spdif_master(ice) && c1) {
/* only for SPDIF master mode, rate was changed */
rate = snd_ak4114_external_rate(ak4114);
/* dev_dbg(ice->card->dev, "ak4114 - input rate changed to %d\n",
rate); */
juli_akm_set_rate_val(ice->akm, rate);
}
}
static int juli_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
/* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN |
AK4114_OCKS0 | AK4114_OCKS1,
/* AK4114_REQ_FORMAT */ AK4114_DIF_I24I2S,
/* AK4114_REG_IO0 */ AK4114_TX1E,
/* AK4114_REG_IO1 */ AK4114_EFH_1024 | AK4114_DIT |
AK4114_IPS(1),
/* AK4114_REG_INT0_MASK */ 0,
/* AK4114_REG_INT1_MASK */ 0
};
static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
int err;
struct juli_spec *spec;
struct snd_akm4xxx *ak;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
err = snd_ak4114_create(ice->card,
juli_ak4114_read,
juli_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
ice, &spec->ak4114);
if (err < 0)
return err;
/* callback for codecs rate setting */
spec->ak4114->change_callback = juli_ak4114_change;
spec->ak4114->change_callback_private = ice;
/* AK4114 in Juli can detect external rate correctly */
spec->ak4114->check_flags = 0;
#if 0
/*
* it seems that the analog doughter board detection does not work reliably, so
* force the analog flag; it should be very rare (if ever) to come at Juli@
* used without the analog daughter board
*/
spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
#else
spec->analog = 1;
#endif
if (spec->analog) {
dev_info(ice->card->dev, "juli@: analog I/O detected\n");
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
ak = ice->akm;
if (!ak)
return -ENOMEM;
ice->akm_codecs = 1;
err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice);
if (err < 0)
return err;
}
/* juli is clocked by Xilinx array */
ice->hw_rates = &juli_rates_info;
ice->is_spdif_master = juli_is_spdif_master;
ice->get_rate = juli_get_rate;
ice->set_rate = juli_set_rate;
ice->set_mclk = juli_set_mclk;
ice->set_spdif_clock = juli_set_spdif_clock;
ice->spdif.ops.open = juli_spdif_in_open;
#ifdef CONFIG_PM_SLEEP
ice->pm_resume = juli_resume;
ice->pm_suspend = juli_suspend;
ice->pm_suspend_enabled = 1;
#endif
return 0;
}
/*
* Juli@ boards don't provide the EEPROM data except for the vendor IDs.
* hence the driver needs to sets up it properly.
*/
static unsigned char juli_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs,
SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0x7f,
[ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */
[ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */
[ICE_EEP2_GPIO_MASK2] = 0x7f,
[ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X |
GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/
[ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
/* entry point */
struct snd_ice1712_card_info snd_vt1724_juli_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_JULI,
.name = "ESI Juli@",
.model = "juli",
.chip_init = juli_init,
.build_controls = juli_add_controls,
.eeprom_size = sizeof(juli_eeprom),
.eeprom_data = juli_eeprom,
},
{ } /* terminator */
};

10
sound/pci/ice1712/juli.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef __SOUND_JULI_H
#define __SOUND_JULI_H
#define JULI_DEVICE_DESC "{ESI,Juli@},"
#define VT1724_SUBDEVICE_JULI 0x31305345 /* Juli@ */
extern struct snd_ice1712_card_info snd_vt1724_juli_cards[];
#endif /* __SOUND_JULI_H */

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

@ -0,0 +1,778 @@
/*
* 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 <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 3 /* in-4 */
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 const char * const 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, 1 << GPIO_MIC_RELAY,
sel ? (1 << 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 const char * const 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[] = {
{
.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 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 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] = {
0x34, 0x36, /* codec 0 & 1 */
};
/*
* initialize the chip
*/
static int 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[] = {
[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[] = {
{
.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 */

974
sound/pci/ice1712/phase.c Normal file
View file

@ -0,0 +1,974 @@
/*
* ALSA driver for ICEnsemble ICE1724 (Envy24)
*
* Lowlevel functions for Terratec PHASE 22
*
* Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
*
* 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
*
*/
/* PHASE 22 overview:
* Audio controller: VIA Envy24HT-S (slightly trimmed down Envy24HT, 4in/4out)
* Analog chip: AK4524 (partially via Philip's 74HCT125)
* Digital receiver: CS8414-CS (supported in this release)
* PHASE 22 revision 2.0 and Terrasoniq/Musonik TS22PCI have CS8416
* (support status unknown, please test and report)
*
* Envy connects to AK4524
* - CS directly from GPIO 10
* - CCLK via 74HCT125's gate #4 from GPIO 4
* - CDTI via 74HCT125's gate #2 from GPIO 5
* CDTI may be completely blocked by 74HCT125's gate #1
* controlled by GPIO 3
*/
/* PHASE 28 overview:
* Audio controller: VIA Envy24HT (full untrimmed version, 4in/8out)
* Analog chip: WM8770 (8 channel 192k DAC, 2 channel 96k ADC)
* Digital receiver: CS8414-CS (supported in this release)
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "phase.h"
#include <sound/tlv.h>
/* AC97 register cache for Phase28 */
struct phase28_spec {
unsigned short master[2];
unsigned short vol[8];
};
/* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
#define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */
#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */
#define WM_PHASE_SWAP 0x12 /* DAC phase */
#define WM_DAC_CTRL1 0x13 /* DAC control bits */
#define WM_MUTE 0x14 /* mute controls */
#define WM_DAC_CTRL2 0x15 /* de-emphasis and zefo-flag */
#define WM_INT_CTRL 0x16 /* interface control */
#define WM_MASTER 0x17 /* master clock and mode */
#define WM_POWERDOWN 0x18 /* power-down controls */
#define WM_ADC_GAIN 0x19 /* ADC gain L(19)/R(1a) */
#define WM_ADC_MUX 0x1b /* input MUX */
#define WM_OUT_MUX1 0x1c /* output MUX */
#define WM_OUT_MUX2 0x1e /* output MUX */
#define WM_RESET 0x1f /* software reset */
/*
* Logarithmic volume values for WM8770
* Computed as 20 * Log10(255 / x)
*/
static const unsigned char wm_vol[256] = {
127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24,
24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18,
17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14,
14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11,
11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#define WM_VOL_MAX (sizeof(wm_vol) - 1)
#define WM_VOL_MUTE 0x8000
static struct snd_akm4xxx akm_phase22 = {
.type = SND_AK4524,
.num_dacs = 2,
.num_adcs = 2,
};
static struct snd_ak4xxx_private akm_phase22_priv = {
.caddr = 2,
.cif = 1,
.data_mask = 1 << 4,
.clk_mask = 1 << 5,
.cs_mask = 1 << 10,
.cs_addr = 1 << 10,
.cs_none = 0,
.add_flags = 1 << 3,
.mask_flags = 0,
};
static int phase22_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
int err;
/* Configure DAC/ADC description for generic part of ice1724 */
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_PHASE22:
case VT1724_SUBDEVICE_TS22:
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
ice->vt1720 = 1; /* Envy24HT-S have 16 bit wide GPIO */
break;
default:
snd_BUG();
return -EINVAL;
}
/* Initialize analog chips */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
ak = ice->akm;
if (!ak)
return -ENOMEM;
ice->akm_codecs = 1;
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_PHASE22:
case VT1724_SUBDEVICE_TS22:
err = snd_ice1712_akm4xxx_init(ak, &akm_phase22,
&akm_phase22_priv, ice);
if (err < 0)
return err;
break;
}
return 0;
}
static int phase22_add_controls(struct snd_ice1712 *ice)
{
int err = 0;
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_PHASE22:
case VT1724_SUBDEVICE_TS22:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
}
return 0;
}
static unsigned char phase22_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 512, mpu 401,
spdif-in/1xADC, 1xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf0, /* vol, 96k, 24bit */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0xff,
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0xff,
[ICE_EEP2_GPIO_MASK] = 0x00,
[ICE_EEP2_GPIO_MASK1] = 0x00,
[ICE_EEP2_GPIO_MASK2] = 0x00,
[ICE_EEP2_GPIO_STATE] = 0x00,
[ICE_EEP2_GPIO_STATE1] = 0x00,
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
static unsigned char phase28_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401,
spdif-in/1xADC, 4xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0xff,
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0x5f,
[ICE_EEP2_GPIO_MASK] = 0x00,
[ICE_EEP2_GPIO_MASK1] = 0x00,
[ICE_EEP2_GPIO_MASK2] = 0x00,
[ICE_EEP2_GPIO_STATE] = 0x00,
[ICE_EEP2_GPIO_STATE1] = 0x00,
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
/*
* write data in the SPI mode
*/
static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs,
unsigned int data, int bits)
{
unsigned int tmp;
int i;
tmp = snd_ice1712_gpio_read(ice);
snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|
PHASE28_SPI_CLK|PHASE28_WM_CS));
tmp |= PHASE28_WM_RW;
tmp &= ~cs;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
for (i = bits - 1; i >= 0; i--) {
tmp &= ~PHASE28_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
if (data & (1 << i))
tmp |= PHASE28_SPI_MOSI;
else
tmp &= ~PHASE28_SPI_MOSI;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PHASE28_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
tmp &= ~PHASE28_SPI_CLK;
tmp |= cs;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PHASE28_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
}
/*
* get the current register value of WM codec
*/
static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
{
reg <<= 1;
return ((unsigned short)ice->akm[0].images[reg] << 8) |
ice->akm[0].images[reg + 1];
}
/*
* set the register value of WM codec
*/
static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
{
phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
}
/*
* set the register value of WM codec and remember it
*/
static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
{
wm_put_nocache(ice, reg, val);
reg <<= 1;
ice->akm[0].images[reg] = val >> 8;
ice->akm[0].images[reg + 1] = val;
}
static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index,
unsigned short vol, unsigned short master)
{
unsigned char nvol;
if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
nvol = 0;
else
nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) *
(master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
wm_put(ice, index, nvol);
wm_put_nocache(ice, index, 0x180 | nvol);
}
/*
* DAC mute control
*/
#define wm_pcm_mute_info snd_ctl_boolean_mono_info
static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ?
0 : 1;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short nval, oval;
int change;
snd_ice1712_save_gpio_status(ice);
oval = wm_get(ice, WM_MUTE);
nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
change = (nval != oval);
if (change)
wm_put(ice, WM_MUTE, nval);
snd_ice1712_restore_gpio_status(ice);
return change;
}
/*
* Master volume attenuation mixer control
*/
static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = WM_VOL_MAX;
return 0;
}
static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct phase28_spec *spec = ice->spec;
int i;
for (i = 0; i < 2; i++)
ucontrol->value.integer.value[i] = spec->master[i] &
~WM_VOL_MUTE;
return 0;
}
static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct phase28_spec *spec = ice->spec;
int ch, change = 0;
snd_ice1712_save_gpio_status(ice);
for (ch = 0; ch < 2; ch++) {
unsigned int vol = ucontrol->value.integer.value[ch];
if (vol > WM_VOL_MAX)
continue;
vol |= spec->master[ch] & WM_VOL_MUTE;
if (vol != spec->master[ch]) {
int dac;
spec->master[ch] = vol;
for (dac = 0; dac < ice->num_total_dacs; dac += 2)
wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
spec->vol[dac + ch],
spec->master[ch]);
change = 1;
}
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
static int phase28_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits_phase28[] = {
/* These come first to reduce init pop noise */
0x1b, 0x044, /* ADC Mux (AC'97 source) */
0x1c, 0x00B, /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
0x1d, 0x009, /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
0x18, 0x000, /* All power-up */
0x16, 0x122, /* I2S, normal polarity, 24bit */
0x17, 0x022, /* 256fs, slave mode */
0x00, 0, /* DAC1 analog mute */
0x01, 0, /* DAC2 analog mute */
0x02, 0, /* DAC3 analog mute */
0x03, 0, /* DAC4 analog mute */
0x04, 0, /* DAC5 analog mute */
0x05, 0, /* DAC6 analog mute */
0x06, 0, /* DAC7 analog mute */
0x07, 0, /* DAC8 analog mute */
0x08, 0x100, /* master analog mute */
0x09, 0xff, /* DAC1 digital full */
0x0a, 0xff, /* DAC2 digital full */
0x0b, 0xff, /* DAC3 digital full */
0x0c, 0xff, /* DAC4 digital full */
0x0d, 0xff, /* DAC5 digital full */
0x0e, 0xff, /* DAC6 digital full */
0x0f, 0xff, /* DAC7 digital full */
0x10, 0xff, /* DAC8 digital full */
0x11, 0x1ff, /* master digital full */
0x12, 0x000, /* phase normal */
0x13, 0x090, /* unmute DAC L/R */
0x14, 0x000, /* all unmute */
0x15, 0x000, /* no deemphasis, no ZFLG */
0x19, 0x000, /* -12dB ADC/L */
0x1a, 0x000, /* -12dB ADC/R */
(unsigned short)-1
};
unsigned int tmp;
struct snd_akm4xxx *ak;
struct phase28_spec *spec;
const unsigned short *p;
int i;
ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
/* Initialize analog chips */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
ak = ice->akm;
if (!ak)
return -ENOMEM;
ice->akm_codecs = 1;
snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for time being */
/* reset the wm codec as the SPI mode */
snd_ice1712_save_gpio_status(ice);
snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|
PHASE28_HP_SEL));
tmp = snd_ice1712_gpio_read(ice);
tmp &= ~PHASE28_WM_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PHASE28_WM_CS;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
tmp |= PHASE28_WM_RESET;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
p = wm_inits_phase28;
for (; *p != (unsigned short)-1; p += 2)
wm_put(ice, p[0], p[1]);
snd_ice1712_restore_gpio_status(ice);
spec->master[0] = WM_VOL_MUTE;
spec->master[1] = WM_VOL_MUTE;
for (i = 0; i < ice->num_total_dacs; i++) {
spec->vol[i] = WM_VOL_MUTE;
wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
}
return 0;
}
/*
* DAC volume attenuation mixer control
*/
static int wm_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int voices = kcontrol->private_value >> 8;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = voices;
uinfo->value.integer.min = 0; /* mute (-101dB) */
uinfo->value.integer.max = 0x7F; /* 0dB */
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 phase28_spec *spec = ice->spec;
int i, ofs, voices;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xff;
for (i = 0; i < voices; i++)
ucontrol->value.integer.value[i] =
spec->vol[ofs+i] & ~WM_VOL_MUTE;
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 phase28_spec *spec = ice->spec;
int i, idx, ofs, voices;
int change = 0;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xff;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < voices; i++) {
unsigned int vol;
vol = ucontrol->value.integer.value[i];
if (vol > 0x7f)
continue;
vol |= spec->vol[ofs+i] & WM_VOL_MUTE;
if (vol != spec->vol[ofs+i]) {
spec->vol[ofs+i] = vol;
idx = WM_DAC_ATTEN + ofs + i;
wm_set_vol(ice, idx, spec->vol[ofs+i],
spec->master[i]);
change = 1;
}
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
/*
* WM8770 mute control
*/
static int wm_mute_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = kcontrol->private_value >> 8;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int wm_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct phase28_spec *spec = ice->spec;
int voices, ofs, i;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xFF;
for (i = 0; i < voices; i++)
ucontrol->value.integer.value[i] =
(spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
return 0;
}
static int wm_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct phase28_spec *spec = ice->spec;
int change = 0, voices, ofs, i;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xFF;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < voices; i++) {
int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
if (ucontrol->value.integer.value[i] != val) {
spec->vol[ofs + i] &= ~WM_VOL_MUTE;
spec->vol[ofs + i] |=
ucontrol->value.integer.value[i] ? 0 :
WM_VOL_MUTE;
wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
spec->master[i]);
change = 1;
}
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
/*
* WM8770 master mute control
*/
#define wm_master_mute_info snd_ctl_boolean_stereo_info
static int wm_master_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct phase28_spec *spec = ice->spec;
ucontrol->value.integer.value[0] =
(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
ucontrol->value.integer.value[1] =
(spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
return 0;
}
static int wm_master_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct phase28_spec *spec = ice->spec;
int change = 0, i;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < 2; i++) {
int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
if (ucontrol->value.integer.value[i] != val) {
int dac;
spec->master[i] &= ~WM_VOL_MUTE;
spec->master[i] |=
ucontrol->value.integer.value[i] ? 0 :
WM_VOL_MUTE;
for (dac = 0; dac < ice->num_total_dacs; dac += 2)
wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
spec->vol[dac + i],
spec->master[i]);
change = 1;
}
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
/* digital master volume */
#define PCM_0dB 0xff
#define PCM_RES 128 /* -64dB */
#define PCM_MIN (PCM_0dB - PCM_RES)
static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0; /* mute (-64dB) */
uinfo->value.integer.max = PCM_RES; /* 0dB */
return 0;
}
static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
mutex_lock(&ice->gpio_mutex);
val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
val = val > PCM_MIN ? (val - PCM_MIN) : 0;
ucontrol->value.integer.value[0] = val;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short ovol, nvol;
int change = 0;
nvol = ucontrol->value.integer.value[0];
if (nvol > PCM_RES)
return -EINVAL;
snd_ice1712_save_gpio_status(ice);
nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
if (ovol != nvol) {
wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
/* update */
wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100);
change = 1;
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
/*
* Deemphasis
*/
#define phase28_deemp_info snd_ctl_boolean_mono_info
static int phase28_deemp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) ==
0xf;
return 0;
}
static int phase28_deemp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int temp, temp2;
temp = wm_get(ice, WM_DAC_CTRL2);
temp2 = temp;
if (ucontrol->value.integer.value[0])
temp |= 0xf;
else
temp &= ~0xf;
if (temp != temp2) {
wm_put(ice, WM_DAC_CTRL2, temp);
return 1;
}
return 0;
}
/*
* ADC Oversampling
*/
static int phase28_oversampling_info(struct snd_kcontrol *k,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[2] = { "128x", "64x" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
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 phase28_oversampling_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) ==
0x8;
return 0;
}
static int phase28_oversampling_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int temp, temp2;
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
temp = wm_get(ice, WM_MASTER);
temp2 = temp;
if (ucontrol->value.enumerated.item[0])
temp |= 0x8;
else
temp &= ~0x8;
if (temp != temp2) {
wm_put(ice, WM_MASTER, temp);
return 1;
}
return 0;
}
static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
static struct snd_kcontrol_new phase28_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = wm_master_mute_info,
.get = wm_master_mute_get,
.put = wm_master_mute_put
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = wm_master_vol_info,
.get = wm_master_vol_get,
.put = wm_master_vol_put,
.tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Front Playback Switch",
.info = wm_mute_info,
.get = wm_mute_get,
.put = wm_mute_put,
.private_value = (2 << 8) | 0
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Front Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
.private_value = (2 << 8) | 0,
.tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Rear Playback Switch",
.info = wm_mute_info,
.get = wm_mute_get,
.put = wm_mute_put,
.private_value = (2 << 8) | 2
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Rear Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
.private_value = (2 << 8) | 2,
.tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Center Playback Switch",
.info = wm_mute_info,
.get = wm_mute_get,
.put = wm_mute_put,
.private_value = (1 << 8) | 4
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Center Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
.private_value = (1 << 8) | 4,
.tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "LFE Playback Switch",
.info = wm_mute_info,
.get = wm_mute_get,
.put = wm_mute_put,
.private_value = (1 << 8) | 5
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "LFE Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
.private_value = (1 << 8) | 5,
.tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Side Playback Switch",
.info = wm_mute_info,
.get = wm_mute_get,
.put = wm_mute_put,
.private_value = (2 << 8) | 6
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Side Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
.private_value = (2 << 8) | 6,
.tlv = { .p = db_scale_wm_dac }
}
};
static struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.info = wm_pcm_mute_info,
.get = wm_pcm_mute_get,
.put = wm_pcm_mute_put
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume",
.info = wm_pcm_vol_info,
.get = wm_pcm_vol_get,
.put = wm_pcm_vol_put,
.tlv = { .p = db_scale_wm_pcm }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Deemphasis Switch",
.info = phase28_deemp_info,
.get = phase28_deemp_get,
.put = phase28_deemp_put
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Oversampling",
.info = phase28_oversampling_info,
.get = phase28_oversampling_get,
.put = phase28_oversampling_put
}
};
static int phase28_add_controls(struct snd_ice1712 *ice)
{
unsigned int i, counts;
int err;
counts = ARRAY_SIZE(phase28_dac_controls);
for (i = 0; i < counts; i++) {
err = snd_ctl_add(ice->card,
snd_ctl_new1(&phase28_dac_controls[i],
ice));
if (err < 0)
return err;
}
for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
err = snd_ctl_add(ice->card,
snd_ctl_new1(&wm_controls[i], ice));
if (err < 0)
return err;
}
return 0;
}
struct snd_ice1712_card_info snd_vt1724_phase_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PHASE22,
.name = "Terratec PHASE 22",
.model = "phase22",
.chip_init = phase22_init,
.build_controls = phase22_add_controls,
.eeprom_size = sizeof(phase22_eeprom),
.eeprom_data = phase22_eeprom,
},
{
.subvendor = VT1724_SUBDEVICE_PHASE28,
.name = "Terratec PHASE 28",
.model = "phase28",
.chip_init = phase28_init,
.build_controls = phase28_add_controls,
.eeprom_size = sizeof(phase28_eeprom),
.eeprom_data = phase28_eeprom,
},
{
.subvendor = VT1724_SUBDEVICE_TS22,
.name = "Terrasoniq TS22 PCI",
.model = "TS22",
.chip_init = phase22_init,
.build_controls = phase22_add_controls,
.eeprom_size = sizeof(phase22_eeprom),
.eeprom_data = phase22_eeprom,
},
{ } /* terminator */
};

53
sound/pci/ice1712/phase.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef __SOUND_PHASE_H
#define __SOUND_PHASE_H
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for Terratec PHASE 22
*
* Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
*
* 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
*
*/
#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
"{Terratec,Phase 28},"\
"{Terrasoniq,TS22},"
#define VT1724_SUBDEVICE_PHASE22 0x3b155011
#define VT1724_SUBDEVICE_PHASE28 0x3b154911
#define VT1724_SUBDEVICE_TS22 0x3b157b11
/* entry point */
extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
/* PHASE28 GPIO bits */
#define PHASE28_SPI_MISO (1 << 21)
#define PHASE28_WM_RESET (1 << 20)
#define PHASE28_SPI_CLK (1 << 19)
#define PHASE28_SPI_MOSI (1 << 18)
#define PHASE28_WM_RW (1 << 17)
#define PHASE28_AC97_RESET (1 << 16)
#define PHASE28_DIGITAL_SEL1 (1 << 15)
#define PHASE28_HP_SEL (1 << 14)
#define PHASE28_WM_CS (1 << 12)
#define PHASE28_AC97_COMMIT (1 << 11)
#define PHASE28_AC97_ADDR (1 << 10)
#define PHASE28_AC97_DATA_LOW (1 << 9)
#define PHASE28_AC97_DATA_HIGH (1 << 8)
#define PHASE28_AC97_DATA_MASK 0xFF
#endif /* __SOUND_PHASE */

835
sound/pci/ice1712/pontis.c Normal file
View file

@ -0,0 +1,835 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Pontis MS300
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/tlv.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "pontis.h"
/* I2C addresses */
#define WM_DEV 0x34
#define CS_DEV 0x20
/* WM8776 registers */
#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */
#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */
#define WM_HP_MASTER 0x02 /* headphone master (both channels) */
/* override LLR */
#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */
#define WM_DAC_ATTEN_R 0x04
#define WM_DAC_MASTER 0x05
#define WM_PHASE_SWAP 0x06 /* DAC phase swap */
#define WM_DAC_CTRL1 0x07
#define WM_DAC_MUTE 0x08
#define WM_DAC_CTRL2 0x09
#define WM_DAC_INT 0x0a
#define WM_ADC_INT 0x0b
#define WM_MASTER_CTRL 0x0c
#define WM_POWERDOWN 0x0d
#define WM_ADC_ATTEN_L 0x0e
#define WM_ADC_ATTEN_R 0x0f
#define WM_ALC_CTRL1 0x10
#define WM_ALC_CTRL2 0x11
#define WM_ALC_CTRL3 0x12
#define WM_NOISE_GATE 0x13
#define WM_LIMITER 0x14
#define WM_ADC_MUX 0x15
#define WM_OUT_MUX 0x16
#define WM_RESET 0x17
/*
* GPIO
*/
#define PONTIS_CS_CS (1<<4) /* CS */
#define PONTIS_CS_CLK (1<<5) /* CLK */
#define PONTIS_CS_RDATA (1<<6) /* CS8416 -> VT1720 */
#define PONTIS_CS_WDATA (1<<7) /* VT1720 -> CS8416 */
/*
* get the current register value of WM codec
*/
static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
{
reg <<= 1;
return ((unsigned short)ice->akm[0].images[reg] << 8) |
ice->akm[0].images[reg + 1];
}
/*
* set the register value of WM codec and remember it
*/
static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
{
unsigned short cval;
cval = (reg << 9) | val;
snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
}
static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
{
wm_put_nocache(ice, reg, val);
reg <<= 1;
ice->akm[0].images[reg] = val >> 8;
ice->akm[0].images[reg + 1] = val;
}
/*
* DAC volume attenuation mixer control (-64dB to 0dB)
*/
#define DAC_0dB 0xff
#define DAC_RES 128
#define DAC_MIN (DAC_0dB - DAC_RES)
static int wm_dac_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0; /* mute */
uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */
return 0;
}
static int wm_dac_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
int i;
mutex_lock(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_DAC_ATTEN_L + i) & 0xff;
val = val > DAC_MIN ? (val - DAC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short oval, nval;
int i, idx, change = 0;
mutex_lock(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nval = ucontrol->value.integer.value[i];
nval = (nval ? (nval + DAC_MIN) : 0) & 0xff;
idx = WM_DAC_ATTEN_L + i;
oval = wm_get(ice, idx) & 0xff;
if (oval != nval) {
wm_put(ice, idx, nval);
wm_put_nocache(ice, idx, nval | 0x100);
change = 1;
}
}
mutex_unlock(&ice->gpio_mutex);
return change;
}
/*
* ADC gain mixer control (-64dB to 0dB)
*/
#define ADC_0dB 0xcf
#define ADC_RES 128
#define ADC_MIN (ADC_0dB - ADC_RES)
static int wm_adc_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0; /* mute (-64dB) */
uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */
return 0;
}
static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
int i;
mutex_lock(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
val = val > ADC_MIN ? (val - ADC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short ovol, nvol;
int i, idx, change = 0;
mutex_lock(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nvol = ucontrol->value.integer.value[i];
nvol = nvol ? (nvol + ADC_MIN) : 0;
idx = WM_ADC_ATTEN_L + i;
ovol = wm_get(ice, idx) & 0xff;
if (ovol != nvol) {
wm_put(ice, idx, nvol);
change = 1;
}
}
mutex_unlock(&ice->gpio_mutex);
return change;
}
/*
* ADC input mux mixer control
*/
#define wm_adc_mux_info snd_ctl_boolean_mono_info
static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int bit = kcontrol->private_value;
mutex_lock(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int bit = kcontrol->private_value;
unsigned short oval, nval;
int change;
mutex_lock(&ice->gpio_mutex);
nval = oval = wm_get(ice, WM_ADC_MUX);
if (ucontrol->value.integer.value[0])
nval |= (1 << bit);
else
nval &= ~(1 << bit);
change = nval != oval;
if (change) {
wm_put(ice, WM_ADC_MUX, nval);
}
mutex_unlock(&ice->gpio_mutex);
return change;
}
/*
* Analog bypass (In -> Out)
*/
#define wm_bypass_info snd_ctl_boolean_mono_info
static int wm_bypass_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_bypass_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val, oval;
int change = 0;
mutex_lock(&ice->gpio_mutex);
val = oval = wm_get(ice, WM_OUT_MUX);
if (ucontrol->value.integer.value[0])
val |= 0x04;
else
val &= ~0x04;
if (val != oval) {
wm_put(ice, WM_OUT_MUX, val);
change = 1;
}
mutex_unlock(&ice->gpio_mutex);
return change;
}
/*
* Left/Right swap
*/
#define wm_chswap_info snd_ctl_boolean_mono_info
static int wm_chswap_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int wm_chswap_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val, oval;
int change = 0;
mutex_lock(&ice->gpio_mutex);
oval = wm_get(ice, WM_DAC_CTRL1);
val = oval & 0x0f;
if (ucontrol->value.integer.value[0])
val |= 0x60;
else
val |= 0x90;
if (val != oval) {
wm_put(ice, WM_DAC_CTRL1, val);
wm_put_nocache(ice, WM_DAC_CTRL1, val);
change = 1;
}
mutex_unlock(&ice->gpio_mutex);
return change;
}
/*
* write data in the SPI mode
*/
static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val)
{
unsigned int tmp = snd_ice1712_gpio_read(ice);
if (val)
tmp |= bit;
else
tmp &= ~bit;
snd_ice1712_gpio_write(ice, tmp);
}
static void spi_send_byte(struct snd_ice1712 *ice, unsigned char data)
{
int i;
for (i = 0; i < 8; i++) {
set_gpio_bit(ice, PONTIS_CS_CLK, 0);
udelay(1);
set_gpio_bit(ice, PONTIS_CS_WDATA, data & 0x80);
udelay(1);
set_gpio_bit(ice, PONTIS_CS_CLK, 1);
udelay(1);
data <<= 1;
}
}
static unsigned int spi_read_byte(struct snd_ice1712 *ice)
{
int i;
unsigned int val = 0;
for (i = 0; i < 8; i++) {
val <<= 1;
set_gpio_bit(ice, PONTIS_CS_CLK, 0);
udelay(1);
if (snd_ice1712_gpio_read(ice) & PONTIS_CS_RDATA)
val |= 1;
udelay(1);
set_gpio_bit(ice, PONTIS_CS_CLK, 1);
udelay(1);
}
return val;
}
static void spi_write(struct snd_ice1712 *ice, unsigned int dev, unsigned int reg, unsigned int data)
{
snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK);
snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK));
set_gpio_bit(ice, PONTIS_CS_CS, 0);
spi_send_byte(ice, dev & ~1); /* WRITE */
spi_send_byte(ice, reg); /* MAP */
spi_send_byte(ice, data); /* DATA */
/* trigger */
set_gpio_bit(ice, PONTIS_CS_CS, 1);
udelay(1);
/* restore */
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
}
static unsigned int spi_read(struct snd_ice1712 *ice, unsigned int dev, unsigned int reg)
{
unsigned int val;
snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK);
snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK));
set_gpio_bit(ice, PONTIS_CS_CS, 0);
spi_send_byte(ice, dev & ~1); /* WRITE */
spi_send_byte(ice, reg); /* MAP */
/* trigger */
set_gpio_bit(ice, PONTIS_CS_CS, 1);
udelay(1);
set_gpio_bit(ice, PONTIS_CS_CS, 0);
spi_send_byte(ice, dev | 1); /* READ */
val = spi_read_byte(ice);
/* trigger */
set_gpio_bit(ice, PONTIS_CS_CS, 1);
udelay(1);
/* restore */
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
return val;
}
/*
* SPDIF input source
*/
static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[] = {
"Coax", /* RXP0 */
"Optical", /* RXP1 */
"CD", /* RXP2 */
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
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 cs_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
ucontrol->value.enumerated.item[0] = ice->gpio.saved[0];
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int change = 0;
mutex_lock(&ice->gpio_mutex);
if (ucontrol->value.enumerated.item[0] != ice->gpio.saved[0]) {
ice->gpio.saved[0] = ucontrol->value.enumerated.item[0] & 3;
val = 0x80 | (ice->gpio.saved[0] << 3);
spi_write(ice, CS_DEV, 0x04, val);
change = 1;
}
mutex_unlock(&ice->gpio_mutex);
return change;
}
/*
* GPIO controls
*/
static int pontis_gpio_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0xffff; /* 16bit */
return 0;
}
static int pontis_gpio_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
/* 4-7 reserved */
ucontrol->value.integer.value[0] = (~ice->gpio.write_mask & 0xffff) | 0x00f0;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int pontis_gpio_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
int changed;
mutex_lock(&ice->gpio_mutex);
/* 4-7 reserved */
val = (~ucontrol->value.integer.value[0] & 0xffff) | 0x00f0;
changed = val != ice->gpio.write_mask;
ice->gpio.write_mask = val;
mutex_unlock(&ice->gpio_mutex);
return changed;
}
static int pontis_gpio_dir_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
/* 4-7 reserved */
ucontrol->value.integer.value[0] = ice->gpio.direction & 0xff0f;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int pontis_gpio_dir_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
int changed;
mutex_lock(&ice->gpio_mutex);
/* 4-7 reserved */
val = ucontrol->value.integer.value[0] & 0xff0f;
changed = (val != ice->gpio.direction);
ice->gpio.direction = val;
mutex_unlock(&ice->gpio_mutex);
return changed;
}
static int pontis_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
ucontrol->value.integer.value[0] = snd_ice1712_gpio_read(ice) & 0xffff;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val, nval;
int changed = 0;
mutex_lock(&ice->gpio_mutex);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
val = snd_ice1712_gpio_read(ice) & 0xffff;
nval = ucontrol->value.integer.value[0] & 0xffff;
if (val != nval) {
snd_ice1712_gpio_write(ice, nval);
changed = 1;
}
mutex_unlock(&ice->gpio_mutex);
return changed;
}
static const DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
/*
* mixers
*/
static struct snd_kcontrol_new pontis_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume",
.info = wm_dac_vol_info,
.get = wm_dac_vol_get,
.put = wm_dac_vol_put,
.tlv = { .p = db_scale_volume },
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume",
.info = wm_adc_vol_info,
.get = wm_adc_vol_get,
.put = wm_adc_vol_put,
.tlv = { .p = db_scale_volume },
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "CD Capture Switch",
.info = wm_adc_mux_info,
.get = wm_adc_mux_get,
.put = wm_adc_mux_put,
.private_value = 0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Capture Switch",
.info = wm_adc_mux_info,
.get = wm_adc_mux_get,
.put = wm_adc_mux_put,
.private_value = 1,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Bypass Switch",
.info = wm_bypass_info,
.get = wm_bypass_get,
.put = wm_bypass_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Swap Output Channels",
.info = wm_chswap_info,
.get = wm_chswap_get,
.put = wm_chswap_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC958 Input Source",
.info = cs_source_info,
.get = cs_source_get,
.put = cs_source_put,
},
/* FIXME: which interface? */
{
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "GPIO Mask",
.info = pontis_gpio_mask_info,
.get = pontis_gpio_mask_get,
.put = pontis_gpio_mask_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "GPIO Direction",
.info = pontis_gpio_mask_info,
.get = pontis_gpio_dir_get,
.put = pontis_gpio_dir_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "GPIO Data",
.info = pontis_gpio_mask_info,
.get = pontis_gpio_data_get,
.put = pontis_gpio_data_put,
},
};
/*
* WM codec registers
*/
static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
mutex_lock(&ice->gpio_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg <= 0x17 && val <= 0xffff)
wm_put(ice, reg, val);
}
mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
mutex_lock(&ice->gpio_mutex);
for (reg = 0; reg <= 0x17; reg++) {
val = wm_get(ice, reg);
snd_iprintf(buffer, "%02x = %04x\n", reg, val);
}
mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_init(struct snd_ice1712 *ice)
{
struct snd_info_entry *entry;
if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
entry->mode |= S_IWUSR;
entry->c.text.write = wm_proc_regs_write;
}
}
static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
mutex_lock(&ice->gpio_mutex);
for (reg = 0; reg <= 0x26; reg++) {
val = spi_read(ice, CS_DEV, reg);
snd_iprintf(buffer, "%02x = %02x\n", reg, val);
}
val = spi_read(ice, CS_DEV, 0x7f);
snd_iprintf(buffer, "%02x = %02x\n", 0x7f, val);
mutex_unlock(&ice->gpio_mutex);
}
static void cs_proc_init(struct snd_ice1712 *ice)
{
struct snd_info_entry *entry;
if (! snd_card_proc_new(ice->card, "cs_codec", &entry))
snd_info_set_text_ops(entry, ice, cs_proc_regs_read);
}
static int pontis_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(pontis_controls); i++) {
err = snd_ctl_add(ice->card, snd_ctl_new1(&pontis_controls[i], ice));
if (err < 0)
return err;
}
wm_proc_init(ice);
cs_proc_init(ice);
return 0;
}
/*
* initialize the chip
*/
static int pontis_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits[] = {
/* These come first to reduce init pop noise */
WM_ADC_MUX, 0x00c0, /* ADC mute */
WM_DAC_MUTE, 0x0001, /* DAC softmute */
WM_DAC_CTRL1, 0x0000, /* DAC mute */
WM_POWERDOWN, 0x0008, /* All power-up except HP */
WM_RESET, 0x0000, /* reset */
};
static const unsigned short wm_inits2[] = {
WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */
WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */
WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */
WM_DAC_CTRL1, 0x0090, /* DAC L/R */
WM_OUT_MUX, 0x0001, /* OUT DAC */
WM_HP_ATTEN_L, 0x0179, /* HP 0dB */
WM_HP_ATTEN_R, 0x0179, /* HP 0dB */
WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */
WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */
WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */
WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */
/* WM_DAC_MASTER, 0x0100, */ /* DAC master muted */
WM_PHASE_SWAP, 0x0000, /* phase normal */
WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */
WM_ADC_ATTEN_L, 0x0000, /* ADC muted */
WM_ADC_ATTEN_R, 0x0000, /* ADC muted */
#if 0
WM_ALC_CTRL1, 0x007b, /* */
WM_ALC_CTRL2, 0x0000, /* */
WM_ALC_CTRL3, 0x0000, /* */
WM_NOISE_GATE, 0x0000, /* */
#endif
WM_DAC_MUTE, 0x0000, /* DAC unmute */
WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */
};
static const unsigned char cs_inits[] = {
0x04, 0x80, /* RUN, RXP0 */
0x05, 0x05, /* slave, 24bit */
0x01, 0x00,
0x02, 0x00,
0x03, 0x00,
};
unsigned int i;
ice->vt1720 = 1;
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
/* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
return -ENOMEM;
ice->akm_codecs = 1;
/* HACK - use this as the SPDIF source.
* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
*/
ice->gpio.saved[0] = 0;
/* initialize WM8776 codec */
for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
wm_put(ice, wm_inits[i], wm_inits[i+1]);
schedule_timeout_uninterruptible(1);
for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
/* initialize CS8416 codec */
/* assert PRST#; MT05 bit 7 */
outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
mdelay(5);
/* deassert PRST# */
outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
for (i = 0; i < ARRAY_SIZE(cs_inits); i += 2)
spi_write(ice, CS_DEV, cs_inits[i], cs_inits[i+1]);
return 0;
}
/*
* Pontis boards don't provide the EEPROM data at all.
* hence the driver needs to sets up it properly.
*/
static unsigned char pontis_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x08, /* clock 256, mpu401, spdif-in/ADC, 1DAC */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0x07,
[ICE_EEP2_GPIO_DIR1] = 0x00,
[ICE_EEP2_GPIO_DIR2] = 0x00, /* ignored */
[ICE_EEP2_GPIO_MASK] = 0x0f, /* 4-7 reserved for CS8416 */
[ICE_EEP2_GPIO_MASK1] = 0xff,
[ICE_EEP2_GPIO_MASK2] = 0x00, /* ignored */
[ICE_EEP2_GPIO_STATE] = 0x06, /* 0-low, 1-high, 2-high */
[ICE_EEP2_GPIO_STATE1] = 0x00,
[ICE_EEP2_GPIO_STATE2] = 0x00, /* ignored */
};
/* entry point */
struct snd_ice1712_card_info snd_vt1720_pontis_cards[] = {
{
.subvendor = VT1720_SUBDEVICE_PONTIS_MS300,
.name = "Pontis MS300",
.model = "ms300",
.chip_init = pontis_init,
.build_controls = pontis_add_controls,
.eeprom_size = sizeof(pontis_eeprom),
.eeprom_data = pontis_eeprom,
},
{ } /* terminator */
};

View file

@ -0,0 +1,33 @@
#ifndef __SOUND_PONTIS_H
#define __SOUND_PONTIS_H
/*
* ALSA driver for VIA VT1724 (Envy24HT)
*
* Lowlevel functions for Pontis MS300 boards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.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
*
*/
#define PONTIS_DEVICE_DESC "{Pontis,MS300},"
#define VT1720_SUBDEVICE_PONTIS_MS300 0x00020002 /* a dummy id for MS300 */
extern struct snd_ice1712_card_info snd_vt1720_pontis_cards[];
#endif /* __SOUND_PONTIS_H */

View file

@ -0,0 +1,822 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for AudioTrak Prodigy 192 cards
* Supported IEC958 input from optional MI/ODI/O add-on card.
*
* Specifics (SW, HW):
* -------------------
* * 49.5MHz crystal
* * SPDIF-OUT on the card:
* - coax (through isolation transformer)/toslink supplied by
* 74HC04 gates - 3 in parallel
* - output switched between on-board CD drive dig-out connector
* and ice1724 SPDTX pin, using 74HC02 NOR gates, controlled
* by GPIO20 (0 = CD dig-out, 1 = SPDTX)
* * SPDTX goes straight to MI/ODI/O card's SPDIF-OUT coax
*
* * MI/ODI/O card: AK4114 based, used for iec958 input only
* - toslink input -> RX0
* - coax input -> RX1
* - 4wire protocol:
* AK4114 ICE1724
* ------------------------------
* CDTO (pin 32) -- GPIO11 pin 86
* CDTI (pin 33) -- GPIO10 pin 77
* CCLK (pin 34) -- GPIO9 pin 76
* CSN (pin 35) -- GPIO8 pin 75
* - output data Mode 7 (24bit, I2S, slave)
* - both MCKO1 and MCKO2 of ak4114 are fed to FPGA, which
* outputs master clock to SPMCLKIN of ice1724.
* Experimentally I found out that only a combination of
* OCKS0=1, OCKS1=1 (128fs, 64fs output) and ice1724 -
* VT1724_MT_I2S_MCLK_128X=0 (256fs input) yields correct
* sampling rate. That means the the FPGA doubles the
* MCK01 rate.
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
* Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
* Copyright (c) 2004 Kouichi ONO <co2b@ceres.dti.ne.jp>
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "prodigy192.h"
#include "stac946x.h"
#include <sound/tlv.h>
struct prodigy192_spec {
struct ak4114 *ak4114;
/* rate change needs atomic mute/unmute of all dacs*/
struct mutex mute_mutex;
};
static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
{
snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val);
}
static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
{
return snd_vt1724_read_i2c(ice, PRODIGY192_STAC9460_ADDR, reg);
}
/*
* DAC mute control
*/
/*
* idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute
*/
static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
unsigned char mute)
{
unsigned char new, old;
int change;
old = stac9460_get(ice, idx);
new = (~mute << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change)
/* dev_dbg(ice->card->dev, "Volume register 0x%02x: 0x%02x\n", idx, new);*/
stac9460_put(ice, idx, new);
return change;
}
#define stac9460_dac_mute_info snd_ctl_boolean_mono_info
static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int idx;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
val = stac9460_get(ice, idx);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
return 0;
}
static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct prodigy192_spec *spec = ice->spec;
int idx, change;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
/* due to possible conflicts with stac9460_set_rate_val, mutexing */
mutex_lock(&spec->mute_mutex);
/*
dev_dbg(ice->card->dev, "Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
ucontrol->value.integer.value[0]);
*/
change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
mutex_unlock(&spec->mute_mutex);
return change;
}
/*
* DAC volume attenuation mixer control
*/
static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0; /* mute */
uinfo->value.integer.max = 0x7f; /* 0dB */
return 0;
}
static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
unsigned char vol;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
vol = stac9460_get(ice, idx) & 0x7f;
ucontrol->value.integer.value[0] = 0x7f - vol;
return 0;
}
static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
unsigned char tmp, ovol, nvol;
int change;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
nvol = ucontrol->value.integer.value[0];
tmp = stac9460_get(ice, idx);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
ovol = (0x7f - nvol) | (tmp & 0x80);
/*
dev_dbg(ice->card->dev, "DAC Volume: reg 0x%02x: 0x%02x\n",
idx, ovol);
*/
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
return change;
}
/*
* ADC mute control
*/
#define stac9460_adc_mute_info snd_ctl_boolean_stereo_info
static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int i;
for (i = 0; i < 2; ++i) {
val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i);
ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
}
return 0;
}
static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
int i, reg;
int change;
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
old = stac9460_get(ice, reg);
new = (~ucontrol->value.integer.value[i]<<7&0x80) | (old&~0x80);
change = (new != old);
if (change)
stac9460_put(ice, reg, new);
}
return change;
}
/*
* ADC gain mixer control
*/
static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0; /* 0dB */
uinfo->value.integer.max = 0x0f; /* 22.5dB */
return 0;
}
static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int i, reg;
unsigned char vol;
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
vol = stac9460_get(ice, reg) & 0x0f;
ucontrol->value.integer.value[i] = 0x0f - vol;
}
return 0;
}
static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int i, reg;
unsigned char ovol, nvol;
int change;
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
nvol = ucontrol->value.integer.value[i] & 0x0f;
ovol = 0x0f - stac9460_get(ice, reg);
change = ((ovol & 0x0f) != nvol);
if (change)
stac9460_put(ice, reg, (0x0f - nvol) | (ovol & ~0x0f));
}
return change;
}
static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[2] = { "Line In", "Mic" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
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 stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1;
return 0;
}
static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
int change;
old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change)
stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
return change;
}
/*
* Handler for setting correct codec rate - called when rate change is detected
*/
static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned char old, new;
int idx;
unsigned char changed[7];
struct prodigy192_spec *spec = ice->spec;
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
else if (rate <= 48000)
new = 0x08; /* 256x, base rate mode */
else if (rate <= 96000)
new = 0x11; /* 256x, mid rate mode */
else
new = 0x12; /* 128x, high rate mode */
old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
if (old == new)
return;
/* change detected, setting master clock, muting first */
/* due to possible conflicts with mute controls - mutexing */
mutex_lock(&spec->mute_mutex);
/* we have to remember current mute status for each DAC */
for (idx = 0; idx < 7 ; ++idx)
changed[idx] = stac9460_dac_mute(ice,
STAC946X_MASTER_VOLUME + idx, 0);
/*dev_dbg(ice->card->dev, "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
udelay(10);
/* unmuting - only originally unmuted dacs -
* i.e. those changed when muting */
for (idx = 0; idx < 7 ; ++idx) {
if (changed[idx])
stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
}
mutex_unlock(&spec->mute_mutex);
}
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
/*
* mixers
*/
static struct snd_kcontrol_new stac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
.private_value = 1,
.tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
.private_value = 1,
.tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Switch",
.count = 6,
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "DAC Volume",
.count = 6,
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
.tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Capture Switch",
.count = 1,
.info = stac9460_adc_mute_info,
.get = stac9460_adc_mute_get,
.put = stac9460_adc_mute_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "ADC Capture Volume",
.count = 1,
.info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
.tlv = { .p = db_scale_adc }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Capture Input",
.info = stac9460_mic_sw_info,
.get = stac9460_mic_sw_get,
.put = stac9460_mic_sw_put,
},
};
/* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */
/* CDTO (pin 32) -- GPIO11 pin 86
* CDTI (pin 33) -- GPIO10 pin 77
* CCLK (pin 34) -- GPIO9 pin 76
* CSN (pin 35) -- GPIO8 pin 75
*/
#define AK4114_ADDR 0x00 /* C1-C0: Chip Address
* (According to datasheet fixed to 00)
*/
/*
* 4wire ak4114 protocol - writing data
*/
static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
unsigned int data, int idx)
{
for (; idx >= 0; idx--) {
/* drop clock */
gpio &= ~VT1724_PRODIGY192_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
/* set data */
if (data & (1 << idx))
gpio |= VT1724_PRODIGY192_CDOUT;
else
gpio &= ~VT1724_PRODIGY192_CDOUT;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
/* raise clock */
gpio |= VT1724_PRODIGY192_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
}
}
/*
* 4wire ak4114 protocol - reading data
*/
static unsigned char read_data(struct snd_ice1712 *ice, unsigned int gpio,
int idx)
{
unsigned char data = 0;
for (; idx >= 0; idx--) {
/* drop clock */
gpio &= ~VT1724_PRODIGY192_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
/* read data */
if (snd_ice1712_gpio_read(ice) & VT1724_PRODIGY192_CDIN)
data |= (1 << idx);
udelay(1);
/* raise clock */
gpio |= VT1724_PRODIGY192_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
}
return data;
}
/*
* 4wire ak4114 protocol - starting sequence
*/
static unsigned int prodigy192_4wire_start(struct snd_ice1712 *ice)
{
unsigned int tmp;
snd_ice1712_save_gpio_status(ice);
tmp = snd_ice1712_gpio_read(ice);
tmp |= VT1724_PRODIGY192_CCLK; /* high at init */
tmp &= ~VT1724_PRODIGY192_CS; /* drop chip select */
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
return tmp;
}
/*
* 4wire ak4114 protocol - final sequence
*/
static void prodigy192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
{
tmp |= VT1724_PRODIGY192_CS; /* raise chip select */
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
snd_ice1712_restore_gpio_status(ice);
}
/*
* Write data to addr register of ak4114
*/
static void prodigy192_ak4114_write(void *private_data, unsigned char addr,
unsigned char data)
{
struct snd_ice1712 *ice = private_data;
unsigned int tmp, addrdata;
tmp = prodigy192_4wire_start(ice);
addrdata = (AK4114_ADDR << 6) | 0x20 | (addr & 0x1f);
addrdata = (addrdata << 8) | data;
write_data(ice, tmp, addrdata, 15);
prodigy192_4wire_finish(ice, tmp);
}
/*
* Read data from addr register of ak4114
*/
static unsigned char prodigy192_ak4114_read(void *private_data,
unsigned char addr)
{
struct snd_ice1712 *ice = private_data;
unsigned int tmp;
unsigned char data;
tmp = prodigy192_4wire_start(ice);
write_data(ice, tmp, (AK4114_ADDR << 6) | (addr & 0x1f), 7);
data = read_data(ice, tmp, 7);
prodigy192_4wire_finish(ice, tmp);
return data;
}
static int ak4114_input_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[2] = { "Toslink", "Coax" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
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 ak4114_input_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
val = prodigy192_ak4114_read(ice, AK4114_REG_IO1);
/* AK4114_IPS0 bit = 0 -> RX0 = Toslink
* AK4114_IPS0 bit = 1 -> RX1 = Coax
*/
ucontrol->value.enumerated.item[0] = (val & AK4114_IPS0) ? 1 : 0;
return 0;
}
static int ak4114_input_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old, itemvalue;
int change;
old = prodigy192_ak4114_read(ice, AK4114_REG_IO1);
/* AK4114_IPS0 could be any bit */
itemvalue = (ucontrol->value.enumerated.item[0]) ? 0xff : 0x00;
new = (itemvalue & AK4114_IPS0) | (old & ~AK4114_IPS0);
change = (new != old);
if (change)
prodigy192_ak4114_write(ice, AK4114_REG_IO1, new);
return change;
}
static struct snd_kcontrol_new ak4114_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIODIO IEC958 Capture Input",
.info = ak4114_input_sw_info,
.get = ak4114_input_sw_get,
.put = ak4114_input_sw_put,
}
};
static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
/* ice1724 expects I2S and provides clock,
* DEM0 disables the deemphasis filter
*/
AK4114_DIF_I24I2S | AK4114_DEM0 ,
AK4114_TX1E,
AK4114_EFH_1024 | AK4114_DIT, /* default input RX0 */
0,
0
};
static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
struct prodigy192_spec *spec = ice->spec;
int err;
err = snd_ak4114_create(ice->card,
prodigy192_ak4114_read,
prodigy192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
ice, &spec->ak4114);
if (err < 0)
return err;
/* AK4114 in Prodigy192 cannot detect external rate correctly.
* No reason to stop capture stream due to incorrect checks */
spec->ak4114->check_flags = AK4114_CHECK_NO_RATE;
return 0;
}
static void stac9460_proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
/* registers 0x0 - 0x14 */
for (reg = 0; reg <= 0x15; reg++) {
val = stac9460_get(ice, reg);
snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
}
}
static void stac9460_proc_init(struct snd_ice1712 *ice)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
}
static int prodigy192_add_controls(struct snd_ice1712 *ice)
{
struct prodigy192_spec *spec = ice->spec;
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(stac_controls); i++) {
err = snd_ctl_add(ice->card,
snd_ctl_new1(&stac_controls[i], ice));
if (err < 0)
return err;
}
if (spec->ak4114) {
/* ak4114 is connected */
for (i = 0; i < ARRAY_SIZE(ak4114_controls); i++) {
err = snd_ctl_add(ice->card,
snd_ctl_new1(&ak4114_controls[i],
ice));
if (err < 0)
return err;
}
err = snd_ak4114_build(spec->ak4114,
NULL, /* ak4114 in MIO/DI/O handles no IEC958 output */
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
}
stac9460_proc_init(ice);
return 0;
}
/*
* check for presence of MI/ODI/O add-on card with digital inputs
*/
static int prodigy192_miodio_exists(struct snd_ice1712 *ice)
{
unsigned char orig_value;
const unsigned char test_data = 0xd1; /* random value */
unsigned char addr = AK4114_REG_INT0_MASK; /* random SAFE address */
int exists = 0;
orig_value = prodigy192_ak4114_read(ice, addr);
prodigy192_ak4114_write(ice, addr, test_data);
if (prodigy192_ak4114_read(ice, addr) == test_data) {
/* ak4114 seems to communicate, apparently exists */
/* writing back original value */
prodigy192_ak4114_write(ice, addr, orig_value);
exists = 1;
}
return exists;
}
/*
* initialize the chip
*/
static int prodigy192_init(struct snd_ice1712 *ice)
{
static const unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
STAC946X_MASTER_CLOCKING, 0x11,
/* STAC946X_MASTER_VOLUME, 0,
STAC946X_LF_VOLUME, 0,
STAC946X_RF_VOLUME, 0,
STAC946X_LR_VOLUME, 0,
STAC946X_RR_VOLUME, 0,
STAC946X_CENTER_VOLUME, 0,
STAC946X_LFE_VOLUME, 0,*/
(unsigned short)-1
};
const unsigned short *p;
int err = 0;
struct prodigy192_spec *spec;
/* prodigy 192 */
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
ice->vt1720 = 0; /* ice1724, e.g. 23 GPIOs */
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
mutex_init(&spec->mute_mutex);
/* initialize codec */
p = stac_inits_prodigy;
for (; *p != (unsigned short)-1; p += 2)
stac9460_put(ice, p[0], p[1]);
ice->gpio.set_pro_rate = stac9460_set_rate_val;
/* MI/ODI/O add on card with AK4114 */
if (prodigy192_miodio_exists(ice)) {
err = prodigy192_ak4114_init(ice);
/* from this moment if err = 0 then
* spec->ak4114 should not be null
*/
dev_dbg(ice->card->dev,
"AK4114 initialized with status %d\n", err);
} else
dev_dbg(ice->card->dev, "AK4114 not found\n");
if (err < 0)
return err;
return 0;
}
/*
* Aureon boards don't provide the EEPROM data except for the vendor IDs.
* hence the driver needs to sets up it properly.
*/
static unsigned char prodigy71_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x6a, /* 49MHz crystal, mpu401,
* spdif-in+ 1 stereo ADC,
* 3 stereo DACs
*/
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0xff,
[ICE_EEP2_GPIO_DIR1] = ~(VT1724_PRODIGY192_CDIN >> 8) ,
[ICE_EEP2_GPIO_DIR2] = 0xbf,
[ICE_EEP2_GPIO_MASK] = 0x00,
[ICE_EEP2_GPIO_MASK1] = 0x00,
[ICE_EEP2_GPIO_MASK2] = 0x00,
[ICE_EEP2_GPIO_STATE] = 0x00,
[ICE_EEP2_GPIO_STATE1] = 0x00,
[ICE_EEP2_GPIO_STATE2] = 0x10, /* GPIO20: 0 = CD drive dig. input
* passthrough,
* 1 = SPDIF-OUT from ice1724
*/
};
/* entry point */
struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY192VE,
.name = "Audiotrak Prodigy 192",
.model = "prodigy192",
.chip_init = prodigy192_init,
.build_controls = prodigy192_add_controls,
.eeprom_size = sizeof(prodigy71_eeprom),
.eeprom_data = prodigy71_eeprom,
},
{ } /* terminator */
};

View file

@ -0,0 +1,19 @@
#ifndef __SOUND_PRODIGY192_H
#define __SOUND_PRODIGY192_H
#define PRODIGY192_DEVICE_DESC "{AudioTrak,Prodigy 192},"
#define PRODIGY192_STAC9460_ADDR 0x54
#define VT1724_SUBDEVICE_PRODIGY192VE 0x34495345 /* PRODIGY 192 VE */
/*
* AudioTrak Prodigy192 GPIO definitions for MI/ODI/O card with
* AK4114 (SPDIF-IN)
*/
#define VT1724_PRODIGY192_CS (1 << 8) /* GPIO8, pin 75 */
#define VT1724_PRODIGY192_CCLK (1 << 9) /* GPIO9, pin 76 */
#define VT1724_PRODIGY192_CDOUT (1 << 10) /* GPIO10, pin 77 */
#define VT1724_PRODIGY192_CDIN (1 << 11) /* GPIO11, pin 86 */
extern struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[];
#endif /* __SOUND_PRODIGY192_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,38 @@
#ifndef __SOUND_PRODIGY_HIFI_H
#define __SOUND_PRODIGY_HIFI_H
/*
* ALSA driver for VIA VT1724 (Envy24HT)
*
* Lowlevel functions for Audiotrak Prodigy Hifi
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.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
*
*/
#define PRODIGY_HIFI_DEVICE_DESC "{Audiotrak,Prodigy 7.1 HIFI},"\
"{Audiotrak Prodigy HD2},"\
"{Hercules Fortissimo IV},"
#define VT1724_SUBDEVICE_PRODIGY_HIFI 0x38315441 /* PRODIGY 7.1 HIFI */
#define VT1724_SUBDEVICE_PRODIGY_HD2 0x37315441 /* PRODIGY HD2 */
#define VT1724_SUBDEVICE_FORTISSIMO4 0x81160100 /* Fortissimo IV */
extern struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[];
#endif /* __SOUND_PRODIGY_HIFI_H */

464
sound/pci/ice1712/psc724.c Normal file
View file

@ -0,0 +1,464 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Philips PSC724 Ultimate Edge
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
*
* 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/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "psc724.h"
#include "wm8766.h"
#include "wm8776.h"
struct psc724_spec {
struct snd_wm8766 wm8766;
struct snd_wm8776 wm8776;
bool mute_all, jack_detect;
struct snd_ice1712 *ice;
struct delayed_work hp_work;
bool hp_connected;
};
/****************************************************************************/
/* PHILIPS PSC724 ULTIMATE EDGE */
/****************************************************************************/
/*
* VT1722 (Envy24GT) - 6 outputs, 4 inputs (only 2 used), 24-bit/96kHz
*
* system configuration ICE_EEP2_SYSCONF=0x42
* XIN1 49.152MHz
* no MPU401
* one stereo ADC, no S/PDIF receiver
* three stereo DACs (FRONT, REAR, CENTER+LFE)
*
* AC-Link configuration ICE_EEP2_ACLINK=0x80
* use I2S, not AC97
*
* I2S converters feature ICE_EEP2_I2S=0x30
* I2S codec has no volume/mute control feature (bug!)
* I2S codec does not support 96KHz or 192KHz (bug!)
* I2S codec 24bits
*
* S/PDIF configuration ICE_EEP2_SPDIF=0xc1
* Enable integrated S/PDIF transmitter
* internal S/PDIF out implemented
* No S/PDIF input
* External S/PDIF out implemented
*
*
* ** connected chips **
*
* WM8776
* 2-channel DAC used for main output and stereo ADC (with 10-channel MUX)
* AIN1: LINE IN, AIN2: CD/VIDEO, AIN3: AUX, AIN4: Front MIC, AIN5: Rear MIC
* Controlled by I2C using VT1722 I2C interface:
* MODE (pin16) -- GND
* CE (pin17) -- GND I2C mode (address=0x34)
* DI (pin18) -- SDA (VT1722 pin70)
* CL (pin19) -- SCLK (VT1722 pin71)
*
* WM8766
* 6-channel DAC used for rear & center/LFE outputs (only 4 channels used)
* Controlled by SPI using VT1722 GPIO pins:
* MODE (pin 1) -- GPIO19 (VT1722 pin99)
* ML/I2S (pin11) -- GPIO18 (VT1722 pin98)
* MC/IWL (pin12) -- GPIO17 (VT1722 pin97)
* MD/DM (pin13) -- GPIO16 (VT1722 pin96)
* MUTE (pin14) -- GPIO20 (VT1722 pin101)
*
* GPIO14 is used as input for headphone jack detection (1 = connected)
* GPIO22 is used as MUTE ALL output, grounding all 6 channels
*
* ** output pins and device names **
*
* 5.1ch name -- output connector color -- device (-D option)
*
* FRONT 2ch -- green -- plughw:0,0
* CENTER(Lch) SUBWOOFER(Rch) -- orange -- plughw:0,2,0
* REAR 2ch -- black -- plughw:0,2,1
*/
/* codec access low-level functions */
#define GPIO_HP_JACK (1 << 14)
#define GPIO_MUTE_SUR (1 << 20)
#define GPIO_MUTE_ALL (1 << 22)
#define JACK_INTERVAL 1000
#define PSC724_SPI_DELAY 1
#define PSC724_SPI_DATA (1 << 16)
#define PSC724_SPI_CLK (1 << 17)
#define PSC724_SPI_LOAD (1 << 18)
#define PSC724_SPI_MASK (PSC724_SPI_DATA | PSC724_SPI_CLK | PSC724_SPI_LOAD)
static void psc724_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
{
struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8766);
struct snd_ice1712 *ice = spec->ice;
u32 st, bits;
int i;
snd_ice1712_save_gpio_status(ice);
st = ((addr & 0x7f) << 9) | (data & 0x1ff);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | PSC724_SPI_MASK);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~PSC724_SPI_MASK);
bits = snd_ice1712_gpio_read(ice) & ~PSC724_SPI_MASK;
snd_ice1712_gpio_write(ice, bits);
for (i = 0; i < 16; i++) {
udelay(PSC724_SPI_DELAY);
bits &= ~PSC724_SPI_CLK;
/* MSB first */
st <<= 1;
if (st & 0x10000)
bits |= PSC724_SPI_DATA;
else
bits &= ~PSC724_SPI_DATA;
snd_ice1712_gpio_write(ice, bits);
/* CLOCK high */
udelay(PSC724_SPI_DELAY);
bits |= PSC724_SPI_CLK;
snd_ice1712_gpio_write(ice, bits);
}
/* LOAD high */
udelay(PSC724_SPI_DELAY);
bits |= PSC724_SPI_LOAD;
snd_ice1712_gpio_write(ice, bits);
/* LOAD low, DATA and CLOCK high */
udelay(PSC724_SPI_DELAY);
bits |= (PSC724_SPI_DATA | PSC724_SPI_CLK);
snd_ice1712_gpio_write(ice, bits);
snd_ice1712_restore_gpio_status(ice);
}
static void psc724_wm8776_write(struct snd_wm8776 *wm, u8 addr, u8 data)
{
struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8776);
snd_vt1724_write_i2c(spec->ice, 0x34, addr, data);
}
/* mute all */
static void psc724_set_master_switch(struct snd_ice1712 *ice, bool on)
{
unsigned int bits = snd_ice1712_gpio_read(ice);
struct psc724_spec *spec = ice->spec;
spec->mute_all = !on;
if (on)
bits &= ~(GPIO_MUTE_ALL | GPIO_MUTE_SUR);
else
bits |= GPIO_MUTE_ALL | GPIO_MUTE_SUR;
snd_ice1712_gpio_write(ice, bits);
}
static bool psc724_get_master_switch(struct snd_ice1712 *ice)
{
struct psc724_spec *spec = ice->spec;
return !spec->mute_all;
}
/* jack detection */
static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
{
struct psc724_spec *spec = ice->spec;
struct snd_ctl_elem_id elem_id;
struct snd_kcontrol *kctl;
u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD;
psc724_set_master_switch(ice, !hp_connected);
if (!hp_connected)
power |= WM8776_PWR_HPPD;
snd_wm8776_set_power(&spec->wm8776, power);
spec->hp_connected = hp_connected;
/* notify about master speaker mute change */
memset(&elem_id, 0, sizeof(elem_id));
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strlcpy(elem_id.name, "Master Speakers Playback Switch",
sizeof(elem_id.name));
kctl = snd_ctl_find_id(ice->card, &elem_id);
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
/* and headphone mute change */
strlcpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name,
sizeof(elem_id.name));
kctl = snd_ctl_find_id(ice->card, &elem_id);
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
}
static void psc724_update_hp_jack_state(struct work_struct *work)
{
struct psc724_spec *spec = container_of(work, struct psc724_spec,
hp_work.work);
struct snd_ice1712 *ice = spec->ice;
bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK;
schedule_delayed_work(&spec->hp_work, msecs_to_jiffies(JACK_INTERVAL));
if (hp_connected == spec->hp_connected)
return;
psc724_set_jack_state(ice, hp_connected);
}
static void psc724_set_jack_detection(struct snd_ice1712 *ice, bool on)
{
struct psc724_spec *spec = ice->spec;
if (spec->jack_detect == on)
return;
spec->jack_detect = on;
if (on) {
bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK;
psc724_set_jack_state(ice, hp_connected);
schedule_delayed_work(&spec->hp_work,
msecs_to_jiffies(JACK_INTERVAL));
} else
cancel_delayed_work_sync(&spec->hp_work);
}
static bool psc724_get_jack_detection(struct snd_ice1712 *ice)
{
struct psc724_spec *spec = ice->spec;
return spec->jack_detect;
}
/* mixer controls */
struct psc724_control {
const char *name;
void (*set)(struct snd_ice1712 *ice, bool on);
bool (*get)(struct snd_ice1712 *ice);
};
static const struct psc724_control psc724_cont[] = {
{
.name = "Master Speakers Playback Switch",
.set = psc724_set_master_switch,
.get = psc724_get_master_switch,
},
{
.name = "Headphone Jack Detection Playback Switch",
.set = psc724_set_jack_detection,
.get = psc724_get_jack_detection,
},
};
static int psc724_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
ucontrol->value.integer.value[0] = psc724_cont[n].get(ice);
return 0;
}
static int psc724_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
psc724_cont[n].set(ice, ucontrol->value.integer.value[0]);
return 0;
}
static const char *front_volume = "Front Playback Volume";
static const char *front_switch = "Front Playback Switch";
static const char *front_zc = "Front Zero Cross Detect Playback Switch";
static const char *front_izd = "Front Infinite Zero Detect Playback Switch";
static const char *front_phase = "Front Phase Invert Playback Switch";
static const char *front_deemph = "Front Deemphasis Playback Switch";
static const char *ain1_switch = "Line Capture Switch";
static const char *ain2_switch = "CD Capture Switch";
static const char *ain3_switch = "AUX Capture Switch";
static const char *ain4_switch = "Front Mic Capture Switch";
static const char *ain5_switch = "Rear Mic Capture Switch";
static const char *rear_volume = "Surround Playback Volume";
static const char *clfe_volume = "CLFE Playback Volume";
static const char *rear_switch = "Surround Playback Switch";
static const char *clfe_switch = "CLFE Playback Switch";
static const char *rear_phase = "Surround Phase Invert Playback Switch";
static const char *clfe_phase = "CLFE Phase Invert Playback Switch";
static const char *rear_deemph = "Surround Deemphasis Playback Switch";
static const char *clfe_deemph = "CLFE Deemphasis Playback Switch";
static const char *rear_clfe_izd = "Rear Infinite Zero Detect Playback Switch";
static const char *rear_clfe_zc = "Rear Zero Cross Detect Playback Switch";
static int psc724_add_controls(struct snd_ice1712 *ice)
{
struct snd_kcontrol_new cont;
struct snd_kcontrol *ctl;
int err, i;
struct psc724_spec *spec = ice->spec;
spec->wm8776.ctl[WM8776_CTL_DAC_VOL].name = front_volume;
spec->wm8776.ctl[WM8776_CTL_DAC_SW].name = front_switch;
spec->wm8776.ctl[WM8776_CTL_DAC_ZC_SW].name = front_zc;
spec->wm8776.ctl[WM8776_CTL_AUX_SW].name = NULL;
spec->wm8776.ctl[WM8776_CTL_DAC_IZD_SW].name = front_izd;
spec->wm8776.ctl[WM8776_CTL_PHASE_SW].name = front_phase;
spec->wm8776.ctl[WM8776_CTL_DEEMPH_SW].name = front_deemph;
spec->wm8776.ctl[WM8776_CTL_INPUT1_SW].name = ain1_switch;
spec->wm8776.ctl[WM8776_CTL_INPUT2_SW].name = ain2_switch;
spec->wm8776.ctl[WM8776_CTL_INPUT3_SW].name = ain3_switch;
spec->wm8776.ctl[WM8776_CTL_INPUT4_SW].name = ain4_switch;
spec->wm8776.ctl[WM8776_CTL_INPUT5_SW].name = ain5_switch;
snd_wm8776_build_controls(&spec->wm8776);
spec->wm8766.ctl[WM8766_CTL_CH1_VOL].name = rear_volume;
spec->wm8766.ctl[WM8766_CTL_CH2_VOL].name = clfe_volume;
spec->wm8766.ctl[WM8766_CTL_CH3_VOL].name = NULL;
spec->wm8766.ctl[WM8766_CTL_CH1_SW].name = rear_switch;
spec->wm8766.ctl[WM8766_CTL_CH2_SW].name = clfe_switch;
spec->wm8766.ctl[WM8766_CTL_CH3_SW].name = NULL;
spec->wm8766.ctl[WM8766_CTL_PHASE1_SW].name = rear_phase;
spec->wm8766.ctl[WM8766_CTL_PHASE2_SW].name = clfe_phase;
spec->wm8766.ctl[WM8766_CTL_PHASE3_SW].name = NULL;
spec->wm8766.ctl[WM8766_CTL_DEEMPH1_SW].name = rear_deemph;
spec->wm8766.ctl[WM8766_CTL_DEEMPH2_SW].name = clfe_deemph;
spec->wm8766.ctl[WM8766_CTL_DEEMPH3_SW].name = NULL;
spec->wm8766.ctl[WM8766_CTL_IZD_SW].name = rear_clfe_izd;
spec->wm8766.ctl[WM8766_CTL_ZC_SW].name = rear_clfe_zc;
snd_wm8766_build_controls(&spec->wm8766);
memset(&cont, 0, sizeof(cont));
cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
for (i = 0; i < ARRAY_SIZE(psc724_cont); i++) {
cont.private_value = i;
cont.name = psc724_cont[i].name;
cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
cont.info = snd_ctl_boolean_mono_info;
cont.get = psc724_ctl_get;
cont.put = psc724_ctl_put;
ctl = snd_ctl_new1(&cont, ice);
if (!ctl)
return -ENOMEM;
err = snd_ctl_add(ice->card, ctl);
if (err < 0)
return err;
}
return 0;
}
static void psc724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
{
struct psc724_spec *spec = ice->spec;
/* restore codec volume settings after rate change (PMCLK stop) */
snd_wm8776_volume_restore(&spec->wm8776);
snd_wm8766_volume_restore(&spec->wm8766);
}
/* power management */
#ifdef CONFIG_PM_SLEEP
static int psc724_resume(struct snd_ice1712 *ice)
{
struct psc724_spec *spec = ice->spec;
snd_wm8776_resume(&spec->wm8776);
snd_wm8766_resume(&spec->wm8766);
return 0;
}
#endif
/* init */
static int psc724_init(struct snd_ice1712 *ice)
{
struct psc724_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
spec->ice = ice;
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
spec->wm8776.ops.write = psc724_wm8776_write;
spec->wm8776.card = ice->card;
snd_wm8776_init(&spec->wm8776);
spec->wm8766.ops.write = psc724_wm8766_write;
spec->wm8766.card = ice->card;
#ifdef CONFIG_PM_SLEEP
ice->pm_resume = psc724_resume;
ice->pm_suspend_enabled = 1;
#endif
snd_wm8766_init(&spec->wm8766);
snd_wm8766_set_if(&spec->wm8766,
WM8766_IF_FMT_I2S | WM8766_IF_IWL_24BIT);
ice->gpio.set_pro_rate = psc724_set_pro_rate;
INIT_DELAYED_WORK(&spec->hp_work, psc724_update_hp_jack_state);
psc724_set_jack_detection(ice, true);
return 0;
}
static void psc724_exit(struct snd_ice1712 *ice)
{
struct psc724_spec *spec = ice->spec;
cancel_delayed_work_sync(&spec->hp_work);
}
/* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */
static unsigned char psc724_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x42, /* 49.152MHz, 1 ADC, 3 DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf0, /* I2S volume, 96kHz, 24bit */
[ICE_EEP2_SPDIF] = 0xc1, /* spdif out-en, out-int, no input */
/* GPIO outputs */
[ICE_EEP2_GPIO_DIR2] = 0x5f, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */
/* GPIO write enable */
[ICE_EEP2_GPIO_MASK] = 0xff, /* read-only */
[ICE_EEP2_GPIO_MASK1] = 0xff, /* read-only */
[ICE_EEP2_GPIO_MASK2] = 0xa0, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */
/* GPIO initial state */
[ICE_EEP2_GPIO_STATE2] = 0x20, /* unmuted, all WM8766 pins low */
};
struct snd_ice1712_card_info snd_vt1724_psc724_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PSC724,
.name = "Philips PSC724 Ultimate Edge",
.model = "psc724",
.chip_init = psc724_init,
.chip_exit = psc724_exit,
.build_controls = psc724_add_controls,
.eeprom_size = sizeof(psc724_eeprom),
.eeprom_data = psc724_eeprom,
},
{} /*terminator*/
};

View file

@ -0,0 +1,13 @@
#ifndef __SOUND_PSC724_H
#define __SOUND_PSC724_H
/* ID */
#define PSC724_DEVICE_DESC \
"{Philips,PSC724 Ultimate Edge},"
#define VT1724_SUBDEVICE_PSC724 0xab170619
/* entry struct */
extern struct snd_ice1712_card_info snd_vt1724_psc724_cards[];
#endif /* __SOUND_PSC724_H */

1132
sound/pci/ice1712/quartet.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
#ifndef __SOUND_QTET_H
#define __SOUND_QTET_H
#define QTET_DEVICE_DESC "{Infrasonic,Quartet},"
#define VT1724_SUBDEVICE_QTET 0x30305349 /* Infrasonic Quartet */
extern struct snd_ice1712_card_info snd_vt1724_qtet_cards[];
#endif /* __SOUND_QTET_H */

643
sound/pci/ice1712/revo.c Normal file
View file

@ -0,0 +1,643 @@
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for M-Audio Audiophile 192, Revolution 7.1 and 5.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "revo.h"
/* a non-standard I2C device for revo51 */
struct revo51_spec {
struct snd_i2c_device *dev;
struct snd_pt2258 *pt2258;
struct ak4114 *ak4114;
};
static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
{
/* assert PRST# to converters; MT05 bit 7 */
outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
mdelay(5);
/* deassert PRST# */
outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
}
/*
* change the rate of Envy24HT, AK4355 and AK4381
*/
static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
unsigned char old, tmp, dfs;
int reg, shift;
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
/* adjust DFS on codecs */
if (rate > 96000)
dfs = 2;
else if (rate > 48000)
dfs = 1;
else
dfs = 0;
if (ak->type == SND_AK4355 || ak->type == SND_AK4358) {
reg = 2;
shift = 4;
} else {
reg = 1;
shift = 3;
}
tmp = snd_akm4xxx_get(ak, 0, reg);
old = (tmp >> shift) & 0x03;
if (old == dfs)
return;
/* reset DFS */
snd_akm4xxx_reset(ak, 1);
tmp = snd_akm4xxx_get(ak, 0, reg);
tmp &= ~(0x03 << shift);
tmp |= dfs << shift;
/* snd_akm4xxx_write(ak, 0, reg, tmp); */
snd_akm4xxx_set(ak, 0, reg, tmp); /* value is written in reset(0) */
snd_akm4xxx_reset(ak, 0);
}
/*
* I2C access to the PT2258 volume controller on GPIO 6/7 (Revolution 5.1)
*/
static void revo_i2c_start(struct snd_i2c_bus *bus)
{
struct snd_ice1712 *ice = bus->private_data;
snd_ice1712_save_gpio_status(ice);
}
static void revo_i2c_stop(struct snd_i2c_bus *bus)
{
struct snd_ice1712 *ice = bus->private_data;
snd_ice1712_restore_gpio_status(ice);
}
static void revo_i2c_direction(struct snd_i2c_bus *bus, int clock, int data)
{
struct snd_ice1712 *ice = bus->private_data;
unsigned int mask, val;
val = 0;
if (clock)
val |= VT1724_REVO_I2C_CLOCK; /* write SCL */
if (data)
val |= VT1724_REVO_I2C_DATA; /* write SDA */
mask = VT1724_REVO_I2C_CLOCK | VT1724_REVO_I2C_DATA;
ice->gpio.direction &= ~mask;
ice->gpio.direction |= val;
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ~mask);
}
static void revo_i2c_setlines(struct snd_i2c_bus *bus, int clk, int data)
{
struct snd_ice1712 *ice = bus->private_data;
unsigned int val = 0;
if (clk)
val |= VT1724_REVO_I2C_CLOCK;
if (data)
val |= VT1724_REVO_I2C_DATA;
snd_ice1712_gpio_write_bits(ice,
VT1724_REVO_I2C_DATA |
VT1724_REVO_I2C_CLOCK, val);
udelay(5);
}
static int revo_i2c_getdata(struct snd_i2c_bus *bus, int ack)
{
struct snd_ice1712 *ice = bus->private_data;
int bit;
if (ack)
udelay(5);
bit = snd_ice1712_gpio_read_bits(ice, VT1724_REVO_I2C_DATA) ? 1 : 0;
return bit;
}
static struct snd_i2c_bit_ops revo51_bit_ops = {
.start = revo_i2c_start,
.stop = revo_i2c_stop,
.direction = revo_i2c_direction,
.setlines = revo_i2c_setlines,
.getdata = revo_i2c_getdata,
};
static int revo51_i2c_init(struct snd_ice1712 *ice,
struct snd_pt2258 *pt)
{
struct revo51_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
/* create the I2C bus */
err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c);
if (err < 0)
return err;
ice->i2c->private_data = ice;
ice->i2c->hw_ops.bit = &revo51_bit_ops;
/* create the I2C device */
err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, &spec->dev);
if (err < 0)
return err;
pt->card = ice->card;
pt->i2c_bus = ice->i2c;
pt->i2c_dev = spec->dev;
spec->pt2258 = pt;
snd_pt2258_reset(pt);
return 0;
}
/*
* initialize the chips on M-Audio Revolution cards
*/
#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
static const struct snd_akm4xxx_dac_channel revo71_front[] = {
{
.name = "PCM Playback Volume",
.num_channels = 2,
/* front channels DAC supports muting */
.switch_name = "PCM Playback Switch",
},
};
static const struct snd_akm4xxx_dac_channel revo71_surround[] = {
AK_DAC("PCM Center Playback Volume", 1),
AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Side Playback Volume", 2),
AK_DAC("PCM Rear Playback Volume", 2),
};
static const struct snd_akm4xxx_dac_channel revo51_dac[] = {
AK_DAC("PCM Playback Volume", 2),
AK_DAC("PCM Center Playback Volume", 1),
AK_DAC("PCM LFE Playback Volume", 1),
AK_DAC("PCM Rear Playback Volume", 2),
AK_DAC("PCM Headphone Volume", 2),
};
static const char *revo51_adc_input_names[] = {
"Mic",
"Line",
"CD",
NULL
};
static const struct snd_akm4xxx_adc_channel revo51_adc[] = {
{
.name = "PCM Capture Volume",
.switch_name = "PCM Capture Switch",
.num_channels = 2,
.input_names = revo51_adc_input_names
},
};
static struct snd_akm4xxx akm_revo_front = {
.type = SND_AK4381,
.num_dacs = 2,
.ops = {
.set_rate_val = revo_set_rate_val
},
.dac_info = revo71_front,
};
static struct snd_ak4xxx_private akm_revo_front_priv = {
.caddr = 1,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
static struct snd_akm4xxx akm_revo_surround = {
.type = SND_AK4355,
.idx_offset = 1,
.num_dacs = 6,
.ops = {
.set_rate_val = revo_set_rate_val
},
.dac_info = revo71_surround,
};
static struct snd_ak4xxx_private akm_revo_surround_priv = {
.caddr = 3,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
static struct snd_akm4xxx akm_revo51 = {
.type = SND_AK4358,
.num_dacs = 8,
.ops = {
.set_rate_val = revo_set_rate_val
},
.dac_info = revo51_dac,
};
static struct snd_ak4xxx_private akm_revo51_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.cs_addr = VT1724_REVO_CS1,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
static struct snd_akm4xxx akm_revo51_adc = {
.type = SND_AK5365,
.num_adcs = 2,
.adc_info = revo51_adc,
};
static struct snd_ak4xxx_private akm_revo51_adc_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.cs_addr = VT1724_REVO_CS0,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
static struct snd_pt2258 ptc_revo51_volume;
/* AK4358 for AP192 DAC, AK5385A for ADC */
static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
struct snd_ice1712 *ice = ak->private_data[0];
int dfs;
revo_set_rate_val(ak, rate);
/* reset CKS */
snd_ice1712_gpio_write_bits(ice, 1 << 8, rate > 96000 ? 1 << 8 : 0);
/* reset DFS pins of AK5385A for ADC, too */
if (rate > 96000)
dfs = 2;
else if (rate > 48000)
dfs = 1;
else
dfs = 0;
snd_ice1712_gpio_write_bits(ice, 3 << 9, dfs << 9);
/* reset ADC */
snd_ice1712_gpio_write_bits(ice, 1 << 11, 0);
snd_ice1712_gpio_write_bits(ice, 1 << 11, 1 << 11);
}
static const struct snd_akm4xxx_dac_channel ap192_dac[] = {
AK_DAC("PCM Playback Volume", 2)
};
static struct snd_akm4xxx akm_ap192 = {
.type = SND_AK4358,
.num_dacs = 2,
.ops = {
.set_rate_val = ap192_set_rate_val
},
.dac_info = ap192_dac,
};
static struct snd_ak4xxx_private akm_ap192_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3,
.cs_addr = VT1724_REVO_CS3,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
/* AK4114 support on Audiophile 192 */
/* CDTO (pin 32) -- GPIO2 pin 52
* CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358)
* CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358)
* CSN (pin 35) -- GPIO7 pin 59
*/
#define AK4114_ADDR 0x00
static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
unsigned int data, int idx)
{
for (; idx >= 0; idx--) {
/* drop clock */
gpio &= ~VT1724_REVO_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
/* set data */
if (data & (1 << idx))
gpio |= VT1724_REVO_CDOUT;
else
gpio &= ~VT1724_REVO_CDOUT;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
/* raise clock */
gpio |= VT1724_REVO_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
}
}
static unsigned char read_data(struct snd_ice1712 *ice, unsigned int gpio,
int idx)
{
unsigned char data = 0;
for (; idx >= 0; idx--) {
/* drop clock */
gpio &= ~VT1724_REVO_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
/* read data */
if (snd_ice1712_gpio_read(ice) & VT1724_REVO_CDIN)
data |= (1 << idx);
udelay(1);
/* raise clock */
gpio |= VT1724_REVO_CCLK;
snd_ice1712_gpio_write(ice, gpio);
udelay(1);
}
return data;
}
static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
{
unsigned int tmp;
snd_ice1712_save_gpio_status(ice);
tmp = snd_ice1712_gpio_read(ice);
tmp |= VT1724_REVO_CCLK; /* high at init */
tmp |= VT1724_REVO_CS0;
tmp &= ~VT1724_REVO_CS3;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
return tmp;
}
static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
{
tmp |= VT1724_REVO_CS3;
tmp |= VT1724_REVO_CS0;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
snd_ice1712_restore_gpio_status(ice);
}
static void ap192_ak4114_write(void *private_data, unsigned char addr,
unsigned char data)
{
struct snd_ice1712 *ice = private_data;
unsigned int tmp, addrdata;
tmp = ap192_4wire_start(ice);
addrdata = (AK4114_ADDR << 6) | 0x20 | (addr & 0x1f);
addrdata = (addrdata << 8) | data;
write_data(ice, tmp, addrdata, 15);
ap192_4wire_finish(ice, tmp);
}
static unsigned char ap192_ak4114_read(void *private_data, unsigned char addr)
{
struct snd_ice1712 *ice = private_data;
unsigned int tmp;
unsigned char data;
tmp = ap192_4wire_start(ice);
write_data(ice, tmp, (AK4114_ADDR << 6) | (addr & 0x1f), 7);
data = read_data(ice, tmp, 7);
ap192_4wire_finish(ice, tmp);
return data;
}
static int ap192_ak4114_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
AK4114_RST | AK4114_PWN | AK4114_OCKS0,
AK4114_DIF_I24I2S,
AK4114_TX1E,
AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(0),
0,
0
};
static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
int err;
struct revo51_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
err = snd_ak4114_create(ice->card,
ap192_ak4114_read,
ap192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
ice, &spec->ak4114);
/* AK4114 in Revo cannot detect external rate correctly.
* No reason to stop capture stream due to incorrect checks */
spec->ak4114->check_flags = AK4114_CHECK_NO_RATE;
return 0; /* error ignored; it's no fatal error */
}
static int revo_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
int err;
/* determine I2C, DACs and ADCs */
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_REVOLUTION71:
ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
ice->gpio.i2s_mclk_changed = revo_i2s_mclk_changed;
break;
case VT1724_SUBDEVICE_REVOLUTION51:
ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
break;
case VT1724_SUBDEVICE_AUDIOPHILE192:
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
break;
default:
snd_BUG();
return -EINVAL;
}
/* second stage of initialization, analog parts and others */
ak = ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ak)
return -ENOMEM;
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_REVOLUTION71:
ice->akm_codecs = 2;
err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front,
&akm_revo_front_priv, ice);
if (err < 0)
return err;
err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo_surround,
&akm_revo_surround_priv, ice);
if (err < 0)
return err;
/* unmute all codecs */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
VT1724_REVO_MUTE);
break;
case VT1724_SUBDEVICE_REVOLUTION51:
ice->akm_codecs = 2;
err = snd_ice1712_akm4xxx_init(ak, &akm_revo51,
&akm_revo51_priv, ice);
if (err < 0)
return err;
err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo51_adc,
&akm_revo51_adc_priv, ice);
if (err < 0)
return err;
err = revo51_i2c_init(ice, &ptc_revo51_volume);
if (err < 0)
return err;
/* unmute all codecs */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
VT1724_REVO_MUTE);
break;
case VT1724_SUBDEVICE_AUDIOPHILE192:
ice->akm_codecs = 1;
err = snd_ice1712_akm4xxx_init(ak, &akm_ap192, &akm_ap192_priv,
ice);
if (err < 0)
return err;
err = ap192_ak4114_init(ice);
if (err < 0)
return err;
/* unmute all codecs */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
VT1724_REVO_MUTE);
break;
}
return 0;
}
static int revo_add_controls(struct snd_ice1712 *ice)
{
struct revo51_spec *spec = ice->spec;
int err;
switch (ice->eeprom.subvendor) {
case VT1724_SUBDEVICE_REVOLUTION71:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
break;
case VT1724_SUBDEVICE_REVOLUTION51:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
spec = ice->spec;
err = snd_pt2258_build_controls(spec->pt2258);
if (err < 0)
return err;
break;
case VT1724_SUBDEVICE_AUDIOPHILE192:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
/* only capture SPDIF over AK4114 */
err = snd_ak4114_build(spec->ak4114, NULL,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
break;
}
return 0;
}
/* entry point */
struct snd_ice1712_card_info snd_vt1724_revo_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_REVOLUTION71,
.name = "M Audio Revolution-7.1",
.model = "revo71",
.chip_init = revo_init,
.build_controls = revo_add_controls,
},
{
.subvendor = VT1724_SUBDEVICE_REVOLUTION51,
.name = "M Audio Revolution-5.1",
.model = "revo51",
.chip_init = revo_init,
.build_controls = revo_add_controls,
},
{
.subvendor = VT1724_SUBDEVICE_AUDIOPHILE192,
.name = "M Audio Audiophile192",
.model = "ap192",
.chip_init = revo_init,
.build_controls = revo_add_controls,
},
{ } /* terminator */
};

55
sound/pci/ice1712/revo.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef __SOUND_REVO_H
#define __SOUND_REVO_H
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for M-Audio Revolution 7.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.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
*
*/
#define REVO_DEVICE_DESC \
"{MidiMan M Audio,Revolution 7.1},"\
"{MidiMan M Audio,Revolution 5.1},"\
"{MidiMan M Audio,Audiophile 192},"
#define VT1724_SUBDEVICE_REVOLUTION71 0x12143036
#define VT1724_SUBDEVICE_REVOLUTION51 0x12143136
#define VT1724_SUBDEVICE_AUDIOPHILE192 0x12143236
/* entry point */
extern struct snd_ice1712_card_info snd_vt1724_revo_cards[];
/*
* MidiMan M-Audio Revolution GPIO definitions
*/
#define VT1724_REVO_CCLK 0x02
#define VT1724_REVO_CDIN 0x04 /* not used */
#define VT1724_REVO_CDOUT 0x08
#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for (revo51) */
#define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */
#define VT1724_REVO_CS2 0x40 /* surround AKM4355 CS (revo71) */
#define VT1724_REVO_I2C_DATA 0x40 /* I2C: PT 2258 SDA (on revo51) */
#define VT1724_REVO_I2C_CLOCK 0x80 /* I2C: PT 2258 SCL (on revo51) */
#define VT1724_REVO_CS3 0x80 /* AK4114 for AP192 */
#define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */
#endif /* __SOUND_REVO_H */

773
sound/pci/ice1712/se.c Normal file
View file

@ -0,0 +1,773 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI
*
* Copyright (c) 2007 Shin-ya Okada sh_okada(at)d4.dion.ne.jp
* (at) -> @
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "se.h"
struct se_spec {
struct {
unsigned char ch1, ch2;
} vol[8];
};
/****************************************************************************/
/* ONKYO WAVIO SE-200PCI */
/****************************************************************************/
/*
* system configuration ICE_EEP2_SYSCONF=0x4b
* XIN1 49.152MHz
* not have UART
* one stereo ADC and a S/PDIF receiver connected
* four stereo DACs connected
*
* AC-Link configuration ICE_EEP2_ACLINK=0x80
* use I2C, not use AC97
*
* I2S converters feature ICE_EEP2_I2S=0x78
* I2S codec has no volume/mute control feature
* I2S codec supports 96KHz and 192KHz
* I2S codec 24bits
*
* S/PDIF configuration ICE_EEP2_SPDIF=0xc3
* Enable integrated S/PDIF transmitter
* internal S/PDIF out implemented
* S/PDIF is stereo
* External S/PDIF out implemented
*
*
* ** connected chips **
*
* WM8740
* A 2ch-DAC of main outputs.
* It setuped as I2S mode by wire, so no way to setup from software.
* The sample-rate are automatically changed.
* ML/I2S (28pin) --------+
* MC/DM1 (27pin) -- 5V |
* MD/DM0 (26pin) -- GND |
* MUTEB (25pin) -- NC |
* MODE (24pin) -- GND |
* CSBIW (23pin) --------+
* |
* RSTB (22pin) --R(1K)-+
* Probably it reduce the noise from the control line.
*
* WM8766
* A 6ch-DAC for surrounds.
* It's control wire was connected to GPIOxx (3-wire serial interface)
* ML/I2S (11pin) -- GPIO18
* MC/IWL (12pin) -- GPIO17
* MD/DM (13pin) -- GPIO16
* MUTE (14pin) -- GPIO01
*
* WM8776
* A 2ch-ADC(with 10ch-selector) plus 2ch-DAC.
* It's control wire was connected to SDA/SCLK (2-wire serial interface)
* MODE (16pin) -- R(1K) -- GND
* CE (17pin) -- R(1K) -- GND 2-wire mode (address=0x34)
* DI (18pin) -- SDA
* CL (19pin) -- SCLK
*
*
* ** output pins and device names **
*
* 7.1ch name -- output connector color -- device (-D option)
*
* FRONT 2ch -- green -- plughw:0,0
* CENTER(Lch) SUBWOOFER(Rch) -- black -- plughw:0,2,0
* SURROUND 2ch -- orange -- plughw:0,2,1
* SURROUND BACK 2ch -- white -- plughw:0,2,2
*
*/
/****************************************************************************/
/* WM8740 interface */
/****************************************************************************/
static void se200pci_WM8740_init(struct snd_ice1712 *ice)
{
/* nothing to do */
}
static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice,
unsigned int rate)
{
/* nothing to do */
}
/****************************************************************************/
/* WM8766 interface */
/****************************************************************************/
static void se200pci_WM8766_write(struct snd_ice1712 *ice,
unsigned int addr, unsigned int data)
{
unsigned int st;
unsigned int bits;
int i;
const unsigned int DATA = 0x010000;
const unsigned int CLOCK = 0x020000;
const unsigned int LOAD = 0x040000;
const unsigned int ALL_MASK = (DATA | CLOCK | LOAD);
snd_ice1712_save_gpio_status(ice);
st = ((addr & 0x7f) << 9) | (data & 0x1ff);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK);
bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK;
snd_ice1712_gpio_write(ice, bits);
for (i = 0; i < 16; i++) {
udelay(1);
bits &= ~CLOCK;
st = (st << 1);
if (st & 0x10000)
bits |= DATA;
else
bits &= ~DATA;
snd_ice1712_gpio_write(ice, bits);
udelay(1);
bits |= CLOCK;
snd_ice1712_gpio_write(ice, bits);
}
udelay(1);
bits |= LOAD;
snd_ice1712_gpio_write(ice, bits);
udelay(1);
bits |= (DATA | CLOCK);
snd_ice1712_gpio_write(ice, bits);
snd_ice1712_restore_gpio_status(ice);
}
static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
unsigned int vol1, unsigned int vol2)
{
switch (ch) {
case 0:
se200pci_WM8766_write(ice, 0x000, vol1);
se200pci_WM8766_write(ice, 0x001, vol2 | 0x100);
break;
case 1:
se200pci_WM8766_write(ice, 0x004, vol1);
se200pci_WM8766_write(ice, 0x005, vol2 | 0x100);
break;
case 2:
se200pci_WM8766_write(ice, 0x006, vol1);
se200pci_WM8766_write(ice, 0x007, vol2 | 0x100);
break;
}
}
static void se200pci_WM8766_init(struct snd_ice1712 *ice)
{
se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
udelay(10);
se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */
se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */
se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */
se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */
se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */
se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */
se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */
se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */
se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */
}
static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice,
unsigned int rate)
{
if (rate > 96000)
se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */
else
se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
}
/****************************************************************************/
/* WM8776 interface */
/****************************************************************************/
static void se200pci_WM8776_write(struct snd_ice1712 *ice,
unsigned int addr, unsigned int data)
{
unsigned int val;
val = (addr << 9) | data;
snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff);
}
static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice,
unsigned int vol1, unsigned int vol2)
{
se200pci_WM8776_write(ice, 0x03, vol1);
se200pci_WM8776_write(ice, 0x04, vol2 | 0x100);
}
static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
unsigned int vol1, unsigned int vol2)
{
se200pci_WM8776_write(ice, 0x0e, vol1);
se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
}
static const char * const se200pci_sel[] = {
"LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
};
static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
unsigned int sel)
{
static unsigned char vals[] = {
/* LINE, CD, MIC, ALL, GND */
0x10, 0x04, 0x08, 0x1c, 0x03
};
if (sel > 4)
sel = 4;
se200pci_WM8776_write(ice, 0x15, vals[sel]);
}
static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
{
/* AFL -- After Fader Listening */
if (afl)
se200pci_WM8776_write(ice, 0x16, 0x005);
else
se200pci_WM8776_write(ice, 0x16, 0x001);
}
static const char * const se200pci_agc[] = {
"Off", "LimiterMode", "ALCMode", NULL
};
static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
{
/* AGC -- Auto Gain Control of the input */
switch (agc) {
case 0:
se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */
break;
case 1:
se200pci_WM8776_write(ice, 0x10, 0x07b);
se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */
break;
case 2:
se200pci_WM8776_write(ice, 0x10, 0x1fb);
se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */
break;
}
}
static void se200pci_WM8776_init(struct snd_ice1712 *ice)
{
int i;
static unsigned short default_values[] = {
0x100, 0x100, 0x100,
0x100, 0x100, 0x100,
0x000, 0x090, 0x000, 0x000,
0x022, 0x022, 0x022,
0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
0x032, 0x000, 0x0a6, 0x001, 0x001
};
se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */
/* ADC and DAC interface is I2S 24bits mode */
/* The sample-rate are automatically changed */
udelay(10);
/* BUT my board can not do reset all, so I load all by manually. */
for (i = 0; i < ARRAY_SIZE(default_values); i++)
se200pci_WM8776_write(ice, i, default_values[i]);
se200pci_WM8776_set_input_selector(ice, 0);
se200pci_WM8776_set_afl(ice, 0);
se200pci_WM8776_set_agc(ice, 0);
se200pci_WM8776_set_input_volume(ice, 0, 0);
se200pci_WM8776_set_output_volume(ice, 0, 0);
/* head phone mute and power down */
se200pci_WM8776_write(ice, 0x00, 0);
se200pci_WM8776_write(ice, 0x01, 0);
se200pci_WM8776_write(ice, 0x02, 0x100);
se200pci_WM8776_write(ice, 0x0d, 0x080);
}
static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice,
unsigned int rate)
{
/* nothing to do */
}
/****************************************************************************/
/* runtime interface */
/****************************************************************************/
static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
{
se200pci_WM8740_set_pro_rate(ice, rate);
se200pci_WM8766_set_pro_rate(ice, rate);
se200pci_WM8776_set_pro_rate(ice, rate);
}
struct se200pci_control {
const char *name;
enum {
WM8766,
WM8776in,
WM8776out,
WM8776sel,
WM8776agc,
WM8776afl
} target;
enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
int ch;
const char * const *member;
const char *comment;
};
static const struct se200pci_control se200pci_cont[] = {
{
.name = "Front Playback Volume",
.target = WM8776out,
.type = VOLUME1,
.comment = "Front(green)"
},
{
.name = "Side Playback Volume",
.target = WM8766,
.type = VOLUME1,
.ch = 1,
.comment = "Surround(orange)"
},
{
.name = "Surround Playback Volume",
.target = WM8766,
.type = VOLUME1,
.ch = 2,
.comment = "SurroundBack(white)"
},
{
.name = "CLFE Playback Volume",
.target = WM8766,
.type = VOLUME1,
.ch = 0,
.comment = "Center(Lch)&SubWoofer(Rch)(black)"
},
{
.name = "Capture Volume",
.target = WM8776in,
.type = VOLUME2
},
{
.name = "Capture Select",
.target = WM8776sel,
.type = ENUM,
.member = se200pci_sel
},
{
.name = "AGC Capture Mode",
.target = WM8776agc,
.type = ENUM,
.member = se200pci_agc
},
{
.name = "AFL Bypass Playback Switch",
.target = WM8776afl,
.type = BOOLEAN
}
};
static int se200pci_get_enum_count(int n)
{
const char * const *member;
int c;
member = se200pci_cont[n].member;
if (!member)
return 0;
for (c = 0; member[c]; c++)
;
return c;
}
static int se200pci_cont_volume_info(struct snd_kcontrol *kc,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0; /* mute */
uinfo->value.integer.max = 0xff; /* 0dB */
return 0;
}
#define se200pci_cont_boolean_info snd_ctl_boolean_mono_info
static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
struct snd_ctl_elem_info *uinfo)
{
int n, c;
n = kc->private_value;
c = se200pci_get_enum_count(n);
if (!c)
return -EINVAL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = c;
if (uinfo->value.enumerated.item >= c)
uinfo->value.enumerated.item = c - 1;
strcpy(uinfo->value.enumerated.name,
se200pci_cont[n].member[uinfo->value.enumerated.item]);
return 0;
}
static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *uc)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
struct se_spec *spec = ice->spec;
int n = kc->private_value;
uc->value.integer.value[0] = spec->vol[n].ch1;
uc->value.integer.value[1] = spec->vol[n].ch2;
return 0;
}
static int se200pci_cont_boolean_get(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *uc)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
struct se_spec *spec = ice->spec;
int n = kc->private_value;
uc->value.integer.value[0] = spec->vol[n].ch1;
return 0;
}
static int se200pci_cont_enum_get(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *uc)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
struct se_spec *spec = ice->spec;
int n = kc->private_value;
uc->value.enumerated.item[0] = spec->vol[n].ch1;
return 0;
}
static void se200pci_cont_update(struct snd_ice1712 *ice, int n)
{
struct se_spec *spec = ice->spec;
switch (se200pci_cont[n].target) {
case WM8766:
se200pci_WM8766_set_volume(ice,
se200pci_cont[n].ch,
spec->vol[n].ch1,
spec->vol[n].ch2);
break;
case WM8776in:
se200pci_WM8776_set_input_volume(ice,
spec->vol[n].ch1,
spec->vol[n].ch2);
break;
case WM8776out:
se200pci_WM8776_set_output_volume(ice,
spec->vol[n].ch1,
spec->vol[n].ch2);
break;
case WM8776sel:
se200pci_WM8776_set_input_selector(ice,
spec->vol[n].ch1);
break;
case WM8776agc:
se200pci_WM8776_set_agc(ice, spec->vol[n].ch1);
break;
case WM8776afl:
se200pci_WM8776_set_afl(ice, spec->vol[n].ch1);
break;
default:
break;
}
}
static int se200pci_cont_volume_put(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *uc)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
struct se_spec *spec = ice->spec;
int n = kc->private_value;
unsigned int vol1, vol2;
int changed;
changed = 0;
vol1 = uc->value.integer.value[0] & 0xff;
vol2 = uc->value.integer.value[1] & 0xff;
if (spec->vol[n].ch1 != vol1) {
spec->vol[n].ch1 = vol1;
changed = 1;
}
if (spec->vol[n].ch2 != vol2) {
spec->vol[n].ch2 = vol2;
changed = 1;
}
if (changed)
se200pci_cont_update(ice, n);
return changed;
}
static int se200pci_cont_boolean_put(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *uc)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
struct se_spec *spec = ice->spec;
int n = kc->private_value;
unsigned int vol1;
vol1 = !!uc->value.integer.value[0];
if (spec->vol[n].ch1 != vol1) {
spec->vol[n].ch1 = vol1;
se200pci_cont_update(ice, n);
return 1;
}
return 0;
}
static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
struct snd_ctl_elem_value *uc)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
struct se_spec *spec = ice->spec;
int n = kc->private_value;
unsigned int vol1;
vol1 = uc->value.enumerated.item[0];
if (vol1 >= se200pci_get_enum_count(n))
return -EINVAL;
if (spec->vol[n].ch1 != vol1) {
spec->vol[n].ch1 = vol1;
se200pci_cont_update(ice, n);
return 1;
}
return 0;
}
static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
static int se200pci_add_controls(struct snd_ice1712 *ice)
{
int i;
struct snd_kcontrol_new cont;
int err;
memset(&cont, 0, sizeof(cont));
cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) {
cont.private_value = i;
cont.name = se200pci_cont[i].name;
cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
cont.tlv.p = NULL;
switch (se200pci_cont[i].type) {
case VOLUME1:
case VOLUME2:
cont.info = se200pci_cont_volume_info;
cont.get = se200pci_cont_volume_get;
cont.put = se200pci_cont_volume_put;
cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
if (se200pci_cont[i].type == VOLUME1)
cont.tlv.p = db_scale_gain1;
else
cont.tlv.p = db_scale_gain2;
break;
case BOOLEAN:
cont.info = se200pci_cont_boolean_info;
cont.get = se200pci_cont_boolean_get;
cont.put = se200pci_cont_boolean_put;
break;
case ENUM:
cont.info = se200pci_cont_enum_info;
cont.get = se200pci_cont_enum_get;
cont.put = se200pci_cont_enum_put;
break;
default:
snd_BUG();
return -EINVAL;
}
err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice));
if (err < 0)
return err;
}
return 0;
}
/****************************************************************************/
/* ONKYO WAVIO SE-90PCI */
/****************************************************************************/
/*
* system configuration ICE_EEP2_SYSCONF=0x4b
* AC-Link configuration ICE_EEP2_ACLINK=0x80
* I2S converters feature ICE_EEP2_I2S=0x78
* S/PDIF configuration ICE_EEP2_SPDIF=0xc3
*
* ** connected chip **
*
* WM8716
* A 2ch-DAC of main outputs.
* It setuped as I2S mode by wire, so no way to setup from software.
* ML/I2S (28pin) -- +5V
* MC/DM1 (27pin) -- GND
* MC/DM0 (26pin) -- GND
* MUTEB (25pin) -- open (internal pull-up)
* MODE (24pin) -- GND
* CSBIWO (23pin) -- +5V
*
*/
/* Nothing to do for this chip. */
/****************************************************************************/
/* probe/initialize/setup */
/****************************************************************************/
static int se_init(struct snd_ice1712 *ice)
{
struct se_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) {
ice->num_total_dacs = 2;
ice->num_total_adcs = 0;
ice->vt1720 = 1;
return 0;
} else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) {
ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
se200pci_WM8740_init(ice);
se200pci_WM8766_init(ice);
se200pci_WM8776_init(ice);
ice->gpio.set_pro_rate = se200pci_set_pro_rate;
return 0;
}
return -ENOENT;
}
static int se_add_controls(struct snd_ice1712 *ice)
{
int err;
err = 0;
/* nothing to do for VT1724_SUBDEVICE_SE90PCI */
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI)
err = se200pci_add_controls(ice);
return err;
}
/****************************************************************************/
/* entry point */
/****************************************************************************/
static unsigned char se200pci_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
[ICE_EEP2_GPIO_DIR] = 0x02, /* WM8766 mute 1=output */
[ICE_EEP2_GPIO_DIR1] = 0x00, /* not used */
[ICE_EEP2_GPIO_DIR2] = 0x07, /* WM8766 ML/MC/MD 1=output */
[ICE_EEP2_GPIO_MASK] = 0x00, /* 0=writable */
[ICE_EEP2_GPIO_MASK1] = 0x00, /* 0=writable */
[ICE_EEP2_GPIO_MASK2] = 0x00, /* 0=writable */
[ICE_EEP2_GPIO_STATE] = 0x00, /* WM8766 mute=0 */
[ICE_EEP2_GPIO_STATE1] = 0x00, /* not used */
[ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */
};
static unsigned char se90pci_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
/* ALL GPIO bits are in input mode */
};
struct snd_ice1712_card_info snd_vt1724_se_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_SE200PCI,
.name = "ONKYO SE200PCI",
.model = "se200pci",
.chip_init = se_init,
.build_controls = se_add_controls,
.eeprom_size = sizeof(se200pci_eeprom),
.eeprom_data = se200pci_eeprom,
},
{
.subvendor = VT1724_SUBDEVICE_SE90PCI,
.name = "ONKYO SE90PCI",
.model = "se90pci",
.chip_init = se_init,
.build_controls = se_add_controls,
.eeprom_size = sizeof(se90pci_eeprom),
.eeprom_data = se90pci_eeprom,
},
{} /*terminator*/
};

15
sound/pci/ice1712/se.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef __SOUND_SE_H
#define __SOUND_SE_H
/* ID */
#define SE_DEVICE_DESC \
"{ONKYO INC,SE-90PCI},"\
"{ONKYO INC,SE-200PCI},"
#define VT1724_SUBDEVICE_SE90PCI 0xb161000
#define VT1724_SUBDEVICE_SE200PCI 0xb160100
/* entry struct */
extern struct snd_ice1712_card_info snd_vt1724_se_cards[];
#endif /* __SOUND_SE_H */

View file

@ -0,0 +1,25 @@
#ifndef __SOUND_STAC946X_H
#define __SOUND_STAC946X_H
#define STAC946X_RESET 0x00
#define STAC946X_STATUS 0x01
#define STAC946X_MASTER_VOLUME 0x02
#define STAC946X_LF_VOLUME 0x03
#define STAC946X_RF_VOLUME 0x04
#define STAC946X_LR_VOLUME 0x05
#define STAC946X_RR_VOLUME 0x06
#define STAC946X_CENTER_VOLUME 0x07
#define STAC946X_LFE_VOLUME 0x08
#define STAC946X_MIC_L_VOLUME 0x09
#define STAC946X_MIC_R_VOLUME 0x0a
#define STAC946X_DEEMPHASIS 0x0c
#define STAC946X_GENERAL_PURPOSE 0x0d
#define STAC946X_AUDIO_PORT_CONTROL 0x0e
#define STAC946X_MASTER_CLOCKING 0x0f
#define STAC946X_POWERDOWN_CTRL1 0x10
#define STAC946X_POWERDOWN_CTRL2 0x11
#define STAC946X_REVISION_CODE 0x12
#define STAC946X_ADDRESS_CONTROL 0x13
#define STAC946X_ADDRESS 0x14
#endif /* __SOUND_STAC946X_H */

View file

@ -0,0 +1,138 @@
/*
* ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT)
*
* Lowlevel functions for VT1720-based motherboards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "vt1720_mobo.h"
static int k8x800_init(struct snd_ice1712 *ice)
{
ice->vt1720 = 1;
/* VT1616 codec */
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
/* WM8728 codec */
/* FIXME: TODO */
return 0;
}
static int k8x800_add_controls(struct snd_ice1712 *ice)
{
/* FIXME: needs some quirks for VT1616? */
return 0;
}
/* EEPROM image */
static unsigned char k8x800_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
[ICE_EEP2_SPDIF] = 0x00, /* - */
[ICE_EEP2_GPIO_DIR] = 0xff,
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0x00, /* - */
[ICE_EEP2_GPIO_MASK] = 0xff,
[ICE_EEP2_GPIO_MASK1] = 0xff,
[ICE_EEP2_GPIO_MASK2] = 0x00, /* - */
[ICE_EEP2_GPIO_STATE] = 0x00,
[ICE_EEP2_GPIO_STATE1] = 0x00,
[ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
static unsigned char sn25p_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
[ICE_EEP2_SPDIF] = 0x41, /* - */
[ICE_EEP2_GPIO_DIR] = 0xff,
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0x00, /* - */
[ICE_EEP2_GPIO_MASK] = 0xff,
[ICE_EEP2_GPIO_MASK1] = 0xff,
[ICE_EEP2_GPIO_MASK2] = 0x00, /* - */
[ICE_EEP2_GPIO_STATE] = 0x00,
[ICE_EEP2_GPIO_STATE1] = 0x00,
[ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
/* entry point */
struct snd_ice1712_card_info snd_vt1720_mobo_cards[] = {
{
.subvendor = VT1720_SUBDEVICE_K8X800,
.name = "Albatron K8X800 Pro II",
.model = "k8x800",
.chip_init = k8x800_init,
.build_controls = k8x800_add_controls,
.eeprom_size = sizeof(k8x800_eeprom),
.eeprom_data = k8x800_eeprom,
},
{
.subvendor = VT1720_SUBDEVICE_ZNF3_150,
.name = "Chaintech ZNF3-150",
/* identical with k8x800 */
.chip_init = k8x800_init,
.build_controls = k8x800_add_controls,
.eeprom_size = sizeof(k8x800_eeprom),
.eeprom_data = k8x800_eeprom,
},
{
.subvendor = VT1720_SUBDEVICE_ZNF3_250,
.name = "Chaintech ZNF3-250",
/* identical with k8x800 */
.chip_init = k8x800_init,
.build_controls = k8x800_add_controls,
.eeprom_size = sizeof(k8x800_eeprom),
.eeprom_data = k8x800_eeprom,
},
{
.subvendor = VT1720_SUBDEVICE_9CJS,
.name = "Chaintech 9CJS",
/* identical with k8x800 */
.chip_init = k8x800_init,
.build_controls = k8x800_add_controls,
.eeprom_size = sizeof(k8x800_eeprom),
.eeprom_data = k8x800_eeprom,
},
{
.subvendor = VT1720_SUBDEVICE_SN25P,
.name = "Shuttle SN25P",
.model = "sn25p",
.chip_init = k8x800_init,
.build_controls = k8x800_add_controls,
.eeprom_size = sizeof(k8x800_eeprom),
.eeprom_data = sn25p_eeprom,
},
{ } /* terminator */
};

View file

@ -0,0 +1,41 @@
#ifndef __SOUND_VT1720_MOBO_H
#define __SOUND_VT1720_MOBO_H
/*
* ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT)
*
* Lowlevel functions for VT1720-based motherboards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.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
*
*/
#define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\
"{Chaintech,ZNF3-150},"\
"{Chaintech,ZNF3-250},"\
"{Chaintech,9CJS},"\
"{Shuttle,SN25P},"
#define VT1720_SUBDEVICE_K8X800 0xf217052c
#define VT1720_SUBDEVICE_ZNF3_150 0x0f2741f6
#define VT1720_SUBDEVICE_ZNF3_250 0x0f2745f6
#define VT1720_SUBDEVICE_9CJS 0x0f272327
#define VT1720_SUBDEVICE_SN25P 0x97123650
extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[];
#endif /* __SOUND_VT1720_MOBO_H */

362
sound/pci/ice1712/wm8766.c Normal file
View file

@ -0,0 +1,362 @@
/*
* ALSA driver for ICEnsemble VT17xx
*
* Lowlevel functions for WM8766 codec
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
*
* 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/delay.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include "wm8766.h"
/* low-level access */
static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
{
if (addr < WM8766_REG_COUNT)
wm->regs[addr] = data;
wm->ops.write(wm, addr, data);
}
/* mixer controls */
static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
static struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = {
[WM8766_CTL_CH1_VOL] = {
.name = "Channel 1 Playback Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8766_tlv,
.reg1 = WM8766_REG_DACL1,
.reg2 = WM8766_REG_DACR1,
.mask1 = WM8766_VOL_MASK,
.mask2 = WM8766_VOL_MASK,
.max = 0xff,
.flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
},
[WM8766_CTL_CH2_VOL] = {
.name = "Channel 2 Playback Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8766_tlv,
.reg1 = WM8766_REG_DACL2,
.reg2 = WM8766_REG_DACR2,
.mask1 = WM8766_VOL_MASK,
.mask2 = WM8766_VOL_MASK,
.max = 0xff,
.flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
},
[WM8766_CTL_CH3_VOL] = {
.name = "Channel 3 Playback Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8766_tlv,
.reg1 = WM8766_REG_DACL3,
.reg2 = WM8766_REG_DACR3,
.mask1 = WM8766_VOL_MASK,
.mask2 = WM8766_VOL_MASK,
.max = 0xff,
.flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
},
[WM8766_CTL_CH1_SW] = {
.name = "Channel 1 Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_MUTE1,
.flags = WM8766_FLAG_INVERT,
},
[WM8766_CTL_CH2_SW] = {
.name = "Channel 2 Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_MUTE2,
.flags = WM8766_FLAG_INVERT,
},
[WM8766_CTL_CH3_SW] = {
.name = "Channel 3 Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_MUTE3,
.flags = WM8766_FLAG_INVERT,
},
[WM8766_CTL_PHASE1_SW] = {
.name = "Channel 1 Phase Invert Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_IFCTRL,
.mask1 = WM8766_PHASE_INVERT1,
},
[WM8766_CTL_PHASE2_SW] = {
.name = "Channel 2 Phase Invert Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_IFCTRL,
.mask1 = WM8766_PHASE_INVERT2,
},
[WM8766_CTL_PHASE3_SW] = {
.name = "Channel 3 Phase Invert Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_IFCTRL,
.mask1 = WM8766_PHASE_INVERT3,
},
[WM8766_CTL_DEEMPH1_SW] = {
.name = "Channel 1 Deemphasis Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_DEEMP1,
},
[WM8766_CTL_DEEMPH2_SW] = {
.name = "Channel 2 Deemphasis Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_DEEMP2,
},
[WM8766_CTL_DEEMPH3_SW] = {
.name = "Channel 3 Deemphasis Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_DEEMP3,
},
[WM8766_CTL_IZD_SW] = {
.name = "Infinite Zero Detect Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL1,
.mask1 = WM8766_DAC_IZD,
},
[WM8766_CTL_ZC_SW] = {
.name = "Zero Cross Detect Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8766_REG_DACCTRL2,
.mask1 = WM8766_DAC2_ZCD,
.flags = WM8766_FLAG_INVERT,
},
};
/* exported functions */
void snd_wm8766_init(struct snd_wm8766 *wm)
{
int i;
static const u16 default_values[] = {
0x000, 0x100,
0x120, 0x000,
0x000, 0x100, 0x000, 0x100, 0x000,
0x000, 0x080,
};
memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl));
snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */
udelay(10);
/* load defaults */
for (i = 0; i < ARRAY_SIZE(default_values); i++)
snd_wm8766_write(wm, i, default_values[i]);
}
void snd_wm8766_resume(struct snd_wm8766 *wm)
{
int i;
for (i = 0; i < WM8766_REG_COUNT; i++)
snd_wm8766_write(wm, i, wm->regs[i]);
}
void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac)
{
u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK;
dac &= WM8766_IF_MASK;
snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac);
}
void snd_wm8766_set_master_mode(struct snd_wm8766 *wm, u16 mode)
{
u16 val = wm->regs[WM8766_REG_DACCTRL3] & ~WM8766_DAC3_MSTR_MASK;
mode &= WM8766_DAC3_MSTR_MASK;
snd_wm8766_write(wm, WM8766_REG_DACCTRL3, val | mode);
}
void snd_wm8766_set_power(struct snd_wm8766 *wm, u16 power)
{
u16 val = wm->regs[WM8766_REG_DACCTRL3] & ~WM8766_DAC3_POWER_MASK;
power &= WM8766_DAC3_POWER_MASK;
snd_wm8766_write(wm, WM8766_REG_DACCTRL3, val | power);
}
void snd_wm8766_volume_restore(struct snd_wm8766 *wm)
{
u16 val = wm->regs[WM8766_REG_DACR1];
/* restore volume after MCLK stopped */
snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE);
}
/* mixer callbacks */
static int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1;
uinfo->value.integer.min = wm->ctl[n].min;
uinfo->value.integer.max = wm->ctl[n].max;
return 0;
}
static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
wm->ctl[n].enum_names);
}
static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
u16 val1, val2;
if (wm->ctl[n].get)
wm->ctl[n].get(wm, &val1, &val2);
else {
val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
val1 >>= __ffs(wm->ctl[n].mask1);
if (wm->ctl[n].flags & WM8766_FLAG_STEREO) {
val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
val2 >>= __ffs(wm->ctl[n].mask2);
if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
val2 &= ~WM8766_VOL_UPDATE;
}
}
if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
}
ucontrol->value.integer.value[0] = val1;
if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
ucontrol->value.integer.value[1] = val2;
return 0;
}
static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
u16 val, regval1, regval2;
/* this also works for enum because value is an union */
regval1 = ucontrol->value.integer.value[0];
regval2 = ucontrol->value.integer.value[1];
if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
}
if (wm->ctl[n].set)
wm->ctl[n].set(wm, regval1, regval2);
else {
val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
val |= regval1 << __ffs(wm->ctl[n].mask1);
/* both stereo controls in one register */
if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
wm->ctl[n].reg1 == wm->ctl[n].reg2) {
val &= ~wm->ctl[n].mask2;
val |= regval2 << __ffs(wm->ctl[n].mask2);
}
snd_wm8766_write(wm, wm->ctl[n].reg1, val);
/* stereo controls in different registers */
if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
wm->ctl[n].reg1 != wm->ctl[n].reg2) {
val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
val |= regval2 << __ffs(wm->ctl[n].mask2);
if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
val |= WM8766_VOL_UPDATE;
snd_wm8766_write(wm, wm->ctl[n].reg2, val);
}
}
return 0;
}
static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num)
{
struct snd_kcontrol_new cont;
struct snd_kcontrol *ctl;
memset(&cont, 0, sizeof(cont));
cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
cont.private_value = num;
cont.name = wm->ctl[num].name;
cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
if (wm->ctl[num].flags & WM8766_FLAG_LIM ||
wm->ctl[num].flags & WM8766_FLAG_ALC)
cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
cont.tlv.p = NULL;
cont.get = snd_wm8766_ctl_get;
cont.put = snd_wm8766_ctl_put;
switch (wm->ctl[num].type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER:
cont.info = snd_wm8766_volume_info;
cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
cont.tlv.p = wm->ctl[num].tlv;
break;
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
wm->ctl[num].max = 1;
if (wm->ctl[num].flags & WM8766_FLAG_STEREO)
cont.info = snd_ctl_boolean_stereo_info;
else
cont.info = snd_ctl_boolean_mono_info;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
cont.info = snd_wm8766_enum_info;
break;
default:
return -EINVAL;
}
ctl = snd_ctl_new1(&cont, wm);
if (!ctl)
return -ENOMEM;
wm->ctl[num].kctl = ctl;
return snd_ctl_add(wm->card, ctl);
}
int snd_wm8766_build_controls(struct snd_wm8766 *wm)
{
int err, i;
for (i = 0; i < WM8766_CTL_COUNT; i++)
if (wm->ctl[i].name) {
err = snd_wm8766_add_control(wm, i);
if (err < 0)
return err;
}
return 0;
}

163
sound/pci/ice1712/wm8766.h Normal file
View file

@ -0,0 +1,163 @@
#ifndef __SOUND_WM8766_H
#define __SOUND_WM8766_H
/*
* ALSA driver for ICEnsemble VT17xx
*
* Lowlevel functions for WM8766 codec
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
*
* 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
*
*/
#define WM8766_REG_DACL1 0x00
#define WM8766_REG_DACR1 0x01
#define WM8766_VOL_MASK 0x1ff /* incl. update bit */
#define WM8766_VOL_UPDATE (1 << 8) /* update volume */
#define WM8766_REG_DACCTRL1 0x02
#define WM8766_DAC_MUTEALL (1 << 0)
#define WM8766_DAC_DEEMPALL (1 << 1)
#define WM8766_DAC_PDWN (1 << 2)
#define WM8766_DAC_ATC (1 << 3)
#define WM8766_DAC_IZD (1 << 4)
#define WM8766_DAC_PL_MASK 0x1e0
#define WM8766_DAC_PL_LL (1 << 5) /* L chan: L signal */
#define WM8766_DAC_PL_LR (2 << 5) /* L chan: R signal */
#define WM8766_DAC_PL_LB (3 << 5) /* L chan: both */
#define WM8766_DAC_PL_RL (1 << 7) /* R chan: L signal */
#define WM8766_DAC_PL_RR (2 << 7) /* R chan: R signal */
#define WM8766_DAC_PL_RB (3 << 7) /* R chan: both */
#define WM8766_REG_IFCTRL 0x03
#define WM8766_IF_FMT_RIGHTJ (0 << 0)
#define WM8766_IF_FMT_LEFTJ (1 << 0)
#define WM8766_IF_FMT_I2S (2 << 0)
#define WM8766_IF_FMT_DSP (3 << 0)
#define WM8766_IF_DSP_LATE (1 << 2) /* in DSP mode */
#define WM8766_IF_LRC_INVERTED (1 << 2) /* in other modes */
#define WM8766_IF_BCLK_INVERTED (1 << 3)
#define WM8766_IF_IWL_16BIT (0 << 4)
#define WM8766_IF_IWL_20BIT (1 << 4)
#define WM8766_IF_IWL_24BIT (2 << 4)
#define WM8766_IF_IWL_32BIT (3 << 4)
#define WM8766_IF_MASK 0x3f
#define WM8766_PHASE_INVERT1 (1 << 6)
#define WM8766_PHASE_INVERT2 (1 << 7)
#define WM8766_PHASE_INVERT3 (1 << 8)
#define WM8766_REG_DACL2 0x04
#define WM8766_REG_DACR2 0x05
#define WM8766_REG_DACL3 0x06
#define WM8766_REG_DACR3 0x07
#define WM8766_REG_MASTDA 0x08
#define WM8766_REG_DACCTRL2 0x09
#define WM8766_DAC2_ZCD (1 << 0)
#define WM8766_DAC2_ZFLAG_ALL (0 << 1)
#define WM8766_DAC2_ZFLAG_1 (1 << 1)
#define WM8766_DAC2_ZFLAG_2 (2 << 1)
#define WM8766_DAC2_ZFLAG_3 (3 << 1)
#define WM8766_DAC2_MUTE1 (1 << 3)
#define WM8766_DAC2_MUTE2 (1 << 4)
#define WM8766_DAC2_MUTE3 (1 << 5)
#define WM8766_DAC2_DEEMP1 (1 << 6)
#define WM8766_DAC2_DEEMP2 (1 << 7)
#define WM8766_DAC2_DEEMP3 (1 << 8)
#define WM8766_REG_DACCTRL3 0x0a
#define WM8766_DAC3_DACPD1 (1 << 1)
#define WM8766_DAC3_DACPD2 (1 << 2)
#define WM8766_DAC3_DACPD3 (1 << 3)
#define WM8766_DAC3_PWRDNALL (1 << 4)
#define WM8766_DAC3_POWER_MASK 0x1e
#define WM8766_DAC3_MASTER (1 << 5)
#define WM8766_DAC3_DAC128FS (0 << 6)
#define WM8766_DAC3_DAC192FS (1 << 6)
#define WM8766_DAC3_DAC256FS (2 << 6)
#define WM8766_DAC3_DAC384FS (3 << 6)
#define WM8766_DAC3_DAC512FS (4 << 6)
#define WM8766_DAC3_DAC768FS (5 << 6)
#define WM8766_DAC3_MSTR_MASK 0x1e0
#define WM8766_REG_MUTE1 0x0c
#define WM8766_MUTE1_MPD (1 << 6)
#define WM8766_REG_MUTE2 0x0f
#define WM8766_MUTE2_MPD (1 << 5)
#define WM8766_REG_RESET 0x1f
#define WM8766_REG_COUNT 0x10 /* don't cache the RESET register */
struct snd_wm8766;
struct snd_wm8766_ops {
void (*write)(struct snd_wm8766 *wm, u16 addr, u16 data);
};
enum snd_wm8766_ctl_id {
WM8766_CTL_CH1_VOL,
WM8766_CTL_CH2_VOL,
WM8766_CTL_CH3_VOL,
WM8766_CTL_CH1_SW,
WM8766_CTL_CH2_SW,
WM8766_CTL_CH3_SW,
WM8766_CTL_PHASE1_SW,
WM8766_CTL_PHASE2_SW,
WM8766_CTL_PHASE3_SW,
WM8766_CTL_DEEMPH1_SW,
WM8766_CTL_DEEMPH2_SW,
WM8766_CTL_DEEMPH3_SW,
WM8766_CTL_IZD_SW,
WM8766_CTL_ZC_SW,
WM8766_CTL_COUNT,
};
#define WM8766_ENUM_MAX 16
#define WM8766_FLAG_STEREO (1 << 0)
#define WM8766_FLAG_VOL_UPDATE (1 << 1)
#define WM8766_FLAG_INVERT (1 << 2)
#define WM8766_FLAG_LIM (1 << 3)
#define WM8766_FLAG_ALC (1 << 4)
struct snd_wm8766_ctl {
struct snd_kcontrol *kctl;
const char *name;
snd_ctl_elem_type_t type;
const char *const enum_names[WM8766_ENUM_MAX];
const unsigned int *tlv;
u16 reg1, reg2, mask1, mask2, min, max, flags;
void (*set)(struct snd_wm8766 *wm, u16 ch1, u16 ch2);
void (*get)(struct snd_wm8766 *wm, u16 *ch1, u16 *ch2);
};
enum snd_wm8766_agc_mode { WM8766_AGC_OFF, WM8766_AGC_LIM, WM8766_AGC_ALC };
struct snd_wm8766 {
struct snd_card *card;
struct snd_wm8766_ctl ctl[WM8766_CTL_COUNT];
enum snd_wm8766_agc_mode agc_mode;
struct snd_wm8766_ops ops;
u16 regs[WM8766_REG_COUNT]; /* 9-bit registers */
};
void snd_wm8766_init(struct snd_wm8766 *wm);
void snd_wm8766_resume(struct snd_wm8766 *wm);
void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac);
void snd_wm8766_set_master_mode(struct snd_wm8766 *wm, u16 mode);
void snd_wm8766_set_power(struct snd_wm8766 *wm, u16 power);
void snd_wm8766_volume_restore(struct snd_wm8766 *wm);
int snd_wm8766_build_controls(struct snd_wm8766 *wm);
#endif /* __SOUND_WM8766_H */

634
sound/pci/ice1712/wm8776.c Normal file
View file

@ -0,0 +1,634 @@
/*
* ALSA driver for ICEnsemble VT17xx
*
* Lowlevel functions for WM8776 codec
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
*
* 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/delay.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include "wm8776.h"
/* low-level access */
static void snd_wm8776_write(struct snd_wm8776 *wm, u16 addr, u16 data)
{
u8 bus_addr = addr << 1 | data >> 8; /* addr + 9th data bit */
u8 bus_data = data & 0xff; /* remaining 8 data bits */
if (addr < WM8776_REG_RESET)
wm->regs[addr] = data;
wm->ops.write(wm, bus_addr, bus_data);
}
/* register-level functions */
static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
const char *ctl_name,
bool active)
{
struct snd_card *card = wm->card;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
struct snd_ctl_elem_id elem_id;
unsigned int index_offset;
memset(&elem_id, 0, sizeof(elem_id));
strlcpy(elem_id.name, ctl_name, sizeof(elem_id.name));
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kctl = snd_ctl_find_id(card, &elem_id);
if (!kctl)
return;
index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
vd = &kctl->vd[index_offset];
if (active)
vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
else
vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
}
static void snd_wm8776_update_agc_ctl(struct snd_wm8776 *wm)
{
int i, flags_on = 0, flags_off = 0;
switch (wm->agc_mode) {
case WM8776_AGC_OFF:
flags_off = WM8776_FLAG_LIM | WM8776_FLAG_ALC;
break;
case WM8776_AGC_LIM:
flags_off = WM8776_FLAG_ALC;
flags_on = WM8776_FLAG_LIM;
break;
case WM8776_AGC_ALC_R:
case WM8776_AGC_ALC_L:
case WM8776_AGC_ALC_STEREO:
flags_off = WM8776_FLAG_LIM;
flags_on = WM8776_FLAG_ALC;
break;
}
for (i = 0; i < WM8776_CTL_COUNT; i++)
if (wm->ctl[i].flags & flags_off)
snd_wm8776_activate_ctl(wm, wm->ctl[i].name, false);
else if (wm->ctl[i].flags & flags_on)
snd_wm8776_activate_ctl(wm, wm->ctl[i].name, true);
}
static void snd_wm8776_set_agc(struct snd_wm8776 *wm, u16 agc, u16 nothing)
{
u16 alc1 = wm->regs[WM8776_REG_ALCCTRL1] & ~WM8776_ALC1_LCT_MASK;
u16 alc2 = wm->regs[WM8776_REG_ALCCTRL2] & ~WM8776_ALC2_LCEN;
switch (agc) {
case 0: /* Off */
wm->agc_mode = WM8776_AGC_OFF;
break;
case 1: /* Limiter */
alc2 |= WM8776_ALC2_LCEN;
wm->agc_mode = WM8776_AGC_LIM;
break;
case 2: /* ALC Right */
alc1 |= WM8776_ALC1_LCSEL_ALCR;
alc2 |= WM8776_ALC2_LCEN;
wm->agc_mode = WM8776_AGC_ALC_R;
break;
case 3: /* ALC Left */
alc1 |= WM8776_ALC1_LCSEL_ALCL;
alc2 |= WM8776_ALC2_LCEN;
wm->agc_mode = WM8776_AGC_ALC_L;
break;
case 4: /* ALC Stereo */
alc1 |= WM8776_ALC1_LCSEL_ALCSTEREO;
alc2 |= WM8776_ALC2_LCEN;
wm->agc_mode = WM8776_AGC_ALC_STEREO;
break;
}
snd_wm8776_write(wm, WM8776_REG_ALCCTRL1, alc1);
snd_wm8776_write(wm, WM8776_REG_ALCCTRL2, alc2);
snd_wm8776_update_agc_ctl(wm);
}
static void snd_wm8776_get_agc(struct snd_wm8776 *wm, u16 *mode, u16 *nothing)
{
*mode = wm->agc_mode;
}
/* mixer controls */
static const DECLARE_TLV_DB_SCALE(wm8776_hp_tlv, -7400, 100, 1);
static const DECLARE_TLV_DB_SCALE(wm8776_dac_tlv, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(wm8776_adc_tlv, -10350, 50, 1);
static const DECLARE_TLV_DB_SCALE(wm8776_lct_tlv, -1600, 100, 0);
static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_tlv, 0, 400, 0);
static const DECLARE_TLV_DB_SCALE(wm8776_ngth_tlv, -7800, 600, 0);
static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_tlv, -1200, 100, 0);
static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_tlv, -2100, 400, 0);
static struct snd_wm8776_ctl snd_wm8776_default_ctl[WM8776_CTL_COUNT] = {
[WM8776_CTL_DAC_VOL] = {
.name = "Master Playback Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_dac_tlv,
.reg1 = WM8776_REG_DACLVOL,
.reg2 = WM8776_REG_DACRVOL,
.mask1 = WM8776_DACVOL_MASK,
.mask2 = WM8776_DACVOL_MASK,
.max = 0xff,
.flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
},
[WM8776_CTL_DAC_SW] = {
.name = "Master Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_DACCTRL1,
.reg2 = WM8776_REG_DACCTRL1,
.mask1 = WM8776_DAC_PL_LL,
.mask2 = WM8776_DAC_PL_RR,
.flags = WM8776_FLAG_STEREO,
},
[WM8776_CTL_DAC_ZC_SW] = {
.name = "Master Zero Cross Detect Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_DACCTRL1,
.mask1 = WM8776_DAC_DZCEN,
},
[WM8776_CTL_HP_VOL] = {
.name = "Headphone Playback Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_hp_tlv,
.reg1 = WM8776_REG_HPLVOL,
.reg2 = WM8776_REG_HPRVOL,
.mask1 = WM8776_HPVOL_MASK,
.mask2 = WM8776_HPVOL_MASK,
.min = 0x2f,
.max = 0x7f,
.flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
},
[WM8776_CTL_HP_SW] = {
.name = "Headphone Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_PWRDOWN,
.mask1 = WM8776_PWR_HPPD,
.flags = WM8776_FLAG_INVERT,
},
[WM8776_CTL_HP_ZC_SW] = {
.name = "Headphone Zero Cross Detect Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_HPLVOL,
.reg2 = WM8776_REG_HPRVOL,
.mask1 = WM8776_VOL_HPZCEN,
.mask2 = WM8776_VOL_HPZCEN,
.flags = WM8776_FLAG_STEREO,
},
[WM8776_CTL_AUX_SW] = {
.name = "AUX Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_OUTMUX,
.mask1 = WM8776_OUTMUX_AUX,
},
[WM8776_CTL_BYPASS_SW] = {
.name = "Bypass Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_OUTMUX,
.mask1 = WM8776_OUTMUX_BYPASS,
},
[WM8776_CTL_DAC_IZD_SW] = {
.name = "Infinite Zero Detect Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_DACCTRL1,
.mask1 = WM8776_DAC_IZD,
},
[WM8776_CTL_PHASE_SW] = {
.name = "Phase Invert Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_PHASESWAP,
.reg2 = WM8776_REG_PHASESWAP,
.mask1 = WM8776_PHASE_INVERTL,
.mask2 = WM8776_PHASE_INVERTR,
.flags = WM8776_FLAG_STEREO,
},
[WM8776_CTL_DEEMPH_SW] = {
.name = "Deemphasis Playback Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_DACCTRL2,
.mask1 = WM8776_DAC2_DEEMPH,
},
[WM8776_CTL_ADC_VOL] = {
.name = "Input Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_adc_tlv,
.reg1 = WM8776_REG_ADCLVOL,
.reg2 = WM8776_REG_ADCRVOL,
.mask1 = WM8776_ADC_GAIN_MASK,
.mask2 = WM8776_ADC_GAIN_MASK,
.max = 0xff,
.flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
},
[WM8776_CTL_ADC_SW] = {
.name = "Input Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_ADCMUX,
.reg2 = WM8776_REG_ADCMUX,
.mask1 = WM8776_ADC_MUTEL,
.mask2 = WM8776_ADC_MUTER,
.flags = WM8776_FLAG_STEREO | WM8776_FLAG_INVERT,
},
[WM8776_CTL_INPUT1_SW] = {
.name = "AIN1 Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_ADCMUX,
.mask1 = WM8776_ADC_MUX_AIN1,
},
[WM8776_CTL_INPUT2_SW] = {
.name = "AIN2 Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_ADCMUX,
.mask1 = WM8776_ADC_MUX_AIN2,
},
[WM8776_CTL_INPUT3_SW] = {
.name = "AIN3 Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_ADCMUX,
.mask1 = WM8776_ADC_MUX_AIN3,
},
[WM8776_CTL_INPUT4_SW] = {
.name = "AIN4 Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_ADCMUX,
.mask1 = WM8776_ADC_MUX_AIN4,
},
[WM8776_CTL_INPUT5_SW] = {
.name = "AIN5 Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_ADCMUX,
.mask1 = WM8776_ADC_MUX_AIN5,
},
[WM8776_CTL_AGC_SEL] = {
.name = "AGC Select Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "Off", "Limiter", "ALC Right", "ALC Left",
"ALC Stereo" },
.max = 5, /* .enum_names item count */
.set = snd_wm8776_set_agc,
.get = snd_wm8776_get_agc,
},
[WM8776_CTL_LIM_THR] = {
.name = "Limiter Threshold Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_lct_tlv,
.reg1 = WM8776_REG_ALCCTRL1,
.mask1 = WM8776_ALC1_LCT_MASK,
.max = 15,
.flags = WM8776_FLAG_LIM,
},
[WM8776_CTL_LIM_ATK] = {
.name = "Limiter Attack Time Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "0.25 ms", "0.5 ms", "1 ms", "2 ms", "4 ms",
"8 ms", "16 ms", "32 ms", "64 ms", "128 ms", "256 ms" },
.max = 11, /* .enum_names item count */
.reg1 = WM8776_REG_ALCCTRL3,
.mask1 = WM8776_ALC3_ATK_MASK,
.flags = WM8776_FLAG_LIM,
},
[WM8776_CTL_LIM_DCY] = {
.name = "Limiter Decay Time Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
"19.2 ms", "38.4 ms", "76.8 ms", "154 ms", "307 ms",
"614 ms", "1.23 s" },
.max = 11, /* .enum_names item count */
.reg1 = WM8776_REG_ALCCTRL3,
.mask1 = WM8776_ALC3_DCY_MASK,
.flags = WM8776_FLAG_LIM,
},
[WM8776_CTL_LIM_TRANWIN] = {
.name = "Limiter Transient Window Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "0 us", "62.5 us", "125 us", "250 us", "500 us",
"1 ms", "2 ms", "4 ms" },
.max = 8, /* .enum_names item count */
.reg1 = WM8776_REG_LIMITER,
.mask1 = WM8776_LIM_TRANWIN_MASK,
.flags = WM8776_FLAG_LIM,
},
[WM8776_CTL_LIM_MAXATTN] = {
.name = "Limiter Maximum Attenuation Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_maxatten_lim_tlv,
.reg1 = WM8776_REG_LIMITER,
.mask1 = WM8776_LIM_MAXATTEN_MASK,
.min = 3,
.max = 12,
.flags = WM8776_FLAG_LIM | WM8776_FLAG_INVERT,
},
[WM8776_CTL_ALC_TGT] = {
.name = "ALC Target Level Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_lct_tlv,
.reg1 = WM8776_REG_ALCCTRL1,
.mask1 = WM8776_ALC1_LCT_MASK,
.max = 15,
.flags = WM8776_FLAG_ALC,
},
[WM8776_CTL_ALC_ATK] = {
.name = "ALC Attack Time Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
"134 ms", "269 ms", "538 ms", "1.08 s", "2.15 s",
"4.3 s", "8.6 s" },
.max = 11, /* .enum_names item count */
.reg1 = WM8776_REG_ALCCTRL3,
.mask1 = WM8776_ALC3_ATK_MASK,
.flags = WM8776_FLAG_ALC,
},
[WM8776_CTL_ALC_DCY] = {
.name = "ALC Decay Time Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "33.5 ms", "67.0 ms", "134 ms", "268 ms",
"536 ms", "1.07 s", "2.14 s", "4.29 s", "8.58 s",
"17.2 s", "34.3 s" },
.max = 11, /* .enum_names item count */
.reg1 = WM8776_REG_ALCCTRL3,
.mask1 = WM8776_ALC3_DCY_MASK,
.flags = WM8776_FLAG_ALC,
},
[WM8776_CTL_ALC_MAXGAIN] = {
.name = "ALC Maximum Gain Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_maxgain_tlv,
.reg1 = WM8776_REG_ALCCTRL1,
.mask1 = WM8776_ALC1_MAXGAIN_MASK,
.min = 1,
.max = 7,
.flags = WM8776_FLAG_ALC,
},
[WM8776_CTL_ALC_MAXATTN] = {
.name = "ALC Maximum Attenuation Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_maxatten_alc_tlv,
.reg1 = WM8776_REG_LIMITER,
.mask1 = WM8776_LIM_MAXATTEN_MASK,
.min = 10,
.max = 15,
.flags = WM8776_FLAG_ALC | WM8776_FLAG_INVERT,
},
[WM8776_CTL_ALC_HLD] = {
.name = "ALC Hold Time Capture Enum",
.type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
.enum_names = { "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
"21.3 ms", "42.7 ms", "85.3 ms", "171 ms", "341 ms",
"683 ms", "1.37 s", "2.73 s", "5.46 s", "10.9 s",
"21.8 s", "43.7 s" },
.max = 16, /* .enum_names item count */
.reg1 = WM8776_REG_ALCCTRL2,
.mask1 = WM8776_ALC2_HOLD_MASK,
.flags = WM8776_FLAG_ALC,
},
[WM8776_CTL_NGT_SW] = {
.name = "Noise Gate Capture Switch",
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
.reg1 = WM8776_REG_NOISEGATE,
.mask1 = WM8776_NGAT_ENABLE,
.flags = WM8776_FLAG_ALC,
},
[WM8776_CTL_NGT_THR] = {
.name = "Noise Gate Threshold Capture Volume",
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
.tlv = wm8776_ngth_tlv,
.reg1 = WM8776_REG_NOISEGATE,
.mask1 = WM8776_NGAT_THR_MASK,
.max = 7,
.flags = WM8776_FLAG_ALC,
},
};
/* exported functions */
void snd_wm8776_init(struct snd_wm8776 *wm)
{
int i;
static const u16 default_values[] = {
0x000, 0x100, 0x000,
0x000, 0x100, 0x000,
0x000, 0x090, 0x000, 0x000,
0x022, 0x022, 0x022,
0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
0x032, 0x000, 0x0a6, 0x001, 0x001
};
memcpy(wm->ctl, snd_wm8776_default_ctl, sizeof(wm->ctl));
snd_wm8776_write(wm, WM8776_REG_RESET, 0x00); /* reset */
udelay(10);
/* load defaults */
for (i = 0; i < ARRAY_SIZE(default_values); i++)
snd_wm8776_write(wm, i, default_values[i]);
}
void snd_wm8776_resume(struct snd_wm8776 *wm)
{
int i;
for (i = 0; i < WM8776_REG_COUNT; i++)
snd_wm8776_write(wm, i, wm->regs[i]);
}
void snd_wm8776_set_dac_if(struct snd_wm8776 *wm, u16 dac)
{
snd_wm8776_write(wm, WM8776_REG_DACIFCTRL, dac);
}
void snd_wm8776_set_adc_if(struct snd_wm8776 *wm, u16 adc)
{
snd_wm8776_write(wm, WM8776_REG_ADCIFCTRL, adc);
}
void snd_wm8776_set_master_mode(struct snd_wm8776 *wm, u16 mode)
{
snd_wm8776_write(wm, WM8776_REG_MSTRCTRL, mode);
}
void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power)
{
snd_wm8776_write(wm, WM8776_REG_PWRDOWN, power);
}
void snd_wm8776_volume_restore(struct snd_wm8776 *wm)
{
u16 val = wm->regs[WM8776_REG_DACRVOL];
/* restore volume after MCLK stopped */
snd_wm8776_write(wm, WM8776_REG_DACRVOL, val | WM8776_VOL_UPDATE);
}
/* mixer callbacks */
static int snd_wm8776_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = (wm->ctl[n].flags & WM8776_FLAG_STEREO) ? 2 : 1;
uinfo->value.integer.min = wm->ctl[n].min;
uinfo->value.integer.max = wm->ctl[n].max;
return 0;
}
static int snd_wm8776_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
wm->ctl[n].enum_names);
}
static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
u16 val1, val2;
if (wm->ctl[n].get)
wm->ctl[n].get(wm, &val1, &val2);
else {
val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
val1 >>= __ffs(wm->ctl[n].mask1);
if (wm->ctl[n].flags & WM8776_FLAG_STEREO) {
val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
val2 >>= __ffs(wm->ctl[n].mask2);
if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE)
val2 &= ~WM8776_VOL_UPDATE;
}
}
if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
}
ucontrol->value.integer.value[0] = val1;
if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
ucontrol->value.integer.value[1] = val2;
return 0;
}
static int snd_wm8776_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
int n = kcontrol->private_value;
u16 val, regval1, regval2;
/* this also works for enum because value is an union */
regval1 = ucontrol->value.integer.value[0];
regval2 = ucontrol->value.integer.value[1];
if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
}
if (wm->ctl[n].set)
wm->ctl[n].set(wm, regval1, regval2);
else {
val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
val |= regval1 << __ffs(wm->ctl[n].mask1);
/* both stereo controls in one register */
if (wm->ctl[n].flags & WM8776_FLAG_STEREO &&
wm->ctl[n].reg1 == wm->ctl[n].reg2) {
val &= ~wm->ctl[n].mask2;
val |= regval2 << __ffs(wm->ctl[n].mask2);
}
snd_wm8776_write(wm, wm->ctl[n].reg1, val);
/* stereo controls in different registers */
if (wm->ctl[n].flags & WM8776_FLAG_STEREO &&
wm->ctl[n].reg1 != wm->ctl[n].reg2) {
val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
val |= regval2 << __ffs(wm->ctl[n].mask2);
if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE)
val |= WM8776_VOL_UPDATE;
snd_wm8776_write(wm, wm->ctl[n].reg2, val);
}
}
return 0;
}
static int snd_wm8776_add_control(struct snd_wm8776 *wm, int num)
{
struct snd_kcontrol_new cont;
struct snd_kcontrol *ctl;
memset(&cont, 0, sizeof(cont));
cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
cont.private_value = num;
cont.name = wm->ctl[num].name;
cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
if (wm->ctl[num].flags & WM8776_FLAG_LIM ||
wm->ctl[num].flags & WM8776_FLAG_ALC)
cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
cont.tlv.p = NULL;
cont.get = snd_wm8776_ctl_get;
cont.put = snd_wm8776_ctl_put;
switch (wm->ctl[num].type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER:
cont.info = snd_wm8776_volume_info;
cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
cont.tlv.p = wm->ctl[num].tlv;
break;
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
wm->ctl[num].max = 1;
if (wm->ctl[num].flags & WM8776_FLAG_STEREO)
cont.info = snd_ctl_boolean_stereo_info;
else
cont.info = snd_ctl_boolean_mono_info;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
cont.info = snd_wm8776_enum_info;
break;
default:
return -EINVAL;
}
ctl = snd_ctl_new1(&cont, wm);
if (!ctl)
return -ENOMEM;
return snd_ctl_add(wm->card, ctl);
}
int snd_wm8776_build_controls(struct snd_wm8776 *wm)
{
int err, i;
for (i = 0; i < WM8776_CTL_COUNT; i++)
if (wm->ctl[i].name) {
err = snd_wm8776_add_control(wm, i);
if (err < 0)
return err;
}
return 0;
}

226
sound/pci/ice1712/wm8776.h Normal file
View file

@ -0,0 +1,226 @@
#ifndef __SOUND_WM8776_H
#define __SOUND_WM8776_H
/*
* ALSA driver for ICEnsemble VT17xx
*
* Lowlevel functions for WM8776 codec
*
* Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
*
* 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
*
*/
#define WM8776_REG_HPLVOL 0x00
#define WM8776_REG_HPRVOL 0x01
#define WM8776_REG_HPMASTER 0x02
#define WM8776_HPVOL_MASK 0x17f /* incl. update bit */
#define WM8776_VOL_HPZCEN (1 << 7) /* zero cross detect */
#define WM8776_VOL_UPDATE (1 << 8) /* update volume */
#define WM8776_REG_DACLVOL 0x03
#define WM8776_REG_DACRVOL 0x04
#define WM8776_REG_DACMASTER 0x05
#define WM8776_DACVOL_MASK 0x1ff /* incl. update bit */
#define WM8776_REG_PHASESWAP 0x06
#define WM8776_PHASE_INVERTL (1 << 0)
#define WM8776_PHASE_INVERTR (1 << 1)
#define WM8776_REG_DACCTRL1 0x07
#define WM8776_DAC_DZCEN (1 << 0)
#define WM8776_DAC_ATC (1 << 1)
#define WM8776_DAC_IZD (1 << 2)
#define WM8776_DAC_TOD (1 << 3)
#define WM8776_DAC_PL_MASK 0xf0
#define WM8776_DAC_PL_LL (1 << 4) /* L chan: L signal */
#define WM8776_DAC_PL_LR (2 << 4) /* L chan: R signal */
#define WM8776_DAC_PL_LB (3 << 4) /* L chan: both */
#define WM8776_DAC_PL_RL (1 << 6) /* R chan: L signal */
#define WM8776_DAC_PL_RR (2 << 6) /* R chan: R signal */
#define WM8776_DAC_PL_RB (3 << 6) /* R chan: both */
#define WM8776_REG_DACMUTE 0x08
#define WM8776_DACMUTE (1 << 0)
#define WM8776_REG_DACCTRL2 0x09
#define WM8776_DAC2_DEEMPH (1 << 0)
#define WM8776_DAC2_ZFLAG_DISABLE (0 << 1)
#define WM8776_DAC2_ZFLAG_OWN (1 << 1)
#define WM8776_DAC2_ZFLAG_BOTH (2 << 1)
#define WM8776_DAC2_ZFLAG_EITHER (3 << 1)
#define WM8776_REG_DACIFCTRL 0x0a
#define WM8776_FMT_RIGHTJ (0 << 0)
#define WM8776_FMT_LEFTJ (1 << 0)
#define WM8776_FMT_I2S (2 << 0)
#define WM8776_FMT_DSP (3 << 0)
#define WM8776_FMT_DSP_LATE (1 << 2) /* in DSP mode */
#define WM8776_FMT_LRC_INVERTED (1 << 2) /* in other modes */
#define WM8776_FMT_BCLK_INVERTED (1 << 3)
#define WM8776_FMT_16BIT (0 << 4)
#define WM8776_FMT_20BIT (1 << 4)
#define WM8776_FMT_24BIT (2 << 4)
#define WM8776_FMT_32BIT (3 << 4)
#define WM8776_REG_ADCIFCTRL 0x0b
#define WM8776_FMT_ADCMCLK_INVERTED (1 << 6)
#define WM8776_FMT_ADCHPD (1 << 8)
#define WM8776_REG_MSTRCTRL 0x0c
#define WM8776_IF_ADC256FS (2 << 0)
#define WM8776_IF_ADC384FS (3 << 0)
#define WM8776_IF_ADC512FS (4 << 0)
#define WM8776_IF_ADC768FS (5 << 0)
#define WM8776_IF_OVERSAMP64 (1 << 3)
#define WM8776_IF_DAC128FS (0 << 4)
#define WM8776_IF_DAC192FS (1 << 4)
#define WM8776_IF_DAC256FS (2 << 4)
#define WM8776_IF_DAC384FS (3 << 4)
#define WM8776_IF_DAC512FS (4 << 4)
#define WM8776_IF_DAC768FS (5 << 4)
#define WM8776_IF_DAC_MASTER (1 << 7)
#define WM8776_IF_ADC_MASTER (1 << 8)
#define WM8776_REG_PWRDOWN 0x0d
#define WM8776_PWR_PDWN (1 << 0)
#define WM8776_PWR_ADCPD (1 << 1)
#define WM8776_PWR_DACPD (1 << 2)
#define WM8776_PWR_HPPD (1 << 3)
#define WM8776_PWR_AINPD (1 << 6)
#define WM8776_REG_ADCLVOL 0x0e
#define WM8776_REG_ADCRVOL 0x0f
#define WM8776_ADC_GAIN_MASK 0xff
#define WM8776_ADC_ZCEN (1 << 8)
#define WM8776_REG_ALCCTRL1 0x10
#define WM8776_ALC1_LCT_MASK 0x0f /* 0=-16dB, 1=-15dB..15=-1dB */
#define WM8776_ALC1_MAXGAIN_MASK 0x70 /* 0,1=0dB, 2=+4dB...7=+24dB */
#define WM8776_ALC1_LCSEL_MASK 0x180
#define WM8776_ALC1_LCSEL_LIMITER (0 << 7)
#define WM8776_ALC1_LCSEL_ALCR (1 << 7)
#define WM8776_ALC1_LCSEL_ALCL (2 << 7)
#define WM8776_ALC1_LCSEL_ALCSTEREO (3 << 7)
#define WM8776_REG_ALCCTRL2 0x11
#define WM8776_ALC2_HOLD_MASK 0x0f /*0=0ms, 1=2.67ms, 2=5.33ms.. */
#define WM8776_ALC2_ZCEN (1 << 7)
#define WM8776_ALC2_LCEN (1 << 8)
#define WM8776_REG_ALCCTRL3 0x12
#define WM8776_ALC3_ATK_MASK 0x0f
#define WM8776_ALC3_DCY_MASK 0xf0
#define WM8776_ALC3_FDECAY (1 << 8)
#define WM8776_REG_NOISEGATE 0x13
#define WM8776_NGAT_ENABLE (1 << 0)
#define WM8776_NGAT_THR_MASK 0x1c /*0=-78dB, 1=-72dB...7=-36dB */
#define WM8776_REG_LIMITER 0x14
#define WM8776_LIM_MAXATTEN_MASK 0x0f
#define WM8776_LIM_TRANWIN_MASK 0x70 /*0=0us, 1=62.5us, 2=125us.. */
#define WM8776_REG_ADCMUX 0x15
#define WM8776_ADC_MUX_AIN1 (1 << 0)
#define WM8776_ADC_MUX_AIN2 (1 << 1)
#define WM8776_ADC_MUX_AIN3 (1 << 2)
#define WM8776_ADC_MUX_AIN4 (1 << 3)
#define WM8776_ADC_MUX_AIN5 (1 << 4)
#define WM8776_ADC_MUTER (1 << 6)
#define WM8776_ADC_MUTEL (1 << 7)
#define WM8776_ADC_LRBOTH (1 << 8)
#define WM8776_REG_OUTMUX 0x16
#define WM8776_OUTMUX_DAC (1 << 0)
#define WM8776_OUTMUX_AUX (1 << 1)
#define WM8776_OUTMUX_BYPASS (1 << 2)
#define WM8776_REG_RESET 0x17
#define WM8776_REG_COUNT 0x17 /* don't cache the RESET register */
struct snd_wm8776;
struct snd_wm8776_ops {
void (*write)(struct snd_wm8776 *wm, u8 addr, u8 data);
};
enum snd_wm8776_ctl_id {
WM8776_CTL_DAC_VOL,
WM8776_CTL_DAC_SW,
WM8776_CTL_DAC_ZC_SW,
WM8776_CTL_HP_VOL,
WM8776_CTL_HP_SW,
WM8776_CTL_HP_ZC_SW,
WM8776_CTL_AUX_SW,
WM8776_CTL_BYPASS_SW,
WM8776_CTL_DAC_IZD_SW,
WM8776_CTL_PHASE_SW,
WM8776_CTL_DEEMPH_SW,
WM8776_CTL_ADC_VOL,
WM8776_CTL_ADC_SW,
WM8776_CTL_INPUT1_SW,
WM8776_CTL_INPUT2_SW,
WM8776_CTL_INPUT3_SW,
WM8776_CTL_INPUT4_SW,
WM8776_CTL_INPUT5_SW,
WM8776_CTL_AGC_SEL,
WM8776_CTL_LIM_THR,
WM8776_CTL_LIM_ATK,
WM8776_CTL_LIM_DCY,
WM8776_CTL_LIM_TRANWIN,
WM8776_CTL_LIM_MAXATTN,
WM8776_CTL_ALC_TGT,
WM8776_CTL_ALC_ATK,
WM8776_CTL_ALC_DCY,
WM8776_CTL_ALC_MAXGAIN,
WM8776_CTL_ALC_MAXATTN,
WM8776_CTL_ALC_HLD,
WM8776_CTL_NGT_SW,
WM8776_CTL_NGT_THR,
WM8776_CTL_COUNT,
};
#define WM8776_ENUM_MAX 16
#define WM8776_FLAG_STEREO (1 << 0)
#define WM8776_FLAG_VOL_UPDATE (1 << 1)
#define WM8776_FLAG_INVERT (1 << 2)
#define WM8776_FLAG_LIM (1 << 3)
#define WM8776_FLAG_ALC (1 << 4)
struct snd_wm8776_ctl {
const char *name;
snd_ctl_elem_type_t type;
const char *const enum_names[WM8776_ENUM_MAX];
const unsigned int *tlv;
u16 reg1, reg2, mask1, mask2, min, max, flags;
void (*set)(struct snd_wm8776 *wm, u16 ch1, u16 ch2);
void (*get)(struct snd_wm8776 *wm, u16 *ch1, u16 *ch2);
};
enum snd_wm8776_agc_mode {
WM8776_AGC_OFF,
WM8776_AGC_LIM,
WM8776_AGC_ALC_R,
WM8776_AGC_ALC_L,
WM8776_AGC_ALC_STEREO
};
struct snd_wm8776 {
struct snd_card *card;
struct snd_wm8776_ctl ctl[WM8776_CTL_COUNT];
enum snd_wm8776_agc_mode agc_mode;
struct snd_wm8776_ops ops;
u16 regs[WM8776_REG_COUNT]; /* 9-bit registers */
};
void snd_wm8776_init(struct snd_wm8776 *wm);
void snd_wm8776_resume(struct snd_wm8776 *wm);
void snd_wm8776_set_dac_if(struct snd_wm8776 *wm, u16 dac);
void snd_wm8776_set_adc_if(struct snd_wm8776 *wm, u16 adc);
void snd_wm8776_set_master_mode(struct snd_wm8776 *wm, u16 mode);
void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power);
void snd_wm8776_volume_restore(struct snd_wm8776 *wm);
int snd_wm8776_build_controls(struct snd_wm8776 *wm);
#endif /* __SOUND_WM8776_H */

516
sound/pci/ice1712/wtm.c Normal file
View file

@ -0,0 +1,516 @@
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Ego Sys Waveterminal 192M
*
* Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
* Some functions are taken from the Prodigy192 driver
* source
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <sound/core.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "wtm.h"
#include "stac946x.h"
/*
* 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
*/
static inline void stac9460_put(struct snd_ice1712 *ice, int reg,
unsigned char val)
{
snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val);
}
static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
{
return snd_vt1724_read_i2c(ice, STAC9460_I2C_ADDR, reg);
}
/*
* 2*ADC 2*DAC no2 ringbuffer r/w on i2c bus
*/
static inline void stac9460_2_put(struct snd_ice1712 *ice, int reg,
unsigned char val)
{
snd_vt1724_write_i2c(ice, STAC9460_2_I2C_ADDR, reg, val);
}
static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
{
return snd_vt1724_read_i2c(ice, STAC9460_2_I2C_ADDR, reg);
}
/*
* DAC mute control
*/
#define stac9460_dac_mute_info snd_ctl_boolean_mono_info
static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int idx, id;
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
id = 0;
} else {
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
idx = id + STAC946X_LF_VOLUME;
}
if (id < 6)
val = stac9460_get(ice, idx);
else
val = stac9460_2_get(ice, idx - 6);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
return 0;
}
static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
int id, idx;
int change;
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
old = stac9460_get(ice, idx);
new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
(old & ~0x80);
change = (new != old);
if (change) {
stac9460_put(ice, idx, new);
stac9460_2_put(ice, idx, new);
}
} else {
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
idx = id + STAC946X_LF_VOLUME;
if (id < 6)
old = stac9460_get(ice, idx);
else
old = stac9460_2_get(ice, idx - 6);
new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
(old & ~0x80);
change = (new != old);
if (change) {
if (id < 6)
stac9460_put(ice, idx, new);
else
stac9460_2_put(ice, idx - 6, new);
}
}
return change;
}
/*
* DAC volume attenuation mixer control
*/
static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0; /* mute */
uinfo->value.integer.max = 0x7f; /* 0dB */
return 0;
}
static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx, id;
unsigned char vol;
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
id = 0;
} else {
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
idx = id + STAC946X_LF_VOLUME;
}
if (id < 6)
vol = stac9460_get(ice, idx) & 0x7f;
else
vol = stac9460_2_get(ice, idx - 6) & 0x7f;
ucontrol->value.integer.value[0] = 0x7f - vol;
return 0;
}
static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx, id;
unsigned char tmp, ovol, nvol;
int change;
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
nvol = ucontrol->value.integer.value[0] & 0x7f;
tmp = stac9460_get(ice, idx);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
} else {
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
idx = id + STAC946X_LF_VOLUME;
nvol = ucontrol->value.integer.value[0] & 0x7f;
if (id < 6)
tmp = stac9460_get(ice, idx);
else
tmp = stac9460_2_get(ice, idx - 6);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
if (id < 6)
stac9460_put(ice, idx, (0x7f - nvol) |
(tmp & 0x80));
else
stac9460_2_put(ice, idx-6, (0x7f - nvol) |
(tmp & 0x80));
}
}
return change;
}
/*
* ADC mute control
*/
#define stac9460_adc_mute_info snd_ctl_boolean_stereo_info
static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int i, id;
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i);
ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
}
} else {
for (i = 0; i < 2; ++i) {
val = stac9460_2_get(ice, STAC946X_MIC_L_VOLUME + i);
ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
}
}
return 0;
}
static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
int i, reg, id;
int change;
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
old = stac9460_get(ice, reg);
new = (~ucontrol->value.integer.value[i]<<7&0x80) |
(old&~0x80);
change = (new != old);
if (change)
stac9460_put(ice, reg, new);
}
} else {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
old = stac9460_2_get(ice, reg);
new = (~ucontrol->value.integer.value[i]<<7&0x80) |
(old&~0x80);
change = (new != old);
if (change)
stac9460_2_put(ice, reg, new);
}
}
return change;
}
/*
*ADC gain mixer control
*/
static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0; /* 0dB */
uinfo->value.integer.max = 0x0f; /* 22.5dB */
return 0;
}
static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int i, reg, id;
unsigned char vol;
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
vol = stac9460_get(ice, reg) & 0x0f;
ucontrol->value.integer.value[i] = 0x0f - vol;
}
} else {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
vol = stac9460_2_get(ice, reg) & 0x0f;
ucontrol->value.integer.value[i] = 0x0f - vol;
}
}
return 0;
}
static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int i, reg, id;
unsigned char ovol, nvol;
int change;
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0) {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
nvol = ucontrol->value.integer.value[i] & 0x0f;
ovol = 0x0f - stac9460_get(ice, reg);
change = ((ovol & 0x0f) != nvol);
if (change)
stac9460_put(ice, reg, (0x0f - nvol) |
(ovol & ~0x0f));
}
} else {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
nvol = ucontrol->value.integer.value[i] & 0x0f;
ovol = 0x0f - stac9460_2_get(ice, reg);
change = ((ovol & 0x0f) != nvol);
if (change)
stac9460_2_put(ice, reg, (0x0f - nvol) |
(ovol & ~0x0f));
}
}
return change;
}
/*
* MIC / LINE switch fonction
*/
#define stac9460_mic_sw_info snd_ctl_boolean_mono_info
static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val;
int id;
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0)
val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
return 0;
}
static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char new, old;
int change, id;
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (id == 0)
old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
if (id == 0)
stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
else
stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
}
return change;
}
/*
* Control tabs
*/
static struct snd_kcontrol_new stac9640_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
.private_value = 1
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
.private_value = 1,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIC/Line switch",
.count = 2,
.info = stac9460_mic_sw_info,
.get = stac9460_mic_sw_get,
.put = stac9460_mic_sw_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Switch",
.count = 8,
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Volume",
.count = 8,
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Switch",
.count = 2,
.info = stac9460_adc_mute_info,
.get = stac9460_adc_mute_get,
.put = stac9460_adc_mute_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Volume",
.count = 2,
.info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
}
};
/*INIT*/
static int wtm_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(stac9640_controls); i++) {
err = snd_ctl_add(ice->card,
snd_ctl_new1(&stac9640_controls[i], ice));
if (err < 0)
return err;
}
return 0;
}
static int wtm_init(struct snd_ice1712 *ice)
{
static unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
(unsigned short)-1
};
unsigned short *p;
/*WTM 192M*/
ice->num_total_dacs = 8;
ice->num_total_adcs = 4;
ice->force_rdma1 = 1;
/*initialize codec*/
p = stac_inits_prodigy;
for (; *p != (unsigned short)-1; p += 2) {
stac9460_put(ice, p[0], p[1]);
stac9460_2_put(ice, p[0], p[1]);
}
return 0;
}
static unsigned char wtm_eeprom[] = {
0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */
0x80, /* ACLINK : I2S */
0xf8, /* I2S: vol; 96k, 24bit, 192k */
0xc1 /*SPDIF: out-en, spidf ext out*/,
0x9f, /* GPIO_DIR */
0xff, /* GPIO_DIR1 */
0x7f, /* GPIO_DIR2 */
0x9f, /* GPIO_MASK */
0xff, /* GPIO_MASK1 */
0x7f, /* GPIO_MASK2 */
0x16, /* GPIO_STATE */
0x80, /* GPIO_STATE1 */
0x00, /* GPIO_STATE2 */
};
/*entry point*/
struct snd_ice1712_card_info snd_vt1724_wtm_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_WTM,
.name = "ESI Waveterminal 192M",
.model = "WT192M",
.chip_init = wtm_init,
.build_controls = wtm_add_controls,
.eeprom_size = sizeof(wtm_eeprom),
.eeprom_data = wtm_eeprom,
},
{} /*terminator*/
};

20
sound/pci/ice1712/wtm.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef __SOUND_WTM_H
#define __SOUND_WTM_H
/* ID */
#define WTM_DEVICE_DESC "{EGO SYS INC,WaveTerminal 192M},"
#define VT1724_SUBDEVICE_WTM 0x36495345 /* WT192M ver1.0 */
/*
*chip addresses on I2C bus
*/
#define AK4114_ADDR 0x20 /*S/PDIF receiver*/
#define STAC9460_I2C_ADDR 0x54 /* ADC*2 | DAC*6 */
#define STAC9460_2_I2C_ADDR 0x56 /* ADC|DAC *2 */
extern struct snd_ice1712_card_info snd_vt1724_wtm_cards[];
#endif /* __SOUND_WTM_H */