mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
12
sound/pci/ice1712/Makefile
Normal file
12
sound/pci/ice1712/Makefile
Normal 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
196
sound/pci/ice1712/ak4xxx.c
Normal 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
97
sound/pci/ice1712/amp.c
Normal 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
48
sound/pci/ice1712/amp.h
Normal 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
2311
sound/pci/ice1712/aureon.c
Normal file
File diff suppressed because it is too large
Load diff
65
sound/pci/ice1712/aureon.h
Normal file
65
sound/pci/ice1712/aureon.h
Normal 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
934
sound/pci/ice1712/delta.c
Normal 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, ®, 1) != 1)
|
||||
dev_err(ice->card->dev,
|
||||
"unable to send register 0x%x byte to CS8427\n", reg);
|
||||
snd_i2c_readbytes(ice->cs8427, ®, 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
166
sound/pci/ice1712/delta.h
Normal 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 */
|
220
sound/pci/ice1712/envy24ht.h
Normal file
220
sound/pci/ice1712/envy24ht.h
Normal 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
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
86
sound/pci/ice1712/ews.h
Normal 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 */
|
359
sound/pci/ice1712/hoontech.c
Normal file
359
sound/pci/ice1712/hoontech.c
Normal 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 */
|
||||
};
|
77
sound/pci/ice1712/hoontech.h
Normal file
77
sound/pci/ice1712/hoontech.h
Normal 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
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
538
sound/pci/ice1712/ice1712.h
Normal 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
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
700
sound/pci/ice1712/juli.c
Normal 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
10
sound/pci/ice1712/juli.h
Normal 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
778
sound/pci/ice1712/maya44.c
Normal 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 */
|
||||
};
|
10
sound/pci/ice1712/maya44.h
Normal file
10
sound/pci/ice1712/maya44.h
Normal 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
974
sound/pci/ice1712/phase.c
Normal 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
53
sound/pci/ice1712/phase.h
Normal 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
835
sound/pci/ice1712/pontis.c
Normal 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", ®, &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 */
|
||||
};
|
33
sound/pci/ice1712/pontis.h
Normal file
33
sound/pci/ice1712/pontis.h
Normal 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 */
|
822
sound/pci/ice1712/prodigy192.c
Normal file
822
sound/pci/ice1712/prodigy192.c
Normal 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 */
|
||||
};
|
19
sound/pci/ice1712/prodigy192.h
Normal file
19
sound/pci/ice1712/prodigy192.h
Normal 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 */
|
1236
sound/pci/ice1712/prodigy_hifi.c
Normal file
1236
sound/pci/ice1712/prodigy_hifi.c
Normal file
File diff suppressed because it is too large
Load diff
38
sound/pci/ice1712/prodigy_hifi.h
Normal file
38
sound/pci/ice1712/prodigy_hifi.h
Normal 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
464
sound/pci/ice1712/psc724.c
Normal 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*/
|
||||
};
|
13
sound/pci/ice1712/psc724.h
Normal file
13
sound/pci/ice1712/psc724.h
Normal 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
1132
sound/pci/ice1712/quartet.c
Normal file
File diff suppressed because it is too large
Load diff
10
sound/pci/ice1712/quartet.h
Normal file
10
sound/pci/ice1712/quartet.h
Normal 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
643
sound/pci/ice1712/revo.c
Normal 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
55
sound/pci/ice1712/revo.h
Normal 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
773
sound/pci/ice1712/se.c
Normal 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
15
sound/pci/ice1712/se.h
Normal 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 */
|
25
sound/pci/ice1712/stac946x.h
Normal file
25
sound/pci/ice1712/stac946x.h
Normal 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 */
|
138
sound/pci/ice1712/vt1720_mobo.c
Normal file
138
sound/pci/ice1712/vt1720_mobo.c
Normal 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 */
|
||||
};
|
||||
|
41
sound/pci/ice1712/vt1720_mobo.h
Normal file
41
sound/pci/ice1712/vt1720_mobo.h
Normal 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
362
sound/pci/ice1712/wm8766.c
Normal 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
163
sound/pci/ice1712/wm8766.h
Normal 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
634
sound/pci/ice1712/wm8776.c
Normal 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
226
sound/pci/ice1712/wm8776.h
Normal 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
516
sound/pci/ice1712/wtm.c
Normal 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
20
sound/pci/ice1712/wtm.h
Normal 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 */
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue