[alsa-devel] [PATCH 2/3] saa7146: Emagic Audiowerk8 low-level ALSA driver
From: Matthias Nyffenegger matthias.nyffenegger@bluewin.ch
Low-level ALSA driver for Emagic Audiowerk8 sound card. Project page: http://sourceforge.net/projects/aw8-alsa Built and tested with Vanilla 2.6.25.16
Signed-off-by: Matthias Nyffenegger matthias.nyffenegger@bluewin.ch --- This is a request for submission to ALSA-tree.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/README.txt ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/README.txt --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/README.txt 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/README.txt 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1 @@ +See sourceforge.net project "Audiowerk8 ALSA driver" (aw8-alsa) Wiki for more information. diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/Makefile ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/Makefile --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/Makefile 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1,8 @@ +# Makefile for Audiowerk module. +# run command in source directory: +# make -C /usr/src/linux M=`pwd` modules modules_install + +EXTRA_CFLAGS := -DSAA7146_SUBSYS_LOG_TAG="AW8" +obj-m := snd-aw8.o +snd-aw8-objs := saa7146i2c.o saa7146i2s.o saa7146audio.o aw8.o aw8alsa.o + diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8alsa.c ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8alsa.c --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8alsa.c 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8alsa.c 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1,826 @@ +/* + * Emagic Audiowerk ALSA driver + * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch> + * + * + * 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/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "log.h" +#include "saa7146audio.h" +#include "aw8.h" + +/* From Takashi Iwai's "Writing an ALSA Driver" developer guide */ +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; + +/* + * Module info + */ +MODULE_AUTHOR("Matthias Nyffenegger matthias.nyffenegger[AT]bluewin.ch"); +MODULE_DESCRIPTION(AW_MODULE_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0:0.2"); + +/** + * Module parameter 'input_type': select capture input digital or analog + * (default) + */ +int input_type; +module_param(input_type, int, 0444); +MODULE_PARM_DESC(input_type, "0=analog (default), 1=digital"); + +#define PCM_0 0 + +/** + * The ALSA 'chip' data structure. + */ +struct awalsa_t { + /* the alsa related stuff */ + struct snd_card *card; + struct snd_pcm *pcm_0; + struct snd_pcm_substream *pcm_in_substreams[MAX_IN_AUDIO_STREAMS]; + struct snd_pcm_substream *pcm_out_substreams[MAX_OUT_AUDIO_STREAMS]; + /* the SAA7146A stuff */ + struct saa7146audio chipaudio; + struct audio_stream *in_streams[MAX_IN_AUDIO_STREAMS]; + struct audio_stream *out_streams[MAX_OUT_AUDIO_STREAMS]; + /*/ the PCI stuff */ + struct pci_dev *pci; + int irq; + /* other useful things */ + spinlock_t isr_lock; +}; + +static int snd_aw_substream_open(struct snd_pcm_substream *substream, int in); +static int snd_aw_substream_close(struct snd_pcm_substream *substream, int in); +static int snd_aw_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +static int snd_aw_pcm_hw_free(struct snd_pcm_substream *substream); +static int snd_aw_pcm_prepare_substream(struct snd_pcm_substream *substream, + int in); +static int snd_aw_pcm_trigger_substream(struct snd_pcm_substream *substream, + int cmd, int in); +static snd_pcm_uframes_t snd_aw_pcm_pointer_substream( + struct snd_pcm_substream *substream, + int in); + +/** + * + * ALSA capture stream definitions and callbacks + * + */ +static struct snd_pcm_hardware snd_aw_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 | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE), + .rates = AW_PCM_RATE, + .rate_min = AW_FS_MIN, + .rate_max = AW_FS_MAX, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 16384, + .period_bytes_min = 64, /* HW restriction, see SAA7146A spec p.24 */ + .period_bytes_max = 8192, + .periods_min = 2, + .periods_max = 2, +}; + +/** + * ALSA capture stream open callback. + */ +static int snd_aw_capture_open(struct snd_pcm_substream *substream) +{ + return snd_aw_substream_open(substream, 1); +} + +/** + * ALSA capture stream close callback. + */ +static int snd_aw_capture_close(struct snd_pcm_substream *substream) +{ + return snd_aw_substream_close(substream, 1); +} + +/** + * Module scoped helper: converts alsa to Audiowerk capture sample format. + */ +static enum aw_formats alsa_2_aw_capture_format(snd_pcm_format_t fmt) +{ + switch (fmt) { + case SNDRV_PCM_FORMAT_S16_LE: + return fmt_s16_2le; + case SNDRV_PCM_FORMAT_S16_BE: + return fmt_s16_2be; + case SNDRV_PCM_FORMAT_S32_LE: + return fmt_s18_4le; + case SNDRV_PCM_FORMAT_S32_BE: + return fmt_s18_4be; + default: + LOG_ERROR("Unsupported capture format %d", fmt); + return fmt_s16_2le; + } +} + +/** + * ALSA capture stream prepare callback. + * Be careful that this callback will be called many times at each set up. + */ +static int snd_aw_pcm_prepare_capture(struct snd_pcm_substream *substream) +{ + return snd_aw_pcm_prepare_substream(substream, 1); +} + +/** + * ALSA capture stream trigger callback. [atomic]. + */ +static int snd_aw_pcm_trigger_capture(struct snd_pcm_substream *substream, + int cmd) +{ + return snd_aw_pcm_trigger_substream(substream, cmd, 1); +} + +/** + * ALSA capture stream hw-buffer pointer callback. [atomic] + */ +static snd_pcm_uframes_t snd_aw_pcm_pointer_capture( + struct snd_pcm_substream *substream) +{ + return snd_aw_pcm_pointer_substream(substream, 1); +} + +/** + * ALSA capture stream callback operations. + */ +static struct snd_pcm_ops snd_aw_capture_ops = { + .open = snd_aw_capture_open, + .close = snd_aw_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aw_pcm_hw_params, + .hw_free = snd_aw_pcm_hw_free, + .prepare = snd_aw_pcm_prepare_capture, + .trigger = snd_aw_pcm_trigger_capture, + .pointer = snd_aw_pcm_pointer_capture, +}; + +/** + * + * ALSA playback stream definitions and callbacks + * + */ +static struct snd_pcm_hardware snd_aw_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 | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE), + .rates = AW_PCM_RATE, + .rate_min = AW_FS_MIN, + .rate_max = AW_FS_MAX, + .channels_min = 2, + .channels_max = AW_MAX_PLAYBACK_CHANNELS_PER_STREAM, + .buffer_bytes_max = 16384, + .period_bytes_min = 64, /* HW restriction, see SAA7146A spec p.24 */ + .period_bytes_max = 8192, + .periods_min = 2, + .periods_max = 2, +}; + +/** + * ALSA playback stream open callback. + */ +static int snd_aw_playback_open(struct snd_pcm_substream *substream) +{ + return snd_aw_substream_open(substream, 0); +} + +/** + * ALSA playback stream close callback. + */ +static int snd_aw_playback_close(struct snd_pcm_substream *substream) +{ + return snd_aw_substream_close(substream, 0); +} + +/** + * Module scoped helper: converts alsa to Audiowerk playback sample format. + */ +static enum aw_formats alsa_2_aw_playback_format(snd_pcm_format_t fmt) +{ + switch (fmt) { + case SNDRV_PCM_FORMAT_S16_LE: + return fmt_s16_2le; + case SNDRV_PCM_FORMAT_S16_BE: + return fmt_s16_2be; + case SNDRV_PCM_FORMAT_S32_LE: + return fmt_s20_4le; + case SNDRV_PCM_FORMAT_S32_BE: + return fmt_s20_4be; + default: + LOG_ERROR("Unsupported playback format %d", fmt); + return fmt_s16_2le; + } +} + +/** + * ALSA playback stream prepare callback. + * Be careful that this callback will be called many times at each set up. + */ +static int snd_aw_pcm_prepare_playback(struct snd_pcm_substream *substream) +{ + return snd_aw_pcm_prepare_substream(substream, 0); +} + +/** + * ALSA playback stream trigger callback. [atomic]. + */ +static int snd_aw_pcm_trigger_playback(struct snd_pcm_substream *substream, + int cmd) +{ + return snd_aw_pcm_trigger_substream(substream, cmd, 0); +} + +/** + * ALSA playback stream hw-buffer pointer callback. [atomic]. + */ +static snd_pcm_uframes_t snd_aw_pcm_pointer_playback( + struct snd_pcm_substream *substream) +{ + return snd_aw_pcm_pointer_substream(substream, 0); +} + +/** + * ALSA playback stream callback operations. + */ +static struct snd_pcm_ops snd_aw_playback_ops = { + .open = snd_aw_playback_open, + .close = snd_aw_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aw_pcm_hw_params, + .hw_free = snd_aw_pcm_hw_free, + .prepare = snd_aw_pcm_prepare_playback, + .trigger = snd_aw_pcm_trigger_playback, + .pointer = snd_aw_pcm_pointer_playback, +}; + +static int snd_aw_substream_open(struct snd_pcm_substream *substream, int in) +{ + struct awalsa_t *awalsa = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int device = substream->pcm->device; + struct snd_pcm_substream **alsa_pcm_substreams = NULL; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + alsa_pcm_substreams = + (in ? awalsa->pcm_in_substreams : awalsa->pcm_out_substreams); + if (alsa_pcm_substreams[substream->number] != NULL) { + LOG_WARN("%s substream %d already open", + (in ? "Capture" : "Playback"), substream->number); + return -EAGAIN; + } + /* Remember substream for interrupt handler where we are given the chip + instance only. */ + alsa_pcm_substreams[substream->number] = substream; + runtime->hw = (in ? snd_aw_capture_hw : snd_aw_playback_hw); + return 0; +} + +static int snd_aw_substream_close(struct snd_pcm_substream *substream, int in) +{ + struct awalsa_t *awalsa = snd_pcm_substream_chip(substream); + int device = substream->pcm->device; + struct snd_pcm_substream **alsa_pcm_substreams = NULL; + struct audio_stream **saa7146_streams = NULL; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + alsa_pcm_substreams = + (in ? awalsa->pcm_in_substreams : awalsa->pcm_out_substreams); + saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams); + alsa_pcm_substreams[substream->number] = NULL; + if (saa7146_streams[substream->number] != NULL) { + saa7146_stream_unprepare(&awalsa->chipaudio, + saa7146_streams[substream->number]); + } + return 0; +} + +/** + * ALSA stream hw_params callback. + */ +static int snd_aw_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int err = 0; + int device = substream->pcm->device; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) { + LOG_ERROR("snd_pcm_lib_malloc_pages ERROR=%d", err); + return err; + } + return 0; +} + +/** + * ALSA stream hw_free callback. + */ +static int snd_aw_pcm_hw_free(struct snd_pcm_substream *substream) +{ + int err = 0; + int device = substream->pcm->device; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + err = snd_pcm_lib_free_pages(substream); + if (err < 0) { + LOG_ERROR("snd_pcm_lib_free_pages ERROR=%d", err); + return err; + } + return 0; +} + +static int snd_aw_pcm_prepare_substream(struct snd_pcm_substream *substream, + int in) +{ + struct awalsa_t *awalsa = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_stream **saa7146_streams = NULL; + struct audio_stream *stream = NULL; + int device = substream->pcm->device; + enum aw_formats fmt = fmt_s16_2le; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + fmt = (in ? alsa_2_aw_capture_format(runtime->format) : + alsa_2_aw_playback_format(runtime->format)); + saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams); + /* as this function may be called many times we must clean up first */ + if (saa7146_streams[substream->number] != NULL) { + saa7146_stream_unprepare(&awalsa->chipaudio, + saa7146_streams[substream->number]); + } + /* try to prepare a new stream with the given parameters */ + if (in) { + stream = saa7146_stream_prepare_capture(&awalsa->chipaudio, + substream->number, + runtime->dma_addr, /* stream buffer phys. base addr. */ + 2 * snd_pcm_lib_period_bytes(substream), /* buf size */ + runtime->channels, + ((fmt == fmt_s16_2le || fmt == fmt_s16_2be) ? 2 : 4), + ((fmt == fmt_s16_2le || fmt == fmt_s18_4le) ? le : be)); + } else { + stream = saa7146_stream_prepare_playback(&awalsa->chipaudio, + substream->number, + runtime->dma_addr, /* stream buffer phys. base addr */ + 2 * snd_pcm_lib_period_bytes(substream), /* buf size */ + runtime->channels, + ((fmt == fmt_s16_2le || fmt == fmt_s16_2be) ? 2 : 4), + ((fmt == fmt_s16_2le || fmt == fmt_s20_4le) ? le : be)); + } + if (stream == NULL) + return -EINVAL; + saa7146_streams[substream->number] = stream; + if (aw8_set_samplerate(&awalsa->chipaudio.chipi2s.chip, runtime->rate) + != 0) + return -EINVAL; + return 0; +} + +static int snd_aw_pcm_trigger_substream(struct snd_pcm_substream *substream, + int cmd, int in) +{ + struct awalsa_t *awalsa = snd_pcm_substream_chip(substream); + int device = substream->pcm->device; + struct audio_stream **saa7146_streams = NULL; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + saa7146_stream_start(&awalsa->chipaudio, + saa7146_streams[substream->number]); + break; + case SNDRV_PCM_TRIGGER_STOP: + saa7146_stream_stop(&awalsa->chipaudio, + saa7146_streams[substream->number]); + break; + default: + LOG_ERROR("Unknown trigger command=%d", cmd); + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_aw_pcm_pointer_substream( + struct snd_pcm_substream *substream, + int in) +{ + struct awalsa_t *awalsa = snd_pcm_substream_chip(substream); + struct audio_stream **saa7146_streams = NULL; + unsigned long hw_ptr = 0; + snd_pcm_uframes_t frames; + int device = substream->pcm->device; + + if (device != PCM_0) { + LOG_ERROR("Invalid device index=%d", device); + return -EINVAL; + } + saa7146_streams = (in ? awalsa->in_streams : awalsa->out_streams); + hw_ptr = saa7146_stream_get_hw_pointer(&awalsa->chipaudio, + saa7146_streams[substream->number]); + frames = bytes_to_frames(substream->runtime, + hw_ptr - (size_t)(substream->runtime->dma_addr)); + return frames; +} + +/** + * + * ALSA device initialization + * + */ + +/** + * ALSA Setup 1 PCM device: + * AW8 substreams: 1 capture /2 playback (1 analog/digital, 1 analog) + * AW2 substreams: 1 capture /2 playback (1 analog, 1 digital) + */ +static int __devinit snd_aw_new_pcm(struct awalsa_t *awalsa) +{ + struct snd_pcm *pcm; + int err; + int pages = 0; + + /* create the pcm device */ + err = snd_pcm_new(awalsa->card, "SAA7146A", /* device ID string */ + PCM_0, /* device index */ + 2, /* playback substreams AW2/8 */ + 1, /* capture substreams AW2/8 */ + &pcm); + if (err != 0) { + LOG_ERROR("snd_pcm_new ERROR=%d", err); + return err; + } + pcm->private_data = awalsa; + strcpy(pcm->name, "Multichannel Capture/Playback"); + awalsa->pcm_0 = pcm; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_aw_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_aw_capture_ops); + LOG_INFO("PCM device [Multichannel Capture/Playback]" + " successfully created"); + /* Pre-allocation 128k of dma buffer */ + pages = 1 << ((17 - PAGE_SHIFT > 0) ? 17 - PAGE_SHIFT : 0); + err = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(awalsa->pci), + pages * PAGE_SIZE, + pages * PAGE_SIZE); + if (err != 0) { + LOG_ERROR("snd_pcm_lib_preallocate_pages_for_all" + " ERROR=%d", err); + return err; + } + return 0; +} + +/** + * ALSA Interrupt Service Routine. + */ +static irqreturn_t snd_aw_interrupt(int irq, void *dev_id) +{ + struct awalsa_t *awalsa = dev_id; + uint32_t isr = 0; + + /* lock isr - block other interrupts */ + spin_lock(awalsa->isr_lock); + isr = saa7146_read(&awalsa->chipaudio.chipi2s.chip, ISR); + /* was it one of SAA7146A (remember we might share irq line ..) */ + if (!isr) { + spin_unlock(awalsa->isr_lock); + return IRQ_NONE; + } + /* clear all interrupts */ + saa7146_write(&awalsa->chipaudio.chipi2s.chip, ISR, isr); + if (isr & A1_in && awalsa->pcm_in_substreams[0] != NULL) + snd_pcm_period_elapsed(awalsa->pcm_in_substreams[0]); + if (isr & A1_out && awalsa->pcm_out_substreams[0] != NULL) + snd_pcm_period_elapsed(awalsa->pcm_out_substreams[0]); + if (isr & A2_out && awalsa->pcm_out_substreams[1] != NULL) + snd_pcm_period_elapsed(awalsa->pcm_out_substreams[1]); + spin_unlock(awalsa->isr_lock); + return IRQ_HANDLED; +} + +/** + * ALSA chip-specific destructor. + * (see "PCI Resource Managements") + */ +static int snd_aw_free(struct awalsa_t *awalsa) +{ + struct saa7146i2s *chipi2s = &awalsa->chipaudio.chipi2s; + + /* disable hardware here if any */ + aw_exit(chipi2s); + if (awalsa->irq >= 0) + free_irq(awalsa->irq, (void *)awalsa); + if (chipi2s->chip.iobase_virt) + iounmap(chipi2s->chip.iobase_virt); + pci_release_regions(awalsa->pci); + pci_disable_device(awalsa->pci); + kfree(awalsa); + return 0; +} + +/** + * ALSA component-destructor. + * (see "Management of Cards and Components") + */ +static int snd_aw_dev_free(struct snd_device *device) +{ + snd_aw_free(device->device_data); + return 0; +} + +/** + * ALSA chip-specific constructor. + * (see "Management of Cards and Components") + */ +static int __devinit snd_aw_create(struct snd_card *card, + struct pci_dev *pci, + struct awalsa_t **rchip) +{ + static struct snd_device_ops ops = { + .dev_free = snd_aw_dev_free, + }; + int err; + struct awalsa_t *awalsa; + *rchip = NULL; + + /* Initialize the PCI entry: This function switches the device 'on'. + In some cases, IRQ and I/O range is assigned. */ + err = pci_enable_device(pci); + if (err != 0) { + LOG_ERROR("pci_enable_device ERROR=%d", err); + return err; + } + pci_set_master(pci); + err = pci_set_dma_mask(pci, DMA_32BIT_MASK); + if (err != 0) { + LOG_ERROR("pci_set_dma_mask ERROR=%d", err); + goto snd_aw_create_exit0; + } + err = pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK); + if (err != 0) { + LOG_ERROR("pci_set_consistent_dma_mask ERROR=%d", err); + goto snd_aw_create_exit0; + } + awalsa = kzalloc(sizeof(*awalsa), GFP_KERNEL); + if (awalsa == NULL) { + LOG_ERROR("Could not allocate memory"); + err = -ENOMEM; + goto snd_aw_create_exit0; + } + awalsa->card = card; + awalsa->pci = pci; + awalsa->irq = -1; + spin_lock_init(&awalsa->isr_lock); + /* (1) PCI resource allocation */ + err = pci_request_regions(pci, "SAA7146A"); + if (err != 0) { + LOG_ERROR("pci_request_regions ERROR=%d", err); + goto snd_aw_create_exit1; + } + /* Get the device's registers physical base address. The device register + space is not available for access by the driver now; it has to be + mapped to a virtual memory space first. */ + awalsa->chipaudio.chipi2s.chip.iobase_phys = pci_resource_start(pci, 0); + awalsa->chipaudio.chipi2s.chip.iolen = pci_resource_len(pci, 0); + /* Map device registers to virtual memory. 'nocache' means that we don't + want the device's registers to be read-cached by the CPU (e.g. as a + result of compiler optimizations). This is important since the CPU + does not necessarily know when a register value has changed (unless + this was by a write action of the driver). */ + awalsa->chipaudio.chipi2s.chip.iobase_virt = + ioremap_nocache(awalsa->chipaudio.chipi2s.chip.iobase_phys, + awalsa->chipaudio.chipi2s.chip.iolen); + err = request_irq(pci->irq, snd_aw_interrupt, IRQF_DISABLED|IRQF_SHARED, + "SAA7146A", (void *)awalsa); + if (err != 0) { + LOG_ERROR("Cannot grab irq %d, ERROR=%d", pci->irq, err); + err = -EBUSY; + goto snd_aw_create_exit2; + } + awalsa->irq = pci->irq; + /* (2) initialization of the chip hardware */ + if (aw_init(&awalsa->chipaudio.chipi2s, input_type) != 0) + goto snd_aw_create_exit2; + /* (3) create alsa device */ + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, awalsa, &ops); + if (err < 0) { + LOG_ERROR("snd_device_new ERROR=%d", err); + goto snd_aw_create_exit2; + } + snd_card_set_dev(card, &pci->dev); + *rchip = awalsa; + return 0; +snd_aw_create_exit2: + snd_aw_free(awalsa); + return err; +snd_aw_create_exit1: + kfree(awalsa); +snd_aw_create_exit0: + pci_disable_device(pci); + return err; +} + +/** + * ALSA device constructor -- see "Constructor" sub-section in Takashi Iwai's + * "Writing an ALSA Driver" developer guide. + */ +static int __devinit snd_aw_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int err; + struct snd_card *card; + struct awalsa_t *awalsa; + + /* (1) */ + if (dev >= SNDRV_CARDS) { + LOG_ERROR("No device available ERROR=%d", -ENODEV); + return -ENODEV; + } + if (!enable[dev]) { + dev++; + LOG_ERROR("No device entry ERROR=%d", -ENOENT); + return -ENOENT; + } + /* (2) */ + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) { + LOG_ERROR("snd_card_new ERROR=%d", -ENOMEM); + return -ENOMEM; + } + /* (3) */ + err = snd_aw_create(card, pci, &awalsa); + if (err < 0) { + LOG_ERROR("snd_aw_create ERROR=%d", err); + goto snd_aw_probe_exit0; + } + /* (4) */ + strcpy(card->driver, "SAA7146A"); + strcpy(card->shortname, AW_CARD_SHORT_NAME); + sprintf(card->longname, + "%s at 0x%lx [%i] irq %i", + card->shortname, + awalsa->chipaudio.chipi2s.chip.iobase_phys, + awalsa->chipaudio.chipi2s.chip.iolen, + awalsa->irq); + LOG_INFO("card-info[%s]", card->longname); + /* (5) */ + err = snd_aw_new_pcm(awalsa); + if (err != 0) { + LOG_ERROR("snd_aw_new_pcm ERROR=%d", err); + goto snd_aw_probe_exit0; + } + /* (6) */ + err = snd_card_register(card); + if (err < 0) { + LOG_ERROR("snd_card_register ERROR=%d", err); + goto snd_aw_probe_exit0; + } + /* (7) */ + pci_set_drvdata(pci, card); + dev++; + return 0; +snd_aw_probe_exit0: + snd_card_free(card); + return err; +} + +/** + * ALSA device destructor -- see "Destructor" sub-section + */ +static void __devexit snd_aw_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +/** + * + * PCI specific stuff + * + */ + +/** + * Organisation of EEPROM of the SAA7146A: + * The data of the subsystem ID and the subsystem vendor ID is organized in + * the EEPROM in the following order: + * + * EE-Addr Value Configuration Space Addr. + * --------------------------------------------------------------------- + * 00 Subsys ID (high byte) 2C, bits: 31 - 24 + * 01 Subsys ID (low byte) 2C, bits: 23 - 16 + * 02 Subsys Vendor ID (high byte) 2C, bits: 15 - 8 + * 03 Subsys Vendor ID (low byte) 2C, bits: 7 - 0 + * 04 Max_Lat 3C, bits: 31 - 24 + * 05 Min_Gnt 3C, bits: 23 - 16 + * + * For AW8 Subsys ID and Subsys Vendor ID are 0xFF on my card. + * For AW2 Subsys ID and Subsys Vendor ID are 0x00 on my card. + */ + +static struct pci_device_id snd_aw_ids[] = { + { PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_aw_ids); + +/** + * pci_driver definition. + */ +static struct pci_driver driver = { + .name = AW_DRIVER_NAME, + .id_table = snd_aw_ids, + .probe = snd_aw_probe, + .remove = __devexit_p(snd_aw_remove), +}; + +/** + * initialization of the module. + */ +static int __init alsa_card_aw_init(void) +{ + int err; + + err = pci_register_driver(&driver); + if (err != 0) { + LOG_ERROR("pci_register_driver ERROR=%d", err); + return err; + } + return 0; +} + +/** + * clean up the module. + */ +static void __exit alsa_card_aw_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_aw_init) +module_exit(alsa_card_aw_exit) diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.c ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.c --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.c 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.c 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1,185 @@ +/* + * Emagic Audiowerk specific functions + * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch> + * Based on GPLed Emagic Audiowerk8 Windows driver provided by Martijn Sipkema. + * + * 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/types.h> +#include <linux/sched.h> +#include "log.h" +#include "saa7146.h" +#include "saa7146i2s.h" +#include "saa7146i2c.h" +#include "aw8.h" + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define SLEEP_DIGIN 100 /* wait [ms] after switching input type adc/daio */ +#define GPIO_0 GPIO_CTRL +#define MASK_GPIO_HI 0x50 +#define MASK_GPIO_LO 0x40 +#define SLEEP_PLL 500 /* wait [ms] after setting new fs */ +#define PLL_DEVICE_ADDR 0x62 /* I2C device address of AW8 PLL chip */ +#define PLL_FREF 1000 /* reference frequency for PLL */ + +/** + * TODO: Description + */ +enum aw_input_types {analog, digital}; + +/* forward declarations */ +static int aw8_device_init(struct saa7146i2s *chipi2s); +static void switch_input(struct saa7146reg *chip, enum aw_input_types type); + +/** + * see aw8.h + */ +int aw_init(struct saa7146i2s *chipi2s, int input_type) +{ + int err = 0; + + saa7146_write(&chipi2s->chip, IER, 0); /* disable all interrupts */ + saa7146_write(&chipi2s->chip, MC1, MRST_N<<16); /* reset SAA7146 */ + /* setup PCI arbitration ctrl (see SAA7146 spec. page 31 for details): + o 8DW burst for all FIFOs + o 8DW (empty DWs in FIFO) threshold for OUT-FIFOs + o 8DW (valid DWs in FIFO) threshold for IN-FIFOs + 0x0e0e0e0e => xxx0 1110 xxx0 1110 xxx0 1110 xxx0 1110 */ + saa7146_write(&chipi2s->chip, PCI_BT_A, 0x0e0e0e0e); + /* enable SAA7146 audio pins */ + saa7146_write(&chipi2s->chip, MC1, (EAP<<16 | EAP)); + /* setup audio clock source (from EMagic GPL driver) */ + saa7146_write(&chipi2s->chip, ACON2, ((0UL<<27) | /* A1_CLKSRC: BCLK1 */ + (1UL<<22) | /* A2_CLKSRC: BCLK1 */ + (0UL<<21) | /* INVERT_BCLK1 */ + (0UL<<20) | /* INVERT_BCLK2 */ + (1UL<<19) | /* BCLK1_OEN: input */ + (0UL<<18))); /* BCLK2_OEN: output */ + /* init ACON1: WS1-4 to output active low */ + chipi2s->acon1 = 0x0000cccc; + saa7146_write(&chipi2s->chip, ACON1, chipi2s->acon1); + if (input_type) + aw_switch_input_daio(&chipi2s->chip); + else + aw_switch_input_adc(&chipi2s->chip); + if (saa7146_i2s_init_audio_interface(chipi2s, a1, 4, 8) != 0 + || saa7146_i2s_init_audio_interface(chipi2s, a2, 4, 8) != 0) + return -1; + err = aw8_device_init(chipi2s); + /* enable interrupts */ + saa7146_write(&chipi2s->chip, IER, (A1_out | A2_out | A1_in)); + return err; +} + +/** + * see aw8.h + */ +void aw_exit(struct saa7146i2s *chipi2s) +{ + saa7146_write(&chipi2s->chip, IER, 0); /* disable all interrupts */ + saa7146_write(&chipi2s->chip, MC1, (MRST_N<<16)); /* reset SAA7146 */ +} + +/** + * see aw8.h + */ +void aw_switch_input_adc(struct saa7146reg *chip) +{ + switch_input(chip, analog); +} + +/** + * see aw8.h + */ +void aw_switch_input_daio(struct saa7146reg *chip) +{ + switch_input(chip, digital); +} + +/** + * see aw8.h + * Details regarding implementation see project wiki. + */ +int aw8_set_samplerate(struct saa7146reg *chip, unsigned int fs) +{ + uint32_t counter = 0; + uint8_t fs_prg_seq[4]; + + fs = fs > AW_FS_MAX ? AW_FS_MAX : fs; + fs = fs < AW_FS_MIN ? AW_FS_MIN : fs; + /* init TSA6060 counter */ + counter = (256 * fs * 4) / PLL_FREF; + fs_prg_seq[0] = counter << 1; + fs_prg_seq[1] = counter >> 7; + fs_prg_seq[2] = (counter >> 15 & 0x03) | 0x18; + fs_prg_seq[3] = 0; + if (saa7146_i2c_write(chip, bps_10k, PLL_DEVICE_ADDR, NULL, 0, + fs_prg_seq, 4) < 4) + return -1; + return 0; +} + +/** + * A1: A2: + * analog in: WS0/SD4 + * analog/digital out: WS1/SD0 analog out: WS3/SD1 + * analog out: WS2/SD2 analog out: WS4/SD3 + */ +static int aw8_device_init(struct saa7146i2s *chipi2s) +{ + if (saa7146_i2s_init_device(chipi2s, a1, in, ws0, sd4_i_a1) != 0 + || saa7146_i2s_init_device(chipi2s, a1, out, ws1, sd0_o_a1) != 0 + || saa7146_i2s_init_device(chipi2s, a1, out, ws2, sd2_io_ax) + != 0 + || saa7146_i2s_init_device(chipi2s, a2, out, ws3, sd1_io_ax) + != 0 + || saa7146_i2s_init_device(chipi2s, a2, out, ws4, sd3_io_ax) + != 0) + return -1; + return 0; +} + +/** + * TODO: Description + */ +static void switch_input(struct saa7146reg *chip, enum aw_input_types type) +{ + /* From EMagic driver... + // Switch SAA7146 to safe mode + setreg(chip, ACON1, 0L); + setreg(chip, ACON2, (12L<<27) | // A1 bitclock == ACLK/384 + (12L<<22) | // A2 bitclock == ACLK/384 + ( 1L<<19)); // BCLK1 is input + setreg(chip, MC1, 0x7fff0000); // Disable audio i/f + setreg(chip, A_TIME_SLOT1, (1L<<TSL_EOS)); + setreg(chip, A_TIME_SLOT2, (1L<<TSL_EOS)); + */ + if (type == analog) { + saa7146_write(chip, GPIO_0, MASK_GPIO_HI); + /* wait until relais is switched and PLL is locked */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(MAX(1, SLEEP_PLL*HZ/1000)); + } else { + saa7146_write(chip, GPIO_0, MASK_GPIO_LO); + /* wait until relais is switched and digital-in is synced. */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(MAX(1, SLEEP_DIGIN*HZ/1000)); + } + /* From EMagic driver... + SetReg(MC1, 0x7fff0200, true); // Enable audio i/f + */ +} + diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.h ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.h --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.h 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.h 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1,80 @@ +/* + * Emagic Audiowerk specific functions + * Copyright (c) 2006- by M. Nyffenegger <matthias.nyffenegger[AT]bluewin.ch> + * + * + * 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 + * + */ +#ifndef AUDIOWERK_H_ +#define AUDIOWERK_H_ + +#include <linux/types.h> +#include "saa7146.h" +#include "saa7146i2s.h" + +# define AW_MODULE_DESCRIPTION "Emagic Audiowerk 8 Driver" +# define AW_CARD_SHORT_NAME "Audiowerk 8" +# define AW_DRIVER_NAME "SAA7146A Audiowerk 8" +# define AW_PCM_RATE SNDRV_PCM_RATE_CONTINUOUS +# define AW_FS_MIN 25000 /* fs lower limit for DAC TDA1305 (ADC 18kHz) */ +# define AW_FS_MAX 48000 /* fs upper limit for DAC TDA1305 (ADC 50kHz) */ +# define AW_MAX_PLAYBACK_CHANNELS_PER_STREAM 4 + +/** + * Audiwerk sampling formats. + */ +enum aw_formats {fmt_s16_2le, fmt_s16_2be, fmt_s18_4le, fmt_s18_4be, + fmt_s20_4le, fmt_s20_4be}; + +/** + * TODO: description + */ +int aw_init(struct saa7146i2s *chipi2s, int input_type); + +/** + * TODO: description + */ +void aw_exit(struct saa7146i2s *chipi2s); + +/** + * Switches the Audiowerk input to analog. + * There are two sysclock generators, switched via a relais on GPIO-0: + * o TSA 6060 PLL for analog input + * o TDA 1315 digital input + * @param chip Used for hardware access over PCI bus. + */ +void aw_switch_input_adc(struct saa7146reg *chip); + +/** + * Switches the Audiowerk input to digital. + * There are two sysclock generators, switched via a relais on GPIO-0: + * o TSA 6060 PLL for analog input + * o TDA 1315 digital input + * @param chip Used for hardware access over PCI bus. + */ +void aw_switch_input_daio(struct saa7146reg *chip); + +/** + * Set sample rate on AW8. + * AW8 supports continuous fs shared by all substreams -> last call to + * aw8_set_samplerate() wins. + * @param chip Used for hardware access over PCI bus. + * @param fs sampling frequency (25-48kHz). + * @return 0 in case of success or -1 in case of failure. + */ +int aw8_set_samplerate(struct saa7146reg *chip, unsigned int fs); + +#endif /*AUDIOWERK_H_*/
At Thu, 23 Oct 2008 00:04:59 +0200, Matthias Nyffenegger wrote:
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/README.txt ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/README.txt --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/README.txt 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/README.txt 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1 @@ +See sourceforge.net project "Audiowerk8 ALSA driver" (aw8-alsa) Wiki for more information.
Hmm, this isn't particularly informative. Such a comment can be put in the corresponding card entry in Documentation/sound/alsa/ALSA-Configuration.txt.
Also, this directory is no right place to put the document. Put any document to Documentation/sound/alsa directory.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/Makefile ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/Makefile --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/Makefile 2008-10-22 23:34:05.000000000 +0200 @@ -0,0 +1,8 @@ +# Makefile for Audiowerk module. +# run command in source directory: +# make -C /usr/src/linux M=`pwd` modules modules_install
+EXTRA_CFLAGS := -DSAA7146_SUBSYS_LOG_TAG="AW8"
Don't use EXTRA_CFLAGS just for this purpose.
+obj-m := snd-aw8.o
The driver can be built in kernel, no?
+snd-aw8-objs := saa7146i2c.o saa7146i2s.o saa7146audio.o aw8.o aw8alsa.o
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8alsa.c ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8alsa.c --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8alsa.c 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8alsa.c 2008-10-22 23:34:05.000000000 +0200
(snip)
+#include <linux/version.h>
You don't need this.
+#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/sched.h>
This, too.
+#include <linux/pci.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "log.h" +#include "saa7146audio.h" +#include "aw8.h"
+/* From Takashi Iwai's "Writing an ALSA Driver" developer guide */
Hmm... I suggest you to remove this.
+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;
+/*
- Module info
- */
+MODULE_AUTHOR("Matthias Nyffenegger matthias.nyffenegger[AT]bluewin.ch"); +MODULE_DESCRIPTION(AW_MODULE_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0:0.2");
+/**
- Module parameter 'input_type': select capture input digital or analog
(default)
- */
+int input_type; +module_param(input_type, int, 0444); +MODULE_PARM_DESC(input_type, "0=analog (default), 1=digital");
+#define PCM_0 0
+/**
- The ALSA 'chip' data structure.
- */
+struct awalsa_t {
Usually *_t is for typedefed objects. No big problem, though.
+/**
- ALSA Interrupt Service Routine.
- */
+static irqreturn_t snd_aw_interrupt(int irq, void *dev_id) +{
- struct awalsa_t *awalsa = dev_id;
- uint32_t isr = 0;
- /* lock isr - block other interrupts */
The spinlock in the interrupt handler isn't for blocking other irqs In your case, apparently no lock is necessary.
- spin_lock(awalsa->isr_lock);
- isr = saa7146_read(&awalsa->chipaudio.chipi2s.chip, ISR);
- /* was it one of SAA7146A (remember we might share irq line ..) */
- if (!isr) {
spin_unlock(awalsa->isr_lock);
return IRQ_NONE;
- }
- /* clear all interrupts */
- saa7146_write(&awalsa->chipaudio.chipi2s.chip, ISR, isr);
- if (isr & A1_in && awalsa->pcm_in_substreams[0] != NULL)
snd_pcm_period_elapsed(awalsa->pcm_in_substreams[0]);
- if (isr & A1_out && awalsa->pcm_out_substreams[0] != NULL)
snd_pcm_period_elapsed(awalsa->pcm_out_substreams[0]);
- if (isr & A2_out && awalsa->pcm_out_substreams[1] != NULL)
snd_pcm_period_elapsed(awalsa->pcm_out_substreams[1]);
- spin_unlock(awalsa->isr_lock);
- return IRQ_HANDLED;
+}
+/**
- ALSA chip-specific destructor.
- (see "PCI Resource Managements")
- */
Better to fix comments...
+/**
- ALSA component-destructor.
- (see "Management of Cards and Components")
- */
Ditto.
+/**
- ALSA chip-specific constructor.
- (see "Management of Cards and Components")
- */
Ditto.
+static int __devinit snd_aw_create(struct snd_card *card,
struct pci_dev *pci,
struct awalsa_t **rchip)
+{
(snip)
- /* (1) PCI resource allocation */
The number if comments can be removed. In my document, it just indicates the procedure step.
+/**
- ALSA device constructor -- see "Constructor" sub-section in Takashi Iwai's
- "Writing an ALSA Driver" developer guide.
- */
Hmm...
+/**
- PCI specific stuff
- */
+/**
- Organisation of EEPROM of the SAA7146A:
- The data of the subsystem ID and the subsystem vendor ID is organized in
- the EEPROM in the following order:
- EE-Addr Value Configuration Space Addr.
- 00 Subsys ID (high byte) 2C, bits: 31 - 24
- 01 Subsys ID (low byte) 2C, bits: 23 - 16
- 02 Subsys Vendor ID (high byte) 2C, bits: 15 - 8
- 03 Subsys Vendor ID (low byte) 2C, bits: 7 - 0
- 04 Max_Lat 3C, bits: 31 - 24
- 05 Min_Gnt 3C, bits: 23 - 16
- For AW8 Subsys ID and Subsys Vendor ID are 0xFF on my card.
- For AW2 Subsys ID and Subsys Vendor ID are 0x00 on my card.
- */
+static struct pci_device_id snd_aw_ids[] = {
- { PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
We have a bad problem here. As mentioned, there are other drivers with the very same device and the very same PCI ID. Since your driver doesn't check the availability of the hardware in some way at probe, it loads even for a video device. The same problem is present in the existing snd-aw2 driver, and we need to find out the solution.
diff -uprN ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.c ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.c --- ../alsa-driver-1.0.17/alsa-kernel/pci/saa7146/aw8.c 1970-01-01 01:00:00.000000000 +0100 +++ ../alsa-driver-1.0.17.mod/alsa-kernel/pci/saa7146/aw8.c 2008-10-22 23:34:05.000000000 +0200 +/**
- TODO: Description
- */
+enum aw_input_types {analog, digital};
Better to use other words, prefix, whatever.
+/* forward declarations */ +static int aw8_device_init(struct saa7146i2s *chipi2s); +static void switch_input(struct saa7146reg *chip, enum aw_input_types type);
+/**
- see aw8.h
- */
+int aw_init(struct saa7146i2s *chipi2s, int input_type)
This function name should be more unique, too.
+static void switch_input(struct saa7146reg *chip, enum aw_input_types type)
(snip)
- if (type == analog) {
saa7146_write(chip, GPIO_0, MASK_GPIO_HI);
/* wait until relais is switched and PLL is locked */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MAX(1, SLEEP_PLL*HZ/1000));
Use msleep() instead.
- } else {
saa7146_write(chip, GPIO_0, MASK_GPIO_LO);
/* wait until relais is switched and digital-in is synced. */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MAX(1, SLEEP_DIGIN*HZ/1000));
Ditto.
thanks,
Takashi
participants (2)
-
Matthias Nyffenegger
-
Takashi Iwai