1
0
Fork 0

[ALSA] Emagic Audiowerk 2 ALSA driver.

Signed-off-by: Cedric Bregardis <cedric.bregardis@free.fr>
Signed-off-by: Jean-Christian Hassler <jhassler@free.fr>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
hifive-unleashed-5.1
Cedric Bregardis 2008-02-20 12:05:13 +01:00 committed by Takashi Iwai
parent 67ebcb0311
commit 98f2a97f20
8 changed files with 1659 additions and 0 deletions

View File

@ -122,6 +122,21 @@ config SND_AU8830
To compile this driver as a module, choose M here: the module
will be called snd-au8830.
config SND_AW2
tristate "Emagic Audiowerk 2"
depends on SND
help
Say Y here to include support for Emagic Audiowerk 2 soundcards.
Supported features: Analog and SPDIF output. Analog or SPDIF input.
Note: Switch between analog and digital input does not always work.
It can produce continuous noise. The workaround is to switch again
(and again) between digital and analog input until it works.
To compile this driver as a module, choose M here: the module
will be called snd-aw2.
config SND_AZT3328
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
depends on SND && EXPERIMENTAL

View File

@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \
ac97/ \
ali5451/ \
au88x0/ \
aw2/ \
ca0106/ \
cs46xx/ \
cs5535audio/ \

View File

@ -0,0 +1,3 @@
snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
obj-$(CONFIG_SND_AW2) += snd-aw2.o

View File

@ -0,0 +1,787 @@
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
* Jean-Christian Hassler <jhassler@free.fr>
*
* This file is part of the Audiowerk2 ALSA driver
*
* The Audiowerk2 ALSA driver 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; version 2.
*
* The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*****************************************************************************/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/control.h>
#include "saa7146.h"
#include "aw2-saa7146.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cedric Bregardis <cedric.bregardis@free.fr>, "
"Jean-Christian Hassler <jhassler@free.fr>");
MODULE_DESCRIPTION("Emagic Audiowerk 2 sound driver");
MODULE_LICENSE("GPL");
/*********************************
* DEFINES
********************************/
#define PCI_VENDOR_ID_SAA7146 0x1131
#define PCI_DEVICE_ID_SAA7146 0x7146
#define CTL_ROUTE_ANALOG 0
#define CTL_ROUTE_DIGITAL 1
/*********************************
* TYPEDEFS
********************************/
/* hardware definition */
static struct snd_pcm_hardware snd_aw2_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_44100,
.rate_min = 44100,
.rate_max = 44100,
.channels_min = 2,
.channels_max = 4,
.buffer_bytes_max = 32768,
.period_bytes_min = 4096,
.period_bytes_max = 32768,
.periods_min = 1,
.periods_max = 1024,
};
static struct snd_pcm_hardware snd_aw2_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_44100,
.rate_min = 44100,
.rate_max = 44100,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 32768,
.period_bytes_min = 4096,
.period_bytes_max = 32768,
.periods_min = 1,
.periods_max = 1024,
};
struct aw2_pcm_device {
struct snd_pcm *pcm;
unsigned int stream_number;
struct aw2 *chip;
};
struct aw2 {
struct snd_aw2_saa7146 saa7146;
struct pci_dev *pci;
int irq;
spinlock_t reg_lock;
struct mutex mtx;
unsigned long iobase_phys;
void __iomem *iobase_virt;
struct snd_card *card;
struct aw2_pcm_device device_playback[NB_STREAM_PLAYBACK];
struct aw2_pcm_device device_capture[NB_STREAM_CAPTURE];
};
/*********************************
* FUNCTION DECLARATIONS
********************************/
static int __init alsa_card_aw2_init(void);
static void __exit alsa_card_aw2_exit(void);
static int snd_aw2_dev_free(struct snd_device *device);
static int __devinit snd_aw2_create(struct snd_card *card,
struct pci_dev *pci, struct aw2 **rchip);
static int __devinit snd_aw2_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id);
static void __devexit snd_aw2_remove(struct pci_dev *pci);
static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params);
static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
int cmd);
static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
int cmd);
static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
*substream);
static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
*substream);
static int __devinit snd_aw2_new_pcm(struct aw2 *chip);
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value
*ucontrol);
static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value
*ucontrol);
/*********************************
* VARIABLES
********************************/
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static struct pci_device_id snd_aw2_ids[] = {
{PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, PCI_ANY_ID, PCI_ANY_ID,
0, 0, 0},
{0}
};
MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
/* pci_driver definition */
static struct pci_driver driver = {
.name = "Emagic Audiowerk 2",
.id_table = snd_aw2_ids,
.probe = snd_aw2_probe,
.remove = __devexit_p(snd_aw2_remove),
};
/* operators for playback PCM alsa interface */
static struct snd_pcm_ops snd_aw2_playback_ops = {
.open = snd_aw2_pcm_playback_open,
.close = snd_aw2_pcm_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_aw2_pcm_hw_params,
.hw_free = snd_aw2_pcm_hw_free,
.prepare = snd_aw2_pcm_prepare_playback,
.trigger = snd_aw2_pcm_trigger_playback,
.pointer = snd_aw2_pcm_pointer_playback,
};
/* operators for capture PCM alsa interface */
static struct snd_pcm_ops snd_aw2_capture_ops = {
.open = snd_aw2_pcm_capture_open,
.close = snd_aw2_pcm_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_aw2_pcm_hw_params,
.hw_free = snd_aw2_pcm_hw_free,
.prepare = snd_aw2_pcm_prepare_capture,
.trigger = snd_aw2_pcm_trigger_capture,
.pointer = snd_aw2_pcm_pointer_capture,
};
static struct snd_kcontrol_new aw2_control __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Route",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = 0xffff,
.info = snd_aw2_control_switch_capture_info,
.get = snd_aw2_control_switch_capture_get,
.put = snd_aw2_control_switch_capture_put
};
/*********************************
* FUNCTION IMPLEMENTATIONS
********************************/
/* initialization of the module */
static int __init alsa_card_aw2_init(void)
{
snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
return pci_register_driver(&driver);
}
/* clean up the module */
static void __exit alsa_card_aw2_exit(void)
{
snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
pci_unregister_driver(&driver);
}
module_init(alsa_card_aw2_init);
module_exit(alsa_card_aw2_exit);
/* component-destructor */
static int snd_aw2_dev_free(struct snd_device *device)
{
struct aw2 *chip = device->device_data;
/* Free hardware */
snd_aw2_saa7146_free(&chip->saa7146);
/* release the irq */
if (chip->irq >= 0)
free_irq(chip->irq, (void *)chip);
/* release the i/o ports & memory */
if (chip->iobase_virt)
iounmap(chip->iobase_virt);
pci_release_regions(chip->pci);
/* disable the PCI entry */
pci_disable_device(chip->pci);
/* release the data */
kfree(chip);
return 0;
}
/* chip-specific constructor */
static int __devinit snd_aw2_create(struct snd_card *card,
struct pci_dev *pci, struct aw2 **rchip)
{
struct aw2 *chip;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_aw2_dev_free,
};
*rchip = NULL;
/* initialize the PCI entry */
err = pci_enable_device(pci);
if (err < 0)
return err;
pci_set_master(pci);
/* check PCI availability (32bit DMA) */
if ((pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) ||
(pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0)) {
printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n");
pci_disable_device(pci);
return -ENXIO;
}
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL) {
pci_disable_device(pci);
return -ENOMEM;
}
/* initialize the stuff */
chip->card = card;
chip->pci = pci;
chip->irq = -1;
/* (1) PCI resource allocation */
err = pci_request_regions(pci, "Audiowerk2");
if (err < 0) {
pci_disable_device(pci);
kfree(chip);
return err;
}
chip->iobase_phys = pci_resource_start(pci, 0);
chip->iobase_virt =
ioremap_nocache(chip->iobase_phys,
pci_resource_len(pci, 0));
if (chip->iobase_virt == NULL) {
printk(KERN_ERR "aw2: unable to remap memory region");
pci_release_regions(pci);
pci_disable_device(pci);
kfree(chip);
return -ENOMEM;
}
if (request_irq(pci->irq, snd_aw2_saa7146_interrupt,
IRQF_SHARED, "Audiowerk2", chip)) {
printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq);
iounmap(chip->iobase_virt);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
return -EBUSY;
}
chip->irq = pci->irq;
/* (2) initialization of the chip hardware */
snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt);
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
free_irq(chip->irq, (void *)chip);
iounmap(chip->iobase_virt);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
return err;
}
snd_card_set_dev(card, &pci->dev);
*rchip = chip;
printk(KERN_INFO
"Audiowerk 2 sound card (saa7146 chipset) detected and "
"managed\n");
return 0;
}
/* constructor */
static int __devinit snd_aw2_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct aw2 *chip;
int err;
/* (1) Continue if device is not enabled, else inc dev */
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
/* (2) Create card instance */
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
return -ENOMEM;
/* (3) Create main component */
err = snd_aw2_create(card, pci, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
/* initialize mutex */
mutex_init(&chip->mtx);
/* init spinlock */
spin_lock_init(&chip->reg_lock);
/* (4) Define driver ID and name string */
strcpy(card->driver, "aw2");
strcpy(card->shortname, "Audiowerk2");
sprintf(card->longname, "%s with SAA7146 irq %i",
card->shortname, chip->irq);
/* (5) Create other components */
snd_aw2_new_pcm(chip);
/* (6) Register card instance */
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
/* (7) Set PCI driver data */
pci_set_drvdata(pci, card);
dev++;
return 0;
}
/* destructor */
static void __devexit snd_aw2_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
/* open callback */
static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_printdd(KERN_DEBUG "aw2: Playback_open \n");
runtime->hw = snd_aw2_playback_hw;
return 0;
}
/* close callback */
static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_printdd(KERN_DEBUG "aw2: Capture_open \n");
runtime->hw = snd_aw2_capture_hw;
return 0;
}
/* close callback */
static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream)
{
/* TODO: something to do ? */
return 0;
}
/* hw_params callback */
static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
/* hw_free callback */
static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
/* prepare callback for playback */
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long period_size, buffer_size;
mutex_lock(&chip->mtx);
period_size = snd_pcm_lib_period_bytes(substream);
buffer_size = snd_pcm_lib_buffer_bytes(substream);
snd_aw2_saa7146_pcm_init_playback(&chip->saa7146,
pcm_device->stream_number,
runtime->dma_addr, period_size,
buffer_size);
/* Define Interrupt callback */
snd_aw2_saa7146_define_it_playback_callback(pcm_device->stream_number,
(snd_aw2_saa7146_it_cb)
snd_pcm_period_elapsed,
(void *)substream);
mutex_unlock(&chip->mtx);
return 0;
}
/* prepare callback for capture */
static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
{
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long period_size, buffer_size;
mutex_lock(&chip->mtx);
period_size = snd_pcm_lib_period_bytes(substream);
buffer_size = snd_pcm_lib_buffer_bytes(substream);
snd_aw2_saa7146_pcm_init_capture(&chip->saa7146,
pcm_device->stream_number,
runtime->dma_addr, period_size,
buffer_size);
/* Define Interrupt callback */
snd_aw2_saa7146_define_it_capture_callback(pcm_device->stream_number,
(snd_aw2_saa7146_it_cb)
snd_pcm_period_elapsed,
(void *)substream);
mutex_unlock(&chip->mtx);
return 0;
}
/* playback trigger callback */
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
int cmd)
{
int status = 0;
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146,
pcm_device->
stream_number);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_aw2_saa7146_pcm_trigger_stop_playback(&chip->saa7146,
pcm_device->
stream_number);
break;
default:
status = -EINVAL;
}
spin_unlock(&chip->reg_lock);
return status;
}
/* capture trigger callback */
static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
int cmd)
{
int status = 0;
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146,
pcm_device->
stream_number);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_aw2_saa7146_pcm_trigger_stop_capture(&chip->saa7146,
pcm_device->
stream_number);
break;
default:
status = -EINVAL;
}
spin_unlock(&chip->reg_lock);
return status;
}
/* playback pointer callback */
static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
*substream)
{
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
unsigned int current_ptr;
/* get the current hardware pointer */
struct snd_pcm_runtime *runtime = substream->runtime;
current_ptr =
snd_aw2_saa7146_get_hw_ptr_playback(&chip->saa7146,
pcm_device->stream_number,
runtime->dma_area,
runtime->buffer_size);
return bytes_to_frames(substream->runtime, current_ptr);
}
/* capture pointer callback */
static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
*substream)
{
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
unsigned int current_ptr;
/* get the current hardware pointer */
struct snd_pcm_runtime *runtime = substream->runtime;
current_ptr =
snd_aw2_saa7146_get_hw_ptr_capture(&chip->saa7146,
pcm_device->stream_number,
runtime->dma_area,
runtime->buffer_size);
return bytes_to_frames(substream->runtime, current_ptr);
}
/* create a pcm device */
static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
{
struct snd_pcm *pcm_playback_ana;
struct snd_pcm *pcm_playback_num;
struct snd_pcm *pcm_capture;
struct aw2_pcm_device *pcm_device;
int err = 0;
/* Create new Alsa PCM device */
err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0,
&pcm_playback_ana);
if (err < 0) {
printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
return err;
}
/* Creation ok */
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA];
/* Set PCM device name */
strcpy(pcm_playback_ana->name, "Analog playback");
/* Associate private data to PCM device */
pcm_playback_ana->private_data = pcm_device;
/* set operators of PCM device */
snd_pcm_set_ops(pcm_playback_ana, SNDRV_PCM_STREAM_PLAYBACK,
&snd_aw2_playback_ops);
/* store PCM device */
pcm_device->pcm = pcm_playback_ana;
/* give base chip pointer to our internal pcm device
structure */
pcm_device->chip = chip;
/* Give stream number to PCM device */
pcm_device->stream_number = NUM_STREAM_PLAYBACK_ANA;
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data
(chip->pci),
64 * 1024, 64 * 1024);
if (err)
printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all "
"error (0x%X)\n", err);
err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0,
&pcm_playback_num);
if (err < 0) {
printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
return err;
}
/* Creation ok */
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG];
/* Set PCM device name */
strcpy(pcm_playback_num->name, "Digital playback");
/* Associate private data to PCM device */
pcm_playback_num->private_data = pcm_device;
/* set operators of PCM device */
snd_pcm_set_ops(pcm_playback_num, SNDRV_PCM_STREAM_PLAYBACK,
&snd_aw2_playback_ops);
/* store PCM device */
pcm_device->pcm = pcm_playback_num;
/* give base chip pointer to our internal pcm device
structure */
pcm_device->chip = chip;
/* Give stream number to PCM device */
pcm_device->stream_number = NUM_STREAM_PLAYBACK_DIG;
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data
(chip->pci),
64 * 1024, 64 * 1024);
if (err)
printk(KERN_ERR
"aw2: snd_pcm_lib_preallocate_pages_for_all error "
"(0x%X)\n", err);
err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1,
&pcm_capture);
if (err < 0) {
printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
return err;
}
/* Creation ok */
pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA];
/* Set PCM device name */
strcpy(pcm_capture->name, "Capture");
/* Associate private data to PCM device */
pcm_capture->private_data = pcm_device;
/* set operators of PCM device */
snd_pcm_set_ops(pcm_capture, SNDRV_PCM_STREAM_CAPTURE,
&snd_aw2_capture_ops);
/* store PCM device */
pcm_device->pcm = pcm_capture;
/* give base chip pointer to our internal pcm device
structure */
pcm_device->chip = chip;
/* Give stream number to PCM device */
pcm_device->stream_number = NUM_STREAM_CAPTURE_ANA;
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data
(chip->pci),
64 * 1024, 64 * 1024);
if (err)
printk(KERN_ERR
"aw2: snd_pcm_lib_preallocate_pages_for_all error "
"(0x%X)\n", err);
/* Create control */
err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip));
if (err < 0) {
printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err);
return err;
}
return 0;
}
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[2] = {
"Analog", "Digital"
};
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 snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value
*ucontrol)
{
struct aw2 *chip = snd_kcontrol_chip(kcontrol);
if (snd_aw2_saa7146_is_using_digital_input(&chip->saa7146))
ucontrol->value.enumerated.item[0] = CTL_ROUTE_DIGITAL;
else
ucontrol->value.enumerated.item[0] = CTL_ROUTE_ANALOG;
return 0;
}
static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value
*ucontrol)
{
struct aw2 *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int is_disgital =
snd_aw2_saa7146_is_using_digital_input(&chip->saa7146);
if (((ucontrol->value.integer.value[0] == CTL_ROUTE_DIGITAL)
&& !is_disgital)
|| ((ucontrol->value.integer.value[0] == CTL_ROUTE_ANALOG)
&& is_disgital)) {
snd_aw2_saa7146_use_digital_input(&chip->saa7146, !is_disgital);
changed = 1;
}
return changed;
}

View File

@ -0,0 +1,464 @@
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
* Jean-Christian Hassler <jhassler@free.fr>
*
* This file is part of the Audiowerk2 ALSA driver
*
* The Audiowerk2 ALSA driver 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; version 2.
*
* The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*****************************************************************************/
#define AW2_SAA7146_M
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "aw2-tsl.h"
#include "saa7146.h"
#include "aw2-saa7146.h"
#define WRITEREG(value, addr) writel((value), chip->base_addr + (addr))
#define READREG(addr) readl(chip->base_addr + (addr))
static struct snd_aw2_saa7146_cb_param
arr_substream_it_playback_cb[NB_STREAM_PLAYBACK];
static struct snd_aw2_saa7146_cb_param
arr_substream_it_capture_cb[NB_STREAM_CAPTURE];
static int snd_aw2_saa7146_get_limit(int size);
/* chip-specific destructor */
int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip)
{
/* disable all irqs */
WRITEREG(0, IER);
/* reset saa7146 */
WRITEREG((MRST_N << 16), MC1);
/* Unset base addr */
chip->base_addr = NULL;
return 0;
}
void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip,
void __iomem *pci_base_addr)
{
/* set PCI burst/threshold
Burst length definition
VALUE BURST LENGTH
000 1 Dword
001 2 Dwords
010 4 Dwords
011 8 Dwords
100 16 Dwords
101 32 Dwords
110 64 Dwords
111 128 Dwords
Threshold definition
VALUE WRITE MODE READ MODE
00 1 Dword of valid data 1 empty Dword
01 4 Dwords of valid data 4 empty Dwords
10 8 Dwords of valid data 8 empty Dwords
11 16 Dwords of valid data 16 empty Dwords */
unsigned int acon2;
unsigned int acon1 = 0;
int i;
/* Set base addr */
chip->base_addr = pci_base_addr;
/* disable all irqs */
WRITEREG(0, IER);
/* reset saa7146 */
WRITEREG((MRST_N << 16), MC1);
/* enable audio interface */
#ifdef __BIG_ENDIAN
acon1 |= A1_SWAP;
acon1 |= A2_SWAP;
#endif
/* WS0_CTRL, WS0_SYNC: input TSL1, I2S */
/* At initialization WS1 and WS2 are disbaled (configured as input */
acon1 |= 0 * WS1_CTRL;
acon1 |= 0 * WS2_CTRL;
/* WS4 is not used. So it must not restart A2.
This is why it is configured as output (force to low) */
acon1 |= 3 * WS4_CTRL;
/* WS3_CTRL, WS3_SYNC: output TSL2, I2S */
acon1 |= 2 * WS3_CTRL;
/* A1 and A2 are active and asynchronous */
acon1 |= 3 * AUDIO_MODE;
WRITEREG(acon1, ACON1);
/* The following comes from original windows driver.
It is needed to have a correct behavior of input and output
simultenously, but I don't know why ! */
WRITEREG(3 * (BurstA1_in) + 3 * (ThreshA1_in) +
3 * (BurstA1_out) + 3 * (ThreshA1_out) +
3 * (BurstA2_out) + 3 * (ThreshA2_out), PCI_BT_A);
/* enable audio port pins */
WRITEREG((EAP << 16) | EAP, MC1);
/* enable I2C */
WRITEREG((EI2C << 16) | EI2C, MC1);
/* enable interrupts */
WRITEREG(A1_out | A2_out | A1_in | IIC_S | IIC_E, IER);
/* audio configuration */
acon2 = A2_CLKSRC | BCLK1_OEN;
WRITEREG(acon2, ACON2);
/* By default use analog input */
snd_aw2_saa7146_use_digital_input(chip, 0);
/* TSL setup */
for (i = 0; i < 8; ++i) {
WRITEREG(tsl1[i], TSL1 + (i * 4));
WRITEREG(tsl2[i], TSL2 + (i * 4));
}
}
void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
int stream_number,
unsigned long dma_addr,
unsigned long period_size,
unsigned long buffer_size)
{
unsigned long dw_page, dw_limit;
/* Configure DMA for substream
Configuration informations: ALSA has allocated continuous memory
pages. So we don't need to use MMU of saa7146.
*/
/* No MMU -> nothing to do with PageA1, we only configure the limit of
PageAx_out register */
/* Disable MMU */
dw_page = (0L << 11);
/* Configure Limit for DMA access.
The limit register defines an address limit, which generates
an interrupt if passed by the actual PCI address pointer.
'0001' means an interrupt will be generated if the lower
6 bits (64 bytes) of the PCI address are zero. '0010'
defines a limit of 128 bytes, '0011' one of 256 bytes, and
so on up to 1 Mbyte defined by '1111'. This interrupt range
can be calculated as follows:
Range = 2^(5 + Limit) bytes.
*/
dw_limit = snd_aw2_saa7146_get_limit(period_size);
dw_page |= (dw_limit << 4);
if (stream_number == 0) {
WRITEREG(dw_page, PageA2_out);
/* Base address for DMA transfert. */
/* This address has been reserved by ALSA. */
/* This is a physical address */
WRITEREG(dma_addr, BaseA2_out);
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA2_out);
} else if (stream_number == 1) {
WRITEREG(dw_page, PageA1_out);
/* Base address for DMA transfert. */
/* This address has been reserved by ALSA. */
/* This is a physical address */
WRITEREG(dma_addr, BaseA1_out);
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA1_out);
} else {
printk(KERN_ERR
"aw2: snd_aw2_saa7146_pcm_init_playback: "
"Substream number is not 0 or 1 -> not managed\n");
}
}
void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
int stream_number, unsigned long dma_addr,
unsigned long period_size,
unsigned long buffer_size)
{
unsigned long dw_page, dw_limit;
/* Configure DMA for substream
Configuration informations: ALSA has allocated continuous memory
pages. So we don't need to use MMU of saa7146.
*/
/* No MMU -> nothing to do with PageA1, we only configure the limit of
PageAx_out register */
/* Disable MMU */
dw_page = (0L << 11);
/* Configure Limit for DMA access.
The limit register defines an address limit, which generates
an interrupt if passed by the actual PCI address pointer.
'0001' means an interrupt will be generated if the lower
6 bits (64 bytes) of the PCI address are zero. '0010'
defines a limit of 128 bytes, '0011' one of 256 bytes, and
so on up to 1 Mbyte defined by '1111'. This interrupt range
can be calculated as follows:
Range = 2^(5 + Limit) bytes.
*/
dw_limit = snd_aw2_saa7146_get_limit(period_size);
dw_page |= (dw_limit << 4);
if (stream_number == 0) {
WRITEREG(dw_page, PageA1_in);
/* Base address for DMA transfert. */
/* This address has been reserved by ALSA. */
/* This is a physical address */
WRITEREG(dma_addr, BaseA1_in);
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA1_in);
} else {
printk(KERN_ERR
"aw2: snd_aw2_saa7146_pcm_init_capture: "
"Substream number is not 0 -> not managed\n");
}
}
void snd_aw2_saa7146_define_it_playback_callback(unsigned int stream_number,
snd_aw2_saa7146_it_cb
p_it_callback,
void *p_callback_param)
{
if (stream_number < NB_STREAM_PLAYBACK) {
arr_substream_it_playback_cb[stream_number].p_it_callback =
(snd_aw2_saa7146_it_cb) p_it_callback;
arr_substream_it_playback_cb[stream_number].p_callback_param =
(void *)p_callback_param;
}
}
void snd_aw2_saa7146_define_it_capture_callback(unsigned int stream_number,
snd_aw2_saa7146_it_cb
p_it_callback,
void *p_callback_param)
{
if (stream_number < NB_STREAM_CAPTURE) {
arr_substream_it_capture_cb[stream_number].p_it_callback =
(snd_aw2_saa7146_it_cb) p_it_callback;
arr_substream_it_capture_cb[stream_number].p_callback_param =
(void *)p_callback_param;
}
}
void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 *chip,
int stream_number)
{
unsigned int acon1 = 0;
/* In aw8 driver, dma transfert is always active. It is
started and stopped in a larger "space" */
acon1 = READREG(ACON1);
if (stream_number == 0) {
WRITEREG((TR_E_A2_OUT << 16) | TR_E_A2_OUT, MC1);
/* WS2_CTRL, WS2_SYNC: output TSL2, I2S */
acon1 |= 2 * WS2_CTRL;
WRITEREG(acon1, ACON1);
} else if (stream_number == 1) {
WRITEREG((TR_E_A1_OUT << 16) | TR_E_A1_OUT, MC1);
/* WS1_CTRL, WS1_SYNC: output TSL1, I2S */
acon1 |= 1 * WS1_CTRL;
WRITEREG(acon1, ACON1);
}
}
void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 *chip,
int stream_number)
{
unsigned int acon1 = 0;
acon1 = READREG(ACON1);
if (stream_number == 0) {
/* WS2_CTRL, WS2_SYNC: output TSL2, I2S */
acon1 &= ~(3 * WS2_CTRL);
WRITEREG(acon1, ACON1);
WRITEREG((TR_E_A2_OUT << 16), MC1);
} else if (stream_number == 1) {
/* WS1_CTRL, WS1_SYNC: output TSL1, I2S */
acon1 &= ~(3 * WS1_CTRL);
WRITEREG(acon1, ACON1);
WRITEREG((TR_E_A1_OUT << 16), MC1);
}
}
void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 *chip,
int stream_number)
{
/* In aw8 driver, dma transfert is always active. It is
started and stopped in a larger "space" */
if (stream_number == 0)
WRITEREG((TR_E_A1_IN << 16) | TR_E_A1_IN, MC1);
}
void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip,
int stream_number)
{
if (stream_number == 0)
WRITEREG((TR_E_A1_IN << 16), MC1);
}
irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id)
{
unsigned int isr;
unsigned int iicsta;
struct snd_aw2_saa7146 *chip = dev_id;
isr = READREG(ISR);
if (!isr)
return IRQ_NONE;
WRITEREG(isr, ISR);
if (isr & (IIC_S | IIC_E)) {
iicsta = READREG(IICSTA);
WRITEREG(0x100, IICSTA);
}
if (isr & A1_out) {
if (arr_substream_it_playback_cb[1].p_it_callback != NULL) {
arr_substream_it_playback_cb[1].
p_it_callback(arr_substream_it_playback_cb[1].
p_callback_param);
}
}
if (isr & A2_out) {
if (arr_substream_it_playback_cb[0].p_it_callback != NULL) {
arr_substream_it_playback_cb[0].
p_it_callback(arr_substream_it_playback_cb[0].
p_callback_param);
}
}
if (isr & A1_in) {
if (arr_substream_it_capture_cb[0].p_it_callback != NULL) {
arr_substream_it_capture_cb[0].
p_it_callback(arr_substream_it_capture_cb[0].
p_callback_param);
}
}
return IRQ_HANDLED;
}
unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 *chip,
int stream_number,
unsigned char *start_addr,
unsigned int buffer_size)
{
long pci_adp = 0;
size_t ptr = 0;
if (stream_number == 0) {
pci_adp = READREG(PCI_ADP3);
ptr = pci_adp - (long)start_addr;
if (ptr == buffer_size)
ptr = 0;
}
if (stream_number == 1) {
pci_adp = READREG(PCI_ADP1);
ptr = pci_adp - (size_t) start_addr;
if (ptr == buffer_size)
ptr = 0;
}
return ptr;
}
unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 *chip,
int stream_number,
unsigned char *start_addr,
unsigned int buffer_size)
{
size_t pci_adp = 0;
size_t ptr = 0;
if (stream_number == 0) {
pci_adp = READREG(PCI_ADP2);
ptr = pci_adp - (size_t) start_addr;
if (ptr == buffer_size)
ptr = 0;
}
return ptr;
}
void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip,
int use_digital)
{
/* FIXME: switch between analog and digital input does not always work.
It can produce a kind of white noise. It seams that received data
are inverted sometime (endian inversion). Why ? I don't know, maybe
a problem of synchronization... However for the time being I have
not found the problem. Workaround: switch again (and again) between
digital and analog input until it works. */
if (use_digital)
WRITEREG(0x40, GPIO_CTRL);
else
WRITEREG(0x50, GPIO_CTRL);
}
int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 *chip)
{
unsigned int reg_val = READREG(GPIO_CTRL);
if ((reg_val & 0xFF) == 0x40)
return 1;
else
return 0;
}
static int snd_aw2_saa7146_get_limit(int size)
{
int limitsize = 32;
int limit = 0;
while (limitsize < size) {
limitsize *= 2;
limit++;
}
return limit;
}

View File

@ -0,0 +1,105 @@
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
* Jean-Christian Hassler <jhassler@free.fr>
*
* This file is part of the Audiowerk2 ALSA driver
*
* The Audiowerk2 ALSA driver 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; version 2.
*
* The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*****************************************************************************/
#ifndef AW2_SAA7146_H
#define AW2_SAA7146_H
#define NB_STREAM_PLAYBACK 2
#define NB_STREAM_CAPTURE 1
#define NUM_STREAM_PLAYBACK_ANA 0
#define NUM_STREAM_PLAYBACK_DIG 1
#define NUM_STREAM_CAPTURE_ANA 0
typedef void (*snd_aw2_saa7146_it_cb) (void *);
struct snd_aw2_saa7146_cb_param {
snd_aw2_saa7146_it_cb p_it_callback;
void *p_callback_param;
};
/* definition of the chip-specific record */
struct snd_aw2_saa7146 {
void __iomem *base_addr;
};
extern void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip,
void __iomem *pci_base_addr);
extern int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip);
extern void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
int stream_number,
unsigned long dma_addr,
unsigned long period_size,
unsigned long buffer_size);
extern void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
int stream_number,
unsigned long dma_addr,
unsigned long period_size,
unsigned long buffer_size);
extern void snd_aw2_saa7146_define_it_playback_callback(unsigned int
stream_number,
snd_aw2_saa7146_it_cb
p_it_callback,
void *p_callback_param);
extern void snd_aw2_saa7146_define_it_capture_callback(unsigned int
stream_number,
snd_aw2_saa7146_it_cb
p_it_callback,
void *p_callback_param);
extern void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146
*chip, int stream_number);
extern void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146
*chip, int stream_number);
extern void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146
*chip,
int stream_number);
extern void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146
*chip, int stream_number);
extern irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id);
extern unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146
*chip,
int stream_number,
unsigned char
*start_addr,
unsigned int
buffer_size);
extern unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146
*chip,
int stream_number,
unsigned char
*start_addr,
unsigned int
buffer_size);
extern void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip,
int use_digital);
extern int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146
*chip);
#endif

View File

@ -0,0 +1,116 @@
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
* Jean-Christian Hassler <jhassler@free.fr>
* Copyright 1998 Emagic Soft- und Hardware GmbH
* Copyright 2002 Martijn Sipkema
*
* This file is part of the Audiowerk2 ALSA driver
*
* The Audiowerk2 ALSA driver 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; version 2.
*
* The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*****************************************************************************/
#define TSL_WS0 (1UL << 31)
#define TSL_WS1 (1UL << 30)
#define TSL_WS2 (1UL << 29)
#define TSL_WS3 (1UL << 28)
#define TSL_WS4 (1UL << 27)
#define TSL_DIS_A1 (1UL << 24)
#define TSL_SDW_A1 (1UL << 23)
#define TSL_SIB_A1 (1UL << 22)
#define TSL_SF_A1 (1UL << 21)
#define TSL_LF_A1 (1UL << 20)
#define TSL_BSEL_A1 (1UL << 17)
#define TSL_DOD_A1 (1UL << 15)
#define TSL_LOW_A1 (1UL << 14)
#define TSL_DIS_A2 (1UL << 11)
#define TSL_SDW_A2 (1UL << 10)
#define TSL_SIB_A2 (1UL << 9)
#define TSL_SF_A2 (1UL << 8)
#define TSL_LF_A2 (1UL << 7)
#define TSL_BSEL_A2 (1UL << 4)
#define TSL_DOD_A2 (1UL << 2)
#define TSL_LOW_A2 (1UL << 1)
#define TSL_EOS (1UL << 0)
/* Audiowerk8 hardware setup: */
/* WS0, SD4, TSL1 - Analog/ digital in */
/* WS1, SD0, TSL1 - Analog out #1, digital out */
/* WS2, SD2, TSL1 - Analog out #2 */
/* WS3, SD1, TSL2 - Analog out #3 */
/* WS4, SD3, TSL2 - Analog out #4 */
/* Audiowerk8 timing: */
/* Timeslot: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... */
/* A1_INPUT: */
/* SD4: <_ADC-L_>-------<_ADC-R_>-------< */
/* WS0: _______________/---------------\_ */
/* A1_OUTPUT: */
/* SD0: <_1-L___>-------<_1-R___>-------< */
/* WS1: _______________/---------------\_ */
/* SD2: >-------<_2-L___>-------<_2-R___> */
/* WS2: -------\_______________/--------- */
/* A2_OUTPUT: */
/* SD1: <_3-L___>-------<_3-R___>-------< */
/* WS3: _______________/---------------\_ */
/* SD3: >-------<_4-L___>-------<_4-R___> */
/* WS4: -------\_______________/--------- */
#ifdef __BIG_ENDIAN
/* TODO: not yet implemented */
#else /* */
static int tsl1[8] = {
1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1,
1 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
0 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
0 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
1 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
1 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
0 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
0 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | 0 * TSL_DIS_A1 |
0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS,
};
static int tsl2[8] = {
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2,
0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2 | TSL_EOS
};
#endif /* */

View File

@ -0,0 +1,168 @@
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
* Jean-Christian Hassler <jhassler@free.fr>
*
* This file is part of the Audiowerk2 ALSA driver
*
* The Audiowerk2 ALSA driver 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; version 2.
*
* The Audiowerk2 ALSA driver 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*****************************************************************************/
/* SAA7146 registers */
#define PCI_BT_A 0x4C
#define IICTFR 0x8C
#define IICSTA 0x90
#define BaseA1_in 0x94
#define ProtA1_in 0x98
#define PageA1_in 0x9C
#define BaseA1_out 0xA0
#define ProtA1_out 0xA4
#define PageA1_out 0xA8
#define BaseA2_in 0xAC
#define ProtA2_in 0xB0
#define PageA2_in 0xB4
#define BaseA2_out 0xB8
#define ProtA2_out 0xBC
#define PageA2_out 0xC0
#define IER 0xDC
#define GPIO_CTRL 0xE0
#define ACON1 0xF4
#define ACON2 0xF8
#define MC1 0xFC
#define MC2 0x100
#define ISR 0x10C
#define PSR 0x110
#define SSR 0x114
#define PCI_ADP1 0x12C
#define PCI_ADP2 0x130
#define PCI_ADP3 0x134
#define PCI_ADP4 0x138
#define LEVEL_REP 0x140
#define FB_BUFFER1 0x144
#define FB_BUFFER2 0x148
#define TSL1 0x180
#define TSL2 0x1C0
#define ME (1UL << 11)
#define LIMIT (1UL << 4)
#define PV (1UL << 3)
/* PSR/ISR/IER */
#define PPEF (1UL << 31)
#define PABO (1UL << 30)
#define IIC_S (1UL << 17)
#define IIC_E (1UL << 16)
#define A2_in (1UL << 15)
#define A2_out (1UL << 14)
#define A1_in (1UL << 13)
#define A1_out (1UL << 12)
#define AFOU (1UL << 11)
#define PIN3 (1UL << 6)
#define PIN2 (1UL << 5)
#define PIN1 (1UL << 4)
#define PIN0 (1UL << 3)
#define ECS (1UL << 2)
#define EC3S (1UL << 1)
#define EC0S (1UL << 0)
/* SSR */
#define PRQ (1UL << 31)
#define PMA (1UL << 30)
#define IIC_EA (1UL << 21)
#define IIC_EW (1UL << 20)
#define IIC_ER (1UL << 19)
#define IIC_EL (1UL << 18)
#define IIC_EF (1UL << 17)
#define AF2_in (1UL << 10)
#define AF2_out (1UL << 9)
#define AF1_in (1UL << 8)
#define AF1_out (1UL << 7)
#define EC5S (1UL << 3)
#define EC4S (1UL << 2)
#define EC2S (1UL << 1)
#define EC1S (1UL << 0)
/* PCI_BT_A */
#define BurstA1_in (1UL << 26)
#define ThreshA1_in (1UL << 24)
#define BurstA1_out (1UL << 18)
#define ThreshA1_out (1UL << 16)
#define BurstA2_in (1UL << 10)
#define ThreshA2_in (1UL << 8)
#define BurstA2_out (1UL << 2)
#define ThreshA2_out (1UL << 0)
/* MC1 */
#define MRST_N (1UL << 15)
#define EAP (1UL << 9)
#define EI2C (1UL << 8)
#define TR_E_A2_OUT (1UL << 3)
#define TR_E_A2_IN (1UL << 2)
#define TR_E_A1_OUT (1UL << 1)
#define TR_E_A1_IN (1UL << 0)
/* MC2 */
#define UPLD_IIC (1UL << 0)
/* ACON1 */
#define AUDIO_MODE (1UL << 29)
#define MAXLEVEL (1UL << 22)
#define A1_SWAP (1UL << 21)
#define A2_SWAP (1UL << 20)
#define WS0_CTRL (1UL << 18)
#define WS0_SYNC (1UL << 16)
#define WS1_CTRL (1UL << 14)
#define WS1_SYNC (1UL << 12)
#define WS2_CTRL (1UL << 10)
#define WS2_SYNC (1UL << 8)
#define WS3_CTRL (1UL << 6)
#define WS3_SYNC (1UL << 4)
#define WS4_CTRL (1UL << 2)
#define WS4_SYNC (1UL << 0)
/* ACON2 */
#define A1_CLKSRC (1UL << 27)
#define A2_CLKSRC (1UL << 22)
#define INVERT_BCLK1 (1UL << 21)
#define INVERT_BCLK2 (1UL << 20)
#define BCLK1_OEN (1UL << 19)
#define BCLK2_OEN (1UL << 18)
/* IICSTA */
#define IICCC (1UL << 8)
#define ABORT (1UL << 7)
#define SPERR (1UL << 6)
#define APERR (1UL << 5)
#define DTERR (1UL << 4)
#define DRERR (1UL << 3)
#define AL (1UL << 2)
#define ERR (1UL << 1)
#define BUSY (1UL << 0)
/* IICTFR */
#define BYTE2 (1UL << 24)
#define BYTE1 (1UL << 16)
#define BYTE0 (1UL << 8)
#define ATRR2 (1UL << 6)
#define ATRR1 (1UL << 4)
#define ATRR0 (1UL << 2)
#define ERR (1UL << 1)
#define BUSY (1UL << 0)
#define START 3
#define CONT 2
#define STOP 1
#define NOP 0