From 02bec490450836ebbd628e97ec03f10b57def8ce Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Tue, 24 Mar 2009 12:24:35 +0100 Subject: ALSA: lx6464es - driver for the digigram lx6464es interface prototype of a driver for the digigram lx6464es 64 channel ethersound interface. Signed-off-by: Tim Blechmann Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 10 + sound/pci/Makefile | 1 + sound/pci/lx6464es/Makefile | 2 + sound/pci/lx6464es/lx6464es.c | 1152 ++++++++++++++++++++++++++++++++ sound/pci/lx6464es/lx6464es.h | 114 ++++ sound/pci/lx6464es/lx_core.c | 1442 +++++++++++++++++++++++++++++++++++++++++ sound/pci/lx6464es/lx_core.h | 242 +++++++ sound/pci/lx6464es/lx_defs.h | 376 +++++++++++ 8 files changed, 3339 insertions(+) create mode 100644 sound/pci/lx6464es/Makefile create mode 100644 sound/pci/lx6464es/lx6464es.c create mode 100644 sound/pci/lx6464es/lx6464es.h create mode 100644 sound/pci/lx6464es/lx_core.c create mode 100644 sound/pci/lx6464es/lx_core.h create mode 100644 sound/pci/lx6464es/lx_defs.h (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93422e3a3f0c..e912b70b6f9c 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -622,6 +622,16 @@ config SND_KORG1212 To compile this driver as a module, choose M here: the module will be called snd-korg1212. +config SND_LX6464ES + tristate "Digigram LX6464ES" + select SND_PCM + help + Say Y here to include support for Digigram LX6464ES boards. + + To compile this driver as a module, choose M here: the module + will be called snd-lx6464es. + + config SND_MAESTRO3 tristate "ESS Allegro/Maestro3" select SND_AC97_CODEC diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 65b25d221cd2..7d83e084dcf4 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_SND) += \ ca0106/ \ cs46xx/ \ cs5535audio/ \ + lx6464es/ \ echoaudio/ \ emu10k1/ \ hda/ \ diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile new file mode 100644 index 000000000000..eb04a6c73d8b --- /dev/null +++ b/sound/pci/lx6464es/Makefile @@ -0,0 +1,2 @@ +snd-lx6464es-objs := lx6464es.o lx_core.o +obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c new file mode 100644 index 000000000000..7bc8b8caa992 --- /dev/null +++ b/sound/pci/lx6464es/lx6464es.c @@ -0,0 +1,1152 @@ +/* -*- linux-c -*- * + * + * ALSA driver for the digigram lx6464es interface + * + * Copyright (c) 2008, 2009 Tim Blechmann + * + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "lx6464es.h" + +MODULE_AUTHOR("Tim Blechmann"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("digigram lx6464es"); +MODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}"); + + +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 const char card_name[] = "LX6464ES"; + + +#define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056 + +static struct pci_device_id snd_lx6464es_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), + .subvendor = PCI_VENDOR_ID_DIGIGRAM, + .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM + }, /* LX6464ES */ + { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), + .subvendor = PCI_VENDOR_ID_DIGIGRAM, + .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM + }, /* LX6464ES-CAE */ + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids); + + + +/* PGO pour USERo dans le registre pci_0x06/loc_0xEC */ +#define CHIPSC_RESET_XILINX (1L<<16) + + +/* alsa callbacks */ +static struct snd_pcm_hardware lx_caps = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_3BE), + .rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_192000), + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 64, + .buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER, + .period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2), + .period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER), + .periods_min = 2, + .periods_max = MAX_STREAM_BUFFER, +}; + +static int lx_set_granularity(struct lx6464es *chip, u32 gran); + + +static int lx_hardware_open(struct lx6464es *chip, + struct snd_pcm_substream *substream) +{ + int err = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + int channels = runtime->channels; + int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + snd_pcm_uframes_t period_size = runtime->period_size; + + snd_printd(LXP "allocating pipe for %d channels\n", channels); + err = lx_pipe_allocate(chip, 0, is_capture, channels); + if (err < 0) { + snd_printk(KERN_ERR LXP "allocating pipe failed\n"); + return err; + } + + err = lx_set_granularity(chip, period_size); + if (err < 0) { + snd_printk(KERN_ERR LXP "setting granularity to %ld failed\n", + period_size); + return err; + } + + return 0; +} + +static int lx_hardware_start(struct lx6464es *chip, + struct snd_pcm_substream *substream) +{ + int err = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + snd_printd(LXP "setting stream format\n"); + err = lx_stream_set_format(chip, runtime, 0, is_capture); + if (err < 0) { + snd_printk(KERN_ERR LXP "setting stream format failed\n"); + return err; + } + + snd_printd(LXP "starting pipe\n"); + err = lx_pipe_start(chip, 0, is_capture); + if (err < 0) { + snd_printk(KERN_ERR LXP "starting pipe failed\n"); + return err; + } + + snd_printd(LXP "waiting for pipe to start\n"); + err = lx_pipe_wait_for_start(chip, 0, is_capture); + if (err < 0) { + snd_printk(KERN_ERR LXP "waiting for pipe failed\n"); + return err; + } + + return err; +} + + +static int lx_hardware_stop(struct lx6464es *chip, + struct snd_pcm_substream *substream) +{ + int err = 0; + int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + snd_printd(LXP "pausing pipe\n"); + err = lx_pipe_pause(chip, 0, is_capture); + if (err < 0) { + snd_printk(KERN_ERR LXP "pausing pipe failed\n"); + return err; + } + + snd_printd(LXP "waiting for pipe to become idle\n"); + err = lx_pipe_wait_for_idle(chip, 0, is_capture); + if (err < 0) { + snd_printk(KERN_ERR LXP "waiting for pipe failed\n"); + return err; + } + + snd_printd(LXP "stopping pipe\n"); + err = lx_pipe_stop(chip, 0, is_capture); + if (err < 0) { + snd_printk(LXP "stopping pipe failed\n"); + return err; + } + + return err; +} + + +static int lx_hardware_close(struct lx6464es *chip, + struct snd_pcm_substream *substream) +{ + int err = 0; + int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + snd_printd(LXP "releasing pipe\n"); + err = lx_pipe_release(chip, 0, is_capture); + if (err < 0) { + snd_printk(LXP "releasing pipe failed\n"); + return err; + } + + return err; +} + + +static int lx_pcm_open(struct snd_pcm_substream *substream) +{ + struct lx6464es *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err = 0; + int board_rate; + + snd_printdd("->lx_pcm_open\n"); + mutex_lock(&chip->setup_mutex); + + /* copy the struct snd_pcm_hardware struct */ + runtime->hw = lx_caps; + +#if 0 + /* buffer-size should better be multiple of period-size */ + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) { + snd_printk(KERN_WARNING LXP "could not constrain periods\n"); + goto exit; + } +#endif + + /* the clock rate cannot be changed */ + board_rate = chip->board_sample_rate; + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, + board_rate, board_rate); + + if (err < 0) { + snd_printk(KERN_WARNING LXP "could not constrain periods\n"); + goto exit; + } + + /* constrain period size */ + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + MICROBLAZE_IBL_MIN, + MICROBLAZE_IBL_MAX); + if (err < 0) { + snd_printk(KERN_WARNING LXP + "could not constrain period size\n"); + goto exit; + } + + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); + + snd_pcm_set_sync(substream); + err = 0; + +exit: + runtime->private_data = chip; + + mutex_unlock(&chip->setup_mutex); + snd_printdd("<-lx_pcm_open, %d\n", err); + return err; +} + +static int lx_pcm_close(struct snd_pcm_substream *substream) +{ + int err = 0; + snd_printdd("->lx_pcm_close\n"); + return err; +} + +static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream + *substream) +{ + struct lx6464es *chip = snd_pcm_substream_chip(substream); + snd_pcm_uframes_t pos; + unsigned long flags; + int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + struct lx_stream *lx_stream = is_capture ? &chip->capture_stream : + &chip->playback_stream; + + snd_printdd("->lx_pcm_stream_pointer\n"); + + spin_lock_irqsave(&chip->lock, flags); + pos = lx_stream->frame_pos * substream->runtime->period_size; + spin_unlock_irqrestore(&chip->lock, flags); + + snd_printdd(LXP "stream_pointer at %ld\n", pos); + return pos; +} + +static int lx_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct lx6464es *chip = snd_pcm_substream_chip(substream); + int err = 0; + const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + snd_printdd("->lx_pcm_prepare\n"); + + mutex_lock(&chip->setup_mutex); + + if (chip->hardware_running[is_capture]) { + err = lx_hardware_stop(chip, substream); + if (err < 0) { + snd_printk(KERN_ERR LXP "failed to stop hardware. " + "Error code %d\n", err); + goto exit; + } + + err = lx_hardware_close(chip, substream); + if (err < 0) { + snd_printk(KERN_ERR LXP "failed to close hardware. " + "Error code %d\n", err); + goto exit; + } + } + + snd_printd(LXP "opening hardware\n"); + err = lx_hardware_open(chip, substream); + if (err < 0) { + snd_printk(KERN_ERR LXP "failed to open hardware. " + "Error code %d\n", err); + goto exit; + } + + err = lx_hardware_start(chip, substream); + if (err < 0) { + snd_printk(KERN_ERR LXP "failed to start hardware. " + "Error code %d\n", err); + goto exit; + } + + chip->hardware_running[is_capture] = 1; + + if (chip->board_sample_rate != substream->runtime->rate) { + if (!err) + chip->board_sample_rate = substream->runtime->rate; + } + +exit: + mutex_unlock(&chip->setup_mutex); + return err; +} + +static int lx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, int is_capture) +{ + struct lx6464es *chip = snd_pcm_substream_chip(substream); + int err = 0; + + snd_printdd("->lx_pcm_hw_params\n"); + + mutex_lock(&chip->setup_mutex); + + /* set dma buffer */ + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + + if (is_capture) + chip->capture_stream.stream = substream; + else + chip->playback_stream.stream = substream; + + mutex_unlock(&chip->setup_mutex); + return err; +} + +static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return lx_pcm_hw_params(substream, hw_params, 0); +} + +static int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return lx_pcm_hw_params(substream, hw_params, 1); +} + +static int lx_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct lx6464es *chip = snd_pcm_substream_chip(substream); + int err = 0; + int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + + snd_printdd("->lx_pcm_hw_free\n"); + mutex_lock(&chip->setup_mutex); + + if (chip->hardware_running[is_capture]) { + err = lx_hardware_stop(chip, substream); + if (err < 0) { + snd_printk(KERN_ERR LXP "failed to stop hardware. " + "Error code %d\n", err); + goto exit; + } + + err = lx_hardware_close(chip, substream); + if (err < 0) { + snd_printk(KERN_ERR LXP "failed to close hardware. " + "Error code %d\n", err); + goto exit; + } + + chip->hardware_running[is_capture] = 0; + } + + err = snd_pcm_lib_free_pages(substream); + + if (is_capture) + chip->capture_stream.stream = 0; + else + chip->playback_stream.stream = 0; + +exit: + mutex_unlock(&chip->setup_mutex); + return err; +} + +static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) +{ + struct snd_pcm_substream *substream = lx_stream->stream; + const int is_capture = lx_stream->is_capture; + + int err; + + const u32 channels = substream->runtime->channels; + const u32 bytes_per_frame = channels * 3; + const u32 period_size = substream->runtime->period_size; + const u32 periods = substream->runtime->periods; + const u32 period_bytes = period_size * bytes_per_frame; + + dma_addr_t buf = substream->dma_buffer.addr; + int i; + + u32 needed, freed; + u32 size_array[5]; + + for (i = 0; i != periods; ++i) { + u32 buffer_index = 0; + + err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, + size_array); + snd_printdd(LXP "starting: needed %d, freed %d\n", + needed, freed); + + err = lx_buffer_give(chip, 0, is_capture, period_bytes, + lower_32_bits(buf), upper_32_bits(buf), + &buffer_index); + + snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n", + buffer_index, (void *)buf, period_bytes); + buf += period_bytes; + } + + err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array); + snd_printdd(LXP "starting: needed %d, freed %d\n", needed, freed); + + snd_printd(LXP "starting: starting stream\n"); + err = lx_stream_start(chip, 0, is_capture); + if (err < 0) + snd_printk(KERN_ERR LXP "couldn't start stream\n"); + else + lx_stream->status = LX_STREAM_STATUS_RUNNING; + + lx_stream->frame_pos = 0; +} + +static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream) +{ + const int is_capture = lx_stream->is_capture; + int err; + + snd_printd(LXP "stopping: stopping stream\n"); + err = lx_stream_stop(chip, 0, is_capture); + if (err < 0) + snd_printk(KERN_ERR LXP "couldn't stop stream\n"); + else + lx_stream->status = LX_STREAM_STATUS_FREE; + +} + +static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip, + struct lx_stream *lx_stream) +{ + switch (lx_stream->status) { + case LX_STREAM_STATUS_SCHEDULE_RUN: + lx_trigger_start(chip, lx_stream); + break; + + case LX_STREAM_STATUS_SCHEDULE_STOP: + lx_trigger_stop(chip, lx_stream); + break; + + default: + break; + } +} + +static void lx_trigger_tasklet(unsigned long data) +{ + struct lx6464es *chip = (struct lx6464es *)data; + unsigned long flags; + + snd_printdd("->lx_trigger_tasklet\n"); + + spin_lock_irqsave(&chip->lock, flags); + lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream); + lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int lx_pcm_trigger_dispatch(struct lx6464es *chip, + struct lx_stream *lx_stream, int cmd) +{ + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN; + break; + + case SNDRV_PCM_TRIGGER_STOP: + lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP; + break; + + default: + err = -EINVAL; + goto exit; + } + tasklet_schedule(&chip->trigger_tasklet); + +exit: + return err; +} + + +static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct lx6464es *chip = snd_pcm_substream_chip(substream); + const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + struct lx_stream *stream = is_capture ? &chip->capture_stream : + &chip->playback_stream; + + snd_printdd("->lx_pcm_trigger\n"); + + return lx_pcm_trigger_dispatch(chip, stream, cmd); +} + +static int snd_lx6464es_free(struct lx6464es *chip) +{ + snd_printdd("->snd_lx6464es_free\n"); + + lx_irq_disable(chip); + + if (chip->irq >= 0) + free_irq(chip->irq, chip); + + iounmap(chip->port_dsp_bar); + ioport_unmap(chip->port_plx_remapped); + + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + + kfree(chip); + + return 0; +} + +static int snd_lx6464es_dev_free(struct snd_device *device) +{ + return snd_lx6464es_free(device->device_data); +} + +/* reset the dsp during initialization */ +static int __devinit lx_init_xilinx_reset(struct lx6464es *chip) +{ + int i; + u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC); + + snd_printdd("->lx_init_xilinx_reset\n"); + + /* activate reset of xilinx */ + plx_reg &= ~CHIPSC_RESET_XILINX; + + lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg); + msleep(1); + + lx_plx_reg_write(chip, ePLX_MBOX3, 0); + msleep(1); + + plx_reg |= CHIPSC_RESET_XILINX; + lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg); + + /* deactivate reset of xilinx */ + for (i = 0; i != 100; ++i) { + u32 reg_mbox3; + msleep(10); + reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3); + if (reg_mbox3) { + snd_printd(LXP "xilinx reset done\n"); + snd_printdd(LXP "xilinx took %d loops\n", i); + break; + } + } + + /* todo: add some error handling? */ + + /* clear mr */ + lx_dsp_reg_write(chip, eReg_CSM, 0); + + /* le xilinx ES peut ne pas etre encore pret, on attend. */ + msleep(600); + + return 0; +} + +static int __devinit lx_init_xilinx_test(struct lx6464es *chip) +{ + u32 reg; + + snd_printdd("->lx_init_xilinx_test\n"); + + /* TEST if we have access to Xilinx/MicroBlaze */ + lx_dsp_reg_write(chip, eReg_CSM, 0); + + reg = lx_dsp_reg_read(chip, eReg_CSM); + + if (reg) { + snd_printk(KERN_ERR LXP "Problem: Reg_CSM %x.\n", reg); + + /* PCI9056_SPACE0_REMAP */ + lx_plx_reg_write(chip, ePLX_PCICR, 1); + + reg = lx_dsp_reg_read(chip, eReg_CSM); + if (reg) { + snd_printk(KERN_ERR LXP "Error: Reg_CSM %x.\n", reg); + return -EAGAIN; /* seems to be appropriate */ + } + } + + snd_printd(LXP "Xilinx/MicroBlaze access test successful\n"); + + return 0; +} + +/* initialize ethersound */ +static int __devinit lx_init_ethersound_config(struct lx6464es *chip) +{ + int i; + u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES); + + u32 default_conf_es = (64 << IOCR_OUTPUTS_OFFSET) | + (64 << IOCR_INPUTS_OFFSET) | + (FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET); + + u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) + | (default_conf_es & CONFES_WRITE_PART_MASK); + + snd_printdd("->lx_init_ethersound\n"); + + chip->freq_ratio = FREQ_RATIO_SINGLE_MODE; + + /* + * write it to the card ! + * this actually kicks the ES xilinx, the first time since poweron. + * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers + * is not ready before this is done, and the bit 2 in Reg_CSES is set. + * */ + lx_dsp_reg_write(chip, eReg_CONFES, conf_es); + + for (i = 0; i != 1000; ++i) { + if (lx_dsp_reg_read(chip, eReg_CSES) & 4) { + snd_printd(LXP "ethersound initialized after %dms\n", + i); + goto ethersound_initialized; + } + msleep(1); + } + snd_printk(KERN_WARNING LXP + "ethersound could not be initialized after %dms\n", i); + return -ETIMEDOUT; + + ethersound_initialized: + snd_printd(LXP "ethersound initialized\n"); + return 0; +} + +static int __devinit lx_init_get_version_features(struct lx6464es *chip) +{ + u32 dsp_version; + + int err; + + snd_printdd("->lx_init_get_version_features\n"); + + err = lx_dsp_get_version(chip, &dsp_version); + + if (err == 0) { + u32 freq; + + snd_printk(LXP "DSP version: V%02d.%02d #%d\n", + (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff, + dsp_version & 0xff); + + /* later: what firmware version do we expect? */ + + /* retrieve Play/Rec features */ + /* done here because we may have to handle alternate + * DSP files. */ + /* later */ + + /* init the EtherSound sample rate */ + err = lx_dsp_get_clock_frequency(chip, &freq); + if (err == 0) + chip->board_sample_rate = freq; + snd_printd(LXP "actual clock frequency %d\n", freq); + } else { + snd_printk(KERN_ERR LXP "DSP corrupted \n"); + err = -EAGAIN; + } + + return err; +} + +static int lx_set_granularity(struct lx6464es *chip, u32 gran) +{ + int err = 0; + u32 snapped_gran = MICROBLAZE_IBL_MIN; + + snd_printdd("->lx_set_granularity\n"); + + /* blocksize is a power of 2 */ + while ((snapped_gran < gran) && + (snapped_gran < MICROBLAZE_IBL_MAX)) { + snapped_gran *= 2; + } + + if (snapped_gran == chip->pcm_granularity) + return 0; + + err = lx_dsp_set_granularity(chip, snapped_gran); + if (err < 0) { + snd_printk(KERN_WARNING LXP "could not set granularity\n"); + err = -EAGAIN; + } + + if (snapped_gran != gran) + snd_printk(LXP "snapped blocksize to %d\n", snapped_gran); + + snd_printd(LXP "set blocksize on board %d\n", snapped_gran); + chip->pcm_granularity = snapped_gran; + + return err; +} + +/* initialize and test the xilinx dsp chip */ +static int __devinit lx_init_dsp(struct lx6464es *chip) +{ + int err; + u8 mac_address[6]; + int i; + + snd_printdd("->lx_init_dsp\n"); + + snd_printd(LXP "initialize board\n"); + err = lx_init_xilinx_reset(chip); + if (err) + return err; + + snd_printd(LXP "testing board\n"); + err = lx_init_xilinx_test(chip); + if (err) + return err; + + snd_printd(LXP "initialize ethersound configuration\n"); + err = lx_init_ethersound_config(chip); + if (err) + return err; + + lx_irq_enable(chip); + + /** \todo the mac address should be ready by not, but it isn't, + * so we wait for it */ + for (i = 0; i != 1000; ++i) { + err = lx_dsp_get_mac(chip, mac_address); + if (err) + return err; + if (mac_address[0] || mac_address[1] || mac_address[2] || + mac_address[3] || mac_address[4] || mac_address[5]) + goto mac_ready; + msleep(1); + } + return -ETIMEDOUT; + +mac_ready: + snd_printd(LXP "mac address ready read after: %dms\n", i); + snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n", + mac_address[0], mac_address[1], mac_address[2], + mac_address[3], mac_address[4], mac_address[5]); + + err = lx_init_get_version_features(chip); + if (err) + return err; + + lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT); + + chip->playback_mute = 0; + + return err; +} + +static struct snd_pcm_ops lx_ops_playback = { + .open = lx_pcm_open, + .close = lx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = lx_pcm_prepare, + .hw_params = lx_pcm_hw_params_playback, + .hw_free = lx_pcm_hw_free, + .trigger = lx_pcm_trigger, + .pointer = lx_pcm_stream_pointer, +}; + +static struct snd_pcm_ops lx_ops_capture = { + .open = lx_pcm_open, + .close = lx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = lx_pcm_prepare, + .hw_params = lx_pcm_hw_params_capture, + .hw_free = lx_pcm_hw_free, + .trigger = lx_pcm_trigger, + .pointer = lx_pcm_stream_pointer, +}; + +static int __devinit lx_pcm_create(struct lx6464es *chip) +{ + int err; + struct snd_pcm *pcm; + + u32 size = 64 * /* channels */ + 3 * /* 24 bit samples */ + MAX_STREAM_BUFFER * /* periods */ + MICROBLAZE_IBL_MAX * /* frames per period */ + 2; /* duplex */ + + size = PAGE_ALIGN(size); + + /* hardcoded device name & channel count */ + err = snd_pcm_new(chip->card, (char *)card_name, 0, + 1, 1, &pcm); + + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture); + + pcm->info_flags = 0; + strcpy(pcm->name, card_name); + + err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + size, size); + if (err < 0) + return err; + + chip->pcm = pcm; + chip->capture_stream.is_capture = 1; + + return 0; +} + +static int lx_control_playback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int lx_control_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct lx6464es *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->playback_mute; + return 0; +} + +static int lx_control_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct lx6464es *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int current_value = chip->playback_mute; + + if (current_value != ucontrol->value.integer.value[0]) { + lx_level_unmute(chip, 0, !current_value); + chip->playback_mute = !current_value; + changed = 1; + } + return changed; +} + +static struct snd_kcontrol_new lx_control_playback_switch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 0, + .info = lx_control_playback_info, + .get = lx_control_playback_get, + .put = lx_control_playback_put +}; + + + +static void lx_proc_levels_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + u32 levels[64]; + int err; + int i, j; + struct lx6464es *chip = entry->private_data; + + snd_iprintf(buffer, "capture levels:\n"); + err = lx_level_peaks(chip, 1, 64, levels); + if (err < 0) + return; + + for (i = 0; i != 8; ++i) { + for (j = 0; j != 8; ++j) + snd_iprintf(buffer, "%08x ", levels[i*8+j]); + snd_iprintf(buffer, "\n"); + } + + snd_iprintf(buffer, "\nplayback levels:\n"); + + err = lx_level_peaks(chip, 0, 64, levels); + if (err < 0) + return; + + for (i = 0; i != 8; ++i) { + for (j = 0; j != 8; ++j) + snd_iprintf(buffer, "%08x ", levels[i*8+j]); + snd_iprintf(buffer, "\n"); + } + + snd_iprintf(buffer, "\n"); +} + +static int __devinit lx_proc_create(struct snd_card *card, struct lx6464es *chip) +{ + struct snd_info_entry *entry; + int err = snd_card_proc_new(card, "levels", &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, chip, lx_proc_levels_read); + return 0; +} + + +static int __devinit snd_lx6464es_create(struct snd_card *card, + struct pci_dev *pci, + struct lx6464es **rchip) +{ + struct lx6464es *chip; + int err; + + static struct snd_device_ops ops = { + .dev_free = snd_lx6464es_dev_free, + }; + + snd_printdd("->snd_lx6464es_create\n"); + + *rchip = NULL; + + /* enable PCI device */ + err = pci_enable_device(pci); + if (err < 0) + return err; + + pci_set_master(pci); + + /* check if we can restrict PCI DMA transfers to 32 bits */ + err = pci_set_dma_mask(pci, DMA_32BIT_MASK); + if (err < 0) { + snd_printk(KERN_ERR "architecture does not support " + "32bit PCI busmaster DMA\n"); + pci_disable_device(pci); + return -ENXIO; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + err = -ENOMEM; + goto alloc_failed; + } + + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + /* initialize synchronization structs */ + spin_lock_init(&chip->lock); + spin_lock_init(&chip->msg_lock); + mutex_init(&chip->setup_mutex); + tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet, + (unsigned long)chip); + tasklet_init(&chip->tasklet_capture, lx_tasklet_capture, + (unsigned long)chip); + tasklet_init(&chip->tasklet_playback, lx_tasklet_playback, + (unsigned long)chip); + + /* request resources */ + err = pci_request_regions(pci, card_name); + if (err < 0) + goto request_regions_failed; + + /* plx port */ + chip->port_plx = pci_resource_start(pci, 1); + chip->port_plx_remapped = ioport_map(chip->port_plx, + pci_resource_len(pci, 1)); + + /* dsp port */ + chip->port_dsp_bar = pci_ioremap_bar(pci, 2); + + err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED, + card_name, chip); + if (err) { + snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq); + goto request_irq_failed; + } + chip->irq = pci->irq; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) + goto device_new_failed; + + err = lx_init_dsp(chip); + if (err < 0) { + snd_printk(KERN_ERR LXP "error during DSP initialization\n"); + return err; + } + + err = lx_pcm_create(chip); + if (err < 0) + return err; + + err = lx_proc_create(card, chip); + if (err < 0) + return err; + + err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch, + chip)); + if (err < 0) + return err; + + snd_card_set_dev(card, &pci->dev); + + *rchip = chip; + return 0; + +device_new_failed: + free_irq(pci->irq, chip); + +request_irq_failed: + pci_release_regions(pci); + +request_regions_failed: + kfree(chip); + +alloc_failed: + pci_disable_device(pci); + + return err; +} + +static int __devinit snd_lx6464es_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct lx6464es *chip; + int err; + + snd_printdd("->snd_lx6464es_probe\n"); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + err = snd_lx6464es_create(card, pci, &chip); + if (err < 0) { + snd_printk(KERN_ERR LXP "error during snd_lx6464es_create\n"); + goto out_free; + } + + strcpy(card->driver, "lx6464es"); + strcpy(card->shortname, "Digigram LX6464ES"); + sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i", + card->shortname, chip->port_plx, + chip->port_dsp_bar, chip->irq); + + err = snd_card_register(card); + if (err < 0) + goto out_free; + + snd_printdd(LXP "initialization successful\n"); + pci_set_drvdata(pci, card); + dev++; + return 0; + +out_free: + snd_card_free(card); + return err; + +} + +static void __devexit snd_lx6464es_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + .name = "Digigram LX6464ES", + .id_table = snd_lx6464es_ids, + .probe = snd_lx6464es_probe, + .remove = __devexit_p(snd_lx6464es_remove), +}; + + +/* module initialization */ +static int __init mod_init(void) +{ + return pci_register_driver(&driver); +} + +static void __exit mod_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(mod_init); +module_exit(mod_exit); diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h new file mode 100644 index 000000000000..012c010c8c89 --- /dev/null +++ b/sound/pci/lx6464es/lx6464es.h @@ -0,0 +1,114 @@ +/* -*- linux-c -*- * + * + * ALSA driver for the digigram lx6464es interface + * + * Copyright (c) 2009 Tim Blechmann + * + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef LX6464ES_H +#define LX6464ES_H + +#include +#include + +#include +#include + +#include "lx_core.h" + +#define LXP "LX6464ES: " + +enum { + ES_cmd_free = 0, /* no command executing */ + ES_cmd_processing = 1, /* execution of a read/write command */ + ES_read_pending = 2, /* a asynchron read command is pending */ + ES_read_finishing = 3, /* a read command has finished waiting (set by + * Interrupt or CancelIrp) */ +}; + +enum lx_stream_status { + LX_STREAM_STATUS_FREE, +/* LX_STREAM_STATUS_OPEN, */ + LX_STREAM_STATUS_SCHEDULE_RUN, +/* LX_STREAM_STATUS_STARTED, */ + LX_STREAM_STATUS_RUNNING, + LX_STREAM_STATUS_SCHEDULE_STOP, +/* LX_STREAM_STATUS_STOPPED, */ +/* LX_STREAM_STATUS_PAUSED */ +}; + + +struct lx_stream { + struct snd_pcm_substream *stream; + snd_pcm_uframes_t frame_pos; + enum lx_stream_status status; /* free, open, running, draining + * pause */ + int is_capture:1; +}; + + +struct lx6464es { + struct snd_card *card; + struct pci_dev *pci; + int irq; + + spinlock_t lock; /* interrupt spinlock */ + struct mutex setup_mutex; /* mutex used in hw_params, open + * and close */ + + struct tasklet_struct trigger_tasklet; /* trigger tasklet */ + struct tasklet_struct tasklet_capture; + struct tasklet_struct tasklet_playback; + + /* ports */ + unsigned long port_plx; /* io port (size=256) */ + void __iomem *port_plx_remapped; /* remapped plx port */ + void __iomem *port_dsp_bar; /* memory port (32-bit, + * non-prefetchable, + * size=8K) */ + + /* messaging */ + spinlock_t msg_lock; /* message spinlock */ + atomic_t send_message_locked; + struct lx_rmh rmh; + + /* configuration */ + uint freq_ratio : 2; + uint playback_mute : 1; + uint hardware_running[2]; + u32 board_sample_rate; /* sample rate read from + * board */ + u32 sample_rate; /* our sample rate */ + u16 pcm_granularity; /* board blocksize */ + + /* dma */ + struct snd_dma_buffer capture_dma_buf; + struct snd_dma_buffer playback_dma_buf; + + /* pcm */ + struct snd_pcm *pcm; + + /* streams */ + struct lx_stream capture_stream; + struct lx_stream playback_stream; +}; + + +#endif /* LX6464ES_H */ diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c new file mode 100644 index 000000000000..a9f8f882b107 --- /dev/null +++ b/sound/pci/lx6464es/lx_core.c @@ -0,0 +1,1442 @@ +/* -*- linux-c -*- * + * + * ALSA driver for the digigram lx6464es interface + * low-level interface + * + * Copyright (c) 2009 Tim Blechmann + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* #define RMH_DEBUG 1 */ + +#include +#include +#include + +#include "lx6464es.h" +#include "lx_core.h" + +/* low-level register access */ + +static const unsigned long dsp_port_offsets[] = { + 0, + 0x400, + 0x401, + 0x402, + 0x403, + 0x404, + 0x405, + 0x406, + 0x407, + 0x408, + 0x409, + 0x40a, + 0x40b, + 0x40c, + + 0x410, + 0x411, + 0x412, + 0x413, + 0x414, + 0x415, + 0x416, + + 0x420, + 0x430, + 0x431, + 0x432, + 0x433, + 0x434, + 0x440 +}; + +static void __iomem *lx_dsp_register(struct lx6464es *chip, int port) +{ + void __iomem *base_address = chip->port_dsp_bar; + return base_address + dsp_port_offsets[port]*4; +} + +unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port) +{ + void __iomem *address = lx_dsp_register(chip, port); + return ioread32(address); +} + +void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len) +{ + void __iomem *address = lx_dsp_register(chip, port); + memcpy_fromio(data, address, len*sizeof(u32)); +} + + +void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data) +{ + void __iomem *address = lx_dsp_register(chip, port); + iowrite32(data, address); +} + +void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data, + u32 len) +{ + void __iomem *address = lx_dsp_register(chip, port); + memcpy_toio(address, data, len*sizeof(u32)); +} + + +static const unsigned long plx_port_offsets[] = { + 0x04, + 0x40, + 0x44, + 0x48, + 0x4c, + 0x50, + 0x54, + 0x58, + 0x5c, + 0x64, + 0x68, + 0x6C +}; + +static void __iomem *lx_plx_register(struct lx6464es *chip, int port) +{ + void __iomem *base_address = chip->port_plx_remapped; + return base_address + plx_port_offsets[port]; +} + +unsigned long lx_plx_reg_read(struct lx6464es *chip, int port) +{ + void __iomem *address = lx_plx_register(chip, port); + return ioread32(address); +} + +void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data) +{ + void __iomem *address = lx_plx_register(chip, port); + iowrite32(data, address); +} + +u32 lx_plx_mbox_read(struct lx6464es *chip, int mbox_nr) +{ + int index; + + switch (mbox_nr) { + case 1: + index = ePLX_MBOX1; break; + case 2: + index = ePLX_MBOX2; break; + case 3: + index = ePLX_MBOX3; break; + case 4: + index = ePLX_MBOX4; break; + case 5: + index = ePLX_MBOX5; break; + case 6: + index = ePLX_MBOX6; break; + case 7: + index = ePLX_MBOX7; break; + case 0: /* reserved for HF flags */ + snd_BUG(); + default: + return 0xdeadbeef; + } + + return lx_plx_reg_read(chip, index); +} + +int lx_plx_mbox_write(struct lx6464es *chip, int mbox_nr, u32 value) +{ + int index = -1; + + switch (mbox_nr) { + case 1: + index = ePLX_MBOX1; break; + case 3: + index = ePLX_MBOX3; break; + case 4: + index = ePLX_MBOX4; break; + case 5: + index = ePLX_MBOX5; break; + case 6: + index = ePLX_MBOX6; break; + case 7: + index = ePLX_MBOX7; break; + case 0: /* reserved for HF flags */ + case 2: /* reserved for Pipe States + * the DSP keeps an image of it */ + snd_BUG(); + return -EBADRQC; + } + + lx_plx_reg_write(chip, index, value); + return 0; +} + + +/* rmh */ + +#ifdef CONFIG_SND_DEBUG +#define CMD_NAME(a) a +#else +#define CMD_NAME(a) NULL +#endif + +#define Reg_CSM_MR 0x00000002 +#define Reg_CSM_MC 0x00000001 + +struct dsp_cmd_info { + u32 dcCodeOp; /* Op Code of the command (usually 1st 24-bits + * word).*/ + u16 dcCmdLength; /* Command length in words of 24 bits.*/ + u16 dcStatusType; /* Status type: 0 for fixed length, 1 for + * random. */ + u16 dcStatusLength; /* Status length (if fixed).*/ + char *dcOpName; +}; + +/* + Initialization and control data for the Microblaze interface + - OpCode: + the opcode field of the command set at the proper offset + - CmdLength + the number of command words + - StatusType + offset in the status registers: 0 means that the return value may be + different from 0, and must be read + - StatusLength + the number of status words (in addition to the return value) +*/ + +static struct dsp_cmd_info dsp_commands[] = +{ + { (CMD_00_INFO_DEBUG << OPCODE_OFFSET) , 1 /*custom*/ + , 1 , 0 /**/ , CMD_NAME("INFO_DEBUG") }, + { (CMD_01_GET_SYS_CFG << OPCODE_OFFSET) , 1 /**/ + , 1 , 2 /**/ , CMD_NAME("GET_SYS_CFG") }, + { (CMD_02_SET_GRANULARITY << OPCODE_OFFSET) , 1 /**/ + , 1 , 0 /**/ , CMD_NAME("SET_GRANULARITY") }, + { (CMD_03_SET_TIMER_IRQ << OPCODE_OFFSET) , 1 /**/ + , 1 , 0 /**/ , CMD_NAME("SET_TIMER_IRQ") }, + { (CMD_04_GET_EVENT << OPCODE_OFFSET) , 1 /**/ + , 1 , 0 /*up to 10*/ , CMD_NAME("GET_EVENT") }, + { (CMD_05_GET_PIPES << OPCODE_OFFSET) , 1 /**/ + , 1 , 2 /*up to 4*/ , CMD_NAME("GET_PIPES") }, + { (CMD_06_ALLOCATE_PIPE << OPCODE_OFFSET) , 1 /**/ + , 0 , 0 /**/ , CMD_NAME("ALLOCATE_PIPE") }, + { (CMD_07_RELEASE_PIPE << OPCODE_OFFSET) , 1 /**/ + , 0 , 0 /**/ , CMD_NAME("RELEASE_PIPE") }, + { (CMD_08_ASK_BUFFERS << OPCODE_OFFSET) , 1 /**/ + , 1 , MAX_STREAM_BUFFER , CMD_NAME("ASK_BUFFERS") }, + { (CMD_09_STOP_PIPE << OPCODE_OFFSET) , 1 /**/ + , 0 , 0 /*up to 2*/ , CMD_NAME("STOP_PIPE") }, + { (CMD_0A_GET_PIPE_SPL_COUNT << OPCODE_OFFSET) , 1 /**/ + , 1 , 1 /*up to 2*/ , CMD_NAME("GET_PIPE_SPL_COUNT") }, + { (CMD_0B_TOGGLE_PIPE_STATE << OPCODE_OFFSET) , 1 /*up to 5*/ + , 1 , 0 /**/ , CMD_NAME("TOGGLE_PIPE_STATE") }, + { (CMD_0C_DEF_STREAM << OPCODE_OFFSET) , 1 /*up to 4*/ + , 1 , 0 /**/ , CMD_NAME("DEF_STREAM") }, + { (CMD_0D_SET_MUTE << OPCODE_OFFSET) , 3 /**/ + , 1 , 0 /**/ , CMD_NAME("SET_MUTE") }, + { (CMD_0E_GET_STREAM_SPL_COUNT << OPCODE_OFFSET) , 1/**/ + , 1 , 2 /**/ , CMD_NAME("GET_STREAM_SPL_COUNT") }, + { (CMD_0F_UPDATE_BUFFER << OPCODE_OFFSET) , 3 /*up to 4*/ + , 0 , 1 /**/ , CMD_NAME("UPDATE_BUFFER") }, + { (CMD_10_GET_BUFFER << OPCODE_OFFSET) , 1 /**/ + , 1 , 4 /**/ , CMD_NAME("GET_BUFFER") }, + { (CMD_11_CANCEL_BUFFER << OPCODE_OFFSET) , 1 /**/ + , 1 , 1 /*up to 4*/ , CMD_NAME("CANCEL_BUFFER") }, + { (CMD_12_GET_PEAK << OPCODE_OFFSET) , 1 /**/ + , 1 , 1 /**/ , CMD_NAME("GET_PEAK") }, + { (CMD_13_SET_STREAM_STATE << OPCODE_OFFSET) , 1 /**/ + , 1 , 0 /**/ , CMD_NAME("SET_STREAM_STATE") }, +}; + +static void lx_message_init(struct lx_rmh *rmh, enum cmd_mb_opcodes cmd) +{ + snd_BUG_ON(cmd >= CMD_14_INVALID); + + rmh->cmd[0] = dsp_commands[cmd].dcCodeOp; + rmh->cmd_len = dsp_commands[cmd].dcCmdLength; + rmh->stat_len = dsp_commands[cmd].dcStatusLength; + rmh->dsp_stat = dsp_commands[cmd].dcStatusType; + rmh->cmd_idx = cmd; + memset(&rmh->cmd[1], 0, (REG_CRM_NUMBER - 1) * sizeof(u32)); + +#ifdef CONFIG_SND_DEBUG + memset(rmh->stat, 0, REG_CRM_NUMBER * sizeof(u32)); +#endif +#ifdef RMH_DEBUG + rmh->cmd_idx = cmd; +#endif +} + +#ifdef RMH_DEBUG +#define LXRMH "lx6464es rmh: " +static void lx_message_dump(struct lx_rmh *rmh) +{ + u8 idx = rmh->cmd_idx; + int i; + + snd_printk(LXRMH "command %s\n", dsp_commands[idx].dcOpName); + + for (i = 0; i != rmh->cmd_len; ++i) + snd_printk(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]); + + for (i = 0; i != rmh->stat_len; ++i) + snd_printk(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]); + snd_printk("\n"); +} +#else +static inline void lx_message_dump(struct lx_rmh *rmh) +{} +#endif + + + +/* sleep 500 - 100 = 400 times 100us -> the timeout is >= 40 ms */ +#define XILINX_TIMEOUT_MS 40 +#define XILINX_POLL_NO_SLEEP 100 +#define XILINX_POLL_ITERATIONS 150 + +static int lx_message_send(struct lx6464es *chip, struct lx_rmh *rmh) +{ + u32 reg = ED_DSP_TIMED_OUT; + int dwloop; + int answer_received; + + if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) { + snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg); + return -EBUSY; + } + + /* write command */ + lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len); + + snd_BUG_ON(atomic_read(&chip->send_message_locked) != 0); + atomic_set(&chip->send_message_locked, 1); + + /* MicoBlaze gogogo */ + lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC); + + /* wait for interrupt to answer */ + for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS; ++dwloop) { + answer_received = atomic_read(&chip->send_message_locked); + if (answer_received == 0) + break; + msleep(1); + } + + if (answer_received == 0) { + /* in Debug mode verify Reg_CSM_MR */ + snd_BUG_ON(!(lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR)); + + /* command finished, read status */ + if (rmh->dsp_stat == 0) + reg = lx_dsp_reg_read(chip, eReg_CRM1); + else + reg = 0; + } else { + int i; + snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! " + "Interrupts disabled?\n"); + + /* attente bit Reg_CSM_MR */ + for (i = 0; i != XILINX_POLL_ITERATIONS; i++) { + if ((lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR)) { + if (rmh->dsp_stat == 0) + reg = lx_dsp_reg_read(chip, eReg_CRM1); + else + reg = 0; + goto polling_successful; + } + + if (i > XILINX_POLL_NO_SLEEP) + msleep(1); + } + snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! " + "polling failed\n"); + +polling_successful: + atomic_set(&chip->send_message_locked, 0); + } + + if ((reg & ERROR_VALUE) == 0) { + /* read response */ + if (rmh->stat_len) { + snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1)); + + lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat, + rmh->stat_len); + } + } else + snd_printk(KERN_WARNING LXP "lx_message_send: error_value %x\n", + reg); + + /* clear Reg_CSM_MR */ + lx_dsp_reg_write(chip, eReg_CSM, 0); + + switch (reg) { + case ED_DSP_TIMED_OUT: + snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n"); + return -ETIMEDOUT; + + case ED_DSP_CRASHED: + snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n"); + return -EAGAIN; + } + + lx_message_dump(rmh); + return 0; +} + +static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh) +{ + u32 reg = ED_DSP_TIMED_OUT; + int dwloop; + + if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) { + snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg); + return -EBUSY; + } + + /* write command */ + lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len); + + /* MicoBlaze gogogo */ + lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC); + + /* wait for interrupt to answer */ + for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS * 1000; ++dwloop) { + if (lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR) { + if (rmh->dsp_stat == 0) + reg = lx_dsp_reg_read(chip, eReg_CRM1); + else + reg = 0; + goto polling_successful; + } else + udelay(1); + } + snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send_atomic! " + "polling failed\n"); + +polling_successful: + if ((reg & ERROR_VALUE) == 0) { + /* read response */ + if (rmh->stat_len) { + snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1)); + lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat, + rmh->stat_len); + } + } else + snd_printk(LXP "rmh error: %08x\n", reg); + + /* clear Reg_CSM_MR */ + lx_dsp_reg_write(chip, eReg_CSM, 0); + + switch (reg) { + case ED_DSP_TIMED_OUT: + snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n"); + return -ETIMEDOUT; + + case ED_DSP_CRASHED: + snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n"); + return -EAGAIN; + } + + lx_message_dump(rmh); + + return reg; +} + + +/* low-level dsp access */ +int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version) +{ + u16 ret; + unsigned long flags; + + spin_lock_irqsave(&chip->msg_lock, flags); + + lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG); + ret = lx_message_send_atomic(chip, &chip->rmh); + + *rdsp_version = chip->rmh.stat[1]; + spin_unlock_irqrestore(&chip->msg_lock, flags); + return ret; +} + +int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq) +{ + u16 ret = 0; + unsigned long flags; + u32 freq_raw = 0; + u32 freq = 0; + u32 frequency = 0; + + spin_lock_irqsave(&chip->msg_lock, flags); + + lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG); + ret = lx_message_send_atomic(chip, &chip->rmh); + + if (ret == 0) { + freq_raw = chip->rmh.stat[0] >> FREQ_FIELD_OFFSET; + freq = freq_raw & XES_FREQ_COUNT8_MASK; + + if ((freq < XES_FREQ_COUNT8_48_MAX) || + (freq > XES_FREQ_COUNT8_44_MIN)) + frequency = 0; /* unknown */ + else if (freq >= XES_FREQ_COUNT8_44_MAX) + frequency = 44100; + else + frequency = 48000; + } + + spin_unlock_irqrestore(&chip->msg_lock, flags); + + *rfreq = frequency * chip->freq_ratio; + + return ret; +} + +int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address) +{ + u32 macmsb, maclsb; + + macmsb = lx_dsp_reg_read(chip, eReg_ADMACESMSB) & 0x00FFFFFF; + maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF; + + /* todo: endianess handling */ + mac_address[5] = ((u8 *)(&maclsb))[0]; + mac_address[4] = ((u8 *)(&maclsb))[1]; + mac_address[3] = ((u8 *)(&maclsb))[2]; + mac_address[2] = ((u8 *)(&macmsb))[0]; + mac_address[1] = ((u8 *)(&macmsb))[1]; + mac_address[0] = ((u8 *)(&macmsb))[2]; + + return 0; +} + + +int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chip->msg_lock, flags); + + lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY); + chip->rmh.cmd[0] |= gran; + + ret = lx_message_send_atomic(chip, &chip->rmh); + spin_unlock_irqrestore(&chip->msg_lock, flags); + return ret; +} + +int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chip->msg_lock, flags); + + lx_message_init(&chip->rmh, CMD_04_GET_EVENT); + chip->rmh.stat_len = 9; /* we don't necessarily need the full length */ + + ret = lx_message_send_atomic(chip, &chip->rmh); + + if (!ret) + memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32)); + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return ret; +} + +#define CSES_TIMEOUT 100 /* microseconds */ +#define CSES_CE 0x0001 +#define CSES_BROADCAST 0x0002 +#define CSES_UPDATE_LDSV 0x0004 + +int lx_dsp_es_check_pipeline(struct lx6464es *chip) +{ + int i; + + for (i = 0; i != CSES_TIMEOUT; ++i) { + /* + * le bit CSES_UPDATE_LDSV est à 1 dés que le macprog + * est pret. il re-passe à 0 lorsque le premier read a + * été fait. pour l'instant on retire le test car ce bit + * passe a 1 environ 200 à 400 ms aprés que le registre + * confES à été écrit (kick du xilinx ES). + * + * On ne teste que le bit CE. + * */ + + u32 cses = lx_dsp_reg_read(chip, eReg_CSES); + + if ((cses & CSES_CE) == 0) + return 0; + + udelay(1); + } + + return -ETIMEDOUT; +} + + +#define PIPE_INFO_TO_CMD(capture, pipe) \ + ((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET) + + + +/* low-level pipe handling */ +int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture, + int channels) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE); + + chip->rmh.cmd[0] |= pipe_cmd; + chip->rmh.cmd[0] |= channels; + + err = lx_message_send_atomic(chip, &chip->rmh); + spin_unlock_irqrestore(&chip->msg_lock, flags); + + if (err != 0) + snd_printk(KERN_ERR "lx6464es: could not allocate pipe\n"); + + return err; +} + +int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + spin_unlock_irqrestore(&chip->msg_lock, flags); + + return err; +} + +int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture, + u32 *r_needed, u32 *r_freed, u32 *size_array) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + +#ifdef CONFIG_SND_DEBUG + if (size_array) + memset(size_array, 0, sizeof(u32)*MAX_STREAM_BUFFER); +#endif + + *r_needed = 0; + *r_freed = 0; + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + + if (!err) { + int i; + for (i = 0; i < MAX_STREAM_BUFFER; ++i) { + u32 stat = chip->rmh.stat[i]; + if (stat & (BF_EOB << BUFF_FLAGS_OFFSET)) { + /* finished */ + *r_freed += 1; + if (size_array) + size_array[i] = stat & MASK_DATA_SIZE; + } else if ((stat & (BF_VALID << BUFF_FLAGS_OFFSET)) + == 0) + /* free */ + *r_needed += 1; + } + +#if 0 + snd_printdd(LXP "CMD_08_ASK_BUFFERS: needed %d, freed %d\n", + *r_needed, *r_freed); + for (i = 0; i < MAX_STREAM_BUFFER; ++i) { + for (i = 0; i != chip->rmh.stat_len; ++i) + snd_printdd(" stat[%d]: %x, %x\n", i, + chip->rmh.stat[i], + chip->rmh.stat[i] & MASK_DATA_SIZE); + } +#endif + } + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + + +int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_09_STOP_PIPE); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + + +int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture) +{ + int err; + + err = lx_pipe_wait_for_idle(chip, pipe, is_capture); + if (err < 0) + return err; + + err = lx_pipe_toggle_state(chip, pipe, is_capture); + + return err; +} + +int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture) +{ + int err = 0; + + err = lx_pipe_wait_for_start(chip, pipe, is_capture); + if (err < 0) + return err; + + err = lx_pipe_toggle_state(chip, pipe, is_capture); + + return err; +} + + +int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture, + u64 *rsample_count) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT); + + chip->rmh.cmd[0] |= pipe_cmd; + chip->rmh.stat_len = 2; /* need all words here! */ + + err = lx_message_send_atomic(chip, &chip->rmh); /* don't sleep! */ + + if (err != 0) + snd_printk(KERN_ERR + "lx6464es: could not query pipe's sample count\n"); + else { + *rsample_count = ((u64)(chip->rmh.stat[0] & MASK_SPL_COUNT_HI) + << 24) /* hi part */ + + chip->rmh.stat[1]; /* lo part */ + } + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + + if (err != 0) + snd_printk(KERN_ERR "lx6464es: could not query pipe's state\n"); + else + *rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F; + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe, + int is_capture, u16 state) +{ + int i; + + /* max 2*PCMOnlyGranularity = 2*1024 at 44100 = < 50 ms: + * timeout 50 ms */ + for (i = 0; i != 50; ++i) { + u16 current_state; + int err = lx_pipe_state(chip, pipe, is_capture, ¤t_state); + + if (err < 0) + return err; + + if (current_state == state) + return 0; + + mdelay(1); + } + + return -ETIMEDOUT; +} + +int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture) +{ + return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_RUN); +} + +int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture) +{ + return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_IDLE); +} + +/* low-level stream handling */ +int lx_stream_set_state(struct lx6464es *chip, u32 pipe, + int is_capture, enum stream_state_t state) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE); + + chip->rmh.cmd[0] |= pipe_cmd; + chip->rmh.cmd[0] |= state; + + err = lx_message_send_atomic(chip, &chip->rmh); + spin_unlock_irqrestore(&chip->msg_lock, flags); + + return err; +} + +int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime, + u32 pipe, int is_capture) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + u32 channels = runtime->channels; + + if (runtime->channels != channels) + snd_printk(KERN_ERR LXP "channel count mismatch: %d vs %d", + runtime->channels, channels); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM); + + chip->rmh.cmd[0] |= pipe_cmd; + + if (runtime->sample_bits == 16) + /* 16 bit format */ + chip->rmh.cmd[0] |= (STREAM_FMT_16b << STREAM_FMT_OFFSET); + + if (snd_pcm_format_little_endian(runtime->format)) + /* little endian/intel format */ + chip->rmh.cmd[0] |= (STREAM_FMT_intel << STREAM_FMT_OFFSET); + + chip->rmh.cmd[0] |= channels-1; + + err = lx_message_send_atomic(chip, &chip->rmh); + spin_unlock_irqrestore(&chip->msg_lock, flags); + + return err; +} + +int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture, + int *rstate) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + + *rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE; + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture, + u64 *r_bytepos) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT); + + chip->rmh.cmd[0] |= pipe_cmd; + + err = lx_message_send_atomic(chip, &chip->rmh); + + *r_bytepos = ((u64) (chip->rmh.stat[0] & MASK_SPL_COUNT_HI) + << 32) /* hi part */ + + chip->rmh.stat[1]; /* lo part */ + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +/* low-level buffer handling */ +int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture, + u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi, + u32 *r_buffer_index) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER); + + chip->rmh.cmd[0] |= pipe_cmd; + chip->rmh.cmd[0] |= BF_NOTIFY_EOB; /* request interrupt notification */ + + /* todo: pause request, circular buffer */ + + chip->rmh.cmd[1] = buffer_size & MASK_DATA_SIZE; + chip->rmh.cmd[2] = buf_address_lo; + + if (buf_address_hi) { + chip->rmh.cmd_len = 4; + chip->rmh.cmd[3] = buf_address_hi; + chip->rmh.cmd[0] |= BF_64BITS_ADR; + } + + err = lx_message_send_atomic(chip, &chip->rmh); + + if (err == 0) { + *r_buffer_index = chip->rmh.stat[0]; + goto done; + } + + if (err == EB_RBUFFERS_TABLE_OVERFLOW) + snd_printk(LXP "lx_buffer_give EB_RBUFFERS_TABLE_OVERFLOW\n"); + + if (err == EB_INVALID_STREAM) + snd_printk(LXP "lx_buffer_give EB_INVALID_STREAM\n"); + + if (err == EB_CMD_REFUSED) + snd_printk(LXP "lx_buffer_give EB_CMD_REFUSED\n"); + + done: + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture, + u32 *r_buffer_size) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER); + + chip->rmh.cmd[0] |= pipe_cmd; + chip->rmh.cmd[0] |= MASK_BUFFER_ID; /* ask for the current buffer: the + * microblaze will seek for it */ + + err = lx_message_send_atomic(chip, &chip->rmh); + + if (err == 0) + *r_buffer_size = chip->rmh.stat[0] & MASK_DATA_SIZE; + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture, + u32 buffer_index) +{ + int err; + unsigned long flags; + + u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe); + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER); + + chip->rmh.cmd[0] |= pipe_cmd; + chip->rmh.cmd[0] |= buffer_index; + + err = lx_message_send_atomic(chip, &chip->rmh); + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + + +/* low-level gain/peak handling + * + * \todo: can we unmute capture/playback channels independently? + * + * */ +int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute) +{ + int err; + unsigned long flags; + + /* bit set to 1: channel muted */ + u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU; + + spin_lock_irqsave(&chip->msg_lock, flags); + lx_message_init(&chip->rmh, CMD_0D_SET_MUTE); + + chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0); + + chip->rmh.cmd[1] = (u32)(mute_mask >> (u64)32); /* hi part */ + chip->rmh.cmd[2] = (u32)(mute_mask & (u64)0xFFFFFFFF); /* lo part */ + + snd_printk("mute %x %x %x\n", chip->rmh.cmd[0], chip->rmh.cmd[1], + chip->rmh.cmd[2]); + + err = lx_message_send_atomic(chip, &chip->rmh); + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +static u32 peak_map[] = { + 0x00000109, /* -90.308dB */ + 0x0000083B, /* -72.247dB */ + 0x000020C4, /* -60.205dB */ + 0x00008273, /* -48.030dB */ + 0x00020756, /* -36.005dB */ + 0x00040C37, /* -30.001dB */ + 0x00081385, /* -24.002dB */ + 0x00101D3F, /* -18.000dB */ + 0x0016C310, /* -15.000dB */ + 0x002026F2, /* -12.001dB */ + 0x002D6A86, /* -9.000dB */ + 0x004026E6, /* -6.004dB */ + 0x005A9DF6, /* -3.000dB */ + 0x0065AC8B, /* -2.000dB */ + 0x00721481, /* -1.000dB */ + 0x007FFFFF, /* FS */ +}; + +int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels, + u32 *r_levels) +{ + int err = 0; + unsigned long flags; + int i; + spin_lock_irqsave(&chip->msg_lock, flags); + + for (i = 0; i < channels; i += 4) { + u32 s0, s1, s2, s3; + + lx_message_init(&chip->rmh, CMD_12_GET_PEAK); + chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, i); + + err = lx_message_send_atomic(chip, &chip->rmh); + + if (err == 0) { + s0 = peak_map[chip->rmh.stat[0] & 0x0F]; + s1 = peak_map[(chip->rmh.stat[0] >> 4) & 0xf]; + s2 = peak_map[(chip->rmh.stat[0] >> 8) & 0xf]; + s3 = peak_map[(chip->rmh.stat[0] >> 12) & 0xf]; + } else + s0 = s1 = s2 = s3 = 0; + + r_levels[0] = s0; + r_levels[1] = s1; + r_levels[2] = s2; + r_levels[3] = s3; + + r_levels += 4; + } + + spin_unlock_irqrestore(&chip->msg_lock, flags); + return err; +} + +/* interrupt handling */ +#define PCX_IRQ_NONE 0 +#define IRQCS_ACTIVE_PCIDB 0x00002000L /* Bit nø 13 */ +#define IRQCS_ENABLE_PCIIRQ 0x00000100L /* Bit nø 08 */ +#define IRQCS_ENABLE_PCIDB 0x00000200L /* Bit nø 09 */ + +static u32 lx_interrupt_test_ack(struct lx6464es *chip) +{ + u32 irqcs = lx_plx_reg_read(chip, ePLX_IRQCS); + + /* Test if PCI Doorbell interrupt is active */ + if (irqcs & IRQCS_ACTIVE_PCIDB) { + u32 temp; + irqcs = PCX_IRQ_NONE; + + while ((temp = lx_plx_reg_read(chip, ePLX_L2PCIDB))) { + /* RAZ interrupt */ + irqcs |= temp; + lx_plx_reg_write(chip, ePLX_L2PCIDB, temp); + } + + return irqcs; + } + return PCX_IRQ_NONE; +} + +static int lx_interrupt_ack(struct lx6464es *chip, u32 *r_irqsrc, + int *r_async_pending, int *r_async_escmd) +{ + u32 irq_async; + u32 irqsrc = lx_interrupt_test_ack(chip); + + if (irqsrc == PCX_IRQ_NONE) + return 0; + + *r_irqsrc = irqsrc; + + irq_async = irqsrc & MASK_SYS_ASYNC_EVENTS; /* + EtherSound response + * (set by xilinx) + EOB */ + + if (irq_async & MASK_SYS_STATUS_ESA) { + irq_async &= ~MASK_SYS_STATUS_ESA; + *r_async_escmd = 1; + } + + if (irqsrc & MASK_SYS_STATUS_CMD_DONE) + /* xilinx command notification */ + atomic_set(&chip->send_message_locked, 0); + + if (irq_async) { + /* snd_printd("interrupt: async event pending\n"); */ + *r_async_pending = 1; + } + + return 1; +} + +static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc, + int *r_freq_changed, + u64 *r_notified_in_pipe_mask, + u64 *r_notified_out_pipe_mask) +{ + int err; + u32 stat[9]; /* answer from CMD_04_GET_EVENT */ + + /* On peut optimiser pour ne pas lire les evenements vides + * les mots de réponse sont dans l'ordre suivant : + * Stat[0] mot de status général + * Stat[1] fin de buffer OUT pF + * Stat[2] fin de buffer OUT pf + * Stat[3] fin de buffer IN pF + * Stat[4] fin de buffer IN pf + * Stat[5] underrun poid fort + * Stat[6] underrun poid faible + * Stat[7] overrun poid fort + * Stat[8] overrun poid faible + * */ + + u64 orun_mask; + u64 urun_mask; +#if 0 + int has_underrun = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0; + int has_overrun = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0; +#endif + int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0; + int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0; + + *r_freq_changed = (irqsrc & MASK_SYS_STATUS_FREQ) ? 1 : 0; + + err = lx_dsp_read_async_events(chip, stat); + if (err < 0) + return err; + + if (eb_pending_in) { + *r_notified_in_pipe_mask = ((u64)stat[3] << 32) + + stat[4]; + snd_printdd(LXP "interrupt: EOBI pending %llx\n", + *r_notified_in_pipe_mask); + } + if (eb_pending_out) { + *r_notified_out_pipe_mask = ((u64)stat[1] << 32) + + stat[2]; + snd_printdd(LXP "interrupt: EOBO pending %llx\n", + *r_notified_out_pipe_mask); + } + + orun_mask = ((u64)stat[7] << 32) + stat[8]; + urun_mask = ((u64)stat[5] << 32) + stat[6]; + + /* todo: handle xrun notification */ + + return err; +} + +static int lx_interrupt_request_new_buffer(struct lx6464es *chip, + struct lx_stream *lx_stream) +{ + struct snd_pcm_substream *substream = lx_stream->stream; + int is_capture = lx_stream->is_capture; + int err; + unsigned long flags; + + const u32 channels = substream->runtime->channels; + const u32 bytes_per_frame = channels * 3; + const u32 period_size = substream->runtime->period_size; + const u32 period_bytes = period_size * bytes_per_frame; + const u32 pos = lx_stream->frame_pos; + const u32 next_pos = ((pos+1) == substream->runtime->periods) ? + 0 : pos + 1; + + dma_addr_t buf = substream->dma_buffer.addr + pos * period_bytes; + u32 buf_hi = 0; + u32 buf_lo = 0; + u32 buffer_index = 0; + + u32 needed, freed; + u32 size_array[MAX_STREAM_BUFFER]; + + snd_printdd("->lx_interrupt_request_new_buffer\n"); + + spin_lock_irqsave(&chip->lock, flags); + + err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array); + snd_printdd(LXP "interrupt: needed %d, freed %d\n", needed, freed); + + unpack_pointer(buf, &buf_lo, &buf_hi); + err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi, + &buffer_index); + snd_printdd(LXP "interrupt: gave buffer index %x on %p (%d bytes)\n", + buffer_index, (void *)buf, period_bytes); + + lx_stream->frame_pos = next_pos; + spin_unlock_irqrestore(&chip->lock, flags); + + return err; +} + +void lx_tasklet_playback(unsigned long data) +{ + struct lx6464es *chip = (struct lx6464es *)data; + struct lx_stream *lx_stream = &chip->playback_stream; + int err; + + snd_printdd("->lx_tasklet_playback\n"); + + err = lx_interrupt_request_new_buffer(chip, lx_stream); + if (err < 0) + snd_printk(KERN_ERR LXP + "cannot request new buffer for playback\n"); + + snd_pcm_period_elapsed(lx_stream->stream); +} + +void lx_tasklet_capture(unsigned long data) +{ + struct lx6464es *chip = (struct lx6464es *)data; + struct lx_stream *lx_stream = &chip->capture_stream; + int err; + + snd_printdd("->lx_tasklet_capture\n"); + err = lx_interrupt_request_new_buffer(chip, lx_stream); + if (err < 0) + snd_printk(KERN_ERR LXP + "cannot request new buffer for capture\n"); + + snd_pcm_period_elapsed(lx_stream->stream); +} + + + +static int lx_interrupt_handle_audio_transfer(struct lx6464es *chip, + u64 notified_in_pipe_mask, + u64 notified_out_pipe_mask) +{ + int err = 0; + + if (notified_in_pipe_mask) { + snd_printdd(LXP "requesting audio transfer for capture\n"); + tasklet_hi_schedule(&chip->tasklet_capture); + } + + if (notified_out_pipe_mask) { + snd_printdd(LXP "requesting audio transfer for playback\n"); + tasklet_hi_schedule(&chip->tasklet_playback); + } + + return err; +} + + +irqreturn_t lx_interrupt(int irq, void *dev_id) +{ + struct lx6464es *chip = dev_id; + int async_pending, async_escmd; + u32 irqsrc; + + spin_lock(&chip->lock); + + snd_printdd("**************************************************\n"); + + if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) { + spin_unlock(&chip->lock); + snd_printdd("IRQ_NONE\n"); + return IRQ_NONE; /* this device did not cause the interrupt */ + } + + if (irqsrc & MASK_SYS_STATUS_CMD_DONE) + goto exit; + +#if 0 + if (irqsrc & MASK_SYS_STATUS_EOBI) + snd_printdd(LXP "interrupt: EOBI\n"); + + if (irqsrc & MASK_SYS_STATUS_EOBO) + snd_printdd(LXP "interrupt: EOBO\n"); + + if (irqsrc & MASK_SYS_STATUS_URUN) + snd_printdd(LXP "interrupt: URUN\n"); + + if (irqsrc & MASK_SYS_STATUS_ORUN) + snd_printdd(LXP "interrupt: ORUN\n"); +#endif + + if (async_pending) { + u64 notified_in_pipe_mask = 0; + u64 notified_out_pipe_mask = 0; + int freq_changed; + int err; + + /* handle async events */ + err = lx_interrupt_handle_async_events(chip, irqsrc, + &freq_changed, + ¬ified_in_pipe_mask, + ¬ified_out_pipe_mask); + if (err) + snd_printk(KERN_ERR LXP + "error handling async events\n"); + + err = lx_interrupt_handle_audio_transfer(chip, + notified_in_pipe_mask, + notified_out_pipe_mask + ); + if (err) + snd_printk(KERN_ERR LXP + "error during audio transfer\n"); + } + + if (async_escmd) { +#if 0 + /* backdoor for ethersound commands + * + * for now, we do not need this + * + * */ + + snd_printdd("lx6464es: interrupt requests escmd handling\n"); +#endif + } + +exit: + spin_unlock(&chip->lock); + return IRQ_HANDLED; /* this device caused the interrupt */ +} + + +static void lx_irq_set(struct lx6464es *chip, int enable) +{ + u32 reg = lx_plx_reg_read(chip, ePLX_IRQCS); + + /* enable/disable interrupts + * + * Set the Doorbell and PCI interrupt enable bits + * + * */ + if (enable) + reg |= (IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB); + else + reg &= ~(IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB); + lx_plx_reg_write(chip, ePLX_IRQCS, reg); +} + +void lx_irq_enable(struct lx6464es *chip) +{ + snd_printdd("->lx_irq_enable\n"); + lx_irq_set(chip, 1); +} + +void lx_irq_disable(struct lx6464es *chip) +{ + snd_printdd("->lx_irq_disable\n"); + lx_irq_set(chip, 0); +} diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h new file mode 100644 index 000000000000..6bd9cbbbc68d --- /dev/null +++ b/sound/pci/lx6464es/lx_core.h @@ -0,0 +1,242 @@ +/* -*- linux-c -*- * + * + * ALSA driver for the digigram lx6464es interface + * low-level interface + * + * Copyright (c) 2009 Tim Blechmann + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef LX_CORE_H +#define LX_CORE_H + +#include + +#include "lx_defs.h" + +#define REG_CRM_NUMBER 12 + +struct lx6464es; + +/* low-level register access */ + +/* dsp register access */ +enum { + eReg_BASE, + eReg_CSM, + eReg_CRM1, + eReg_CRM2, + eReg_CRM3, + eReg_CRM4, + eReg_CRM5, + eReg_CRM6, + eReg_CRM7, + eReg_CRM8, + eReg_CRM9, + eReg_CRM10, + eReg_CRM11, + eReg_CRM12, + + eReg_ICR, + eReg_CVR, + eReg_ISR, + eReg_RXHTXH, + eReg_RXMTXM, + eReg_RHLTXL, + eReg_RESETDSP, + + eReg_CSUF, + eReg_CSES, + eReg_CRESMSB, + eReg_CRESLSB, + eReg_ADMACESMSB, + eReg_ADMACESLSB, + eReg_CONFES, + + eMaxPortLx +}; + +unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port); +void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len); +void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data); +void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data, + u32 len); + +/* plx register access */ +enum { + ePLX_PCICR, + + ePLX_MBOX0, + ePLX_MBOX1, + ePLX_MBOX2, + ePLX_MBOX3, + ePLX_MBOX4, + ePLX_MBOX5, + ePLX_MBOX6, + ePLX_MBOX7, + + ePLX_L2PCIDB, + ePLX_IRQCS, + ePLX_CHIPSC, + + eMaxPort +}; + +unsigned long lx_plx_reg_read(struct lx6464es *chip, int port); +void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data); + +/* rhm */ +struct lx_rmh { + u16 cmd_len; /* length of the command to send (WORDs) */ + u16 stat_len; /* length of the status received (WORDs) */ + u16 dsp_stat; /* status type, RMP_SSIZE_XXX */ + u16 cmd_idx; /* index of the command */ + u32 cmd[REG_CRM_NUMBER]; + u32 stat[REG_CRM_NUMBER]; +}; + + +/* low-level dsp access */ +int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version); +int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq); +int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran); +int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data); +int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address); + + +/* low-level pipe handling */ +int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture, + int channels); +int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture); +int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture, + u64 *rsample_count); +int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate); +int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture); +int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture); +int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture); + +int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture); +int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture); + +/* low-level stream handling */ +int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime, + u32 pipe, int is_capture); +int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture, + int *rstate); +int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture, + u64 *r_bytepos); + +int lx_stream_set_state(struct lx6464es *chip, u32 pipe, + int is_capture, enum stream_state_t state); + +static inline int lx_stream_start(struct lx6464es *chip, u32 pipe, + int is_capture) +{ + snd_printdd("->lx_stream_start\n"); + return lx_stream_set_state(chip, pipe, is_capture, SSTATE_RUN); +} + +static inline int lx_stream_pause(struct lx6464es *chip, u32 pipe, + int is_capture) +{ + snd_printdd("->lx_stream_pause\n"); + return lx_stream_set_state(chip, pipe, is_capture, SSTATE_PAUSE); +} + +static inline int lx_stream_stop(struct lx6464es *chip, u32 pipe, + int is_capture) +{ + snd_printdd("->lx_stream_stop\n"); + return lx_stream_set_state(chip, pipe, is_capture, SSTATE_STOP); +} + +/* low-level buffer handling */ +int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture, + u32 *r_needed, u32 *r_freed, u32 *size_array); +int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture, + u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi, + u32 *r_buffer_index); +int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture, + u32 *r_buffer_size); +int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture, + u32 buffer_index); + +/* low-level gain/peak handling */ +int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute); +int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels, + u32 *r_levels); + + +/* interrupt handling */ +irqreturn_t lx_interrupt(int irq, void *dev_id); +void lx_irq_enable(struct lx6464es *chip); +void lx_irq_disable(struct lx6464es *chip); + +void lx_tasklet_capture(unsigned long data); +void lx_tasklet_playback(unsigned long data); + + +/* Stream Format Header Defines (for LIN and IEEE754) */ +#define HEADER_FMT_BASE HEADER_FMT_BASE_LIN +#define HEADER_FMT_BASE_LIN 0xFED00000 +#define HEADER_FMT_BASE_FLOAT 0xFAD00000 +#define HEADER_FMT_MONO 0x00000080 /* bit 23 in header_lo. WARNING: old + * bit 22 is ignored in float + * format */ +#define HEADER_FMT_INTEL 0x00008000 +#define HEADER_FMT_16BITS 0x00002000 +#define HEADER_FMT_24BITS 0x00004000 +#define HEADER_FMT_UPTO11 0x00000200 /* frequency is less or equ. to 11k. + * */ +#define HEADER_FMT_UPTO32 0x00000100 /* frequency is over 11k and less + * then 32k.*/ + + +#define BIT_FMP_HEADER 23 +#define BIT_FMP_SD 22 +#define BIT_FMP_MULTICHANNEL 19 + +#define START_STATE 1 +#define PAUSE_STATE 0 + + + + + +/* from PcxAll_e.h */ +/* Start/Pause condition for pipes (PCXStartPipe, PCXPausePipe) */ +#define START_PAUSE_IMMEDIATE 0 +#define START_PAUSE_ON_SYNCHRO 1 +#define START_PAUSE_ON_TIME_CODE 2 + + +/* Pipe / Stream state */ +#define START_STATE 1 +#define PAUSE_STATE 0 + +static inline void unpack_pointer(dma_addr_t ptr, u32 *r_low, u32 *r_high) +{ + *r_low = (u32)(ptr & 0xffffffff); +#if BITS_PER_LONG == 32 + *r_high = 0; +#else + *r_high = (u32)((u64)ptr>>32); +#endif +} + +#endif /* LX_CORE_H */ diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h new file mode 100644 index 000000000000..49d36bdd512c --- /dev/null +++ b/sound/pci/lx6464es/lx_defs.h @@ -0,0 +1,376 @@ +/* -*- linux-c -*- * + * + * ALSA driver for the digigram lx6464es interface + * adapted upstream headers + * + * Copyright (c) 2009 Tim Blechmann + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef LX_DEFS_H +#define LX_DEFS_H + +/* code adapted from ethersound.h */ +#define XES_FREQ_COUNT8_MASK 0x00001FFF /* compteur 25MHz entre 8 ech. */ +#define XES_FREQ_COUNT8_44_MIN 0x00001288 /* 25M / + * [ 44k - ( 44.1k + 48k ) / 2 ] + * * 8 */ +#define XES_FREQ_COUNT8_44_MAX 0x000010F0 /* 25M / [ ( 44.1k + 48k ) / 2 ] + * * 8 */ +#define XES_FREQ_COUNT8_48_MAX 0x00000F08 /* 25M / + * [ 48k + ( 44.1k + 48k ) / 2 ] + * * 8 */ + +/* code adapted from LXES_registers.h */ + +#define IOCR_OUTPUTS_OFFSET 0 /* (rw) offset for the number of OUTs in the + * ConfES register. */ +#define IOCR_INPUTS_OFFSET 8 /* (rw) offset for the number of INs in the + * ConfES register. */ +#define FREQ_RATIO_OFFSET 19 /* (rw) offset for frequency ratio in the + * ConfES register. */ +#define FREQ_RATIO_SINGLE_MODE 0x01 /* value for single mode frequency ratio: + * sample rate = frequency rate. */ + +#define CONFES_READ_PART_MASK 0x00070000 +#define CONFES_WRITE_PART_MASK 0x00F80000 + +/* code adapted from if_drv_mb.h */ + +#define MASK_SYS_STATUS_ERROR (1L << 31) /* events that lead to a PCI irq if + * not yet pending */ +#define MASK_SYS_STATUS_URUN (1L << 30) +#define MASK_SYS_STATUS_ORUN (1L << 29) +#define MASK_SYS_STATUS_EOBO (1L << 28) +#define MASK_SYS_STATUS_EOBI (1L << 27) +#define MASK_SYS_STATUS_FREQ (1L << 26) +#define MASK_SYS_STATUS_ESA (1L << 25) /* reserved, this is set by the + * XES */ +#define MASK_SYS_STATUS_TIMER (1L << 24) + +#define MASK_SYS_ASYNC_EVENTS (MASK_SYS_STATUS_ERROR | \ + MASK_SYS_STATUS_URUN | \ + MASK_SYS_STATUS_ORUN | \ + MASK_SYS_STATUS_EOBO | \ + MASK_SYS_STATUS_EOBI | \ + MASK_SYS_STATUS_FREQ | \ + MASK_SYS_STATUS_ESA) + +#define MASK_SYS_PCI_EVENTS (MASK_SYS_ASYNC_EVENTS | \ + MASK_SYS_STATUS_TIMER) + +#define MASK_SYS_TIMER_COUNT 0x0000FFFF + +#define MASK_SYS_STATUS_EOT_PLX (1L << 22) /* event that remains + * internal: reserved fo end + * of plx dma */ +#define MASK_SYS_STATUS_XES (1L << 21) /* event that remains + * internal: pending XES + * IRQ */ +#define MASK_SYS_STATUS_CMD_DONE (1L << 20) /* alternate command + * management: notify driver + * instead of polling */ + + +#define MAX_STREAM_BUFFER 5 /* max amount of stream buffers. */ + +#define MICROBLAZE_IBL_MIN 32 +#define MICROBLAZE_IBL_DEFAULT 128 +#define MICROBLAZE_IBL_MAX 512 +/* #define MASK_GRANULARITY (2*MICROBLAZE_IBL_MAX-1) */ + + + +/* command opcodes, see reference for details */ + +/* + the capture bit position in the object_id field in driver commands + depends upon the number of managed channels. For now, 64 IN + 64 OUT are + supported. HOwever, the communication protocol forsees 1024 channels, hence + bit 10 indicates a capture (input) object). +*/ +#define ID_IS_CAPTURE (1L << 10) +#define ID_OFFSET 13 /* object ID is at the 13th bit in the + * 1st command word.*/ +#define ID_CH_MASK 0x3F +#define OPCODE_OFFSET 24 /* offset of the command opcode in the first + * command word.*/ + +enum cmd_mb_opcodes { + CMD_00_INFO_DEBUG = 0x00, + CMD_01_GET_SYS_CFG = 0x01, + CMD_02_SET_GRANULARITY = 0x02, + CMD_03_SET_TIMER_IRQ = 0x03, + CMD_04_GET_EVENT = 0x04, + CMD_05_GET_PIPES = 0x05, + + CMD_06_ALLOCATE_PIPE = 0x06, + CMD_07_RELEASE_PIPE = 0x07, + CMD_08_ASK_BUFFERS = 0x08, + CMD_09_STOP_PIPE = 0x09, + CMD_0A_GET_PIPE_SPL_COUNT = 0x0a, + CMD_0B_TOGGLE_PIPE_STATE = 0x0b, + + CMD_0C_DEF_STREAM = 0x0c, + CMD_0D_SET_MUTE = 0x0d, + CMD_0E_GET_STREAM_SPL_COUNT = 0x0e, + CMD_0F_UPDATE_BUFFER = 0x0f, + CMD_10_GET_BUFFER = 0x10, + CMD_11_CANCEL_BUFFER = 0x11, + CMD_12_GET_PEAK = 0x12, + CMD_13_SET_STREAM_STATE = 0x13, + CMD_14_INVALID = 0x14, +}; + +/* pipe states */ +enum pipe_state_t { + PSTATE_IDLE = 0, /* the pipe is not processed in the XES_IRQ + * (free or stopped, or paused). */ + PSTATE_RUN = 1, /* sustained play/record state. */ + PSTATE_PURGE = 2, /* the ES channels are now off, render pipes do + * not DMA, record pipe do a last DMA. */ + PSTATE_ACQUIRE = 3, /* the ES channels are now on, render pipes do + * not yet increase their sample count, record + * pipes do not DMA. */ + PSTATE_CLOSING = 4, /* the pipe is releasing, and may not yet + * receive an "alloc" command. */ +}; + +/* stream states */ +enum stream_state_t { + SSTATE_STOP = 0x00, /* setting to stop resets the stream spl + * count.*/ + SSTATE_RUN = (0x01 << 0), /* start DMA and spl count handling. */ + SSTATE_PAUSE = (0x01 << 1), /* pause DMA and spl count handling. */ +}; + +/* buffer flags */ +enum buffer_flags { + BF_VALID = 0x80, /* set if the buffer is valid, clear if free.*/ + BF_CURRENT = 0x40, /* set if this is the current buffer (there is + * always a current buffer).*/ + BF_NOTIFY_EOB = 0x20, /* set if this buffer must cause a PCI event + * when finished.*/ + BF_CIRCULAR = 0x10, /* set if buffer[1] must be copied to buffer[0] + * by the end of this buffer.*/ + BF_64BITS_ADR = 0x08, /* set if the hi part of the address is valid.*/ + BF_xx = 0x04, /* future extension.*/ + BF_EOB = 0x02, /* set if finished, but not yet free.*/ + BF_PAUSE = 0x01, /* pause stream at buffer end.*/ + BF_ZERO = 0x00, /* no flags (init).*/ +}; + +/** +* Stream Flags definitions +*/ +enum stream_flags { + SF_ZERO = 0x00000000, /* no flags (stream invalid). */ + SF_VALID = 0x10000000, /* the stream has a valid DMA_conf + * info (setstreamformat). */ + SF_XRUN = 0x20000000, /* the stream is un x-run state. */ + SF_START = 0x40000000, /* the DMA is running.*/ + SF_ASIO = 0x80000000, /* ASIO.*/ +}; + + +#define MASK_SPL_COUNT_HI 0x00FFFFFF /* 4 MSBits are status bits */ +#define PSTATE_OFFSET 28 /* 4 MSBits are status bits */ + + +#define MASK_STREAM_HAS_MAPPING (1L << 12) +#define MASK_STREAM_IS_ASIO (1L << 9) +#define STREAM_FMT_OFFSET 10 /* the stream fmt bits start at the 10th + * bit in the command word. */ + +#define STREAM_FMT_16b 0x02 +#define STREAM_FMT_intel 0x01 + +#define FREQ_FIELD_OFFSET 15 /* offset of the freq field in the response + * word */ + +#define BUFF_FLAGS_OFFSET 24 /* offset of the buffer flags in the + * response word. */ +#define MASK_DATA_SIZE 0x00FFFFFF /* this must match the field size of + * datasize in the buffer_t structure. */ + +#define MASK_BUFFER_ID 0xFF /* the cancel command awaits a buffer ID, + * may be 0xFF for "current". */ + + +/* code adapted from PcxErr_e.h */ + +/* Bits masks */ + +#define ERROR_MASK 0x8000 + +#define SOURCE_MASK 0x7800 + +#define E_SOURCE_BOARD 0x4000 /* 8 >> 1 */ +#define E_SOURCE_DRV 0x2000 /* 4 >> 1 */ +#define E_SOURCE_API 0x1000 /* 2 >> 1 */ +/* Error tools */ +#define E_SOURCE_TOOLS 0x0800 /* 1 >> 1 */ +/* Error pcxaudio */ +#define E_SOURCE_AUDIO 0x1800 /* 3 >> 1 */ +/* Error virtual pcx */ +#define E_SOURCE_VPCX 0x2800 /* 5 >> 1 */ +/* Error dispatcher */ +#define E_SOURCE_DISPATCHER 0x3000 /* 6 >> 1 */ +/* Error from CobraNet firmware */ +#define E_SOURCE_COBRANET 0x3800 /* 7 >> 1 */ + +#define E_SOURCE_USER 0x7800 + +#define CLASS_MASK 0x0700 + +#define CODE_MASK 0x00FF + +/* Bits values */ + +/* Values for the error/warning bit */ +#define ERROR_VALUE 0x8000 +#define WARNING_VALUE 0x0000 + +/* Class values */ +#define E_CLASS_GENERAL 0x0000 +#define E_CLASS_INVALID_CMD 0x0100 +#define E_CLASS_INVALID_STD_OBJECT 0x0200 +#define E_CLASS_RSRC_IMPOSSIBLE 0x0300 +#define E_CLASS_WRONG_CONTEXT 0x0400 +#define E_CLASS_BAD_SPECIFIC_PARAMETER 0x0500 +#define E_CLASS_REAL_TIME_ERROR 0x0600 +#define E_CLASS_DIRECTSHOW 0x0700 +#define E_CLASS_FREE 0x0700 + + +/* Complete DRV error code for the general class */ +#define ED_GN (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_GENERAL) +#define ED_CONCURRENCY (ED_GN | 0x01) +#define ED_DSP_CRASHED (ED_GN | 0x02) +#define ED_UNKNOWN_BOARD (ED_GN | 0x03) +#define ED_NOT_INSTALLED (ED_GN | 0x04) +#define ED_CANNOT_OPEN_SVC_MANAGER (ED_GN | 0x05) +#define ED_CANNOT_READ_REGISTRY (ED_GN | 0x06) +#define ED_DSP_VERSION_MISMATCH (ED_GN | 0x07) +#define ED_UNAVAILABLE_FEATURE (ED_GN | 0x08) +#define ED_CANCELLED (ED_GN | 0x09) +#define ED_NO_RESPONSE_AT_IRQA (ED_GN | 0x10) +#define ED_INVALID_ADDRESS (ED_GN | 0x11) +#define ED_DSP_CORRUPTED (ED_GN | 0x12) +#define ED_PENDING_OPERATION (ED_GN | 0x13) +#define ED_NET_ALLOCATE_MEMORY_IMPOSSIBLE (ED_GN | 0x14) +#define ED_NET_REGISTER_ERROR (ED_GN | 0x15) +#define ED_NET_THREAD_ERROR (ED_GN | 0x16) +#define ED_NET_OPEN_ERROR (ED_GN | 0x17) +#define ED_NET_CLOSE_ERROR (ED_GN | 0x18) +#define ED_NET_NO_MORE_PACKET (ED_GN | 0x19) +#define ED_NET_NO_MORE_BUFFER (ED_GN | 0x1A) +#define ED_NET_SEND_ERROR (ED_GN | 0x1B) +#define ED_NET_RECEIVE_ERROR (ED_GN | 0x1C) +#define ED_NET_WRONG_MSG_SIZE (ED_GN | 0x1D) +#define ED_NET_WAIT_ERROR (ED_GN | 0x1E) +#define ED_NET_EEPROM_ERROR (ED_GN | 0x1F) +#define ED_INVALID_RS232_COM_NUMBER (ED_GN | 0x20) +#define ED_INVALID_RS232_INIT (ED_GN | 0x21) +#define ED_FILE_ERROR (ED_GN | 0x22) +#define ED_INVALID_GPIO_CMD (ED_GN | 0x23) +#define ED_RS232_ALREADY_OPENED (ED_GN | 0x24) +#define ED_RS232_NOT_OPENED (ED_GN | 0x25) +#define ED_GPIO_ALREADY_OPENED (ED_GN | 0x26) +#define ED_GPIO_NOT_OPENED (ED_GN | 0x27) +#define ED_REGISTRY_ERROR (ED_GN | 0x28) /* <- NCX */ +#define ED_INVALID_SERVICE (ED_GN | 0x29) /* <- NCX */ + +#define ED_READ_FILE_ALREADY_OPENED (ED_GN | 0x2a) /* <- Decalage + * pour RCX + * (old 0x28) + * */ +#define ED_READ_FILE_INVALID_COMMAND (ED_GN | 0x2b) /* ~ */ +#define ED_READ_FILE_INVALID_PARAMETER (ED_GN | 0x2c) /* ~ */ +#define ED_READ_FILE_ALREADY_CLOSED (ED_GN | 0x2d) /* ~ */ +#define ED_READ_FILE_NO_INFORMATION (ED_GN | 0x2e) /* ~ */ +#define ED_READ_FILE_INVALID_HANDLE (ED_GN | 0x2f) /* ~ */ +#define ED_READ_FILE_END_OF_FILE (ED_GN | 0x30) /* ~ */ +#define ED_READ_FILE_ERROR (ED_GN | 0x31) /* ~ */ + +#define ED_DSP_CRASHED_EXC_DSPSTACK_OVERFLOW (ED_GN | 0x32) /* <- Decalage pour + * PCX (old 0x14) */ +#define ED_DSP_CRASHED_EXC_SYSSTACK_OVERFLOW (ED_GN | 0x33) /* ~ */ +#define ED_DSP_CRASHED_EXC_ILLEGAL (ED_GN | 0x34) /* ~ */ +#define ED_DSP_CRASHED_EXC_TIMER_REENTRY (ED_GN | 0x35) /* ~ */ +#define ED_DSP_CRASHED_EXC_FATAL_ERROR (ED_GN | 0x36) /* ~ */ + +#define ED_FLASH_PCCARD_NOT_PRESENT (ED_GN | 0x37) + +#define ED_NO_CURRENT_CLOCK (ED_GN | 0x38) + +/* Complete DRV error code for real time class */ +#define ED_RT (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_REAL_TIME_ERROR) +#define ED_DSP_TIMED_OUT (ED_RT | 0x01) +#define ED_DSP_CHK_TIMED_OUT (ED_RT | 0x02) +#define ED_STREAM_OVERRUN (ED_RT | 0x03) +#define ED_DSP_BUSY (ED_RT | 0x04) +#define ED_DSP_SEMAPHORE_TIME_OUT (ED_RT | 0x05) +#define ED_BOARD_TIME_OUT (ED_RT | 0x06) +#define ED_XILINX_ERROR (ED_RT | 0x07) +#define ED_COBRANET_ITF_NOT_RESPONDING (ED_RT | 0x08) + +/* Complete BOARD error code for the invaid standard object class */ +#define EB_ISO (ERROR_VALUE | E_SOURCE_BOARD | \ + E_CLASS_INVALID_STD_OBJECT) +#define EB_INVALID_EFFECT (EB_ISO | 0x00) +#define EB_INVALID_PIPE (EB_ISO | 0x40) +#define EB_INVALID_STREAM (EB_ISO | 0x80) +#define EB_INVALID_AUDIO (EB_ISO | 0xC0) + +/* Complete BOARD error code for impossible resource allocation class */ +#define EB_RI (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_RSRC_IMPOSSIBLE) +#define EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE (EB_RI | 0x01) +#define EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE (EB_RI | 0x02) + +#define EB_ALLOCATE_MEM_STREAM_IMPOSSIBLE \ + EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE +#define EB_ALLOCATE_MEM_PIPE_IMPOSSIBLE \ + EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE + +#define EB_ALLOCATE_DIFFERED_CMD_IMPOSSIBLE (EB_RI | 0x03) +#define EB_TOO_MANY_DIFFERED_CMD (EB_RI | 0x04) +#define EB_RBUFFERS_TABLE_OVERFLOW (EB_RI | 0x05) +#define EB_ALLOCATE_EFFECTS_IMPOSSIBLE (EB_RI | 0x08) +#define EB_ALLOCATE_EFFECT_POS_IMPOSSIBLE (EB_RI | 0x09) +#define EB_RBUFFER_NOT_AVAILABLE (EB_RI | 0x0A) +#define EB_ALLOCATE_CONTEXT_LIII_IMPOSSIBLE (EB_RI | 0x0B) +#define EB_STATUS_DIALOG_IMPOSSIBLE (EB_RI | 0x1D) +#define EB_CONTROL_CMD_IMPOSSIBLE (EB_RI | 0x1E) +#define EB_STATUS_SEND_IMPOSSIBLE (EB_RI | 0x1F) +#define EB_ALLOCATE_PIPE_IMPOSSIBLE (EB_RI | 0x40) +#define EB_ALLOCATE_STREAM_IMPOSSIBLE (EB_RI | 0x80) +#define EB_ALLOCATE_AUDIO_IMPOSSIBLE (EB_RI | 0xC0) + +/* Complete BOARD error code for wrong call context class */ +#define EB_WCC (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_WRONG_CONTEXT) +#define EB_CMD_REFUSED (EB_WCC | 0x00) +#define EB_START_STREAM_REFUSED (EB_WCC | 0xFC) +#define EB_SPC_REFUSED (EB_WCC | 0xFD) +#define EB_CSN_REFUSED (EB_WCC | 0xFE) +#define EB_CSE_REFUSED (EB_WCC | 0xFF) + + + + +#endif /* LX_DEFS_H */ -- cgit v1.2.1 From 7852fd08fdc78bf43150137bdbfdfdccdefffe0f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Apr 2009 12:25:52 +0200 Subject: ALSA: lx6464es - Use snd_card_create() Use snd_card_create() instead of the obsoleted snd_card_new(). Signed-off-by: Takashi Iwai --- sound/pci/lx6464es/lx6464es.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 7bc8b8caa992..870bfc58c697 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -1091,9 +1091,9 @@ static int __devinit snd_lx6464es_probe(struct pci_dev *pci, return -ENOENT; } - card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; + err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + if (err < 0) + return err; err = snd_lx6464es_create(card, pci, &chip); if (err < 0) { -- cgit v1.2.1 From d7dee4d7744d039bf28e4f6d4f5674f44820265a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Apr 2009 12:27:12 +0200 Subject: ALSA: lx6464es - Disable lx_message_send() Disable lx_message_send() function temporarily as it's not used anywhere. Signed-off-by: Takashi Iwai --- sound/pci/lx6464es/lx_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index a9f8f882b107..5812780d6e89 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -314,6 +314,7 @@ static inline void lx_message_dump(struct lx_rmh *rmh) #define XILINX_POLL_NO_SLEEP 100 #define XILINX_POLL_ITERATIONS 150 +#if 0 /* not used now */ static int lx_message_send(struct lx6464es *chip, struct lx_rmh *rmh) { u32 reg = ED_DSP_TIMED_OUT; @@ -404,6 +405,7 @@ polling_successful: lx_message_dump(rmh); return 0; } +#endif /* not used now */ static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh) { -- cgit v1.2.1 From 4e01f54bfd3f423db8fd6c91c4f0471f18aa0c50 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Apr 2009 08:53:34 +0200 Subject: ALSA: hda - Add Creative CA0110-IBG support Added the support for Creative SB X-Fi boards with UAA (HD-audio) mode. In the HD-audio mode, no multiple streams are supported by just it behaves like a normal HD-audio device. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 13 + sound/pci/hda/Makefile | 4 + sound/pci/hda/hda_codec.c | 1 + sound/pci/hda/hda_intel.c | 5 + sound/pci/hda/patch_ca0110.c | 574 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 597 insertions(+) create mode 100644 sound/pci/hda/patch_ca0110.c (limited to 'sound/pci') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index eb2a19b894a0..c710150d5065 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -139,6 +139,19 @@ config SND_HDA_CODEC_CONEXANT snd-hda-codec-conexant. This module is automatically loaded at probing. +config SND_HDA_CODEC_CA0110 + bool "Build Creative CA0110-IBG codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Creative CA0110-IBG codec support in + snd-hda-intel driver, found on some Creative X-Fi cards. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-ca0110. + This module is automatically loaded at probing. + config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 50f9d0967251..e3081d4586cc 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-atihdmi-objs := patch_atihdmi.o +snd-hda-codec-ca0110-objs := patch_ca0110.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o @@ -40,6 +41,9 @@ endif ifdef CONFIG_SND_HDA_CODEC_ATIHDMI obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o endif +ifdef CONFIG_SND_HDA_CODEC_CA0110 +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o +endif ifdef CONFIG_SND_HDA_CODEC_CONEXANT obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index fd6e6f337d10..37f24ce7c3a2 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -48,6 +48,7 @@ static struct hda_vendor_id hda_vendor_ids[] = { { 0x1095, "Silicon Image" }, { 0x10de, "Nvidia" }, { 0x10ec, "Realtek" }, + { 0x1102, "Creative" }, { 0x1106, "VIA" }, { 0x111d, "IDT" }, { 0x11c1, "LSI" }, diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 21e99cfa8c49..21a3092fad00 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2513,6 +2513,11 @@ static struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA }, /* Teradici */ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, + /* Creative X-Fi (CA0110-IBG) */ + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_GENERIC }, /* AMD Generic, PCI class code and Vendor ID for HD Audio */ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c new file mode 100644 index 000000000000..7ec41daa3f0c --- /dev/null +++ b/sound/pci/hda/patch_ca0110.c @@ -0,0 +1,574 @@ +/* + * HD audio interface patch for Creative X-Fi CA0110-IBG chip + * + * Copyright (c) 2008 Takashi Iwai + * + * This 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; either version 2 of the License, or + * (at your option) any later version. + * + * This 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + +/* + */ + +struct ca0110_spec { + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; + hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; + hda_nid_t hp_dac; + hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t adcs[AUTO_PIN_LAST]; + hda_nid_t dig_out; + hda_nid_t dig_in; + unsigned int num_inputs; + const char *input_labels[AUTO_PIN_LAST]; + struct hda_pcm pcm_rec[2]; /* PCM information */ +}; + +/* + * PCM callbacks + */ +static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +/* + * Analog capture + */ +static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adcs[substream->number], + stream_tag, 0, format); + return 0; +} + +static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + + snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]); + return 0; +} + +/* + */ + +static char *dirstr[2] = { "Playback", "Capture" }; + +static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); +} + +static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); +} + +#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) +#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) +#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) +#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) +#define add_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 0) +#define add_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 0) + +static int ca0110_build_controls(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + static char *prefix[AUTO_CFG_MAX_OUTS] = { + "Front", "Surround", NULL, "Side", "Multi" + }; + hda_nid_t mutenid; + int i, err; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP) + mutenid = spec->out_pins[i]; + else + mutenid = spec->multiout.dac_nids[i]; + if (!prefix[i]) { + err = add_mono_switch(codec, mutenid, + "Center", 1); + if (err < 0) + return err; + err = add_mono_switch(codec, mutenid, + "LFE", 1); + if (err < 0) + return err; + err = add_mono_volume(codec, spec->multiout.dac_nids[i], + "Center", 1); + if (err < 0) + return err; + err = add_mono_volume(codec, spec->multiout.dac_nids[i], + "LFE", 1); + if (err < 0) + return err; + } else { + err = add_out_switch(codec, mutenid, + prefix[i]); + if (err < 0) + return err; + err = add_out_volume(codec, spec->multiout.dac_nids[i], + prefix[i]); + if (err < 0) + return err; + } + } + if (cfg->hp_outs) { + if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP) + mutenid = cfg->hp_pins[0]; + else + mutenid = spec->multiout.dac_nids[i]; + + err = add_out_switch(codec, mutenid, "Headphone"); + if (err < 0) + return err; + if (spec->hp_dac) { + err = add_out_volume(codec, spec->hp_dac, "Headphone"); + if (err < 0) + return err; + } + } + for (i = 0; i < spec->num_inputs; i++) { + const char *label = spec->input_labels[i]; + if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP) + mutenid = spec->input_pins[i]; + else + mutenid = spec->adcs[i]; + err = add_in_switch(codec, mutenid, label); + if (err < 0) + return err; + err = add_in_volume(codec, spec->adcs[i], label); + if (err < 0) + return err; + } + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); + if (err < 0) + return err; + err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; + } + if (spec->dig_in) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + err = add_in_volume(codec, spec->dig_in, "IEC958"); + } + return 0; +} + +/* + */ +static struct hda_pcm_stream ca0110_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .ops = { + .open = ca0110_playback_pcm_open, + .prepare = ca0110_playback_pcm_prepare, + .cleanup = ca0110_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0110_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0110_capture_pcm_prepare, + .cleanup = ca0110_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream ca0110_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ca0110_dig_playback_pcm_open, + .close = ca0110_dig_playback_pcm_close, + .prepare = ca0110_dig_playback_pcm_prepare + }, +}; + +static struct hda_pcm_stream ca0110_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int ca0110_build_pcms(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "CA0110 Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.num_dacs * 2; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + codec->num_pcms++; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info++; + info->name = "CA0110 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0110_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0110_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + if (dac) + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); +} + +static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) +{ + if (pin) { + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); + if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + if (adc) + snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); +} + +static int ca0110_init(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) + init_output(codec, spec->out_pins[i], + spec->multiout.dac_nids[i]); + init_output(codec, cfg->hp_pins[0], spec->hp_dac); + init_output(codec, cfg->dig_out_pins[0], spec->dig_out); + + for (i = 0; i < spec->num_inputs; i++) + init_input(codec, spec->input_pins[i], spec->adcs[i]); + init_input(codec, cfg->dig_in_pin, spec->dig_in); + return 0; +} + +static void ca0110_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +static struct hda_codec_ops ca0110_patch_ops = { + .build_controls = ca0110_build_controls, + .build_pcms = ca0110_build_pcms, + .init = ca0110_init, + .free = ca0110_free, +}; + + +static void parse_line_outs(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, n; + unsigned int def_conf; + hda_nid_t nid; + + n = 0; + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + if (!def_conf) + continue; /* invalid pin */ + if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1) + continue; + spec->out_pins[n++] = nid; + } + spec->multiout.dac_nids = spec->dacs; + spec->multiout.num_dacs = n; +} + +static void parse_hp_out(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + unsigned int def_conf; + hda_nid_t nid, dac; + + if (!cfg->hp_outs) + return; + nid = cfg->hp_pins[0]; + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + if (!def_conf) { + cfg->hp_outs = 0; + return; + } + if (snd_hda_get_connections(codec, nid, &dac, 1) != 1) + return; + + for (i = 0; i < cfg->line_outs; i++) + if (dac == spec->dacs[i]) + break; + if (i >= cfg->line_outs) { + spec->hp_dac = dac; + spec->multiout.hp_nid = dac; + } +} + +static void parse_input(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid, pin; + int n, i, j; + + n = 0; + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = (wcaps & AC_WCAP_TYPE) >> + AC_WCAP_TYPE_SHIFT; + if (type != AC_WID_AUD_IN) + continue; + if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) + continue; + if (pin == cfg->dig_in_pin) { + spec->dig_in = nid; + continue; + } + for (j = 0; j < AUTO_PIN_LAST; j++) + if (cfg->input_pins[j] == pin) + break; + if (j >= AUTO_PIN_LAST) + continue; + spec->input_pins[n] = pin; + spec->input_labels[n] = auto_pin_cfg_labels[j]; + spec->adcs[n] = nid; + n++; + } + spec->num_inputs = n; +} + +static void parse_digital(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (cfg->dig_outs && + snd_hda_get_connections(codec, cfg->dig_out_pins[0], + &spec->dig_out, 1) == 1) + spec->multiout.dig_out_nid = cfg->dig_out_pins[0]; +} + +static int ca0110_parse_auto_config(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + parse_line_outs(codec); + parse_hp_out(codec); + parse_digital(codec); + parse_input(codec); + return 0; +} + + +int patch_ca0110(struct hda_codec *codec) +{ + struct ca0110_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + codec->bus->needs_damn_long_delay = 1; + + err = ca0110_parse_auto_config(codec); + if (err < 0) + goto error; + + codec->patch_ops = ca0110_patch_ops; + + return 0; + + error: + kfree(codec->spec); + codec->spec = NULL; + return err; +} + + +/* + * patch entries + */ +static struct hda_codec_preset snd_hda_preset_ca0110[] = { + { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 }, + { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 }, + { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 }, + {} /* terminator */ +}; + +MODULE_ALIAS("snd-hda-codec-id:1102000a"); +MODULE_ALIAS("snd-hda-codec-id:1102000b"); +MODULE_ALIAS("snd-hda-codec-id:1102000d"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); + +static struct hda_codec_preset_list ca0110_list = { + .preset = snd_hda_preset_ca0110, + .owner = THIS_MODULE, +}; + +static int __init patch_ca0110_init(void) +{ + return snd_hda_add_codec_preset(&ca0110_list); +} + +static void __exit patch_ca0110_exit(void) +{ + snd_hda_delete_codec_preset(&ca0110_list); +} + +module_init(patch_ca0110_init) +module_exit(patch_ca0110_exit) -- cgit v1.2.1 From 18cb7109d3e83195b605ff2905981020e86f72ca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Apr 2009 10:22:24 +0200 Subject: ALSA: hda - Check strcpy length Check the length to copy via strlen() beforehand to avoid the stack corruption, or use strlcpy() to be safe in HD-audio codes. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/hda_intel.c | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 37f24ce7c3a2..48f0cea7df14 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1431,6 +1431,8 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec, memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; id.index = idx; + if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) + return NULL; strcpy(id.name, name); return snd_ctl_find_id(codec->bus->card, &id); } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 21a3092fad00..41db5d4da478 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1830,7 +1830,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, &pcm); if (err < 0) return err; - strcpy(pcm->name, cpcm->name); + strlcpy(pcm->name, cpcm->name, sizeof(pcm->name)); apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); if (apcm == NULL) return -ENOMEM; @@ -2358,9 +2358,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, } strcpy(card->driver, "HDA-Intel"); - strcpy(card->shortname, driver_short_names[chip->driver_type]); - sprintf(card->longname, "%s at 0x%lx irq %i", - card->shortname, chip->addr, chip->irq); + strlcpy(card->shortname, driver_short_names[chip->driver_type], + sizeof(card->shortname)); + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx irq %i", + card->shortname, chip->addr, chip->irq); *rchip = chip; return 0; -- cgit v1.2.1 From 67667263674663767ddf4250bab2437a00ee780e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Apr 2009 10:49:25 +0200 Subject: ALSA: hda - Fix channels_max setting for CA0110 Added the missing definition of max channels for CA0110, which resulted in an error at opening PCM devices. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0110.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 7ec41daa3f0c..9398d92f18bd 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -309,7 +309,7 @@ static int ca0110_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.num_dacs * 2; + spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; @@ -418,6 +418,7 @@ static void parse_line_outs(struct hda_codec *codec) } spec->multiout.dac_nids = spec->dacs; spec->multiout.num_dacs = n; + spec->multiout.max_channels = n * 2; } static void parse_hp_out(struct hda_codec *codec) -- cgit v1.2.1 From 7670dc41b51983b369f9adfb8694a580e7b0cef2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Apr 2009 10:51:11 +0200 Subject: ALSA: hda - Use snd_hda_codec_get_pincfg() in patch_ca0110.c Use the new function to reduce the access and allow the user setup via sysfs, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0110.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 9398d92f18bd..392d108c3558 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -408,8 +408,7 @@ static void parse_line_outs(struct hda_codec *codec) n = 0; for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; - def_conf = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); + def_conf = snd_hda_codec_get_pincfg(codec, nid); if (!def_conf) continue; /* invalid pin */ if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1) @@ -432,8 +431,7 @@ static void parse_hp_out(struct hda_codec *codec) if (!cfg->hp_outs) return; nid = cfg->hp_pins[0]; - def_conf = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); + def_conf = snd_hda_codec_get_pincfg(codec, nid); if (!def_conf) { cfg->hp_outs = 0; return; -- cgit v1.2.1 From 92c7c8a7d6e03eb4c0a3c5888e35dbc45f24744c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Mar 2009 07:32:14 +0100 Subject: ALSA: hda - Cache PCM and STREAM parameters queries Cache quries for PCM and STREAM parameters as well as ampcap and pincap sharing the hash table. This will reduce the superfluous access of the same codec verbs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 97 +++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 36 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a4e5e5952115..3d8bf39e6d98 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1053,6 +1053,8 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); /* FIXME: more better hash key? */ #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24)) +#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24)) +#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24)) #define INFO_AMP_CAPS (1<<0) #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) @@ -1143,19 +1145,32 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, } EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) +static unsigned int +query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key, + unsigned int (*func)(struct hda_codec *, hda_nid_t)) { struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid)); + info = get_alloc_amp_hash(codec, key); if (!info) return 0; if (!info->head.val) { - info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); info->head.val |= INFO_AMP_CAPS; + info->amp_caps = func(codec, nid); } return info->amp_caps; } + +static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); +} + +u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) +{ + return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), + read_pin_cap); +} EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); /* @@ -2538,6 +2553,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, } EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); +static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val = 0; + if (nid != codec->afg && + (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) + val = snd_hda_param_read(codec, nid, AC_PAR_PCM); + if (!val || val == -1) + val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); + if (!val || val == -1) + return 0; + return val; +} + +static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) +{ + return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid), + get_pcm_param); +} + +static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + if (!streams || streams == -1) + streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); + if (!streams || streams == -1) + return 0; + return streams; +} + +static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) +{ + return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid), + get_stream_param); +} + /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec @@ -2556,15 +2606,8 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, { unsigned int i, val, wcaps; - val = 0; wcaps = get_wcaps(codec, nid); - if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) { - val = snd_hda_param_read(codec, nid, AC_PAR_PCM); - if (val == -1) - return -EIO; - } - if (!val) - val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); + val = query_pcm_param(codec, nid); if (ratesp) { u32 rates = 0; @@ -2586,15 +2629,9 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u64 formats = 0; unsigned int streams, bps; - streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); - if (streams == -1) + streams = query_stream_param(codec, nid); + if (!streams) return -EIO; - if (!streams) { - streams = snd_hda_param_read(codec, codec->afg, - AC_PAR_STREAM); - if (streams == -1) - return -EIO; - } bps = 0; if (streams & AC_SUPFMT_PCM) { @@ -2668,17 +2705,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, int i; unsigned int val = 0, rate, stream; - if (nid != codec->afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) { - val = snd_hda_param_read(codec, nid, AC_PAR_PCM); - if (val == -1) - return 0; - } - if (!val) { - val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); - if (val == -1) - return 0; - } + val = query_pcm_param(codec, nid); + if (!val) + return 0; rate = format & 0xff00; for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) @@ -2690,12 +2719,8 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, if (i >= AC_PAR_PCM_RATE_BITS) return 0; - stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); - if (stream == -1) - return 0; - if (!stream && nid != codec->afg) - stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); - if (!stream || stream == -1) + stream = query_stream_param(codec, nid); + if (!stream) return 0; if (stream & AC_SUPFMT_PCM) { -- cgit v1.2.1 From b613291fb21a2d74eb8323d97fe9aa5d281b306c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Mar 2009 07:36:09 +0100 Subject: ALSA: hda - Retry codec-verbs at errors The current error-recovery scheme for the codec communication errors doesn't work always well. Especially falling back to the single-command mode causes the fatal problem on many systems. In this patch, the problematic verb is re-issued again after the error (even with polling mode) instead of the single-cmd mode. The single-cmd mode will be used only when specified via the command option explicitly, mainly just for testing. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 17 +++++++++++++---- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_intel.c | 19 ++++++++++--------- 3 files changed, 24 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3d8bf39e6d98..1736ccbebc72 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -174,14 +174,23 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, unsigned int verb, unsigned int parm) { struct hda_bus *bus = codec->bus; - unsigned int res; + unsigned int cmd, res; + int repeated = 0; - res = make_codec_cmd(codec, nid, direct, verb, parm); + cmd = make_codec_cmd(codec, nid, direct, verb, parm); snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - if (!bus->ops.command(bus, res)) + again: + if (!bus->ops.command(bus, cmd)) { res = bus->ops.get_response(bus); - else + if (res == -1 && bus->rirb_error) { + if (repeated++ < 1) { + snd_printd(KERN_WARNING "hda_codec: " + "Trying verb 0x%08x again\n", cmd); + goto again; + } + } + } else res = (unsigned int)-1; mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2fdecf4b0eb6..cd8979c7670b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -623,6 +623,7 @@ struct hda_bus { /* misc op flags */ unsigned int needs_damn_long_delay :1; unsigned int shutdown :1; /* being unloaded */ + unsigned int rirb_error:1; /* error in codec communication */ }; /* diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 30829ee920c3..803b72098ed3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -604,6 +604,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) } if (!chip->rirb.cmds) { smp_rmb(); + bus->rirb_error = 0; return chip->rirb.res; /* the last value */ } if (time_after(jiffies, timeout)) @@ -623,8 +624,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) chip->irq = -1; pci_disable_msi(chip->pci); chip->msi = 0; - if (azx_acquire_irq(chip, 1) < 0) + if (azx_acquire_irq(chip, 1) < 0) { + bus->rirb_error = 1; return -1; + } goto again; } @@ -644,14 +647,12 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) return -1; } - snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " - "switching to single_cmd mode: last cmd=0x%08x\n", - chip->last_cmd); - chip->rirb.rp = azx_readb(chip, RIRBWP); - chip->rirb.cmds = 0; - /* switch to single_cmd mode */ - chip->single_cmd = 1; - azx_free_cmd_io(chip); + snd_printk(KERN_ERR "hda_intel: azx_get_response timeout (ERROR): " + "last cmd=0x%08x\n", chip->last_cmd); + spin_lock_irq(&chip->reg_lock); + chip->rirb.cmds = 0; /* reset the index */ + bus->rirb_error = 1; + spin_unlock_irq(&chip->reg_lock); return -1; } -- cgit v1.2.1 From 586be3fcf97eec22fbc0ef6d67e823706aea7167 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Mar 2009 07:43:24 +0100 Subject: ALSA: hda - Add debug prints for Realtek auto-init Added a couple of debug prints to show the checked id numbers in alc_subsystem_id(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 82097790f6f3..ee92c73df083 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1022,6 +1022,9 @@ static void alc_subsystem_id(struct hda_codec *codec, if (codec->vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); + snd_printd("realtek: No valid SSID, " + "checking pincfg 0x%08x for NID 0x%x\n", + nid, ass); if (!(ass & 1) && !(ass & 0x100000)) return; if ((ass >> 30) != 1) /* no physical connection */ @@ -1036,6 +1039,8 @@ static void alc_subsystem_id(struct hda_codec *codec, if (((ass >> 16) & 0xf) != tmp) return; do_sku: + snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", + ass & 0xffff, codec->vendor_id); /* * 0 : override * 1 : Swap Jack -- cgit v1.2.1 From a3b48c88f2d5a34c0e25aec0a3dab8069e5a9a72 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Apr 2009 13:37:29 +0200 Subject: ALSA: hda - minor optimization in hda_set_power_state() Check the target power-state before checking EAPD exception to reduce unneeded verb executions. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b649033a4c81..b91f6ed5cc58 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2348,7 +2348,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, if (wcaps & AC_WCAP_POWER) { unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wid_type == AC_WID_PIN) { + if (power_state == AC_PWRST_D3 && + wid_type == AC_WID_PIN) { unsigned int pincap; /* * don't power down the widget if it controls @@ -2360,7 +2361,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, nid, 0, AC_VERB_GET_EAPD_BTLENABLE, 0); eapd &= 0x02; - if (power_state == AC_PWRST_D3 && eapd) + if (eapd) continue; } } -- cgit v1.2.1 From dfed0ef9b3ff9e37903920b6938ed33344ad0b3d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Apr 2009 18:33:12 +0200 Subject: ALSA: hda - Fix a typo in debug print for realtek auto-detection The NID and ASS numbers were swapped... Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a6ec87a5c066..887712046c00 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1026,7 +1026,7 @@ static void alc_subsystem_id(struct hda_codec *codec, ass = snd_hda_codec_get_pincfg(codec, nid); snd_printd("realtek: No valid SSID, " "checking pincfg 0x%08x for NID 0x%x\n", - nid, ass); + ass, nid); if (!(ass & 1) && !(ass & 0x100000)) return; if ((ass >> 30) != 1) /* no physical connection */ -- cgit v1.2.1 From 4a79ba34cada6a5a4ee86ed53aa8a73ba1e6fc51 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Apr 2009 16:31:35 +0200 Subject: ALSA: hda - Add amp initialization for realtek auto mode In the realtek auto-probing mode, the initialization of amp with some magic COEF or EAPD verbs is applied only when the codec SSID has valid values to satisfy the realtek's definition. However, many devices don't provide in that way, thus the device doesn't work as is. This patch allows the same initialization code even if the SSID doesn't pass the bit test. Also, alc_subsystem_id() is changed just to check and define the type, so that it's called in the parser, instead of the initializer. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 239 +++++++++++++++++++++++++----------------- 1 file changed, 145 insertions(+), 94 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 583603f449b4..3a6306302c70 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -253,6 +253,15 @@ enum { /* for GPIO Poll */ #define GPIO_MASK 0x03 +/* extra amp-initialization sequence types */ +enum { + ALC_INIT_NONE, + ALC_INIT_DEFAULT, + ALC_INIT_GPIO1, + ALC_INIT_GPIO2, + ALC_INIT_GPIO3, +}; + struct alc_spec { /* codec parameterization */ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ @@ -322,6 +331,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ + int init_amp; /* for virtual master */ hda_nid_t vmaster_nid; @@ -994,74 +1004,21 @@ static void alc888_coef_init(struct hda_codec *codec) AC_VERB_SET_PROC_COEF, 0x3030); } -/* 32-bit subsystem ID for BIOS loading in HD Audio codec. - * 31 ~ 16 : Manufacture ID - * 15 ~ 8 : SKU ID - * 7 ~ 0 : Assembly ID - * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 - */ -static void alc_subsystem_id(struct hda_codec *codec, - unsigned int porta, unsigned int porte, - unsigned int portd) +static void alc_auto_init_amp(struct hda_codec *codec, int type) { - unsigned int ass, tmp, i; - unsigned nid; - struct alc_spec *spec = codec->spec; - - ass = codec->subsystem_id & 0xffff; - if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) - goto do_sku; - - /* - * 31~30 : port conetcivity - * 29~21 : reserve - * 20 : PCBEEP input - * 19~16 : Check sum (15:1) - * 15~1 : Custom - * 0 : override - */ - nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - snd_printd("realtek: No valid SSID, " - "checking pincfg 0x%08x for NID 0x%x\n", - ass, nid); - if (!(ass & 1) && !(ass & 0x100000)) - return; - if ((ass >> 30) != 1) /* no physical connection */ - return; + unsigned int tmp; - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return; -do_sku: - snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->vendor_id); - /* - * 0 : override - * 1 : Swap Jack - * 2 : 0 --> Desktop, 1 --> Laptop - * 3~5 : External Amplifier control - * 7~6 : Reserved - */ - tmp = (ass & 0x38) >> 3; /* external Amp control */ - switch (tmp) { - case 1: + switch (type) { + case ALC_INIT_GPIO1: snd_hda_sequence_write(codec, alc_gpio1_init_verbs); break; - case 3: + case ALC_INIT_GPIO2: snd_hda_sequence_write(codec, alc_gpio2_init_verbs); break; - case 7: + case ALC_INIT_GPIO3: snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; - case 5: /* set EAPD output high */ + case ALC_INIT_DEFAULT: switch (codec->vendor_id) { case 0x10ec0260: snd_hda_codec_write(codec, 0x0f, 0, @@ -1115,7 +1072,7 @@ do_sku: tmp | 0x2010); break; case 0x10ec0888: - /*alc888_coef_init(codec);*/ /* called in alc_init() */ + alc888_coef_init(codec); break; case 0x10ec0267: case 0x10ec0268: @@ -1130,7 +1087,104 @@ do_sku: tmp | 0x3000); break; } - default: + break; + } +} + +static void alc_init_auto_hp(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->autocfg.hp_pins[0]) + return; + + if (!spec->autocfg.speaker_pins[0]) { + if (spec->autocfg.line_out_pins[0]) + spec->autocfg.speaker_pins[0] = + spec->autocfg.line_out_pins[0]; + else + return; + } + + snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_HP_EVENT); + spec->unsol_event = alc_sku_unsol_event; +} + +/* check subsystem ID and set up device-specific initialization; + * return 1 if initialized, 0 if invalid SSID + */ +/* 32-bit subsystem ID for BIOS loading in HD Audio codec. + * 31 ~ 16 : Manufacture ID + * 15 ~ 8 : SKU ID + * 7 ~ 0 : Assembly ID + * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 + */ +static int alc_subsystem_id(struct hda_codec *codec, + hda_nid_t porta, hda_nid_t porte, + hda_nid_t portd) +{ + unsigned int ass, tmp, i; + unsigned nid; + struct alc_spec *spec = codec->spec; + + ass = codec->subsystem_id & 0xffff; + if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) + goto do_sku; + + /* invalid SSID, check the special NID pin defcfg instead */ + /* + * 31~30 : port conetcivity + * 29~21 : reserve + * 20 : PCBEEP input + * 19~16 : Check sum (15:1) + * 15~1 : Custom + * 0 : override + */ + nid = 0x1d; + if (codec->vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + snd_printd("realtek: No valid SSID, " + "checking pincfg 0x%08x for NID 0x%x\n", + nid, ass); + if (!(ass & 1) && !(ass & 0x100000)) + return 0; + if ((ass >> 30) != 1) /* no physical connection */ + return 0; + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return 0; +do_sku: + snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", + ass & 0xffff, codec->vendor_id); + /* + * 0 : override + * 1 : Swap Jack + * 2 : 0 --> Desktop, 1 --> Laptop + * 3~5 : External Amplifier control + * 7~6 : Reserved + */ + tmp = (ass & 0x38) >> 3; /* external Amp control */ + switch (tmp) { + case 1: + spec->init_amp = ALC_INIT_GPIO1; + break; + case 3: + spec->init_amp = ALC_INIT_GPIO2; + break; + case 7: + spec->init_amp = ALC_INIT_GPIO3; + break; + case 5: + spec->init_amp = ALC_INIT_DEFAULT; break; } @@ -1138,7 +1192,7 @@ do_sku: * when the external headphone out jack is plugged" */ if (!(ass & 0x8000)) - return; + return 1; /* * 10~8 : Jack location * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered @@ -1146,14 +1200,6 @@ do_sku: * 15 : 1 --> enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!spec->autocfg.speaker_pins[0]) { - if (spec->autocfg.line_out_pins[0]) - spec->autocfg.speaker_pins[0] = - spec->autocfg.line_out_pins[0]; - else - return; - } - if (!spec->autocfg.hp_pins[0]) { tmp = (ass >> 11) & 0x3; /* HP to chassis */ if (tmp == 0) @@ -1163,23 +1209,23 @@ do_sku: else if (tmp == 2) spec->autocfg.hp_pins[0] = portd; else - return; + return 1; } - if (spec->autocfg.hp_pins[0]) - snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_HP_EVENT); -#if 0 /* it's broken in some acses -- temporarily disabled */ - if (spec->autocfg.input_pins[AUTO_PIN_MIC] && - spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]) - snd_hda_codec_write(codec, - spec->autocfg.input_pins[AUTO_PIN_MIC], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_MIC_EVENT); -#endif /* disabled */ + alc_init_auto_hp(codec); + return 1; +} - spec->unsol_event = alc_sku_unsol_event; +static void alc_ssid_check(struct hda_codec *codec, + hda_nid_t porta, hda_nid_t porte, hda_nid_t portd) +{ + if (!alc_subsystem_id(codec, porta, porte, portd)) { + struct alc_spec *spec = codec->spec; + snd_printd("realtek: " + "Enable default setup for auto mode as fallback\n"); + spec->init_amp = ALC_INIT_DEFAULT; + alc_init_auto_hp(codec); + } } /* @@ -2923,8 +2969,7 @@ static int alc_init(struct hda_codec *codec) unsigned int i; alc_fix_pll(codec); - if (codec->vendor_id == 0x10ec0888) - alc888_coef_init(codec); + alc_auto_init_amp(codec, spec->init_amp); for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); @@ -4198,7 +4243,6 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i < spec->autocfg.line_outs; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -4303,6 +4347,8 @@ static int alc880_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + alc_ssid_check(codec, 0x15, 0x1b, 0x14); + return 1; } @@ -5678,7 +5724,6 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; hda_nid_t nid; - alc_subsystem_id(codec, 0x10, 0x15, 0x0f); nid = spec->autocfg.line_out_pins[0]; if (nid) { int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -5788,6 +5833,8 @@ static int alc260_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + alc_ssid_check(codec, 0x10, 0x15, 0x0f); + return 1; } @@ -7013,7 +7060,6 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -9154,7 +9200,6 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -9317,6 +9362,7 @@ static int patch_alc883(struct hda_codec *codec) if (!spec->capsrc_nids) spec->capsrc_nids = alc883_capsrc_nids; spec->capture_style = CAPT_MIX; /* matrix-style capture */ + spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */ break; case 0x10ec0889: spec->stream_name_analog = "ALC889 Analog"; @@ -10842,6 +10888,8 @@ static int alc262_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + alc_ssid_check(codec, 0x15, 0x14, 0x1b); + return 1; } @@ -13925,7 +13973,6 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - alc_subsystem_id(codec, 0x0e, 0x0f, 0x0b); for (i = 0; i < spec->autocfg.line_outs; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -14008,6 +14055,8 @@ static int alc861_parse_auto_config(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); set_capture_mixer(spec); + alc_ssid_check(codec, 0x0e, 0x0f, 0x0b); + return 1; } @@ -14889,7 +14938,6 @@ static void alc861vd_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -15107,6 +15155,8 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + alc_ssid_check(codec, 0x15, 0x1b, 0x14); + return 1; } @@ -16931,7 +16981,6 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; - alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -17028,6 +17077,8 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + alc_ssid_check(codec, 0x15, 0x1b, 0x14); + return 1; } -- cgit v1.2.1 From 2a2ed0dfc9ec44a899c7d4672f73f2c045099118 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Apr 2009 13:01:26 +0200 Subject: ALSA: hda - Don't enable auto-mute but for speakers in patch_realtek.c Enable auto-muting in model=auto only for devices with HP and speakers. For devices with HP and line-outs, don't enable the auto-muting. Also, add a debug print to show the auto-mute feature. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3a6306302c70..96475dc95fbb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1099,13 +1099,16 @@ static void alc_init_auto_hp(struct hda_codec *codec) return; if (!spec->autocfg.speaker_pins[0]) { - if (spec->autocfg.line_out_pins[0]) + if (spec->autocfg.line_out_pins[0] && + spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) spec->autocfg.speaker_pins[0] = spec->autocfg.line_out_pins[0]; else return; } + snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", + spec->autocfg.hp_pins[0]); snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT); -- cgit v1.2.1 From cb6605c1e4d2a2eaffdde433fbfe1567ca688458 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Apr 2009 13:03:19 +0200 Subject: ALSA: hda - Fix a typo in patch_realtek.c again The commmit dfed0ef9b3ff9e37903920b6938ed33344ad0b3d was reverted accidentally by the merge of auto-detection fix patch. Fixed again now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 96475dc95fbb..3e7207b927c8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1151,7 +1151,7 @@ static int alc_subsystem_id(struct hda_codec *codec, ass = snd_hda_codec_get_pincfg(codec, nid); snd_printd("realtek: No valid SSID, " "checking pincfg 0x%08x for NID 0x%x\n", - nid, ass); + ass, nid); if (!(ass & 1) && !(ass & 0x100000)) return 0; if ((ass >> 30) != 1) /* no physical connection */ -- cgit v1.2.1 From 514bf54cd8c7f172816d3c003a6d022e9165a29b Mon Sep 17 00:00:00 2001 From: James Gardiner Date: Sun, 3 May 2009 04:00:44 -0400 Subject: ALSA: hda - Addition for HP dv4-1222nr laptop support Signed-off-by: James Gardiner Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 44 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 917bc5d3ac2c..76487de33c84 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -100,6 +100,7 @@ enum { STAC_HP_M4, STAC_HP_DV5, STAC_HP_HDX, + STAC_HP_DV4_1222NR, STAC_92HD71BXX_MODELS }; @@ -1836,6 +1837,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { [STAC_HP_M4] = NULL, [STAC_HP_DV5] = NULL, [STAC_HP_HDX] = NULL, + [STAC_HP_DV4_1222NR] = NULL, }; static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { @@ -1847,6 +1849,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { [STAC_HP_M4] = "hp-m4", [STAC_HP_DV5] = "hp-dv5", [STAC_HP_HDX] = "hp-hdx", + [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", }; static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { @@ -1855,6 +1858,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "DFI LanParty", STAC_92HD71BXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_92HD71BXX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb, + "HP dv4-1222nr", STAC_HP_DV4_1222NR), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, "HP", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, @@ -4520,27 +4525,38 @@ static int stac92xx_resume(struct hda_codec *codec) return 0; } - /* - * using power check for controlling mute led of HP HDX notebooks + * using power check for controlling mute led of HP notebooks * check for mute state only on Speakers (nid = 0x10) * * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise * the LED is NOT working properly ! + * + * Changed name to reflect that it now works for any designated + * model, not just HP HDX. */ #ifdef CONFIG_SND_HDA_POWER_SAVE -static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec, +static int stac92xx_hp_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct sigmatel_spec *spec = codec->spec; + unsigned int gpio_bit = 0; /* gets rid of compiler warning */ + + switch (spec->board_config) { + case STAC_HP_DV4_1222NR: + gpio_bit = 0x01; + break; + case STAC_HP_HDX: + gpio_bit = 0x08; + } if (nid == 0x10) { if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) - spec->gpio_data &= ~0x08; /* orange */ + spec->gpio_data &= ~gpio_bit; /* orange */ else - spec->gpio_data |= 0x08; /* white */ + spec->gpio_data |= gpio_bit; /* white */ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, @@ -5219,6 +5235,22 @@ again: spec->num_smuxes = 0; spec->num_dmuxes = 1; break; + case STAC_HP_DV4_1222NR: + spec->num_dmics = 1; + /* I don't know if it needs 1 or 2 smuxes - will wait for + * bug reports to fix if needed + */ + spec->num_smuxes = 1; + spec->num_dmuxes = 1; +#ifdef CONFIG_SND_HDA_POWER_SAVE + /* This controls MUTE LED */ + spec->gpio_mask |= 0x01; + spec->gpio_dir |= 0x01; + spec->gpio_data |= 0x01; + codec->patch_ops.check_power_status = + stac92xx_hp_check_power_status; +#endif + /* fallthrough */ case STAC_HP_DV5: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); @@ -5239,7 +5271,7 @@ again: /* register check_power_status callback. */ codec->patch_ops.check_power_status = - stac92xx_hp_hdx_check_power_status; + stac92xx_hp_check_power_status; #endif break; }; -- cgit v1.2.1 From 92d71005e2f305d6dca126d8b8497e885b842dba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 May 2009 17:18:34 +0200 Subject: ALSA: ice1724 - Check error in set_rate function The set_rate might return error but the current code doesn't check it. This patch adds a proper error check. Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1724.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 128510e77a78..5c5ef7fa3e83 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -626,7 +626,7 @@ static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice, return 0; } -static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, +static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, int force) { unsigned long flags; @@ -634,17 +634,18 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, unsigned int i, old_rate; if (rate > ice->hw_rates->list[ice->hw_rates->count - 1]) - return; + return -EINVAL; + spin_lock_irqsave(&ice->reg_lock, flags); if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { /* running? we cannot change the rate now... */ spin_unlock_irqrestore(&ice->reg_lock, flags); - return; + return -EBUSY; } if (!force && is_pro_rate_locked(ice)) { spin_unlock_irqrestore(&ice->reg_lock, flags); - return; + return (rate == ice->cur_rate) ? 0 : -EBUSY; } old_rate = ice->get_rate(ice); @@ -652,7 +653,7 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, ice->set_rate(ice, rate); else if (rate == ice->cur_rate) { spin_unlock_irqrestore(&ice->reg_lock, flags); - return; + return 0; } ice->cur_rate = rate; @@ -674,13 +675,15 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, } if (ice->spdif.ops.setup_rate) ice->spdif.ops.setup_rate(ice, rate); + + return 0; } static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); - int i, chs; + int i, chs, err; chs = params_channels(hw_params); mutex_lock(&ice->open_mutex); @@ -715,7 +718,11 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream, } } mutex_unlock(&ice->open_mutex); - snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0); + + err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0); + if (err < 0) + return err; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } -- cgit v1.2.1 From a5b7b5c1d05387ffeaf0487482806ec6c5968ac7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 May 2009 17:22:07 +0200 Subject: ALSA: ice1724 - Clean up definitions of DMA records Rename some vt1724_pcm_reg records to more generic and consistent ones. Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1724.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 5c5ef7fa3e83..8afa043dab0e 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -855,20 +855,39 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr #endif } -static const struct vt1724_pcm_reg vt1724_playback_pro_reg = { +static const struct vt1724_pcm_reg vt1724_pdma0_reg = { .addr = VT1724_MT_PLAYBACK_ADDR, .size = VT1724_MT_PLAYBACK_SIZE, .count = VT1724_MT_PLAYBACK_COUNT, .start = VT1724_PDMA0_START, }; -static const struct vt1724_pcm_reg vt1724_capture_pro_reg = { +static const struct vt1724_pcm_reg vt1724_pdma4_reg = { + .addr = VT1724_MT_PDMA4_ADDR, + .size = VT1724_MT_PDMA4_SIZE, + .count = VT1724_MT_PDMA4_COUNT, + .start = VT1724_PDMA4_START, +}; + +static const struct vt1724_pcm_reg vt1724_rdma0_reg = { .addr = VT1724_MT_CAPTURE_ADDR, .size = VT1724_MT_CAPTURE_SIZE, .count = VT1724_MT_CAPTURE_COUNT, .start = VT1724_RDMA0_START, }; +static const struct vt1724_pcm_reg vt1724_rdma1_reg = { + .addr = VT1724_MT_RDMA1_ADDR, + .size = VT1724_MT_RDMA1_SIZE, + .count = VT1724_MT_RDMA1_COUNT, + .start = VT1724_RDMA1_START, +}; + +#define vt1724_playback_pro_reg vt1724_pdma0_reg +#define vt1724_playback_spdif_reg vt1724_pdma4_reg +#define vt1724_capture_pro_reg vt1724_rdma0_reg +#define vt1724_capture_spdif_reg vt1724_rdma1_reg + static const struct snd_pcm_hardware snd_vt1724_playback_pro = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -1084,20 +1103,6 @@ static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device) * SPDIF PCM */ -static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = { - .addr = VT1724_MT_PDMA4_ADDR, - .size = VT1724_MT_PDMA4_SIZE, - .count = VT1724_MT_PDMA4_COUNT, - .start = VT1724_PDMA4_START, -}; - -static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = { - .addr = VT1724_MT_RDMA1_ADDR, - .size = VT1724_MT_RDMA1_SIZE, - .count = VT1724_MT_RDMA1_COUNT, - .start = VT1724_RDMA1_START, -}; - /* update spdif control bits; call with reg_lock */ static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val) { -- cgit v1.2.1 From d82b64f4764755d765038cf95b1dbe7db039592a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 May 2009 17:25:42 +0200 Subject: ALSA: ice1724 - Add PCI postint to reset sequence Add the PCI posting to ensure the reset sequence in snd_vt1724_chip_reset(). Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1724.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 8afa043dab0e..1f05c59690a1 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2258,8 +2258,10 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice, static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice) { outb(VT1724_RESET , ICEREG1724(ice, CONTROL)); + inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */ msleep(10); outb(0, ICEREG1724(ice, CONTROL)); + inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */ msleep(10); } -- cgit v1.2.1 From 2bf864ac963c5126a9c7c3dce0958390fb612270 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 May 2009 17:30:17 +0200 Subject: ALSA: ice1724 - Allow spec driver to create own routing controls Added a new flag, own_routing, to allow spec drivers to create own routing controls. Also, the basic get/put calls are changed to be external for later use by maya44 driver. Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1712.h | 12 ++++++++++-- sound/pci/ice1712/ice1724.c | 33 ++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 15 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index fdae6deba16b..adc909ec125c 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -335,6 +335,7 @@ struct snd_ice1712 { unsigned int force_rdma1:1; /* VT1720/4 - RDMA1 as non-spdif */ unsigned int midi_output:1; /* VT1720/4: MIDI output triggered */ unsigned int midi_input:1; /* VT1720/4: MIDI input triggered */ + unsigned int own_routing:1; /* VT1720/4: use own routing ctls */ unsigned int num_total_dacs; /* total DACs */ unsigned int num_total_adcs; /* total ADCs */ unsigned int cur_rate; /* current rate */ @@ -458,10 +459,17 @@ static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice, return snd_ice1712_gpio_read(ice) & mask; } +/* route access functions */ +int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift); +int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val, + int shift); + int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice); -int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template, - const struct snd_ak4xxx_private *priv, struct snd_ice1712 *ice); +int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, + const struct snd_akm4xxx *template, + const struct snd_ak4xxx_private *priv, + struct snd_ice1712 *ice); void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice); int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 1f05c59690a1..5a735eed317d 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1975,7 +1975,7 @@ static inline int digital_route_shift(int idx) return idx * 3; } -static int get_route_val(struct snd_ice1712 *ice, int shift) +int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift) { unsigned long val; unsigned char eitem; @@ -1994,7 +1994,8 @@ static int get_route_val(struct snd_ice1712 *ice, int shift) return eitem; } -static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift) +int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val, + int shift) { unsigned int old_val, nval; int change; @@ -2022,7 +2023,7 @@ static int snd_vt1724_pro_route_analog_get(struct snd_kcontrol *kcontrol, struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ucontrol->value.enumerated.item[0] = - get_route_val(ice, analog_route_shift(idx)); + snd_ice1724_get_route_val(ice, analog_route_shift(idx)); return 0; } @@ -2031,8 +2032,9 @@ static int snd_vt1724_pro_route_analog_put(struct snd_kcontrol *kcontrol, { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return put_route_val(ice, ucontrol->value.enumerated.item[0], - analog_route_shift(idx)); + return snd_ice1724_put_route_val(ice, + ucontrol->value.enumerated.item[0], + analog_route_shift(idx)); } static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol, @@ -2041,7 +2043,7 @@ static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ucontrol->value.enumerated.item[0] = - get_route_val(ice, digital_route_shift(idx)); + snd_ice1724_get_route_val(ice, digital_route_shift(idx)); return 0; } @@ -2050,11 +2052,13 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol, { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return put_route_val(ice, ucontrol->value.enumerated.item[0], - digital_route_shift(idx)); + return snd_ice1724_put_route_val(ice, + ucontrol->value.enumerated.item[0], + digital_route_shift(idx)); } -static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = { +static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Playback Route", .info = snd_vt1724_pro_route_info, @@ -2291,9 +2295,12 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice) if (snd_BUG_ON(!ice->pcm)) return -EIO; - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice)); - if (err < 0) - return err; + if (!ice->own_routing) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice)); + if (err < 0) + return err; + } err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice)); if (err < 0) @@ -2340,7 +2347,7 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice) if (err < 0) return err; - if (ice->num_total_dacs > 0) { + if (!ice->own_routing && ice->num_total_dacs > 0) { struct snd_kcontrol_new tmp = snd_vt1724_mixer_pro_analog_route; tmp.count = ice->num_total_dacs; if (ice->vt1720 && tmp.count > 2) -- cgit v1.2.1 From 72cbfd45fac627de4bd38c203baaac40cd26477c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 6 May 2009 17:33:19 +0200 Subject: ALSA: ice1724 - Add ESI Maya44 support Added the support for ESI Maya44 board to ice1724 driver. Signed-off-by: Takashi Iwai --- sound/pci/ice1712/Makefile | 2 +- sound/pci/ice1712/ice1724.c | 3 + sound/pci/ice1712/maya44.c | 779 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/ice1712/maya44.h | 10 + 4 files changed, 793 insertions(+), 1 deletion(-) create mode 100644 sound/pci/ice1712/maya44.c create mode 100644 sound/pci/ice1712/maya44.h (limited to 'sound/pci') diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index f99fe089495d..536eae2ccf94 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile @@ -5,7 +5,7 @@ snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 5a735eed317d..36ade77cf371 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -49,6 +49,7 @@ #include "prodigy192.h" #include "prodigy_hifi.h" #include "juli.h" +#include "maya44.h" #include "phase.h" #include "wtm.h" #include "se.h" @@ -65,6 +66,7 @@ MODULE_SUPPORTED_DEVICE("{" PRODIGY192_DEVICE_DESC PRODIGY_HIFI_DEVICE_DESC JULI_DEVICE_DESC + MAYA44_DEVICE_DESC PHASE_DEVICE_DESC WTM_DEVICE_DESC SE_DEVICE_DESC @@ -2125,6 +2127,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_prodigy_hifi_cards, snd_vt1724_prodigy192_cards, snd_vt1724_juli_cards, + snd_vt1724_maya44_cards, snd_vt1724_phase_cards, snd_vt1724_wtm_cards, snd_vt1724_se_cards, diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c new file mode 100644 index 000000000000..3e1c20ae2f1c --- /dev/null +++ b/sound/pci/ice1712/maya44.c @@ -0,0 +1,779 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for ESI Maya44 cards + * + * Copyright (c) 2009 Takashi Iwai + * Based on the patches by Rainer Zimmermann + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "envy24ht.h" +#include "maya44.h" + +/* WM8776 register indexes */ +#define WM8776_REG_HEADPHONE_L 0x00 +#define WM8776_REG_HEADPHONE_R 0x01 +#define WM8776_REG_HEADPHONE_MASTER 0x02 +#define WM8776_REG_DAC_ATTEN_L 0x03 +#define WM8776_REG_DAC_ATTEN_R 0x04 +#define WM8776_REG_DAC_ATTEN_MASTER 0x05 +#define WM8776_REG_DAC_PHASE 0x06 +#define WM8776_REG_DAC_CONTROL 0x07 +#define WM8776_REG_DAC_MUTE 0x08 +#define WM8776_REG_DAC_DEEMPH 0x09 +#define WM8776_REG_DAC_IF_CONTROL 0x0a +#define WM8776_REG_ADC_IF_CONTROL 0x0b +#define WM8776_REG_MASTER_MODE_CONTROL 0x0c +#define WM8776_REG_POWERDOWN 0x0d +#define WM8776_REG_ADC_ATTEN_L 0x0e +#define WM8776_REG_ADC_ATTEN_R 0x0f +#define WM8776_REG_ADC_ALC1 0x10 +#define WM8776_REG_ADC_ALC2 0x11 +#define WM8776_REG_ADC_ALC3 0x12 +#define WM8776_REG_ADC_NOISE_GATE 0x13 +#define WM8776_REG_ADC_LIMITER 0x14 +#define WM8776_REG_ADC_MUX 0x15 +#define WM8776_REG_OUTPUT_MUX 0x16 +#define WM8776_REG_RESET 0x17 + +#define WM8776_NUM_REGS 0x18 + +/* clock ratio identifiers for snd_wm8776_set_rate() */ +#define WM8776_CLOCK_RATIO_128FS 0 +#define WM8776_CLOCK_RATIO_192FS 1 +#define WM8776_CLOCK_RATIO_256FS 2 +#define WM8776_CLOCK_RATIO_384FS 3 +#define WM8776_CLOCK_RATIO_512FS 4 +#define WM8776_CLOCK_RATIO_768FS 5 + +enum { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS }; +enum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES }; + +struct snd_wm8776 { + unsigned char addr; + unsigned short regs[WM8776_NUM_REGS]; + unsigned char volumes[WM_NUM_VOLS][2]; + unsigned int switch_bits; +}; + +struct snd_maya44 { + struct snd_ice1712 *ice; + struct snd_wm8776 wm[2]; + struct mutex mutex; +}; + + +/* write the given register and save the data to the cache */ +static void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm, + unsigned char reg, unsigned short val) +{ + /* + * WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB + * of the address field + */ + snd_vt1724_write_i2c(ice, wm->addr, + (reg << 1) | ((val >> 8) & 1), + val & 0xff); + wm->regs[reg] = val; +} + +/* + * update the given register with and/or mask and save the data to the cache + */ +static int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm, + unsigned char reg, + unsigned short mask, unsigned short val) +{ + val |= wm->regs[reg] & ~mask; + if (val != wm->regs[reg]) { + wm8776_write(ice, wm, reg, val); + return 1; + } + return 0; +} + + +/* + * WM8776 volume controls + */ + +struct maya_vol_info { + unsigned int maxval; /* volume range: 0..maxval */ + unsigned char regs[2]; /* left and right registers */ + unsigned short mask; /* value mask */ + unsigned short offset; /* zero-value offset */ + unsigned short mute; /* mute bit */ + unsigned short update; /* update bits */ + unsigned char mux_bits[2]; /* extra bits for ADC mute */ +}; + +static struct maya_vol_info vol_info[WM_NUM_VOLS] = { + [WM_VOL_HP] = { + .maxval = 80, + .regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R }, + .mask = 0x7f, + .offset = 0x30, + .mute = 0x00, + .update = 0x180, /* update and zero-cross enable */ + }, + [WM_VOL_DAC] = { + .maxval = 255, + .regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R }, + .mask = 0xff, + .offset = 0x01, + .mute = 0x00, + .update = 0x100, /* zero-cross enable */ + }, + [WM_VOL_ADC] = { + .maxval = 91, + .regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R }, + .mask = 0xff, + .offset = 0xa5, + .mute = 0xa5, + .update = 0x100, /* update */ + .mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */ + }, +}; + +/* + * dB tables + */ +/* headphone output: mute, -73..+6db (1db step) */ +static const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1); +/* DAC output: mute, -127..0db (0.5db step) */ +static const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1); +/* ADC gain: mute, -21..+24db (0.5db step) */ +static const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1); + +static int maya_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int idx = kcontrol->private_value; + struct maya_vol_info *vol = &vol_info[idx]; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = vol->maxval; + return 0; +} + +static int maya_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + struct snd_wm8776 *wm = + &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; + unsigned int idx = kcontrol->private_value; + + mutex_lock(&chip->mutex); + ucontrol->value.integer.value[0] = wm->volumes[idx][0]; + ucontrol->value.integer.value[1] = wm->volumes[idx][1]; + mutex_unlock(&chip->mutex); + return 0; +} + +static int maya_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + struct snd_wm8776 *wm = + &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; + unsigned int idx = kcontrol->private_value; + struct maya_vol_info *vol = &vol_info[idx]; + unsigned int val, data; + int ch, changed = 0; + + mutex_lock(&chip->mutex); + for (ch = 0; ch < 2; ch++) { + val = ucontrol->value.integer.value[ch]; + if (val > vol->maxval) + val = vol->maxval; + if (val == wm->volumes[idx][ch]) + continue; + if (!val) + data = vol->mute; + else + data = (val - 1) + vol->offset; + data |= vol->update; + changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch], + vol->mask | vol->update, data); + if (vol->mux_bits[ch]) + wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX, + vol->mux_bits[ch], + val ? 0 : vol->mux_bits[ch]); + wm->volumes[idx][ch] = val; + } + mutex_unlock(&chip->mutex); + return changed; +} + +/* + * WM8776 switch controls + */ + +#define COMPOSE_SW_VAL(idx, reg, mask) ((idx) | ((reg) << 8) | ((mask) << 16)) +#define GET_SW_VAL_IDX(val) ((val) & 0xff) +#define GET_SW_VAL_REG(val) (((val) >> 8) & 0xff) +#define GET_SW_VAL_MASK(val) (((val) >> 16) & 0xff) + +#define maya_sw_info snd_ctl_boolean_mono_info + +static int maya_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + struct snd_wm8776 *wm = + &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; + unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value); + + ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1; + return 0; +} + +static int maya_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + struct snd_wm8776 *wm = + &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)]; + unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value); + unsigned int mask, val; + int changed; + + mutex_lock(&chip->mutex); + mask = 1 << idx; + wm->switch_bits &= ~mask; + val = ucontrol->value.integer.value[0]; + if (val) + wm->switch_bits |= mask; + mask = GET_SW_VAL_MASK(kcontrol->private_value); + changed = wm8776_write_bits(chip->ice, wm, + GET_SW_VAL_REG(kcontrol->private_value), + mask, val ? mask : 0); + mutex_unlock(&chip->mutex); + return changed; +} + +/* + * GPIO pins (known ones for maya44) + */ +#define GPIO_PHANTOM_OFF 2 +#define GPIO_MIC_RELAY 4 +#define GPIO_SPDIF_IN_INV 5 +#define GPIO_MUST_BE_0 7 + +/* + * GPIO switch controls + */ + +#define COMPOSE_GPIO_VAL(shift, inv) ((shift) | ((inv) << 8)) +#define GET_GPIO_VAL_SHIFT(val) ((val) & 0xff) +#define GET_GPIO_VAL_INV(val) (((val) >> 8) & 1) + +static int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask, + unsigned int bits) +{ + unsigned int data; + data = snd_ice1712_gpio_read(ice); + if ((data & mask) == bits) + return 0; + snd_ice1712_gpio_write(ice, (data & ~mask) | bits); + return 1; +} + +#define maya_gpio_sw_info snd_ctl_boolean_mono_info + +static int maya_gpio_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value); + unsigned int val; + + val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1; + if (GET_GPIO_VAL_INV(kcontrol->private_value)) + val = !val; + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value); + unsigned int val, mask; + int changed; + + mutex_lock(&chip->mutex); + mask = 1 << shift; + val = ucontrol->value.integer.value[0]; + if (GET_GPIO_VAL_INV(kcontrol->private_value)) + val = !val; + val = val ? mask : 0; + changed = maya_set_gpio_bits(chip->ice, mask, val); + mutex_unlock(&chip->mutex); + return changed; +} + +/* + * capture source selection + */ + +/* known working input slots (0-4) */ +#define MAYA_LINE_IN 1 /* in-2 */ +#define MAYA_MIC_IN 4 /* in-5 */ + +static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line) +{ + wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX, + 0x1f, 1 << line); +} + +static int maya_rec_src_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "Line", "Mic" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ARRAY_SIZE(texts); + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int maya_rec_src_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + int sel; + + if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY)) + sel = 1; + else + sel = 0; + ucontrol->value.enumerated.item[0] = sel; + return 0; +} + +static int maya_rec_src_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + int sel = ucontrol->value.enumerated.item[0]; + int changed; + + mutex_lock(&chip->mutex); + changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY, + sel ? GPIO_MIC_RELAY : 0); + wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN); + mutex_unlock(&chip->mutex); + return changed; +} + +/* + * Maya44 routing switch settings have different meanings than the standard + * ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c). + */ +static int maya_pb_route_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { + "PCM Out", /* 0 */ + "Input 1", "Input 2", "Input 3", "Input 4" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ARRAY_SIZE(texts); + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int maya_pb_route_shift(int idx) +{ + static const unsigned char shift[10] = + { 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 }; + return shift[idx % 10]; +} + +static int maya_pb_route_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + ucontrol->value.enumerated.item[0] = + snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx)); + return 0; +} + +static int maya_pb_route_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return snd_ice1724_put_route_val(chip->ice, + ucontrol->value.enumerated.item[0], + maya_pb_route_shift(idx)); +} + + +/* + * controls to be added + */ + +static struct snd_kcontrol_new maya_controls[] __devinitdata = { + { + .name = "Crossmix Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = maya_vol_info, + .get = maya_vol_get, + .put = maya_vol_put, + .tlv = { .p = db_scale_hp }, + .private_value = WM_VOL_HP, + .count = 2, + }, + { + .name = "PCM Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = maya_vol_info, + .get = maya_vol_get, + .put = maya_vol_put, + .tlv = { .p = db_scale_dac }, + .private_value = WM_VOL_DAC, + .count = 2, + }, + { + .name = "Line Capture Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = maya_vol_info, + .get = maya_vol_get, + .put = maya_vol_put, + .tlv = { .p = db_scale_adc }, + .private_value = WM_VOL_ADC, + .count = 2, + }, + { + .name = "PCM Playback Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = maya_sw_info, + .get = maya_sw_get, + .put = maya_sw_put, + .private_value = COMPOSE_SW_VAL(WM_SW_DAC, + WM8776_REG_OUTPUT_MUX, 0x01), + .count = 2, + }, + { + .name = "Bypass Playback Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = maya_sw_info, + .get = maya_sw_get, + .put = maya_sw_put, + .private_value = COMPOSE_SW_VAL(WM_SW_BYPASS, + WM8776_REG_OUTPUT_MUX, 0x04), + .count = 2, + }, + { + .name = "Capture Source", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = maya_rec_src_info, + .get = maya_rec_src_get, + .put = maya_rec_src_put, + }, + { + .name = "Mic Phantom Power Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = maya_gpio_sw_info, + .get = maya_gpio_sw_get, + .put = maya_gpio_sw_put, + .private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1), + }, + { + .name = "SPDIF Capture Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = maya_gpio_sw_info, + .get = maya_gpio_sw_get, + .put = maya_gpio_sw_put, + .private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Playback Route", + .info = maya_pb_route_info, + .get = maya_pb_route_get, + .put = maya_pb_route_put, + .count = 4, /* FIXME: do controls 5-9 have any meaning? */ + }, +}; + +static int __devinit maya44_add_controls(struct snd_ice1712 *ice) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(maya_controls); i++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i], + ice->spec)); + if (err < 0) + return err; + } + return 0; +} + + +/* + * initialize a wm8776 chip + */ +static void __devinit wm8776_init(struct snd_ice1712 *ice, + struct snd_wm8776 *wm, unsigned int addr) +{ + static const unsigned short inits_wm8776[] = { + 0x02, 0x100, /* R2: headphone L+R muted + update */ + 0x05, 0x100, /* R5: DAC output L+R muted + update */ + 0x06, 0x000, /* R6: DAC output phase normal */ + 0x07, 0x091, /* R7: DAC enable zero cross detection, + normal output */ + 0x08, 0x000, /* R8: DAC soft mute off */ + 0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */ + 0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */ + 0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit, + highpass filter enabled */ + 0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */ + 0x0d, 0x000, /* R13: all power up */ + 0x0e, 0x100, /* R14: ADC left muted, + enable zero cross detection */ + 0x0f, 0x100, /* R15: ADC right muted, + enable zero cross detection */ + /* R16: ALC...*/ + 0x11, 0x000, /* R17: disable ALC */ + /* R18: ALC...*/ + /* R19: noise gate...*/ + 0x15, 0x000, /* R21: ADC input mux init, mute all inputs */ + 0x16, 0x001, /* R22: output mux, select DAC */ + 0xff, 0xff + }; + + const unsigned short *ptr; + unsigned char reg; + unsigned short data; + + wm->addr = addr; + /* enable DAC output; mute bypass, aux & all inputs */ + wm->switch_bits = (1 << WM_SW_DAC); + + ptr = inits_wm8776; + while (*ptr != 0xff) { + reg = *ptr++; + data = *ptr++; + wm8776_write(ice, wm, reg, data); + } +} + + +/* + * change the rate on the WM8776 codecs. + * this assumes that the VT17xx's rate is changed by the calling function. + * NOTE: even though the WM8776's are running in slave mode and rate + * selection is automatic, we need to call snd_wm8776_set_rate() here + * to make sure some flags are set correctly. + */ +static void set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + struct snd_maya44 *chip = ice->spec; + unsigned int ratio, adc_ratio, val; + int i; + + switch (rate) { + case 192000: + ratio = WM8776_CLOCK_RATIO_128FS; + break; + case 176400: + ratio = WM8776_CLOCK_RATIO_128FS; + break; + case 96000: + ratio = WM8776_CLOCK_RATIO_256FS; + break; + case 88200: + ratio = WM8776_CLOCK_RATIO_384FS; + break; + case 48000: + ratio = WM8776_CLOCK_RATIO_512FS; + break; + case 44100: + ratio = WM8776_CLOCK_RATIO_512FS; + break; + case 32000: + ratio = WM8776_CLOCK_RATIO_768FS; + break; + case 0: + /* no hint - S/PDIF input is master, simply return */ + return; + default: + snd_BUG(); + return; + } + + /* + * this currently sets the same rate for ADC and DAC, but limits + * ADC rate to 256X (96kHz). For 256X mode (96kHz), this sets ADC + * oversampling to 64x, as recommended by WM8776 datasheet. + * Setting the rate is not really necessary in slave mode. + */ + adc_ratio = ratio; + if (adc_ratio < WM8776_CLOCK_RATIO_256FS) + adc_ratio = WM8776_CLOCK_RATIO_256FS; + + val = adc_ratio; + if (adc_ratio == WM8776_CLOCK_RATIO_256FS) + val |= 8; + val |= ratio << 4; + + mutex_lock(&chip->mutex); + for (i = 0; i < 2; i++) + wm8776_write_bits(ice, &chip->wm[i], + WM8776_REG_MASTER_MODE_CONTROL, + 0x180, val); + mutex_unlock(&chip->mutex); +} + +/* + * supported sample rates (to override the default one) + */ + +static unsigned int rates[] = { + 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 +}; + +/* playback rates: 32..192 kHz */ +static struct snd_pcm_hw_constraint_list dac_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0 +}; + + +/* + * chip addresses on I2C bus + */ +static unsigned char wm8776_addr[2] __devinitdata = { + 0x34, 0x36, /* codec 0 & 1 */ +}; + +/* + * initialize the chip + */ +static int __devinit maya44_init(struct snd_ice1712 *ice) +{ + int i; + struct snd_maya44 *chip; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + mutex_init(&chip->mutex); + chip->ice = ice; + ice->spec = chip; + + /* initialise codecs */ + ice->num_total_dacs = 4; + ice->num_total_adcs = 4; + ice->akm_codecs = 0; + + for (i = 0; i < 2; i++) { + wm8776_init(ice, &chip->wm[i], wm8776_addr[i]); + wm8776_select_input(chip, i, MAYA_LINE_IN); + } + + /* set card specific rates */ + ice->hw_rates = &dac_rates; + + /* register change rate notifier */ + ice->gpio.set_pro_rate = set_rate; + + /* RDMA1 (2nd input channel) is used for ADC by default */ + ice->force_rdma1 = 1; + + /* have an own routing control */ + ice->own_routing = 1; + + return 0; +} + + +/* + * Maya44 boards don't provide the EEPROM data except for the vendor IDs. + * hence the driver needs to sets up it properly. + */ + +static unsigned char maya44_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x45, + /* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */ + [ICE_EEP2_ACLINK] = 0x80, + /* I2S */ + [ICE_EEP2_I2S] = 0xf8, + /* vol, 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, + /* enable spdif out, spdif out supp, spdif-in, ext spdif out */ + [ICE_EEP2_GPIO_DIR] = 0xff, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0xff, + [ICE_EEP2_GPIO_MASK] = 0/*0x9f*/, + [ICE_EEP2_GPIO_MASK1] = 0/*0xff*/, + [ICE_EEP2_GPIO_MASK2] = 0/*0x7f*/, + [ICE_EEP2_GPIO_STATE] = (1 << GPIO_PHANTOM_OFF) | + (1 << GPIO_SPDIF_IN_INV), + [ICE_EEP2_GPIO_STATE1] = 0x00, + [ICE_EEP2_GPIO_STATE2] = 0x00, +}; + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_MAYA44, + .name = "ESI Maya44", + .model = "maya44", + .chip_init = maya44_init, + .build_controls = maya44_add_controls, + .eeprom_size = sizeof(maya44_eeprom), + .eeprom_data = maya44_eeprom, + }, + { } /* terminator */ +}; diff --git a/sound/pci/ice1712/maya44.h b/sound/pci/ice1712/maya44.h new file mode 100644 index 000000000000..eafd03a8f4b5 --- /dev/null +++ b/sound/pci/ice1712/maya44.h @@ -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 */ -- cgit v1.2.1 From 41d5545d23d2ccf4d34725094dccebd37f15c1c4 Mon Sep 17 00:00:00 2001 From: Kacper Szczesniak Date: Thu, 7 May 2009 12:47:43 +0200 Subject: ALSA: hda - Add support for MacBook 5.1 (Aluminium) Signed-off-by: Kacper Szczesniak Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3e7207b927c8..b9495342c921 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -205,6 +205,7 @@ enum { ALC882_ASUS_A7M, ALC885_MACPRO, ALC885_MBP3, + ALC885_MB5, ALC885_IMAC24, ALC882_AUTO, ALC882_MODEL_LAST, @@ -6164,6 +6165,16 @@ static struct hda_input_mux alc882_capture_source = { { "CD", 0x4 }, }, }; + +static struct hda_input_mux mb5_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + /* * 2ch mode */ @@ -6293,6 +6304,20 @@ static struct snd_kcontrol_new alc885_mbp3_mixer[] = { HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), { } /* end */ }; + +static struct snd_kcontrol_new alc885_mb5_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x19, 0x00, HDA_INPUT), + { } /* end */ +}; static struct snd_kcontrol_new alc882_w2jc_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6520,6 +6545,38 @@ static struct hda_verb alc882_macpro_init_verbs[] = { { } }; +/* Macbook 5,1 */ +static struct hda_verb alc885_mb5_init_verbs[] = { + /* Front mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* LineOut mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin: output 0 (0x0d) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* HP Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + { } +}; + /* Macbook Pro rev3 */ static struct hda_verb alc885_mbp3_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ @@ -6864,6 +6921,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { [ALC882_ASUS_A7J] = "asus-a7j", [ALC882_ASUS_A7M] = "asus-a7m", [ALC885_MACPRO] = "macpro", + [ALC885_MB5] = "mb5", [ALC885_MBP3] = "mbp3", [ALC885_IMAC24] = "imac24", [ALC882_AUTO] = "auto", @@ -6944,6 +7002,18 @@ static struct alc_config_preset alc882_presets[] = { .unsol_event = alc885_mbp3_unsol_event, .init_hook = alc885_mbp3_automute, }, + [ALC885_MB5] = { + .mixers = { alc885_mb5_mixer }, + .init_verbs = { alc885_mb5_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mbp_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mbp_6ch_modes), + .input_mux = &mb5_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + }, [ALC885_MACPRO] = { .mixers = { alc882_macpro_mixer }, .init_verbs = { alc882_macpro_init_verbs }, @@ -7249,6 +7319,9 @@ static int patch_alc882(struct hda_codec *codec) case 0x106b3800: /* MacbookPro4,1 - latter revision */ board_config = ALC885_MBP3; break; + case 0x106b3f00: /* Macbook 5,1 */ + board_config = ALC885_MB5; + break; default: /* ALC889A is handled better as ALC888-compatible */ if (codec->revision_id == 0x100101 || -- cgit v1.2.1 From 9da29271bea5d831d745f3ceb7f6f6b2def13a5b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 7 May 2009 16:31:14 +0200 Subject: ALSA: hda - Fix secondary SPDIF on VT1708S and VT1702 codecs VIA VT1708S and VT1702 codecs can have two SPDIF outputs. One of them should have been handled as the extra digital out, but it's not properly accessed. This patch fixes the handling of the secondary SPDIF on these codecs with the slave dig-out as found in patch_sigmatel.c. This makes the use of such a device easier (for normal users). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 111 +++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 60 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b25a5cc637d6..8e004fb6961a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -205,7 +205,7 @@ struct via_spec { /* playback */ struct hda_multi_out multiout; - hda_nid_t extra_dig_out_nid; + hda_nid_t slave_dig_outs[2]; /* capture */ unsigned int num_adc_nids; @@ -731,21 +731,6 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_close(codec, &spec->multiout); } -/* setup SPDIF output stream */ -static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid, - unsigned int stream_tag, unsigned int format) -{ - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); - /* turn on again (if needed) */ - if (codec->spdif_ctls & AC_DIG1_ENABLE) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - codec->spdif_ctls & 0xff); -} - static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -753,19 +738,16 @@ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - hda_nid_t nid; - - /* 1st or 2nd S/PDIF */ - if (substream->number == 0) - nid = spec->multiout.dig_out_nid; - else if (substream->number == 1) - nid = spec->extra_dig_out_nid; - else - return -1; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} - mutex_lock(&codec->spdif_mutex); - setup_dig_playback_stream(codec, nid, stream_tag, format); - mutex_unlock(&codec->spdif_mutex); +static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); return 0; } @@ -842,7 +824,8 @@ static struct hda_pcm_stream vt1708_pcm_digital_playback = { .ops = { .open = via_dig_playback_pcm_open, .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup }, }; @@ -874,13 +857,6 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; spec->multiout.share_spdif = 1; - - if (spec->extra_dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->extra_dig_out_nid); - if (err < 0) - return err; - } } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -1013,10 +989,6 @@ static void via_unsol_event(struct hda_codec *codec, via_gpio_control(codec); } -static hda_nid_t slave_dig_outs[] = { - 0, -}; - static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1051,8 +1023,9 @@ static int via_init(struct hda_codec *codec) snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); - /* no slave outs */ - codec->slave_dig_outs = slave_dig_outs; + /* assign slave outs */ + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; return 0; } @@ -2134,7 +2107,8 @@ static struct hda_pcm_stream vt1708B_pcm_digital_playback = { .ops = { .open = via_dig_playback_pcm_open, .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup }, }; @@ -2589,14 +2563,15 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = { }; static struct hda_pcm_stream vt1708S_pcm_digital_playback = { - .substreams = 2, + .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup }, }; @@ -2805,14 +2780,37 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, return 0; } +/* fill out digital output widgets; one for master and one for slave outputs */ +static void fill_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t nid; + int conn; + + nid = spec->autocfg.dig_out_pins[i]; + if (!nid) + continue; + conn = snd_hda_get_connections(codec, nid, &nid, 1); + if (conn < 1) + continue; + if (!spec->multiout.dig_out_nid) + spec->multiout.dig_out_nid = nid; + else { + spec->slave_dig_outs[0] = nid; + break; /* at most two dig outs */ + } + } +} + static int vt1708S_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int err; - static hda_nid_t vt1708s_ignore[] = {0x21, 0}; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - vt1708s_ignore); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); @@ -2833,10 +2831,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID; - - spec->extra_dig_out_nid = 0x15; + fill_dig_outs(codec); if (spec->kctls.list) spec->mixers[spec->num_mixers++] = spec->kctls.list; @@ -3000,7 +2995,8 @@ static struct hda_pcm_stream vt1702_pcm_digital_playback = { .ops = { .open = via_dig_playback_pcm_open, .close = via_dig_playback_pcm_close, - .prepare = via_dig_playback_pcm_prepare + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup }, }; @@ -3128,10 +3124,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int err; - static hda_nid_t vt1702_ignore[] = {0x1C, 0}; - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, - vt1702_ignore); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); @@ -3152,10 +3146,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) spec->multiout.max_channels = spec->multiout.num_dacs * 2; - if (spec->autocfg.dig_outs) - spec->multiout.dig_out_nid = VT1702_DIGOUT_NID; - - spec->extra_dig_out_nid = 0x1B; + fill_dig_outs(codec); if (spec->kctls.list) spec->mixers[spec->num_mixers++] = spec->kctls.list; -- cgit v1.2.1 From 42171c17f267d7fdc5167ad7b6b5fb9edfd04186 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 May 2009 14:11:43 +0200 Subject: ALSA: hda - Fix and clean up hippo-compat HP auto-muting The speaker auto-muting per HP plugging for ALC262 HIPPO and compatible devices is slightly buggy as the "Master" or "Front" mixer control can still toggle the speaker output even if the headphone is plugged. This patch fixes the issue, and clean up the hippo-related codes together with fixes of some inconsistent mixer names. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 372 ++++++++++++++++++------------------------ 1 file changed, 158 insertions(+), 214 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b9495342c921..8b242c9da4dd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9534,24 +9534,6 @@ static struct snd_kcontrol_new alc262_base_mixer[] = { { } /* end */ }; -static struct snd_kcontrol_new alc262_hippo1_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - /*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - { } /* end */ -}; - /* update HP, line and mono-out pins according to the master switch */ static void alc262_hp_master_update(struct hda_codec *codec) { @@ -9772,46 +9754,132 @@ static struct hda_input_mux alc262_hp_rp5700_capture_source = { }, }; -/* bind hp and internal speaker mute (with plug check) */ -static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* bind hp and internal speaker mute (with plug check) as master switch */ +static void alc262_hippo_master_update(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; + struct alc_spec *spec = codec->spec; + hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; + hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; + hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; + unsigned int mute; - /* change hp mute */ - change = snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); - if (change) { - /* change speaker according to HP jack state */ - struct alc_spec *spec = codec->spec; - unsigned int mute; - if (spec->jack_present) - mute = HDA_AMP_MUTE; - else - mute = snd_hda_codec_amp_read(codec, 0x15, 0, - HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + /* HP */ + mute = spec->master_sw ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + /* mute internal speaker per jack sense */ + if (spec->jack_present) + mute = HDA_AMP_MUTE; + if (line_nid) + snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + if (speaker_nid && speaker_nid != line_nid) + snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, mute); +} + +#define alc262_hippo_master_sw_get alc262_hp_master_sw_get + +static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int val = !!*ucontrol->value.integer.value; + + if (val == spec->master_sw) + return 0; + spec->master_sw = val; + alc262_hippo_master_update(codec); + return 1; +} + +#define ALC262_HIPPO_MASTER_SWITCH \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Master Playback Switch", \ + .info = snd_ctl_boolean_mono_info, \ + .get = alc262_hippo_master_sw_get, \ + .put = alc262_hippo_master_sw_put, \ } - return change; + +static struct snd_kcontrol_new alc262_hippo_mixer[] = { + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new alc262_hippo1_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hippo_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; + unsigned int present; + + /* need to execute and sync at first */ + snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, hp_nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & 0x80000000) != 0; + alc262_hippo_master_update(codec); } +static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hippo_automute(codec); +} + +static void alc262_hippo_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc262_hippo_automute(codec); +} + +static void alc262_hippo1_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + alc262_hippo_automute(codec); +} + + static struct snd_kcontrol_new alc262_sony_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc262_sony_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - }, + ALC262_HIPPO_MASTER_SWITCH, HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), @@ -9820,8 +9888,8 @@ static struct snd_kcontrol_new alc262_sony_mixer[] = { }; static struct snd_kcontrol_new alc262_benq_t31_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), @@ -10076,69 +10144,6 @@ static void alc262_toshiba_s06_init_hook(struct hda_codec *codec) alc262_dmic_automute(codec); } -/* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hippo_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - unsigned int mute; - unsigned int present; - - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } -} - -/* unsolicited event for HP jack sensing */ -static void alc262_hippo_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc262_hippo_automute(codec); -} - -static void alc262_hippo1_automute(struct hda_codec *codec) -{ - unsigned int mute; - unsigned int present; - - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - present = (present & 0x80000000) != 0; - if (present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } -} - -/* unsolicited event for HP jack sensing */ -static void alc262_hippo1_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc262_hippo1_automute(codec); -} - /* * nec model * 0x15 = headphone @@ -10406,14 +10411,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = { HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc262_sony_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), - }, + ALC262_HIPPO_MASTER_SWITCH, HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), @@ -11068,7 +11066,7 @@ static struct alc_config_preset alc262_presets[] = { .input_mux = &alc262_capture_source, }, [ALC262_HIPPO] = { - .mixers = { alc262_base_mixer }, + .mixers = { alc262_hippo_mixer }, .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs}, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, @@ -11078,7 +11076,7 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_automute, + .init_hook = alc262_hippo_init_hook, }, [ALC262_HIPPO_1] = { .mixers = { alc262_hippo1_mixer }, @@ -11090,8 +11088,8 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, - .unsol_event = alc262_hippo1_unsol_event, - .init_hook = alc262_hippo1_automute, + .unsol_event = alc262_hippo_unsol_event, + .init_hook = alc262_hippo1_init_hook, }, [ALC262_FUJITSU] = { .mixers = { alc262_fujitsu_mixer }, @@ -11185,7 +11183,7 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_automute, + .init_hook = alc262_hippo_init_hook, }, [ALC262_BENQ_T31] = { .mixers = { alc262_benq_t31_mixer }, @@ -11197,7 +11195,7 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_automute, + .init_hook = alc262_hippo_init_hook, }, [ALC262_ULTRA] = { .mixers = { alc262_ultra_mixer }, @@ -11262,7 +11260,7 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_automute, + .init_hook = alc262_hippo_init_hook, }, [ALC262_TYAN] = { .mixers = { alc262_tyan_mixer }, @@ -11419,6 +11417,17 @@ static struct snd_kcontrol_new alc268_base_mixer[] = { { } }; +static struct snd_kcontrol_new alc268_toshiba_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT), + { } +}; + /* bind Beep switches of both NID 0x0f and 0x10 */ static struct hda_bind_ctls alc268_bind_beep_sw = { .ops = &snd_hda_bind_sw, @@ -11442,8 +11451,6 @@ static struct hda_verb alc268_eapd_verbs[] = { }; /* Toshiba specific */ -#define alc268_toshiba_automute alc262_hippo_automute - static struct hda_verb alc268_toshiba_verbs[] = { {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } /* end */ @@ -11579,13 +11586,8 @@ static struct hda_verb alc268_acer_verbs[] = { }; /* unsolicited event for HP jack sensing */ -static void alc268_toshiba_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc268_toshiba_automute(codec); -} +#define alc268_toshiba_unsol_event alc262_hippo_unsol_event +#define alc268_toshiba_init_hook alc262_hippo_init_hook static void alc268_acer_unsol_event(struct hda_codec *codec, unsigned int res) @@ -12230,7 +12232,7 @@ static struct alc_config_preset alc268_presets[] = { .input_mux = &alc268_capture_source, }, [ALC268_TOSHIBA] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer, alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_toshiba_verbs }, @@ -12244,7 +12246,7 @@ static struct alc_config_preset alc268_presets[] = { .channel_mode = alc268_modes, .input_mux = &alc268_capture_source, .unsol_event = alc268_toshiba_unsol_event, - .init_hook = alc268_toshiba_automute, + .init_hook = alc268_toshiba_init_hook, }, [ALC268_ACER] = { .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, @@ -12327,7 +12329,7 @@ static struct alc_config_preset alc268_presets[] = { .channel_mode = alc268_modes, .input_mux = &alc268_capture_source, .unsol_event = alc268_toshiba_unsol_event, - .init_hook = alc268_toshiba_automute + .init_hook = alc268_toshiba_init_hook }, #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = { @@ -15552,10 +15554,8 @@ static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { }; static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { - HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line-Out Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, HDA_CODEC_VOLUME("e-Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_VOLUME("e-Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), @@ -15568,15 +15568,11 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { }; static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = { - HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT), + ALC262_HIPPO_MASTER_SWITCH, + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), @@ -16084,51 +16080,25 @@ static void alc662_eeepc_mic_automute(struct hda_codec *codec) static void alc662_eeepc_unsol_event(struct hda_codec *codec, unsigned int res) { - if ((res >> 26) == ALC880_HP_EVENT) - alc262_hippo1_automute( codec ); - if ((res >> 26) == ALC880_MIC_EVENT) alc662_eeepc_mic_automute(codec); + else + alc262_hippo_unsol_event(codec, res); } static void alc662_eeepc_inithook(struct hda_codec *codec) { - alc262_hippo1_automute( codec ); + alc262_hippo1_init_hook(codec); alc662_eeepc_mic_automute(codec); } -static void alc662_eeepc_ep20_automute(struct hda_codec *codec) -{ - unsigned int mute; - unsigned int present; - - snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - present = (present & 0x80000000) != 0; - if (present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } -} - -/* unsolicited event for HP jack sensing */ -static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc662_eeepc_ep20_automute(codec); -} - static void alc662_eeepc_ep20_inithook(struct hda_codec *codec) { - alc662_eeepc_ep20_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + alc262_hippo_master_update(codec); } static void alc663_m51va_speaker_automute(struct hda_codec *codec) @@ -16462,35 +16432,9 @@ static void alc663_g50v_inithook(struct hda_codec *codec) alc662_eeepc_mic_automute(codec); } -/* bind hp and internal speaker mute (with plug check) */ -static int alc662_ecs_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); - if (change) - alc262_hippo1_automute(codec); - return change; -} - static struct snd_kcontrol_new alc662_ecs_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc662_ecs_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - }, + ALC262_HIPPO_MASTER_SWITCH, HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT), @@ -16682,7 +16626,7 @@ static struct alc_config_preset alc662_presets[] = { .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, - .unsol_event = alc662_eeepc_ep20_unsol_event, + .unsol_event = alc662_eeepc_unsol_event, .init_hook = alc662_eeepc_ep20_inithook, }, [ALC662_ECS] = { -- cgit v1.2.1 From b72519b518211ecb7c4c7b5e660ac6235ca7d1b7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 May 2009 14:31:55 +0200 Subject: ALSA: hda - Clean up for ALC262 HP model auto-mute functions Just clean up, no functional changes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 54 +++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8b242c9da4dd..7c043b3b81a5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9589,14 +9589,7 @@ static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec, alc262_hp_wildwest_automute(codec); } -static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - *ucontrol->value.integer.value = spec->master_sw; - return 0; -} +#define alc262_hp_master_sw_get alc260_hp_master_sw_get static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -9612,14 +9605,17 @@ static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol, return 1; } +#define ALC262_HP_MASTER_SWITCH \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Master Playback Switch", \ + .info = snd_ctl_boolean_mono_info, \ + .get = alc262_hp_master_sw_get, \ + .put = alc262_hp_master_sw_put, \ + } + static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = alc262_hp_master_sw_get, - .put = alc262_hp_master_sw_put, - }, + ALC262_HP_MASTER_SWITCH, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), @@ -9643,13 +9639,7 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { }; static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = alc262_hp_master_sw_get, - .put = alc262_hp_master_sw_put, - }, + ALC262_HP_MASTER_SWITCH, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), @@ -9676,17 +9666,14 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { }; /* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hp_t5735_automute(struct hda_codec *codec, int force) +static void alc262_hp_t5735_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + unsigned int present; - if (force || !spec->sense_updated) { - unsigned int present; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; - spec->sense_updated = 1; - } + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE, spec->jack_present ? HDA_AMP_MUTE : 0); } @@ -9696,13 +9683,10 @@ static void alc262_hp_t5735_unsol_event(struct hda_codec *codec, { if ((res >> 26) != ALC880_HP_EVENT) return; - alc262_hp_t5735_automute(codec, 1); + alc262_hp_t5735_automute(codec); } -static void alc262_hp_t5735_init_hook(struct hda_codec *codec) -{ - alc262_hp_t5735_automute(codec, 1); -} +#define alc262_hp_t5735_init_hook alc262_hp_t5735_automute static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), -- cgit v1.2.1 From a9fd4f3fcdc7e5757424f7e5888f660cf34bb1a9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 May 2009 15:57:59 +0200 Subject: ALSA: hda - Clean up Realtek auto-mute unsol routines Most of unsol handlers defined in patch_realtek.c can be classified to two types, mute via amp of pins and mute via ctl bits of pins. Thus there are a big room to generalize each implementation. This patch creates two generic functions, alc_automute_amp() and alc_automute_pin(). The latter is actually changed from the previous alc_sku_automute(). Each caller needs to initialize hp_pins and speaker_pins properly at own init_hook. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 901 +++++++++++++++--------------------------- 1 file changed, 311 insertions(+), 590 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7c043b3b81a5..34f6fb72f2bc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -926,20 +926,26 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } -static void alc_sku_automute(struct hda_codec *codec) +static void alc_automute_pin(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; unsigned int present; - unsigned int hp_nid = spec->autocfg.hp_pins[0]; - unsigned int sp_nid = spec->autocfg.speaker_pins[0]; + unsigned int nid = spec->autocfg.hp_pins[0]; + int i; /* need to execute and sync at first */ - snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, hp_nid, 0, + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->jack_present ? 0 : PIN_OUT); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { + nid = spec->autocfg.speaker_pins[i]; + if (!nid) + break; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->jack_present ? 0 : PIN_OUT); + } } #if 0 /* it's broken in some acses -- temporarily disabled */ @@ -974,16 +980,19 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) res >>= 28; else res >>= 26; - if (res == ALC880_HP_EVENT) - alc_sku_automute(codec); - - if (res == ALC880_MIC_EVENT) + switch (res) { + case ALC880_HP_EVENT: + alc_automute_pin(codec); + break; + case ALC880_MIC_EVENT: alc_mic_automute(codec); + break; + } } static void alc_inithook(struct hda_codec *codec) { - alc_sku_automute(codec); + alc_automute_pin(codec); alc_mic_automute(codec); } @@ -1364,32 +1373,58 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { {} }; -static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec) +static void alc_automute_amp(struct hda_codec *codec) { - unsigned int present; - unsigned int bits; - /* Line out presence */ - present = snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - /* HP out presence */ - present = present || snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; + struct alc_spec *spec = codec->spec; + unsigned int val, mute; + hda_nid_t nid; + int i; + + spec->jack_present = 0; + for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { + nid = spec->autocfg.hp_pins[i]; + if (!nid) + break; + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + if (val & AC_PINSENSE_PRESENCE) { + spec->jack_present = 1; + break; + } + } + + mute = spec->jack_present ? HDA_AMP_MUTE : 0; /* Toggle internal speakers muting */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - /* Toggle internal bass muting */ - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); + for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { + nid = spec->autocfg.speaker_pins[i]; + if (!nid) + break; + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } } -static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec, - unsigned int res) +static void alc_automute_amp_unsol_event(struct hda_codec *codec, + unsigned int res) { - if (res >> 26 == ALC880_HP_EVENT) - alc888_fujitsu_xa3530_automute(codec); + if (codec->vendor_id == 0x10ec0880) + res >>= 28; + else + res >>= 26; + if (res == ALC880_HP_EVENT) + alc_automute_amp(codec); } +static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x17; /* line-out */ + spec->autocfg.hp_pins[1] = 0x1b; /* hp */ + spec->autocfg.speaker_pins[0] = 0x14; /* speaker */ + spec->autocfg.speaker_pins[1] = 0x15; /* bass */ + alc_automute_amp(codec); +} /* * ALC888 Acer Aspire 4930G model @@ -1456,22 +1491,13 @@ static struct snd_kcontrol_new alc888_base_mixer[] = { { } /* end */ }; -static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec) +static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned int bits; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} + struct alc_spec *spec = codec->spec; -static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if (res >> 26 == ALC880_HP_EVENT) - alc888_acer_aspire_4930g_automute(codec); + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); } /* @@ -2439,21 +2465,6 @@ static struct hda_verb alc880_beep_init_verbs[] = { { } }; -/* toggle speaker-output according to the hp-jack state */ -static void alc880_uniwill_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} - /* auto-toggle front mic */ static void alc880_uniwill_mic_automute(struct hda_codec *codec) { @@ -2466,9 +2477,14 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec) snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } -static void alc880_uniwill_automute(struct hda_codec *codec) +static void alc880_uniwill_init_hook(struct hda_codec *codec) { - alc880_uniwill_hp_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x16; + alc_automute_amp(codec); alc880_uniwill_mic_automute(codec); } @@ -2479,24 +2495,22 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec, * definition. 4bit tag is placed at 28 bit! */ switch (res >> 28) { - case ALC880_HP_EVENT: - alc880_uniwill_hp_automute(codec); - break; case ALC880_MIC_EVENT: alc880_uniwill_mic_automute(codec); break; + default: + alc_automute_amp_unsol_event(codec, res); + break; } } -static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec) +static void alc880_uniwill_p53_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + struct alc_spec *spec = codec->spec; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + alc_automute_amp(codec); } static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) @@ -2518,10 +2532,10 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, /* Looks like the unsol event is incompatible with the standard * definition. 4bit tag is placed at 28 bit! */ - if ((res >> 28) == ALC880_HP_EVENT) - alc880_uniwill_p53_hp_automute(codec); if ((res >> 28) == ALC880_DCVOL_EVENT) alc880_uniwill_p53_dcvol_automute(codec); + else + alc_automute_amp_unsol_event(codec, res); } /* @@ -2753,30 +2767,18 @@ static struct hda_verb alc880_lg_init_verbs[] = { {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* jack sense */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, { } }; /* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_automute(struct hda_codec *codec) +static void alc880_lg_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} + struct alc_spec *spec = codec->spec; -static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res) -{ - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - if ((res >> 28) == 0x01) - alc880_lg_automute(codec); + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x17; + alc_automute_amp(codec); } /* @@ -2850,30 +2852,18 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = { {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* jack sense */ - {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, { } }; /* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_lw_automute(struct hda_codec *codec) +static void alc880_lg_lw_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} + struct alc_spec *spec = codec->spec; -static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res) -{ - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 28 bit! - */ - if ((res >> 28) == 0x01) - alc880_lg_lw_automute(codec); + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); } static struct snd_kcontrol_new alc880_medion_rim_mixer[] = { @@ -2920,16 +2910,10 @@ static struct hda_verb alc880_medion_rim_init_verbs[] = { /* toggle speaker-output according to the hp-jack state */ static void alc880_medion_rim_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - if (present) + struct alc_spec *spec = codec->spec; + alc_automute_amp(codec); + /* toggle EAPD */ + if (spec->jack_present) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); else snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2); @@ -2945,6 +2929,15 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec, alc880_medion_rim_automute(codec); } +static void alc880_medion_rim_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + alc880_medion_rim_automute(codec); +} + #ifdef CONFIG_SND_HDA_POWER_SAVE static struct hda_amp_list alc880_loopbacks[] = { { 0x0b, HDA_INPUT, 0 }, @@ -3803,7 +3796,7 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_f1734_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, - .init_hook = alc880_uniwill_p53_hp_automute, + .init_hook = alc880_uniwill_p53_init_hook, }, [ALC880_ASUS] = { .mixers = { alc880_asus_mixer }, @@ -3880,7 +3873,7 @@ static struct alc_config_preset alc880_presets[] = { .need_dac_fix = 1, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_unsol_event, - .init_hook = alc880_uniwill_automute, + .init_hook = alc880_uniwill_init_hook, }, [ALC880_UNIWILL_P53] = { .mixers = { alc880_uniwill_p53_mixer }, @@ -3892,7 +3885,7 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_threestack_modes, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, - .init_hook = alc880_uniwill_p53_hp_automute, + .init_hook = alc880_uniwill_p53_init_hook, }, [ALC880_FUJITSU] = { .mixers = { alc880_fujitsu_mixer }, @@ -3906,7 +3899,7 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, - .init_hook = alc880_uniwill_p53_hp_automute, + .init_hook = alc880_uniwill_p53_init_hook, }, [ALC880_CLEVO] = { .mixers = { alc880_three_stack_mixer }, @@ -3931,8 +3924,8 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_lg_ch_modes, .need_dac_fix = 1, .input_mux = &alc880_lg_capture_source, - .unsol_event = alc880_lg_unsol_event, - .init_hook = alc880_lg_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc880_lg_init_hook, #ifdef CONFIG_SND_HDA_POWER_SAVE .loopbacks = alc880_lg_loopbacks, #endif @@ -3947,8 +3940,8 @@ static struct alc_config_preset alc880_presets[] = { .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes), .channel_mode = alc880_lg_lw_modes, .input_mux = &alc880_lg_lw_capture_source, - .unsol_event = alc880_lg_lw_unsol_event, - .init_hook = alc880_lg_lw_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc880_lg_lw_init_hook, }, [ALC880_MEDION_RIM] = { .mixers = { alc880_medion_rim_mixer }, @@ -3962,7 +3955,7 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_medion_rim_capture_source, .unsol_event = alc880_medion_rim_unsol_event, - .init_hook = alc880_medion_rim_automute, + .init_hook = alc880_medion_rim_init_hook, }, #ifdef CONFIG_SND_DEBUG [ALC880_TEST] = { @@ -6666,45 +6659,23 @@ static struct hda_verb alc885_imac24_init_verbs[] = { }; /* Toggle speaker-output according to the hp-jack state */ -static void alc885_imac24_automute(struct hda_codec *codec) +static void alc885_imac24_automute_init_hook(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} + struct alc_spec *spec = codec->spec; -/* Processes unsolicited events. */ -static void alc885_imac24_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Headphone insertion or removal. */ - if ((res >> 26) == ALC880_HP_EVENT) - alc885_imac24_automute(codec); + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x18; + spec->autocfg.speaker_pins[1] = 0x1a; + alc_automute_amp(codec); } -static void alc885_mbp3_automute(struct hda_codec *codec) +static void alc885_mbp3_init_hook(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); + struct alc_spec *spec = codec->spec; -} -static void alc885_mbp3_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Headphone insertion or removal. */ - if ((res >> 26) == ALC880_HP_EVENT) - alc885_mbp3_automute(codec); + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); } @@ -6729,24 +6700,25 @@ static struct hda_verb alc882_targa_verbs[] = { /* toggle speaker-output according to the hp-jack state */ static void alc882_targa_automute(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + struct alc_spec *spec = codec->spec; + alc_automute_amp(codec); snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, - present ? 1 : 3); + spec->jack_present ? 1 : 3); +} + +static void alc882_targa_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + alc882_targa_automute(codec); } static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) { - /* Looks like the unsol event is incompatible with the standard - * definition. 4bit tag is placed at 26 bit! - */ - if (((res >> 26) == ALC880_HP_EVENT)) { + if ((res >> 26) == ALC880_HP_EVENT) alc882_targa_automute(codec); - } } static struct hda_verb alc882_asus_a7j_verbs[] = { @@ -6828,7 +6800,7 @@ static void alc885_macpro_init_hook(struct hda_codec *codec) static void alc885_imac24_init_hook(struct hda_codec *codec) { alc885_macpro_init_hook(codec); - alc885_imac24_automute(codec); + alc885_imac24_automute_init_hook(codec); } /* @@ -6999,8 +6971,8 @@ static struct alc_config_preset alc882_presets[] = { .input_mux = &alc882_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc885_mbp3_unsol_event, - .init_hook = alc885_mbp3_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc885_mbp3_init_hook, }, [ALC885_MB5] = { .mixers = { alc885_mb5_mixer }, @@ -7036,7 +7008,7 @@ static struct alc_config_preset alc882_presets[] = { .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), .channel_mode = alc882_ch_modes, .input_mux = &alc882_capture_source, - .unsol_event = alc885_imac24_unsol_event, + .unsol_event = alc_automute_amp_unsol_event, .init_hook = alc885_imac24_init_hook, }, [ALC882_TARGA] = { @@ -7053,7 +7025,7 @@ static struct alc_config_preset alc882_presets[] = { .need_dac_fix = 1, .input_mux = &alc882_capture_source, .unsol_event = alc882_targa_unsol_event, - .init_hook = alc882_targa_automute, + .init_hook = alc882_targa_init_hook, }, [ALC882_ASUS_A7J] = { .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, @@ -7903,8 +7875,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x1a, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), @@ -8053,16 +8023,14 @@ static struct hda_verb alc883_init_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc883_mitac_hp_automute(struct hda_codec *codec) +static void alc883_mitac_init_hook(struct hda_codec *codec) { - unsigned int present; + struct alc_spec *spec = codec->spec; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x17; + alc_automute_amp(codec); } /* auto-toggle front mic */ @@ -8079,25 +8047,6 @@ static void alc883_mitac_mic_automute(struct hda_codec *codec) } */ -static void alc883_mitac_automute(struct hda_codec *codec) -{ - alc883_mitac_hp_automute(codec); - /* alc883_mitac_mic_automute(codec); */ -} - -static void alc883_mitac_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - alc883_mitac_hp_automute(codec); - break; - case ALC880_MIC_EVENT: - /* alc883_mitac_mic_automute(codec); */ - break; - } -} - static struct hda_verb alc883_mitac_verbs[] = { /* HP */ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -8215,29 +8164,15 @@ static struct hda_verb alc888_6st_dell_verbs[] = { { } }; -static void alc888_3st_hp_front_automute(struct hda_codec *codec) +static void alc888_3st_hp_init_hook(struct hda_codec *codec) { - unsigned int present, bits; - - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} + struct alc_spec *spec = codec->spec; -static void alc888_3st_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - alc888_3st_hp_front_automute(codec); - break; - } + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x18; + alc_automute_amp(codec); } static struct hda_verb alc888_3st_hp_verbs[] = { @@ -8334,56 +8269,18 @@ static struct hda_verb alc883_medion_md2_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc883_medion_md2_automute(struct hda_codec *codec) +static void alc883_medion_md2_init_hook(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -static void alc883_medion_md2_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc883_medion_md2_automute(codec); -} - -/* toggle speaker-output according to the hp-jack state */ -static void alc883_tagra_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, - present ? 1 : 3); -} + struct alc_spec *spec = codec->spec; -static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc883_tagra_automute(codec); + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + alc_automute_amp(codec); } /* toggle speaker-output according to the hp-jack state */ -static void alc883_clevo_m720_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} +#define alc883_tagra_init_hook alc882_targa_init_hook +#define alc883_tagra_unsol_event alc882_targa_unsol_event static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) { @@ -8395,9 +8292,13 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } -static void alc883_clevo_m720_automute(struct hda_codec *codec) +static void alc883_clevo_m720_init_hook(struct hda_codec *codec) { - alc883_clevo_m720_hp_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); alc883_clevo_m720_mic_automute(codec); } @@ -8405,52 +8306,32 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, unsigned int res) { switch (res >> 26) { - case ALC880_HP_EVENT: - alc883_clevo_m720_hp_automute(codec); - break; case ALC880_MIC_EVENT: alc883_clevo_m720_mic_automute(codec); break; + default: + alc_automute_amp_unsol_event(codec, res); + break; } } /* toggle speaker-output according to the hp-jack state */ -static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec) +static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} + struct alc_spec *spec = codec->spec; -static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc883_2ch_fujitsu_pi2515_automute(codec); + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + alc_automute_amp(codec); } -static void alc883_haier_w66_automute(struct hda_codec *codec) +static void alc883_haier_w66_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - 0x80, bits); -} + struct alc_spec *spec = codec->spec; -static void alc883_haier_w66_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc883_haier_w66_automute(codec); + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); } static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) @@ -8489,23 +8370,14 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec, } /* toggle speaker-output according to the hp-jack state */ -static void alc883_acer_aspire_automute(struct hda_codec *codec) +static void alc883_acer_aspire_init_hook(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} + struct alc_spec *spec = codec->spec; -static void alc883_acer_aspire_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc883_acer_aspire_automute(codec); + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->autocfg.speaker_pins[1] = 0x16; + alc_automute_amp(codec); } static struct hda_verb alc883_acer_eapd_verbs[] = { @@ -8526,75 +8398,29 @@ static struct hda_verb alc883_acer_eapd_verbs[] = { { } }; -static void alc888_6st_dell_front_automute(struct hda_codec *codec) +static void alc888_6st_dell_init_hook(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} + struct alc_spec *spec = codec->spec; -static void alc888_6st_dell_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - /* printk(KERN_DEBUG "hp_event\n"); */ - alc888_6st_dell_front_automute(codec); - break; - } + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->autocfg.speaker_pins[3] = 0x17; + alc_automute_amp(codec); } -static void alc888_lenovo_sky_front_automute(struct hda_codec *codec) +static void alc888_lenovo_sky_init_hook(struct hda_codec *codec) { - unsigned int mute; - unsigned int present; - - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - present = (present & 0x80000000) != 0; - if (present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } -} + struct alc_spec *spec = codec->spec; -static void alc883_lenovo_sky_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc888_lenovo_sky_front_automute(codec); + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + spec->autocfg.speaker_pins[3] = 0x17; + spec->autocfg.speaker_pins[4] = 0x1a; + alc_automute_amp(codec); } /* @@ -8682,39 +8508,33 @@ static void alc883_nb_mic_automute(struct hda_codec *codec) 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); } -static void alc883_M90V_speaker_automute(struct hda_codec *codec) +static void alc883_M90V_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + struct alc_spec *spec = codec->spec; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - bits = present ? 0 : PIN_OUT; - snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - bits); - snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - bits); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - bits); + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x15; + spec->autocfg.speaker_pins[2] = 0x16; + alc_automute_pin(codec); } static void alc883_mode2_unsol_event(struct hda_codec *codec, unsigned int res) { switch (res >> 26) { - case ALC880_HP_EVENT: - alc883_M90V_speaker_automute(codec); - break; case ALC880_MIC_EVENT: alc883_nb_mic_automute(codec); break; + default: + alc_sku_unsol_event(codec, res); + break; } } static void alc883_mode2_inithook(struct hda_codec *codec) { - alc883_M90V_speaker_automute(codec); + alc883_M90V_init_hook(codec); alc883_nb_mic_automute(codec); } @@ -8731,32 +8551,13 @@ static struct hda_verb alc888_asus_eee1601_verbs[] = { { } /* end */ }; -static void alc883_eee1601_speaker_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - bits = present ? 0 : PIN_OUT; - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - bits); -} - -static void alc883_eee1601_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - alc883_eee1601_speaker_automute(codec); - break; - } -} - static void alc883_eee1601_inithook(struct hda_codec *codec) { - alc883_eee1601_speaker_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x1b; + alc_automute_pin(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -8969,7 +8770,7 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, .unsol_event = alc883_tagra_unsol_event, - .init_hook = alc883_tagra_automute, + .init_hook = alc883_tagra_init_hook, }, [ALC883_TARGA_2ch_DIG] = { .mixers = { alc883_tagra_2ch_mixer}, @@ -8983,7 +8784,7 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_tagra_unsol_event, - .init_hook = alc883_tagra_automute, + .init_hook = alc883_tagra_init_hook, }, [ALC883_ACER] = { .mixers = { alc883_base_mixer }, @@ -9008,8 +8809,8 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, - .unsol_event = alc883_acer_aspire_unsol_event, - .init_hook = alc883_acer_aspire_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_acer_aspire_init_hook, }, [ALC888_ACER_ASPIRE_4930G] = { .mixers = { alc888_base_mixer, @@ -9028,8 +8829,8 @@ static struct alc_config_preset alc883_presets[] = { .num_mux_defs = ARRAY_SIZE(alc888_2_capture_sources), .input_mux = alc888_2_capture_sources, - .unsol_event = alc888_acer_aspire_4930g_unsol_event, - .init_hook = alc888_acer_aspire_4930g_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc888_acer_aspire_4930g_init_hook, }, [ALC883_MEDION] = { .mixers = { alc883_fivestack_mixer, @@ -9053,8 +8854,8 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, - .unsol_event = alc883_medion_md2_unsol_event, - .init_hook = alc883_medion_md2_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_medion_md2_init_hook, }, [ALC883_LAPTOP_EAPD] = { .mixers = { alc883_base_mixer }, @@ -9075,7 +8876,7 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_clevo_m720_unsol_event, - .init_hook = alc883_clevo_m720_automute, + .init_hook = alc883_clevo_m720_init_hook, }, [ALC883_LENOVO_101E_2ch] = { .mixers = { alc883_lenovo_101e_2ch_mixer}, @@ -9099,8 +8900,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .need_dac_fix = 1, .input_mux = &alc883_lenovo_nb0763_capture_source, - .unsol_event = alc883_medion_md2_unsol_event, - .init_hook = alc883_medion_md2_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_medion_md2_init_hook, }, [ALC888_LENOVO_MS7195_DIG] = { .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, @@ -9124,8 +8925,8 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, - .unsol_event = alc883_haier_w66_unsol_event, - .init_hook = alc883_haier_w66_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_haier_w66_init_hook, }, [ALC888_3ST_HP] = { .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, @@ -9136,8 +8937,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc888_3st_hp_modes, .need_dac_fix = 1, .input_mux = &alc883_capture_source, - .unsol_event = alc888_3st_hp_unsol_event, - .init_hook = alc888_3st_hp_front_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc888_3st_hp_init_hook, }, [ALC888_6ST_DELL] = { .mixers = { alc883_base_mixer, alc883_chmode_mixer }, @@ -9149,8 +8950,8 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, - .unsol_event = alc888_6st_dell_unsol_event, - .init_hook = alc888_6st_dell_front_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc888_6st_dell_init_hook, }, [ALC883_MITAC] = { .mixers = { alc883_mitac_mixer }, @@ -9160,8 +8961,8 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, - .unsol_event = alc883_mitac_unsol_event, - .init_hook = alc883_mitac_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_mitac_init_hook, }, [ALC883_FUJITSU_PI2515] = { .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, @@ -9173,8 +8974,8 @@ static struct alc_config_preset alc883_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_fujitsu_pi2515_capture_source, - .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, - .init_hook = alc883_2ch_fujitsu_pi2515_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_2ch_fujitsu_pi2515_init_hook, }, [ALC888_FUJITSU_XA3530] = { .mixers = { alc888_base_mixer, alc883_chmode_mixer }, @@ -9191,8 +8992,8 @@ static struct alc_config_preset alc883_presets[] = { .num_mux_defs = ARRAY_SIZE(alc888_2_capture_sources), .input_mux = alc888_2_capture_sources, - .unsol_event = alc888_fujitsu_xa3530_unsol_event, - .init_hook = alc888_fujitsu_xa3530_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc888_fujitsu_xa3530_init_hook, }, [ALC888_LENOVO_SKY] = { .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, @@ -9204,8 +9005,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_sixstack_modes, .need_dac_fix = 1, .input_mux = &alc883_lenovo_sky_capture_source, - .unsol_event = alc883_lenovo_sky_unsol_event, - .init_hook = alc888_lenovo_sky_front_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc888_lenovo_sky_init_hook, }, [ALC888_ASUS_M90V] = { .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, @@ -9233,7 +9034,7 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .need_dac_fix = 1, .input_mux = &alc883_asus_eee1601_capture_source, - .unsol_event = alc883_eee1601_unsol_event, + .unsol_event = alc_sku_unsol_event, .init_hook = alc883_eee1601_inithook, }, [ALC1200_ASUS_P5Q] = { @@ -9666,28 +9467,15 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { }; /* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hp_t5735_automute(struct hda_codec *codec) +static void alc262_hp_t5735_init_hook(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; - snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE, - spec->jack_present ? HDA_AMP_MUTE : 0); -} - -static void alc262_hp_t5735_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc262_hp_t5735_automute(codec); + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */ + alc_automute_amp(codec); } -#define alc262_hp_t5735_init_hook alc262_hp_t5735_automute - static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), @@ -9914,34 +9702,15 @@ static struct hda_verb alc262_tyan_verbs[] = { }; /* unsolicited event for HP jack sensing */ -static void alc262_tyan_automute(struct hda_codec *codec) +static void alc262_tyan_init_hook(struct hda_codec *codec) { - unsigned int mute; - unsigned int present; + struct alc_spec *spec = codec->spec; - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - present = (present & 0x80000000) != 0; - if (present) { - /* mute line output on ATX panel */ - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute line output if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x15; + alc_automute_amp(codec); } -static void alc262_tyan_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc262_tyan_automute(codec); -} #define alc262_capture_mixer alc882_capture_mixer #define alc262_capture_alt_mixer alc882_capture_alt_mixer @@ -10096,35 +9865,24 @@ static void alc262_dmic_automute(struct hda_codec *codec) AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09); } -/* toggle speaker-output according to the hp-jack state */ -static void alc262_toshiba_s06_speaker_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0 : PIN_OUT; - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, bits); -} - - /* unsolicited event for HP jack sensing */ static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec, unsigned int res) { - if ((res >> 26) == ALC880_HP_EVENT) - alc262_toshiba_s06_speaker_automute(codec); if ((res >> 26) == ALC880_MIC_EVENT) alc262_dmic_automute(codec); - + else + alc_sku_unsol_event(codec, res); } static void alc262_toshiba_s06_init_hook(struct hda_codec *codec) { - alc262_toshiba_s06_speaker_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_pin(codec); alc262_dmic_automute(codec); } @@ -11135,7 +10893,7 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, - .unsol_event = alc262_hp_t5735_unsol_event, + .unsol_event = alc_automute_amp_unsol_event, .init_hook = alc262_hp_t5735_init_hook, }, [ALC262_HP_RP5700] = { @@ -11256,8 +11014,8 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, - .unsol_event = alc262_tyan_unsol_event, - .init_hook = alc262_tyan_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc262_tyan_init_hook, }, }; @@ -11646,30 +11404,15 @@ static struct hda_verb alc268_dell_verbs[] = { }; /* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc268_dell_automute(struct hda_codec *codec) +static void alc268_dell_init_hook(struct hda_codec *codec) { - unsigned int present; - unsigned int mute; - - present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0); - if (present & 0x80000000) - mute = HDA_AMP_MUTE; - else - mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); -} + struct alc_spec *spec = codec->spec; -static void alc268_dell_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != ALC880_HP_EVENT) - return; - alc268_dell_automute(codec); + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_pin(codec); } -#define alc268_dell_init_hook alc268_dell_automute - static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), @@ -11688,16 +11431,6 @@ static struct hda_verb alc267_quanta_il1_verbs[] = { { } }; -static void alc267_quanta_il1_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - present ? 0 : PIN_OUT); -} - static void alc267_quanta_il1_mic_automute(struct hda_codec *codec) { unsigned int present; @@ -11709,9 +11442,13 @@ static void alc267_quanta_il1_mic_automute(struct hda_codec *codec) present ? 0x00 : 0x01); } -static void alc267_quanta_il1_automute(struct hda_codec *codec) +static void alc267_quanta_il1_init_hook(struct hda_codec *codec) { - alc267_quanta_il1_hp_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_pin(codec); alc267_quanta_il1_mic_automute(codec); } @@ -11719,12 +11456,12 @@ static void alc267_quanta_il1_unsol_event(struct hda_codec *codec, unsigned int res) { switch (res >> 26) { - case ALC880_HP_EVENT: - alc267_quanta_il1_hp_automute(codec); - break; case ALC880_MIC_EVENT: alc267_quanta_il1_mic_automute(codec); break; + default: + alc_sku_unsol_event(codec, res); + break; } } @@ -12198,7 +11935,7 @@ static struct alc_config_preset alc268_presets[] = { .channel_mode = alc268_modes, .input_mux = &alc268_capture_source, .unsol_event = alc267_quanta_il1_unsol_event, - .init_hook = alc267_quanta_il1_automute, + .init_hook = alc267_quanta_il1_init_hook, }, [ALC268_3ST] = { .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, @@ -12293,7 +12030,7 @@ static struct alc_config_preset alc268_presets[] = { .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, - .unsol_event = alc268_dell_unsol_event, + .unsol_event = alc_sku_unsol_event, .init_hook = alc268_dell_init_hook, .input_mux = &alc268_capture_source, }, @@ -14727,19 +14464,6 @@ static struct hda_verb alc861vd_lenovo_unsol_verbs[] = { {} }; -/* toggle speaker-output according to the hp-jack state */ -static void alc861vd_lenovo_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - unsigned char bits; - - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} - static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) { unsigned int present; @@ -14752,9 +14476,13 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) HDA_AMP_MUTE, bits); } -static void alc861vd_lenovo_automute(struct hda_codec *codec) +static void alc861vd_lenovo_init_hook(struct hda_codec *codec) { - alc861vd_lenovo_hp_automute(codec); + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1b; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); alc861vd_lenovo_mic_automute(codec); } @@ -14762,12 +14490,12 @@ static void alc861vd_lenovo_unsol_event(struct hda_codec *codec, unsigned int res) { switch (res >> 26) { - case ALC880_HP_EVENT: - alc861vd_lenovo_hp_automute(codec); - break; case ALC880_MIC_EVENT: alc861vd_lenovo_mic_automute(codec); break; + default: + alc_automute_amp_unsol_event(codec, res); + break; } } @@ -14817,20 +14545,13 @@ static struct hda_verb alc861vd_dallas_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc861vd_dallas_automute(struct hda_codec *codec) +static void alc861vd_dallas_init_hook(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} + struct alc_spec *spec = codec->spec; -static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) == ALC880_HP_EVENT) - alc861vd_dallas_automute(codec); + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + alc_automute_amp(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -14944,7 +14665,7 @@ static struct alc_config_preset alc861vd_presets[] = { .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, .unsol_event = alc861vd_lenovo_unsol_event, - .init_hook = alc861vd_lenovo_automute, + .init_hook = alc861vd_lenovo_init_hook, }, [ALC861VD_DALLAS] = { .mixers = { alc861vd_dallas_mixer }, @@ -14954,8 +14675,8 @@ static struct alc_config_preset alc861vd_presets[] = { .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_dallas_capture_source, - .unsol_event = alc861vd_dallas_unsol_event, - .init_hook = alc861vd_dallas_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc861vd_dallas_init_hook, }, [ALC861VD_HP] = { .mixers = { alc861vd_hp_mixer }, @@ -14966,8 +14687,8 @@ static struct alc_config_preset alc861vd_presets[] = { .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_hp_capture_source, - .unsol_event = alc861vd_dallas_unsol_event, - .init_hook = alc861vd_dallas_automute, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc861vd_dallas_init_hook, }, [ALC660VD_ASUS_V1S] = { .mixers = { alc861vd_lenovo_mixer }, @@ -14982,7 +14703,7 @@ static struct alc_config_preset alc861vd_presets[] = { .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, .unsol_event = alc861vd_lenovo_unsol_event, - .init_hook = alc861vd_lenovo_automute, + .init_hook = alc861vd_lenovo_init_hook, }, }; -- cgit v1.2.1 From 01f2bd48d08c6bbde12d86b66c760612e33e49a9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 May 2009 08:12:43 +0200 Subject: ALSA: hda - Add missing models for Realtek codecs Added the missing descriptions and the model names for Realtek codecs to the documentation and the config table. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 34f6fb72f2bc..db896121535a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -16184,6 +16184,8 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { [ALC663_ASUS_MODE4] = "asus-mode4", [ALC663_ASUS_MODE5] = "asus-mode5", [ALC663_ASUS_MODE6] = "asus-mode6", + [ALC272_DELL] = "dell", + [ALC272_DELL_ZM1] = "dell-zm1", [ALC662_AUTO] = "auto", }; -- cgit v1.2.1 From 9541ba1d665542c96f7c0b5b836bbc1fd9d961b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Pockel=C3=A9?= Date: Tue, 12 May 2009 08:08:53 +0200 Subject: ALSA: hda - Add support of Samsung NC10 mini notebook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add specific configuration for Samsung NC10 mini notebook. Internal mic/speakers will be correctly muted when plugging in external ones. Mixer controls are added for speakers, headphones and PC beep. "Boost" is added for the microphones. Signed-off-by: Chris Pockelé Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index db896121535a..c14020ab3cb6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -190,6 +190,7 @@ enum { ALC663_ASUS_MODE6, ALC272_DELL, ALC272_DELL_ZM1, + ALC272_SAMSUNG_NC10, ALC662_AUTO, ALC662_MODEL_LAST, }; @@ -15120,6 +15121,38 @@ static struct hda_input_mux alc663_m51va_capture_source = { }, }; +#if 1 /* set to 0 for testing other input sources below */ +static struct hda_input_mux alc272_nc10_capture_source = { + .num_items = 2, + .items = { + { "Autoselect Mic", 0x0 }, + { "Internal Mic", 0x1 }, + }, +}; +#else +static struct hda_input_mux alc272_nc10_capture_source = { + .num_items = 16, + .items = { + { "Autoselect Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "In-0x02", 0x2 }, + { "In-0x03", 0x3 }, + { "In-0x04", 0x4 }, + { "In-0x05", 0x5 }, + { "In-0x06", 0x6 }, + { "In-0x07", 0x7 }, + { "In-0x08", 0x8 }, + { "In-0x09", 0x9 }, + { "In-0x0a", 0x0a }, + { "In-0x0b", 0x0b }, + { "In-0x0c", 0x0c }, + { "In-0x0d", 0x0d }, + { "In-0x0e", 0x0e }, + { "In-0x0f", 0x0f }, + }, +}; +#endif + /* * 2ch mode */ @@ -16151,6 +16184,23 @@ static struct snd_kcontrol_new alc662_ecs_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc272_nc10_mixer[] = { + /* Master Playback automatically created from Speaker and Headphone */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc662_loopbacks alc880_loopbacks #endif @@ -16186,6 +16236,7 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { [ALC663_ASUS_MODE6] = "asus-mode6", [ALC272_DELL] = "dell", [ALC272_DELL_ZM1] = "dell-zm1", + [ALC272_SAMSUNG_NC10] = "samsung-nc10", [ALC662_AUTO] = "auto", }; @@ -16243,6 +16294,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), @@ -16514,6 +16566,18 @@ static struct alc_config_preset alc662_presets[] = { .unsol_event = alc663_m51va_unsol_event, .init_hook = alc663_m51va_inithook, }, + [ALC272_SAMSUNG_NC10] = { + .mixers = { alc272_nc10_mixer }, + .init_verbs = { alc662_init_verbs, + alc663_21jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc272_dac_nids), + .dac_nids = alc272_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .input_mux = &alc272_nc10_capture_source, + .unsol_event = alc663_mode4_unsol_event, + .init_hook = alc663_mode4_inithook, + }, }; -- cgit v1.2.1 From 8e365f9b6e583e82dff96ff415ff9cc2106f5b3c Mon Sep 17 00:00:00 2001 From: Raphael Doursenaud Date: Tue, 12 May 2009 11:49:39 +0200 Subject: ALSA: hdsp: allow firmware loading from inside the kernel Allow the use of the FIRMWARE_IN_KERNEL option with hdsp cards and in-kernel driver. Also corrected a typo in the comment. Signed-off-by: Raphael Doursenaud Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 314e73531bd1..d0d46295b652 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -402,9 +402,9 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_DMA_AREA_BYTES ((HDSP_MAX_CHANNELS+1) * HDSP_CHANNEL_BUFFER_BYTES) #define HDSP_DMA_AREA_KILOBYTES (HDSP_DMA_AREA_BYTES/1024) -/* use hotplug firmeare loader? */ +/* use hotplug firmware loader? */ #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) -#if !defined(HDSP_USE_HWDEP_LOADER) && !defined(CONFIG_SND_HDSP) +#if !defined(HDSP_USE_HWDEP_LOADER) #define HDSP_FW_LOADER #endif #endif -- cgit v1.2.1 From 511b4c171a8054506e8898c40833e31270689b8b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 May 2009 11:51:46 +0200 Subject: ALSA: hdsp - Add a comment about external firmwares for hdsp When the hdsp driver is built in kernel, the corresponding firmware files have to be built into the kernel as well (otherwise the boot will hang up for fairly long time), but there is no way to control it in Kconfig yet, unfortunately. Instead, show a comment for user just to make sure. Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93422e3a3f0c..7478971cd21d 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -532,6 +532,9 @@ config SND_HDSP To compile this driver as a module, choose M here: the module will be called snd-hdsp. +comment "Don't forget to add built-in firmwares for HDSP driver" + depends on SND_HDSP=y + config SND_HDSPM tristate "RME Hammerfall DSP MADI" select SND_HWDEP -- cgit v1.2.1 From 22b530e0287724f00dde9a15a94d408c3334e86c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 13 May 2009 11:32:52 +0200 Subject: ALSA: hda - Disable fallback to model=acer for Acer laptops The model=acer for ALC883/889 doesn't work well for the recent Acer Aspire laptops. Since model=auto works better nowadays, it's safer to use the default fallback instead of the Acer specific one. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c14020ab3cb6..51954bacaa01 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8622,8 +8622,10 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", ALC888_ACER_ASPIRE_4930G), - /* default Acer */ - SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), + /* default Acer -- disabled as it causes more problems. + * model=auto should work fine now + */ + /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */ SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), -- cgit v1.2.1 From 8cc72361481f00253f1e468ade5795427386d593 Mon Sep 17 00:00:00 2001 From: Wai Yew CHAY Date: Thu, 14 May 2009 08:05:58 +0200 Subject: ALSA: SB X-Fi driver merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Sound Blaster X-Fi driver supports Creative solutions based on 20K1 and 20K2 chipsets. Supported hardware : Creative Sound Blaster X-Fi Titanium Fatal1ty® Champion Series Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series Creative Sound Blaster X-Fi Titanium Professional Audio Creative Sound Blaster X-Fi Titanium Creative Sound Blaster X-Fi Elite Pro Creative Sound Blaster X-Fi Platinum Creative Sound Blaster X-Fi Fatal1ty Creative Sound Blaster X-Fi XtremeGamer Creative Sound Blaster X-Fi XtremeMusic Current release features: * ALSA PCM Playback * ALSA Record * ALSA Mixer Note: * External I/O modules detection not included. Signed-off-by: Wai Yew CHAY Singed-off-by: Ryan RICHARDS Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 10 + sound/pci/Makefile | 1 + sound/pci/ctxfi/Makefile | 5 + sound/pci/ctxfi/ct20k1reg.h | 634 ++++++++++++ sound/pci/ctxfi/ct20k2reg.h | 85 ++ sound/pci/ctxfi/ctamixer.c | 488 +++++++++ sound/pci/ctxfi/ctamixer.h | 96 ++ sound/pci/ctxfi/ctatc.c | 1605 ++++++++++++++++++++++++++++++ sound/pci/ctxfi/ctatc.h | 155 +++ sound/pci/ctxfi/ctdaio.c | 769 +++++++++++++++ sound/pci/ctxfi/ctdaio.h | 122 +++ sound/pci/ctxfi/ctdrv.h | 30 + sound/pci/ctxfi/cthardware.c | 108 ++ sound/pci/ctxfi/cthardware.h | 160 +++ sound/pci/ctxfi/cthw20k1.c | 2230 ++++++++++++++++++++++++++++++++++++++++++ sound/pci/ctxfi/cthw20k1.h | 26 + sound/pci/ctxfi/cthw20k2.c | 2133 ++++++++++++++++++++++++++++++++++++++++ sound/pci/ctxfi/cthw20k2.h | 26 + sound/pci/ctxfi/ctimap.c | 112 +++ sound/pci/ctxfi/ctimap.h | 40 + sound/pci/ctxfi/ctmixer.c | 1108 +++++++++++++++++++++ sound/pci/ctxfi/ctmixer.h | 67 ++ sound/pci/ctxfi/ctpcm.c | 499 ++++++++++ sound/pci/ctxfi/ctpcm.h | 27 + sound/pci/ctxfi/ctresource.c | 297 ++++++ sound/pci/ctxfi/ctresource.h | 72 ++ sound/pci/ctxfi/ctsrc.c | 886 +++++++++++++++++ sound/pci/ctxfi/ctsrc.h | 149 +++ sound/pci/ctxfi/ctvmem.c | 254 +++++ sound/pci/ctxfi/ctvmem.h | 49 + sound/pci/ctxfi/xfi.c | 125 +++ 31 files changed, 12368 insertions(+) create mode 100644 sound/pci/ctxfi/Makefile create mode 100644 sound/pci/ctxfi/ct20k1reg.h create mode 100644 sound/pci/ctxfi/ct20k2reg.h create mode 100644 sound/pci/ctxfi/ctamixer.c create mode 100644 sound/pci/ctxfi/ctamixer.h create mode 100644 sound/pci/ctxfi/ctatc.c create mode 100644 sound/pci/ctxfi/ctatc.h create mode 100644 sound/pci/ctxfi/ctdaio.c create mode 100644 sound/pci/ctxfi/ctdaio.h create mode 100644 sound/pci/ctxfi/ctdrv.h create mode 100644 sound/pci/ctxfi/cthardware.c create mode 100644 sound/pci/ctxfi/cthardware.h create mode 100644 sound/pci/ctxfi/cthw20k1.c create mode 100644 sound/pci/ctxfi/cthw20k1.h create mode 100644 sound/pci/ctxfi/cthw20k2.c create mode 100644 sound/pci/ctxfi/cthw20k2.h create mode 100644 sound/pci/ctxfi/ctimap.c create mode 100644 sound/pci/ctxfi/ctimap.h create mode 100644 sound/pci/ctxfi/ctmixer.c create mode 100644 sound/pci/ctxfi/ctmixer.h create mode 100644 sound/pci/ctxfi/ctpcm.c create mode 100644 sound/pci/ctxfi/ctpcm.h create mode 100644 sound/pci/ctxfi/ctresource.c create mode 100644 sound/pci/ctxfi/ctresource.h create mode 100644 sound/pci/ctxfi/ctsrc.c create mode 100644 sound/pci/ctxfi/ctsrc.h create mode 100644 sound/pci/ctxfi/ctvmem.c create mode 100644 sound/pci/ctxfi/ctvmem.h create mode 100644 sound/pci/ctxfi/xfi.c (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93422e3a3f0c..3a7640feaf92 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -275,6 +275,16 @@ config SND_CS5535AUDIO To compile this driver as a module, choose M here: the module will be called snd-cs5535audio. +config SND_CTXFI + tristate "Creative Sound Blaster X-Fi" + select SND_PCM + help + If you want to use soundcards based on Creative Sound Blastr X-Fi + boards with 20k1 or 20k2 chips, say Y here. + + To compile this driver as a module, choose M here: the module + will be called snd-ctxfi. + config SND_DARLA20 tristate "(Echoaudio) Darla20" select FW_LOADER diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 65b25d221cd2..6a1281ec01e3 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_SND) += \ ali5451/ \ au88x0/ \ aw2/ \ + ctxfi/ \ ca0106/ \ cs46xx/ \ cs5535audio/ \ diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile new file mode 100644 index 000000000000..29043237f9f8 --- /dev/null +++ b/sound/pci/ctxfi/Makefile @@ -0,0 +1,5 @@ +snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ + ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \ + cthw20k2.o cthw20k1.o + +obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h new file mode 100644 index 000000000000..c62e6775dab3 --- /dev/null +++ b/sound/pci/ctxfi/ct20k1reg.h @@ -0,0 +1,634 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + */ + +#ifndef CT20K1REG_H +#define CT20k1REG_H + +/* 20k1 registers */ +#define DSPXRAM_START 0x000000 +#define DSPXRAM_END 0x013FFC +#define DSPAXRAM_START 0x020000 +#define DSPAXRAM_END 0x023FFC +#define DSPYRAM_START 0x040000 +#define DSPYRAM_END 0x04FFFC +#define DSPAYRAM_START 0x020000 +#define DSPAYRAM_END 0x063FFC +#define DSPMICRO_START 0x080000 +#define DSPMICRO_END 0x0B3FFC +#define DSP0IO_START 0x100000 +#define DSP0IO_END 0x101FFC +#define AUDIORINGIPDSP0_START 0x100000 +#define AUDIORINGIPDSP0_END 0x1003FC +#define AUDIORINGOPDSP0_START 0x100400 +#define AUDIORINGOPDSP0_END 0x1007FC +#define AUDPARARINGIODSP0_START 0x100800 +#define AUDPARARINGIODSP0_END 0x100BFC +#define DSP0LOCALHWREG_START 0x100C00 +#define DSP0LOCALHWREG_END 0x100C3C +#define DSP0XYRAMAGINDEX_START 0x100C40 +#define DSP0XYRAMAGINDEX_END 0x100C5C +#define DSP0XYRAMAGMDFR_START 0x100C60 +#define DSP0XYRAMAGMDFR_END 0x100C7C +#define DSP0INTCONTLVEC_START 0x100C80 +#define DSP0INTCONTLVEC_END 0x100CD8 +#define INTCONTLGLOBALREG_START 0x100D1C +#define INTCONTLGLOBALREG_END 0x100D3C +#define HOSTINTFPORTADDRCONTDSP0 0x100D40 +#define HOSTINTFPORTDATADSP0 0x100D44 +#define TIME0PERENBDSP0 0x100D60 +#define TIME0COUNTERDSP0 0x100D64 +#define TIME1PERENBDSP0 0x100D68 +#define TIME1COUNTERDSP0 0x100D6C +#define TIME2PERENBDSP0 0x100D70 +#define TIME2COUNTERDSP0 0x100D74 +#define TIME3PERENBDSP0 0x100D78 +#define TIME3COUNTERDSP0 0x100D7C +#define XRAMINDOPERREFNOUP_STARTDSP0 0x100D80 +#define XRAMINDOPERREFNOUP_ENDDSP0 0x100D9C +#define XRAMINDOPERREFUP_STARTDSP0 0x100DA0 +#define XRAMINDOPERREFUP_ENDDSP0 0x100DBC +#define YRAMINDOPERREFNOUP_STARTDSP0 0x100DC0 +#define YRAMINDOPERREFNOUP_ENDDSP0 0x100DDC +#define YRAMINDOPERREFUP_STARTDSP0 0x100DE0 +#define YRAMINDOPERREFUP_ENDDSP0 0x100DFC +#define DSP0CONDCODE 0x100E00 +#define DSP0STACKFLAG 0x100E04 +#define DSP0PROGCOUNTSTACKPTREG 0x100E08 +#define DSP0PROGCOUNTSTACKDATAREG 0x100E0C +#define DSP0CURLOOPADDRREG 0x100E10 +#define DSP0CURLOOPCOUNT 0x100E14 +#define DSP0TOPLOOPCOUNTSTACK 0x100E18 +#define DSP0TOPLOOPADDRSTACK 0x100E1C +#define DSP0LOOPSTACKPTR 0x100E20 +#define DSP0STASSTACKDATAREG 0x100E24 +#define DSP0STASSTACKPTR 0x100E28 +#define DSP0PROGCOUNT 0x100E2C +#define GLOBDSPDEBGREG 0x100E30 +#define GLOBDSPBREPTRREG 0x100E30 +#define DSP0XYRAMBASE_START 0x100EA0 +#define DSP0XYRAMBASE_END 0x100EBC +#define DSP0XYRAMLENG_START 0x100EC0 +#define DSP0XYRAMLENG_END 0x100EDC +#define SEMAPHOREREGDSP0 0x100EE0 +#define DSP0INTCONTMASKREG 0x100EE4 +#define DSP0INTCONTPENDREG 0x100EE8 +#define DSP0INTCONTSERVINT 0x100EEC +#define DSPINTCONTEXTINTMODREG 0x100EEC +#define GPIODSP0 0x100EFC +#define DMADSPBASEADDRREG_STARTDSP0 0x100F00 +#define DMADSPBASEADDRREG_ENDDSP0 0x100F1C +#define DMAHOSTBASEADDRREG_STARTDSP0 0x100F20 +#define DMAHOSTBASEADDRREG_ENDDSP0 0x100F3C +#define DMADSPCURADDRREG_STARTDSP0 0x100F40 +#define DMADSPCURADDRREG_ENDDSP0 0x100F5C +#define DMAHOSTCURADDRREG_STARTDSP0 0x100F60 +#define DMAHOSTCURADDRREG_ENDDSP0 0x100F7C +#define DMATANXCOUNTREG_STARTDSP0 0x100F80 +#define DMATANXCOUNTREG_ENDDSP0 0x100F9C +#define DMATIMEBUGREG_STARTDSP0 0x100FA0 +#define DMATIMEBUGREG_ENDDSP0 0x100FAC +#define DMACNTLMODFREG_STARTDSP0 0x100FA0 +#define DMACNTLMODFREG_ENDDSP0 0x100FAC + +#define DMAGLOBSTATSREGDSP0 0x100FEC +#define DSP0XGPRAM_START 0x101000 +#define DSP0XGPRAM_END 0x1017FC +#define DSP0YGPRAM_START 0x101800 +#define DSP0YGPRAM_END 0x101FFC + + + + +#define AUDIORINGIPDSP1_START 0x102000 +#define AUDIORINGIPDSP1_END 0x1023FC +#define AUDIORINGOPDSP1_START 0x102400 +#define AUDIORINGOPDSP1_END 0x1027FC +#define AUDPARARINGIODSP1_START 0x102800 +#define AUDPARARINGIODSP1_END 0x102BFC +#define DSP1LOCALHWREG_START 0x102C00 +#define DSP1LOCALHWREG_END 0x102C3C +#define DSP1XYRAMAGINDEX_START 0x102C40 +#define DSP1XYRAMAGINDEX_END 0x102C5C +#define DSP1XYRAMAGMDFR_START 0x102C60 +#define DSP1XYRAMAGMDFR_END 0x102C7C +#define DSP1INTCONTLVEC_START 0x102C80 +#define DSP1INTCONTLVEC_END 0x102CD8 +#define HOSTINTFPORTADDRCONTDSP1 0x102D40 +#define HOSTINTFPORTDATADSP1 0x102D44 +#define TIME0PERENBDSP1 0x102D60 +#define TIME0COUNTERDSP1 0x102D64 +#define TIME1PERENBDSP1 0x102D68 +#define TIME1COUNTERDSP1 0x102D6C +#define TIME2PERENBDSP1 0x102D70 +#define TIME2COUNTERDSP1 0x102D74 +#define TIME3PERENBDSP1 0x102D78 +#define TIME3COUNTERDSP1 0x102D7C +#define XRAMINDOPERREFNOUP_STARTDSP1 0x102D80 +#define XRAMINDOPERREFNOUP_ENDDSP1 0x102D9C +#define XRAMINDOPERREFUP_STARTDSP1 0x102DA0 +#define XRAMINDOPERREFUP_ENDDSP1 0x102DBC +#define YRAMINDOPERREFNOUP_STARTDSP1 0x102DC0 +#define YRAMINDOPERREFNOUP_ENDDSP1 0x102DDC +#define YRAMINDOPERREFUP_STARTDSP1 0x102DE0 +#define YRAMINDOPERREFUP_ENDDSP1 0x102DFC + +#define DSP1CONDCODE 0x102E00 +#define DSP1STACKFLAG 0x102E04 +#define DSP1PROGCOUNTSTACKPTREG 0x102E08 +#define DSP1PROGCOUNTSTACKDATAREG 0x102E0C +#define DSP1CURLOOPADDRREG 0x102E10 +#define DSP1CURLOOPCOUNT 0x102E14 +#define DSP1TOPLOOPCOUNTSTACK 0x102E18 +#define DSP1TOPLOOPADDRSTACK 0x102E1C +#define DSP1LOOPSTACKPTR 0x102E20 +#define DSP1STASSTACKDATAREG 0x102E24 +#define DSP1STASSTACKPTR 0x102E28 +#define DSP1PROGCOUNT 0x102E2C +#define DSP1XYRAMBASE_START 0x102EA0 +#define DSP1XYRAMBASE_END 0x102EBC +#define DSP1XYRAMLENG_START 0x102EC0 +#define DSP1XYRAMLENG_END 0x102EDC +#define SEMAPHOREREGDSP1 0x102EE0 +#define DSP1INTCONTMASKREG 0x102EE4 +#define DSP1INTCONTPENDREG 0x102EE8 +#define DSP1INTCONTSERVINT 0x102EEC +#define GPIODSP1 0x102EFC +#define DMADSPBASEADDRREG_STARTDSP1 0x102F00 +#define DMADSPBASEADDRREG_ENDDSP1 0x102F1C +#define DMAHOSTBASEADDRREG_STARTDSP1 0x102F20 +#define DMAHOSTBASEADDRREG_ENDDSP1 0x102F3C +#define DMADSPCURADDRREG_STARTDSP1 0x102F40 +#define DMADSPCURADDRREG_ENDDSP1 0x102F5C +#define DMAHOSTCURADDRREG_STARTDSP1 0x102F60 +#define DMAHOSTCURADDRREG_ENDDSP1 0x102F7C +#define DMATANXCOUNTREG_STARTDSP1 0x102F80 +#define DMATANXCOUNTREG_ENDDSP1 0x102F9C +#define DMATIMEBUGREG_STARTDSP1 0x102FA0 +#define DMATIMEBUGREG_ENDDSP1 0x102FAC +#define DMACNTLMODFREG_STARTDSP1 0x102FA0 +#define DMACNTLMODFREG_ENDDSP1 0x102FAC + +#define DMAGLOBSTATSREGDSP1 0x102FEC +#define DSP1XGPRAM_START 0x103000 +#define DSP1XGPRAM_END 0x1033FC +#define DSP1YGPRAM_START 0x103400 +#define DSP1YGPRAM_END 0x1037FC + + + +#define AUDIORINGIPDSP2_START 0x104000 +#define AUDIORINGIPDSP2_END 0x1043FC +#define AUDIORINGOPDSP2_START 0x104400 +#define AUDIORINGOPDSP2_END 0x1047FC +#define AUDPARARINGIODSP2_START 0x104800 +#define AUDPARARINGIODSP2_END 0x104BFC +#define DSP2LOCALHWREG_START 0x104C00 +#define DSP2LOCALHWREG_END 0x104C3C +#define DSP2XYRAMAGINDEX_START 0x104C40 +#define DSP2XYRAMAGINDEX_END 0x104C5C +#define DSP2XYRAMAGMDFR_START 0x104C60 +#define DSP2XYRAMAGMDFR_END 0x104C7C +#define DSP2INTCONTLVEC_START 0x104C80 +#define DSP2INTCONTLVEC_END 0x104CD8 +#define HOSTINTFPORTADDRCONTDSP2 0x104D40 +#define HOSTINTFPORTDATADSP2 0x104D44 +#define TIME0PERENBDSP2 0x104D60 +#define TIME0COUNTERDSP2 0x104D64 +#define TIME1PERENBDSP2 0x104D68 +#define TIME1COUNTERDSP2 0x104D6C +#define TIME2PERENBDSP2 0x104D70 +#define TIME2COUNTERDSP2 0x104D74 +#define TIME3PERENBDSP2 0x104D78 +#define TIME3COUNTERDSP2 0x104D7C +#define XRAMINDOPERREFNOUP_STARTDSP2 0x104D80 +#define XRAMINDOPERREFNOUP_ENDDSP2 0x104D9C +#define XRAMINDOPERREFUP_STARTDSP2 0x104DA0 +#define XRAMINDOPERREFUP_ENDDSP2 0x104DBC +#define YRAMINDOPERREFNOUP_STARTDSP2 0x104DC0 +#define YRAMINDOPERREFNOUP_ENDDSP2 0x104DDC +#define YRAMINDOPERREFUP_STARTDSP2 0x104DE0 +#define YRAMINDOPERREFUP_ENDDSP2 0x104DFC +#define DSP2CONDCODE 0x104E00 +#define DSP2STACKFLAG 0x104E04 +#define DSP2PROGCOUNTSTACKPTREG 0x104E08 +#define DSP2PROGCOUNTSTACKDATAREG 0x104E0C +#define DSP2CURLOOPADDRREG 0x104E10 +#define DSP2CURLOOPCOUNT 0x104E14 +#define DSP2TOPLOOPCOUNTSTACK 0x104E18 +#define DSP2TOPLOOPADDRSTACK 0x104E1C +#define DSP2LOOPSTACKPTR 0x104E20 +#define DSP2STASSTACKDATAREG 0x104E24 +#define DSP2STASSTACKPTR 0x104E28 +#define DSP2PROGCOUNT 0x104E2C +#define DSP2XYRAMBASE_START 0x104EA0 +#define DSP2XYRAMBASE_END 0x104EBC +#define DSP2XYRAMLENG_START 0x104EC0 +#define DSP2XYRAMLENG_END 0x104EDC +#define SEMAPHOREREGDSP2 0x104EE0 +#define DSP2INTCONTMASKREG 0x104EE4 +#define DSP2INTCONTPENDREG 0x104EE8 +#define DSP2INTCONTSERVINT 0x104EEC +#define GPIODSP2 0x104EFC +#define DMADSPBASEADDRREG_STARTDSP2 0x104F00 +#define DMADSPBASEADDRREG_ENDDSP2 0x104F1C +#define DMAHOSTBASEADDRREG_STARTDSP2 0x104F20 +#define DMAHOSTBASEADDRREG_ENDDSP2 0x104F3C +#define DMADSPCURADDRREG_STARTDSP2 0x104F40 +#define DMADSPCURADDRREG_ENDDSP2 0x104F5C +#define DMAHOSTCURADDRREG_STARTDSP2 0x104F60 +#define DMAHOSTCURADDRREG_ENDDSP2 0x104F7C +#define DMATANXCOUNTREG_STARTDSP2 0x104F80 +#define DMATANXCOUNTREG_ENDDSP2 0x104F9C +#define DMATIMEBUGREG_STARTDSP2 0x104FA0 +#define DMATIMEBUGREG_ENDDSP2 0x104FAC +#define DMACNTLMODFREG_STARTDSP2 0x104FA0 +#define DMACNTLMODFREG_ENDDSP2 0x104FAC + +#define DMAGLOBSTATSREGDSP2 0x104FEC +#define DSP2XGPRAM_START 0x105000 +#define DSP2XGPRAM_END 0x1051FC +#define DSP2YGPRAM_START 0x105800 +#define DSP2YGPRAM_END 0x1059FC + + + +#define AUDIORINGIPDSP3_START 0x106000 +#define AUDIORINGIPDSP3_END 0x1063FC +#define AUDIORINGOPDSP3_START 0x106400 +#define AUDIORINGOPDSP3_END 0x1067FC +#define AUDPARARINGIODSP3_START 0x106800 +#define AUDPARARINGIODSP3_END 0x106BFC +#define DSP3LOCALHWREG_START 0x106C00 +#define DSP3LOCALHWREG_END 0x106C3C +#define DSP3XYRAMAGINDEX_START 0x106C40 +#define DSP3XYRAMAGINDEX_END 0x106C5C +#define DSP3XYRAMAGMDFR_START 0x106C60 +#define DSP3XYRAMAGMDFR_END 0x106C7C +#define DSP3INTCONTLVEC_START 0x106C80 +#define DSP3INTCONTLVEC_END 0x106CD8 +#define HOSTINTFPORTADDRCONTDSP3 0x106D40 +#define HOSTINTFPORTDATADSP3 0x106D44 +#define TIME0PERENBDSP3 0x106D60 +#define TIME0COUNTERDSP3 0x106D64 +#define TIME1PERENBDSP3 0x106D68 +#define TIME1COUNTERDSP3 0x106D6C +#define TIME2PERENBDSP3 0x106D70 +#define TIME2COUNTERDSP3 0x106D74 +#define TIME3PERENBDSP3 0x106D78 +#define TIME3COUNTERDSP3 0x106D7C +#define XRAMINDOPERREFNOUP_STARTDSP3 0x106D80 +#define XRAMINDOPERREFNOUP_ENDDSP3 0x106D9C +#define XRAMINDOPERREFUP_STARTDSP3 0x106DA0 +#define XRAMINDOPERREFUP_ENDDSP3 0x106DBC +#define YRAMINDOPERREFNOUP_STARTDSP3 0x106DC0 +#define YRAMINDOPERREFNOUP_ENDDSP3 0x106DDC +#define YRAMINDOPERREFUP_STARTDSP3 0x106DE0 +#define YRAMINDOPERREFUP_ENDDSP3 0x100DFC + +#define DSP3CONDCODE 0x106E00 +#define DSP3STACKFLAG 0x106E04 +#define DSP3PROGCOUNTSTACKPTREG 0x106E08 +#define DSP3PROGCOUNTSTACKDATAREG 0x106E0C +#define DSP3CURLOOPADDRREG 0x106E10 +#define DSP3CURLOOPCOUNT 0x106E14 +#define DSP3TOPLOOPCOUNTSTACK 0x106E18 +#define DSP3TOPLOOPADDRSTACK 0x106E1C +#define DSP3LOOPSTACKPTR 0x106E20 +#define DSP3STASSTACKDATAREG 0x106E24 +#define DSP3STASSTACKPTR 0x106E28 +#define DSP3PROGCOUNT 0x106E2C +#define DSP3XYRAMBASE_START 0x106EA0 +#define DSP3XYRAMBASE_END 0x106EBC +#define DSP3XYRAMLENG_START 0x106EC0 +#define DSP3XYRAMLENG_END 0x106EDC +#define SEMAPHOREREGDSP3 0x106EE0 +#define DSP3INTCONTMASKREG 0x106EE4 +#define DSP3INTCONTPENDREG 0x106EE8 +#define DSP3INTCONTSERVINT 0x106EEC +#define GPIODSP3 0x106EFC +#define DMADSPBASEADDRREG_STARTDSP3 0x106F00 +#define DMADSPBASEADDRREG_ENDDSP3 0x106F1C +#define DMAHOSTBASEADDRREG_STARTDSP3 0x106F20 +#define DMAHOSTBASEADDRREG_ENDDSP3 0x106F3C +#define DMADSPCURADDRREG_STARTDSP3 0x106F40 +#define DMADSPCURADDRREG_ENDDSP3 0x106F5C +#define DMAHOSTCURADDRREG_STARTDSP3 0x106F60 +#define DMAHOSTCURADDRREG_ENDDSP3 0x106F7C +#define DMATANXCOUNTREG_STARTDSP3 0x106F80 +#define DMATANXCOUNTREG_ENDDSP3 0x106F9C +#define DMATIMEBUGREG_STARTDSP3 0x106FA0 +#define DMATIMEBUGREG_ENDDSP3 0x106FAC +#define DMACNTLMODFREG_STARTDSP3 0x106FA0 +#define DMACNTLMODFREG_ENDDSP3 0x106FAC + +#define DMAGLOBSTATSREGDSP3 0x106FEC +#define DSP3XGPRAM_START 0x107000 +#define DSP3XGPRAM_END 0x1071FC +#define DSP3YGPRAM_START 0x107800 +#define DSP3YGPRAM_END 0x1079FC + +/* end of DSP reg definitions */ + +#define DSPAIMAP_START 0x108000 +#define DSPAIMAP_END 0x1083FC +#define DSPPIMAP_START 0x108400 +#define DSPPIMAP_END 0x1087FC +#define DSPPOMAP_START 0x108800 +#define DSPPOMAP_END 0x108BFC +#define DSPPOCTL 0x108C00 +#define TKCTL_START 0x110000 +#define TKCTL_END 0x110FFC +#define TKCC_START 0x111000 +#define TKCC_END 0x111FFC +#define TKIMAP_START 0x112000 +#define TKIMAP_END 0x112FFC +#define TKDCTR16 0x113000 +#define TKPB16 0x113004 +#define TKBS16 0x113008 +#define TKDCTR32 0x11300C +#define TKPB32 0x113010 +#define TKBS32 0x113014 +#define ICDCTR16 0x113018 +#define ITBS16 0x11301C +#define ICDCTR32 0x113020 +#define ITBS32 0x113024 +#define ITSTART 0x113028 +#define TKSQ 0x11302C + +#define TKSCCTL_START 0x114000 +#define TKSCCTL_END 0x11403C +#define TKSCADR_START 0x114100 +#define TKSCADR_END 0x11413C +#define TKSCDATAX_START 0x114800 +#define TKSCDATAX_END 0x1149FC +#define TKPCDATAX_START 0x120000 +#define TKPCDATAX_END 0x12FFFC + +#define MALSA 0x130000 +#define MAPPHA 0x130004 +#define MAPPLA 0x130008 +#define MALSB 0x130010 +#define MAPPHB 0x130014 +#define MAPPLB 0x130018 + +#define TANSPORTMAPABREGS_START 0x130020 +#define TANSPORTMAPABREGS_END 0x13A2FC + +#define PTPAHX 0x13B000 +#define PTPALX 0x13B004 + +#define TANSPPAGETABLEPHYADDR015_START 0x13B008 +#define TANSPPAGETABLEPHYADDR015_END 0x13B07C +#define TRNQADRX_START 0x13B100 +#define TRNQADRX_END 0x13B13C +#define TRNQTIMX_START 0x13B200 +#define TRNQTIMX_END 0x13B23C +#define TRNQAPARMX_START 0x13B300 +#define TRNQAPARMX_END 0x13B33C + +#define TRNQCNT 0x13B400 +#define TRNCTL 0x13B404 +#define TRNIS 0x13B408 +#define TRNCURTS 0x13B40C + +#define AMOP_START 0x140000 +#define AMOPLO 0x140000 +#define AMOPHI 0x140004 +#define AMOP_END 0x147FFC +#define PMOP_START 0x148000 +#define PMOPLO 0x148000 +#define PMOPHI 0x148004 +#define PMOP_END 0x14FFFC +#define PCURR_START 0x150000 +#define PCURR_END 0x153FFC +#define PTRAG_START 0x154000 +#define PTRAG_END 0x157FFC +#define PSR_START 0x158000 +#define PSR_END 0x15BFFC + +#define PFSTAT4SEG_START 0x160000 +#define PFSTAT4SEG_END 0x160BFC +#define PFSTAT2SEG_START 0x160C00 +#define PFSTAT2SEG_END 0x1617FC +#define PFTARG4SEG_START 0x164000 +#define PFTARG4SEG_END 0x164BFC +#define PFTARG2SEG_START 0x164C00 +#define PFTARG2SEG_END 0x1657FC +#define PFSR4SEG_START 0x168000 +#define PFSR4SEG_END 0x168BFC +#define PFSR2SEG_START 0x168C00 +#define PFSR2SEG_END 0x1697FC +#define PCURRMS4SEG_START 0x16C000 +#define PCURRMS4SEG_END 0x16CCFC +#define PCURRMS2SEG_START 0x16CC00 +#define PCURRMS2SEG_END 0x16D7FC +#define PTARGMS4SEG_START 0x170000 +#define PTARGMS4SEG_END 0x172FFC +#define PTARGMS2SEG_START 0x173000 +#define PTARGMS2SEG_END 0x1747FC +#define PSRMS4SEG_START 0x170000 +#define PSRMS4SEG_END 0x172FFC +#define PSRMS2SEG_START 0x173000 +#define PSRMS2SEG_END 0x1747FC + +#define PRING_LO_START 0x190000 +#define PRING_LO_END 0x193FFC +#define PRING_HI_START 0x194000 +#define PRING_HI_END 0x197FFC +#define PRING_LO_HI_START 0x198000 +#define PRING_LO_HI 0x198000 +#define PRING_LO_HI_END 0x19BFFC + +#define PINTFIFO 0x1A0000 +#define SRCCTL 0x1B0000 +#define SRCCCR 0x1B0004 +#define SRCIMAP 0x1B0008 +#define SRCODDC 0x1B000C +#define SRCCA 0x1B0010 +#define SRCCF 0x1B0014 +#define SRCSA 0x1B0018 +#define SRCLA 0x1B001C +#define SRCCTLSWR 0x1B0020 + +/* SRC HERE */ +#define SRCALBA 0x1B002C +#define SRCMCTL 0x1B012C +#define SRCCERR 0x1B022C +#define SRCITB 0x1B032C +#define SRCIPM 0x1B082C +#define SRCIP 0x1B102C +#define SRCENBSTAT 0x1B202C +#define SRCENBLO 0x1B212C +#define SRCENBHI 0x1B222C +#define SRCENBS 0x1B232C +#define SRCENB 0x1B282C +#define SRCENB07 0x1B282C +#define SRCENBS07 0x1B302C + +#define SRCDN0Z 0x1B0030 +#define SRCDN0Z0 0x1B0030 +#define SRCDN0Z1 0x1B0034 +#define SRCDN0Z2 0x1B0038 +#define SRCDN0Z3 0x1B003C +#define SRCDN1Z 0x1B0040 +#define SRCDN1Z0 0x1B0040 +#define SRCDN1Z1 0x1B0044 +#define SRCDN1Z2 0x1B0048 +#define SRCDN1Z3 0x1B004C +#define SRCDN1Z4 0x1B0050 +#define SRCDN1Z5 0x1B0054 +#define SRCDN1Z6 0x1B0058 +#define SRCDN1Z7 0x1B005C +#define SRCUPZ 0x1B0060 +#define SRCUPZ0 0x1B0060 +#define SRCUPZ1 0x1B0064 +#define SRCUPZ2 0x1B0068 +#define SRCUPZ3 0x1B006C +#define SRCUPZ4 0x1B0070 +#define SRCUPZ5 0x1B0074 +#define SRCUPZ6 0x1B0078 +#define SRCUPZ7 0x1B007C +#define SRCCD0 0x1B0080 +#define SRCCD1 0x1B0084 +#define SRCCD2 0x1B0088 +#define SRCCD3 0x1B008C +#define SRCCD4 0x1B0090 +#define SRCCD5 0x1B0094 +#define SRCCD6 0x1B0098 +#define SRCCD7 0x1B009C +#define SRCCD8 0x1B00A0 +#define SRCCD9 0x1B00A4 +#define SRCCDA 0x1B00A8 +#define SRCCDB 0x1B00AC +#define SRCCDC 0x1B00B0 +#define SRCCDD 0x1B00B4 +#define SRCCDE 0x1B00B8 +#define SRCCDF 0x1B00BC +#define SRCCD10 0x1B00C0 +#define SRCCD11 0x1B00C4 +#define SRCCD12 0x1B00C8 +#define SRCCD13 0x1B00CC +#define SRCCD14 0x1B00D0 +#define SRCCD15 0x1B00D4 +#define SRCCD16 0x1B00D8 +#define SRCCD17 0x1B00DC +#define SRCCD18 0x1B00E0 +#define SRCCD19 0x1B00E4 +#define SRCCD1A 0x1B00E8 +#define SRCCD1B 0x1B00EC +#define SRCCD1C 0x1B00F0 +#define SRCCD1D 0x1B00F4 +#define SRCCD1E 0x1B00F8 +#define SRCCD1F 0x1B00FC + +#define SRCCONTRBLOCK_START 0x1B0100 +#define SRCCONTRBLOCK_END 0x1BFFFC +#define FILTOP_START 0x1C0000 +#define FILTOP_END 0x1C05FC +#define FILTIMAP_START 0x1C0800 +#define FILTIMAP_END 0x1C0DFC +#define FILTZ1_START 0x1C1000 +#define FILTZ1_END 0x1C15FC +#define FILTZ2_START 0x1C1800 +#define FILTZ2_END 0x1C1DFC +#define DAOIMAP_START 0x1C5000 +#define DAOIMAP 0x1C5000 +#define DAOIMAP_END 0x1C5124 + +#define AC97D 0x1C5400 +#define AC97A 0x1C5404 +#define AC97CTL 0x1C5408 +#define I2SCTL 0x1C5420 + +#define SPOS 0x1C5440 +#define SPOSA 0x1C5440 +#define SPOSB 0x1C5444 +#define SPOSC 0x1C5448 +#define SPOSD 0x1C544C + +#define SPISA 0x1C5450 +#define SPISB 0x1C5454 +#define SPISC 0x1C5458 +#define SPISD 0x1C545C + +#define SPFSCTL 0x1C5460 + +#define SPFS0 0x1C5468 +#define SPFS1 0x1C546C +#define SPFS2 0x1C5470 +#define SPFS3 0x1C5474 +#define SPFS4 0x1C5478 +#define SPFS5 0x1C547C + +#define SPOCTL 0x1C5480 +#define SPICTL 0x1C5484 +#define SPISTS 0x1C5488 +#define SPINTP 0x1C548C +#define SPINTE 0x1C5490 +#define SPUTCTLAB 0x1C5494 +#define SPUTCTLCD 0x1C5498 + +#define SRTSPA 0x1C54C0 +#define SRTSPB 0x1C54C4 +#define SRTSPC 0x1C54C8 +#define SRTSPD 0x1C54CC + +#define SRTSCTL 0x1C54D0 +#define SRTSCTLA 0x1C54D0 +#define SRTSCTLB 0x1C54D4 +#define SRTSCTLC 0x1C54D8 +#define SRTSCTLD 0x1C54DC + +#define SRTI2S 0x1C54E0 +#define SRTICTL 0x1C54F0 + +#define WC 0x1C6000 +#define TIMR 0x1C6004 + +#define GIP 0x1C6010 +#define GIE 0x1C6014 +#define DIE 0x1C6018 +#define DIC 0x1C601C +#define GPIO 0x1C6020 +#define GPIOCTL 0x1C6024 +#define GPIP 0x1C6028 +#define GPIE 0x1C602C +#define DSPINT0 0x1C6030 +#define DSPEIOC 0x1C6034 +#define MUADAT 0x1C6040 +#define MUACMD 0x1C6044 +#define MUASTAT 0x1C6044 +#define MUBDAT 0x1C6048 +#define MUBCMD 0x1C604C +#define MUBSTAT 0x1C604C +#define UARTCMA 0x1C6050 +#define UARTCMB 0x1C6054 +#define UARTIP 0x1C6058 +#define UARTIE 0x1C605C +#define PLLCTL 0x1C6060 +#define PLLDCD 0x1C6064 +#define GCTL 0x1C6070 +#define ID0 0x1C6080 +#define ID1 0x1C6084 +#define ID2 0x1C6088 +#define ID3 0x1C608C +#define SDRCTL 0x1C7000 + + +#define I2SA_L 0x0L +#define I2SA_R 0x1L +#define I2SB_L 0x8L +#define I2SB_R 0x9L +#define I2SC_L 0x10L +#define I2SC_R 0x11L +#define I2SD_L 0x18L +#define I2SD_R 0x19L + +#endif /* CT20K1REG_H */ + + diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h new file mode 100644 index 000000000000..2d07986f57cc --- /dev/null +++ b/sound/pci/ctxfi/ct20k2reg.h @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + */ + +#ifndef _20K2REGISTERS_H_ +#define _20K2REGISTERS_H_ + + +/* Timer Registers */ +#define TIMER_TIMR 0x1B7004 +#define INTERRUPT_GIP 0x1B7010 +#define INTERRUPT_GIE 0x1B7014 + +/* I2C Registers */ +#define I2C_IF_ADDRESS 0x1B9000 +#define I2C_IF_WDATA 0x1B9004 +#define I2C_IF_RDATA 0x1B9008 +#define I2C_IF_STATUS 0x1B900C +#define I2C_IF_WLOCK 0x1B9010 + +/* Global Control Registers */ +#define GLOBAL_CNTL_GCTL 0x1B7090 + +/* PLL Registers */ +#define PLL_CTL 0x1B7080 +#define PLL_STAT 0x1B7084 +#define PLL_ENB 0x1B7088 + +/* SRC Registers */ +#define SRC_CTL 0x1A0000 /* 0x1A0000 + (256 * Chn) */ +#define SRC_CCR 0x1A0004 /* 0x1A0004 + (256 * Chn) */ +#define SRC_IMAP 0x1A0008 /* 0x1A0008 + (256 * Chn) */ +#define SRC_CA 0x1A0010 /* 0x1A0010 + (256 * Chn) */ +#define SRC_CF 0x1A0014 /* 0x1A0014 + (256 * Chn) */ +#define SRC_SA 0x1A0018 /* 0x1A0018 + (256 * Chn) */ +#define SRC_LA 0x1A001C /* 0x1A001C + (256 * Chn) */ +#define SRC_CTLSWR 0x1A0020 /* 0x1A0020 + (256 * Chn) */ +#define SRC_CD 0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */ +#define SRC_MCTL 0x1A012C +#define SRC_IP 0x1A102C /* 0x1A102C + (256 * Regn) */ +#define SRC_ENB 0x1A282C /* 0x1A282C + (256 * Regn) */ +#define SRC_ENBSTAT 0x1A202C +#define SRC_ENBSA 0x1A232C +#define SRC_DN0Z 0x1A0030 +#define SRC_DN1Z 0x1A0040 +#define SRC_UPZ 0x1A0060 + +/* GPIO Registers */ +#define GPIO_DATA 0x1B7020 +#define GPIO_CTRL 0x1B7024 + +/* Virtual memory registers */ +#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */ +#define VMEM_PTPAH 0x1C6304 /* 0x1C6304 + (16 * Chn) */ +#define VMEM_CTL 0x1C7000 + +/* Transport Registers */ +#define TRANSPORT_ENB 0x1B6000 +#define TRANSPORT_CTL 0x1B6004 +#define TRANSPORT_INT 0x1B6008 + +/* Audio IO */ +#define AUDIO_IO_AIM 0x1B5000 /* 0x1B5000 + (0x04 * Chn) */ +#define AUDIO_IO_TX_CTL 0x1B5400 /* 0x1B5400 + (0x40 * Chn) */ +#define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */ +#define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */ +#define AUDIO_IO_RX_CTL 0x1B5410 /* 0x1B5410 + (0x40 * Chn) */ +#define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */ +#define AUDIO_IO_MCLK 0x1B5600 +#define AUDIO_IO_TX_BLRCLK 0x1B5604 +#define AUDIO_IO_RX_BLRCLK 0x1B5608 + +/* Mixer */ +#define MIXER_AMOPLO 0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */ +#define MIXER_AMOPHI 0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */ +#define MIXER_PRING_LO_HI 0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */ +#define MIXER_PMOPLO 0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */ +#define MIXER_PMOPHI 0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */ +#define MIXER_AR_ENABLE 0x19000C + +#endif diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c new file mode 100644 index 000000000000..119791aa70eb --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.c @@ -0,0 +1,488 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctamixer.c + * + * @Brief + * This file contains the implementation of the Audio Mixer + * resource management object. + * + * @Author Liu Chun + * @Date May 21 2008 + * + */ + +#include "ctamixer.h" +#include "cthardware.h" +#include + +#define AMIXER_RESOURCE_NUM 256 +#define SUM_RESOURCE_NUM 256 + +#define AMIXER_Y_IMMEDIATE 1 + +#define BLANK_SLOT 4094 + +static int amixer_master(struct rsc *rsc) +{ + rsc->conj = 0; + return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0]; +} + +static int amixer_next_conj(struct rsc *rsc) +{ + rsc->conj++; + return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; +} + +static int amixer_index(const struct rsc *rsc) +{ + return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; +} + +static int amixer_output_slot(const struct rsc *rsc) +{ + return (amixer_index(rsc) << 4) + 0x4; +} + +static struct rsc_ops amixer_basic_rsc_ops = { + .master = amixer_master, + .next_conj = amixer_next_conj, + .index = amixer_index, + .output_slot = amixer_output_slot, +}; + +static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) +{ + struct hw *hw = NULL; + + hw = (struct hw *)amixer->rsc.hw; + hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); + amixer->input = rsc; + if (NULL == rsc) + hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT); + else + hw->amixer_set_x(amixer->rsc.ctrl_blk, + rsc->ops->output_slot(rsc)); + + return 0; +} + +/* y is a 14-bit immediate constant */ +static int amixer_set_y(struct amixer *amixer, unsigned int y) +{ + struct hw *hw = NULL; + + hw = (struct hw *)amixer->rsc.hw; + hw->amixer_set_y(amixer->rsc.ctrl_blk, y); + + return 0; +} + +static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) +{ + struct hw *hw = NULL; + + hw = (struct hw *)amixer->rsc.hw; + hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv); + + return 0; +} + +static int amixer_set_sum(struct amixer *amixer, struct sum *sum) +{ + struct hw *hw = NULL; + + hw = (struct hw *)amixer->rsc.hw; + amixer->sum = sum; + if (NULL == sum) { + hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); + } else { + hw->amixer_set_se(amixer->rsc.ctrl_blk, 1); + hw->amixer_set_sadr(amixer->rsc.ctrl_blk, + sum->rsc.ops->index(&sum->rsc)); + } + + return 0; +} + +static int amixer_commit_write(struct amixer *amixer) +{ + struct hw *hw = NULL; + unsigned int index = 0; + int i = 0; + struct rsc *input = NULL; + struct sum *sum = NULL; + + hw = (struct hw *)amixer->rsc.hw; + input = amixer->input; + sum = amixer->sum; + + /* Program master and conjugate resources */ + amixer->rsc.ops->master(&amixer->rsc); + if (NULL != input) + input->ops->master(input); + + if (NULL != sum) + sum->rsc.ops->master(&sum->rsc); + + for (i = 0; i < amixer->rsc.msr; i++) { + hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk); + if (NULL != input) { + hw->amixer_set_x(amixer->rsc.ctrl_blk, + input->ops->output_slot(input)); + input->ops->next_conj(input); + } + if (NULL != sum) { + hw->amixer_set_sadr(amixer->rsc.ctrl_blk, + sum->rsc.ops->index(&sum->rsc)); + sum->rsc.ops->next_conj(&sum->rsc); + } + index = amixer->rsc.ops->output_slot(&amixer->rsc); + hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); + amixer->rsc.ops->next_conj(&amixer->rsc); + } + amixer->rsc.ops->master(&amixer->rsc); + if (NULL != input) + input->ops->master(input); + + if (NULL != sum) + sum->rsc.ops->master(&sum->rsc); + + return 0; +} + +static int amixer_commit_raw_write(struct amixer *amixer) +{ + struct hw *hw = NULL; + unsigned int index = 0; + + hw = (struct hw *)amixer->rsc.hw; + index = amixer->rsc.ops->output_slot(&amixer->rsc); + hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); + + return 0; +} + +static int amixer_get_y(struct amixer *amixer) +{ + struct hw *hw = NULL; + + hw = (struct hw *)amixer->rsc.hw; + return hw->amixer_get_y(amixer->rsc.ctrl_blk); +} + +static int amixer_setup(struct amixer *amixer, struct rsc *input, + unsigned int scale, struct sum *sum) +{ + amixer_set_input(amixer, input); + amixer_set_y(amixer, scale); + amixer_set_sum(amixer, sum); + amixer_commit_write(amixer); + return 0; +} + +static struct amixer_rsc_ops amixer_ops = { + .set_input = amixer_set_input, + .set_invalid_squash = amixer_set_invalid_squash, + .set_scale = amixer_set_y, + .set_sum = amixer_set_sum, + .commit_write = amixer_commit_write, + .commit_raw_write = amixer_commit_raw_write, + .setup = amixer_setup, + .get_scale = amixer_get_y, +}; + +static int amixer_rsc_init(struct amixer *amixer, + const struct amixer_desc *desc, + struct amixer_mgr *mgr) +{ + int err = 0; + + err = rsc_init(&amixer->rsc, amixer->idx[0], + AMIXER, desc->msr, mgr->mgr.hw); + if (err) + return err; + + /* Set amixer specific operations */ + amixer->rsc.ops = &amixer_basic_rsc_ops; + amixer->ops = &amixer_ops; + amixer->input = NULL; + amixer->sum = NULL; + + amixer_setup(amixer, NULL, 0, NULL); + + return 0; +} + +static int amixer_rsc_uninit(struct amixer *amixer) +{ + amixer_setup(amixer, NULL, 0, NULL); + rsc_uninit(&amixer->rsc); + amixer->ops = NULL; + amixer->input = NULL; + amixer->sum = NULL; + return 0; +} + +static int get_amixer_rsc(struct amixer_mgr *mgr, + const struct amixer_desc *desc, + struct amixer **ramixer) +{ + int err = 0, i = 0; + unsigned int idx = 0; + struct amixer *amixer = NULL; + unsigned long flags; + + *ramixer = NULL; + + /* Allocate mem for amixer resource */ + amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); + if (NULL == amixer) { + err = -ENOMEM; + return err; + } + + /* Check whether there are sufficient + * amixer resources to meet request. */ + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i = 0; i < desc->msr; i++) { + err = mgr_get_resource(&mgr->mgr, 1, &idx); + if (err) + break; + + amixer->idx[i] = idx; + } + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + if (err) { + printk(KERN_ERR "Can't meet AMIXER resource request!\n"); + goto error; + } + + err = amixer_rsc_init(amixer, desc, mgr); + if (err) + goto error; + + *ramixer = amixer; + + return 0; + +error: + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i--; i >= 0; i--) + mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + kfree(amixer); + return err; +} + +static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) +{ + unsigned long flags; + int i = 0; + + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i = 0; i < amixer->rsc.msr; i++) + mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + amixer_rsc_uninit(amixer); + kfree(amixer); + + return 0; +} + +int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) +{ + int err = 0; + struct amixer_mgr *amixer_mgr; + + *ramixer_mgr = NULL; + amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); + if (NULL == amixer_mgr) + return -ENOMEM; + + err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); + if (err) + goto error; + + spin_lock_init(&amixer_mgr->mgr_lock); + + amixer_mgr->get_amixer = get_amixer_rsc; + amixer_mgr->put_amixer = put_amixer_rsc; + + *ramixer_mgr = amixer_mgr; + + return 0; + +error: + kfree(amixer_mgr); + return err; +} + +int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) +{ + rsc_mgr_uninit(&amixer_mgr->mgr); + kfree(amixer_mgr); + return 0; +} + +/* SUM resource management */ + +static int sum_master(struct rsc *rsc) +{ + rsc->conj = 0; + return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0]; +} + +static int sum_next_conj(struct rsc *rsc) +{ + rsc->conj++; + return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; +} + +static int sum_index(const struct rsc *rsc) +{ + return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; +} + +static int sum_output_slot(const struct rsc *rsc) +{ + return (sum_index(rsc) << 4) + 0xc; +} + +static struct rsc_ops sum_basic_rsc_ops = { + .master = sum_master, + .next_conj = sum_next_conj, + .index = sum_index, + .output_slot = sum_output_slot, +}; + +static int sum_rsc_init(struct sum *sum, + const struct sum_desc *desc, + struct sum_mgr *mgr) +{ + int err = 0; + + err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); + if (err) + return err; + + sum->rsc.ops = &sum_basic_rsc_ops; + + return 0; +} + +static int sum_rsc_uninit(struct sum *sum) +{ + rsc_uninit(&sum->rsc); + return 0; +} + +static int get_sum_rsc(struct sum_mgr *mgr, + const struct sum_desc *desc, + struct sum **rsum) +{ + int err = 0, i = 0; + unsigned int idx = 0; + struct sum *sum = NULL; + unsigned long flags; + + *rsum = NULL; + + /* Allocate mem for sum resource */ + sum = kzalloc(sizeof(*sum), GFP_KERNEL); + if (NULL == sum) { + err = -ENOMEM; + return err; + } + + /* Check whether there are sufficient sum resources to meet request. */ + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i = 0; i < desc->msr; i++) { + err = mgr_get_resource(&mgr->mgr, 1, &idx); + if (err) + break; + + sum->idx[i] = idx; + } + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + if (err) { + printk(KERN_ERR "Can't meet SUM resource request!\n"); + goto error; + } + + err = sum_rsc_init(sum, desc, mgr); + if (err) + goto error; + + *rsum = sum; + + return 0; + +error: + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i--; i >= 0; i--) + mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + kfree(sum); + return err; +} + +static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) +{ + unsigned long flags; + int i = 0; + + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i = 0; i < sum->rsc.msr; i++) + mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + sum_rsc_uninit(sum); + kfree(sum); + + return 0; +} + +int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) +{ + int err = 0; + struct sum_mgr *sum_mgr; + + *rsum_mgr = NULL; + sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); + if (NULL == sum_mgr) + return -ENOMEM; + + err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); + if (err) + goto error; + + spin_lock_init(&sum_mgr->mgr_lock); + + sum_mgr->get_sum = get_sum_rsc; + sum_mgr->put_sum = put_sum_rsc; + + *rsum_mgr = sum_mgr; + + return 0; + +error: + kfree(sum_mgr); + return err; +} + +int sum_mgr_destroy(struct sum_mgr *sum_mgr) +{ + rsc_mgr_uninit(&sum_mgr->mgr); + kfree(sum_mgr); + return 0; +} + diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h new file mode 100644 index 000000000000..cc49e5ab4750 --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.h @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctamixer.h + * + * @Brief + * This file contains the definition of the Audio Mixer + * resource management object. + * + * @Author Liu Chun + * @Date May 21 2008 + * + */ + +#ifndef CTAMIXER_H +#define CTAMIXER_H + +#include "ctresource.h" +#include + +/* Define the descriptor of a summation node resource */ +struct sum { + struct rsc rsc; /* Basic resource info */ + unsigned char idx[8]; +}; + +/* Define sum resource request description info */ +struct sum_desc { + unsigned int msr; +}; + +struct sum_mgr { + struct rsc_mgr mgr; /* Basic resource manager info */ + spinlock_t mgr_lock; + + /* request one sum resource */ + int (*get_sum)(struct sum_mgr *mgr, + const struct sum_desc *desc, struct sum **rsum); + /* return one sum resource */ + int (*put_sum)(struct sum_mgr *mgr, struct sum *sum); +}; + +/* Constructor and destructor of daio resource manager */ +int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr); +int sum_mgr_destroy(struct sum_mgr *sum_mgr); + +/* Define the descriptor of a amixer resource */ +struct amixer_rsc_ops; + +struct amixer { + struct rsc rsc; /* Basic resource info */ + unsigned char idx[8]; + struct rsc *input; /* pointer to a resource acting as source */ + struct sum *sum; /* Put amixer output to this summation node */ + struct amixer_rsc_ops *ops; /* AMixer specific operations */ +}; + +struct amixer_rsc_ops { + int (*set_input)(struct amixer *amixer, struct rsc *rsc); + int (*set_scale)(struct amixer *amixer, unsigned int scale); + int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv); + int (*set_sum)(struct amixer *amixer, struct sum *sum); + int (*commit_write)(struct amixer *amixer); + /* Only for interleaved recording */ + int (*commit_raw_write)(struct amixer *amixer); + int (*setup)(struct amixer *amixer, struct rsc *input, + unsigned int scale, struct sum *sum); + int (*get_scale)(struct amixer *amixer); +}; + +/* Define amixer resource request description info */ +struct amixer_desc { + unsigned int msr; +}; + +struct amixer_mgr { + struct rsc_mgr mgr; /* Basic resource manager info */ + spinlock_t mgr_lock; + + /* request one amixer resource */ + int (*get_amixer)(struct amixer_mgr *mgr, + const struct amixer_desc *desc, + struct amixer **ramixer); + /* return one amixer resource */ + int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer); +}; + +/* Constructor and destructor of amixer resource manager */ +int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr); +int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr); + +#endif /* CTAMIXER_H */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c new file mode 100644 index 000000000000..5f35374863fb --- /dev/null +++ b/sound/pci/ctxfi/ctatc.c @@ -0,0 +1,1605 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctatc.c + * + * @Brief + * This file contains the implementation of the device resource management + * object. + * + * @Author Liu Chun + * @Date Mar 28 2008 + */ + +#include "ctatc.h" +#include "ctpcm.h" +#include "ctmixer.h" +#include "ctdrv.h" +#include "cthardware.h" +#include "ctsrc.h" +#include "ctamixer.h" +#include "ctdaio.h" +#include +#include +#include +#include + +#define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */ +#define DAIONUM 7 +#define MAX_MULTI_CHN 8 + +#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \ + | IEC958_AES0_CON_NOT_COPYRIGHT) \ + | ((IEC958_AES1_CON_MIXER \ + | IEC958_AES1_CON_ORIGINAL) << 8) \ + | (0x10 << 16) \ + | ((IEC958_AES3_CON_FS_48000) << 24)) + +static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { + [CTSB0760] = {.subsys = PCI_SUBSYS_CREATIVE_SB0760, + .nm_model = "SB076x"}, + [CTHENDRIX] = {.subsys = PCI_SUBSYS_CREATIVE_HENDRIX, + .nm_model = "Hendrix"}, + [CTSB08801] = {.subsys = PCI_SUBSYS_CREATIVE_SB08801, + .nm_model = "SB0880"}, + [CTSB08802] = {.subsys = PCI_SUBSYS_CREATIVE_SB08802, + .nm_model = "SB0880"}, + [CTSB08803] = {.subsys = PCI_SUBSYS_CREATIVE_SB08803, + .nm_model = "SB0880"} +}; + +static struct ct_atc_chip_details atc_chip_details[] = { + {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K1, + .sub_details = NULL, + .nm_card = "X-Fi 20k1"}, + {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K2, + .sub_details = atc_sub_details, + .nm_card = "X-Fi 20k2"}, + {} /* terminator */ +}; + +static struct { + int (*create)(struct ct_atc *atc, + enum CTALSADEVS device, const char *device_name); + int (*destroy)(void *alsa_dev); + const char *public_name; +} alsa_dev_funcs[NUM_CTALSADEVS] = { + [FRONT] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Front/WaveIn"}, + [REAR] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Rear"}, + [CLFE] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Center/LFE"}, + [SURROUND] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "Surround"}, + [IEC958] = { .create = ct_alsa_pcm_create, + .destroy = NULL, + .public_name = "IEC958 Non-audio"}, + + [MIXER] = { .create = ct_alsa_mix_create, + .destroy = NULL, + .public_name = "Mixer"} +}; + +typedef int (*create_t)(void *, void **); +typedef int (*destroy_t)(void *); + +static struct { + int (*create)(void *hw, void **rmgr); + int (*destroy)(void *mgr); +} rsc_mgr_funcs[NUM_RSCTYP] = { + [SRC] = { .create = (create_t)src_mgr_create, + .destroy = (destroy_t)src_mgr_destroy }, + [SRCIMP] = { .create = (create_t)srcimp_mgr_create, + .destroy = (destroy_t)srcimp_mgr_destroy }, + [AMIXER] = { .create = (create_t)amixer_mgr_create, + .destroy = (destroy_t)amixer_mgr_destroy }, + [SUM] = { .create = (create_t)sum_mgr_create, + .destroy = (destroy_t)sum_mgr_destroy }, + [DAIO] = { .create = (create_t)daio_mgr_create, + .destroy = (destroy_t)daio_mgr_destroy } +}; + +static int +atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm); + +/* * + * Only mono and interleaved modes are supported now. + * Always allocates a contiguous channel block. + * */ + +static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + unsigned long flags; + struct snd_pcm_runtime *runtime; + struct ct_vm *vm; + + if (NULL == apcm->substream) + return 0; + + runtime = apcm->substream->runtime; + vm = atc->vm; + + spin_lock_irqsave(&atc->vm_lock, flags); + apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); + spin_unlock_irqrestore(&atc->vm_lock, flags); + + if (NULL == apcm->vm_block) + return -ENOENT; + + return 0; +} + +static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + unsigned long flags; + struct ct_vm *vm; + + if (NULL == apcm->vm_block) + return; + + vm = atc->vm; + + spin_lock_irqsave(&atc->vm_lock, flags); + vm->unmap(vm, apcm->vm_block); + spin_unlock_irqrestore(&atc->vm_lock, flags); + + apcm->vm_block = NULL; +} + +static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) +{ + struct ct_vm *vm; + void *kvirt_addr; + unsigned long phys_addr; + unsigned long flags; + + spin_lock_irqsave(&atc->vm_lock, flags); + vm = atc->vm; + kvirt_addr = vm->get_ptp_virt(vm, index); + if (kvirt_addr == NULL) + phys_addr = (~0UL); + else + phys_addr = virt_to_phys(kvirt_addr); + + spin_unlock_irqrestore(&atc->vm_lock, flags); + + return phys_addr; +} + +static unsigned int convert_format(snd_pcm_format_t snd_format) +{ + switch (snd_format) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + return SRC_SF_U8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + return SRC_SF_S16; + case SNDRV_PCM_FORMAT_S24_3LE: + return SRC_SF_S24; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + return SRC_SF_S32; + default: + printk(KERN_ERR "not recognized snd format is %d \n", + snd_format); + return SRC_SF_S16; + } +} + +static unsigned int +atc_get_pitch(unsigned int input_rate, unsigned int output_rate) +{ + unsigned int pitch = 0; + int b = 0; + + /* get pitch and convert to fixed-point 8.24 format. */ + pitch = (input_rate / output_rate) << 24; + input_rate %= output_rate; + input_rate /= 100; + output_rate /= 100; + for (b = 31; ((b >= 0) && !(input_rate >> b)); ) + b--; + + if (b >= 0) { + input_rate <<= (31 - b); + input_rate /= output_rate; + b = 24 - (31 - b); + if (b >= 0) + input_rate <<= b; + else + input_rate >>= -b; + + pitch |= input_rate; + } + + return pitch; +} + +static int select_rom(unsigned int pitch) +{ + if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) { + /* 0.26 <= pitch <= 1.72 */ + return 1; + } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) { + /* pitch == 1.8375 */ + return 2; + } else if (0x02000000 == pitch) { + /* pitch == 2 */ + return 3; + } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) { + /* 0 <= pitch <= 8 */ + return 0; + } else { + return -ENOENT; + } +} + +static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct src_desc desc = {0}; + struct amixer_desc mix_dsc = {0}; + struct src *src = NULL; + struct amixer *amixer = NULL; + int err = 0; + int n_amixer = apcm->substream->runtime->channels, i = 0; + int device = apcm->substream->pcm->device; + unsigned int pitch = 0; + unsigned long flags; + + if (NULL != apcm->src) { + /* Prepared pcm playback */ + return 0; + } + + /* Get SRC resource */ + desc.multi = apcm->substream->runtime->channels; + desc.msr = atc->msr; + desc.mode = MEMRD; + err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); + if (err) + goto error1; + + pitch = atc_get_pitch(apcm->substream->runtime->rate, + (atc->rsr * atc->msr)); + src = apcm->src; + src->ops->set_pitch(src, pitch); + src->ops->set_rom(src, select_rom(pitch)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); + + /* Get AMIXER resource */ + n_amixer = (n_amixer < 2) ? 2 : n_amixer; + apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + if (NULL == apcm->amixers) { + err = -ENOMEM; + goto error1; + } + mix_dsc.msr = atc->msr; + for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { + err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, + (struct amixer **)&apcm->amixers[i]); + if (err) + goto error1; + + apcm->n_amixer++; + } + + /* Set up device virtual mem map */ + err = ct_map_audio_buffer(atc, apcm); + if (err < 0) + goto error1; + + /* Connect resources */ + src = apcm->src; + for (i = 0; i < n_amixer; i++) { + amixer = apcm->amixers[i]; + spin_lock_irqsave(&atc->atc_lock, flags); + amixer->ops->setup(amixer, &src->rsc, + INIT_VOL, atc->pcm[i+device*2]); + spin_unlock_irqrestore(&atc->atc_lock, flags); + src = src->ops->next_interleave(src); + if (NULL == src) + src = apcm->src; + } + + return 0; + +error1: + atc_pcm_release_resources(atc, apcm); + return err; +} + +static int +atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; + struct srcimp *srcimp = NULL; + int i = 0; + + if (NULL != apcm->srcimps) { + for (i = 0; i < apcm->n_srcimp; i++) { + srcimp = apcm->srcimps[i]; + srcimp->ops->unmap(srcimp); + srcimp_mgr->put_srcimp(srcimp_mgr, srcimp); + apcm->srcimps[i] = NULL; + } + kfree(apcm->srcimps); + apcm->srcimps = NULL; + } + + if (NULL != apcm->srccs) { + for (i = 0; i < apcm->n_srcc; i++) { + src_mgr->put_src(src_mgr, apcm->srccs[i]); + apcm->srccs[i] = NULL; + } + kfree(apcm->srccs); + apcm->srccs = NULL; + } + + if (NULL != apcm->amixers) { + for (i = 0; i < apcm->n_amixer; i++) { + amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]); + apcm->amixers[i] = NULL; + } + kfree(apcm->amixers); + apcm->amixers = NULL; + } + + if (NULL != apcm->mono) { + sum_mgr->put_sum(sum_mgr, apcm->mono); + apcm->mono = NULL; + } + + if (NULL != apcm->src) { + src_mgr->put_src(src_mgr, apcm->src); + apcm->src = NULL; + } + + if (NULL != apcm->vm_block) { + /* Undo device virtual mem map */ + ct_unmap_audio_buffer(atc, apcm); + apcm->vm_block = NULL; + } + + return 0; +} + +static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + unsigned int max_cisz = 0; + struct src *src = apcm->src; + + max_cisz = src->multi * src->rsc.msr; + max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8); + + src->ops->set_sa(src, apcm->vm_block->addr); + src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); + src->ops->set_ca(src, apcm->vm_block->addr + max_cisz); + src->ops->set_cisz(src, max_cisz); + + src->ops->set_bm(src, 1); + src->ops->set_state(src, SRC_STATE_INIT); + src->ops->commit_write(src); + + return 0; +} + +static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + int i = 0; + + src = apcm->src; + src->ops->set_bm(src, 0); + src->ops->set_state(src, SRC_STATE_OFF); + src->ops->commit_write(src); + + if (NULL != apcm->srccs) { + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + src->ops->set_bm(src, 0); + src->ops->set_state(src, SRC_STATE_OFF); + src->ops->commit_write(src); + } + } + + apcm->started = 0; + + return 0; +} + +static int +atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = apcm->src; + u32 size = 0, max_cisz = 0; + int position = 0; + + position = src->ops->get_ca(src); + + size = apcm->vm_block->size; + max_cisz = src->multi * src->rsc.msr; + max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8); + + return (position + size - max_cisz - apcm->vm_block->addr) % size; +} + +struct src_node_conf_t { + unsigned int pitch; + unsigned int msr:8; + unsigned int mix_msr:8; + unsigned int imp_msr:8; + unsigned int vo:1; +}; + +static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, + struct src_node_conf_t *conf, int *n_srcc) +{ + unsigned int pitch = 0; + + /* get pitch and convert to fixed-point 8.24 format. */ + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + *n_srcc = 0; + + if (1 == atc->msr) { + *n_srcc = apcm->substream->runtime->channels; + conf[0].pitch = pitch; + conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1; + conf[0].vo = 1; + } else if (2 == atc->msr) { + if (0x8000000 < pitch) { + /* Need two-stage SRCs, SRCIMPs and + * AMIXERs for converting format */ + conf[0].pitch = (atc->msr << 24); + conf[0].msr = conf[0].mix_msr = 1; + conf[0].imp_msr = atc->msr; + conf[0].vo = 0; + conf[1].pitch = atc_get_pitch(atc->rsr, + apcm->substream->runtime->rate); + conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1; + conf[1].vo = 1; + *n_srcc = apcm->substream->runtime->channels * 2; + } else if (0x1000000 < pitch) { + /* Need one-stage SRCs, SRCIMPs and + * AMIXERs for converting format */ + conf[0].pitch = pitch; + conf[0].msr = conf[0].mix_msr + = conf[0].imp_msr = atc->msr; + conf[0].vo = 1; + *n_srcc = apcm->substream->runtime->channels; + } + } +} + +static int +atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; + struct src_desc src_dsc = {0}; + struct src *src = NULL; + struct srcimp_desc srcimp_dsc = {0}; + struct srcimp *srcimp = NULL; + struct amixer_desc mix_dsc = {0}; + struct sum_desc sum_dsc = {0}; + unsigned int pitch = 0; + int multi = 0, err = 0, i = 0; + int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; + struct src_node_conf_t src_node_conf[2] = {{0} }; + + /* The numbers of converting SRCs and SRCIMPs should be determined + * by pitch value. */ + + multi = apcm->substream->runtime->channels; + + /* get pitch and convert to fixed-point 8.24 format. */ + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + + setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc); + n_sum = (1 == multi) ? 1 : 0; + n_amixer += n_sum * 2 + n_srcc; + n_srcimp += n_srcc; + if ((multi > 1) && (0x8000000 >= pitch)) { + /* Need extra AMIXERs and SRCIMPs for special treatment + * of interleaved recording of conjugate channels */ + n_amixer += multi * atc->msr; + n_srcimp += multi * atc->msr; + } else { + n_srcimp += multi; + } + + if (n_srcc) { + apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); + if (NULL == apcm->srccs) + return -ENOMEM; + } + if (n_amixer) { + apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + if (NULL == apcm->amixers) { + err = -ENOMEM; + goto error1; + } + } + apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); + if (NULL == apcm->srcimps) { + err = -ENOMEM; + goto error1; + } + + /* Allocate SRCs for sample rate conversion if needed */ + src_dsc.multi = 1; + src_dsc.mode = ARCRW; + for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) { + src_dsc.msr = src_node_conf[i/multi].msr; + err = src_mgr->get_src(src_mgr, &src_dsc, + (struct src **)&apcm->srccs[i]); + if (err) + goto error1; + + src = apcm->srccs[i]; + pitch = src_node_conf[i/multi].pitch; + src->ops->set_pitch(src, pitch); + src->ops->set_rom(src, select_rom(pitch)); + src->ops->set_vo(src, src_node_conf[i/multi].vo); + + apcm->n_srcc++; + } + + /* Allocate AMIXERs for routing SRCs of conversion if needed */ + for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { + if (i < (n_sum*2)) + mix_dsc.msr = atc->msr; + else if (i < (n_sum*2+n_srcc)) + mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr; + else + mix_dsc.msr = 1; + + err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, + (struct amixer **)&apcm->amixers[i]); + if (err) + goto error1; + + apcm->n_amixer++; + } + + /* Allocate a SUM resource to mix all input channels together */ + sum_dsc.msr = atc->msr; + err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono); + if (err) + goto error1; + + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + /* Allocate SRCIMP resources */ + for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) { + if (i < (n_srcc)) + srcimp_dsc.msr = src_node_conf[i/multi].imp_msr; + else if (1 == multi) + srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1; + else + srcimp_dsc.msr = 1; + + err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp); + if (err) + goto error1; + + apcm->srcimps[i] = srcimp; + apcm->n_srcimp++; + } + + /* Allocate a SRC for writing data to host memory */ + src_dsc.multi = apcm->substream->runtime->channels; + src_dsc.msr = 1; + src_dsc.mode = MEMWR; + err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src); + if (err) + goto error1; + + src = apcm->src; + src->ops->set_pitch(src, pitch); + + /* Set up device virtual mem map */ + err = ct_map_audio_buffer(atc, apcm); + if (err < 0) + goto error1; + + return 0; + +error1: + atc_pcm_release_resources(atc, apcm); + return err; +} + +static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + struct amixer *amixer = NULL; + struct srcimp *srcimp = NULL; + struct ct_mixer *mixer = atc->mixer; + struct sum *mono = NULL; + struct rsc *out_ports[8] = {NULL}; + int err = 0, i = 0, j = 0, n_sum = 0, multi = 0; + unsigned int pitch = 0; + int mix_base = 0, imp_base = 0; + + if (NULL != apcm->src) { + /* Prepared pcm capture */ + return 0; + } + + /* Get needed resources. */ + err = atc_pcm_capture_get_resources(atc, apcm); + if (err) + return err; + + /* Connect resources */ + mixer->get_output_ports(mixer, MIX_PCMO_FRONT, + &out_ports[0], &out_ports[1]); + + multi = apcm->substream->runtime->channels; + if (1 == multi) { + mono = apcm->mono; + for (i = 0; i < 2; i++) { + amixer = apcm->amixers[i]; + amixer->ops->setup(amixer, out_ports[i], + MONO_SUM_SCALE, mono); + } + out_ports[0] = &mono->rsc; + n_sum = 1; + mix_base = n_sum * 2; + } + + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + srcimp = apcm->srcimps[imp_base+i]; + amixer = apcm->amixers[mix_base+i]; + srcimp->ops->map(srcimp, src, out_ports[i%multi]); + amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); + out_ports[i%multi] = &amixer->rsc; + } + + pitch = atc_get_pitch((atc->rsr * atc->msr), + apcm->substream->runtime->rate); + + if ((multi > 1) && (pitch <= 0x8000000)) { + /* Special connection for interleaved + * recording with conjugate channels */ + for (i = 0; i < multi; i++) { + out_ports[i]->ops->master(out_ports[i]); + for (j = 0; j < atc->msr; j++) { + amixer = apcm->amixers[apcm->n_srcc+j*multi+i]; + amixer->ops->set_input(amixer, out_ports[i]); + amixer->ops->set_scale(amixer, INIT_VOL); + amixer->ops->set_sum(amixer, NULL); + amixer->ops->commit_raw_write(amixer); + out_ports[i]->ops->next_conj(out_ports[i]); + + srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i]; + srcimp->ops->map(srcimp, apcm->src, + &amixer->rsc); + } + } + } else { + for (i = 0; i < multi; i++) { + srcimp = apcm->srcimps[apcm->n_srcc+i]; + srcimp->ops->map(srcimp, apcm->src, out_ports[i]); + } + } + + return 0; +} + +static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + int i = 0, multi = 0; + + if (apcm->started) + return 0; + + apcm->started = 1; + multi = apcm->substream->runtime->channels; + /* Set up converting SRCs */ + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + src->ops->set_pm(src, ((i%multi) != (multi-1))); + src_mgr->src_disable(src_mgr, src); + } + + /* Set up recording SRC */ + src = apcm->src; + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_sa(src, apcm->vm_block->addr); + src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size); + src->ops->set_ca(src, apcm->vm_block->addr); + src_mgr->src_disable(src_mgr, src); + + /* Disable relevant SRCs firstly */ + src_mgr->commit_write(src_mgr); + + /* Enable SRCs respectively */ + for (i = 0; i < apcm->n_srcc; i++) { + src = apcm->srccs[i]; + src->ops->set_state(src, SRC_STATE_RUN); + src->ops->commit_write(src); + src_mgr->src_enable_s(src_mgr, src); + } + src = apcm->src; + src->ops->set_bm(src, 1); + src->ops->set_state(src, SRC_STATE_RUN); + src->ops->commit_write(src); + src_mgr->src_enable_s(src_mgr, src); + + /* Enable relevant SRCs synchronously */ + src_mgr->commit_write(src_mgr); + + return 0; +} + +static int +atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = apcm->src; + + return src->ops->get_ca(src) - apcm->vm_block->addr; +} + +static int spdif_passthru_playback_get_resources(struct ct_atc *atc, + struct ct_atc_pcm *apcm) +{ + struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; + struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; + struct src_desc desc = {0}; + struct amixer_desc mix_dsc = {0}; + struct src *src = NULL; + int err = 0; + int n_amixer = apcm->substream->runtime->channels, i = 0; + unsigned int pitch = 0, rsr = atc->pll_rate; + + /* Get SRC resource */ + desc.multi = apcm->substream->runtime->channels; + desc.msr = 1; + while (apcm->substream->runtime->rate > (rsr * desc.msr)) + desc.msr <<= 1; + + desc.mode = MEMRD; + err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src); + if (err) + goto error1; + + pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr)); + src = apcm->src; + src->ops->set_pitch(src, pitch); + src->ops->set_rom(src, select_rom(pitch)); + src->ops->set_sf(src, convert_format(apcm->substream->runtime->format)); + src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL)); + src->ops->set_bp(src, 1); + + /* Get AMIXER resource */ + n_amixer = (n_amixer < 2) ? 2 : n_amixer; + apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + if (NULL == apcm->amixers) { + err = -ENOMEM; + goto error1; + } + mix_dsc.msr = desc.msr; + for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) { + err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc, + (struct amixer **)&apcm->amixers[i]); + if (err) + goto error1; + + apcm->n_amixer++; + } + + /* Set up device virtual mem map */ + err = ct_map_audio_buffer(atc, apcm); + if (err < 0) + goto error1; + + return 0; + +error1: + atc_pcm_release_resources(atc, apcm); + return err; +} + +static int +spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio); + unsigned long flags; + unsigned int rate = apcm->substream->runtime->rate; + unsigned int status = 0; + int err = 0; + unsigned char iec958_con_fs = 0; + + switch (rate) { + case 48000: + iec958_con_fs = IEC958_AES3_CON_FS_48000; + break; + case 44100: + iec958_con_fs = IEC958_AES3_CON_FS_44100; + break; + case 32000: + iec958_con_fs = IEC958_AES3_CON_FS_32000; + break; + default: + return -ENOENT; + } + + spin_lock_irqsave(&atc->atc_lock, flags); + dao->ops->get_spos(dao, &status); + if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { + status &= ((~IEC958_AES3_CON_FS) << 24); + status |= (iec958_con_fs << 24); + dao->ops->set_spos(dao, status); + dao->ops->commit_write(dao); + } + if ((rate != atc->pll_rate) && (32000 != rate)) { + err = ((struct hw *)atc->hw)->pll_init(atc->hw, rate); + atc->pll_rate = err ? 0 : rate; + } + spin_unlock_irqrestore(&atc->atc_lock, flags); + + return err; +} + +static int +spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) +{ + struct src *src = NULL; + struct amixer *amixer = NULL; + struct dao *dao = NULL; + int err = 0; + int i = 0; + unsigned long flags; + + if (NULL != apcm->src) + return 0; + + /* Configure SPDIFOO and PLL to passthrough mode; + * determine pll_rate. */ + err = spdif_passthru_playback_setup(atc, apcm); + if (err) + return err; + + /* Get needed resources. */ + err = spdif_passthru_playback_get_resources(atc, apcm); + if (err) + return err; + + /* Connect resources */ + src = apcm->src; + for (i = 0; i < apcm->n_amixer; i++) { + amixer = apcm->amixers[i]; + amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); + src = src->ops->next_interleave(src); + if (NULL == src) + src = apcm->src; + } + /* Connect to SPDIFOO */ + spin_lock_irqsave(&atc->atc_lock, flags); + dao = container_of(atc->daios[SPDIFOO], struct dao, daio); + amixer = apcm->amixers[0]; + dao->ops->set_left_input(dao, &amixer->rsc); + amixer = apcm->amixers[1]; + dao->ops->set_right_input(dao, &amixer->rsc); + spin_unlock_irqrestore(&atc->atc_lock, flags); + + return 0; +} + +static int atc_select_line_in(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + struct ct_mixer *mixer = atc->mixer; + struct src *src = NULL; + + if (hw->is_adc_source_selected(hw, ADC_LINEIN)) + return 0; + + mixer->set_input_left(mixer, MIX_MIC_IN, NULL); + mixer->set_input_right(mixer, MIX_MIC_IN, NULL); + + hw->select_adc_source(hw, ADC_LINEIN); + + src = atc->srcs[2]; + mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); + src = atc->srcs[3]; + mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); + + return 0; +} + +static int atc_select_mic_in(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + struct ct_mixer *mixer = atc->mixer; + struct src *src = NULL; + + if (hw->is_adc_source_selected(hw, ADC_MICIN)) + return 0; + + mixer->set_input_left(mixer, MIX_LINE_IN, NULL); + mixer->set_input_right(mixer, MIX_LINE_IN, NULL); + + hw->select_adc_source(hw, ADC_MICIN); + + src = atc->srcs[2]; + mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc); + src = atc->srcs[3]; + mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc); + + return 0; +} + +static int atc_have_digit_io_switch(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + return hw->have_digit_io_switch(hw); +} + +static int atc_select_digit_io(struct ct_atc *atc) +{ + struct hw *hw = atc->hw; + + if (hw->is_adc_source_selected(hw, ADC_NONE)) + return 0; + + hw->select_adc_source(hw, ADC_NONE); + + return 0; +} + +static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type) +{ + struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO]; + + if (state) + daio_mgr->daio_enable(daio_mgr, atc->daios[type]); + else + daio_mgr->daio_disable(daio_mgr, atc->daios[type]); + + daio_mgr->commit_write(daio_mgr); + + return 0; +} + +static int +atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type) +{ + struct dao *dao = container_of(atc->daios[type], struct dao, daio); + return dao->ops->get_spos(dao, status); +} + +static int +atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type) +{ + struct dao *dao = container_of(atc->daios[type], struct dao, daio); + + dao->ops->set_spos(dao, status); + dao->ops->commit_write(dao); + return 0; +} + +static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO1); +} + +static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO4); +} + +static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO3); +} + +static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEO2); +} + +static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, LINEIM); +} + +static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, SPDIFOO); +} + +static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state) +{ + return atc_daio_unmute(atc, state, SPDIFIO); +} + +static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status) +{ + return atc_dao_get_status(atc, status, SPDIFOO); +} + +static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status) +{ + return atc_dao_set_status(atc, status, SPDIFOO); +} + +static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) +{ + unsigned long flags; + struct dao_desc da_dsc = {0}; + struct dao *dao = NULL; + int err = 0; + struct ct_mixer *mixer = atc->mixer; + struct rsc *rscs[2] = {NULL}; + unsigned int spos = 0; + + spin_lock_irqsave(&atc->atc_lock, flags); + dao = container_of(atc->daios[SPDIFOO], struct dao, daio); + da_dsc.msr = state ? 1 : atc->msr; + da_dsc.passthru = state ? 1 : 0; + err = dao->ops->reinit(dao, &da_dsc); + if (state) { + spos = IEC958_DEFAULT_CON; + } else { + mixer->get_output_ports(mixer, MIX_SPDIF_OUT, + &rscs[0], &rscs[1]); + dao->ops->set_left_input(dao, rscs[0]); + dao->ops->set_right_input(dao, rscs[1]); + /* Restore PLL to atc->rsr if needed. */ + if (atc->pll_rate != atc->rsr) { + err = ((struct hw *)atc->hw)->pll_init(atc->hw, + atc->rsr); + atc->pll_rate = err ? 0 : atc->rsr; + } + } + dao->ops->set_spos(dao, spos); + dao->ops->commit_write(dao); + spin_unlock_irqrestore(&atc->atc_lock, flags); + + return err; +} + +static int ct_atc_destroy(struct ct_atc *atc) +{ + struct daio_mgr *daio_mgr = NULL; + struct dao *dao = NULL; + struct dai *dai = NULL; + struct daio *daio = NULL; + struct sum_mgr *sum_mgr = NULL; + struct src_mgr *src_mgr = NULL; + struct srcimp_mgr *srcimp_mgr = NULL; + struct srcimp *srcimp = NULL; + struct ct_mixer *mixer = NULL; + int i = 0; + + if (NULL == atc) + return 0; + + /* Stop hardware and disable all interrupts */ + if (NULL != atc->hw) + ((struct hw *)atc->hw)->card_stop(atc->hw); + + /* Destroy internal mixer objects */ + if (NULL != atc->mixer) { + mixer = atc->mixer; + mixer->set_input_left(mixer, MIX_LINE_IN, NULL); + mixer->set_input_right(mixer, MIX_LINE_IN, NULL); + mixer->set_input_left(mixer, MIX_MIC_IN, NULL); + mixer->set_input_right(mixer, MIX_MIC_IN, NULL); + mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL); + mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL); + ct_mixer_destroy(atc->mixer); + } + + if (NULL != atc->daios) { + daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; + for (i = 0; i < atc->n_daio; i++) { + daio = atc->daios[i]; + if (daio->type < LINEIM) { + dao = container_of(daio, struct dao, daio); + dao->ops->clear_left_input(dao); + dao->ops->clear_right_input(dao); + } else { + dai = container_of(daio, struct dai, daio); + /* some thing to do for dai ... */ + } + daio_mgr->put_daio(daio_mgr, daio); + } + kfree(atc->daios); + } + + if (NULL != atc->pcm) { + sum_mgr = atc->rsc_mgrs[SUM]; + for (i = 0; i < atc->n_pcm; i++) + sum_mgr->put_sum(sum_mgr, atc->pcm[i]); + + kfree(atc->pcm); + } + + if (NULL != atc->srcs) { + src_mgr = atc->rsc_mgrs[SRC]; + for (i = 0; i < atc->n_src; i++) + src_mgr->put_src(src_mgr, atc->srcs[i]); + + kfree(atc->srcs); + } + + if (NULL != atc->srcimps) { + srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + for (i = 0; i < atc->n_srcimp; i++) { + srcimp = atc->srcimps[i]; + srcimp->ops->unmap(srcimp); + srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]); + } + kfree(atc->srcimps); + } + + for (i = 0; i < NUM_RSCTYP; i++) { + if ((NULL != rsc_mgr_funcs[i].destroy) && + (NULL != atc->rsc_mgrs[i])) + rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]); + + } + + if (NULL != atc->hw) + destroy_hw_obj((struct hw *)atc->hw); + + /* Destroy device virtual memory manager object */ + if (NULL != atc->vm) { + ct_vm_destroy(atc->vm); + atc->vm = NULL; + } + + kfree(atc); + + return 0; +} + +static int atc_dev_free(struct snd_device *dev) +{ + struct ct_atc *atc = dev->device_data; + return ct_atc_destroy(atc); +} + +static int atc_identify_card(struct ct_atc *atc) +{ + u16 subsys = 0; + u8 revision = 0; + struct pci_dev *pci = atc->pci; + const struct ct_atc_chip_details *d; + enum CTCARDS i; + + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys); + pci_read_config_byte(pci, PCI_REVISION_ID, &revision); + atc->chip_details = NULL; + atc->model = NUM_CTCARDS; + for (d = atc_chip_details; d->vendor; d++) { + if (d->vendor != pci->vendor || d->device != pci->device) + continue; + + if (NULL == d->sub_details) { + atc->chip_details = d; + break; + } + for (i = 0; i < NUM_CTCARDS; i++) { + if ((d->sub_details[i].subsys == subsys) || + (((subsys & 0x6000) == 0x6000) && + ((d->sub_details[i].subsys & 0x6000) == 0x6000))) { + atc->model = i; + break; + } + } + if (i >= NUM_CTCARDS) + continue; + + atc->chip_details = d; + break; + /* not take revision into consideration now */ + } + if (!d->vendor) + return -ENOENT; + + return 0; +} + +static int ct_create_alsa_devs(struct ct_atc *atc) +{ + enum CTALSADEVS i; + struct hw *hw = atc->hw; + int err; + + switch (hw->get_chip_type(hw)) { + case ATC20K1: + alsa_dev_funcs[MIXER].public_name = "20K1"; + break; + case ATC20K2: + alsa_dev_funcs[MIXER].public_name = "20K2"; + break; + default: + alsa_dev_funcs[MIXER].public_name = "Unknown"; + break; + } + + for (i = 0; i < NUM_CTALSADEVS; i++) { + if (NULL == alsa_dev_funcs[i].create) + continue; + + err = alsa_dev_funcs[i].create(atc, i, + alsa_dev_funcs[i].public_name); + if (err) { + printk(KERN_ERR "Creating alsa device %d failed!\n", i); + return err; + } + } + + return 0; +} + +static int atc_create_hw_devs(struct ct_atc *atc) +{ + struct hw *hw = NULL; + struct card_conf info = {0}; + int i = 0, err = 0; + + err = create_hw_obj(atc->pci, &hw); + if (err) { + printk(KERN_ERR "Failed to create hw obj!!!\n"); + return err; + } + atc->hw = hw; + + /* Initialize card hardware. */ + info.rsr = atc->rsr; + info.msr = atc->msr; + info.vm_pgt_phys = atc_get_ptp_phys(atc, 0); + err = hw->card_init(hw, &info); + if (err < 0) + return err; + + for (i = 0; i < NUM_RSCTYP; i++) { + if (NULL == rsc_mgr_funcs[i].create) + continue; + + err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); + if (err) { + printk(KERN_ERR "Failed to create rsc_mgr %d!!!\n", i); + return err; + } + } + + return 0; +} + +static int atc_get_resources(struct ct_atc *atc) +{ + struct daio_desc da_desc = {0}; + struct daio_mgr *daio_mgr = NULL; + struct src_desc src_dsc = {0}; + struct src_mgr *src_mgr = NULL; + struct srcimp_desc srcimp_dsc = {0}; + struct srcimp_mgr *srcimp_mgr = NULL; + struct sum_desc sum_dsc = {0}; + struct sum_mgr *sum_mgr = NULL; + int err = 0, i = 0; + unsigned short subsys_id = 0; + + atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); + if (NULL == atc->daios) + return -ENOMEM; + + atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + if (NULL == atc->srcs) + return -ENOMEM; + + atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); + if (NULL == atc->srcimps) + return -ENOMEM; + + atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); + if (NULL == atc->pcm) + return -ENOMEM; + + daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; + da_desc.msr = atc->msr; + for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) { + da_desc.type = i; + err = daio_mgr->get_daio(daio_mgr, &da_desc, + (struct daio **)&atc->daios[i]); + if (err) { + printk(KERN_ERR "Failed to get DAIO " + "resource %d!!!\n", i); + return err; + } + atc->n_daio++; + } + pci_read_config_word(atc->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + da_desc.type = SPDIFI1; + } else { + da_desc.type = SPDIFIO; + } + err = daio_mgr->get_daio(daio_mgr, &da_desc, + (struct daio **)&atc->daios[i]); + if (err) { + printk(KERN_ERR "Failed to get S/PDIF-in resource!!!\n"); + return err; + } + atc->n_daio++; + + src_mgr = atc->rsc_mgrs[SRC]; + src_dsc.multi = 1; + src_dsc.msr = atc->msr; + src_dsc.mode = ARCRW; + for (i = 0, atc->n_src = 0; i < (2*2); i++) { + err = src_mgr->get_src(src_mgr, &src_dsc, + (struct src **)&atc->srcs[i]); + if (err) + return err; + + atc->n_src++; + } + + srcimp_mgr = atc->rsc_mgrs[SRCIMP]; + srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */ + for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) { + err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, + (struct srcimp **)&atc->srcimps[i]); + if (err) + return err; + + atc->n_srcimp++; + } + srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */ + for (i = 0; i < (2*1); i++) { + err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, + (struct srcimp **)&atc->srcimps[2*1+i]); + if (err) + return err; + + atc->n_srcimp++; + } + + sum_mgr = atc->rsc_mgrs[SUM]; + sum_dsc.msr = atc->msr; + for (i = 0, atc->n_pcm = 0; i < (2*4); i++) { + err = sum_mgr->get_sum(sum_mgr, &sum_dsc, + (struct sum **)&atc->pcm[i]); + if (err) + return err; + + atc->n_pcm++; + } + + err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); + if (err) { + printk(KERN_ERR "Failed to create mixer obj!!!\n"); + return err; + } + + return 0; +} + +static void +atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, + struct src **srcs, struct srcimp **srcimps) +{ + struct rsc *rscs[2] = {NULL}; + struct src *src = NULL; + struct srcimp *srcimp = NULL; + int i = 0; + + rscs[0] = &dai->daio.rscl; + rscs[1] = &dai->daio.rscr; + for (i = 0; i < 2; i++) { + src = srcs[i]; + srcimp = srcimps[i]; + srcimp->ops->map(srcimp, src, rscs[i]); + src_mgr->src_disable(src_mgr, src); + } + + src_mgr->commit_write(src_mgr); /* Actually disable SRCs */ + + src = srcs[0]; + src->ops->set_pm(src, 1); + for (i = 0; i < 2; i++) { + src = srcs[i]; + src->ops->set_state(src, SRC_STATE_RUN); + src->ops->commit_write(src); + src_mgr->src_enable_s(src_mgr, src); + } + + dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc)); + dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc)); + + dai->ops->set_enb_src(dai, 1); + dai->ops->set_enb_srt(dai, 1); + dai->ops->commit_write(dai); + + src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */ +} + +static void atc_connect_resources(struct ct_atc *atc) +{ + struct dai *dai = NULL; + struct dao *dao = NULL; + struct src *src = NULL; + struct sum *sum = NULL; + struct ct_mixer *mixer = NULL; + struct rsc *rscs[2] = {NULL}; + int i = 0, j = 0; + + mixer = atc->mixer; + + for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) { + mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]); + dao = container_of(atc->daios[j], struct dao, daio); + dao->ops->set_left_input(dao, rscs[0]); + dao->ops->set_right_input(dao, rscs[1]); + } + + dai = container_of(atc->daios[LINEIM], struct dai, daio); + atc_connect_dai(atc->rsc_mgrs[SRC], dai, + (struct src **)&atc->srcs[2], + (struct srcimp **)&atc->srcimps[2]); + src = atc->srcs[2]; + mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc); + src = atc->srcs[3]; + mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc); + + dai = container_of(atc->daios[SPDIFIO], struct dai, daio); + atc_connect_dai(atc->rsc_mgrs[SRC], dai, + (struct src **)&atc->srcs[0], + (struct srcimp **)&atc->srcimps[0]); + + src = atc->srcs[0]; + mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc); + src = atc->srcs[1]; + mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc); + + for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) { + sum = atc->pcm[j]; + mixer->set_input_left(mixer, i, &sum->rsc); + sum = atc->pcm[j+1]; + mixer->set_input_right(mixer, i, &sum->rsc); + } +} + +static void atc_set_ops(struct ct_atc *atc) +{ + /* Set operations */ + atc->map_audio_buffer = ct_map_audio_buffer; + atc->unmap_audio_buffer = ct_unmap_audio_buffer; + atc->pcm_playback_prepare = atc_pcm_playback_prepare; + atc->pcm_release_resources = atc_pcm_release_resources; + atc->pcm_playback_start = atc_pcm_playback_start; + atc->pcm_playback_stop = atc_pcm_stop; + atc->pcm_playback_position = atc_pcm_playback_position; + atc->pcm_capture_prepare = atc_pcm_capture_prepare; + atc->pcm_capture_start = atc_pcm_capture_start; + atc->pcm_capture_stop = atc_pcm_stop; + atc->pcm_capture_position = atc_pcm_capture_position; + atc->spdif_passthru_playback_prepare = spdif_passthru_playback_prepare; + atc->get_ptp_phys = atc_get_ptp_phys; + atc->select_line_in = atc_select_line_in; + atc->select_mic_in = atc_select_mic_in; + atc->select_digit_io = atc_select_digit_io; + atc->line_front_unmute = atc_line_front_unmute; + atc->line_surround_unmute = atc_line_surround_unmute; + atc->line_clfe_unmute = atc_line_clfe_unmute; + atc->line_rear_unmute = atc_line_rear_unmute; + atc->line_in_unmute = atc_line_in_unmute; + atc->spdif_out_unmute = atc_spdif_out_unmute; + atc->spdif_in_unmute = atc_spdif_in_unmute; + atc->spdif_out_get_status = atc_spdif_out_get_status; + atc->spdif_out_set_status = atc_spdif_out_set_status; + atc->spdif_out_passthru = atc_spdif_out_passthru; + atc->have_digit_io_switch = atc_have_digit_io_switch; +} + +/** + * ct_atc_create - create and initialize a hardware manager + * @card: corresponding alsa card object + * @pci: corresponding kernel pci device object + * @ratc: return created object address in it + * + * Creates and initializes a hardware manager. + * + * Creates kmallocated ct_atc structure. Initializes hardware. + * Returns 0 if suceeds, or negative error code if fails. + */ + +int ct_atc_create(struct snd_card *card, struct pci_dev *pci, + unsigned int rsr, unsigned int msr, struct ct_atc **ratc) +{ + struct ct_atc *atc = NULL; + static struct snd_device_ops ops = { + .dev_free = atc_dev_free, + }; + int err = 0; + + *ratc = NULL; + + atc = kzalloc(sizeof(*atc), GFP_KERNEL); + if (NULL == atc) + return -ENOMEM; + + atc->card = card; + atc->pci = pci; + atc->rsr = rsr; + atc->msr = msr; + + /* Set operations */ + atc_set_ops(atc); + + spin_lock_init(&atc->atc_lock); + spin_lock_init(&atc->vm_lock); + + /* Find card model */ + err = atc_identify_card(atc); + if (err < 0) { + printk(KERN_ERR "ctatc: Card not recognised\n"); + goto error1; + } + + /* Set up device virtual memory management object */ + err = ct_vm_create(&atc->vm); + if (err < 0) + goto error1; + + /* Create all atc hw devices */ + err = atc_create_hw_devs(atc); + if (err < 0) + goto error1; + + /* Get resources */ + err = atc_get_resources(atc); + if (err < 0) + goto error1; + + /* Build topology */ + atc_connect_resources(atc); + + atc->create_alsa_devs = ct_create_alsa_devs; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); + if (err < 0) + goto error1; + + snd_card_set_dev(card, &pci->dev); + + *ratc = atc; + return 0; + +error1: + ct_atc_destroy(atc); + printk(KERN_ERR "Something wrong!!!\n"); + return err; +} + diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h new file mode 100644 index 000000000000..286c993d461a --- /dev/null +++ b/sound/pci/ctxfi/ctatc.h @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctatc.h + * + * @Brief + * This file contains the definition of the device resource management object. + * + * @Author Liu Chun + * @Date Mar 28 2008 + * + */ + +#ifndef CTATC_H +#define CTATC_H + +#include +#include +#include +#include +#include + +#include "ctvmem.h" +#include "ctresource.h" + +enum CTALSADEVS { /* Types of alsa devices */ + FRONT, + REAR, + CLFE, + SURROUND, + IEC958, + MIXER, + NUM_CTALSADEVS /* This should always be the last */ +}; + +enum CTCARDS { + CTSB0760, + CTHENDRIX, + CTSB08801, + CTSB08802, + CTSB08803, + NUM_CTCARDS /* This should always be the last */ +}; + +struct ct_atc_chip_sub_details { + u16 subsys; + const char *nm_model; +}; + +struct ct_atc_chip_details { + u16 vendor; + u16 device; + const struct ct_atc_chip_sub_details *sub_details; + const char *nm_card; +}; + +struct ct_atc; + +/* alsa pcm stream descriptor */ +struct ct_atc_pcm { + struct snd_pcm_substream *substream; + void (*interrupt)(struct ct_atc_pcm *apcm); + unsigned int started:1; + unsigned int stop_timer:1; + struct timer_list timer; + spinlock_t timer_lock; + unsigned int position; + + /* Only mono and interleaved modes are supported now. */ + struct ct_vm_block *vm_block; + void *src; /* SRC for interacting with host memory */ + void **srccs; /* SRCs for sample rate conversion */ + void **srcimps; /* SRC Input Mappers */ + void **amixers; /* AMIXERs for routing converted data */ + void *mono; /* A SUM resource for mixing chs to one */ + unsigned char n_srcc; /* Number of converting SRCs */ + unsigned char n_srcimp; /* Number of SRC Input Mappers */ + unsigned char n_amixer; /* Number of AMIXERs */ +}; + +/* Chip resource management object */ +struct ct_atc { + struct pci_dev *pci; + struct snd_card *card; + unsigned int rsr; /* reference sample rate in Hz */ + unsigned int msr; /* master sample rate in rsr */ + unsigned int pll_rate; /* current rate of Phase Lock Loop */ + + const struct ct_atc_chip_details *chip_details; + enum CTCARDS model; + /* Create all alsa devices */ + int (*create_alsa_devs)(struct ct_atc *atc); + + struct ct_vm *vm; /* device virtual memory manager for this card */ + int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index); + + spinlock_t atc_lock; + spinlock_t vm_lock; + + int (*pcm_playback_prepare)(struct ct_atc *atc, + struct ct_atc_pcm *apcm); + int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + int (*pcm_playback_position)(struct ct_atc *atc, + struct ct_atc_pcm *apcm); + int (*spdif_passthru_playback_prepare)(struct ct_atc *atc, + struct ct_atc_pcm *apcm); + int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm); + int (*pcm_capture_position)(struct ct_atc *atc, + struct ct_atc_pcm *apcm); + int (*pcm_release_resources)(struct ct_atc *atc, + struct ct_atc_pcm *apcm); + int (*select_line_in)(struct ct_atc *atc); + int (*select_mic_in)(struct ct_atc *atc); + int (*select_digit_io)(struct ct_atc *atc); + int (*line_front_unmute)(struct ct_atc *atc, unsigned char state); + int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state); + int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state); + int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state); + int (*line_in_unmute)(struct ct_atc *atc, unsigned char state); + int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state); + int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state); + int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status); + int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status); + int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state); + int (*have_digit_io_switch)(struct ct_atc *atc); + + /* Don't touch! Used for internal object. */ + void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */ + void *mixer; /* internal mixer object */ + void *hw; /* chip specific hardware access object */ + void **daios; /* digital audio io resources */ + void **pcm; /* SUMs for collecting all pcm stream */ + void **srcs; /* Sample Rate Converters for input signal */ + void **srcimps; /* input mappers for SRCs */ + unsigned char n_daio; + unsigned char n_src; + unsigned char n_srcimp; + unsigned char n_pcm; +}; + + +int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, + unsigned int rsr, unsigned int msr, + struct ct_atc **ratc); + +#endif /* CTATC_H */ diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c new file mode 100644 index 000000000000..a2aea399eba1 --- /dev/null +++ b/sound/pci/ctxfi/ctdaio.c @@ -0,0 +1,769 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctdaio.c + * + * @Brief + * This file contains the implementation of Digital Audio Input Output + * resource management object. + * + * @Author Liu Chun + * @Date May 23 2008 + * + */ + +#include "ctdaio.h" +#include "cthardware.h" +#include "ctimap.h" +#include +#include + +#define DAIO_RESOURCE_NUM NUM_DAIOTYP +#define DAIO_OUT_MAX SPDIFOO + +union daio_usage { + struct { + unsigned short lineo1:1; + unsigned short lineo2:1; + unsigned short lineo3:1; + unsigned short lineo4:1; + unsigned short spdifoo:1; + unsigned short lineim:1; + unsigned short spdifio:1; + unsigned short spdifi1:1; + } bf; + unsigned short data; +}; + +struct daio_rsc_idx { + unsigned short left; + unsigned short right; +}; + +struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { + [LINEO1] = {.left = 0x00, .right = 0x01}, + [LINEO2] = {.left = 0x18, .right = 0x19}, + [LINEO3] = {.left = 0x08, .right = 0x09}, + [LINEO4] = {.left = 0x10, .right = 0x11}, + [LINEIM] = {.left = 0x1b5, .right = 0x1bd}, + [SPDIFOO] = {.left = 0x20, .right = 0x21}, + [SPDIFIO] = {.left = 0x15, .right = 0x1d}, + [SPDIFI1] = {.left = 0x95, .right = 0x9d}, +}; + +struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { + [LINEO1] = {.left = 0x40, .right = 0x41}, + [LINEO2] = {.left = 0x70, .right = 0x71}, + [LINEO3] = {.left = 0x50, .right = 0x51}, + [LINEO4] = {.left = 0x60, .right = 0x61}, + [LINEIM] = {.left = 0x45, .right = 0xc5}, + [SPDIFOO] = {.left = 0x00, .right = 0x01}, + [SPDIFIO] = {.left = 0x05, .right = 0x85}, +}; + +static int daio_master(struct rsc *rsc) +{ + /* Actually, this is not the resource index of DAIO. + * For DAO, it is the input mapper index. And, for DAI, + * it is the output time-slot index. */ + return rsc->conj = rsc->idx; +} + +static int daio_index(const struct rsc *rsc) +{ + return rsc->conj; +} + +static int daio_out_next_conj(struct rsc *rsc) +{ + return rsc->conj += 2; +} + +static int daio_in_next_conj_20k1(struct rsc *rsc) +{ + return rsc->conj += 0x200; +} + +static int daio_in_next_conj_20k2(struct rsc *rsc) +{ + return rsc->conj += 0x100; +} + +static struct rsc_ops daio_out_rsc_ops = { + .master = daio_master, + .next_conj = daio_out_next_conj, + .index = daio_index, + .output_slot = NULL, +}; + +static struct rsc_ops daio_in_rsc_ops_20k1 = { + .master = daio_master, + .next_conj = daio_in_next_conj_20k1, + .index = NULL, + .output_slot = daio_index, +}; + +static struct rsc_ops daio_in_rsc_ops_20k2 = { + .master = daio_master, + .next_conj = daio_in_next_conj_20k2, + .index = NULL, + .output_slot = daio_index, +}; + +static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) +{ + switch (hw->get_chip_type(hw)) { + case ATC20K1: + switch (type) { + case SPDIFOO: return 0; + case SPDIFIO: return 0; + case SPDIFI1: return 1; + case LINEO1: return 4; + case LINEO2: return 7; + case LINEO3: return 5; + case LINEO4: return 6; + case LINEIM: return 7; + default: return -EINVAL; + } + case ATC20K2: + switch (type) { + case SPDIFOO: return 0; + case SPDIFIO: return 0; + case LINEO1: return 4; + case LINEO2: return 7; + case LINEO3: return 5; + case LINEO4: return 6; + case LINEIM: return 4; + default: return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc); + +static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos) +{ + ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos); + return 0; +} + +static int dao_spdif_set_spos(struct dao *dao, unsigned int spos) +{ + ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos); + return 0; +} + +static int dao_commit_write(struct dao *dao) +{ + ((struct hw *)dao->hw)->dao_commit_write(dao->hw, + daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk); + return 0; +} + +static int dao_set_left_input(struct dao *dao, struct rsc *input) +{ + struct imapper *entry = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); + if (NULL == entry) + return -ENOMEM; + + /* Program master and conjugate resources */ + input->ops->master(input); + daio->rscl.ops->master(&daio->rscl); + for (i = 0; i < daio->rscl.msr; i++, entry++) { + entry->slot = input->ops->output_slot(input); + entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl); + dao->mgr->imap_add(dao->mgr, entry); + dao->imappers[i] = entry; + + input->ops->next_conj(input); + daio->rscl.ops->next_conj(&daio->rscl); + } + input->ops->master(input); + daio->rscl.ops->master(&daio->rscl); + + return 0; +} + +static int dao_set_right_input(struct dao *dao, struct rsc *input) +{ + struct imapper *entry = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); + if (NULL == entry) + return -ENOMEM; + + /* Program master and conjugate resources */ + input->ops->master(input); + daio->rscr.ops->master(&daio->rscr); + for (i = 0; i < daio->rscr.msr; i++, entry++) { + entry->slot = input->ops->output_slot(input); + entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr); + dao->mgr->imap_add(dao->mgr, entry); + dao->imappers[daio->rscl.msr + i] = entry; + + input->ops->next_conj(input); + daio->rscr.ops->next_conj(&daio->rscr); + } + input->ops->master(input); + daio->rscr.ops->master(&daio->rscr); + + return 0; +} + +static int dao_clear_left_input(struct dao *dao) +{ + struct imapper *entry = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + if (NULL == dao->imappers[0]) + return 0; + + entry = dao->imappers[0]; + dao->mgr->imap_delete(dao->mgr, entry); + /* Program conjugate resources */ + for (i = 1; i < daio->rscl.msr; i++) { + entry = dao->imappers[i]; + dao->mgr->imap_delete(dao->mgr, entry); + dao->imappers[i] = NULL; + } + + kfree(dao->imappers[0]); + dao->imappers[0] = NULL; + + return 0; +} + +static int dao_clear_right_input(struct dao *dao) +{ + struct imapper *entry = NULL; + struct daio *daio = &dao->daio; + int i = 0; + + if (NULL == dao->imappers[daio->rscl.msr]) + return 0; + + entry = dao->imappers[daio->rscl.msr]; + dao->mgr->imap_delete(dao->mgr, entry); + /* Program conjugate resources */ + for (i = 1; i < daio->rscr.msr; i++) { + entry = dao->imappers[daio->rscl.msr + i]; + dao->mgr->imap_delete(dao->mgr, entry); + dao->imappers[daio->rscl.msr + i] = NULL; + } + + kfree(dao->imappers[daio->rscl.msr]); + dao->imappers[daio->rscl.msr] = NULL; + + return 0; +} + +static struct dao_rsc_ops dao_ops = { + .set_spos = dao_spdif_set_spos, + .commit_write = dao_commit_write, + .get_spos = dao_spdif_get_spos, + .reinit = dao_rsc_reinit, + .set_left_input = dao_set_left_input, + .set_right_input = dao_set_right_input, + .clear_left_input = dao_clear_left_input, + .clear_right_input = dao_clear_right_input, +}; + +static int dai_set_srt_srcl(struct dai *dai, struct rsc *src) +{ + src->ops->master(src); + ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk, + src->ops->index(src)); + return 0; +} + +static int dai_set_srt_srcr(struct dai *dai, struct rsc *src) +{ + src->ops->master(src); + ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk, + src->ops->index(src)); + return 0; +} + +static int dai_set_srt_msr(struct dai *dai, unsigned int msr) +{ + unsigned int rsr = 0; + + for (rsr = 0; msr > 1; msr >>= 1) + rsr++; + + ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr); + return 0; +} + +static int dai_set_enb_src(struct dai *dai, unsigned int enb) +{ + ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb); + return 0; +} + +static int dai_set_enb_srt(struct dai *dai, unsigned int enb) +{ + ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb); + return 0; +} + +static int dai_commit_write(struct dai *dai) +{ + ((struct hw *)dai->hw)->dai_commit_write(dai->hw, + daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); + return 0; +} + +static struct dai_rsc_ops dai_ops = { + .set_srt_srcl = dai_set_srt_srcl, + .set_srt_srcr = dai_set_srt_srcr, + .set_srt_msr = dai_set_srt_msr, + .set_enb_src = dai_set_enb_src, + .set_enb_srt = dai_set_enb_srt, + .commit_write = dai_commit_write, +}; + +static int daio_rsc_init(struct daio *daio, + const struct daio_desc *desc, + void *hw) +{ + int err = 0; + unsigned int idx_l = 0, idx_r = 0; + + switch (((struct hw *)hw)->get_chip_type(hw)) { + case ATC20K1: + idx_l = idx_20k1[desc->type].left; + idx_r = idx_20k1[desc->type].right; + break; + case ATC20K2: + idx_l = idx_20k2[desc->type].left; + idx_r = idx_20k2[desc->type].right; + break; + default: + return -EINVAL; + } + err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw); + if (err) + return err; + + err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw); + if (err) + goto error1; + + /* Set daio->rscl/r->ops to daio specific ones */ + if (desc->type <= DAIO_OUT_MAX) { + daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; + } else { + switch (((struct hw *)hw)->get_chip_type(hw)) { + case ATC20K1: + daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1; + break; + case ATC20K2: + daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2; + break; + default: + break; + } + } + daio->type = desc->type; + + return 0; + +error1: + rsc_uninit(&daio->rscl); + return err; +} + +static int daio_rsc_uninit(struct daio *daio) +{ + rsc_uninit(&daio->rscl); + rsc_uninit(&daio->rscr); + + return 0; +} + +static int dao_rsc_init(struct dao *dao, + const struct daio_desc *desc, + struct daio_mgr *mgr) +{ + struct hw *hw = mgr->mgr.hw; + unsigned int conf = 0; + int err = 0; + + err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw); + if (err) + return err; + + dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL); + if (NULL == dao->imappers) { + err = -ENOMEM; + goto error1; + } + dao->ops = &dao_ops; + dao->mgr = mgr; + dao->hw = hw; + err = hw->dao_get_ctrl_blk(&dao->ctrl_blk); + if (err) + goto error2; + + hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, + daio_device_index(dao->daio.type, hw)); + hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); + + conf |= (desc->msr & 0x7) | (desc->passthru << 3); + hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk, + daio_device_index(dao->daio.type, hw), conf); + hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, + daio_device_index(dao->daio.type, hw)); + hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); + + return 0; + +error2: + kfree(dao->imappers); + dao->imappers = NULL; +error1: + daio_rsc_uninit(&dao->daio); + return err; +} + +static int dao_rsc_uninit(struct dao *dao) +{ + if (NULL != dao->imappers) { + if (NULL != dao->imappers[0]) + dao_clear_left_input(dao); + + if (NULL != dao->imappers[dao->daio.rscl.msr]) + dao_clear_right_input(dao); + + kfree(dao->imappers); + dao->imappers = NULL; + } + ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk); + dao->hw = dao->ctrl_blk = NULL; + daio_rsc_uninit(&dao->daio); + + return 0; +} + +static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc) +{ + struct daio_mgr *mgr = dao->mgr; + struct daio_desc dsc = {0}; + + dsc.type = dao->daio.type; + dsc.msr = desc->msr; + dsc.passthru = desc->passthru; + dao_rsc_uninit(dao); + return dao_rsc_init(dao, &dsc, mgr); +} + +static int dai_rsc_init(struct dai *dai, + const struct daio_desc *desc, + struct daio_mgr *mgr) +{ + int err = 0; + struct hw *hw = mgr->mgr.hw; + unsigned int rsr = 0, msr = 0; + + err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw); + if (err) + return err; + + dai->ops = &dai_ops; + dai->hw = mgr->mgr.hw; + err = hw->dai_get_ctrl_blk(&dai->ctrl_blk); + if (err) + goto error1; + + for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1) + rsr++; + + hw->dai_srt_set_rsr(dai->ctrl_blk, rsr); + hw->dai_srt_set_drat(dai->ctrl_blk, 0); + /* default to disabling control of a SRC */ + hw->dai_srt_set_ec(dai->ctrl_blk, 0); + hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */ + hw->dai_commit_write(hw, + daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk); + + return 0; + +error1: + daio_rsc_uninit(&dai->daio); + return err; +} + +static int dai_rsc_uninit(struct dai *dai) +{ + ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk); + dai->hw = dai->ctrl_blk = NULL; + daio_rsc_uninit(&dai->daio); + return 0; +} + +static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) +{ + if (((union daio_usage *)mgr->rscs)->data & (0x1 << type)) + return -ENOENT; + + ((union daio_usage *)mgr->rscs)->data |= (0x1 << type); + + return 0; +} + +static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type) +{ + ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type); + + return 0; +} + +static int get_daio_rsc(struct daio_mgr *mgr, + const struct daio_desc *desc, + struct daio **rdaio) +{ + int err = 0; + struct dai *dai = NULL; + struct dao *dao = NULL; + unsigned long flags; + + *rdaio = NULL; + + /* Check whether there are sufficient daio resources to meet request. */ + spin_lock_irqsave(&mgr->mgr_lock, flags); + err = daio_mgr_get_rsc(&mgr->mgr, desc->type); + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + if (err) { + printk(KERN_ERR "Can't meet DAIO resource request!\n"); + return err; + } + + /* Allocate mem for daio resource */ + if (desc->type <= DAIO_OUT_MAX) { + dao = kzalloc(sizeof(*dao), GFP_KERNEL); + if (NULL == dao) { + err = -ENOMEM; + goto error; + } + err = dao_rsc_init(dao, desc, mgr); + if (err) + goto error; + + *rdaio = &dao->daio; + } else { + dai = kzalloc(sizeof(*dai), GFP_KERNEL); + if (NULL == dai) { + err = -ENOMEM; + goto error; + } + err = dai_rsc_init(dai, desc, mgr); + if (err) + goto error; + + *rdaio = &dai->daio; + } + + mgr->daio_enable(mgr, *rdaio); + mgr->commit_write(mgr); + + return 0; + +error: + if (NULL != dao) + kfree(dao); + else if (NULL != dai) + kfree(dai); + + spin_lock_irqsave(&mgr->mgr_lock, flags); + daio_mgr_put_rsc(&mgr->mgr, desc->type); + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + return err; +} + +static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio) +{ + unsigned long flags; + + mgr->daio_disable(mgr, daio); + mgr->commit_write(mgr); + + spin_lock_irqsave(&mgr->mgr_lock, flags); + daio_mgr_put_rsc(&mgr->mgr, daio->type); + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + + if (daio->type <= DAIO_OUT_MAX) { + dao_rsc_uninit(container_of(daio, struct dao, daio)); + kfree(container_of(daio, struct dao, daio)); + } else { + dai_rsc_uninit(container_of(daio, struct dai, daio)); + kfree(container_of(daio, struct dai, daio)); + } + + return 0; +} + +static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio) +{ + struct hw *hw = mgr->mgr.hw; + + if (DAIO_OUT_MAX >= daio->type) { + hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, + daio_device_index(daio->type, hw)); + } else { + hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk, + daio_device_index(daio->type, hw)); + } + return 0; +} + +static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio) +{ + struct hw *hw = mgr->mgr.hw; + + if (DAIO_OUT_MAX >= daio->type) { + hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, + daio_device_index(daio->type, hw)); + } else { + hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk, + daio_device_index(daio->type, hw)); + } + return 0; +} + +static int daio_map_op(void *data, struct imapper *entry) +{ + struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr; + struct hw *hw = mgr->hw; + + hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot); + hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next); + hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr); + hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk); + + return 0; +} + +static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&mgr->imap_lock, flags); + if ((0 == entry->addr) && (mgr->init_imap_added)) { + input_mapper_delete(&mgr->imappers, mgr->init_imap, + daio_map_op, mgr); + mgr->init_imap_added = 0; + } + err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr); + spin_unlock_irqrestore(&mgr->imap_lock, flags); + + return err; +} + +static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&mgr->imap_lock, flags); + err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr); + if (list_empty(&mgr->imappers)) { + input_mapper_add(&mgr->imappers, mgr->init_imap, + daio_map_op, mgr); + mgr->init_imap_added = 1; + } + spin_unlock_irqrestore(&mgr->imap_lock, flags); + + return err; +} + +static int daio_mgr_commit_write(struct daio_mgr *mgr) +{ + struct hw *hw = mgr->mgr.hw; + + hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); + return 0; +} + +int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) +{ + int err = 0, i = 0; + struct daio_mgr *daio_mgr; + struct imapper *entry; + + *rdaio_mgr = NULL; + daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL); + if (NULL == daio_mgr) + return -ENOMEM; + + err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw); + if (err) + goto error1; + + spin_lock_init(&daio_mgr->mgr_lock); + spin_lock_init(&daio_mgr->imap_lock); + INIT_LIST_HEAD(&daio_mgr->imappers); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (NULL == entry) { + err = -ENOMEM; + goto error2; + } + entry->slot = entry->addr = entry->next = entry->user = 0; + list_add(&entry->list, &daio_mgr->imappers); + daio_mgr->init_imap = entry; + daio_mgr->init_imap_added = 1; + + daio_mgr->get_daio = get_daio_rsc; + daio_mgr->put_daio = put_daio_rsc; + daio_mgr->daio_enable = daio_mgr_enb_daio; + daio_mgr->daio_disable = daio_mgr_dsb_daio; + daio_mgr->imap_add = daio_imap_add; + daio_mgr->imap_delete = daio_imap_delete; + daio_mgr->commit_write = daio_mgr_commit_write; + + for (i = 0; i < 8; i++) { + ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i); + ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i); + } + ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk); + + *rdaio_mgr = daio_mgr; + + return 0; + +error2: + rsc_mgr_uninit(&daio_mgr->mgr); +error1: + kfree(daio_mgr); + return err; +} + +int daio_mgr_destroy(struct daio_mgr *daio_mgr) +{ + unsigned long flags; + + /* free daio input mapper list */ + spin_lock_irqsave(&daio_mgr->imap_lock, flags); + free_input_mapper_list(&daio_mgr->imappers); + spin_unlock_irqrestore(&daio_mgr->imap_lock, flags); + + rsc_mgr_uninit(&daio_mgr->mgr); + kfree(daio_mgr); + + return 0; +} + diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h new file mode 100644 index 000000000000..0f52ce571ee8 --- /dev/null +++ b/sound/pci/ctxfi/ctdaio.h @@ -0,0 +1,122 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctdaio.h + * + * @Brief + * This file contains the definition of Digital Audio Input Output + * resource management object. + * + * @Author Liu Chun + * @Date May 23 2008 + * + */ + +#ifndef CTDAIO_H +#define CTDAIO_H + +#include "ctresource.h" +#include "ctimap.h" +#include +#include + +/* Define the descriptor of a daio resource */ +enum DAIOTYP { + LINEO1, + LINEO2, + LINEO3, + LINEO4, + SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */ + LINEIM, + SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */ + SPDIFI1, /* S/PDIF In on internal Drive Bay */ + NUM_DAIOTYP +}; + +struct dao_rsc_ops; +struct dai_rsc_ops; +struct daio_mgr; + +struct daio { + struct rsc rscl; /* Basic resource info for left TX/RX */ + struct rsc rscr; /* Basic resource info for right TX/RX */ + enum DAIOTYP type; +}; + +struct dao { + struct daio daio; + struct dao_rsc_ops *ops; /* DAO specific operations */ + struct imapper **imappers; + struct daio_mgr *mgr; + void *hw; + void *ctrl_blk; +}; + +struct dai { + struct daio daio; + struct dai_rsc_ops *ops; /* DAI specific operations */ + void *hw; + void *ctrl_blk; +}; + +struct dao_desc { + unsigned int msr:4; + unsigned int passthru:1; +}; + +struct dao_rsc_ops { + int (*set_spos)(struct dao *dao, unsigned int spos); + int (*commit_write)(struct dao *dao); + int (*get_spos)(struct dao *dao, unsigned int *spos); + int (*reinit)(struct dao *dao, const struct dao_desc *desc); + int (*set_left_input)(struct dao *dao, struct rsc *input); + int (*set_right_input)(struct dao *dao, struct rsc *input); + int (*clear_left_input)(struct dao *dao); + int (*clear_right_input)(struct dao *dao); +}; + +struct dai_rsc_ops { + int (*set_srt_srcl)(struct dai *dai, struct rsc *src); + int (*set_srt_srcr)(struct dai *dai, struct rsc *src); + int (*set_srt_msr)(struct dai *dai, unsigned int msr); + int (*set_enb_src)(struct dai *dai, unsigned int enb); + int (*set_enb_srt)(struct dai *dai, unsigned int enb); + int (*commit_write)(struct dai *dai); +}; + +/* Define daio resource request description info */ +struct daio_desc { + unsigned int type:4; + unsigned int msr:4; + unsigned int passthru:1; +}; + +struct daio_mgr { + struct rsc_mgr mgr; /* Basic resource manager info */ + spinlock_t mgr_lock; + spinlock_t imap_lock; + struct list_head imappers; + struct imapper *init_imap; + unsigned int init_imap_added; + + /* request one daio resource */ + int (*get_daio)(struct daio_mgr *mgr, + const struct daio_desc *desc, struct daio **rdaio); + /* return one daio resource */ + int (*put_daio)(struct daio_mgr *mgr, struct daio *daio); + int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio); + int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio); + int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry); + int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry); + int (*commit_write)(struct daio_mgr *mgr); +}; + +/* Constructor and destructor of daio resource manager */ +int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr); +int daio_mgr_destroy(struct daio_mgr *daio_mgr); + +#endif /* CTDAIO_H */ diff --git a/sound/pci/ctxfi/ctdrv.h b/sound/pci/ctxfi/ctdrv.h new file mode 100644 index 000000000000..f776a44f52cf --- /dev/null +++ b/sound/pci/ctxfi/ctdrv.h @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @file ctdrv.h + * + * @breaf + * This file contains the definition of card IDs supported by this driver. + * + * @author Liu Chun + * + */ + +#ifndef CTDRV_H +#define CTDRV_H + +#define PCI_VENDOR_CREATIVE 0x1102 +#define PCI_DEVICE_CREATIVE_20K1 0x0005 +#define PCI_DEVICE_CREATIVE_20K2 0x000B +#define PCI_SUBVENDOR_CREATIVE 0x1102 +#define PCI_SUBSYS_CREATIVE_SB0760 0x0024 +#define PCI_SUBSYS_CREATIVE_SB08801 0x0041 +#define PCI_SUBSYS_CREATIVE_SB08802 0x0042 +#define PCI_SUBSYS_CREATIVE_SB08803 0x0043 +#define PCI_SUBSYS_CREATIVE_HENDRIX 0x6000 + +#endif /* CTDRV_H */ diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c new file mode 100644 index 000000000000..8e58860f641c --- /dev/null +++ b/sound/pci/ctxfi/cthardware.c @@ -0,0 +1,108 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File cthardware.c + * + * @Brief + * This file contains the implementation of hardware access methord. + * + * @Author Liu Chun + * @Date Jun 26 2008 + * + */ + +#include "cthardware.h" +#include "cthw20k1.h" +#include "cthw20k2.h" +#include + +static enum CHIPTYP get_chip_type(struct hw *hw) +{ + enum CHIPTYP type = ATCNONE; + + switch (hw->pci->device) { + case 0x0005: /* 20k1 device */ + type = ATC20K1; + break; + case 0x000B: /* 20k2 device */ + type = ATC20K2; + break; + default: + type = ATCNONE; + break; + } + + return type; +} + +int create_hw_obj(struct pci_dev *pci, struct hw **rhw) +{ + int err = 0; + + switch (pci->device) { + case 0x0005: /* 20k1 device */ + err = create_20k1_hw_obj(rhw); + break; + case 0x000B: /* 20k2 device */ + err = create_20k2_hw_obj(rhw); + break; + default: + err = -ENODEV; + break; + } + if (err) + return err; + + (*rhw)->pci = pci; + (*rhw)->get_chip_type = get_chip_type; + + return 0; +} + +int destroy_hw_obj(struct hw *hw) +{ + int err = 0; + + switch (hw->pci->device) { + case 0x0005: /* 20k1 device */ + err = destroy_20k1_hw_obj(hw); + break; + case 0x000B: /* 20k2 device */ + err = destroy_20k2_hw_obj(hw); + break; + default: + err = -ENODEV; + break; + } + + return err; +} + +unsigned int get_field(unsigned int data, unsigned int field) +{ + int i; + + BUG_ON(!field); + /* @field should always be greater than 0 */ + for (i = 0; !(field & (1 << i)); ) + i++; + + return (data & field) >> i; +} + +void set_field(unsigned int *data, unsigned int field, unsigned int value) +{ + int i; + + BUG_ON(!field); + /* @field should always be greater than 0 */ + for (i = 0; !(field & (1 << i)); ) + i++; + + *data = (*data & (~field)) | ((value << i) & field); +} + diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h new file mode 100644 index 000000000000..b0512df8b334 --- /dev/null +++ b/sound/pci/ctxfi/cthardware.h @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File cthardware.h + * + * @Brief + * This file contains the definition of hardware access methord. + * + * @Author Liu Chun + * @Date May 13 2008 + * + */ + +#ifndef CTHARDWARE_H +#define CTHARDWARE_H + +#include +#include + +enum CHIPTYP { + ATC20K1, + ATC20K2, + ATCNONE +}; + +/* Type of input source for ADC */ +enum ADCSRC{ + ADC_MICIN, + ADC_LINEIN, + ADC_VIDEO, + ADC_AUX, + ADC_NONE /* Switch to digital input */ +}; + +struct card_conf { + /* device virtual mem page table page physical addr + * (supporting one page table page now) */ + unsigned long vm_pgt_phys; + unsigned int rsr; /* reference sample rate in Hzs*/ + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct hw { + int (*card_init)(struct hw *hw, struct card_conf *info); + int (*card_stop)(struct hw *hw); + int (*pll_init)(struct hw *hw, unsigned int rsr); + enum CHIPTYP (*get_chip_type)(struct hw *hw); + int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); + int (*select_adc_source)(struct hw *hw, enum ADCSRC source); + int (*have_digit_io_switch)(struct hw *hw); + + /* SRC operations */ + int (*src_rsc_get_ctrl_blk)(void **rblk); + int (*src_rsc_put_ctrl_blk)(void *blk); + int (*src_set_state)(void *blk, unsigned int state); + int (*src_set_bm)(void *blk, unsigned int bm); + int (*src_set_rsr)(void *blk, unsigned int rsr); + int (*src_set_sf)(void *blk, unsigned int sf); + int (*src_set_wr)(void *blk, unsigned int wr); + int (*src_set_pm)(void *blk, unsigned int pm); + int (*src_set_rom)(void *blk, unsigned int rom); + int (*src_set_vo)(void *blk, unsigned int vo); + int (*src_set_st)(void *blk, unsigned int st); + int (*src_set_ie)(void *blk, unsigned int ie); + int (*src_set_ilsz)(void *blk, unsigned int ilsz); + int (*src_set_bp)(void *blk, unsigned int bp); + int (*src_set_cisz)(void *blk, unsigned int cisz); + int (*src_set_ca)(void *blk, unsigned int ca); + int (*src_set_sa)(void *blk, unsigned int sa); + int (*src_set_la)(void *blk, unsigned int la); + int (*src_set_pitch)(void *blk, unsigned int pitch); + int (*src_set_clear_zbufs)(void *blk, unsigned int clear); + int (*src_set_dirty)(void *blk, unsigned int flags); + int (*src_set_dirty_all)(void *blk); + int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk); + int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk); + unsigned int (*src_get_dirty)(void *blk); + unsigned int (*src_dirty_conj_mask)(void); + int (*src_mgr_get_ctrl_blk)(void **rblk); + int (*src_mgr_put_ctrl_blk)(void *blk); + /* syncly enable src @idx */ + int (*src_mgr_enbs_src)(void *blk, unsigned int idx); + /* enable src @idx */ + int (*src_mgr_enb_src)(void *blk, unsigned int idx); + /* disable src @idx */ + int (*src_mgr_dsb_src)(void *blk, unsigned int idx); + int (*src_mgr_commit_write)(struct hw *hw, void *blk); + + /* SRC Input Mapper operations */ + int (*srcimp_mgr_get_ctrl_blk)(void **rblk); + int (*srcimp_mgr_put_ctrl_blk)(void *blk); + int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot); + int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user); + int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next); + int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr); + int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk); + + /* AMIXER operations */ + int (*amixer_rsc_get_ctrl_blk)(void **rblk); + int (*amixer_rsc_put_ctrl_blk)(void *blk); + int (*amixer_mgr_get_ctrl_blk)(void **rblk); + int (*amixer_mgr_put_ctrl_blk)(void *blk); + int (*amixer_set_mode)(void *blk, unsigned int mode); + int (*amixer_set_iv)(void *blk, unsigned int iv); + int (*amixer_set_x)(void *blk, unsigned int x); + int (*amixer_set_y)(void *blk, unsigned int y); + int (*amixer_set_sadr)(void *blk, unsigned int sadr); + int (*amixer_set_se)(void *blk, unsigned int se); + int (*amixer_set_dirty)(void *blk, unsigned int flags); + int (*amixer_set_dirty_all)(void *blk); + int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk); + int (*amixer_get_y)(void *blk); + unsigned int (*amixer_get_dirty)(void *blk); + + /* DAIO operations */ + int (*dai_get_ctrl_blk)(void **rblk); + int (*dai_put_ctrl_blk)(void *blk); + int (*dai_srt_set_srco)(void *blk, unsigned int src); + int (*dai_srt_set_srcm)(void *blk, unsigned int src); + int (*dai_srt_set_rsr)(void *blk, unsigned int rsr); + int (*dai_srt_set_drat)(void *blk, unsigned int drat); + int (*dai_srt_set_ec)(void *blk, unsigned int ec); + int (*dai_srt_set_et)(void *blk, unsigned int et); + int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk); + int (*dao_get_ctrl_blk)(void **rblk); + int (*dao_put_ctrl_blk)(void *blk); + int (*dao_set_spos)(void *blk, unsigned int spos); + int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk); + int (*dao_get_spos)(void *blk, unsigned int *spos); + + int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk); + int (*daio_mgr_put_ctrl_blk)(void *blk); + int (*daio_mgr_enb_dai)(void *blk, unsigned int idx); + int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx); + int (*daio_mgr_enb_dao)(void *blk, unsigned int idx); + int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx); + int (*daio_mgr_dao_init)(void *blk, unsigned int idx, + unsigned int conf); + int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot); + int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next); + int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); + int (*daio_mgr_commit_write)(struct hw *hw, void *blk); + + struct pci_dev *pci; /* the pci kernel structure of this card */ + int irq; + unsigned long io_base; + unsigned long mem_base; +}; + +int create_hw_obj(struct pci_dev *pci, struct hw **rhw); +int destroy_hw_obj(struct hw *hw); + +unsigned int get_field(unsigned int data, unsigned int field); +void set_field(unsigned int *data, unsigned int field, unsigned int value); + +#endif /* CTHARDWARE_H */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c new file mode 100644 index 000000000000..53572d92ef5d --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.c @@ -0,0 +1,2230 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File cthw20k1.c + * + * @Brief + * This file contains the implementation of hardware access methord for 20k1. + * + * @Author Liu Chun + * @Date Jun 24 2008 + * + */ + +#include "cthw20k1.h" +#include "ct20k1reg.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ + +struct hw20k1 { + struct hw hw; + spinlock_t reg_20k1_lock; + spinlock_t reg_pci_lock; +}; + +static u32 hw_read_20kx(struct hw *hw, u32 reg); +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); +static u32 hw_read_pci(struct hw *hw, u32 reg); +static void hw_write_pci(struct hw *hw, u32 reg, u32 data); + +/* + * Type definition block. + * The layout of control structures can be directly applied on 20k2 chip. + */ + +/* + * SRC control block definitions. + */ + +/* SRC resource control block */ +#define SRCCTL_STATE 0x00000007 +#define SRCCTL_BM 0x00000008 +#define SRCCTL_RSR 0x00000030 +#define SRCCTL_SF 0x000001C0 +#define SRCCTL_WR 0x00000200 +#define SRCCTL_PM 0x00000400 +#define SRCCTL_ROM 0x00001800 +#define SRCCTL_VO 0x00002000 +#define SRCCTL_ST 0x00004000 +#define SRCCTL_IE 0x00008000 +#define SRCCTL_ILSZ 0x000F0000 +#define SRCCTL_BP 0x00100000 + +#define SRCCCR_CISZ 0x000007FF +#define SRCCCR_CWA 0x001FF800 +#define SRCCCR_D 0x00200000 +#define SRCCCR_RS 0x01C00000 +#define SRCCCR_NAL 0x3E000000 +#define SRCCCR_RA 0xC0000000 + +#define SRCCA_CA 0x03FFFFFF +#define SRCCA_RS 0x1C000000 +#define SRCCA_NAL 0xE0000000 + +#define SRCSA_SA 0x03FFFFFF + +#define SRCLA_LA 0x03FFFFFF + +/* Mixer Parameter Ring ram Low and Hight register. + * Fixed-point value in 8.24 format for parameter channel */ +#define MPRLH_PITCH 0xFFFFFFFF + +/* SRC resource register dirty flags */ +union src_dirty { + struct { + u16 ctl:1; + u16 ccr:1; + u16 sa:1; + u16 la:1; + u16 ca:1; + u16 mpr:1; + u16 czbfs:1; /* Clear Z-Buffers */ + u16 rsv:9; + } bf; + u16 data; +}; + +struct src_rsc_ctrl_blk { + unsigned int ctl; + unsigned int ccr; + unsigned int ca; + unsigned int sa; + unsigned int la; + unsigned int mpr; + union src_dirty dirty; +}; + +/* SRC manager control block */ +union src_mgr_dirty { + struct { + u16 enb0:1; + u16 enb1:1; + u16 enb2:1; + u16 enb3:1; + u16 enb4:1; + u16 enb5:1; + u16 enb6:1; + u16 enb7:1; + u16 enbsa:1; + u16 rsv:7; + } bf; + u16 data; +}; + +struct src_mgr_ctrl_blk { + unsigned int enbsa; + unsigned int enb[8]; + union src_mgr_dirty dirty; +}; + +/* SRCIMP manager control block */ +#define SRCAIM_ARC 0x00000FFF +#define SRCAIM_NXT 0x00FF0000 +#define SRCAIM_SRC 0xFF000000 + +struct srcimap { + unsigned int srcaim; + unsigned int idx; +}; + +/* SRCIMP manager register dirty flags */ +union srcimp_mgr_dirty { + struct { + u16 srcimap:1; + u16 rsv:15; + } bf; + u16 data; +}; + +struct srcimp_mgr_ctrl_blk { + struct srcimap srcimap; + union srcimp_mgr_dirty dirty; +}; + +/* + * Function implementation block. + */ + +static int src_get_rsc_ctrl_blk(void **rblk) +{ + struct src_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_put_rsc_ctrl_blk(void *blk) +{ + kfree((struct src_rsc_ctrl_blk *)blk); + + return 0; +} + +static int src_set_state(void *blk, unsigned int state) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_STATE, state); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bm(void *blk, unsigned int bm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BM, bm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rsr(void *blk, unsigned int rsr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_RSR, rsr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_sf(void *blk, unsigned int sf) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_SF, sf); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_wr(void *blk, unsigned int wr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_WR, wr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_pm(void *blk, unsigned int pm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_PM, pm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rom(void *blk, unsigned int rom) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ROM, rom); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_vo(void *blk, unsigned int vo) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_VO, vo); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_st(void *blk, unsigned int st) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ST, st); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ie(void *blk, unsigned int ie) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_IE, ie); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ilsz(void *blk, unsigned int ilsz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bp(void *blk, unsigned int bp) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BP, bp); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_cisz(void *blk, unsigned int cisz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ccr, SRCCCR_CISZ, cisz); + ctl->dirty.bf.ccr = 1; + return 0; +} + +static int src_set_ca(void *blk, unsigned int ca) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ca, SRCCA_CA, ca); + ctl->dirty.bf.ca = 1; + return 0; +} + +static int src_set_sa(void *blk, unsigned int sa) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->sa, SRCSA_SA, sa); + ctl->dirty.bf.sa = 1; + return 0; +} + +static int src_set_la(void *blk, unsigned int la) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->la, SRCLA_LA, la); + ctl->dirty.bf.la = 1; + return 0; +} + +static int src_set_pitch(void *blk, unsigned int pitch) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->mpr, MPRLH_PITCH, pitch); + ctl->dirty.bf.mpr = 1; + return 0; +} + +static int src_set_clear_zbufs(void *blk, unsigned int clear) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); + return 0; +} + +static int src_set_dirty(void *blk, unsigned int flags) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int src_set_dirty_all(void *blk) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +#define AR_SLOT_SIZE 4096 +#define AR_SLOT_BLOCK_SIZE 16 +#define AR_PTS_PITCH 6 +#define AR_PARAM_SRC_OFFSET 0x60 + +static unsigned int src_param_pitch_mixer(unsigned int src_idx) +{ + return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE + - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; + +} + +static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + int i = 0; + + if (ctl->dirty.bf.czbfs) { + /* Clear Z-Buffer registers */ + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0); + + for (i = 0; i < 4; i++) + hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0); + + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0); + + ctl->dirty.bf.czbfs = 0; + } + if (ctl->dirty.bf.mpr) { + /* Take the parameter mixer resource in the same group as that + * the idx src is in for simplicity. Unlike src, all conjugate + * parameter mixer resources must be programmed for + * corresponding conjugate src resources. */ + unsigned int pm_idx = src_param_pitch_mixer(idx); + hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr); + hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3); + hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0); + ctl->dirty.bf.mpr = 0; + } + if (ctl->dirty.bf.sa) { + hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa); + ctl->dirty.bf.sa = 0; + } + if (ctl->dirty.bf.la) { + hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la); + ctl->dirty.bf.la = 0; + } + if (ctl->dirty.bf.ca) { + hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca); + ctl->dirty.bf.ca = 0; + } + + /* Write srccf register */ + hw_write_20kx(hw, SRCCF+idx*0x100, 0x0); + + if (ctl->dirty.bf.ccr) { + hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr); + ctl->dirty.bf.ccr = 0; + } + if (ctl->dirty.bf.ctl) { + hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl); + ctl->dirty.bf.ctl = 0; + } + + return 0; +} + +static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100); + ctl->dirty.bf.ca = 0; + + return get_field(ctl->ca, SRCCA_CA); +} + +static unsigned int src_get_dirty(void *blk) +{ + return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; +} + +static unsigned int src_dirty_conj_mask(void) +{ + return 0x20; +} + +static int src_mgr_enbs_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0); + ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + return 0; +} + +static int src_mgr_enb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_dsb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_commit_write(struct hw *hw, void *blk) +{ + struct src_mgr_ctrl_blk *ctl = blk; + int i = 0; + unsigned int ret = 0; + + if (ctl->dirty.bf.enbsa) { + do { + ret = hw_read_20kx(hw, SRCENBSTAT); + } while (ret & 0x1); + hw_write_20kx(hw, SRCENBS, ctl->enbsa); + ctl->dirty.bf.enbsa = 0; + } + for (i = 0; i < 8; i++) { + if ((ctl->dirty.data & (0x1 << i))) { + hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]); + ctl->dirty.data &= ~(0x1 << i); + } + } + + return 0; +} + +static int src_mgr_get_ctrl_blk(void **rblk) +{ + struct src_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct src_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_get_ctrl_blk(void **rblk) +{ + struct srcimp_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int srcimp_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct srcimp_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + ctl->srcimap.idx = addr; + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_commit_write(struct hw *hw, void *blk) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srcimap) { + hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100, + ctl->srcimap.srcaim); + ctl->dirty.bf.srcimap = 0; + } + + return 0; +} + +/* + * AMIXER control block definitions. + */ + +#define AMOPLO_M 0x00000003 +#define AMOPLO_X 0x0003FFF0 +#define AMOPLO_Y 0xFFFC0000 + +#define AMOPHI_SADR 0x000000FF +#define AMOPHI_SE 0x80000000 + +/* AMIXER resource register dirty flags */ +union amixer_dirty { + struct { + u16 amoplo:1; + u16 amophi:1; + u16 rsv:14; + } bf; + u16 data; +}; + +/* AMIXER resource control block */ +struct amixer_rsc_ctrl_blk { + unsigned int amoplo; + unsigned int amophi; + union amixer_dirty dirty; +}; + +static int amixer_set_mode(void *blk, unsigned int mode) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_M, mode); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_iv(void *blk, unsigned int iv) +{ + /* 20k1 amixer does not have this field */ + return 0; +} + +static int amixer_set_x(void *blk, unsigned int x) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_X, x); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_y(void *blk, unsigned int y) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_Y, y); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_sadr(void *blk, unsigned int sadr) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SADR, sadr); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_se(void *blk, unsigned int se) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SE, se); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_dirty(void *blk, unsigned int flags) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int amixer_set_dirty_all(void *blk) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { + hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo); + ctl->dirty.bf.amoplo = 0; + hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi); + ctl->dirty.bf.amophi = 0; + } + + return 0; +} + +static int amixer_get_y(void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + return get_field(ctl->amoplo, AMOPLO_Y); +} + +static unsigned int amixer_get_dirty(void *blk) +{ + return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; +} + +static int amixer_rsc_get_ctrl_blk(void **rblk) +{ + struct amixer_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int amixer_rsc_put_ctrl_blk(void *blk) +{ + kfree((struct amixer_rsc_ctrl_blk *)blk); + + return 0; +} + +static int amixer_mgr_get_ctrl_blk(void **rblk) +{ + /*amixer_mgr_ctrl_blk_t *blk;*/ + + *rblk = NULL; + /*blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk;*/ + + return 0; +} + +static int amixer_mgr_put_ctrl_blk(void *blk) +{ + /*kfree((amixer_mgr_ctrl_blk_t *)blk);*/ + + return 0; +} + +/* + * DAIO control block definitions. + */ + +/* Receiver Sample Rate Tracker Control register */ +#define SRTCTL_SRCR 0x000000FF +#define SRTCTL_SRCL 0x0000FF00 +#define SRTCTL_RSR 0x00030000 +#define SRTCTL_DRAT 0x000C0000 +#define SRTCTL_RLE 0x10000000 +#define SRTCTL_RLP 0x20000000 +#define SRTCTL_EC 0x40000000 +#define SRTCTL_ET 0x80000000 + +/* DAIO Receiver register dirty flags */ +union dai_dirty { + struct { + u16 srtctl:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* DAIO Receiver control block */ +struct dai_ctrl_blk { + unsigned int srtctl; + union dai_dirty dirty; +}; + +/* S/PDIF Transmitter register dirty flags */ +union dao_dirty { + struct { + u16 spos:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* S/PDIF Transmitter control block */ +struct dao_ctrl_blk { + unsigned int spos; /* S/PDIF Output Channel Status Register */ + union dao_dirty dirty; +}; + +/* Audio Input Mapper RAM */ +#define AIM_ARC 0x00000FFF +#define AIM_NXT 0x007F0000 + +struct daoimap { + unsigned int aim; + unsigned int idx; +}; + +/* I2S Transmitter/Receiver Control register */ +#define I2SCTL_EA 0x00000004 +#define I2SCTL_EI 0x00000010 + +/* S/PDIF Transmitter Control register */ +#define SPOCTL_OE 0x00000001 +#define SPOCTL_OS 0x0000000E +#define SPOCTL_RIV 0x00000010 +#define SPOCTL_LIV 0x00000020 +#define SPOCTL_SR 0x000000C0 + +/* S/PDIF Receiver Control register */ +#define SPICTL_EN 0x00000001 +#define SPICTL_I24 0x00000002 +#define SPICTL_IB 0x00000004 +#define SPICTL_SM 0x00000008 +#define SPICTL_VM 0x00000010 + +/* DAIO manager register dirty flags */ +union daio_mgr_dirty { + struct { + u32 i2soctl:4; + u32 i2sictl:4; + u32 spoctl:4; + u32 spictl:4; + u32 daoimap:1; + u32 rsv:15; + } bf; + u32 data; +}; + +/* DAIO manager control block */ +struct daio_mgr_ctrl_blk { + unsigned int i2sctl; + unsigned int spoctl; + unsigned int spictl; + struct daoimap daoimap; + union daio_mgr_dirty dirty; +}; + +static int dai_srt_set_srcr(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_SRCR, src); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_srcl(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_SRCL, src); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_rsr(void *blk, unsigned int rsr) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_RSR, rsr); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_drat(void *blk, unsigned int drat) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_DRAT, drat); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_ec(void *blk, unsigned int ec) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_srt_set_et(void *blk, unsigned int et) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0); + ctl->dirty.bf.srtctl = 1; + return 0; +} + +static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dai_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srtctl) { + if (idx < 4) { + /* S/PDIF SRTs */ + hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl); + } else { + /* I2S SRT */ + hw_write_20kx(hw, SRTICTL, ctl->srtctl); + } + ctl->dirty.bf.srtctl = 0; + } + + return 0; +} + +static int dai_get_ctrl_blk(void **rblk) +{ + struct dai_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dai_put_ctrl_blk(void *blk) +{ + kfree((struct dai_ctrl_blk *)blk); + + return 0; +} + +static int dao_set_spos(void *blk, unsigned int spos) +{ + ((struct dao_ctrl_blk *)blk)->spos = spos; + ((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1; + return 0; +} + +static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dao_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.spos) { + if (idx < 4) { + /* S/PDIF SPOSx */ + hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos); + } + ctl->dirty.bf.spos = 0; + } + + return 0; +} + +static int dao_get_spos(void *blk, unsigned int *spos) +{ + *spos = ((struct dao_ctrl_blk *)blk)->spos; + return 0; +} + +static int dao_get_ctrl_blk(void **rblk) +{ + struct dao_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dao_put_ctrl_blk(void *blk) +{ + kfree((struct dao_ctrl_blk *)blk); + + return 0; +} + +static int daio_mgr_enb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF input */ + set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1); + ctl->dirty.bf.spictl |= (0x1 << idx); + } else { + /* I2S input */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1); + ctl->dirty.bf.i2sictl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_dsb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF input */ + set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0); + ctl->dirty.bf.spictl |= (0x1 << idx); + } else { + /* I2S input */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0); + ctl->dirty.bf.i2sictl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_enb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1); + ctl->dirty.bf.spoctl |= (0x1 << idx); + } else { + /* I2S output */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1); + ctl->dirty.bf.i2soctl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_dsb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0); + ctl->dirty.bf.spoctl |= (0x1 << idx); + } else { + /* I2S output */ + idx %= 4; + set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0); + ctl->dirty.bf.i2soctl |= (0x1 << idx); + } + return 0; +} + +static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + switch ((conf & 0x7)) { + case 0: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3); + break; /* CDIF */ + case 1: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0); + break; + case 2: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1); + break; + case 4: + set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2); + break; + default: + break; + } + set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8), + (conf >> 4) & 0x1); /* Non-audio */ + set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8), + (conf >> 4) & 0x1); /* Non-audio */ + set_field(&ctl->spoctl, SPOCTL_OS << (idx*8), + ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */ + + ctl->dirty.bf.spoctl |= (0x1 << idx); + } else { + /* I2S output */ + /*idx %= 4; */ + } + return 0; +} + +static int daio_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_ARC, slot); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_NXT, next); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + ctl->daoimap.idx = addr; + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_commit_write(struct hw *hw, void *blk) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + int i = 0; + + if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) { + for (i = 0; i < 4; i++) { + if ((ctl->dirty.bf.i2sictl & (0x1 << i))) + ctl->dirty.bf.i2sictl &= ~(0x1 << i); + + if ((ctl->dirty.bf.i2soctl & (0x1 << i))) + ctl->dirty.bf.i2soctl &= ~(0x1 << i); + } + hw_write_20kx(hw, I2SCTL, ctl->i2sctl); + mdelay(1); + } + if (ctl->dirty.bf.spoctl) { + for (i = 0; i < 4; i++) { + if ((ctl->dirty.bf.spoctl & (0x1 << i))) + ctl->dirty.bf.spoctl &= ~(0x1 << i); + } + hw_write_20kx(hw, SPOCTL, ctl->spoctl); + mdelay(1); + } + if (ctl->dirty.bf.spictl) { + for (i = 0; i < 4; i++) { + if ((ctl->dirty.bf.spictl & (0x1 << i))) + ctl->dirty.bf.spictl &= ~(0x1 << i); + } + hw_write_20kx(hw, SPICTL, ctl->spictl); + mdelay(1); + } + if (ctl->dirty.bf.daoimap) { + hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4, + ctl->daoimap.aim); + ctl->dirty.bf.daoimap = 0; + } + + return 0; +} + +static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) +{ + struct daio_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + blk->i2sctl = hw_read_20kx(hw, I2SCTL); + blk->spoctl = hw_read_20kx(hw, SPOCTL); + blk->spictl = hw_read_20kx(hw, SPICTL); + + *rblk = blk; + + return 0; +} + +static int daio_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct daio_mgr_ctrl_blk *)blk); + + return 0; +} + +/* Card hardware initialization block */ +struct dac_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct adc_conf { + unsigned int msr; /* master sample rate in rsrs */ + unsigned char input; /* the input source of ADC */ + unsigned char mic20db; /* boost mic by 20db if input is microphone */ +}; + +struct daio_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct trn_conf { + unsigned long vm_pgt_phys; +}; + +static int hw_daio_init(struct hw *hw, const struct daio_conf *info) +{ + u32 i2sorg = 0; + u32 spdorg = 0; + + /* Read I2S CTL. Keep original value. */ + /*i2sorg = hw_read_20kx(hw, I2SCTL);*/ + i2sorg = 0x94040404; /* enable all audio out and I2S-D input */ + /* Program I2S with proper master sample rate and enable + * the correct I2S channel. */ + i2sorg &= 0xfffffffc; + + /* Enable S/PDIF-out-A in fixed 24-bit data + * format and default to 48kHz. */ + /* Disable all before doing any changes. */ + hw_write_20kx(hw, SPOCTL, 0x0); + spdorg = 0x05; + + switch (info->msr) { + case 1: + i2sorg |= 1; + spdorg |= (0x0 << 6); + break; + case 2: + i2sorg |= 2; + spdorg |= (0x1 << 6); + break; + case 4: + i2sorg |= 3; + spdorg |= (0x2 << 6); + break; + default: + i2sorg |= 1; + break; + } + + hw_write_20kx(hw, I2SCTL, i2sorg); + hw_write_20kx(hw, SPOCTL, spdorg); + + /* Enable S/PDIF-in-A in fixed 24-bit data format. */ + /* Disable all before doing any changes. */ + hw_write_20kx(hw, SPICTL, 0x0); + mdelay(1); + spdorg = 0x0a0a0a0a; + hw_write_20kx(hw, SPICTL, spdorg); + mdelay(1); + + return 0; +} + +/* TRANSPORT operations */ +static int hw_trn_init(struct hw *hw, const struct trn_conf *info) +{ + u32 trnctl = 0; + unsigned long ptp_phys_low = 0, ptp_phys_high = 0; + + /* Set up device page table */ + if ((~0UL) == info->vm_pgt_phys) { + printk(KERN_ERR "Wrong device page table page address!\n"); + return -1; + } + + trnctl = 0x13; /* 32-bit, 4k-size page */ +#if BITS_PER_LONG == 64 + ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); + ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); + trnctl |= (1<<2); +#elif BITS_PER_LONG == 32 + ptp_phys_low = info->vm_pgt_phys & (~0UL); + ptp_phys_high = 0; +#else +# error "Unknown BITS_PER_LONG!" +#endif +#if PAGE_SIZE == 8192 + trnctl |= (1<<5); +#endif + hw_write_20kx(hw, PTPALX, ptp_phys_low); + hw_write_20kx(hw, PTPAHX, ptp_phys_high); + hw_write_20kx(hw, TRNCTL, trnctl); + hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */ + + return 0; +} + +/* Card initialization */ +#define GCTL_EAC 0x00000001 +#define GCTL_EAI 0x00000002 +#define GCTL_BEP 0x00000004 +#define GCTL_BES 0x00000008 +#define GCTL_DSP 0x00000010 +#define GCTL_DBP 0x00000020 +#define GCTL_ABP 0x00000040 +#define GCTL_TBP 0x00000080 +#define GCTL_SBP 0x00000100 +#define GCTL_FBP 0x00000200 +#define GCTL_XA 0x00000400 +#define GCTL_ET 0x00000800 +#define GCTL_PR 0x00001000 +#define GCTL_MRL 0x00002000 +#define GCTL_SDE 0x00004000 +#define GCTL_SDI 0x00008000 +#define GCTL_SM 0x00010000 +#define GCTL_SR 0x00020000 +#define GCTL_SD 0x00040000 +#define GCTL_SE 0x00080000 +#define GCTL_AID 0x00100000 + +static int hw_pll_init(struct hw *hw, unsigned int rsr) +{ + unsigned int pllctl; + int i = 0; + + pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731; + for (i = 0; i < 3; i++) { + if (hw_read_20kx(hw, PLLCTL) == pllctl) + break; + + hw_write_20kx(hw, PLLCTL, pllctl); + mdelay(40); + } + if (i >= 3) { + printk(KERN_ALERT "PLL initialization failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +static int hw_auto_init(struct hw *hw) +{ + unsigned int gctl; + int i; + + gctl = hw_read_20kx(hw, GCTL); + set_field(&gctl, GCTL_EAI, 0); + hw_write_20kx(hw, GCTL, gctl); + set_field(&gctl, GCTL_EAI, 1); + hw_write_20kx(hw, GCTL, gctl); + mdelay(10); + for (i = 0; i < 400000; i++) { + gctl = hw_read_20kx(hw, GCTL); + if (get_field(gctl, GCTL_AID)) + break; + } + if (!get_field(gctl, GCTL_AID)) { + printk(KERN_ALERT "Card Auto-init failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +static int i2c_unlock(struct hw *hw) +{ + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + return 0; + + hw_write_pci(hw, 0xcc, 0x8c); + hw_write_pci(hw, 0xcc, 0x0e); + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + return 0; + + hw_write_pci(hw, 0xcc, 0xee); + hw_write_pci(hw, 0xcc, 0xaa); + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + return 0; + + return -1; +} + +static void i2c_lock(struct hw *hw) +{ + if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa) + hw_write_pci(hw, 0xcc, 0x00); +} + +static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) +{ + unsigned int ret = 0; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); + hw_write_pci(hw, 0xE0, device); + hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff)); +} + +/* DAC operations */ + +static int hw_reset_dac(struct hw *hw) +{ + u32 i = 0; + u16 gpioorg = 0; + unsigned int ret = 0; + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); + hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ + + /* To be effective, need to reset the DAC twice. */ + for (i = 0; i < 2; i++) { + /* set gpio */ + mdelay(100); + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xfffd; + hw_write_20kx(hw, GPIO, gpioorg); + mdelay(1); + hw_write_20kx(hw, GPIO, gpioorg | 0x2); + } + + i2c_write(hw, 0x00180080, 0x01, 0x80); + i2c_write(hw, 0x00180080, 0x02, 0x10); + + i2c_lock(hw); + + return 0; +} + +static int hw_dac_init(struct hw *hw, const struct dac_conf *info) +{ + u32 data = 0; + u16 gpioorg = 0; + u16 subsys_id = 0; + unsigned int ret = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x, unmute outputs */ + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xffbf; /* set GPIO6 to low */ + gpioorg |= 2; /* set GPIO1 to high */ + hw_write_20kx(hw, GPIO, gpioorg); + return 0; + } + + /* mute outputs */ + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xffbf; + hw_write_20kx(hw, GPIO, gpioorg); + + hw_reset_dac(hw); + + if (i2c_unlock(hw)) + return -1; + + hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); + + switch (info->msr) { + case 1: + data = 0x24; + break; + case 2: + data = 0x25; + break; + case 4: + data = 0x26; + break; + default: + data = 0x24; + break; + } + + i2c_write(hw, 0x00180080, 0x06, data); + i2c_write(hw, 0x00180080, 0x09, data); + i2c_write(hw, 0x00180080, 0x0c, data); + i2c_write(hw, 0x00180080, 0x0f, data); + + i2c_lock(hw); + + /* unmute outputs */ + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg = gpioorg | 0x40; + hw_write_20kx(hw, GPIO, gpioorg); + + return 0; +} + +/* ADC operations */ + +static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + return data; +} + +static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data = ((data & (0x1<<7)) && (data & (0x1<<8))); + break; + case ADC_LINEIN: + data = (!(data & (0x1<<7)) && (data & (0x1<<8))); + break; + case ADC_NONE: /* Digital I/O */ + data = (!(data & (0x1<<8))); + break; + default: + data = 0; + } + return data; +} + +static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data = (data & (0x1 << 7)) ? 1 : 0; + break; + case ADC_LINEIN: + data = (data & (0x1 << 7)) ? 0 : 1; + break; + default: + data = 0; + } + return data; +} + +static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) +{ + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x cards */ + return is_adc_input_selected_SB055x(hw, type); + } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + return is_adc_input_selected_hendrix(hw, type); + } else if ((subsys_id & 0xf000) == 0x6000) { + /* Vista compatible cards */ + return is_adc_input_selected_hendrix(hw, type); + } else { + return is_adc_input_selected_SBx(hw, type); + } +} + +static int +adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) +{ + u32 data = 0; + + /* + * check and set the following GPIO bits accordingly + * ADC_Gain = GPIO2 + * DRM_off = GPIO3 + * Mic_Pwr_on = GPIO7 + * Digital_IO_Sel = GPIO8 + * Mic_Sw = GPIO9 + * Aux/MicLine_Sw = GPIO12 + */ + data = hw_read_20kx(hw, GPIO); + data &= 0xec73; + switch (type) { + case ADC_MICIN: + data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ; + data |= boost ? (0x1<<2) : 0; + break; + case ADC_LINEIN: + data |= (0x1<<8); + break; + case ADC_AUX: + data |= (0x1<<8) | (0x1<<12); + break; + case ADC_NONE: + data |= (0x1<<12); /* set to digital */ + break; + default: + return -1; + } + + hw_write_20kx(hw, GPIO, data); + + return 0; +} + + +static int +adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) +{ + u32 data = 0; + u32 i2c_data = 0; + unsigned int ret = 0; + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); /* i2c ready poll */ + /* set i2c access mode as Direct Control */ + hw_write_pci(hw, 0xEC, 0x05); + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data |= ((0x1 << 7) | (0x1 << 8)); + i2c_data = 0x1; /* Mic-in */ + break; + case ADC_LINEIN: + data &= ~(0x1 << 7); + data |= (0x1 << 8); + i2c_data = 0x2; /* Line-in */ + break; + case ADC_NONE: + data &= ~(0x1 << 8); + i2c_data = 0x0; /* set to Digital */ + break; + default: + i2c_lock(hw); + return -1; + } + hw_write_20kx(hw, GPIO, data); + i2c_write(hw, 0x001a0080, 0x2a, i2c_data); + if (boost) { + i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ + } else { + i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ + } + + i2c_lock(hw); + + return 0; +} + +static int +adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) +{ + u32 data = 0; + u32 i2c_data = 0; + unsigned int ret = 0; + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); /* i2c ready poll */ + /* set i2c access mode as Direct Control */ + hw_write_pci(hw, 0xEC, 0x05); + + data = hw_read_20kx(hw, GPIO); + switch (type) { + case ADC_MICIN: + data |= (0x1 << 7); + i2c_data = 0x1; /* Mic-in */ + break; + case ADC_LINEIN: + data &= ~(0x1 << 7); + i2c_data = 0x2; /* Line-in */ + break; + default: + i2c_lock(hw); + return -1; + } + hw_write_20kx(hw, GPIO, data); + i2c_write(hw, 0x001a0080, 0x2a, i2c_data); + if (boost) { + i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */ + } else { + i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */ + i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */ + } + + i2c_lock(hw); + + return 0; +} + +static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) +{ + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x cards */ + return adc_input_select_SB055x(hw, type, (ADC_MICIN == type)); + } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); + } else if ((subsys_id & 0xf000) == 0x6000) { + /* Vista compatible cards */ + return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); + } else { + return adc_input_select_SBx(hw, type, (ADC_MICIN == type)); + } +} + +static int adc_init_SB055x(struct hw *hw, int input, int mic20db) +{ + return adc_input_select_SB055x(hw, input, mic20db); +} + +static int adc_init_SBx(struct hw *hw, int input, int mic20db) +{ + u16 gpioorg; + u16 input_source; + u32 adcdata = 0; + unsigned int ret = 0; + + input_source = 0x100; /* default to analog */ + switch (input) { + case ADC_MICIN: + adcdata = 0x1; + input_source = 0x180; /* set GPIO7 to select Mic */ + break; + case ADC_LINEIN: + adcdata = 0x2; + break; + case ADC_VIDEO: + adcdata = 0x4; + break; + case ADC_AUX: + adcdata = 0x8; + break; + case ADC_NONE: + adcdata = 0x0; + input_source = 0x0; /* set to Digital */ + break; + default: + break; + } + + if (i2c_unlock(hw)) + return -1; + + do { + ret = hw_read_pci(hw, 0xEC); + } while (!(ret & 0x800000)); /* i2c ready poll */ + hw_write_pci(hw, 0xEC, 0x05); /* write to i2c status control */ + + i2c_write(hw, 0x001a0080, 0x0e, 0x08); + i2c_write(hw, 0x001a0080, 0x18, 0x0a); + i2c_write(hw, 0x001a0080, 0x28, 0x86); + i2c_write(hw, 0x001a0080, 0x2a, adcdata); + + if (mic20db) { + i2c_write(hw, 0x001a0080, 0x1c, 0xf7); + i2c_write(hw, 0x001a0080, 0x1e, 0xf7); + } else { + i2c_write(hw, 0x001a0080, 0x1c, 0xcf); + i2c_write(hw, 0x001a0080, 0x1e, 0xcf); + } + + if (!(hw_read_20kx(hw, ID0) & 0x100)) + i2c_write(hw, 0x001a0080, 0x16, 0x26); + + i2c_lock(hw); + + gpioorg = (u16)hw_read_20kx(hw, GPIO); + gpioorg &= 0xfe7f; + gpioorg |= input_source; + hw_write_20kx(hw, GPIO, gpioorg); + + return 0; +} + +static int hw_adc_init(struct hw *hw, const struct adc_conf *info) +{ + int err = 0; + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* Sb055x card */ + err = adc_init_SB055x(hw, info->input, info->mic20db); + } else { + err = adc_init_SBx(hw, info->input, info->mic20db); + } + + return err; +} + +static int hw_have_digit_io_switch(struct hw *hw) +{ + u16 subsys_id = 0; + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + /* SB073x and Vista compatible cards have no digit IO switch */ + return !((subsys_id == 0x0029) || (subsys_id == 0x0031) + || ((subsys_id & 0xf000) == 0x6000)); +} + +#define UAA_CFG_PWRSTATUS 0x44 +#define UAA_CFG_SPACE_FLAG 0xA0 +#define UAA_CORE_CHANGE 0x3FFC +static int uaa_to_xfi(struct pci_dev *pci) +{ + unsigned int bar0, bar1, bar2, bar3, bar4, bar5; + unsigned int cmd, irq, cl_size, l_timer, pwr; + unsigned int CTLA, CTLZ, CTLL, CTLX, CTL_, CTLF, CTLi; + unsigned int is_uaa = 0; + unsigned int data[4] = {0}; + unsigned int io_base; + void *mem_base; + int i = 0; + + /* By default, Hendrix card UAA Bar0 should be using memory... */ + io_base = pci_resource_start(pci, 0); + mem_base = ioremap(io_base, pci_resource_len(pci, 0)); + if (NULL == mem_base) + return -ENOENT; + + CTLX = ___constant_swab32(*((unsigned int *)"CTLX")); + CTL_ = ___constant_swab32(*((unsigned int *)"CTL-")); + CTLF = ___constant_swab32(*((unsigned int *)"CTLF")); + CTLi = ___constant_swab32(*((unsigned int *)"CTLi")); + CTLA = ___constant_swab32(*((unsigned int *)"CTLA")); + CTLZ = ___constant_swab32(*((unsigned int *)"CTLZ")); + CTLL = ___constant_swab32(*((unsigned int *)"CTLL")); + + /* Read current mode from Mode Change Register */ + for (i = 0; i < 4; i++) + data[i] = readl(mem_base + UAA_CORE_CHANGE); + + /* Determine current mode... */ + if (data[0] == CTLA) { + is_uaa = ((data[1] == CTLZ && data[2] == CTLL + && data[3] == CTLA) || (data[1] == CTLA + && data[2] == CTLZ && data[3] == CTLL)); + } else if (data[0] == CTLZ) { + is_uaa = (data[1] == CTLL + && data[2] == CTLA && data[3] == CTLA); + } else if (data[0] == CTLL) { + is_uaa = (data[1] == CTLA + && data[2] == CTLA && data[3] == CTLZ); + } else { + is_uaa = 0; + } + + if (!is_uaa) { + /* Not in UAA mode currently. Return directly. */ + iounmap(mem_base); + return 0; + } + + pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4); + pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5); + pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq); + pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size); + pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer); + pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr); + pci_read_config_dword(pci, PCI_COMMAND, &cmd); + + /* Set up X-Fi core PCI configuration space. */ + /* Switch to X-Fi config space with BAR0 exposed. */ + pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321); + /* Copy UAA's BAR5 into X-Fi BAR0 */ + pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5); + /* Switch to X-Fi config space without BAR0 exposed. */ + pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4); + pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq); + pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size); + pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer); + pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr); + pci_write_config_dword(pci, PCI_COMMAND, cmd); + + /* Switch to X-Fi mode */ + writel(CTLX, (mem_base + UAA_CORE_CHANGE)); + writel(CTL_, (mem_base + UAA_CORE_CHANGE)); + writel(CTLF, (mem_base + UAA_CORE_CHANGE)); + writel(CTLi, (mem_base + UAA_CORE_CHANGE)); + + iounmap(mem_base); + + return 0; +} + +static int hw_card_start(struct hw *hw) +{ + int err = 0; + struct pci_dev *pci = hw->pci; + u16 subsys_id = 0; + unsigned int dma_mask = 0; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + /* Set DMA transfer mask */ + dma_mask = CT_XFI_DMA_MASK; + if (pci_set_dma_mask(pci, dma_mask) < 0 || + pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + printk(KERN_ERR "architecture does not support PCI " + "busmaster DMA with mask 0x%x\n", dma_mask); + err = -ENXIO; + goto error1; + } + + err = pci_request_regions(pci, "XFi"); + if (err < 0) + goto error1; + + /* Switch to X-Fi mode from UAA mode if neeeded */ + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id); + if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) { + err = uaa_to_xfi(pci); + if (err) + goto error2; + + hw->io_base = pci_resource_start(pci, 5); + } else { + hw->io_base = pci_resource_start(pci, 0); + } + + /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, + atc->chip_details->nm_card, hw))) { + goto error2; + } + hw->irq = pci->irq; + */ + + pci_set_master(pci); + + return 0; + +error2: + pci_release_regions(pci); + hw->io_base = 0; +error1: + pci_disable_device(pci); + return err; +} + +static int hw_card_stop(struct hw *hw) +{ + /* TODO: Disable interrupt and so on... */ + return 0; +} + +static int hw_card_shutdown(struct hw *hw) +{ + if (hw->irq >= 0) + free_irq(hw->irq, hw); + + hw->irq = -1; + + if (NULL != ((void *)hw->mem_base)) + iounmap((void *)hw->mem_base); + + hw->mem_base = (unsigned long)NULL; + + if (hw->io_base) + pci_release_regions(hw->pci); + + hw->io_base = 0; + + pci_disable_device(hw->pci); + + return 0; +} + +static int hw_card_init(struct hw *hw, struct card_conf *info) +{ + int err; + unsigned int gctl; + u16 subsys_id = 0; + u32 data = 0; + struct dac_conf dac_info = {0}; + struct adc_conf adc_info = {0}; + struct daio_conf daio_info = {0}; + struct trn_conf trn_info = {0}; + + /* Get PCI io port base address and do Hendrix switch if needed. */ + if (!hw->io_base) { + err = hw_card_start(hw); + if (err) + return err; + } + + /* PLL init */ + err = hw_pll_init(hw, info->rsr); + if (err < 0) + return err; + + /* kick off auto-init */ + err = hw_auto_init(hw); + if (err < 0) + return err; + + /* Enable audio ring */ + gctl = hw_read_20kx(hw, GCTL); + set_field(&gctl, GCTL_EAC, 1); + set_field(&gctl, GCTL_DBP, 1); + set_field(&gctl, GCTL_TBP, 1); + set_field(&gctl, GCTL_FBP, 1); + set_field(&gctl, GCTL_ET, 1); + hw_write_20kx(hw, GCTL, gctl); + mdelay(10); + + /* Reset all global pending interrupts */ + hw_write_20kx(hw, GIE, 0); + /* Reset all SRC pending interrupts */ + hw_write_20kx(hw, SRCIP, 0); + mdelay(30); + + pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); + /* Detect the card ID and configure GPIO accordingly. */ + if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + /* SB055x cards */ + hw_write_20kx(hw, GPIOCTL, 0x13fe); + } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { + /* SB073x cards */ + hw_write_20kx(hw, GPIOCTL, 0x00e6); + } else if ((subsys_id & 0xf000) == 0x6000) { + /* Vista compatible cards */ + hw_write_20kx(hw, GPIOCTL, 0x00c2); + } else { + hw_write_20kx(hw, GPIOCTL, 0x01e6); + } + + trn_info.vm_pgt_phys = info->vm_pgt_phys; + err = hw_trn_init(hw, &trn_info); + if (err < 0) + return err; + + daio_info.msr = info->msr; + err = hw_daio_init(hw, &daio_info); + if (err < 0) + return err; + + dac_info.msr = info->msr; + err = hw_dac_init(hw, &dac_info); + if (err < 0) + return err; + + adc_info.msr = info->msr; + adc_info.input = ADC_LINEIN; + adc_info.mic20db = 0; + err = hw_adc_init(hw, &adc_info); + if (err < 0) + return err; + + data = hw_read_20kx(hw, SRCMCTL); + data |= 0x1; /* Enables input from the audio ring */ + hw_write_20kx(hw, SRCMCTL, data); + + return 0; +} + +static u32 hw_read_20kx(struct hw *hw, u32 reg) +{ + u32 value; + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + outl(reg, hw->io_base + 0x0); + value = inl(hw->io_base + 0x4); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + + return value; +} + +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + outl(reg, hw->io_base + 0x0); + outl(data, hw->io_base + 0x4); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags); + +} + +static u32 hw_read_pci(struct hw *hw, u32 reg) +{ + u32 value; + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); + outl(reg, hw->io_base + 0x10); + value = inl(hw->io_base + 0x14); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); + + return value; +} + +static void hw_write_pci(struct hw *hw, u32 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); + outl(reg, hw->io_base + 0x10); + outl(data, hw->io_base + 0x14); + spin_unlock_irqrestore( + &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); +} + +int create_20k1_hw_obj(struct hw **rhw) +{ + struct hw *hw; + struct hw20k1 *hw20k1; + + *rhw = NULL; + hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL); + if (NULL == hw20k1) + return -ENOMEM; + + spin_lock_init(&hw20k1->reg_20k1_lock); + spin_lock_init(&hw20k1->reg_pci_lock); + + hw = &hw20k1->hw; + + hw->io_base = 0; + hw->mem_base = (unsigned long)NULL; + hw->irq = -1; + + hw->card_init = hw_card_init; + hw->card_stop = hw_card_stop; + hw->pll_init = hw_pll_init; + hw->is_adc_source_selected = hw_is_adc_input_selected; + hw->select_adc_source = hw_adc_input_select; + hw->have_digit_io_switch = hw_have_digit_io_switch; + + hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; + hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; + hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; + hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; + hw->src_set_state = src_set_state; + hw->src_set_bm = src_set_bm; + hw->src_set_rsr = src_set_rsr; + hw->src_set_sf = src_set_sf; + hw->src_set_wr = src_set_wr; + hw->src_set_pm = src_set_pm; + hw->src_set_rom = src_set_rom; + hw->src_set_vo = src_set_vo; + hw->src_set_st = src_set_st; + hw->src_set_ie = src_set_ie; + hw->src_set_ilsz = src_set_ilsz; + hw->src_set_bp = src_set_bp; + hw->src_set_cisz = src_set_cisz; + hw->src_set_ca = src_set_ca; + hw->src_set_sa = src_set_sa; + hw->src_set_la = src_set_la; + hw->src_set_pitch = src_set_pitch; + hw->src_set_dirty = src_set_dirty; + hw->src_set_clear_zbufs = src_set_clear_zbufs; + hw->src_set_dirty_all = src_set_dirty_all; + hw->src_commit_write = src_commit_write; + hw->src_get_ca = src_get_ca; + hw->src_get_dirty = src_get_dirty; + hw->src_dirty_conj_mask = src_dirty_conj_mask; + hw->src_mgr_enbs_src = src_mgr_enbs_src; + hw->src_mgr_enb_src = src_mgr_enb_src; + hw->src_mgr_dsb_src = src_mgr_dsb_src; + hw->src_mgr_commit_write = src_mgr_commit_write; + + hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; + hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; + hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; + hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; + hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; + hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; + hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; + + hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; + hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; + hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; + hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; + hw->amixer_set_mode = amixer_set_mode; + hw->amixer_set_iv = amixer_set_iv; + hw->amixer_set_x = amixer_set_x; + hw->amixer_set_y = amixer_set_y; + hw->amixer_set_sadr = amixer_set_sadr; + hw->amixer_set_se = amixer_set_se; + hw->amixer_set_dirty = amixer_set_dirty; + hw->amixer_set_dirty_all = amixer_set_dirty_all; + hw->amixer_commit_write = amixer_commit_write; + hw->amixer_get_y = amixer_get_y; + hw->amixer_get_dirty = amixer_get_dirty; + + hw->dai_get_ctrl_blk = dai_get_ctrl_blk; + hw->dai_put_ctrl_blk = dai_put_ctrl_blk; + hw->dai_srt_set_srco = dai_srt_set_srcr; + hw->dai_srt_set_srcm = dai_srt_set_srcl; + hw->dai_srt_set_rsr = dai_srt_set_rsr; + hw->dai_srt_set_drat = dai_srt_set_drat; + hw->dai_srt_set_ec = dai_srt_set_ec; + hw->dai_srt_set_et = dai_srt_set_et; + hw->dai_commit_write = dai_commit_write; + + hw->dao_get_ctrl_blk = dao_get_ctrl_blk; + hw->dao_put_ctrl_blk = dao_put_ctrl_blk; + hw->dao_set_spos = dao_set_spos; + hw->dao_commit_write = dao_commit_write; + hw->dao_get_spos = dao_get_spos; + + hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; + hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; + hw->daio_mgr_enb_dai = daio_mgr_enb_dai; + hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; + hw->daio_mgr_enb_dao = daio_mgr_enb_dao; + hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; + hw->daio_mgr_dao_init = daio_mgr_dao_init; + hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; + hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; + hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; + hw->daio_mgr_commit_write = daio_mgr_commit_write; + + *rhw = hw; + + return 0; +} + +int destroy_20k1_hw_obj(struct hw *hw) +{ + if (hw->io_base) + hw_card_shutdown(hw); + + kfree(container_of(hw, struct hw20k1, hw)); + return 0; +} diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h new file mode 100644 index 000000000000..02f72fb448a6 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k1.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File cthw20k1.h + * + * @Brief + * This file contains the definition of hardware access methord. + * + * @Author Liu Chun + * @Date May 13 2008 + * + */ + +#ifndef CTHW20K1_H +#define CTHW20K1_H + +#include "cthardware.h" + +int create_20k1_hw_obj(struct hw **rhw); +int destroy_20k1_hw_obj(struct hw *hw); + +#endif /* CTHW20K1_H */ diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c new file mode 100644 index 000000000000..cdcb75cc33b6 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.c @@ -0,0 +1,2133 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File cthw20k2.c + * + * @Brief + * This file contains the implementation of hardware access methord for 20k2. + * + * @Author Liu Chun + * @Date May 14 2008 + * + */ + +#include "cthw20k2.h" +#include "ct20k2reg.h" +#include +#include +#include +#include +#include +#include +#include + +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ + +static u32 hw_read_20kx(struct hw *hw, u32 reg); +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); + +/* + * Type definition block. + * The layout of control structures can be directly applied on 20k2 chip. + */ + +/* + * SRC control block definitions. + */ + +/* SRC resource control block */ +#define SRCCTL_STATE 0x00000007 +#define SRCCTL_BM 0x00000008 +#define SRCCTL_RSR 0x00000030 +#define SRCCTL_SF 0x000001C0 +#define SRCCTL_WR 0x00000200 +#define SRCCTL_PM 0x00000400 +#define SRCCTL_ROM 0x00001800 +#define SRCCTL_VO 0x00002000 +#define SRCCTL_ST 0x00004000 +#define SRCCTL_IE 0x00008000 +#define SRCCTL_ILSZ 0x000F0000 +#define SRCCTL_BP 0x00100000 + +#define SRCCCR_CISZ 0x000007FF +#define SRCCCR_CWA 0x001FF800 +#define SRCCCR_D 0x00200000 +#define SRCCCR_RS 0x01C00000 +#define SRCCCR_NAL 0x3E000000 +#define SRCCCR_RA 0xC0000000 + +#define SRCCA_CA 0x0FFFFFFF +#define SRCCA_RS 0xE0000000 + +#define SRCSA_SA 0x0FFFFFFF + +#define SRCLA_LA 0x0FFFFFFF + +/* Mixer Parameter Ring ram Low and Hight register. + * Fixed-point value in 8.24 format for parameter channel */ +#define MPRLH_PITCH 0xFFFFFFFF + +/* SRC resource register dirty flags */ +union src_dirty { + struct { + u16 ctl:1; + u16 ccr:1; + u16 sa:1; + u16 la:1; + u16 ca:1; + u16 mpr:1; + u16 czbfs:1; /* Clear Z-Buffers */ + u16 rsv:9; + } bf; + u16 data; +}; + +struct src_rsc_ctrl_blk { + unsigned int ctl; + unsigned int ccr; + unsigned int ca; + unsigned int sa; + unsigned int la; + unsigned int mpr; + union src_dirty dirty; +}; + +/* SRC manager control block */ +union src_mgr_dirty { + struct { + u16 enb0:1; + u16 enb1:1; + u16 enb2:1; + u16 enb3:1; + u16 enb4:1; + u16 enb5:1; + u16 enb6:1; + u16 enb7:1; + u16 enbsa:1; + u16 rsv:7; + } bf; + u16 data; +}; + +struct src_mgr_ctrl_blk { + unsigned int enbsa; + unsigned int enb[8]; + union src_mgr_dirty dirty; +}; + +/* SRCIMP manager control block */ +#define SRCAIM_ARC 0x00000FFF +#define SRCAIM_NXT 0x00FF0000 +#define SRCAIM_SRC 0xFF000000 + +struct srcimap { + unsigned int srcaim; + unsigned int idx; +}; + +/* SRCIMP manager register dirty flags */ +union srcimp_mgr_dirty { + struct { + u16 srcimap:1; + u16 rsv:15; + } bf; + u16 data; +}; + +struct srcimp_mgr_ctrl_blk { + struct srcimap srcimap; + union srcimp_mgr_dirty dirty; +}; + +/* + * Function implementation block. + */ + +static int src_get_rsc_ctrl_blk(void **rblk) +{ + struct src_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_put_rsc_ctrl_blk(void *blk) +{ + kfree((struct src_rsc_ctrl_blk *)blk); + + return 0; +} + +static int src_set_state(void *blk, unsigned int state) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_STATE, state); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bm(void *blk, unsigned int bm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BM, bm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rsr(void *blk, unsigned int rsr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_RSR, rsr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_sf(void *blk, unsigned int sf) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_SF, sf); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_wr(void *blk, unsigned int wr) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_WR, wr); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_pm(void *blk, unsigned int pm) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_PM, pm); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_rom(void *blk, unsigned int rom) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ROM, rom); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_vo(void *blk, unsigned int vo) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_VO, vo); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_st(void *blk, unsigned int st) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ST, st); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ie(void *blk, unsigned int ie) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_IE, ie); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_ilsz(void *blk, unsigned int ilsz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_bp(void *blk, unsigned int bp) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ctl, SRCCTL_BP, bp); + ctl->dirty.bf.ctl = 1; + return 0; +} + +static int src_set_cisz(void *blk, unsigned int cisz) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ccr, SRCCCR_CISZ, cisz); + ctl->dirty.bf.ccr = 1; + return 0; +} + +static int src_set_ca(void *blk, unsigned int ca) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->ca, SRCCA_CA, ca); + ctl->dirty.bf.ca = 1; + return 0; +} + +static int src_set_sa(void *blk, unsigned int sa) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->sa, SRCSA_SA, sa); + ctl->dirty.bf.sa = 1; + return 0; +} + +static int src_set_la(void *blk, unsigned int la) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->la, SRCLA_LA, la); + ctl->dirty.bf.la = 1; + return 0; +} + +static int src_set_pitch(void *blk, unsigned int pitch) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->mpr, MPRLH_PITCH, pitch); + ctl->dirty.bf.mpr = 1; + return 0; +} + +static int src_set_clear_zbufs(void *blk, unsigned int clear) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0); + return 0; +} + +static int src_set_dirty(void *blk, unsigned int flags) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int src_set_dirty_all(void *blk) +{ + ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +#define AR_SLOT_SIZE 4096 +#define AR_SLOT_BLOCK_SIZE 16 +#define AR_PTS_PITCH 6 +#define AR_PARAM_SRC_OFFSET 0x60 + +static unsigned int src_param_pitch_mixer(unsigned int src_idx) +{ + return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE + - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE; + +} + +static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + int i = 0; + + if (ctl->dirty.bf.czbfs) { + /* Clear Z-Buffer registers */ + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0); + + for (i = 0; i < 4; i++) + hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0); + + for (i = 0; i < 8; i++) + hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0); + + ctl->dirty.bf.czbfs = 0; + } + if (ctl->dirty.bf.mpr) { + /* Take the parameter mixer resource in the same group as that + * the idx src is in for simplicity. Unlike src, all conjugate + * parameter mixer resources must be programmed for + * corresponding conjugate src resources. */ + unsigned int pm_idx = src_param_pitch_mixer(idx); + hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr); + hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3); + hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0); + ctl->dirty.bf.mpr = 0; + } + if (ctl->dirty.bf.sa) { + hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa); + ctl->dirty.bf.sa = 0; + } + if (ctl->dirty.bf.la) { + hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la); + ctl->dirty.bf.la = 0; + } + if (ctl->dirty.bf.ca) { + hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca); + ctl->dirty.bf.ca = 0; + } + + /* Write srccf register */ + hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0); + + if (ctl->dirty.bf.ccr) { + hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr); + ctl->dirty.bf.ccr = 0; + } + if (ctl->dirty.bf.ctl) { + hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl); + ctl->dirty.bf.ctl = 0; + } + + return 0; +} + +static int src_get_ca(struct hw *hw, unsigned int idx, void *blk) +{ + struct src_rsc_ctrl_blk *ctl = blk; + + ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100); + ctl->dirty.bf.ca = 0; + + return get_field(ctl->ca, SRCCA_CA); +} + +static unsigned int src_get_dirty(void *blk) +{ + return ((struct src_rsc_ctrl_blk *)blk)->dirty.data; +} + +static unsigned int src_dirty_conj_mask(void) +{ + return 0x20; +} + +static int src_mgr_enbs_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1; + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + return 0; +} + +static int src_mgr_enb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_dsb_src(void *blk, unsigned int idx) +{ + ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32)); + ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32)); + return 0; +} + +static int src_mgr_commit_write(struct hw *hw, void *blk) +{ + struct src_mgr_ctrl_blk *ctl = blk; + int i = 0; + unsigned int ret = 0; + + if (ctl->dirty.bf.enbsa) { + do { + ret = hw_read_20kx(hw, SRC_ENBSTAT); + } while (ret & 0x1); + hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa); + ctl->dirty.bf.enbsa = 0; + } + for (i = 0; i < 8; i++) { + if ((ctl->dirty.data & (0x1 << i))) { + hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]); + ctl->dirty.data &= ~(0x1 << i); + } + } + + return 0; +} + +static int src_mgr_get_ctrl_blk(void **rblk) +{ + struct src_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int src_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct src_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_get_ctrl_blk(void **rblk) +{ + struct srcimp_mgr_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int srcimp_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct srcimp_mgr_ctrl_blk *)blk); + + return 0; +} + +static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapuser(void *blk, unsigned int user) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next); + ctl->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + ((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr; + ((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1; + return 0; +} + +static int srcimp_mgr_commit_write(struct hw *hw, void *blk) +{ + struct srcimp_mgr_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srcimap) { + hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100, + ctl->srcimap.srcaim); + ctl->dirty.bf.srcimap = 0; + } + + return 0; +} + +/* + * AMIXER control block definitions. + */ + +#define AMOPLO_M 0x00000003 +#define AMOPLO_IV 0x00000004 +#define AMOPLO_X 0x0003FFF0 +#define AMOPLO_Y 0xFFFC0000 + +#define AMOPHI_SADR 0x000000FF +#define AMOPHI_SE 0x80000000 + +/* AMIXER resource register dirty flags */ +union amixer_dirty { + struct { + u16 amoplo:1; + u16 amophi:1; + u16 rsv:14; + } bf; + u16 data; +}; + +/* AMIXER resource control block */ +struct amixer_rsc_ctrl_blk { + unsigned int amoplo; + unsigned int amophi; + union amixer_dirty dirty; +}; + +static int amixer_set_mode(void *blk, unsigned int mode) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_M, mode); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_iv(void *blk, unsigned int iv) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_IV, iv); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_x(void *blk, unsigned int x) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_X, x); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_y(void *blk, unsigned int y) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amoplo, AMOPLO_Y, y); + ctl->dirty.bf.amoplo = 1; + return 0; +} + +static int amixer_set_sadr(void *blk, unsigned int sadr) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SADR, sadr); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_se(void *blk, unsigned int se) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + set_field(&ctl->amophi, AMOPHI_SE, se); + ctl->dirty.bf.amophi = 1; + return 0; +} + +static int amixer_set_dirty(void *blk, unsigned int flags) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff); + return 0; +} + +static int amixer_set_dirty_all(void *blk) +{ + ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0); + return 0; +} + +static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) { + hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo); + ctl->dirty.bf.amoplo = 0; + hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi); + ctl->dirty.bf.amophi = 0; + } + + return 0; +} + +static int amixer_get_y(void *blk) +{ + struct amixer_rsc_ctrl_blk *ctl = blk; + + return get_field(ctl->amoplo, AMOPLO_Y); +} + +static unsigned int amixer_get_dirty(void *blk) +{ + return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data; +} + +static int amixer_rsc_get_ctrl_blk(void **rblk) +{ + struct amixer_rsc_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int amixer_rsc_put_ctrl_blk(void *blk) +{ + kfree((struct amixer_rsc_ctrl_blk *)blk); + + return 0; +} + +static int amixer_mgr_get_ctrl_blk(void **rblk) +{ + *rblk = NULL; + + return 0; +} + +static int amixer_mgr_put_ctrl_blk(void *blk) +{ + return 0; +} + +/* + * DAIO control block definitions. + */ + +/* Receiver Sample Rate Tracker Control register */ +#define SRTCTL_SRCO 0x000000FF +#define SRTCTL_SRCM 0x0000FF00 +#define SRTCTL_RSR 0x00030000 +#define SRTCTL_DRAT 0x00300000 +#define SRTCTL_EC 0x01000000 +#define SRTCTL_ET 0x10000000 + +/* DAIO Receiver register dirty flags */ +union dai_dirty { + struct { + u16 srt:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* DAIO Receiver control block */ +struct dai_ctrl_blk { + unsigned int srt; + union dai_dirty dirty; +}; + +/* Audio Input Mapper RAM */ +#define AIM_ARC 0x00000FFF +#define AIM_NXT 0x007F0000 + +struct daoimap { + unsigned int aim; + unsigned int idx; +}; + +/* Audio Transmitter Control and Status register */ +#define ATXCTL_EN 0x00000001 +#define ATXCTL_MODE 0x00000010 +#define ATXCTL_CD 0x00000020 +#define ATXCTL_RAW 0x00000100 +#define ATXCTL_MT 0x00000200 +#define ATXCTL_NUC 0x00003000 +#define ATXCTL_BEN 0x00010000 +#define ATXCTL_BMUX 0x00700000 +#define ATXCTL_B24 0x01000000 +#define ATXCTL_CPF 0x02000000 +#define ATXCTL_RIV 0x10000000 +#define ATXCTL_LIV 0x20000000 +#define ATXCTL_RSAT 0x40000000 +#define ATXCTL_LSAT 0x80000000 + +/* XDIF Transmitter register dirty flags */ +union dao_dirty { + struct { + u16 atxcsl:1; + u16 rsv:15; + } bf; + u16 data; +}; + +/* XDIF Transmitter control block */ +struct dao_ctrl_blk { + /* XDIF Transmitter Channel Status Low Register */ + unsigned int atxcsl; + union dao_dirty dirty; +}; + +/* Audio Receiver Control register */ +#define ARXCTL_EN 0x00000001 + +/* DAIO manager register dirty flags */ +union daio_mgr_dirty { + struct { + u32 atxctl:8; + u32 arxctl:8; + u32 daoimap:1; + u32 rsv:15; + } bf; + u32 data; +}; + +/* DAIO manager control block */ +struct daio_mgr_ctrl_blk { + struct daoimap daoimap; + unsigned int txctl[8]; + unsigned int rxctl[8]; + union daio_mgr_dirty dirty; +}; + +static int dai_srt_set_srco(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_SRCO, src); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_srcm(void *blk, unsigned int src) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_SRCM, src); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_rsr(void *blk, unsigned int rsr) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_RSR, rsr); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_drat(void *blk, unsigned int drat) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_DRAT, drat); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_ec(void *blk, unsigned int ec) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_srt_set_et(void *blk, unsigned int et) +{ + struct dai_ctrl_blk *ctl = blk; + + set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0); + ctl->dirty.bf.srt = 1; + return 0; +} + +static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dai_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.srt) { + hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt); + ctl->dirty.bf.srt = 0; + } + + return 0; +} + +static int dai_get_ctrl_blk(void **rblk) +{ + struct dai_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dai_put_ctrl_blk(void *blk) +{ + kfree((struct dai_ctrl_blk *)blk); + + return 0; +} + +static int dao_set_spos(void *blk, unsigned int spos) +{ + ((struct dao_ctrl_blk *)blk)->atxcsl = spos; + ((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1; + return 0; +} + +static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk) +{ + struct dao_ctrl_blk *ctl = blk; + + if (ctl->dirty.bf.atxcsl) { + if (idx < 4) { + /* S/PDIF SPOSx */ + hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx, + ctl->atxcsl); + } + ctl->dirty.bf.atxcsl = 0; + } + + return 0; +} + +static int dao_get_spos(void *blk, unsigned int *spos) +{ + *spos = ((struct dao_ctrl_blk *)blk)->atxcsl; + return 0; +} + +static int dao_get_ctrl_blk(void **rblk) +{ + struct dao_ctrl_blk *blk; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + *rblk = blk; + + return 0; +} + +static int dao_put_ctrl_blk(void *blk) +{ + kfree((struct dao_ctrl_blk *)blk); + + return 0; +} + +static int daio_mgr_enb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->rxctl[idx], ARXCTL_EN, 1); + ctl->dirty.bf.arxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_dsb_dai(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->rxctl[idx], ARXCTL_EN, 0); + + ctl->dirty.bf.arxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_enb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->txctl[idx], ATXCTL_EN, 1); + ctl->dirty.bf.atxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_dsb_dao(void *blk, unsigned int idx) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->txctl[idx], ATXCTL_EN, 0); + ctl->dirty.bf.atxctl |= (0x1 << idx); + return 0; +} + +static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + if (idx < 4) { + /* S/PDIF output */ + switch ((conf & 0x7)) { + case 1: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 0); + break; + case 2: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 1); + break; + case 4: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 2); + break; + case 8: + set_field(&ctl->txctl[idx], ATXCTL_NUC, 3); + break; + default: + break; + } + /* CDIF */ + set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7))); + /* Non-audio */ + set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1); + /* Non-audio */ + set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1); + set_field(&ctl->txctl[idx], ATXCTL_RAW, + ((conf >> 3) & 0x1) ? 0 : 0); + ctl->dirty.bf.atxctl |= (0x1 << idx); + } else { + /* I2S output */ + /*idx %= 4; */ + } + return 0; +} + +static int daio_mgr_set_imaparc(void *blk, unsigned int slot) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_ARC, slot); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapnxt(void *blk, unsigned int next) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + + set_field(&ctl->daoimap.aim, AIM_NXT, next); + ctl->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) +{ + ((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr; + ((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1; + return 0; +} + +static int daio_mgr_commit_write(struct hw *hw, void *blk) +{ + struct daio_mgr_ctrl_blk *ctl = blk; + unsigned int data = 0; + int i = 0; + + for (i = 0; i < 8; i++) { + if ((ctl->dirty.bf.atxctl & (0x1 << i))) { + data = ctl->txctl[i]; + hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); + ctl->dirty.bf.atxctl &= ~(0x1 << i); + mdelay(1); + } + if ((ctl->dirty.bf.arxctl & (0x1 << i))) { + data = ctl->rxctl[i]; + hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); + ctl->dirty.bf.arxctl &= ~(0x1 << i); + mdelay(1); + } + } + if (ctl->dirty.bf.daoimap) { + hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4, + ctl->daoimap.aim); + ctl->dirty.bf.daoimap = 0; + } + + return 0; +} + +static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) +{ + struct daio_mgr_ctrl_blk *blk; + int i = 0; + + *rblk = NULL; + blk = kzalloc(sizeof(*blk), GFP_KERNEL); + if (NULL == blk) + return -ENOMEM; + + for (i = 0; i < 8; i++) { + blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i)); + blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i)); + } + + *rblk = blk; + + return 0; +} + +static int daio_mgr_put_ctrl_blk(void *blk) +{ + kfree((struct daio_mgr_ctrl_blk *)blk); + + return 0; +} + +/* Card hardware initialization block */ +struct dac_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct adc_conf { + unsigned int msr; /* master sample rate in rsrs */ + unsigned char input; /* the input source of ADC */ + unsigned char mic20db; /* boost mic by 20db if input is microphone */ +}; + +struct daio_conf { + unsigned int msr; /* master sample rate in rsrs */ +}; + +struct trn_conf { + unsigned long vm_pgt_phys; +}; + +static int hw_daio_init(struct hw *hw, const struct daio_conf *info) +{ + u32 dwData = 0; + int i; + + /* Program I2S with proper sample rate and enable the correct I2S + * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */ + if (1 == info->msr) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101); + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else if (2 == info->msr) { + hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111); + /* Specify all playing 96khz + * EA [0] - Enabled + * RTA [4:5] - 96kHz + * EB [8] - Enabled + * RTB [12:13] - 96kHz + * EC [16] - Enabled + * RTC [20:21] - 96kHz + * ED [24] - Enabled + * RTD [28:29] - 96kHz */ + hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); + hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); + } else { + printk(KERN_ALERT "ERROR!!! Invalid sampling rate!!!\n"); + return -EINVAL; + } + + for (i = 0; i < 8; i++) { + if (i <= 3) { + /* 1st 3 channels are SPDIFs (SB0960) */ + if (i == 3) + dwData = 0x1001001; + else + dwData = 0x1000001; + + hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), dwData); + hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), dwData); + + /* Initialize the SPDIF Out Channel status registers. + * The value specified here is based on the typical + * values provided in the specification, namely: Clock + * Accuracy of 1000ppm, Sample Rate of 48KHz, + * unspecified source number, Generation status = 1, + * Category code = 0x12 (Digital Signal Mixer), + * Mode = 0, Emph = 0, Copy Permitted, AN = 0 + * (indicating that we're transmitting digital audio, + * and the Professional Use bit is 0. */ + + hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i), + 0x02109204); /* Default to 48kHz */ + + hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); + } else { + /* Next 5 channels are I2S (SB0960) */ + dwData = 0x11; + hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), dwData); + if (2 == info->msr) { + /* Four channels per sample period */ + dwData |= 0x1000; + } + hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), dwData); + } + } + + return 0; +} + +/* TRANSPORT operations */ +static int hw_trn_init(struct hw *hw, const struct trn_conf *info) +{ + u32 vmctl = 0, data = 0; + unsigned long ptp_phys_low = 0, ptp_phys_high = 0; + int i = 0; + + /* Set up device page table */ + if ((~0UL) == info->vm_pgt_phys) { + printk(KERN_ALERT "Wrong device page table page address!!!\n"); + return -1; + } + + vmctl = 0x80000C0F; /* 32-bit, 4k-size page */ +#if BITS_PER_LONG == 64 + ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); + ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); + vmctl |= (3<<8); +#elif BITS_PER_LONG == 32 + ptp_phys_low = info->vm_pgt_phys & (~0UL); + ptp_phys_high = 0; +#else +# error "Unknown BITS_PER_LONG!" +#endif +#if PAGE_SIZE == 8192 +# error "Don't support 8k-page!" +#endif + /* Write page table physical address to all PTPAL registers */ + for (i = 0; i < 64; i++) { + hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low); + hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high); + } + /* Enable virtual memory transfer */ + hw_write_20kx(hw, VMEM_CTL, vmctl); + /* Enable transport bus master and queueing of request */ + hw_write_20kx(hw, TRANSPORT_CTL, 0x03); + hw_write_20kx(hw, TRANSPORT_INT, 0x200c01); + /* Enable transport ring */ + data = hw_read_20kx(hw, TRANSPORT_ENB); + hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03)); + + return 0; +} + +/* Card initialization */ +#define GCTL_AIE 0x00000001 +#define GCTL_UAA 0x00000002 +#define GCTL_DPC 0x00000004 +#define GCTL_DBP 0x00000008 +#define GCTL_ABP 0x00000010 +#define GCTL_TBP 0x00000020 +#define GCTL_SBP 0x00000040 +#define GCTL_FBP 0x00000080 +#define GCTL_ME 0x00000100 +#define GCTL_AID 0x00001000 + +#define PLLCTL_SRC 0x00000007 +#define PLLCTL_SPE 0x00000008 +#define PLLCTL_RD 0x000000F0 +#define PLLCTL_FD 0x0001FF00 +#define PLLCTL_OD 0x00060000 +#define PLLCTL_B 0x00080000 +#define PLLCTL_AS 0x00100000 +#define PLLCTL_LF 0x03E00000 +#define PLLCTL_SPS 0x1C000000 +#define PLLCTL_AD 0x60000000 + +#define PLLSTAT_CCS 0x00000007 +#define PLLSTAT_SPL 0x00000008 +#define PLLSTAT_CRD 0x000000F0 +#define PLLSTAT_CFD 0x0001FF00 +#define PLLSTAT_SL 0x00020000 +#define PLLSTAT_FAS 0x00040000 +#define PLLSTAT_B 0x00080000 +#define PLLSTAT_PD 0x00100000 +#define PLLSTAT_OCA 0x00200000 +#define PLLSTAT_NCA 0x00400000 + +static int hw_pll_init(struct hw *hw, unsigned int rsr) +{ + unsigned int pllenb; + unsigned int pllctl; + unsigned int pllstat; + int i; + + pllenb = 0xB; + hw_write_20kx(hw, PLL_ENB, pllenb); + pllctl = 0x20D00000; + set_field(&pllctl, PLLCTL_FD, 16 - 4); + hw_write_20kx(hw, PLL_CTL, pllctl); + mdelay(40); + pllctl = hw_read_20kx(hw, PLL_CTL); + set_field(&pllctl, PLLCTL_B, 0); + if (48000 == rsr) { + set_field(&pllctl, PLLCTL_FD, 16 - 2); + set_field(&pllctl, PLLCTL_RD, 1 - 1); + } else { /* 44100 */ + set_field(&pllctl, PLLCTL_FD, 147 - 2); + set_field(&pllctl, PLLCTL_RD, 10 - 1); + } + hw_write_20kx(hw, PLL_CTL, pllctl); + mdelay(40); + for (i = 0; i < 1000; i++) { + pllstat = hw_read_20kx(hw, PLL_STAT); + if (get_field(pllstat, PLLSTAT_PD)) + continue; + + if (get_field(pllstat, PLLSTAT_B) != + get_field(pllctl, PLLCTL_B)) + continue; + + if (get_field(pllstat, PLLSTAT_CCS) != + get_field(pllctl, PLLCTL_SRC)) + continue; + + if (get_field(pllstat, PLLSTAT_CRD) != + get_field(pllctl, PLLCTL_RD)) + continue; + + if (get_field(pllstat, PLLSTAT_CFD) != + get_field(pllctl, PLLCTL_FD)) + continue; + + break; + } + if (i >= 1000) { + printk(KERN_ALERT "PLL initialization failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +static int hw_auto_init(struct hw *hw) +{ + unsigned int gctl; + int i; + + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + set_field(&gctl, GCTL_AIE, 0); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + set_field(&gctl, GCTL_AIE, 1); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + mdelay(10); + for (i = 0; i < 400000; i++) { + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + if (get_field(gctl, GCTL_AID)) + break; + } + if (!get_field(gctl, GCTL_AID)) { + printk(KERN_ALERT "Card Auto-init failed!!!\n"); + return -EBUSY; + } + + return 0; +} + +/* DAC operations */ + +#define CS4382_MC1 0x1 +#define CS4382_MC2 0x2 +#define CS4382_MC3 0x3 +#define CS4382_FC 0x4 +#define CS4382_IC 0x5 +#define CS4382_XC1 0x6 +#define CS4382_VCA1 0x7 +#define CS4382_VCB1 0x8 +#define CS4382_XC2 0x9 +#define CS4382_VCA2 0xA +#define CS4382_VCB2 0xB +#define CS4382_XC3 0xC +#define CS4382_VCA3 0xD +#define CS4382_VCB3 0xE +#define CS4382_XC4 0xF +#define CS4382_VCA4 0x10 +#define CS4382_VCB4 0x11 +#define CS4382_CREV 0x12 + +/* I2C status */ +#define STATE_LOCKED 0x00 +#define STATE_UNLOCKED 0xAA +#define DATA_READY 0x800000 /* Used with I2C_IF_STATUS */ +#define DATA_ABORT 0x10000 /* Used with I2C_IF_STATUS */ + +#define I2C_STATUS_DCM 0x00000001 +#define I2C_STATUS_BC 0x00000006 +#define I2C_STATUS_APD 0x00000008 +#define I2C_STATUS_AB 0x00010000 +#define I2C_STATUS_DR 0x00800000 + +#define I2C_ADDRESS_PTAD 0x0000FFFF +#define I2C_ADDRESS_SLAD 0x007F0000 + +struct REGS_CS4382 { + u32 dwModeControl_1; + u32 dwModeControl_2; + u32 dwModeControl_3; + + u32 dwFilterControl; + u32 dwInvertControl; + + u32 dwMixControl_P1; + u32 dwVolControl_A1; + u32 dwVolControl_B1; + + u32 dwMixControl_P2; + u32 dwVolControl_A2; + u32 dwVolControl_B2; + + u32 dwMixControl_P3; + u32 dwVolControl_A3; + u32 dwVolControl_B3; + + u32 dwMixControl_P4; + u32 dwVolControl_A4; + u32 dwVolControl_B4; +}; + +static u8 m_bAddressSize, m_bDataSize, m_bDeviceID; + +static int I2CUnlockFullAccess(struct hw *hw) +{ + u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] = {0xB3, 0xD4}; + + /* Send keys for forced BIOS mode */ + hw_write_20kx(hw, I2C_IF_WLOCK, + UnlockKeySequence_FLASH_FULLACCESS_MODE[0]); + hw_write_20kx(hw, I2C_IF_WLOCK, + UnlockKeySequence_FLASH_FULLACCESS_MODE[1]); + /* Check whether the chip is unlocked */ + if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED) + return 0; + + return -1; +} + +static int I2CLockChip(struct hw *hw) +{ + /* Write twice */ + hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); + hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); + if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED) + return 0; + + return -1; +} + +static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) +{ + int err = 0; + unsigned int RegI2CStatus; + unsigned int RegI2CAddress; + + err = I2CUnlockFullAccess(hw); + if (err < 0) + return err; + + m_bAddressSize = bAddressSize; + m_bDataSize = bDataSize; + m_bDeviceID = bDeviceID; + + RegI2CAddress = 0; + set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, bDeviceID); + + hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + + set_field(&RegI2CStatus, I2C_STATUS_DCM, 1); /* Direct control mode */ + + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + + return 0; +} + +static int I2CUninit(struct hw *hw) +{ + unsigned int RegI2CStatus; + unsigned int RegI2CAddress; + + RegI2CAddress = 0; + set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ + + hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + + set_field(&RegI2CStatus, I2C_STATUS_DCM, 0); /* I2C mode */ + + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + + return I2CLockChip(hw); +} + +static int I2CWaitDataReady(struct hw *hw) +{ + int i = 0x400000; + unsigned int ret = 0; + + do { + ret = hw_read_20kx(hw, I2C_IF_STATUS); + } while ((!(ret & DATA_READY)) && --i); + + return i; +} + +static int I2CRead(struct hw *hw, u16 wAddress, u32 *pdwData) +{ + unsigned int RegI2CStatus; + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + set_field(&RegI2CStatus, I2C_STATUS_BC, + (4 == m_bAddressSize) ? 0 : m_bAddressSize); + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + if (!I2CWaitDataReady(hw)) + return -1; + + hw_write_20kx(hw, I2C_IF_WDATA, (u32)wAddress); + if (!I2CWaitDataReady(hw)) + return -1; + + /* Force a read operation */ + hw_write_20kx(hw, I2C_IF_RDATA, 0); + if (!I2CWaitDataReady(hw)) + return -1; + + *pdwData = hw_read_20kx(hw, I2C_IF_RDATA); + + return 0; +} + +static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) +{ + unsigned int dwI2CData = (dwData << (m_bAddressSize * 8)) | wAddress; + unsigned int RegI2CStatus; + + RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + + set_field(&RegI2CStatus, I2C_STATUS_BC, + (4 == (m_bAddressSize + m_bDataSize)) ? + 0 : (m_bAddressSize + m_bDataSize)); + + hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + I2CWaitDataReady(hw); + /* Dummy write to trigger the write oprtation */ + hw_write_20kx(hw, I2C_IF_WDATA, 0); + I2CWaitDataReady(hw); + + /* This is the real data */ + hw_write_20kx(hw, I2C_IF_WDATA, dwI2CData); + I2CWaitDataReady(hw); + + return 0; +} + +static int hw_dac_init(struct hw *hw, const struct dac_conf *info) +{ + int err = 0; + u32 dwData = 0; + int i = 0; + struct REGS_CS4382 cs4382_Read = {0}; + struct REGS_CS4382 cs4382_Def = { + 0x00000001, /* Mode Control 1 */ + 0x00000000, /* Mode Control 2 */ + 0x00000084, /* Mode Control 3 */ + 0x00000000, /* Filter Control */ + 0x00000000, /* Invert Control */ + 0x00000024, /* Mixing Control Pair 1 */ + 0x00000000, /* Vol Control A1 */ + 0x00000000, /* Vol Control B1 */ + 0x00000024, /* Mixing Control Pair 2 */ + 0x00000000, /* Vol Control A2 */ + 0x00000000, /* Vol Control B2 */ + 0x00000024, /* Mixing Control Pair 3 */ + 0x00000000, /* Vol Control A3 */ + 0x00000000, /* Vol Control B3 */ + 0x00000024, /* Mixing Control Pair 4 */ + 0x00000000, /* Vol Control A4 */ + 0x00000000 /* Vol Control B4 */ + }; + + /* Set DAC reset bit as output */ + dwData = hw_read_20kx(hw, GPIO_CTRL); + dwData |= 0x02; + hw_write_20kx(hw, GPIO_CTRL, dwData); + + err = I2CInit(hw, 0x18, 1, 1); + if (err < 0) + goto End; + + for (i = 0; i < 2; i++) { + /* Reset DAC twice just in-case the chip + * didn't initialized properly */ + dwData = hw_read_20kx(hw, GPIO_DATA); + /* GPIO data bit 1 */ + dwData &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(10); + dwData |= 0x2; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(50); + + /* Reset the 2nd time */ + dwData &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(10); + dwData |= 0x2; + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(50); + + if (I2CRead(hw, CS4382_MC1, &cs4382_Read.dwModeControl_1)) + continue; + + if (I2CRead(hw, CS4382_MC2, &cs4382_Read.dwModeControl_2)) + continue; + + if (I2CRead(hw, CS4382_MC3, &cs4382_Read.dwModeControl_3)) + continue; + + if (I2CRead(hw, CS4382_FC, &cs4382_Read.dwFilterControl)) + continue; + + if (I2CRead(hw, CS4382_IC, &cs4382_Read.dwInvertControl)) + continue; + + if (I2CRead(hw, CS4382_XC1, &cs4382_Read.dwMixControl_P1)) + continue; + + if (I2CRead(hw, CS4382_VCA1, &cs4382_Read.dwVolControl_A1)) + continue; + + if (I2CRead(hw, CS4382_VCB1, &cs4382_Read.dwVolControl_B1)) + continue; + + if (I2CRead(hw, CS4382_XC2, &cs4382_Read.dwMixControl_P2)) + continue; + + if (I2CRead(hw, CS4382_VCA2, &cs4382_Read.dwVolControl_A2)) + continue; + + if (I2CRead(hw, CS4382_VCB2, &cs4382_Read.dwVolControl_B2)) + continue; + + if (I2CRead(hw, CS4382_XC3, &cs4382_Read.dwMixControl_P3)) + continue; + + if (I2CRead(hw, CS4382_VCA3, &cs4382_Read.dwVolControl_A3)) + continue; + + if (I2CRead(hw, CS4382_VCB3, &cs4382_Read.dwVolControl_B3)) + continue; + + if (I2CRead(hw, CS4382_XC4, &cs4382_Read.dwMixControl_P4)) + continue; + + if (I2CRead(hw, CS4382_VCA4, &cs4382_Read.dwVolControl_A4)) + continue; + + if (I2CRead(hw, CS4382_VCB4, &cs4382_Read.dwVolControl_B4)) + continue; + + if (memcmp(&cs4382_Read, &cs4382_Def, + sizeof(struct REGS_CS4382))) + continue; + else + break; + } + + if (i >= 2) + goto End; + + /* Note: Every I2C write must have some delay. + * This is not a requirement but the delay works here... */ + I2CWrite(hw, CS4382_MC1, 0x80); + I2CWrite(hw, CS4382_MC2, 0x10); + if (1 == info->msr) { + I2CWrite(hw, CS4382_XC1, 0x24); + I2CWrite(hw, CS4382_XC2, 0x24); + I2CWrite(hw, CS4382_XC3, 0x24); + I2CWrite(hw, CS4382_XC4, 0x24); + } else if (2 == info->msr) { + I2CWrite(hw, CS4382_XC1, 0x25); + I2CWrite(hw, CS4382_XC2, 0x25); + I2CWrite(hw, CS4382_XC3, 0x25); + I2CWrite(hw, CS4382_XC4, 0x25); + } else { + I2CWrite(hw, CS4382_XC1, 0x26); + I2CWrite(hw, CS4382_XC2, 0x26); + I2CWrite(hw, CS4382_XC3, 0x26); + I2CWrite(hw, CS4382_XC4, 0x26); + } + + return 0; +End: + + I2CUninit(hw); + return -1; +} + +/* ADC operations */ +#define MAKE_WM8775_ADDR(addr, data) (u32)(((addr<<1)&0xFE)|((data>>8)&0x1)) +#define MAKE_WM8775_DATA(data) (u32)(data&0xFF) + +#define WM8775_IC 0x0B +#define WM8775_MMC 0x0C +#define WM8775_AADCL 0x0E +#define WM8775_AADCR 0x0F +#define WM8775_ADCMC 0x15 +#define WM8775_RESET 0x17 + +static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO_DATA); + switch (type) { + case ADC_MICIN: + data = (data & (0x1 << 14)) ? 1 : 0; + break; + case ADC_LINEIN: + data = (data & (0x1 << 14)) ? 0 : 1; + break; + default: + data = 0; + } + return data; +} + +static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) +{ + u32 data = 0; + + data = hw_read_20kx(hw, GPIO_DATA); + switch (type) { + case ADC_MICIN: + data |= (0x1 << 14); + hw_write_20kx(hw, GPIO_DATA, data); + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + MAKE_WM8775_DATA(0x101)); /* Mic-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + break; + case ADC_LINEIN: + data &= ~(0x1 << 14); + hw_write_20kx(hw, GPIO_DATA, data); + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + MAKE_WM8775_DATA(0x102)); /* Line-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + break; + default: + break; + } + + return 0; +} + +static int hw_adc_init(struct hw *hw, const struct adc_conf *info) +{ + int err = 0; + u32 dwMux = 2, dwData = 0, dwCtl = 0; + + /* Set ADC reset bit as output */ + dwData = hw_read_20kx(hw, GPIO_CTRL); + dwData |= (0x1 << 15); + hw_write_20kx(hw, GPIO_CTRL, dwData); + + /* Initialize I2C */ + err = I2CInit(hw, 0x1A, 1, 1); + if (err < 0) { + printk(KERN_ALERT "Failure to acquire I2C!!!\n"); + goto error; + } + + /* Make ADC in normal operation */ + dwData = hw_read_20kx(hw, GPIO_DATA); + dwData &= ~(0x1 << 15); + mdelay(10); + dwData |= (0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, dwData); + mdelay(50); + + /* Set the master mode (256fs) */ + if (1 == info->msr) { + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), + MAKE_WM8775_DATA(0x02)); + } else if (2 == info->msr) { + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), + MAKE_WM8775_DATA(0x0A)); + } else { + printk(KERN_ALERT "Invalid master sampling " + "rate (msr %d)!!!\n", info->msr); + err = -EINVAL; + goto error; + } + + /* Configure GPIO bit 14 change to line-in/mic-in */ + dwCtl = hw_read_20kx(hw, GPIO_CTRL); + dwCtl |= 0x1<<14; + hw_write_20kx(hw, GPIO_CTRL, dwCtl); + + /* Check using Mic-in or Line-in */ + dwData = hw_read_20kx(hw, GPIO_DATA); + + if (dwMux == 1) { + /* Configures GPIO data to select Mic-in */ + dwData |= 0x1<<14; + hw_write_20kx(hw, GPIO_DATA, dwData); + + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + MAKE_WM8775_DATA(0x101)); /* Mic-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + } else if (dwMux == 2) { + /* Configures GPIO data to select Line-in */ + dwData &= ~(0x1<<14); + hw_write_20kx(hw, GPIO_DATA, dwData); + + /* Setup ADC */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + MAKE_WM8775_DATA(0x102)); /* Line-in */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + MAKE_WM8775_DATA(0xCF)); /* No boost */ + } else { + printk(KERN_ALERT "ERROR!!! Invalid input mux!!!\n"); + err = -EINVAL; + goto error; + } + + return 0; + +error: + I2CUninit(hw); + return err; +} + +static int hw_have_digit_io_switch(struct hw *hw) +{ + return 0; +} + +static int hw_card_start(struct hw *hw) +{ + int err = 0; + struct pci_dev *pci = hw->pci; + unsigned int gctl; + unsigned int dma_mask = 0; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + /* Set DMA transfer mask */ + dma_mask = CT_XFI_DMA_MASK; + if (pci_set_dma_mask(pci, dma_mask) < 0 || + pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + printk(KERN_ERR "architecture does not support PCI " + "busmaster DMA with mask 0x%x\n", dma_mask); + err = -ENXIO; + goto error1; + } + + err = pci_request_regions(pci, "XFi"); + if (err < 0) + goto error1; + + hw->io_base = pci_resource_start(hw->pci, 2); + hw->mem_base = (unsigned long)ioremap(hw->io_base, + pci_resource_len(hw->pci, 2)); + if (NULL == (void *)hw->mem_base) { + err = -ENOENT; + goto error2; + } + + /* Switch to 20k2 mode from UAA mode. */ + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + set_field(&gctl, GCTL_UAA, 0); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + + /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, + atc->chip_details->nm_card, hw))) { + goto error3; + } + hw->irq = pci->irq; + */ + + pci_set_master(pci); + + return 0; + +/*error3: + iounmap((void *)hw->mem_base); + hw->mem_base = (unsigned long)NULL;*/ +error2: + pci_release_regions(pci); + hw->io_base = 0; +error1: + pci_disable_device(pci); + return err; +} + +static int hw_card_stop(struct hw *hw) +{ + /* TODO: Disable interrupt and so on... */ + return 0; +} + +static int hw_card_shutdown(struct hw *hw) +{ + if (hw->irq >= 0) + free_irq(hw->irq, hw); + + hw->irq = -1; + + if (NULL != ((void *)hw->mem_base)) + iounmap((void *)hw->mem_base); + + hw->mem_base = (unsigned long)NULL; + + if (hw->io_base) + pci_release_regions(hw->pci); + + hw->io_base = 0; + + pci_disable_device(hw->pci); + + return 0; +} + +static int hw_card_init(struct hw *hw, struct card_conf *info) +{ + int err; + unsigned int gctl; + u32 data = 0; + struct dac_conf dac_info = {0}; + struct adc_conf adc_info = {0}; + struct daio_conf daio_info = {0}; + struct trn_conf trn_info = {0}; + + /* Get PCI io port/memory base address and + * do 20kx core switch if needed. */ + if (!hw->io_base) { + err = hw_card_start(hw); + if (err) + return err; + } + + /* PLL init */ + err = hw_pll_init(hw, info->rsr); + if (err < 0) + return err; + + /* kick off auto-init */ + err = hw_auto_init(hw); + if (err < 0) + return err; + + gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL); + set_field(&gctl, GCTL_DBP, 1); + set_field(&gctl, GCTL_TBP, 1); + set_field(&gctl, GCTL_FBP, 1); + set_field(&gctl, GCTL_DPC, 0); + hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); + + /* Reset all global pending interrupts */ + hw_write_20kx(hw, INTERRUPT_GIE, 0); + /* Reset all SRC pending interrupts */ + hw_write_20kx(hw, SRC_IP, 0); + + /* TODO: detect the card ID and configure GPIO accordingly. */ + /* Configures GPIO (0xD802 0x98028) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/ + /* Configures GPIO (SB0880) */ + /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/ + hw_write_20kx(hw, GPIO_CTRL, 0xD802); + + /* Enable audio ring */ + hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01); + + trn_info.vm_pgt_phys = info->vm_pgt_phys; + err = hw_trn_init(hw, &trn_info); + if (err < 0) + return err; + + daio_info.msr = info->msr; + err = hw_daio_init(hw, &daio_info); + if (err < 0) + return err; + + dac_info.msr = info->msr; + err = hw_dac_init(hw, &dac_info); + if (err < 0) + return err; + + adc_info.msr = info->msr; + adc_info.input = ADC_LINEIN; + adc_info.mic20db = 0; + err = hw_adc_init(hw, &adc_info); + if (err < 0) + return err; + + data = hw_read_20kx(hw, SRC_MCTL); + data |= 0x1; /* Enables input from the audio ring */ + hw_write_20kx(hw, SRC_MCTL, data); + + return 0; +} + +static u32 hw_read_20kx(struct hw *hw, u32 reg) +{ + return readl((void *)(hw->mem_base + reg)); +} + +static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) +{ + writel(data, (void *)(hw->mem_base + reg)); +} + +int create_20k2_hw_obj(struct hw **rhw) +{ + struct hw *hw; + + *rhw = NULL; + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (NULL == hw) + return -ENOMEM; + + hw->io_base = 0; + hw->mem_base = (unsigned long)NULL; + hw->irq = -1; + + hw->card_init = hw_card_init; + hw->card_stop = hw_card_stop; + hw->pll_init = hw_pll_init; + hw->is_adc_source_selected = hw_is_adc_input_selected; + hw->select_adc_source = hw_adc_input_select; + hw->have_digit_io_switch = hw_have_digit_io_switch; + + hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; + hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; + hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; + hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; + hw->src_set_state = src_set_state; + hw->src_set_bm = src_set_bm; + hw->src_set_rsr = src_set_rsr; + hw->src_set_sf = src_set_sf; + hw->src_set_wr = src_set_wr; + hw->src_set_pm = src_set_pm; + hw->src_set_rom = src_set_rom; + hw->src_set_vo = src_set_vo; + hw->src_set_st = src_set_st; + hw->src_set_ie = src_set_ie; + hw->src_set_ilsz = src_set_ilsz; + hw->src_set_bp = src_set_bp; + hw->src_set_cisz = src_set_cisz; + hw->src_set_ca = src_set_ca; + hw->src_set_sa = src_set_sa; + hw->src_set_la = src_set_la; + hw->src_set_pitch = src_set_pitch; + hw->src_set_dirty = src_set_dirty; + hw->src_set_clear_zbufs = src_set_clear_zbufs; + hw->src_set_dirty_all = src_set_dirty_all; + hw->src_commit_write = src_commit_write; + hw->src_get_ca = src_get_ca; + hw->src_get_dirty = src_get_dirty; + hw->src_dirty_conj_mask = src_dirty_conj_mask; + hw->src_mgr_enbs_src = src_mgr_enbs_src; + hw->src_mgr_enb_src = src_mgr_enb_src; + hw->src_mgr_dsb_src = src_mgr_dsb_src; + hw->src_mgr_commit_write = src_mgr_commit_write; + + hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; + hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; + hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; + hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; + hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; + hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; + hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; + + hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; + hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; + hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; + hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; + hw->amixer_set_mode = amixer_set_mode; + hw->amixer_set_iv = amixer_set_iv; + hw->amixer_set_x = amixer_set_x; + hw->amixer_set_y = amixer_set_y; + hw->amixer_set_sadr = amixer_set_sadr; + hw->amixer_set_se = amixer_set_se; + hw->amixer_set_dirty = amixer_set_dirty; + hw->amixer_set_dirty_all = amixer_set_dirty_all; + hw->amixer_commit_write = amixer_commit_write; + hw->amixer_get_y = amixer_get_y; + hw->amixer_get_dirty = amixer_get_dirty; + + hw->dai_get_ctrl_blk = dai_get_ctrl_blk; + hw->dai_put_ctrl_blk = dai_put_ctrl_blk; + hw->dai_srt_set_srco = dai_srt_set_srco; + hw->dai_srt_set_srcm = dai_srt_set_srcm; + hw->dai_srt_set_rsr = dai_srt_set_rsr; + hw->dai_srt_set_drat = dai_srt_set_drat; + hw->dai_srt_set_ec = dai_srt_set_ec; + hw->dai_srt_set_et = dai_srt_set_et; + hw->dai_commit_write = dai_commit_write; + + hw->dao_get_ctrl_blk = dao_get_ctrl_blk; + hw->dao_put_ctrl_blk = dao_put_ctrl_blk; + hw->dao_set_spos = dao_set_spos; + hw->dao_commit_write = dao_commit_write; + hw->dao_get_spos = dao_get_spos; + + hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; + hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; + hw->daio_mgr_enb_dai = daio_mgr_enb_dai; + hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; + hw->daio_mgr_enb_dao = daio_mgr_enb_dao; + hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; + hw->daio_mgr_dao_init = daio_mgr_dao_init; + hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; + hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; + hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; + hw->daio_mgr_commit_write = daio_mgr_commit_write; + + *rhw = hw; + + return 0; +} + +int destroy_20k2_hw_obj(struct hw *hw) +{ + if (hw->io_base) + hw_card_shutdown(hw); + + kfree(hw); + return 0; +} diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h new file mode 100644 index 000000000000..d2b7daab6815 --- /dev/null +++ b/sound/pci/ctxfi/cthw20k2.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File cthw20k2.h + * + * @Brief + * This file contains the definition of hardware access methord. + * + * @Author Liu Chun + * @Date May 13 2008 + * + */ + +#ifndef CTHW20K2_H +#define CTHW20K2_H + +#include "cthardware.h" + +int create_20k2_hw_obj(struct hw **rhw); +int destroy_20k2_hw_obj(struct hw *hw); + +#endif /* CTHW20K2_H */ diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c new file mode 100644 index 000000000000..d34eacd902ce --- /dev/null +++ b/sound/pci/ctxfi/ctimap.c @@ -0,0 +1,112 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctimap.c + * + * @Brief + * This file contains the implementation of generic input mapper operations + * for input mapper management. + * + * @Author Liu Chun + * @Date May 23 2008 + * + */ + +#include "ctimap.h" +#include + +int input_mapper_add(struct list_head *mappers, struct imapper *entry, + int (*map_op)(void *, struct imapper *), void *data) +{ + struct list_head *pos, *pre, *head; + struct imapper *pre_ent, *pos_ent; + + head = mappers; + + if (list_empty(head)) { + entry->next = entry->addr; + map_op(data, entry); + list_add(&entry->list, head); + return 0; + } + + list_for_each(pos, head) { + pos_ent = list_entry(pos, struct imapper, list); + if (pos_ent->slot > entry->slot) { + /* found a position in list */ + break; + } + } + + if (pos != head) { + pre = pos->prev; + if (pre == head) + pre = head->prev; + + __list_add(&entry->list, pos->prev, pos); + } else { + pre = head->prev; + pos = head->next; + list_add_tail(&entry->list, head); + } + + pre_ent = list_entry(pre, struct imapper, list); + pos_ent = list_entry(pos, struct imapper, list); + + entry->next = pos_ent->addr; + map_op(data, entry); + pre_ent->next = entry->addr; + map_op(data, pre_ent); + + return 0; +} + +int input_mapper_delete(struct list_head *mappers, struct imapper *entry, + int (*map_op)(void *, struct imapper *), void *data) +{ + struct list_head *next, *pre, *head; + struct imapper *pre_ent, *next_ent; + + head = mappers; + + if (list_empty(head)) + return 0; + + pre = (entry->list.prev == head) ? head->prev : entry->list.prev; + next = (entry->list.next == head) ? head->next : entry->list.next; + + if (pre == &entry->list) { + /* entry is the only one node in mappers list */ + entry->next = entry->addr = entry->user = entry->slot = 0; + map_op(data, entry); + list_del(&entry->list); + return 0; + } + + pre_ent = list_entry(pre, struct imapper, list); + next_ent = list_entry(next, struct imapper, list); + + pre_ent->next = next_ent->addr; + map_op(data, pre_ent); + list_del(&entry->list); + + return 0; +} + +void free_input_mapper_list(struct list_head *head) +{ + struct imapper *entry = NULL; + struct list_head *pos = NULL; + + while (!list_empty(head)) { + pos = head->next; + list_del(pos); + entry = list_entry(pos, struct imapper, list); + kfree(entry); + } +} + diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h new file mode 100644 index 000000000000..53ccf9be8b68 --- /dev/null +++ b/sound/pci/ctxfi/ctimap.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctimap.h + * + * @Brief + * This file contains the definition of generic input mapper operations + * for input mapper management. + * + * @Author Liu Chun + * @Date May 23 2008 + * + */ + +#ifndef CTIMAP_H +#define CTIMAP_H + +#include + +struct imapper { + unsigned short slot; /* the id of the slot containing input data */ + unsigned short user; /* the id of the user resource consuming data */ + unsigned short addr; /* the input mapper ram id */ + unsigned short next; /* the next input mapper ram id */ + struct list_head list; +}; + +int input_mapper_add(struct list_head *mappers, struct imapper *entry, + int (*map_op)(void *, struct imapper *), void *data); + +int input_mapper_delete(struct list_head *mappers, struct imapper *entry, + int (*map_op)(void *, struct imapper *), void *data); + +void free_input_mapper_list(struct list_head *mappers); + +#endif /* CTIMAP_H */ diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c new file mode 100644 index 000000000000..c80d692952f9 --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.c @@ -0,0 +1,1108 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctmixer.c + * + * @Brief + * This file contains the implementation of alsa mixer device functions. + * + * @Author Liu Chun + * @Date May 28 2008 + * + */ + + +#include "ctmixer.h" +#include "ctamixer.h" +#include +#include +#include +#include +#include + +enum CT_SUM_CTL { + SUM_IN_F, + SUM_IN_R, + SUM_IN_C, + SUM_IN_S, + SUM_IN_F_C, + + NUM_CT_SUMS +}; + +enum CT_AMIXER_CTL { + /* volume control mixers */ + AMIXER_MASTER_F, + AMIXER_MASTER_R, + AMIXER_MASTER_C, + AMIXER_MASTER_S, + AMIXER_PCM_F, + AMIXER_PCM_R, + AMIXER_PCM_C, + AMIXER_PCM_S, + AMIXER_SPDIFI, + AMIXER_LINEIN, + AMIXER_MIC, + AMIXER_SPDIFO, + AMIXER_WAVE_F, + AMIXER_WAVE_R, + AMIXER_WAVE_C, + AMIXER_WAVE_S, + AMIXER_MASTER_F_C, + AMIXER_PCM_F_C, + AMIXER_SPDIFI_C, + AMIXER_LINEIN_C, + AMIXER_MIC_C, + + /* this should always be the last one */ + NUM_CT_AMIXERS +}; + +enum CTALSA_MIXER_CTL { + /* volume control mixers */ + MIXER_MASTER_P, + MIXER_PCM_P, + MIXER_LINEIN_P, + MIXER_MIC_P, + MIXER_SPDIFI_P, + MIXER_SPDIFO_P, + MIXER_WAVEF_P, + MIXER_WAVER_P, + MIXER_WAVEC_P, + MIXER_WAVES_P, + MIXER_MASTER_C, + MIXER_PCM_C, + MIXER_LINEIN_C, + MIXER_MIC_C, + MIXER_SPDIFI_C, + + /* switch control mixers */ + MIXER_PCM_C_S, + MIXER_LINEIN_C_S, + MIXER_MIC_C_S, + MIXER_SPDIFI_C_S, + MIXER_LINEIN_P_S, + MIXER_SPDIFO_P_S, + MIXER_SPDIFI_P_S, + MIXER_WAVEF_P_S, + MIXER_WAVER_P_S, + MIXER_WAVEC_P_S, + MIXER_WAVES_P_S, + MIXER_DIGITAL_IO_S, + MIXER_IEC958_MASK, + MIXER_IEC958_DEFAULT, + MIXER_IEC958_STREAM, + + /* this should always be the last one */ + NUM_CTALSA_MIXERS +}; + +#define VOL_MIXER_START MIXER_MASTER_P +#define VOL_MIXER_END MIXER_SPDIFI_C +#define VOL_MIXER_NUM (VOL_MIXER_END - VOL_MIXER_START + 1) +#define SWH_MIXER_START MIXER_PCM_C_S +#define SWH_MIXER_END MIXER_DIGITAL_IO_S +#define SWH_CAPTURE_START MIXER_PCM_C_S +#define SWH_CAPTURE_END MIXER_SPDIFI_C_S + +#define CHN_NUM 2 + +struct ct_kcontrol_init { + unsigned char ctl; + char *name; +}; + +static struct ct_kcontrol_init +ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { + [MIXER_MASTER_P] = { + .ctl = 1, + .name = "Master Playback Volume", + }, + [MIXER_MASTER_C] = { + .ctl = 1, + .name = "Master Capture Volume", + }, + [MIXER_PCM_P] = { + .ctl = 1, + .name = "PCM Playback Volume", + }, + [MIXER_PCM_C] = { + .ctl = 1, + .name = "PCM Capture Volume", + }, + [MIXER_LINEIN_P] = { + .ctl = 1, + .name = "Line-in Playback Volume", + }, + [MIXER_LINEIN_C] = { + .ctl = 1, + .name = "Line-in Capture Volume", + }, + [MIXER_MIC_P] = { + .ctl = 1, + .name = "Mic Playback Volume", + }, + [MIXER_MIC_C] = { + .ctl = 1, + .name = "Mic Capture Volume", + }, + [MIXER_SPDIFI_P] = { + .ctl = 1, + .name = "S/PDIF-in Playback Volume", + }, + [MIXER_SPDIFI_C] = { + .ctl = 1, + .name = "S/PDIF-in Capture Volume", + }, + [MIXER_SPDIFO_P] = { + .ctl = 1, + .name = "S/PDIF-out Playback Volume", + }, + [MIXER_WAVEF_P] = { + .ctl = 1, + .name = "Front Playback Volume", + }, + [MIXER_WAVES_P] = { + .ctl = 1, + .name = "Surround Playback Volume", + }, + [MIXER_WAVEC_P] = { + .ctl = 1, + .name = "Center/LFE Playback Volume", + }, + [MIXER_WAVER_P] = { + .ctl = 1, + .name = "Rear Playback Volume", + }, + + [MIXER_PCM_C_S] = { + .ctl = 1, + .name = "PCM Capture Switch", + }, + [MIXER_LINEIN_C_S] = { + .ctl = 1, + .name = "Line-in Capture Switch", + }, + [MIXER_MIC_C_S] = { + .ctl = 1, + .name = "Mic Capture Switch", + }, + [MIXER_SPDIFI_C_S] = { + .ctl = 1, + .name = "S/PDIF-in Capture Switch", + }, + [MIXER_LINEIN_P_S] = { + .ctl = 1, + .name = "Line-in Playback Switch", + }, + [MIXER_SPDIFO_P_S] = { + .ctl = 1, + .name = "S/PDIF-out Playback Switch", + }, + [MIXER_SPDIFI_P_S] = { + .ctl = 1, + .name = "S/PDIF-in Playback Switch", + }, + [MIXER_WAVEF_P_S] = { + .ctl = 1, + .name = "Front Playback Switch", + }, + [MIXER_WAVES_P_S] = { + .ctl = 1, + .name = "Surround Playback Switch", + }, + [MIXER_WAVEC_P_S] = { + .ctl = 1, + .name = "Center/LFE Playback Switch", + }, + [MIXER_WAVER_P_S] = { + .ctl = 1, + .name = "Rear Playback Switch", + }, + [MIXER_DIGITAL_IO_S] = { + .ctl = 0, + .name = "Digit-IO Playback Switch", + }, +}; + +static void +ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); + +static void +ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type); + +static struct snd_kcontrol *kctls[2] = {NULL}; + +static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index) +{ + switch (alsa_index) { + case MIXER_MASTER_P: return AMIXER_MASTER_F; + case MIXER_MASTER_C: return AMIXER_MASTER_F_C; + case MIXER_PCM_P: return AMIXER_PCM_F; + case MIXER_PCM_C: + case MIXER_PCM_C_S: return AMIXER_PCM_F_C; + case MIXER_LINEIN_P: return AMIXER_LINEIN; + case MIXER_LINEIN_C: + case MIXER_LINEIN_C_S: return AMIXER_LINEIN_C; + case MIXER_MIC_P: return AMIXER_MIC; + case MIXER_MIC_C: + case MIXER_MIC_C_S: return AMIXER_MIC_C; + case MIXER_SPDIFI_P: return AMIXER_SPDIFI; + case MIXER_SPDIFI_C: + case MIXER_SPDIFI_C_S: return AMIXER_SPDIFI_C; + case MIXER_SPDIFO_P: return AMIXER_SPDIFO; + case MIXER_WAVEF_P: return AMIXER_WAVE_F; + case MIXER_WAVES_P: return AMIXER_WAVE_S; + case MIXER_WAVEC_P: return AMIXER_WAVE_C; + case MIXER_WAVER_P: return AMIXER_WAVE_R; + default: return NUM_CT_AMIXERS; + } +} + +static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index) +{ + switch (index) { + case AMIXER_MASTER_F: return AMIXER_MASTER_F_C; + case AMIXER_PCM_F: return AMIXER_PCM_F_C; + case AMIXER_SPDIFI: return AMIXER_SPDIFI_C; + case AMIXER_LINEIN: return AMIXER_LINEIN_C; + case AMIXER_MIC: return AMIXER_MIC_C; + default: return NUM_CT_AMIXERS; + } +} + +static unsigned char +get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type) +{ + return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START))) + ? 1 : 0; +} + +static void +set_switch_state(struct ct_mixer *mixer, + enum CTALSA_MIXER_CTL type, unsigned char state) +{ + if (state) + mixer->switch_state |= (0x1 << (type - SWH_MIXER_START)); + else + mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START)); +} + +/* Map integer value ranging from 0 to 65535 to 14-bit float value ranging + * from 2^-6 to (1+1023/1024) */ +static unsigned int uint16_to_float14(unsigned int x) +{ + unsigned int i = 0; + + if (x < 17) + return 0; + + x *= 2031; + x /= 65535; + x += 16; + + /* i <= 6 */ + for (i = 0; !(x & 0x400); i++) + x <<= 1; + + x = (((7 - i) & 0x7) << 10) | (x & 0x3ff); + + return x; +} + +static unsigned int float14_to_uint16(unsigned int x) +{ + unsigned int e = 0; + + if (!x) + return x; + + e = (x >> 10) & 0x7; + x &= 0x3ff; + x += 1024; + x >>= (7 - e); + x -= 16; + x *= 65535; + x /= 2031; + + return x; +} + +static int ct_alsa_mix_volume_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 = 43690; + uinfo->value.integer.step = 128; + + return 0; +} + +static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); + struct amixer *amixer = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + amixer = ((struct ct_mixer *)atc->mixer)-> + amixers[type*CHN_NUM+i]; + /* Convert 14-bit float-point scale to 16-bit integer volume */ + ucontrol->value.integer.value[i] = + (float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xffff); + } + + return 0; +} + +static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + struct ct_mixer *mixer = atc->mixer; + enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); + struct amixer *amixer = NULL; + int i = 0, j = 0, change = 0, val = 0; + + for (i = 0; i < 2; i++) { + /* Convert 16-bit integer volume to 14-bit float-point scale */ + val = (ucontrol->value.integer.value[i] & 0xffff); + amixer = mixer->amixers[type*CHN_NUM+i]; + if ((float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xff80) + != (val & 0xff80)) { + val = uint16_to_float14(val); + amixer->ops->set_scale(amixer, val); + amixer->ops->commit_write(amixer); + change = 1; + /* Synchronize Master/PCM playback AMIXERs. */ + if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) { + for (j = 1; j < 4; j++) { + amixer = mixer-> + amixers[(type+j)*CHN_NUM+i]; + amixer->ops->set_scale(amixer, val); + amixer->ops->commit_write(amixer); + } + } + } + } + + return change; +} + +static struct snd_kcontrol_new vol_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = ct_alsa_mix_volume_info, + .get = ct_alsa_mix_volume_get, + .put = ct_alsa_mix_volume_put +}; + +static void +do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type) +{ + + if (MIXER_LINEIN_C_S == type) { + atc->select_line_in(atc); + set_switch_state(atc->mixer, MIXER_MIC_C_S, 0); + snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctls[1]->id); + } else if (MIXER_MIC_C_S == type) { + atc->select_mic_in(atc); + set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0); + snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctls[0]->id); + } +} + +static void +do_digit_io_switch(struct ct_atc *atc, int state) +{ + struct ct_mixer *mixer = atc->mixer; + + if (state) { + atc->select_digit_io(atc); + atc->spdif_out_unmute(atc, + get_switch_state(mixer, MIXER_SPDIFO_P_S)); + atc->spdif_in_unmute(atc, 1); + atc->line_in_unmute(atc, 0); + return; + } + + if (get_switch_state(mixer, MIXER_LINEIN_C_S)) + atc->select_line_in(atc); + else if (get_switch_state(mixer, MIXER_MIC_C_S)) + atc->select_mic_in(atc); + + atc->spdif_out_unmute(atc, 0); + atc->spdif_in_unmute(atc, 0); + atc->line_in_unmute(atc, 1); + return; +} + +static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->value.integer.step = 1; + + return 0; +} + +static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_mixer *mixer = + ((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer; + enum CTALSA_MIXER_CTL type = kcontrol->private_value; + + ucontrol->value.integer.value[0] = get_switch_state(mixer, type); + return 0; +} + +static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + struct ct_mixer *mixer = atc->mixer; + enum CTALSA_MIXER_CTL type = kcontrol->private_value; + int state = 0; + + state = ucontrol->value.integer.value[0]; + if (get_switch_state(mixer, type) == state) + return 0; + + set_switch_state(mixer, type, state); + /* Do changes in mixer. */ + if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) { + if (state) { + ct_mixer_recording_select(mixer, + get_amixer_index(type)); + } else { + ct_mixer_recording_unselect(mixer, + get_amixer_index(type)); + } + } + /* Do changes out of mixer. */ + if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) + do_line_mic_switch(atc, type); + else if (MIXER_WAVEF_P_S == type) + atc->line_front_unmute(atc, state); + else if (MIXER_WAVES_P_S == type) + atc->line_surround_unmute(atc, state); + else if (MIXER_WAVEC_P_S == type) + atc->line_clfe_unmute(atc, state); + else if (MIXER_WAVER_P_S == type) + atc->line_rear_unmute(atc, state); + else if (MIXER_LINEIN_P_S == type) + atc->line_in_unmute(atc, state); + else if (MIXER_SPDIFO_P_S == type) + atc->spdif_out_unmute(atc, state); + else if (MIXER_SPDIFI_P_S == type) + atc->spdif_in_unmute(atc, state); + else if (MIXER_DIGITAL_IO_S == type) + do_digit_io_switch(atc, state); + + return 1; +} + +static struct snd_kcontrol_new swh_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = ct_alsa_mix_switch_info, + .get = ct_alsa_mix_switch_get, + .put = ct_alsa_mix_switch_put +}; + +static int ct_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; + + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; + + return 0; +} + +static int ct_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + unsigned int status = 0; + + atc->spdif_out_get_status(atc, &status); + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; + + return 0; +} + +static int ct_spdif_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ct_atc *atc = snd_kcontrol_chip(kcontrol); + int change = 1; + unsigned int status = 0, old_status = 0; + + status = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + + atc->spdif_out_get_status(atc, &old_status); + change = (old_status != status); + if (change) + atc->spdif_out_set_status(atc, status); + + return change; +} + +static struct snd_kcontrol_new iec958_mask_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .count = 1, + .info = ct_spdif_info, + .get = ct_spdif_get_mask, + .private_value = MIXER_IEC958_MASK +}; + +static struct snd_kcontrol_new iec958_default_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .count = 1, + .info = ct_spdif_info, + .get = ct_spdif_default_get, + .put = ct_spdif_put, + .private_value = MIXER_IEC958_DEFAULT +}; + +static struct snd_kcontrol_new iec958_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .count = 1, + .info = ct_spdif_info, + .get = ct_spdif_get, + .put = ct_spdif_put, + .private_value = MIXER_IEC958_STREAM +}; + +#define NUM_IEC958_CTL 3 + +static int +ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) +{ + struct snd_kcontrol *kctl = NULL; + int err = 0; + + kctl = snd_ctl_new1(new, mixer->atc); + if (NULL == kctl) + return -ENOMEM; + + if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface) + kctl->id.device = IEC958; + + err = snd_ctl_add(mixer->atc->card, kctl); + if (err) + return err; + + switch (new->private_value) { + case MIXER_LINEIN_C_S: + kctls[0] = kctl; break; + case MIXER_MIC_C_S: + kctls[1] = kctl; break; + default: + break; + } + + return 0; +} + +static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) +{ + enum CTALSA_MIXER_CTL type = 0; + struct ct_atc *atc = mixer->atc; + int err = 0; + + /* Create snd kcontrol instances on demand */ + for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) { + if (ct_kcontrol_init_table[type].ctl) { + vol_ctl.name = ct_kcontrol_init_table[type].name; + vol_ctl.private_value = (unsigned long)type; + err = ct_mixer_kcontrol_new(mixer, &vol_ctl); + if (err) + return err; + } + } + + ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = + atc->have_digit_io_switch(atc); + for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) { + if (ct_kcontrol_init_table[type].ctl) { + swh_ctl.name = ct_kcontrol_init_table[type].name; + swh_ctl.private_value = (unsigned long)type; + err = ct_mixer_kcontrol_new(mixer, &swh_ctl); + if (err) + return err; + } + } + + err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl); + if (err) + return err; + + err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl); + if (err) + return err; + + err = ct_mixer_kcontrol_new(mixer, &iec958_ctl); + if (err) + return err; + + atc->line_front_unmute(atc, 1); + set_switch_state(mixer, MIXER_WAVEF_P_S, 1); + atc->line_surround_unmute(atc, 0); + set_switch_state(mixer, MIXER_WAVES_P_S, 0); + atc->line_clfe_unmute(atc, 0); + set_switch_state(mixer, MIXER_WAVEC_P_S, 0); + atc->line_rear_unmute(atc, 0); + set_switch_state(mixer, MIXER_WAVER_P_S, 0); + atc->spdif_out_unmute(atc, 0); + set_switch_state(mixer, MIXER_SPDIFO_P_S, 0); + atc->line_in_unmute(atc, 0); + set_switch_state(mixer, MIXER_LINEIN_P_S, 0); + atc->spdif_in_unmute(atc, 0); + set_switch_state(mixer, MIXER_SPDIFI_P_S, 0); + + set_switch_state(mixer, MIXER_PCM_C_S, 1); + set_switch_state(mixer, MIXER_LINEIN_C_S, 1); + set_switch_state(mixer, MIXER_SPDIFI_C_S, 1); + + return 0; +} + +static void +ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) +{ + struct amixer *amix_d = NULL; + struct sum *sum_c = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + amix_d = mixer->amixers[type*CHN_NUM+i]; + sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i]; + amix_d->ops->set_sum(amix_d, sum_c); + amix_d->ops->commit_write(amix_d); + } +} + +static void +ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) +{ + struct amixer *amix_d = NULL; + int i = 0; + + for (i = 0; i < 2; i++) { + amix_d = mixer->amixers[type*CHN_NUM+i]; + amix_d->ops->set_sum(amix_d, NULL); + amix_d->ops->commit_write(amix_d); + } +} + +static int ct_mixer_get_resources(struct ct_mixer *mixer) +{ + struct sum_mgr *sum_mgr = NULL; + struct sum *sum = NULL; + struct sum_desc sum_desc = {0}; + struct amixer_mgr *amixer_mgr = NULL; + struct amixer *amixer = NULL; + struct amixer_desc am_desc = {0}; + int err = 0; + int i = 0; + + /* Allocate sum resources for mixer obj */ + sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; + sum_desc.msr = mixer->atc->msr; + for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { + err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); + if (err) { + printk(KERN_ERR "Failed to get sum resources for " + "front output!\n"); + break; + } + mixer->sums[i] = sum; + } + if (err) + goto error1; + + /* Allocate amixer resources for mixer obj */ + amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; + am_desc.msr = mixer->atc->msr; + for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { + err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); + if (err) { + printk(KERN_ERR "Failed to get amixer resources for " + "mixer obj!\n"); + break; + } + mixer->amixers[i] = amixer; + } + if (err) + goto error2; + + return 0; + +error2: + for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { + if (NULL != mixer->amixers[i]) { + amixer = mixer->amixers[i]; + amixer_mgr->put_amixer(amixer_mgr, amixer); + mixer->amixers[i] = NULL; + } + } +error1: + for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { + if (NULL != mixer->sums[i]) { + sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); + mixer->sums[i] = NULL; + } + } + + return err; +} + +static int ct_mixer_get_mem(struct ct_mixer **rmixer) +{ + struct ct_mixer *mixer = NULL; + int err = 0; + + *rmixer = NULL; + /* Allocate mem for mixer obj */ + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + if (NULL == mixer) + return -ENOMEM; + + mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), + GFP_KERNEL); + if (NULL == mixer->amixers) { + err = -ENOMEM; + goto error1; + } + mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); + if (NULL == mixer->sums) { + err = -ENOMEM; + goto error2; + } + + *rmixer = mixer; + return 0; + +error2: + kfree(mixer->amixers); +error1: + kfree(mixer); + return err; +} + +static int ct_mixer_topology_build(struct ct_mixer *mixer) +{ + struct sum *sum = NULL; + struct amixer *amix_d = NULL, *amix_s = NULL; + enum CT_AMIXER_CTL i = 0, j = 0; + + /* Build topology from destination to source */ + + /* Set up Master mixer */ + for (i = AMIXER_MASTER_F, j = SUM_IN_F; + i <= AMIXER_MASTER_S; i++, j++) { + amix_d = mixer->amixers[i*CHN_NUM]; + sum = mixer->sums[j*CHN_NUM]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[i*CHN_NUM+1]; + sum = mixer->sums[j*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + } + + /* Set up Wave-out mixer */ + for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F; + i <= AMIXER_WAVE_S; i++, j++) { + amix_d = mixer->amixers[i*CHN_NUM]; + amix_s = mixer->amixers[j*CHN_NUM]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[i*CHN_NUM+1]; + amix_s = mixer->amixers[j*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + } + + /* Set up S/PDIF-out mixer */ + amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM]; + amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1]; + amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL); + + /* Set up PCM-in mixer */ + for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) { + amix_d = mixer->amixers[i*CHN_NUM]; + sum = mixer->sums[j*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[i*CHN_NUM+1]; + sum = mixer->sums[j*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + } + + /* Set up Line-in mixer */ + amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM]; + sum = mixer->sums[SUM_IN_F*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Mic-in mixer */ + amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM]; + sum = mixer->sums[SUM_IN_F*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up S/PDIF-in mixer */ + amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM]; + sum = mixer->sums[SUM_IN_F*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Master recording mixer */ + amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL); + + /* Set up PCM-in recording mixer */ + amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Line-in recording mixer */ + amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up Mic-in recording mixer */ + amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + /* Set up S/PDIF-in recording mixer */ + amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1]; + sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1]; + amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum); + + return 0; +} + +static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc) +{ + amixer->ops->set_input(amixer, rsc); + amixer->ops->commit_write(amixer); + + return 0; +} + +static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type) +{ + switch (type) { + case MIX_WAVE_FRONT: return AMIXER_WAVE_F; + case MIX_WAVE_SURROUND: return AMIXER_WAVE_S; + case MIX_WAVE_CENTLFE: return AMIXER_WAVE_C; + case MIX_WAVE_REAR: return AMIXER_WAVE_R; + case MIX_PCMO_FRONT: return AMIXER_MASTER_F_C; + case MIX_SPDIF_OUT: return AMIXER_SPDIFO; + case MIX_LINE_IN: return AMIXER_LINEIN; + case MIX_MIC_IN: return AMIXER_MIC; + case MIX_SPDIF_IN: return AMIXER_SPDIFI; + case MIX_PCMI_FRONT: return AMIXER_PCM_F; + case MIX_PCMI_SURROUND: return AMIXER_PCM_S; + case MIX_PCMI_CENTLFE: return AMIXER_PCM_C; + case MIX_PCMI_REAR: return AMIXER_PCM_R; + default: return 0; + } +} + +static int mixer_get_output_ports(struct ct_mixer *mixer, + enum MIXER_PORT_T type, + struct rsc **rleft, struct rsc **rright) +{ + enum CT_AMIXER_CTL amix = port_to_amixer(type); + + if (NULL != rleft) + *rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc; + + if (NULL != rright) + *rright = + &((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc; + + return 0; +} + +static int mixer_set_input_left(struct ct_mixer *mixer, + enum MIXER_PORT_T type, struct rsc *rsc) +{ + enum CT_AMIXER_CTL amix = port_to_amixer(type); + + mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); + amix = get_recording_amixer(amix); + if (amix < NUM_CT_AMIXERS) + mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc); + + return 0; +} + +static int +mixer_set_input_right(struct ct_mixer *mixer, + enum MIXER_PORT_T type, struct rsc *rsc) +{ + enum CT_AMIXER_CTL amix = port_to_amixer(type); + + mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); + amix = get_recording_amixer(amix); + if (amix < NUM_CT_AMIXERS) + mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc); + + return 0; +} + +int ct_mixer_destroy(struct ct_mixer *mixer) +{ + struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; + struct amixer_mgr *amixer_mgr = + (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; + struct amixer *amixer = NULL; + int i = 0; + + /* Release amixer resources */ + for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { + if (NULL != mixer->amixers[i]) { + amixer = mixer->amixers[i]; + amixer_mgr->put_amixer(amixer_mgr, amixer); + } + } + + /* Release sum resources */ + for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { + if (NULL != mixer->sums[i]) + sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]); + } + + /* Release mem assigned to mixer object */ + kfree(mixer->sums); + kfree(mixer->amixers); + kfree(mixer); + + return 0; +} + +int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer) +{ + struct ct_mixer *mixer = NULL; + int err = 0; + + *rmixer = NULL; + + /* Allocate mem for mixer obj */ + err = ct_mixer_get_mem(&mixer); + if (err) + return err; + + mixer->switch_state = 0; + mixer->atc = atc; + /* Set operations */ + mixer->get_output_ports = mixer_get_output_ports; + mixer->set_input_left = mixer_set_input_left; + mixer->set_input_right = mixer_set_input_right; + + /* Allocate chip resources for mixer obj */ + err = ct_mixer_get_resources(mixer); + if (err) + goto error; + + /* Build internal mixer topology */ + ct_mixer_topology_build(mixer); + + *rmixer = mixer; + + return 0; + +error: + ct_mixer_destroy(mixer); + return err; +} + +int ct_alsa_mix_create(struct ct_atc *atc, + enum CTALSADEVS device, + const char *device_name) +{ + int err = 0; + + /* Create snd kcontrol instances on demand */ + vol_ctl.device = swh_ctl.device = device; + err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer); + if (err) + return err; + + strcpy(atc->card->mixername, device_name); + + return 0; +} diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h new file mode 100644 index 000000000000..e2d96ebde746 --- /dev/null +++ b/sound/pci/ctxfi/ctmixer.h @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctmixer.h + * + * @Brief + * This file contains the definition of the mixer device functions. + * + * @Author Liu Chun + * @Date Mar 28 2008 + * + */ + +#ifndef CTMIXER_H +#define CTMIXER_H + +#include "ctatc.h" +#include "ctresource.h" + +#define INIT_VOL 0x1c00 + +enum MIXER_PORT_T { + MIX_WAVE_FRONT, + MIX_WAVE_REAR, + MIX_WAVE_CENTLFE, + MIX_WAVE_SURROUND, + MIX_SPDIF_OUT, + MIX_PCMO_FRONT, + MIX_MIC_IN, + MIX_LINE_IN, + MIX_SPDIF_IN, + MIX_PCMI_FRONT, + MIX_PCMI_REAR, + MIX_PCMI_CENTLFE, + MIX_PCMI_SURROUND, + + NUM_MIX_PORTS +}; + +/* alsa mixer descriptor */ +struct ct_mixer { + struct ct_atc *atc; + + void **amixers; /* amixer resources for volume control */ + void **sums; /* sum resources for signal collection */ + unsigned int switch_state; /* A bit-map to indicate state of switches */ + + int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type, + struct rsc **rleft, struct rsc **rright); + + int (*set_input_left)(struct ct_mixer *mixer, + enum MIXER_PORT_T type, struct rsc *rsc); + int (*set_input_right)(struct ct_mixer *mixer, + enum MIXER_PORT_T type, struct rsc *rsc); +}; + +int ct_alsa_mix_create(struct ct_atc *atc, + enum CTALSADEVS device, + const char *device_name); +int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer); +int ct_mixer_destroy(struct ct_mixer *mixer); + +#endif /* CTMIXER_H */ diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c new file mode 100644 index 000000000000..73d4fdbbb9f4 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.c @@ -0,0 +1,499 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctpcm.c + * + * @Brief + * This file contains the definition of the pcm device functions. + * + * @Author Liu Chun + * @Date Apr 2 2008 + * + */ + +#include "ctpcm.h" +#include + +/* Hardware descriptions for playback */ +static struct snd_pcm_hardware ct_pcm_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_192000), + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = (64), + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE), + .rates = (SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_32000), + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = (64), + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* Hardware descriptions for capture */ +static struct snd_pcm_hardware ct_pcm_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_96000), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = (384), + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm) +{ + struct ct_atc_pcm *apcm = atc_pcm; + + if (NULL == apcm->substream) + return; + + snd_pcm_period_elapsed(apcm->substream); +} + +static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) +{ + struct ct_atc_pcm *apcm = runtime->private_data; + struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); + + atc->pcm_release_resources(atc, apcm); + kfree(apcm); + runtime->private_data = NULL; +} + +/* pcm playback operations */ +static int ct_pcm_playback_open(struct snd_pcm_substream *substream) +{ + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm; + int err; + + apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); + if (NULL == apcm) + return -ENOMEM; + + spin_lock_init(&apcm->timer_lock); + apcm->stop_timer = 0; + apcm->substream = substream; + apcm->interrupt = ct_atc_pcm_interrupt; + runtime->private_data = apcm; + runtime->private_free = ct_atc_pcm_free_substream; + if (IEC958 == substream->pcm->device) { + runtime->hw = ct_spdif_passthru_playback_hw; + atc->spdif_out_passthru(atc, 1); + } else { + runtime->hw = ct_pcm_playback_hw; + if (FRONT == substream->pcm->device) + runtime->hw.channels_max = 8; + } + + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) { + kfree(apcm); + return err; + } + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + 1024, UINT_MAX); + if (err < 0) { + kfree(apcm); + return err; + } + + return 0; +} + +static int ct_pcm_playback_close(struct snd_pcm_substream *substream) +{ + struct ct_atc *atc = snd_pcm_substream_chip(substream); + + /* TODO: Notify mixer inactive. */ + if (IEC958 == substream->pcm->device) + atc->spdif_out_passthru(atc, 0); + + /* The ct_atc_pcm object will be freed by runtime->private_free */ + + return 0; +} + +static int ct_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)); +} + +static int ct_pcm_hw_free(struct snd_pcm_substream *substream) +{ + /* Free snd-allocated pages */ + return snd_pcm_lib_free_pages(substream); +} + +static void ct_pcm_timer_callback(unsigned long data) +{ + struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; + struct snd_pcm_substream *substream = apcm->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int period_size = runtime->period_size; + unsigned int buffer_size = runtime->buffer_size; + unsigned long flags; + unsigned int position = 0, dist = 0, interval = 0; + + position = substream->ops->pointer(substream); + dist = (position + buffer_size - apcm->position) % buffer_size; + if ((dist >= period_size) || + (position/period_size != apcm->position/period_size)) { + apcm->interrupt(apcm); + apcm->position = position; + } + /* Add extra HZ*5/1000 to avoid overrun issue when recording + * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ + interval = ((period_size - (position % period_size)) + * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; + spin_lock_irqsave(&apcm->timer_lock, flags); + apcm->timer.expires = jiffies + interval; + if (!apcm->stop_timer) + add_timer(&apcm->timer); + + spin_unlock_irqrestore(&apcm->timer_lock, flags); +} + +static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) +{ + unsigned long flags; + + spin_lock_irqsave(&apcm->timer_lock, flags); + if (timer_pending(&apcm->timer)) { + /* The timer has already been started. */ + spin_unlock_irqrestore(&apcm->timer_lock, flags); + return 0; + } + + init_timer(&apcm->timer); + apcm->timer.data = (unsigned long)apcm; + apcm->timer.function = ct_pcm_timer_callback; + spin_unlock_irqrestore(&apcm->timer_lock, flags); + apcm->position = 0; + + return 0; +} + +static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) +{ + struct snd_pcm_runtime *runtime = apcm->substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&apcm->timer_lock, flags); + if (timer_pending(&apcm->timer)) { + /* The timer has already been started. */ + spin_unlock_irqrestore(&apcm->timer_lock, flags); + return 0; + } + + apcm->timer.expires = jiffies + (runtime->period_size * HZ + + (runtime->rate - 1)) / runtime->rate; + apcm->stop_timer = 0; + add_timer(&apcm->timer); + spin_unlock_irqrestore(&apcm->timer_lock, flags); + + return 0; +} + +static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) +{ + unsigned long flags; + + spin_lock_irqsave(&apcm->timer_lock, flags); + apcm->stop_timer = 1; + del_timer(&apcm->timer); + spin_unlock_irqrestore(&apcm->timer_lock, flags); + + try_to_del_timer_sync(&apcm->timer); + + return 0; +} + +static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + int err; + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = runtime->private_data; + + if (IEC958 == substream->pcm->device) + err = atc->spdif_passthru_playback_prepare(atc, apcm); + else + err = atc->pcm_playback_prepare(atc, apcm); + + if (err < 0) { + printk(KERN_ERR "Preparing pcm playback failed!!!\n"); + return err; + } + + ct_pcm_timer_prepare(apcm); + + return 0; +} + +static int +ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + atc->pcm_playback_start(atc, apcm); + ct_pcm_timer_start(apcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ct_pcm_timer_stop(apcm); + atc->pcm_playback_stop(atc, apcm); + break; + default: + break; + } + + return 0; +} + +static snd_pcm_uframes_t +ct_pcm_playback_pointer(struct snd_pcm_substream *substream) +{ + unsigned long position; + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = runtime->private_data; + + /* Read out playback position */ + position = atc->pcm_playback_position(atc, apcm); + position = bytes_to_frames(runtime, position); + return position; +} + +/* pcm capture operations */ +static int ct_pcm_capture_open(struct snd_pcm_substream *substream) +{ + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm; + int err; + + apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); + if (NULL == apcm) + return -ENOMEM; + + spin_lock_init(&apcm->timer_lock); + apcm->started = 0; + apcm->stop_timer = 0; + apcm->substream = substream; + apcm->interrupt = ct_atc_pcm_interrupt; + runtime->private_data = apcm; + runtime->private_free = ct_atc_pcm_free_substream; + runtime->hw = ct_pcm_capture_hw; + runtime->hw.rate_max = atc->rsr * atc->msr; + + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) { + kfree(apcm); + return err; + } + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + 1024, UINT_MAX); + if (err < 0) { + kfree(apcm); + return err; + } + + return 0; +} + +static int ct_pcm_capture_close(struct snd_pcm_substream *substream) +{ + /* The ct_atc_pcm object will be freed by runtime->private_free */ + /* TODO: Notify mixer inactive. */ + return 0; +} + +static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + int err; + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = runtime->private_data; + + err = atc->pcm_capture_prepare(atc, apcm); + if (err < 0) { + printk(KERN_ERR "Preparing pcm capture failed!!!\n"); + return err; + } + + ct_pcm_timer_prepare(apcm); + + return 0; +} + +static int +ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + atc->pcm_capture_start(atc, apcm); + ct_pcm_timer_start(apcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + ct_pcm_timer_stop(apcm); + atc->pcm_capture_stop(atc, apcm); + break; + default: + ct_pcm_timer_stop(apcm); + atc->pcm_capture_stop(atc, apcm); + break; + } + + return 0; +} + +static snd_pcm_uframes_t +ct_pcm_capture_pointer(struct snd_pcm_substream *substream) +{ + unsigned long position; + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = runtime->private_data; + + /* Read out playback position */ + position = atc->pcm_capture_position(atc, apcm); + position = bytes_to_frames(runtime, position); + return position; +} + +/* PCM operators for playback */ +static struct snd_pcm_ops ct_pcm_playback_ops = { + .open = ct_pcm_playback_open, + .close = ct_pcm_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = ct_pcm_hw_params, + .hw_free = ct_pcm_hw_free, + .prepare = ct_pcm_playback_prepare, + .trigger = ct_pcm_playback_trigger, + .pointer = ct_pcm_playback_pointer, +}; + +/* PCM operators for capture */ +static struct snd_pcm_ops ct_pcm_capture_ops = { + .open = ct_pcm_capture_open, + .close = ct_pcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = ct_pcm_hw_params, + .hw_free = ct_pcm_hw_free, + .prepare = ct_pcm_capture_prepare, + .trigger = ct_pcm_capture_trigger, + .pointer = ct_pcm_capture_pointer, +}; + +/* Create ALSA pcm device */ +int ct_alsa_pcm_create(struct ct_atc *atc, + enum CTALSADEVS device, + const char *device_name) +{ + struct snd_pcm *pcm; + int err; + int playback_count, capture_count; + char name[128]; + + strncpy(name, device_name, sizeof(name)); + playback_count = (IEC958 == device) ? 1 : 8; + capture_count = (FRONT == device) ? 1 : 0; + err = snd_pcm_new(atc->card, name, device, + playback_count, capture_count, &pcm); + if (err < 0) { + printk(KERN_ERR "snd_pcm_new failed!! Err=%d\n", err); + return err; + } + + pcm->private_data = atc; + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, device_name); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); + + if (FRONT == device) + snd_pcm_set_ops(pcm, + SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops); + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(atc->pci), 128*1024, 128*1024); + + return 0; +} diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h new file mode 100644 index 000000000000..178da0dca647 --- /dev/null +++ b/sound/pci/ctxfi/ctpcm.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctpcm.h + * + * @Brief + * This file contains the definition of the pcm device functions. + * + * @Author Liu Chun + * @Date Mar 28 2008 + * + */ + +#ifndef CTPCM_H +#define CTPCM_H + +#include "ctatc.h" + +int ct_alsa_pcm_create(struct ct_atc *atc, + enum CTALSADEVS device, + const char *device_name); + +#endif /* CTPCM_H */ diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c new file mode 100644 index 000000000000..414fc23267cd --- /dev/null +++ b/sound/pci/ctxfi/ctresource.c @@ -0,0 +1,297 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctresource.c + * + * @Brief + * This file contains the implementation of some generic helper functions. + * + * @Author Liu Chun + * @Date May 15 2008 + * + */ + +#include "ctresource.h" +#include "cthardware.h" +#include +#include + +#define AUDIO_SLOT_BLOCK_NUM 256 + +/* Resource allocation based on bit-map management mechanism */ +static int +get_resource(u8 *rscs, unsigned int amount, + unsigned int multi, unsigned int *ridx) +{ + int i = 0, j = 0, k = 0, n = 0; + + /* Check whether there are sufficient resources to meet request. */ + for (i = 0, n = multi; i < amount; i++) { + j = i / 8; + k = i % 8; + if (rscs[j] & ((u8)1 << k)) { + n = multi; + continue; + } + if (!(--n)) + break; /* found sufficient contiguous resources */ + } + + if (i >= amount) { + /* Can not find sufficient contiguous resources */ + return -ENOENT; + } + + /* Mark the contiguous bits in resource bit-map as used */ + for (n = multi; n > 0; n--) { + j = i / 8; + k = i % 8; + rscs[j] |= ((u8)1 << k); + i--; + } + + *ridx = i + 1; + + return 0; +} + +static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) +{ + unsigned int i = 0, j = 0, k = 0, n = 0; + + /* Mark the contiguous bits in resource bit-map as used */ + for (n = multi, i = idx; n > 0; n--) { + j = i / 8; + k = i % 8; + rscs[j] &= ~((u8)1 << k); + i++; + } + + return 0; +} + +int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx) +{ + int err = 0; + + if (n > mgr->avail) + return -ENOENT; + + err = get_resource(mgr->rscs, mgr->amount, n, ridx); + if (!err) + mgr->avail -= n; + + return err; +} + +int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx) +{ + put_resource(mgr->rscs, n, idx); + mgr->avail += n; + + return 0; +} + +static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = { + /* SRC channel is at Audio Ring slot 1 every 16 slots. */ + [SRC] = 0x1, + [AMIXER] = 0x4, + [SUM] = 0xc, +}; + +static int rsc_index(const struct rsc *rsc) +{ + return rsc->conj; +} + +static int audio_ring_slot(const struct rsc *rsc) +{ + return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type]; +} + +static int rsc_next_conj(struct rsc *rsc) +{ + unsigned int i; + for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); ) + i++; + rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i); + return rsc->conj; +} + +static int rsc_master(struct rsc *rsc) +{ + return rsc->conj = rsc->idx; +} + +static struct rsc_ops rsc_generic_ops = { + .index = rsc_index, + .output_slot = audio_ring_slot, + .master = rsc_master, + .next_conj = rsc_next_conj, +}; + +int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) +{ + int err = 0; + + rsc->idx = idx; + rsc->conj = idx; + rsc->type = type; + rsc->msr = msr; + rsc->hw = hw; + rsc->ops = &rsc_generic_ops; + if (NULL == hw) { + rsc->ctrl_blk = NULL; + return 0; + } + + switch (type) { + case SRC: + err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk); + break; + case AMIXER: + err = ((struct hw *)hw)-> + amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk); + break; + case SRCIMP: + case SUM: + case DAIO: + break; + default: + printk(KERN_ERR "Invalid resource type value %d!\n", type); + return -EINVAL; + } + + if (err) { + printk(KERN_ERR "Failed to get resource control block!\n"); + return err; + } + + return 0; +} + +int rsc_uninit(struct rsc *rsc) +{ + if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) { + switch (rsc->type) { + case SRC: + ((struct hw *)rsc->hw)-> + src_rsc_put_ctrl_blk(rsc->ctrl_blk); + break; + case AMIXER: + ((struct hw *)rsc->hw)-> + amixer_rsc_put_ctrl_blk(rsc->ctrl_blk); + break; + case SUM: + case DAIO: + break; + default: + printk(KERN_ERR "Invalid resource type value %d!\n", + rsc->type); + break; + } + + rsc->hw = rsc->ctrl_blk = NULL; + } + + rsc->idx = rsc->conj = 0; + rsc->type = NUM_RSCTYP; + rsc->msr = 0; + + return 0; +} + +int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, + unsigned int amount, void *hw_obj) +{ + int err = 0; + struct hw *hw = hw_obj; + + mgr->type = NUM_RSCTYP; + + mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL); + if (NULL == mgr->rscs) + return -ENOMEM; + + switch (type) { + case SRC: + err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk); + break; + case SRCIMP: + err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk); + break; + case AMIXER: + err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk); + break; + case DAIO: + err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk); + break; + case SUM: + break; + default: + printk(KERN_ERR "Invalid resource type value %d!\n", type); + err = -EINVAL; + goto error; + } + + if (err) { + printk(KERN_ERR "Failed to get manager control block!\n"); + goto error; + } + + mgr->type = type; + mgr->avail = mgr->amount = amount; + mgr->hw = hw; + + return 0; + +error: + kfree(mgr->rscs); + return err; +} + +int rsc_mgr_uninit(struct rsc_mgr *mgr) +{ + if (NULL != mgr->rscs) { + kfree(mgr->rscs); + mgr->rscs = NULL; + } + + if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) { + switch (mgr->type) { + case SRC: + ((struct hw *)mgr->hw)-> + src_mgr_put_ctrl_blk(mgr->ctrl_blk); + break; + case SRCIMP: + ((struct hw *)mgr->hw)-> + srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk); + break; + case AMIXER: + ((struct hw *)mgr->hw)-> + amixer_mgr_put_ctrl_blk(mgr->ctrl_blk); + break; + case DAIO: + ((struct hw *)mgr->hw)-> + daio_mgr_put_ctrl_blk(mgr->ctrl_blk); + break; + case SUM: + break; + default: + printk(KERN_ERR "Invalid resource type value %d!\n", + mgr->type); + break; + } + + mgr->hw = mgr->ctrl_blk = NULL; + } + + mgr->type = NUM_RSCTYP; + mgr->avail = mgr->amount = 0; + + return 0; +} diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h new file mode 100644 index 000000000000..0838c2e84f8b --- /dev/null +++ b/sound/pci/ctxfi/ctresource.h @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctresource.h + * + * @Brief + * This file contains the definition of generic hardware resources for + * resource management. + * + * @Author Liu Chun + * @Date May 13 2008 + * + */ + +#ifndef CTRESOURCE_H +#define CTRESOURCE_H + +#include + +enum RSCTYP { + SRC, + SRCIMP, + AMIXER, + SUM, + DAIO, + NUM_RSCTYP /* This must be the last one and less than 16 */ +}; + +struct rsc_ops; + +struct rsc { + u32 idx:12; /* The index of a resource */ + u32 type:4; /* The type (RSCTYP) of a resource */ + u32 conj:12; /* Current conjugate index */ + u32 msr:4; /* The Master Sample Rate a resource working on */ + void *ctrl_blk; /* Chip specific control info block for a resource */ + void *hw; /* Chip specific object for hardware access means */ + struct rsc_ops *ops; /* Generic resource operations */ +}; + +struct rsc_ops { + int (*master)(struct rsc *rsc); /* Move to master resource */ + int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */ + int (*index)(const struct rsc *rsc); /* Return the index of resource */ + /* Return the output slot number */ + int (*output_slot)(const struct rsc *rsc); +}; + +int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw); +int rsc_uninit(struct rsc *rsc); + +struct rsc_mgr { + enum RSCTYP type; /* The type (RSCTYP) of resource to manage */ + unsigned int amount; /* The total amount of a kind of resource */ + unsigned int avail; /* The amount of currently available resources */ + unsigned char *rscs; /* The bit-map for resource allocation */ + void *ctrl_blk; /* Chip specific control info block */ + void *hw; /* Chip specific object for hardware access */ +}; + +/* Resource management is based on bit-map mechanism */ +int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, + unsigned int amount, void *hw); +int rsc_mgr_uninit(struct rsc_mgr *mgr); +int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx); +int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx); + +#endif /* CTRESOURCE_H */ diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c new file mode 100644 index 000000000000..d3e0ad5ed9fb --- /dev/null +++ b/sound/pci/ctxfi/ctsrc.c @@ -0,0 +1,886 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctsrc.c + * + * @Brief + * This file contains the implementation of the Sample Rate Convertor + * resource management object. + * + * @Author Liu Chun + * @Date May 13 2008 + * + */ + +#include "ctsrc.h" +#include "cthardware.h" +#include + +#define SRC_RESOURCE_NUM 64 +#define SRCIMP_RESOURCE_NUM 256 + +static unsigned int conj_mask; + +static int src_default_config_memrd(struct src *src); +static int src_default_config_memwr(struct src *src); +static int src_default_config_arcrw(struct src *src); + +static int (*src_default_config[3])(struct src *) = { + [MEMRD] = src_default_config_memrd, + [MEMWR] = src_default_config_memwr, + [ARCRW] = src_default_config_arcrw +}; + +static int src_set_state(struct src *src, unsigned int state) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_state(src->rsc.ctrl_blk, state); + + return 0; +} + +static int src_set_bm(struct src *src, unsigned int bm) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_bm(src->rsc.ctrl_blk, bm); + + return 0; +} + +static int src_set_sf(struct src *src, unsigned int sf) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_sf(src->rsc.ctrl_blk, sf); + + return 0; +} + +static int src_set_pm(struct src *src, unsigned int pm) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_pm(src->rsc.ctrl_blk, pm); + + return 0; +} + +static int src_set_rom(struct src *src, unsigned int rom) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_rom(src->rsc.ctrl_blk, rom); + + return 0; +} + +static int src_set_vo(struct src *src, unsigned int vo) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_vo(src->rsc.ctrl_blk, vo); + + return 0; +} + +static int src_set_st(struct src *src, unsigned int st) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_st(src->rsc.ctrl_blk, st); + + return 0; +} + +static int src_set_bp(struct src *src, unsigned int bp) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_bp(src->rsc.ctrl_blk, bp); + + return 0; +} + +static int src_set_cisz(struct src *src, unsigned int cisz) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_cisz(src->rsc.ctrl_blk, cisz); + + return 0; +} + +static int src_set_ca(struct src *src, unsigned int ca) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_ca(src->rsc.ctrl_blk, ca); + + return 0; +} + +static int src_set_sa(struct src *src, unsigned int sa) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_sa(src->rsc.ctrl_blk, sa); + + return 0; +} + +static int src_set_la(struct src *src, unsigned int la) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_la(src->rsc.ctrl_blk, la); + + return 0; +} + +static int src_set_pitch(struct src *src, unsigned int pitch) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_pitch(src->rsc.ctrl_blk, pitch); + + return 0; +} + +static int src_set_clear_zbufs(struct src *src) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); + + return 0; +} + +static int src_commit_write(struct src *src) +{ + struct hw *hw = NULL; + int i = 0; + unsigned int dirty = 0; + + hw = (struct hw *)src->rsc.hw; + src->rsc.ops->master(&src->rsc); + if (src->rsc.msr > 1) { + /* Save dirty flags for conjugate resource programming */ + dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask; + } + hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); + + /* Program conjugate parameter mixer resources */ + if (MEMWR == src->mode) + return 0; + + for (i = 1; i < src->rsc.msr; i++) { + src->rsc.ops->next_conj(&src->rsc); + hw->src_set_dirty(src->rsc.ctrl_blk, dirty); + hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); + } + src->rsc.ops->master(&src->rsc); + + return 0; +} + +static int src_get_ca(struct src *src) +{ + struct hw *hw = NULL; + + hw = (struct hw *)src->rsc.hw; + return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); +} + +static int src_init(struct src *src) +{ + src_default_config[src->mode](src); + + return 0; +} + +static struct src *src_next_interleave(struct src *src) +{ + return src->intlv; +} + +static int src_default_config_memrd(struct src *src) +{ + struct hw *hw = src->rsc.hw; + unsigned int rsr = 0, msr = 0; + + hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); + hw->src_set_bm(src->rsc.ctrl_blk, 1); + for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1) + rsr++; + + hw->src_set_rsr(src->rsc.ctrl_blk, rsr); + hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16); + hw->src_set_wr(src->rsc.ctrl_blk, 0); + hw->src_set_pm(src->rsc.ctrl_blk, 0); + hw->src_set_rom(src->rsc.ctrl_blk, 0); + hw->src_set_vo(src->rsc.ctrl_blk, 0); + hw->src_set_st(src->rsc.ctrl_blk, 0); + hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1); + hw->src_set_cisz(src->rsc.ctrl_blk, 0x80); + hw->src_set_sa(src->rsc.ctrl_blk, 0x0); + hw->src_set_la(src->rsc.ctrl_blk, 0x1000); + hw->src_set_ca(src->rsc.ctrl_blk, 0x80); + hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); + hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); + + src->rsc.ops->master(&src->rsc); + hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); + + for (msr = 1; msr < src->rsc.msr; msr++) { + src->rsc.ops->next_conj(&src->rsc); + hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); + hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); + } + src->rsc.ops->master(&src->rsc); + + return 0; +} + +static int src_default_config_memwr(struct src *src) +{ + struct hw *hw = src->rsc.hw; + + hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); + hw->src_set_bm(src->rsc.ctrl_blk, 1); + hw->src_set_rsr(src->rsc.ctrl_blk, 0); + hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16); + hw->src_set_wr(src->rsc.ctrl_blk, 1); + hw->src_set_pm(src->rsc.ctrl_blk, 0); + hw->src_set_rom(src->rsc.ctrl_blk, 0); + hw->src_set_vo(src->rsc.ctrl_blk, 0); + hw->src_set_st(src->rsc.ctrl_blk, 0); + hw->src_set_ilsz(src->rsc.ctrl_blk, 0); + hw->src_set_cisz(src->rsc.ctrl_blk, 0x80); + hw->src_set_sa(src->rsc.ctrl_blk, 0x0); + hw->src_set_la(src->rsc.ctrl_blk, 0x1000); + hw->src_set_ca(src->rsc.ctrl_blk, 0x80); + hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); + hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); + + src->rsc.ops->master(&src->rsc); + hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); + + return 0; +} + +static int src_default_config_arcrw(struct src *src) +{ + struct hw *hw = src->rsc.hw; + unsigned int rsr = 0, msr = 0; + unsigned int dirty; + + hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); + hw->src_set_bm(src->rsc.ctrl_blk, 0); + for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1) + rsr++; + + hw->src_set_rsr(src->rsc.ctrl_blk, rsr); + hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32); + hw->src_set_wr(src->rsc.ctrl_blk, 0); + hw->src_set_pm(src->rsc.ctrl_blk, 0); + hw->src_set_rom(src->rsc.ctrl_blk, 0); + hw->src_set_vo(src->rsc.ctrl_blk, 0); + hw->src_set_st(src->rsc.ctrl_blk, 0); + hw->src_set_ilsz(src->rsc.ctrl_blk, 0); + hw->src_set_cisz(src->rsc.ctrl_blk, 0x80); + hw->src_set_sa(src->rsc.ctrl_blk, 0x0); + /*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/ + hw->src_set_la(src->rsc.ctrl_blk, 0x1000); + /*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/ + hw->src_set_ca(src->rsc.ctrl_blk, 0x80); + hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000); + hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); + + dirty = hw->src_get_dirty(src->rsc.ctrl_blk); + src->rsc.ops->master(&src->rsc); + for (msr = 0; msr < src->rsc.msr; msr++) { + hw->src_set_dirty(src->rsc.ctrl_blk, dirty); + hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc), + src->rsc.ctrl_blk); + src->rsc.ops->next_conj(&src->rsc); + } + src->rsc.ops->master(&src->rsc); + + return 0; +} + +static struct src_rsc_ops src_rsc_ops = { + .set_state = src_set_state, + .set_bm = src_set_bm, + .set_sf = src_set_sf, + .set_pm = src_set_pm, + .set_rom = src_set_rom, + .set_vo = src_set_vo, + .set_st = src_set_st, + .set_bp = src_set_bp, + .set_cisz = src_set_cisz, + .set_ca = src_set_ca, + .set_sa = src_set_sa, + .set_la = src_set_la, + .set_pitch = src_set_pitch, + .set_clr_zbufs = src_set_clear_zbufs, + .commit_write = src_commit_write, + .get_ca = src_get_ca, + .init = src_init, + .next_interleave = src_next_interleave, +}; + +static int +src_rsc_init(struct src *src, u32 idx, + const struct src_desc *desc, struct src_mgr *mgr) +{ + int err = 0; + int i = 0, n = 0; + struct src *p; + + n = (MEMRD == desc->mode) ? desc->multi : 1; + for (i = 0, p = src; i < n; i++, p++) { + err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw); + if (err) + goto error1; + + /* Initialize src specific rsc operations */ + p->ops = &src_rsc_ops; + p->multi = (0 == i) ? desc->multi : 1; + p->mode = desc->mode; + src_default_config[desc->mode](p); + mgr->src_enable(mgr, p); + p->intlv = p + 1; + } + (--p)->intlv = NULL; /* Set @intlv of the last SRC to NULL */ + + mgr->commit_write(mgr); + + return 0; + +error1: + for (i--, p--; i >= 0; i--, p--) { + mgr->src_disable(mgr, p); + rsc_uninit(&p->rsc); + } + mgr->commit_write(mgr); + return err; +} + +static int src_rsc_uninit(struct src *src, struct src_mgr *mgr) +{ + int i = 0, n = 0; + struct src *p; + + n = (MEMRD == src->mode) ? src->multi : 1; + for (i = 0, p = src; i < n; i++, p++) { + mgr->src_disable(mgr, p); + rsc_uninit(&p->rsc); + p->multi = 0; + p->ops = NULL; + p->mode = NUM_SRCMODES; + p->intlv = NULL; + } + mgr->commit_write(mgr); + + return 0; +} + +static int +get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) +{ + unsigned int idx = SRC_RESOURCE_NUM; + int err = 0; + struct src *src = NULL; + unsigned long flags; + + *rsrc = NULL; + + /* Check whether there are sufficient src resources to meet request. */ + spin_lock_irqsave(&mgr->mgr_lock, flags); + if (MEMRD == desc->mode) + err = mgr_get_resource(&mgr->mgr, desc->multi, &idx); + else + err = mgr_get_resource(&mgr->mgr, 1, &idx); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + if (err) { + printk(KERN_ERR "Can't meet SRC resource request!\n"); + return err; + } + + /* Allocate mem for master src resource */ + if (MEMRD == desc->mode) + src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL); + else + src = kzalloc(sizeof(*src), GFP_KERNEL); + + if (NULL == src) { + err = -ENOMEM; + goto error1; + } + + err = src_rsc_init(src, idx, desc, mgr); + if (err) + goto error2; + + *rsrc = src; + + return 0; + +error2: + kfree(src); +error1: + spin_lock_irqsave(&mgr->mgr_lock, flags); + if (MEMRD == desc->mode) + mgr_put_resource(&mgr->mgr, desc->multi, idx); + else + mgr_put_resource(&mgr->mgr, 1, idx); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + return err; +} + +static int put_src_rsc(struct src_mgr *mgr, struct src *src) +{ + unsigned long flags; + + spin_lock_irqsave(&mgr->mgr_lock, flags); + src->rsc.ops->master(&src->rsc); + if (MEMRD == src->mode) + mgr_put_resource(&mgr->mgr, src->multi, + src->rsc.ops->index(&src->rsc)); + else + mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc)); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + src_rsc_uninit(src, mgr); + kfree(src); + + return 0; +} + +static int src_enable_s(struct src_mgr *mgr, struct src *src) +{ + struct hw *hw = mgr->mgr.hw; + int i = 0; + + src->rsc.ops->master(&src->rsc); + for (i = 0; i < src->rsc.msr; i++) { + hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk, + src->rsc.ops->index(&src->rsc)); + src->rsc.ops->next_conj(&src->rsc); + } + src->rsc.ops->master(&src->rsc); + + return 0; +} + +static int src_enable(struct src_mgr *mgr, struct src *src) +{ + struct hw *hw = mgr->mgr.hw; + int i = 0; + + src->rsc.ops->master(&src->rsc); + for (i = 0; i < src->rsc.msr; i++) { + hw->src_mgr_enb_src(mgr->mgr.ctrl_blk, + src->rsc.ops->index(&src->rsc)); + src->rsc.ops->next_conj(&src->rsc); + } + src->rsc.ops->master(&src->rsc); + + return 0; +} + +static int src_disable(struct src_mgr *mgr, struct src *src) +{ + struct hw *hw = mgr->mgr.hw; + int i = 0; + + src->rsc.ops->master(&src->rsc); + for (i = 0; i < src->rsc.msr; i++) { + hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk, + src->rsc.ops->index(&src->rsc)); + src->rsc.ops->next_conj(&src->rsc); + } + src->rsc.ops->master(&src->rsc); + + return 0; +} + +static int src_mgr_commit_write(struct src_mgr *mgr) +{ + struct hw *hw = mgr->mgr.hw; + + hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk); + + return 0; +} + +int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) +{ + int err = 0, i = 0; + struct src_mgr *src_mgr; + + *rsrc_mgr = NULL; + src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL); + if (NULL == src_mgr) + return -ENOMEM; + + err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw); + if (err) + goto error1; + + spin_lock_init(&src_mgr->mgr_lock); + conj_mask = ((struct hw *)hw)->src_dirty_conj_mask(); + + src_mgr->get_src = get_src_rsc; + src_mgr->put_src = put_src_rsc; + src_mgr->src_enable_s = src_enable_s; + src_mgr->src_enable = src_enable; + src_mgr->src_disable = src_disable; + src_mgr->commit_write = src_mgr_commit_write; + + /* Disable all SRC resources. */ + for (i = 0; i < 256; i++) + ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i); + + ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk); + + *rsrc_mgr = src_mgr; + + return 0; + +error1: + kfree(src_mgr); + return err; +} + +int src_mgr_destroy(struct src_mgr *src_mgr) +{ + rsc_mgr_uninit(&src_mgr->mgr); + kfree(src_mgr); + + return 0; +} + +/* SRCIMP resource manager operations */ + +static int srcimp_master(struct rsc *rsc) +{ + rsc->conj = 0; + return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0]; +} + +static int srcimp_next_conj(struct rsc *rsc) +{ + rsc->conj++; + return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj]; +} + +static int srcimp_index(const struct rsc *rsc) +{ + return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj]; +} + +static struct rsc_ops srcimp_basic_rsc_ops = { + .master = srcimp_master, + .next_conj = srcimp_next_conj, + .index = srcimp_index, + .output_slot = NULL, +}; + +static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input) +{ + struct imapper *entry = NULL; + int i = 0; + + srcimp->rsc.ops->master(&srcimp->rsc); + src->rsc.ops->master(&src->rsc); + input->ops->master(input); + + /* Program master and conjugate resources */ + for (i = 0; i < srcimp->rsc.msr; i++) { + entry = &srcimp->imappers[i]; + entry->slot = input->ops->output_slot(input); + entry->user = src->rsc.ops->index(&src->rsc); + entry->addr = srcimp->rsc.ops->index(&srcimp->rsc); + srcimp->mgr->imap_add(srcimp->mgr, entry); + srcimp->mapped |= (0x1 << i); + + srcimp->rsc.ops->next_conj(&srcimp->rsc); + input->ops->next_conj(input); + } + + srcimp->rsc.ops->master(&srcimp->rsc); + input->ops->master(input); + + return 0; +} + +static int srcimp_unmap(struct srcimp *srcimp) +{ + int i = 0; + + /* Program master and conjugate resources */ + for (i = 0; i < srcimp->rsc.msr; i++) { + if (srcimp->mapped & (0x1 << i)) { + srcimp->mgr->imap_delete(srcimp->mgr, + &srcimp->imappers[i]); + srcimp->mapped &= ~(0x1 << i); + } + } + + return 0; +} + +static struct srcimp_rsc_ops srcimp_ops = { + .map = srcimp_map, + .unmap = srcimp_unmap +}; + +static int srcimp_rsc_init(struct srcimp *srcimp, + const struct srcimp_desc *desc, + struct srcimp_mgr *mgr) +{ + int err = 0; + + err = rsc_init(&srcimp->rsc, srcimp->idx[0], + SRCIMP, desc->msr, mgr->mgr.hw); + if (err) + return err; + + /* Reserve memory for imapper nodes */ + srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr, + GFP_KERNEL); + if (NULL == srcimp->imappers) { + err = -ENOMEM; + goto error1; + } + + /* Set srcimp specific operations */ + srcimp->rsc.ops = &srcimp_basic_rsc_ops; + srcimp->ops = &srcimp_ops; + srcimp->mgr = mgr; + + srcimp->rsc.ops->master(&srcimp->rsc); + + return 0; + +error1: + rsc_uninit(&srcimp->rsc); + return err; +} + +static int srcimp_rsc_uninit(struct srcimp *srcimp) +{ + if (NULL != srcimp->imappers) { + kfree(srcimp->imappers); + srcimp->imappers = NULL; + } + srcimp->ops = NULL; + srcimp->mgr = NULL; + rsc_uninit(&srcimp->rsc); + + return 0; +} + +static int get_srcimp_rsc(struct srcimp_mgr *mgr, + const struct srcimp_desc *desc, + struct srcimp **rsrcimp) +{ + int err = 0, i = 0; + unsigned int idx = 0; + struct srcimp *srcimp = NULL; + unsigned long flags; + + *rsrcimp = NULL; + + /* Allocate mem for SRCIMP resource */ + srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL); + if (NULL == srcimp) { + err = -ENOMEM; + return err; + } + + /* Check whether there are sufficient SRCIMP resources. */ + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i = 0; i < desc->msr; i++) { + err = mgr_get_resource(&mgr->mgr, 1, &idx); + if (err) + break; + + srcimp->idx[i] = idx; + } + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + if (err) { + printk(KERN_ERR "Can't meet SRCIMP resource request!\n"); + goto error1; + } + + err = srcimp_rsc_init(srcimp, desc, mgr); + if (err) + goto error1; + + *rsrcimp = srcimp; + + return 0; + +error1: + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i--; i >= 0; i--) + mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + kfree(srcimp); + return err; +} + +static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp) +{ + unsigned long flags; + int i = 0; + + spin_lock_irqsave(&mgr->mgr_lock, flags); + for (i = 0; i < srcimp->rsc.msr; i++) + mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]); + + spin_unlock_irqrestore(&mgr->mgr_lock, flags); + srcimp_rsc_uninit(srcimp); + kfree(srcimp); + + return 0; +} + +static int srcimp_map_op(void *data, struct imapper *entry) +{ + struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr; + struct hw *hw = mgr->hw; + + hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot); + hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user); + hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next); + hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr); + hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk); + + return 0; +} + +static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&mgr->imap_lock, flags); + if ((0 == entry->addr) && (mgr->init_imap_added)) { + input_mapper_delete(&mgr->imappers, + mgr->init_imap, srcimp_map_op, mgr); + mgr->init_imap_added = 0; + } + err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr); + spin_unlock_irqrestore(&mgr->imap_lock, flags); + + return err; +} + +static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&mgr->imap_lock, flags); + err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr); + if (list_empty(&mgr->imappers)) { + input_mapper_add(&mgr->imappers, mgr->init_imap, + srcimp_map_op, mgr); + mgr->init_imap_added = 1; + } + spin_unlock_irqrestore(&mgr->imap_lock, flags); + + return err; +} + +int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) +{ + int err = 0; + struct srcimp_mgr *srcimp_mgr; + struct imapper *entry; + + *rsrcimp_mgr = NULL; + srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL); + if (NULL == srcimp_mgr) + return -ENOMEM; + + err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw); + if (err) + goto error1; + + spin_lock_init(&srcimp_mgr->mgr_lock); + spin_lock_init(&srcimp_mgr->imap_lock); + INIT_LIST_HEAD(&srcimp_mgr->imappers); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (NULL == entry) { + err = -ENOMEM; + goto error2; + } + entry->slot = entry->addr = entry->next = entry->user = 0; + list_add(&entry->list, &srcimp_mgr->imappers); + srcimp_mgr->init_imap = entry; + srcimp_mgr->init_imap_added = 1; + + srcimp_mgr->get_srcimp = get_srcimp_rsc; + srcimp_mgr->put_srcimp = put_srcimp_rsc; + srcimp_mgr->imap_add = srcimp_imap_add; + srcimp_mgr->imap_delete = srcimp_imap_delete; + + *rsrcimp_mgr = srcimp_mgr; + + return 0; + +error2: + rsc_mgr_uninit(&srcimp_mgr->mgr); +error1: + kfree(srcimp_mgr); + return err; +} + +int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr) +{ + unsigned long flags; + + /* free src input mapper list */ + spin_lock_irqsave(&srcimp_mgr->imap_lock, flags); + free_input_mapper_list(&srcimp_mgr->imappers); + spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags); + + rsc_mgr_uninit(&srcimp_mgr->mgr); + kfree(srcimp_mgr); + + return 0; +} diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h new file mode 100644 index 000000000000..259366aabcac --- /dev/null +++ b/sound/pci/ctxfi/ctsrc.h @@ -0,0 +1,149 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctsrc.h + * + * @Brief + * This file contains the definition of the Sample Rate Convertor + * resource management object. + * + * @Author Liu Chun + * @Date May 13 2008 + * + */ + +#ifndef CTSRC_H +#define CTSRC_H + +#include "ctresource.h" +#include "ctimap.h" +#include +#include + +#define SRC_STATE_OFF 0x0 +#define SRC_STATE_INIT 0x4 +#define SRC_STATE_RUN 0x5 + +#define SRC_SF_U8 0x0 +#define SRC_SF_S16 0x1 +#define SRC_SF_S24 0x2 +#define SRC_SF_S32 0x3 +#define SRC_SF_F32 0x4 + +/* Define the descriptor of a src resource */ +enum SRCMODE { + MEMRD, /* Read data from host memory */ + MEMWR, /* Write data to host memory */ + ARCRW, /* Read from and write to audio ring channel */ + NUM_SRCMODES +}; + +struct src_rsc_ops; + +struct src { + struct rsc rsc; /* Basic resource info */ + struct src *intlv; /* Pointer to next interleaved SRC in a series */ + struct src_rsc_ops *ops; /* SRC specific operations */ + /* Number of contiguous srcs for interleaved usage */ + unsigned char multi; + unsigned char mode; /* Working mode of this SRC resource */ +}; + +struct src_rsc_ops { + int (*set_state)(struct src *src, unsigned int state); + int (*set_bm)(struct src *src, unsigned int bm); + int (*set_sf)(struct src *src, unsigned int sf); + int (*set_pm)(struct src *src, unsigned int pm); + int (*set_rom)(struct src *src, unsigned int rom); + int (*set_vo)(struct src *src, unsigned int vo); + int (*set_st)(struct src *src, unsigned int st); + int (*set_bp)(struct src *src, unsigned int bp); + int (*set_cisz)(struct src *src, unsigned int cisz); + int (*set_ca)(struct src *src, unsigned int ca); + int (*set_sa)(struct src *src, unsigned int sa); + int (*set_la)(struct src *src, unsigned int la); + int (*set_pitch)(struct src *src, unsigned int pitch); + int (*set_clr_zbufs)(struct src *src); + int (*commit_write)(struct src *src); + int (*get_ca)(struct src *src); + int (*init)(struct src *src); + struct src* (*next_interleave)(struct src *src); +}; + +/* Define src resource request description info */ +struct src_desc { + /* Number of contiguous master srcs for interleaved usage */ + unsigned char multi; + unsigned char msr; + unsigned char mode; /* Working mode of the requested srcs */ +}; + +/* Define src manager object */ +struct src_mgr { + struct rsc_mgr mgr; /* Basic resource manager info */ + spinlock_t mgr_lock; + + /* request src resource */ + int (*get_src)(struct src_mgr *mgr, + const struct src_desc *desc, struct src **rsrc); + /* return src resource */ + int (*put_src)(struct src_mgr *mgr, struct src *src); + int (*src_enable_s)(struct src_mgr *mgr, struct src *src); + int (*src_enable)(struct src_mgr *mgr, struct src *src); + int (*src_disable)(struct src_mgr *mgr, struct src *src); + int (*commit_write)(struct src_mgr *mgr); +}; + +/* Define the descriptor of a SRC Input Mapper resource */ +struct srcimp_mgr; +struct srcimp_rsc_ops; + +struct srcimp { + struct rsc rsc; + unsigned char idx[8]; + struct imapper *imappers; + unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */ + struct srcimp_mgr *mgr; + struct srcimp_rsc_ops *ops; +}; + +struct srcimp_rsc_ops { + int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input); + int (*unmap)(struct srcimp *srcimp); +}; + +/* Define SRCIMP resource request description info */ +struct srcimp_desc { + unsigned int msr; +}; + +struct srcimp_mgr { + struct rsc_mgr mgr; /* Basic resource manager info */ + spinlock_t mgr_lock; + spinlock_t imap_lock; + struct list_head imappers; + struct imapper *init_imap; + unsigned int init_imap_added; + + /* request srcimp resource */ + int (*get_srcimp)(struct srcimp_mgr *mgr, + const struct srcimp_desc *desc, + struct srcimp **rsrcimp); + /* return srcimp resource */ + int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp); + int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry); + int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry); +}; + +/* Constructor and destructor of SRC resource manager */ +int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr); +int src_mgr_destroy(struct src_mgr *src_mgr); +/* Constructor and destructor of SRCIMP resource manager */ +int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr); +int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr); + +#endif /* CTSRC_H */ diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c new file mode 100644 index 000000000000..46ca04ce9210 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.c @@ -0,0 +1,254 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctvmem.c + * + * @Brief + * This file contains the implementation of virtual memory management object + * for card device. + * + * @Author Liu Chun + * @Date Apr 1 2008 + */ + +#include "ctvmem.h" +#include +#include +#include /* for PAGE_SIZE macro definition */ +#include +#include + +#define CT_PTES_PER_PAGE (PAGE_SIZE / sizeof(void *)) +#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * PAGE_SIZE) + +/* * + * Find or create vm block based on requested @size. + * @size must be page aligned. + * */ +static struct ct_vm_block * +get_vm_block(struct ct_vm *vm, unsigned int size) +{ + struct ct_vm_block *block = NULL, *entry = NULL; + struct list_head *pos = NULL; + + list_for_each(pos, &vm->unused) { + entry = list_entry(pos, struct ct_vm_block, list); + if (entry->size >= size) + break; /* found a block that is big enough */ + } + if (pos == &vm->unused) + return NULL; + + if (entry->size == size) { + /* Move the vm node from unused list to used list directly */ + list_del(&entry->list); + list_add(&entry->list, &vm->used); + vm->size -= size; + return entry; + } + + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (NULL == block) + return NULL; + + block->addr = entry->addr; + block->size = size; + list_add(&block->list, &vm->used); + entry->addr += size; + entry->size -= size; + vm->size -= size; + + return block; +} + +static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) +{ + struct ct_vm_block *entry = NULL, *pre_ent = NULL; + struct list_head *pos = NULL, *pre = NULL; + + list_del(&block->list); + vm->size += block->size; + + list_for_each(pos, &vm->unused) { + entry = list_entry(pos, struct ct_vm_block, list); + if (entry->addr >= (block->addr + block->size)) + break; /* found a position */ + } + if (pos == &vm->unused) { + list_add_tail(&block->list, &vm->unused); + entry = block; + } else { + if ((block->addr + block->size) == entry->addr) { + entry->addr = block->addr; + entry->size += block->size; + kfree(block); + } else { + __list_add(&block->list, pos->prev, pos); + entry = block; + } + } + + pos = &entry->list; + pre = pos->prev; + while (pre != &vm->unused) { + entry = list_entry(pos, struct ct_vm_block, list); + pre_ent = list_entry(pre, struct ct_vm_block, list); + if ((pre_ent->addr + pre_ent->size) > entry->addr) + break; + + pre_ent->size += entry->size; + list_del(pos); + kfree(entry); + pos = pre; + pre = pos->prev; + } +} + +/* Map host addr (kmalloced/vmalloced) to device logical addr. */ +static struct ct_vm_block * +ct_vm_map(struct ct_vm *vm, void *host_addr, int size) +{ + struct ct_vm_block *block = NULL; + unsigned long pte_start; + unsigned long i; + unsigned long pages; + unsigned long start_phys; + unsigned long *ptp; + + /* do mapping */ + if ((unsigned long)host_addr >= VMALLOC_START) { + printk(KERN_ERR "Fail! Not support vmalloced addr now!\n"); + return NULL; + } + + if (size > vm->size) { + printk(KERN_ERR "Fail! No sufficient device virtural " + "memory space available!\n"); + return NULL; + } + + start_phys = (virt_to_phys(host_addr) & PAGE_MASK); + pages = (PAGE_ALIGN(virt_to_phys(host_addr) + size) + - start_phys) >> PAGE_SHIFT; + + ptp = vm->ptp[0]; + + block = get_vm_block(vm, (pages << PAGE_SHIFT)); + if (block == NULL) { + printk(KERN_ERR "No virtual memory block that is big " + "enough to allocate!\n"); + return NULL; + } + + pte_start = (block->addr >> PAGE_SHIFT); + for (i = 0; i < pages; i++) + ptp[pte_start+i] = start_phys + (i << PAGE_SHIFT); + + block->addr += (virt_to_phys(host_addr) & (~PAGE_MASK)); + block->size = size; + + return block; +} + +static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) +{ + /* do unmapping */ + block->size = ((block->addr + block->size + PAGE_SIZE - 1) + & PAGE_MASK) - (block->addr & PAGE_MASK); + block->addr &= PAGE_MASK; + put_vm_block(vm, block); +} + +/* * + * return the host (kmalloced) addr of the @index-th device + * page talbe page on success, or NULL on failure. + * The first returned NULL indicates the termination. + * */ +static void * +ct_get_ptp_virt(struct ct_vm *vm, int index) +{ + void *addr; + + addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index]; + + return addr; +} + +int ct_vm_create(struct ct_vm **rvm) +{ + struct ct_vm *vm; + struct ct_vm_block *block; + int i; + + *rvm = NULL; + + vm = kzalloc(sizeof(*vm), GFP_KERNEL); + if (NULL == vm) + return -ENOMEM; + + /* Allocate page table pages */ + for (i = 0; i < CT_PTP_NUM; i++) { + vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (NULL == vm->ptp[i]) + break; + } + if (!i) { + /* no page table pages are allocated */ + kfree(vm); + return -ENOMEM; + } + vm->size = CT_ADDRS_PER_PAGE * i; + /* Initialise remaining ptps */ + for (; i < CT_PTP_NUM; i++) + vm->ptp[i] = NULL; + + vm->map = ct_vm_map; + vm->unmap = ct_vm_unmap; + vm->get_ptp_virt = ct_get_ptp_virt; + INIT_LIST_HEAD(&vm->unused); + INIT_LIST_HEAD(&vm->used); + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (NULL != block) { + block->addr = 0; + block->size = vm->size; + list_add(&block->list, &vm->unused); + } + + *rvm = vm; + return 0; +} + +/* The caller must ensure no mapping pages are being used + * by hardware before calling this function */ +void ct_vm_destroy(struct ct_vm *vm) +{ + int i; + struct list_head *pos = NULL; + struct ct_vm_block *entry = NULL; + + /* free used and unused list nodes */ + while (!list_empty(&vm->used)) { + pos = vm->used.next; + list_del(pos); + entry = list_entry(pos, struct ct_vm_block, list); + kfree(entry); + } + while (!list_empty(&vm->unused)) { + pos = vm->unused.next; + list_del(pos); + entry = list_entry(pos, struct ct_vm_block, list); + kfree(entry); + } + + /* free allocated page table pages */ + for (i = 0; i < CT_PTP_NUM; i++) + kfree(vm->ptp[i]); + + vm->size = 0; + + kfree(vm); +} diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h new file mode 100644 index 000000000000..4eb5bdd5cad4 --- /dev/null +++ b/sound/pci/ctxfi/ctvmem.h @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + * @File ctvmem.h + * + * @Brief + * This file contains the definition of virtual memory management object + * for card device. + * + * @Author Liu Chun + * @Date Mar 28 2008 + */ + +#ifndef CTVMEM_H +#define CTVMEM_H + +#define CT_PTP_NUM 1 /* num of device page table pages */ + +#include +#include + +struct ct_vm_block { + unsigned int addr; /* starting logical addr of this block */ + unsigned int size; /* size of this device virtual mem block */ + struct list_head list; +}; + +/* Virtual memory management object for card device */ +struct ct_vm { + void *ptp[CT_PTP_NUM]; /* Device page table pages */ + unsigned int size; /* Available addr space in bytes */ + struct list_head unused; /* List of unused blocks */ + struct list_head used; /* List of used blocks */ + + /* Map host addr (kmalloced/vmalloced) to device logical addr. */ + struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, int size); + /* Unmap device logical addr area. */ + void (*unmap)(struct ct_vm *, struct ct_vm_block *block); + void *(*get_ptp_virt)(struct ct_vm *vm, int index); +}; + +int ct_vm_create(struct ct_vm **rvm); +void ct_vm_destroy(struct ct_vm *vm); + +#endif /* CTVMEM_H */ diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c new file mode 100644 index 000000000000..f91144036512 --- /dev/null +++ b/sound/pci/ctxfi/xfi.c @@ -0,0 +1,125 @@ +/* + * xfi linux driver. + * + * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + */ + +#include +#include +#include +#include +#include +#include "ctatc.h" +#include "ctdrv.h" + +MODULE_AUTHOR("Creative Technology Ltd"); +MODULE_DESCRIPTION("X-Fi driver version 1.03"); +MODULE_LICENSE("GPLv2"); +MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); + +static unsigned int reference_rate = 48000; +static unsigned int multiple = 2; +module_param(reference_rate, uint, S_IRUGO); +module_param(multiple, uint, S_IRUGO); + +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 ct_pci_dev_ids[] = { + /* only X-Fi is supported, so... */ + { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K1) }, + { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K2) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); + +static int __devinit +ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct ct_atc *atc; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[dev]) { + dev++; + return -ENOENT; + } + err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + if (err) + return err; + if ((reference_rate != 48000) && (reference_rate != 44100)) { + printk(KERN_ERR "Invalid reference_rate value %u!!!\n" + "The valid values for reference_rate " + "are 48000 and 44100.\nValue 48000 is " + "assumed.\n", reference_rate); + reference_rate = 48000; + } + if ((multiple != 1) && (multiple != 2)) { + printk(KERN_ERR "Invalid multiple value %u!!!\nThe valid " + "values for multiple are 1 and 2.\nValue 2 " + "is assumed.\n", multiple); + multiple = 2; + } + err = ct_atc_create(card, pci, reference_rate, multiple, &atc); + if (err < 0) + goto error; + + card->private_data = atc; + + /* Create alsa devices supported by this card */ + err = atc->create_alsa_devs(atc); + if (err < 0) + goto error; + + strcpy(card->driver, "SB-XFi"); + strcpy(card->shortname, "Creative X-Fi"); + strcpy(card->longname, "Creative ALSA Driver X-Fi"); + + err = snd_card_register(card); + if (err < 0) + goto error; + + pci_set_drvdata(pci, card); + dev++; + + return 0; + +error: + snd_card_free(card); + return err; +} + +static void __devexit ct_card_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver ct_driver = { + .name = "SB-XFi", + .id_table = ct_pci_dev_ids, + .probe = ct_card_probe, + .remove = __devexit_p(ct_card_remove), +}; + +static int __init ct_card_init(void) +{ + return pci_register_driver(&ct_driver); +} + +static void __exit ct_card_exit(void) +{ + pci_unregister_driver(&ct_driver); +} + +module_init(ct_card_init) +module_exit(ct_card_exit) -- cgit v1.2.1 From d0da727e025da8b443a4a614dbb7a031b89857d0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 10:56:04 +0200 Subject: ALSA: ctxfi - Add missing inclusion of linux/delay.h Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 1 + sound/pci/ctxfi/cthw20k2.c | 1 + 2 files changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 53572d92ef5d..44283bd7b2df 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -25,6 +25,7 @@ #include #include #include +#include #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index cdcb75cc33b6..9c2d38b4faa1 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -24,6 +24,7 @@ #include #include #include +#include #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ -- cgit v1.2.1 From 9fc20f030ba457d20eb994e1def7e2ce7d5ae1a8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 15:14:18 +0200 Subject: ALSA: ctxfi - Move PCI ID definitions to linux/pci_ids.h Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 17 +++++++++-------- sound/pci/ctxfi/ctdrv.h | 30 ------------------------------ sound/pci/ctxfi/xfi.c | 6 +++--- 3 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 sound/pci/ctxfi/ctdrv.h (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 5f35374863fb..073fe2a59dae 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -18,7 +18,6 @@ #include "ctatc.h" #include "ctpcm.h" #include "ctmixer.h" -#include "ctdrv.h" #include "cthardware.h" #include "ctsrc.h" #include "ctamixer.h" @@ -40,23 +39,25 @@ | ((IEC958_AES3_CON_FS_48000) << 24)) static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { - [CTSB0760] = {.subsys = PCI_SUBSYS_CREATIVE_SB0760, + [CTSB0760] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB0760, .nm_model = "SB076x"}, - [CTHENDRIX] = {.subsys = PCI_SUBSYS_CREATIVE_HENDRIX, + [CTHENDRIX] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, .nm_model = "Hendrix"}, - [CTSB08801] = {.subsys = PCI_SUBSYS_CREATIVE_SB08801, + [CTSB08801] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08801, .nm_model = "SB0880"}, - [CTSB08802] = {.subsys = PCI_SUBSYS_CREATIVE_SB08802, + [CTSB08802] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08802, .nm_model = "SB0880"}, - [CTSB08803] = {.subsys = PCI_SUBSYS_CREATIVE_SB08803, + [CTSB08803] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08803, .nm_model = "SB0880"} }; static struct ct_atc_chip_details atc_chip_details[] = { - {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K1, + {.vendor = PCI_VENDOR_ID_CREATIVE, + .device = PCI_DEVICE_ID_CREATIVE_20K1, .sub_details = NULL, .nm_card = "X-Fi 20k1"}, - {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K2, + {.vendor = PCI_VENDOR_ID_CREATIVE, + .device = PCI_DEVICE_ID_CREATIVE_20K2, .sub_details = atc_sub_details, .nm_card = "X-Fi 20k2"}, {} /* terminator */ diff --git a/sound/pci/ctxfi/ctdrv.h b/sound/pci/ctxfi/ctdrv.h deleted file mode 100644 index f776a44f52cf..000000000000 --- a/sound/pci/ctxfi/ctdrv.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. - * - * This source file is released under GPL v2 license (no other versions). - * See the COPYING file included in the main directory of this source - * distribution for the license terms and conditions. - * - * @file ctdrv.h - * - * @breaf - * This file contains the definition of card IDs supported by this driver. - * - * @author Liu Chun - * - */ - -#ifndef CTDRV_H -#define CTDRV_H - -#define PCI_VENDOR_CREATIVE 0x1102 -#define PCI_DEVICE_CREATIVE_20K1 0x0005 -#define PCI_DEVICE_CREATIVE_20K2 0x000B -#define PCI_SUBVENDOR_CREATIVE 0x1102 -#define PCI_SUBSYS_CREATIVE_SB0760 0x0024 -#define PCI_SUBSYS_CREATIVE_SB08801 0x0041 -#define PCI_SUBSYS_CREATIVE_SB08802 0x0042 -#define PCI_SUBSYS_CREATIVE_SB08803 0x0043 -#define PCI_SUBSYS_CREATIVE_HENDRIX 0x6000 - -#endif /* CTDRV_H */ diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index f91144036512..b7368fded53c 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -11,10 +11,10 @@ #include #include #include +#include #include #include #include "ctatc.h" -#include "ctdrv.h" MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); @@ -32,8 +32,8 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static struct pci_device_id ct_pci_dev_ids[] = { /* only X-Fi is supported, so... */ - { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K1) }, - { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K2) }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1) }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2) }, { 0, } }; MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); -- cgit v1.2.1 From 35b053becb64eba13f3ea5c8c51023997169ff34 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 15:17:16 +0200 Subject: ALSA: ctxfi - Avoid unneeded pci_read_config_*() calls Use struct pci subsystem_device and revision fields instead of unneeded calls of pci_read_config_*(). Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 073fe2a59dae..f52345002334 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1190,14 +1190,14 @@ static int atc_dev_free(struct snd_device *dev) static int atc_identify_card(struct ct_atc *atc) { - u16 subsys = 0; - u8 revision = 0; + u16 subsys; + u8 revision; struct pci_dev *pci = atc->pci; const struct ct_atc_chip_details *d; enum CTCARDS i; - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys); - pci_read_config_byte(pci, PCI_REVISION_ID, &revision); + subsys = pci->subsystem_device; + revision = pci->revision; atc->chip_details = NULL; atc->model = NUM_CTCARDS; for (d = atc_chip_details; d->vendor; d++) { @@ -1308,7 +1308,7 @@ static int atc_get_resources(struct ct_atc *atc) struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr = NULL; int err = 0, i = 0; - unsigned short subsys_id = 0; + unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); if (NULL == atc->daios) @@ -1339,7 +1339,7 @@ static int atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - pci_read_config_word(atc->pci, PCI_SUBSYSTEM_ID, &subsys_id); + subsys_id = atc->pci->subsystem_device; if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { /* SB073x cards */ da_desc.type = SPDIFI1; -- cgit v1.2.1 From b3e0afe61e8271a8d082478752a67e5c279c8f23 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 15:19:30 +0200 Subject: ALSA: ctxfi - Add prefix to debug prints Added ctxfi: prefix to each debug print. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctamixer.c | 4 ++-- sound/pci/ctxfi/ctatc.c | 16 +++++++++------- sound/pci/ctxfi/cthw20k2.c | 17 +++++++++-------- sound/pci/ctxfi/ctmixer.c | 6 +++--- sound/pci/ctxfi/ctpcm.c | 6 +++--- sound/pci/ctxfi/ctresource.c | 20 ++++++++++++-------- sound/pci/ctxfi/ctsrc.c | 4 ++-- sound/pci/ctxfi/ctvmem.c | 7 ++++--- sound/pci/ctxfi/xfi.c | 15 ++++++++------- 9 files changed, 52 insertions(+), 43 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index 119791aa70eb..859e996ad728 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -259,7 +259,7 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet AMIXER resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n"); goto error; } @@ -413,7 +413,7 @@ static int get_sum_rsc(struct sum_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet SUM resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n"); goto error; } diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index f52345002334..ead104ea1e35 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -191,7 +191,7 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) case SNDRV_PCM_FORMAT_S32_LE: return SRC_SF_S32; default: - printk(KERN_ERR "not recognized snd format is %d \n", + printk(KERN_ERR "ctxfi: not recognized snd format is %d \n", snd_format); return SRC_SF_S16; } @@ -1254,7 +1254,8 @@ static int ct_create_alsa_devs(struct ct_atc *atc) err = alsa_dev_funcs[i].create(atc, i, alsa_dev_funcs[i].public_name); if (err) { - printk(KERN_ERR "Creating alsa device %d failed!\n", i); + printk(KERN_ERR "ctxfi: " + "Creating alsa device %d failed!\n", i); return err; } } @@ -1289,7 +1290,8 @@ static int atc_create_hw_devs(struct ct_atc *atc) err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); if (err) { - printk(KERN_ERR "Failed to create rsc_mgr %d!!!\n", i); + printk(KERN_ERR "ctxfi: " + "Failed to create rsc_mgr %d!!!\n", i); return err; } } @@ -1333,7 +1335,7 @@ static int atc_get_resources(struct ct_atc *atc) err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { - printk(KERN_ERR "Failed to get DAIO " + printk(KERN_ERR "ctxfi: Failed to get DAIO " "resource %d!!!\n", i); return err; } @@ -1349,7 +1351,7 @@ static int atc_get_resources(struct ct_atc *atc) err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { - printk(KERN_ERR "Failed to get S/PDIF-in resource!!!\n"); + printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n"); return err; } atc->n_daio++; @@ -1400,7 +1402,7 @@ static int atc_get_resources(struct ct_atc *atc) err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer); if (err) { - printk(KERN_ERR "Failed to create mixer obj!!!\n"); + printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n"); return err; } @@ -1600,7 +1602,7 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, error1: ct_atc_destroy(atc); - printk(KERN_ERR "Something wrong!!!\n"); + printk(KERN_ERR "ctxfi: Something wrong!!!\n"); return err; } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 9c2d38b4faa1..7c24c2ca96bd 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1144,7 +1144,7 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111); hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0); } else { - printk(KERN_ALERT "ERROR!!! Invalid sampling rate!!!\n"); + printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n"); return -EINVAL; } @@ -1197,7 +1197,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { - printk(KERN_ALERT "Wrong device page table page address!!!\n"); + printk(KERN_ALERT "ctxfi: " + "Wrong device page table page address!!!\n"); return -1; } @@ -1314,7 +1315,7 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr) break; } if (i >= 1000) { - printk(KERN_ALERT "PLL initialization failed!!!\n"); + printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n"); return -EBUSY; } @@ -1338,7 +1339,7 @@ static int hw_auto_init(struct hw *hw) break; } if (!get_field(gctl, GCTL_AID)) { - printk(KERN_ALERT "Card Auto-init failed!!!\n"); + printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n"); return -EBUSY; } @@ -1762,7 +1763,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) /* Initialize I2C */ err = I2CInit(hw, 0x1A, 1, 1); if (err < 0) { - printk(KERN_ALERT "Failure to acquire I2C!!!\n"); + printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n"); goto error; } @@ -1782,7 +1783,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { - printk(KERN_ALERT "Invalid master sampling " + printk(KERN_ALERT "ctxfi: Invalid master sampling " "rate (msr %d)!!!\n", info->msr); err = -EINVAL; goto error; @@ -1820,7 +1821,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ } else { - printk(KERN_ALERT "ERROR!!! Invalid input mux!!!\n"); + printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); err = -EINVAL; goto error; } @@ -1852,7 +1853,7 @@ static int hw_card_start(struct hw *hw) dma_mask = CT_XFI_DMA_MASK; if (pci_set_dma_mask(pci, dma_mask) < 0 || pci_set_consistent_dma_mask(pci, dma_mask) < 0) { - printk(KERN_ERR "architecture does not support PCI " + printk(KERN_ERR "ctxfi: architecture does not support PCI " "busmaster DMA with mask 0x%x\n", dma_mask); err = -ENXIO; goto error1; diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index c80d692952f9..b7950768c3f8 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -760,7 +760,7 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer) for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) { err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum); if (err) { - printk(KERN_ERR "Failed to get sum resources for " + printk(KERN_ERR "ctxfi:Failed to get sum resources for " "front output!\n"); break; } @@ -775,8 +775,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer) for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) { err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer); if (err) { - printk(KERN_ERR "Failed to get amixer resources for " - "mixer obj!\n"); + printk(KERN_ERR "ctxfi:Failed to get amixer resources " + "for mixer obj!\n"); break; } mixer->amixers[i] = amixer; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 73d4fdbbb9f4..a64cb0ed8759 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -284,7 +284,7 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) err = atc->pcm_playback_prepare(atc, apcm); if (err < 0) { - printk(KERN_ERR "Preparing pcm playback failed!!!\n"); + printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n"); return err; } @@ -389,7 +389,7 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) err = atc->pcm_capture_prepare(atc, apcm); if (err < 0) { - printk(KERN_ERR "Preparing pcm capture failed!!!\n"); + printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n"); return err; } @@ -477,7 +477,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc, err = snd_pcm_new(atc->card, name, device, playback_count, capture_count, &pcm); if (err < 0) { - printk(KERN_ERR "snd_pcm_new failed!! Err=%d\n", err); + printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err); return err; } diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c index 414fc23267cd..da21a717a07a 100644 --- a/sound/pci/ctxfi/ctresource.c +++ b/sound/pci/ctxfi/ctresource.c @@ -162,12 +162,14 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) case DAIO: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", type); + printk(KERN_ERR + "ctxfi: Invalid resource type value %d!\n", type); return -EINVAL; } if (err) { - printk(KERN_ERR "Failed to get resource control block!\n"); + printk(KERN_ERR + "ctxfi: Failed to get resource control block!\n"); return err; } @@ -190,8 +192,8 @@ int rsc_uninit(struct rsc *rsc) case DAIO: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", - rsc->type); + printk(KERN_ERR "ctxfi: " + "Invalid resource type value %d!\n", rsc->type); break; } @@ -233,13 +235,15 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, case SUM: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", type); + printk(KERN_ERR + "ctxfi: Invalid resource type value %d!\n", type); err = -EINVAL; goto error; } if (err) { - printk(KERN_ERR "Failed to get manager control block!\n"); + printk(KERN_ERR + "ctxfi: Failed to get manager control block!\n"); goto error; } @@ -282,8 +286,8 @@ int rsc_mgr_uninit(struct rsc_mgr *mgr) case SUM: break; default: - printk(KERN_ERR "Invalid resource type value %d!\n", - mgr->type); + printk(KERN_ERR "ctxfi: " + "Invalid resource type value %d!\n", mgr->type); break; } diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index d3e0ad5ed9fb..77e118c5bc97 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -431,7 +431,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet SRC resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n"); return err; } @@ -740,7 +740,7 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr, } spin_unlock_irqrestore(&mgr->mgr_lock, flags); if (err) { - printk(KERN_ERR "Can't meet SRCIMP resource request!\n"); + printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n"); goto error1; } diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 46ca04ce9210..cecf77e3ee86 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -121,12 +121,13 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) /* do mapping */ if ((unsigned long)host_addr >= VMALLOC_START) { - printk(KERN_ERR "Fail! Not support vmalloced addr now!\n"); + printk(KERN_ERR "ctxfi: " + "Fail! Not support vmalloced addr now!\n"); return NULL; } if (size > vm->size) { - printk(KERN_ERR "Fail! No sufficient device virtural " + printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " "memory space available!\n"); return NULL; } @@ -139,7 +140,7 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) block = get_vm_block(vm, (pages << PAGE_SHIFT)); if (block == NULL) { - printk(KERN_ERR "No virtual memory block that is big " + printk(KERN_ERR "ctxfi: No virtual memory block that is big " "enough to allocate!\n"); return NULL; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index b7368fded53c..19310ed26e88 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -57,16 +57,17 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err) return err; if ((reference_rate != 48000) && (reference_rate != 44100)) { - printk(KERN_ERR "Invalid reference_rate value %u!!!\n" - "The valid values for reference_rate " - "are 48000 and 44100.\nValue 48000 is " - "assumed.\n", reference_rate); + printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n", + reference_rate); + printk(KERN_ERR "ctxfi: The valid values for reference_rate " + "are 48000 and 44100, Value 48000 is assumed.\n"); reference_rate = 48000; } if ((multiple != 1) && (multiple != 2)) { - printk(KERN_ERR "Invalid multiple value %u!!!\nThe valid " - "values for multiple are 1 and 2.\nValue 2 " - "is assumed.\n", multiple); + printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n", + multiple); + printk(KERN_ERR "ctxfi: The valid values for multiple are " + "1 and 2, Value 2 is assumed.\n"); multiple = 2; } err = ct_atc_create(card, pci, reference_rate, multiple, &atc); -- cgit v1.2.1 From 7442f9dadb8626bd9e7e01445560499560b99f5d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 May 2009 18:20:33 +0200 Subject: ALSA: hda - Add a quirk entry for Macbook Pro 5,1 Added the codec SSID for MacBook Pro 5,1 as compatible as MP51. However, the headphone auto-muting function doesn't work. So, this is just a tentative solution. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 51954bacaa01..1bf9d054aae6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7293,6 +7293,10 @@ static int patch_alc882(struct hda_codec *codec) board_config = ALC885_MBP3; break; case 0x106b3f00: /* Macbook 5,1 */ + case 0x106b4000: /* Macbook Pro 5,1 - FIXME: HP jack sense + * seems not working, so apparently + * no perfect solution yet + */ board_config = ALC885_MB5; break; default: -- cgit v1.2.1 From 2fc998907947f92b1b9113faad531d7f5a857987 Mon Sep 17 00:00:00 2001 From: Nickolas Lloyd Date: Fri, 15 May 2009 15:33:30 +0200 Subject: ALSA: hda - add controls to toggle DC bias on mic ports This patch adds a mixer control for the STAC92XX boards to control the DC bias of mic ports, allowing recording from both powered and non-powered sources. It replaces the "Mic Output Switch" with "Mic Jack Mode" to switch between Mic, Line In, and Line Out. Signed-off-by: Nickolas Lloyd Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 111 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ecf53f755a09..02950980778e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -634,6 +634,96 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; } +static unsigned int stac92xx_vref_set(struct hda_codec *codec, + hda_nid_t nid, unsigned int new_vref) +{ + unsigned int error; + unsigned int pincfg; + pincfg = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + pincfg &= 0xff; + pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + pincfg |= new_vref; + + if (new_vref == AC_PINCTL_VREF_HIZ) + pincfg |= AC_PINCTL_OUT_EN; + else + pincfg |= AC_PINCTL_IN_EN; + + error = snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg); + if (error < 0) + return error; + else + return 1; +} + +static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int vref; + vref = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + vref &= AC_PINCTL_VREFEN; + return vref; +} + +static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int new_vref; + unsigned int error; + + if (ucontrol->value.enumerated.item[0] == 0) + new_vref = AC_PINCTL_VREF_80; + else if (ucontrol->value.enumerated.item[0] == 1) + new_vref = AC_PINCTL_VREF_GRD; + else + new_vref = AC_PINCTL_VREF_HIZ; + + if (new_vref != stac92xx_vref_get(codec, kcontrol->private_value)) { + error = stac92xx_vref_set(codec, + kcontrol->private_value, new_vref); + return error; + } + + return 0; +} + +static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int vref = stac92xx_vref_get(codec, kcontrol->private_value); + if (vref == AC_PINCTL_VREF_80) + ucontrol->value.enumerated.item[0] = 0; + else if (vref == AC_PINCTL_VREF_GRD) + ucontrol->value.enumerated.item[0] = 1; + else if (vref == AC_PINCTL_VREF_HIZ) + ucontrol->value.enumerated.item[0] = 2; + + return 0; +} + +static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { + "Mic In", "Line In", "Line Out" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = 3; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= 3) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -995,6 +1085,17 @@ static struct hda_verb stac9205_core_init[] = { .private_value = verb_read | (verb_write << 16), \ } +#define DC_BIAS(xname, idx, nid) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = idx, \ + .info = stac92xx_dc_bias_info, \ + .get = stac92xx_dc_bias_get, \ + .put = stac92xx_dc_bias_put, \ + .private_value = nid, \ + } + static struct snd_kcontrol_new stac9200_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), @@ -2702,7 +2803,8 @@ enum { STAC_CTL_WIDGET_AMP_VOL, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, - STAC_CTL_WIDGET_CLFE_SWITCH + STAC_CTL_WIDGET_CLFE_SWITCH, + STAC_CTL_WIDGET_DC_BIAS }; static struct snd_kcontrol_new stac92xx_control_templates[] = { @@ -2714,6 +2816,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), + DC_BIAS(NULL, 0, 0), }; /* add dynamic controls */ @@ -3165,9 +3268,9 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } if (spec->mic_switch) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, - "Mic as Output Switch", - (spec->mic_switch << 8) | 1); + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_DC_BIAS, + "Mic Jack Mode", + spec->mic_switch); if (err < 0) return err; } -- cgit v1.2.1 From 812a2cca295ee7f56cd1b988a0f93646285c214a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 16 May 2009 10:00:49 +0200 Subject: ALSA: hda - Split codec->name to vendor and chip name strings Split the name string in hda_codec struct to vendor_name and chip_name strings to be stored directly from the preset name. Since mostly only the chip name is referred in many patch_*.c, this results in the reduction of many codes in the end. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 36 ++++++++++++++++--------- sound/pci/hda/hda_codec.h | 3 ++- sound/pci/hda/hda_hwdep.c | 9 ++++--- sound/pci/hda/hda_proc.c | 8 ++++-- sound/pci/hda/patch_realtek.c | 63 ++++++------------------------------------- 5 files changed, 46 insertions(+), 73 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b91f6ed5cc58..77385de01744 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -623,7 +623,10 @@ static int get_codec_name(struct hda_codec *codec) const struct hda_vendor_id *c; const char *vendor = NULL; u16 vendor_id = codec->vendor_id >> 16; - char tmp[16], name[32]; + char tmp[16]; + + if (codec->vendor_name) + goto get_chip_name; for (c = hda_vendor_ids; c->id; c++) { if (c->id == vendor_id) { @@ -635,14 +638,21 @@ static int get_codec_name(struct hda_codec *codec) sprintf(tmp, "Generic %04x", vendor_id); vendor = tmp; } + codec->vendor_name = kstrdup(vendor, GFP_KERNEL); + if (!codec->vendor_name) + return -ENOMEM; + + get_chip_name: + if (codec->chip_name) + return 0; + if (codec->preset && codec->preset->name) - snprintf(name, sizeof(name), "%s %s", vendor, - codec->preset->name); - else - snprintf(name, sizeof(name), "%s ID %x", vendor, - codec->vendor_id & 0xffff); - codec->name = kstrdup(name, GFP_KERNEL); - if (!codec->name) + codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL); + else { + sprintf(tmp, "ID %x", codec->vendor_id & 0xffff); + codec->chip_name = kstrdup(tmp, GFP_KERNEL); + } + if (!codec->chip_name) return -ENOMEM; return 0; } @@ -848,7 +858,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) module_put(codec->owner); free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); - kfree(codec->name); + kfree(codec->vendor_name); + kfree(codec->chip_name); kfree(codec->modelname); kfree(codec->wcaps); kfree(codec); @@ -989,15 +1000,16 @@ int snd_hda_codec_configure(struct hda_codec *codec) int err; codec->preset = find_codec_preset(codec); - if (!codec->name) { + if (!codec->vendor_name || !codec->chip_name) { err = get_codec_name(codec); if (err < 0) return err; } /* audio codec should override the mixer name */ if (codec->afg || !*codec->bus->card->mixername) - strlcpy(codec->bus->card->mixername, codec->name, - sizeof(codec->bus->card->mixername)); + snprintf(codec->bus->card->mixername, + sizeof(codec->bus->card->mixername), + "%s %s", codec->vendor_name, codec->chip_name); if (is_generic_config(codec)) { err = snd_hda_parse_generic_codec(codec); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cd8979c7670b..c5bd40f77bb9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -748,7 +748,8 @@ struct hda_codec { /* detected preset */ const struct hda_codec_preset *preset; struct module *owner; - const char *name; /* codec name */ + const char *vendor_name; /* codec vendor name */ + const char *chip_name; /* codec chip name */ const char *modelname; /* model name for preset */ /* set by patch */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 1c57505c2874..6812fbe80fa4 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -242,7 +242,8 @@ CODEC_INFO_SHOW(subsystem_id); CODEC_INFO_SHOW(revision_id); CODEC_INFO_SHOW(afg); CODEC_INFO_SHOW(mfg); -CODEC_INFO_STR_SHOW(name); +CODEC_INFO_STR_SHOW(vendor_name); +CODEC_INFO_STR_SHOW(chip_name); CODEC_INFO_STR_SHOW(modelname); #define CODEC_INFO_STORE(type) \ @@ -275,7 +276,8 @@ static ssize_t type##_store(struct device *dev, \ CODEC_INFO_STORE(vendor_id); CODEC_INFO_STORE(subsystem_id); CODEC_INFO_STORE(revision_id); -CODEC_INFO_STR_STORE(name); +CODEC_INFO_STR_STORE(vendor_name); +CODEC_INFO_STR_STORE(chip_name); CODEC_INFO_STR_STORE(modelname); #define CODEC_ACTION_STORE(type) \ @@ -499,7 +501,8 @@ static struct device_attribute codec_attrs[] = { CODEC_ATTR_RW(revision_id), CODEC_ATTR_RO(afg), CODEC_ATTR_RO(mfg), - CODEC_ATTR_RW(name), + CODEC_ATTR_RW(vendor_name), + CODEC_ATTR_RW(chip_name), CODEC_ATTR_RW(modelname), CODEC_ATTR_RW(init_verbs), CODEC_ATTR_RW(hints), diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 93d7499350c6..418c5d1badaa 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -466,8 +466,12 @@ static void print_codec_info(struct snd_info_entry *entry, hda_nid_t nid; int i, nodes; - snd_iprintf(buffer, "Codec: %s\n", - codec->name ? codec->name : "Not Set"); + snd_iprintf(buffer, "Codec: "); + if (codec->vendor_name && codec->chip_name) + snd_iprintf(buffer, "%s %s\n", + codec->vendor_name, codec->chip_name); + else + snd_iprintf(buffer, "Not Set\n"); snd_iprintf(buffer, "Address: %d\n", codec->addr); snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id); snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1bf9d054aae6..4bc3e7e53555 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -277,13 +277,13 @@ struct alc_spec { */ unsigned int num_init_verbs; - char *stream_name_analog; /* analog PCM stream */ + char stream_name_analog[16]; /* analog PCM stream */ struct hda_pcm_stream *stream_analog_playback; struct hda_pcm_stream *stream_analog_capture; struct hda_pcm_stream *stream_analog_alt_playback; struct hda_pcm_stream *stream_analog_alt_capture; - char *stream_name_digital; /* digital PCM stream */ + char stream_name_digital[16]; /* digital PCM stream */ struct hda_pcm_stream *stream_digital_playback; struct hda_pcm_stream *stream_digital_capture; @@ -3169,7 +3169,10 @@ static int alc_build_pcms(struct hda_codec *codec) if (spec->no_analog) goto skip_analog; + snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; + if (spec->stream_analog_playback) { if (snd_BUG_ON(!spec->multiout.dac_nids)) return -EINVAL; @@ -3195,6 +3198,9 @@ static int alc_build_pcms(struct hda_codec *codec) skip_analog: /* SPDIF for stream index #1 */ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); codec->num_pcms = 2; codec->slave_dig_outs = spec->multiout.slave_dig_outs; info = spec->pcm_rec + 1; @@ -4432,12 +4438,10 @@ static int patch_alc880(struct hda_codec *codec) if (board_config != ALC880_AUTO) setup_preset(spec, &alc880_presets[board_config]); - spec->stream_name_analog = "ALC880 Analog"; spec->stream_analog_playback = &alc880_pcm_analog_playback; spec->stream_analog_capture = &alc880_pcm_analog_capture; spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; - spec->stream_name_digital = "ALC880 Digital"; spec->stream_digital_playback = &alc880_pcm_digital_playback; spec->stream_digital_capture = &alc880_pcm_digital_capture; @@ -6078,11 +6082,9 @@ static int patch_alc260(struct hda_codec *codec) if (board_config != ALC260_AUTO) setup_preset(spec, &alc260_presets[board_config]); - spec->stream_name_analog = "ALC260 Analog"; spec->stream_analog_playback = &alc260_pcm_analog_playback; spec->stream_analog_capture = &alc260_pcm_analog_capture; - spec->stream_name_digital = "ALC260 Digital"; spec->stream_digital_playback = &alc260_pcm_digital_playback; spec->stream_digital_capture = &alc260_pcm_digital_capture; @@ -7337,14 +7339,6 @@ static int patch_alc882(struct hda_codec *codec) if (board_config != ALC882_AUTO) setup_preset(spec, &alc882_presets[board_config]); - if (codec->vendor_id == 0x10ec0885) { - spec->stream_name_analog = "ALC885 Analog"; - spec->stream_name_digital = "ALC885 Digital"; - } else { - spec->stream_name_analog = "ALC882 Analog"; - spec->stream_name_digital = "ALC882 Digital"; - } - spec->stream_analog_playback = &alc882_pcm_analog_playback; spec->stream_analog_capture = &alc882_pcm_analog_capture; /* FIXME: setup DAC5 */ @@ -9232,13 +9226,6 @@ static int patch_alc883(struct hda_codec *codec) switch (codec->vendor_id) { case 0x10ec0888: - if (codec->revision_id == 0x100101) { - spec->stream_name_analog = "ALC1200 Analog"; - spec->stream_name_digital = "ALC1200 Digital"; - } else { - spec->stream_name_analog = "ALC888 Analog"; - spec->stream_name_digital = "ALC888 Digital"; - } if (!spec->num_adc_nids) { spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); spec->adc_nids = alc883_adc_nids; @@ -9249,8 +9236,6 @@ static int patch_alc883(struct hda_codec *codec) spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */ break; case 0x10ec0889: - spec->stream_name_analog = "ALC889 Analog"; - spec->stream_name_digital = "ALC889 Digital"; if (!spec->num_adc_nids) { spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids); spec->adc_nids = alc889_adc_nids; @@ -9261,8 +9246,6 @@ static int patch_alc883(struct hda_codec *codec) capture */ break; default: - spec->stream_name_analog = "ALC883 Analog"; - spec->stream_name_digital = "ALC883 Digital"; if (!spec->num_adc_nids) { spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); spec->adc_nids = alc883_adc_nids; @@ -11087,11 +11070,9 @@ static int patch_alc262(struct hda_codec *codec) if (board_config != ALC262_AUTO) setup_preset(spec, &alc262_presets[board_config]); - spec->stream_name_analog = "ALC262 Analog"; spec->stream_analog_playback = &alc262_pcm_analog_playback; spec->stream_analog_capture = &alc262_pcm_analog_capture; - spec->stream_name_digital = "ALC262 Digital"; spec->stream_digital_playback = &alc262_pcm_digital_playback; spec->stream_digital_capture = &alc262_pcm_digital_capture; @@ -12117,14 +12098,6 @@ static int patch_alc268(struct hda_codec *codec) if (board_config != ALC268_AUTO) setup_preset(spec, &alc268_presets[board_config]); - if (codec->vendor_id == 0x10ec0267) { - spec->stream_name_analog = "ALC267 Analog"; - spec->stream_name_digital = "ALC267 Digital"; - } else { - spec->stream_name_analog = "ALC268 Analog"; - spec->stream_name_digital = "ALC268 Digital"; - } - spec->stream_analog_playback = &alc268_pcm_analog_playback; spec->stream_analog_capture = &alc268_pcm_analog_capture; spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture; @@ -12979,7 +12952,6 @@ static int patch_alc269(struct hda_codec *codec) if (board_config != ALC269_AUTO) setup_preset(spec, &alc269_presets[board_config]); - spec->stream_name_analog = "ALC269 Analog"; if (codec->subsystem_id == 0x17aa3bf8) { /* Due to a hardware problem on Lenovo Ideadpad, we need to * fix the sample rate of analog I/O to 44.1kHz @@ -12990,7 +12962,6 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_analog_playback = &alc269_pcm_analog_playback; spec->stream_analog_capture = &alc269_pcm_analog_capture; } - spec->stream_name_digital = "ALC269 Digital"; spec->stream_digital_playback = &alc269_pcm_digital_playback; spec->stream_digital_capture = &alc269_pcm_digital_capture; @@ -14080,11 +14051,9 @@ static int patch_alc861(struct hda_codec *codec) if (board_config != ALC861_AUTO) setup_preset(spec, &alc861_presets[board_config]); - spec->stream_name_analog = "ALC861 Analog"; spec->stream_analog_playback = &alc861_pcm_analog_playback; spec->stream_analog_capture = &alc861_pcm_analog_capture; - spec->stream_name_digital = "ALC861 Digital"; spec->stream_digital_playback = &alc861_pcm_digital_playback; spec->stream_digital_capture = &alc861_pcm_digital_capture; @@ -15007,13 +14976,8 @@ static int patch_alc861vd(struct hda_codec *codec) setup_preset(spec, &alc861vd_presets[board_config]); if (codec->vendor_id == 0x10ec0660) { - spec->stream_name_analog = "ALC660-VD Analog"; - spec->stream_name_digital = "ALC660-VD Digital"; /* always turn on EAPD */ add_verb(spec, alc660vd_eapd_verbs); - } else { - spec->stream_name_analog = "ALC861VD Analog"; - spec->stream_name_digital = "ALC861VD Digital"; } spec->stream_analog_playback = &alc861vd_pcm_analog_playback; @@ -16936,17 +16900,6 @@ static int patch_alc662(struct hda_codec *codec) if (board_config != ALC662_AUTO) setup_preset(spec, &alc662_presets[board_config]); - if (codec->vendor_id == 0x10ec0663) { - spec->stream_name_analog = "ALC663 Analog"; - spec->stream_name_digital = "ALC663 Digital"; - } else if (codec->vendor_id == 0x10ec0272) { - spec->stream_name_analog = "ALC272 Analog"; - spec->stream_name_digital = "ALC272 Digital"; - } else { - spec->stream_name_analog = "ALC662 Analog"; - spec->stream_name_digital = "ALC662 Digital"; - } - spec->stream_analog_playback = &alc662_pcm_analog_playback; spec->stream_analog_capture = &alc662_pcm_analog_capture; -- cgit v1.2.1 From 6c627f3978bf3059d45713e2e1240b7c43cc85f5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 May 2009 12:33:36 +0200 Subject: ALSA: hda - Show the actual chip name in 'unkown model' messages Show the actual chip name in 'unknown model..' info messages for Realtek codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4bc3e7e53555..62f4d0ca7e14 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4410,8 +4410,8 @@ static int patch_alc880(struct hda_codec *codec) alc880_models, alc880_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC880, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC880_AUTO; } @@ -6054,8 +6054,9 @@ static int patch_alc260(struct hda_codec *codec) alc260_models, alc260_cfg_tbl); if (board_config < 0) { - snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, " - "trying auto-probe from BIOS...\n"); + snd_printd(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", + codec->chip_name); board_config = ALC260_AUTO; } @@ -7308,8 +7309,9 @@ static int patch_alc882(struct hda_codec *codec) alc_free(codec); return patch_alc883(codec); } - printk(KERN_INFO "hda_codec: Unknown model for ALC882, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", + codec->chip_name); board_config = ALC882_AUTO; } } @@ -9196,8 +9198,8 @@ static int patch_alc883(struct hda_codec *codec) alc883_models, alc883_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC883, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC883_AUTO; } @@ -11040,8 +11042,8 @@ static int patch_alc262(struct hda_codec *codec) alc262_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC262, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC262_AUTO; } @@ -12076,8 +12078,8 @@ static int patch_alc268(struct hda_codec *codec) alc268_cfg_tbl); if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC268, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC268_AUTO; } @@ -12924,8 +12926,8 @@ static int patch_alc269(struct hda_codec *codec) alc269_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC269, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC269_AUTO; } @@ -14023,8 +14025,8 @@ static int patch_alc861(struct hda_codec *codec) alc861_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC861, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC861_AUTO; } @@ -14947,8 +14949,8 @@ static int patch_alc861vd(struct hda_codec *codec) alc861vd_cfg_tbl); if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/" - "ALC861VD, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC861VD_AUTO; } @@ -16872,8 +16874,8 @@ static int patch_alc662(struct hda_codec *codec) alc662_models, alc662_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC662, " - "trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for %s, " + "trying auto-probe from BIOS...\n", codec->chip_name); board_config = ALC662_AUTO; } -- cgit v1.2.1 From 313f6e2d40bd67e394a65e7d64acd0cf9c9d248d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 May 2009 12:40:52 +0200 Subject: ALSA: hda - Avoid conflicts with snd-ctxfi driver The PCI entries of Creative with HD-audio class can be the devices with emu20k1/emu20k2 chips. These are supported better by snd-ctxfi driver. With that driver, the device will mutate from HD-audio to its native class. This patch adds a simple ifdef to avoid the conflict of device probe between snd-hda-intel and snd-ctxfi drivers. 1102:0009 seems still OK to be added as it has no emu20kx chip, and is a pure HD-audio device. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 04f19f8cad84..7bb6dd2cb6cc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2517,10 +2517,19 @@ static struct pci_device_id azx_ids[] = { /* Teradici */ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, /* Creative X-Fi (CA0110-IBG) */ +#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE) + /* the following entry conflicts with snd-ctxfi driver, + * as ctxfi driver mutates from HD-audio to native mode with + * a special command sequence. + */ { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, .driver_data = AZX_DRIVER_GENERIC }, +#else + /* this entry seems still valid -- i.e. without emu20kx chip */ + { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC }, +#endif /* AMD Generic, PCI class code and Vendor ID for HD Audio */ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, -- cgit v1.2.1 From eb4c41d30ba1f973c8b10be3644571ab997ae0d6 Mon Sep 17 00:00:00 2001 From: Torben Schulz Date: Mon, 18 May 2009 15:02:35 +0200 Subject: ALSA: hda - Improved MacBook 3,1 support This patch adds support for MacBook 3,1 sound by adding a model new "mb31" with the appropriate init verbs, mixers and channel modes to the ALC883 configuration. patch_alc882() and patch_alc883() are modified to handle the MacBook 3,1 sound-chip (Realtek ALC889A) correctly. Signed-off-by: Torben Schulz Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 151 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 146 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 62f4d0ca7e14..b942019461a1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -240,6 +240,7 @@ enum { ALC883_3ST_6ch_INTEL, ALC888_ASUS_M90V, ALC888_ASUS_EEE1601, + ALC889A_MB31, ALC1200_ASUS_P5Q, ALC883_AUTO, ALC883_MODEL_LAST, @@ -7291,7 +7292,7 @@ static int patch_alc882(struct hda_codec *codec) case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */ case 0x106b00a4: /* MacbookPro4,1 */ case 0x106b2c00: /* Macbook Pro rev3 */ - case 0x106b3600: /* Macbook 3.1 */ + /* Macbook 3.1 (0x106b3600) is handled by patch_alc883() */ case 0x106b3800: /* MacbookPro4,1 - latter revision */ board_config = ALC885_MBP3; break; @@ -7493,6 +7494,17 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = { }, }; +static struct hda_input_mux alc889A_mb31_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + /* Front Mic (0x01) unused */ + { "Line", 0x2 }, + /* Line 2 (0x03) unused */ + /* CD (0x04) unsused? */ + }, +}; + /* * 2ch mode */ @@ -7611,6 +7623,49 @@ static struct hda_channel_mode alc883_sixstack_modes[2] = { { 8, alc883_sixstack_ch8_init }, }; +/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */ +static struct hda_verb alc889A_mb31_ch2_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ + { } /* end */ +}; + +/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */ +static struct hda_verb alc889A_mb31_ch4_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ + { } /* end */ +}; + +/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */ +static struct hda_verb alc889A_mb31_ch5_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */ + { } /* end */ +}; + +/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */ +static struct hda_verb alc889A_mb31_ch6_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */ + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */ + { } /* end */ +}; + +static struct hda_channel_mode alc889A_mb31_6ch_modes[4] = { + { 2, alc889A_mb31_ch2_init }, + { 4, alc889A_mb31_ch4_init }, + { 5, alc889A_mb31_ch5_init }, + { 6, alc889A_mb31_ch6_init }, +}; + static struct hda_verb alc883_medion_eapd_verbs[] = { /* eanable EAPD on medion laptop */ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, @@ -7889,6 +7944,32 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc889A_mb31_mixer[] = { + /* Output mixers */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT), + /* Output switches */ + HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT), + /* Boost mixers */ + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT), + /* Input mixers */ + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + { } /* end */ +}; + static struct hda_bind_ctls alc883_bind_cap_vol = { .ops = &snd_hda_bind_vol, .values = { @@ -8561,6 +8642,42 @@ static void alc883_eee1601_inithook(struct hda_codec *codec) alc_automute_pin(codec); } +static struct hda_verb alc889A_mb31_verbs[] = { + /* Init rear pin (used as headphone output) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* Init line pin (used as output in 4ch and 6ch mode) */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */ + /* Init line 2 pin (used as headphone out by default) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */ + { } /* end */ +}; + +/* Mute speakers according to the headphone jack state */ +static void alc889A_mb31_automute(struct hda_codec *codec) +{ + unsigned int present; + + /* Mute only in 2ch or 4ch mode */ + if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0) + == 0x00) { + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + } +} + +static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc889A_mb31_automute(codec); +} + #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc883_loopbacks alc880_loopbacks #endif @@ -8601,6 +8718,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", [ALC1200_ASUS_P5Q] = "asus-p5q", + [ALC889A_MB31] = "mb31", [ALC883_AUTO] = "auto", }; @@ -9052,6 +9170,21 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, }, + [ALC889A_MB31] = { + .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer}, + .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs, + alc880_gpio1_init_verbs }, + .adc_nids = alc883_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .dac_nids = alc883_dac_nids, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .channel_mode = alc889A_mb31_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes), + .input_mux = &alc889A_mb31_capture_source, + .dig_out_nid = ALC883_DIGOUT_NID, + .unsol_event = alc889A_mb31_unsol_event, + .init_hook = alc889A_mb31_automute, + }, }; @@ -9197,10 +9330,18 @@ static int patch_alc883(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST, alc883_models, alc883_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); - board_config = ALC883_AUTO; + if (board_config < 0 || board_config >= ALC883_MODEL_LAST) { + /* Pick up systems that don't supply PCI SSID */ + switch (codec->subsystem_id) { + case 0x106b3600: /* Macbook 3.1 */ + board_config = ALC889A_MB31; + break; + default: + printk(KERN_INFO + "hda_codec: Unknown model for %s, trying " + "auto-probe from BIOS...\n", codec->chip_name); + board_config = ALC883_AUTO; + } } if (board_config == ALC883_AUTO) { -- cgit v1.2.1 From 4abc1cc2f9fe4b6bb3acc1d78e2c15af47b8133d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 May 2009 12:16:46 +0200 Subject: ALSA: hda - Add prefix to kernel messages Add proper prefix to each kernel message in hda_intel.c. Also, avoid the unneeded prefix when CONFIG_SND_VERBOSE_PRINTK is used together with snd_print*(). Reference: bko#13207 http://bugzilla.kernel.org/show_bug.cgi?id=13207 Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7bb6dd2cb6cc..49fd973b85cc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -128,8 +128,11 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{ULI, M5461}}"); MODULE_DESCRIPTION("Intel HDA driver"); +#ifdef CONFIG_SND_VERBOSE_PRINTK +#define SFX /* nop */ +#else #define SFX "hda-intel: " - +#endif /* * registers @@ -620,7 +623,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) } if (chip->msi) { - snd_printk(KERN_WARNING "hda_intel: No response from codec, " + snd_printk(KERN_WARNING SFX "No response from codec, " "disabling MSI: last cmd=0x%08x\n", chip->last_cmd); free_irq(chip->irq, chip); chip->irq = -1; @@ -634,7 +637,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) } if (!chip->polling_mode) { - snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, " + snd_printk(KERN_WARNING SFX "azx_get_response timeout, " "switching to polling mode: last cmd=0x%08x\n", chip->last_cmd); chip->polling_mode = 1; @@ -649,7 +652,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) return -1; } - snd_printk(KERN_ERR "hda_intel: azx_get_response timeout (ERROR): " + snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): " "last cmd=0x%08x\n", chip->last_cmd); spin_lock_irq(&chip->reg_lock); chip->rirb.cmds = 0; /* reset the index */ @@ -776,7 +779,7 @@ static int azx_reset(struct azx *chip) /* check to see if controller is ready */ if (!azx_readb(chip, GCTL)) { - snd_printd("azx_reset: controller not ready!\n"); + snd_printd(SFX "azx_reset: controller not ready!\n"); return -EBUSY; } @@ -786,7 +789,7 @@ static int azx_reset(struct azx *chip) /* detect codecs */ if (!chip->codec_mask) { chip->codec_mask = azx_readw(chip, STATESTS); - snd_printdd("codec_mask = 0x%x\n", chip->codec_mask); + snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask); } return 0; @@ -954,12 +957,12 @@ static void azx_init_pci(struct azx *chip) case AZX_DRIVER_SCH: pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) { - pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, \ + pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP)); pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); - snd_printdd("HDA snoop disabled, enabling ... %s\n",\ - (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \ + snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n", + (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) ? "Failed" : "OK"); } break; @@ -1099,7 +1102,7 @@ static int azx_setup_periods(struct azx *chip, pos_align; pos_adj = frames_to_bytes(runtime, pos_adj); if (pos_adj >= period_bytes) { - snd_printk(KERN_WARNING "Too big adjustment %d\n", + snd_printk(KERN_WARNING SFX "Too big adjustment %d\n", bdl_pos_adj[chip->dev_index]); pos_adj = 0; } else { @@ -1123,7 +1126,7 @@ static int azx_setup_periods(struct azx *chip, return 0; error: - snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n", + snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n", azx_dev->bufsize, period_bytes); return -EINVAL; } @@ -1216,7 +1219,7 @@ static int probe_codec(struct azx *chip, int addr) chip->probing = 0; if (res == -1) return -EIO; - snd_printdd("hda_intel: codec #%d probed OK\n", addr); + snd_printdd(SFX "codec #%d probed OK\n", addr); return 0; } @@ -1271,8 +1274,8 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, /* Some BIOSen give you wrong codec addresses * that don't exist */ - snd_printk(KERN_WARNING - "hda_intel: Codec #%d probe error; " + snd_printk(KERN_WARNING SFX + "Codec #%d probe error; " "disabling it...\n", c); chip->codec_mask &= ~(1 << c); /* More badly, accessing to a non-existing @@ -1488,7 +1491,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) bufsize = snd_pcm_lib_buffer_bytes(substream); period_bytes = snd_pcm_lib_period_bytes(substream); - snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", + snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", bufsize, format_val); if (bufsize != azx_dev->bufsize || @@ -2265,7 +2268,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, synchronize_irq(chip->irq); gcap = azx_readw(chip, GCAP); - snd_printdd("chipset global capabilities = 0x%x\n", gcap); + snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap); /* ATI chips seems buggy about 64bit DMA addresses */ if (chip->driver_type == AZX_DRIVER_ATI) @@ -2309,7 +2312,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); if (!chip->azx_dev) { - snd_printk(KERN_ERR "cannot malloc azx_dev\n"); + snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n"); goto errout; } -- cgit v1.2.1 From fa7979663190240b838ab8c8bad7f59e618bf77c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 May 2009 12:50:04 +0200 Subject: ALSA: hda - Fix digital beep tone calculation The digital beep tone is calculated in two different ways depending on the codec chip. The standard one is using a divider, and another one is a linear tone for IDT/STAC codecs. Currently, only the latter type is used for all codecs, which resulted in a wrong tone pitch. This patch adds the calculation of the standard HD-audio type. Also clean-up the fields in hda_beep struct. Reference: bko#13162 http://bugzilla.kernel.org/show_bug.cgi?id=13162 Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 55 +++++++++++++++++++++++++++++++++--------- sound/pci/hda/hda_beep.h | 5 ++-- sound/pci/hda/patch_sigmatel.c | 2 ++ 3 files changed, 49 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 4de5bacd3929..29272f2e95a0 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -45,6 +45,46 @@ static void snd_hda_generate_beep(struct work_struct *work) AC_VERB_SET_BEEP_CONTROL, beep->tone); } +/* (non-standard) Linear beep tone calculation for IDT/STAC codecs + * + * The tone frequency of beep generator on IDT/STAC codecs is + * defined from the 8bit tone parameter, in Hz, + * freq = 48000 * (257 - tone) / 1024 + * that is from 12kHz to 93.75kHz in step of 46.875 hz + */ +static int beep_linear_tone(struct hda_beep *beep, int hz) +{ + hz *= 1000; /* fixed point */ + hz = hz - DIGBEEP_HZ_MIN; + if (hz < 0) + hz = 0; /* turn off PC beep*/ + else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN)) + hz = 0xff; + else { + hz /= DIGBEEP_HZ_STEP; + hz++; + } + return hz; +} + +/* HD-audio standard beep tone parameter calculation + * + * The tone frequency in Hz is calculated as + * freq = 48000 / (tone * 4) + * from 47Hz to 12kHz + */ +static int beep_standard_tone(struct hda_beep *beep, int hz) +{ + if (hz <= 0) + return 0; /* disabled */ + hz = 12000 / hz; + if (hz > 0xff) + return 0xff; + if (hz <= 0) + return 1; + return hz; +} + static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, unsigned int code, int hz) { @@ -55,21 +95,14 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, if (hz) hz = 1000; case SND_TONE: - hz *= 1000; /* fixed point */ - hz = hz - DIGBEEP_HZ_MIN; - if (hz < 0) - hz = 0; /* turn off PC beep*/ - else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN)) - hz = 0xff; - else { - hz /= DIGBEEP_HZ_STEP; - hz++; - } + if (beep->linear_tone) + beep->tone = beep_linear_tone(beep, hz); + else + beep->tone = beep_standard_tone(beep, hz); break; default: return -1; } - beep->tone = hz; /* schedule beep event */ schedule_work(&beep->beep_work); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 51bf6a5daf39..0c3de787c717 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -30,8 +30,9 @@ struct hda_beep { struct hda_codec *codec; char phys[32]; int tone; - int nid; - int enabled; + hda_nid_t nid; + unsigned int enabled:1; + unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ struct work_struct beep_work; /* scheduled task for beep event */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 02950980778e..17310814f121 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3737,6 +3737,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out err = snd_hda_attach_beep_device(codec, nid); if (err < 0) return err; + /* IDT/STAC codecs have linear beep tone parameter */ + codec->beep->linear_tone = 1; /* if no beep switch is available, make its own one */ caps = query_amp_caps(codec, nid, HDA_OUTPUT); if (codec->beep && -- cgit v1.2.1 From 3e3ee6dc949d25423f72bb5ee5f58dfa79ac85c7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 20 May 2009 16:47:59 +0200 Subject: ALSA: ctxfi - Add depends on X86 The ctxfi driver requires explicitly the 4k page size, and gives a build error on architectures with non-4k pages. As a workaround, just add the kconfig dependency on X86, which is the only architecture ever tested. Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 3a7640feaf92..2d7fef5df017 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -277,6 +277,7 @@ config SND_CS5535AUDIO config SND_CTXFI tristate "Creative Sound Blaster X-Fi" + depends on X86 select SND_PCM help If you want to use soundcards based on Creative Sound Blastr X-Fi -- cgit v1.2.1 From 87488957a68293357a94c8142de7d0ae17914912 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Thu, 21 May 2009 18:32:59 -0400 Subject: ALSA: hda - fix audio on HP TX25xx series notebooks Fixes https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4121 Taken from https://bugzilla.redhat.com/show_bug.cgi?id=498060 Signed-off-by: Adam Williamson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b8a0d3e79272..bcbb736f94f0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12058,6 +12058,7 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL), SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x103c, 0x30f1, "HP TX25xx series", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), -- cgit v1.2.1 From afe6d7e3c4a9aba020637f4ae15527a89ba31f21 Mon Sep 17 00:00:00 2001 From: Andreas Mohr Date: Fri, 22 May 2009 17:48:58 +0200 Subject: ALSA: Kill truncate warning by shortening Sigmatel-specific AC97 control name ALSA sound/core/control.c:232: Control name 'Sigmatel Surround Phase Inversion Playback Switch' truncated to 'Sigmatel Surround Phase Inversion Playback ' bootup message by omitting weird Sigmatel prefix in this case; also fix up the related ca0106 mixer control removal part by using identical naming there. Signed-off-by: Andreas Mohr Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_patch.c | 7 +++++-- sound/pci/ca0106/ca0106_mixer.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 81bc93e5f1e3..7337abdbe4e3 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -958,10 +958,13 @@ static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97) } static const struct snd_kcontrol_new snd_ac97_sigmatel_4speaker = -AC97_SINGLE("Sigmatel 4-Speaker Stereo Playback Switch", AC97_SIGMATEL_DAC2INVERT, 2, 1, 0); +AC97_SINGLE("Sigmatel 4-Speaker Stereo Playback Switch", + AC97_SIGMATEL_DAC2INVERT, 2, 1, 0); +/* "Sigmatel " removed due to excessive name length: */ static const struct snd_kcontrol_new snd_ac97_sigmatel_phaseinvert = -AC97_SINGLE("Sigmatel Surround Phase Inversion Playback Switch", AC97_SIGMATEL_DAC2INVERT, 3, 1, 0); +AC97_SINGLE("Surround Phase Inversion Playback Switch", + AC97_SIGMATEL_DAC2INVERT, 3, 1, 0); static const struct snd_kcontrol_new snd_ac97_sigmatel_controls[] = { AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0), diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index ad2888705d2a..c111efe61c3c 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -800,7 +800,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) "Capture Volume", "External Amplifier", "Sigmatel 4-Speaker Stereo Playback Switch", - "Sigmatel Surround Phase Inversion Playback ", + "Surround Phase Inversion Playback Switch", NULL }; static char *ca0106_rename_ctls[] = { -- cgit v1.2.1 From 93574844bc3906941b89d6b6f72e01e87413f3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ozan=20=C3=87a=C4=9Flayan?= Date: Sat, 23 May 2009 15:00:04 +0300 Subject: ALSA: hda - Add forced codec-slots for ASUS W5Fm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASUS W5Fm needs the fixed codec-slots to probe to override the BIOS problem like W5F. Tested-by: Alp Kılıç Signed-off-by: Ozan Çağlayan Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 21e99cfa8c49..3128e1a6bc65 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2141,6 +2141,7 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = { /* including bogus ALC268 in slot#2 that conflicts with ALC888 */ SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01), /* forced codec slots */ + SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103), SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103), {} }; -- cgit v1.2.1 From 679d92ed1403b6cf9a19aa42ec62b81cae1aa017 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 May 2009 19:00:08 +0200 Subject: ALSA: hda - Add 5stack-no-fp model for STAC927x The recent fix for the headphone volume control on IDT/STAC codecs resulted in the removal of invalid "Side" volume eventually. But, if the front panel doesn't exist, this setup could be regarded as a sort of regression, as reported in kernel bug #13250. Now as a workaround, a new model 5stack-no-fp is added so that the user without the front panel can choose this one explicitly. Reference: bko#13250 http://bugzilla.kernel.org/show_bug.cgi?id=13250 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 03b3646018a1..d2fd8ef6aef8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -150,6 +150,7 @@ enum { STAC_D965_REF, STAC_D965_3ST, STAC_D965_5ST, + STAC_D965_5ST_NO_FP, STAC_DELL_3ST, STAC_DELL_BIOS, STAC_927X_MODELS @@ -2154,6 +2155,13 @@ static unsigned int d965_5st_pin_configs[14] = { 0x40000100, 0x40000100 }; +static unsigned int d965_5st_no_fp_pin_configs[14] = { + 0x40000100, 0x40000100, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x01442070, + 0x40000100, 0x40000100 +}; + static unsigned int dell_3st_pin_configs[14] = { 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, 0x01111212, 0x01116211, 0x01813050, 0x01112214, @@ -2166,6 +2174,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { [STAC_D965_REF] = ref927x_pin_configs, [STAC_D965_3ST] = d965_3st_pin_configs, [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, [STAC_DELL_3ST] = dell_3st_pin_configs, [STAC_DELL_BIOS] = NULL, }; @@ -2176,6 +2185,7 @@ static const char *stac927x_models[STAC_927X_MODELS] = { [STAC_D965_REF] = "ref", [STAC_D965_3ST] = "3stack", [STAC_D965_5ST] = "5stack", + [STAC_D965_5ST_NO_FP] = "5stack-no-fp", [STAC_DELL_3ST] = "dell-3stack", [STAC_DELL_BIOS] = "dell-bios", }; -- cgit v1.2.1 From 461c6c3a0a23a8fac1a4b636e93ff5dfe599a241 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 25 May 2009 08:06:02 +0200 Subject: ALSA: hda - Add missing check of pin vref 50 and others in Realtek codecs Some Realtek codecs like ALC861 seem to support only VREF50 while the current driver assumes it's only VREF80. Check other VREF bits to set the correct value. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bcbb736f94f0..0fd258eba3a5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -776,6 +776,12 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; if (pincap & AC_PINCAP_VREF_80) val = PIN_VREF80; + else if (pincap & AC_PINCAP_VREF_50) + val = PIN_VREF50; + else if (pincap & AC_PINCAP_VREF_100) + val = PIN_VREF100; + else if (pincap & AC_PINCAP_VREF_GRD) + val = PIN_VREFGRD; } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } -- cgit v1.2.1 From 345c03ef0f2bd0c933a1eca27732af7edf5e8d4d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 25 May 2009 10:05:00 +0200 Subject: sound: oxygen: reset DMA when stream is closed When a PCM stream is closed, flush the corresponding DMA channel. Otherwise, the DMA controller would continue to output the last sample which would result in a DC offset on the output. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen_pcm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index c262049961e1..3b5ca70c9d4d 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -487,10 +487,14 @@ static int oxygen_hw_free(struct snd_pcm_substream *substream) { struct oxygen *chip = snd_pcm_substream_chip(substream); unsigned int channel = oxygen_substream_channel(substream); + unsigned int channel_mask = 1 << channel; spin_lock_irq(&chip->reg_lock); - chip->interrupt_mask &= ~(1 << channel); + chip->interrupt_mask &= ~channel_mask; oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + + oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); + oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); spin_unlock_irq(&chip->reg_lock); return snd_pcm_lib_free_pages(substream); -- cgit v1.2.1 From 53bb705d12e0f642c131cdcab2c8e3be7364e505 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 25 May 2009 10:05:43 +0200 Subject: sound: virtuoso: add another DX PCI ID Add another PCI ID for a second revision of the Xonar DX. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/virtuoso.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index bc5ce11c8b14..ebc4e8514f1d 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -188,6 +188,7 @@ static struct pci_device_id xonar_ids[] __devinitdata = { { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX }, { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV }, + { OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX }, { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 }, { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, -- cgit v1.2.1 From b990ae963a3f80a659b30562c1e3214b386ecce3 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 25 May 2009 10:06:22 +0200 Subject: sound: virtuoso: enable HDAV S/PDIF input The Xonar HDAV1.3 has a digital input jack, so enable the corresponding device. This is not related to the HDMI stuff, which stays unsupported. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/virtuoso.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index ebc4e8514f1d..71e38f48c672 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -1022,7 +1022,8 @@ static const struct oxygen_model model_xonar_hdav = { .model_data_size = sizeof(struct xonar_data), .device_config = PLAYBACK_0_TO_I2S | PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2, + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, .dac_channels = 8, .dac_volume_min = 255 - 2*60, .dac_volume_max = 255, -- cgit v1.2.1 From 04f9890df1bad2115665b7027e664aaffa44088d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 25 May 2009 10:11:29 +0200 Subject: sound: virtuoso: add Xonar Essence ST support Add support for the Asus Xonar Essence ST and its daughterboard. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 4 +-- sound/pci/oxygen/virtuoso.c | 60 +++++++++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 21 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93422e3a3f0c..699c280bbd67 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -764,8 +764,8 @@ config SND_VIRTUOSO select SND_OXYGEN_LIB help Say Y here to include support for sound cards based on the - Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and - Essence STX. + Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, + Essence ST (Deluxe), and Essence STX. Support for the HDAV1.3 (Deluxe) is very experimental. To compile this driver as a module, choose M here: the module diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 71e38f48c672..bf971f7cfdc6 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -113,8 +113,8 @@ */ /* - * Xonar Essence STX - * ----------------- + * Xonar Essence ST (Deluxe)/STX + * ----------------------------- * * CMI8788: * @@ -180,6 +180,8 @@ enum { MODEL_DX, MODEL_HDAV, /* without daughterboard */ MODEL_HDAV_H6, /* with H6 daughterboard */ + MODEL_ST, + MODEL_ST_H6, MODEL_STX, }; @@ -191,6 +193,7 @@ static struct pci_device_id xonar_ids[] __devinitdata = { { OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX }, { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 }, { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX }, + { OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } }; @@ -211,9 +214,9 @@ MODULE_DEVICE_TABLE(pci, xonar_ids); #define GPIO_DX_FRONT_PANEL 0x0002 #define GPIO_DX_INPUT_ROUTE 0x0100 -#define GPIO_HDAV_DB_MASK 0x0030 -#define GPIO_HDAV_DB_H6 0x0000 -#define GPIO_HDAV_DB_XX 0x0020 +#define GPIO_DB_MASK 0x0030 +#define GPIO_DB_H6 0x0000 +#define GPIO_DB_XX 0x0020 #define GPIO_ST_HP_REAR 0x0002 #define GPIO_ST_HP 0x0080 @@ -531,7 +534,7 @@ static void xonar_hdav_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } -static void xonar_stx_init(struct oxygen *chip) +static void xonar_st_init(struct oxygen *chip) { struct xonar_data *data = chip->model_data; @@ -540,12 +543,11 @@ static void xonar_stx_init(struct oxygen *chip) OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_SPEED_FAST); + if (chip->model.private_data == MODEL_ST_H6) + chip->model.dac_channels = 8; data->anti_pop_delay = 100; - data->dacs = 1; + data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1; data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; - data->ext_power_reg = OXYGEN_GPI_DATA; - data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; - data->ext_power_bit = GPI_DX_EXT_POWER; data->pcm1796_oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -561,6 +563,17 @@ static void xonar_stx_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } +static void xonar_stx_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + data->ext_power_reg = OXYGEN_GPI_DATA; + data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->ext_power_bit = GPI_DX_EXT_POWER; + + xonar_st_init(chip); +} + static void xonar_disable_output(struct oxygen *chip) { struct xonar_data *data = chip->model_data; @@ -1036,7 +1049,7 @@ static const struct oxygen_model model_xonar_hdav = { static const struct oxygen_model model_xonar_st = { .longname = "Asus Virtuoso 100", .chip = "AV200", - .init = xonar_stx_init, + .init = xonar_st_init, .control_filter = xonar_st_control_filter, .mixer_init = xonar_st_mixer_init, .cleanup = xonar_st_cleanup, @@ -1069,6 +1082,7 @@ static int __devinit get_xonar_model(struct oxygen *chip, [MODEL_D2] = &model_xonar_d2, [MODEL_D2X] = &model_xonar_d2, [MODEL_HDAV] = &model_xonar_hdav, + [MODEL_ST] = &model_xonar_st, [MODEL_STX] = &model_xonar_st, }; static const char *const names[] = { @@ -1078,6 +1092,8 @@ static int __devinit get_xonar_model(struct oxygen *chip, [MODEL_D2X] = "Xonar D2X", [MODEL_HDAV] = "Xonar HDAV1.3", [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6", + [MODEL_ST] = "Xonar Essence ST", + [MODEL_ST_H6] = "Xonar Essence ST+H6", [MODEL_STX] = "Xonar Essence STX", }; unsigned int model = id->driver_data; @@ -1094,21 +1110,27 @@ static int __devinit get_xonar_model(struct oxygen *chip, chip->model.init = xonar_dx_init; break; case MODEL_HDAV: - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_HDAV_DB_MASK); - switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & - GPIO_HDAV_DB_MASK) { - case GPIO_HDAV_DB_H6: + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + case GPIO_DB_H6: model = MODEL_HDAV_H6; break; - case GPIO_HDAV_DB_XX: + case GPIO_DB_XX: snd_printk(KERN_ERR "unknown daughterboard\n"); return -ENODEV; } break; + case MODEL_ST: + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + case GPIO_DB_H6: + model = MODEL_ST_H6; + break; + } + break; case MODEL_STX: - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_HDAV_DB_MASK); + chip->model.init = xonar_stx_init; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); break; } -- cgit v1.2.1 From 4fcd39207f4c91185cc89e3e6a28cbb643034ff1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 25 May 2009 18:34:52 +0200 Subject: ALSA: hda - Reset CORB/RIRB at retrying the verb communication When a codec communication error occurs, the CORB/RIRB counters should be reset first before re-issuing the verb. Simply call azx_free_cmd_io() and azx_init_cmd_io() to achieve that. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 49fd973b85cc..3fc75e2061ab 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -522,6 +522,7 @@ static void azx_init_cmd_io(struct azx *chip) /* RIRB set up */ chip->rirb.addr = chip->rb.addr + 2048; chip->rirb.buf = (u32 *)(chip->rb.area + 2048); + chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0; azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); @@ -533,7 +534,6 @@ static void azx_init_cmd_io(struct azx *chip) azx_writew(chip, RINTCNT, 1); /* enable rirb dma and response irq */ azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); - chip->rirb.rp = chip->rirb.cmds = 0; } static void azx_free_cmd_io(struct azx *chip) @@ -654,9 +654,11 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): " "last cmd=0x%08x\n", chip->last_cmd); + /* re-initialize CORB/RIRB */ spin_lock_irq(&chip->reg_lock); - chip->rirb.cmds = 0; /* reset the index */ bus->rirb_error = 1; + azx_free_cmd_io(chip); + azx_init_cmd_io(chip); spin_unlock_irq(&chip->reg_lock); return -1; } -- cgit v1.2.1 From a693a26fe0807c3c406d6b5295d222b646677288 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 12:58:58 +0200 Subject: ALSA: riptide - Code clean up A code clean up, coding style fixes. The firmware loading routine is split to an own function to improve the readability. Signed-off-by: Takashi Iwai --- sound/pci/riptide/riptide.c | 286 +++++++++++++++++++++----------------------- 1 file changed, 134 insertions(+), 152 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index e51a5ef1954d..460be038f9c7 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -507,41 +507,19 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip); */ static struct pci_device_id snd_riptide_ids[] = { - { - .vendor = 0x127a,.device = 0x4310, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, - { - .vendor = 0x127a,.device = 0x4320, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, - { - .vendor = 0x127a,.device = 0x4330, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, - { - .vendor = 0x127a,.device = 0x4340, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, + { PCI_DEVICE(0x127a, 0x4310) }, + { PCI_DEVICE(0x127a, 0x4320) }, + { PCI_DEVICE(0x127a, 0x4330) }, + { PCI_DEVICE(0x127a, 0x4340) }, {0,}, }; #ifdef SUPPORT_JOYSTICK static struct pci_device_id snd_riptide_joystick_ids[] __devinitdata = { - { - .vendor = 0x127a,.device = 0x4312, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, - { - .vendor = 0x127a,.device = 0x4322, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, - {.vendor = 0x127a,.device = 0x4332, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, - {.vendor = 0x127a,.device = 0x4342, - .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID, - }, + { PCI_DEVICE(0x127a, 0x4312) }, + { PCI_DEVICE(0x127a, 0x4322) }, + { PCI_DEVICE(0x127a, 0x4332) }, + { PCI_DEVICE(0x127a, 0x4342) }, {0,}, }; #endif @@ -1209,12 +1187,79 @@ static int riptide_resume(struct pci_dev *pci) } #endif +static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) +{ + union firmware_version firmware = { .ret = CMDRET_ZERO }; + int i, timeout, err; + + for (i = 0; i < 2; i++) { + WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0); + WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0); + } + SET_GRESET(cif->hwport); + udelay(100); + UNSET_GRESET(cif->hwport); + udelay(100); + + for (timeout = 100000; --timeout; udelay(10)) { + if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport)) + break; + } + if (!timeout) { + snd_printk(KERN_ERR + "Riptide: device not ready, audio status: 0x%x " + "ready: %d gerr: %d\n", + READ_AUDIO_STATUS(cif->hwport), + IS_READY(cif->hwport), IS_GERR(cif->hwport)); + return -EIO; + } else { + snd_printdd + ("Riptide: audio status: 0x%x ready: %d gerr: %d\n", + READ_AUDIO_STATUS(cif->hwport), + IS_READY(cif->hwport), IS_GERR(cif->hwport)); + } + + SEND_GETV(cif, &firmware.ret); + snd_printdd("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n", + firmware.firmware.ASIC, firmware.firmware.CODEC, + firmware.firmware.AUXDSP, firmware.firmware.PROG); + + for (i = 0; i < FIRMWARE_VERSIONS; i++) { + if (!memcmp(&firmware_versions[i], &firmware, sizeof(firmware))) + break; + } + if (i >= FIRMWARE_VERSIONS) + return 0; /* no match */ + + if (!chip) + return 1; /* OK */ + + snd_printdd("Writing Firmware\n"); + if (!chip->fw_entry) { + err = request_firmware(&chip->fw_entry, "riptide.hex", + &chip->pci->dev); + if (err) { + snd_printk(KERN_ERR + "Riptide: Firmware not available %d\n", err); + return -EIO; + } + } + err = loadfirmware(cif, chip->fw_entry->data, chip->fw_entry->size); + if (err) { + snd_printk(KERN_ERR + "Riptide: Could not load firmware %d\n", err); + return err; + } + + chip->firmware = firmware; + + return 1; /* OK */ +} + static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip) { - int timeout, tries; union cmdret rptr = CMDRET_ZERO; - union firmware_version firmware; - int i, j, err, has_firmware; + int err, tries; if (!cif) return -EINVAL; @@ -1227,75 +1272,11 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip) cif->is_reset = 0; tries = RESET_TRIES; - has_firmware = 0; - while (has_firmware == 0 && tries-- > 0) { - for (i = 0; i < 2; i++) { - WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0); - WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0); - } - SET_GRESET(cif->hwport); - udelay(100); - UNSET_GRESET(cif->hwport); - udelay(100); - - for (timeout = 100000; --timeout; udelay(10)) { - if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport)) - break; - } - if (timeout == 0) { - snd_printk(KERN_ERR - "Riptide: device not ready, audio status: 0x%x ready: %d gerr: %d\n", - READ_AUDIO_STATUS(cif->hwport), - IS_READY(cif->hwport), IS_GERR(cif->hwport)); - return -EIO; - } else { - snd_printdd - ("Riptide: audio status: 0x%x ready: %d gerr: %d\n", - READ_AUDIO_STATUS(cif->hwport), - IS_READY(cif->hwport), IS_GERR(cif->hwport)); - } - - SEND_GETV(cif, &rptr); - for (i = 0; i < 4; i++) - firmware.ret.retwords[i] = rptr.retwords[i]; - - snd_printdd - ("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n", - firmware.firmware.ASIC, firmware.firmware.CODEC, - firmware.firmware.AUXDSP, firmware.firmware.PROG); - - for (j = 0; j < FIRMWARE_VERSIONS; j++) { - has_firmware = 1; - for (i = 0; i < 4; i++) { - if (firmware_versions[j].ret.retwords[i] != - firmware.ret.retwords[i]) - has_firmware = 0; - } - if (has_firmware) - break; - } - - if (chip != NULL && has_firmware == 0) { - snd_printdd("Writing Firmware\n"); - if (!chip->fw_entry) { - if ((err = - request_firmware(&chip->fw_entry, - "riptide.hex", - &chip->pci->dev)) != 0) { - snd_printk(KERN_ERR - "Riptide: Firmware not available %d\n", - err); - return -EIO; - } - } - err = loadfirmware(cif, chip->fw_entry->data, - chip->fw_entry->size); - if (err) - snd_printk(KERN_ERR - "Riptide: Could not load firmware %d\n", - err); - } - } + do { + err = try_to_load_firmware(cif, chip); + if (err < 0) + return err; + } while (!err && --tries); SEND_SACR(cif, 0, AC97_RESET); SEND_RACR(cif, AC97_RESET, &rptr); @@ -1337,11 +1318,6 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip) SET_AIE(cif->hwport); SET_AIACK(cif->hwport); cif->is_reset = 1; - if (chip) { - for (i = 0; i < 4; i++) - chip->firmware.ret.retwords[i] = - firmware.ret.retwords[i]; - } return 0; } @@ -2094,8 +2070,8 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) static int dev; struct snd_card *card; struct snd_riptide *chip; - unsigned short addr; - int err = 0; + unsigned short val; + int err; if (dev >= SNDRV_CARDS) return -ENODEV; @@ -2107,60 +2083,63 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) return err; - if ((err = snd_riptide_create(card, pci, &chip)) < 0) { - snd_card_free(card); - return err; - } + err = snd_riptide_create(card, pci, &chip); + if (err < 0) + goto error; card->private_data = chip; - if ((err = snd_riptide_pcm(chip, 0, NULL)) < 0) { - snd_card_free(card); - return err; - } - if ((err = snd_riptide_mixer(chip)) < 0) { - snd_card_free(card); - return err; - } - pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, LEGACY_ENABLE_ALL - | (opl3_port[dev] ? LEGACY_ENABLE_FM : 0) + err = snd_riptide_pcm(chip, 0, NULL); + if (err < 0) + goto error; + err = snd_riptide_mixer(chip); + if (err < 0) + goto error; + + val = LEGACY_ENABLE_ALL; + if (opl3_port[dev]) + val |= LEGACY_ENABLE_FM; #ifdef SUPPORT_JOYSTICK - | (joystick_port[dev] ? LEGACY_ENABLE_GAMEPORT : - 0) + if (joystick_port[dev]) + val |= LEGACY_ENABLE_GAMEPORT; #endif - | (mpu_port[dev] - ? (LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU) : - 0) - | ((chip->irq << 4) & 0xF0)); - if ((addr = mpu_port[dev]) != 0) { - pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, addr); - if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE, - addr, 0, chip->irq, 0, - &chip->rmidi)) < 0) + if (mpu_port[dev]) + val |= LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU; + val |= (chip->irq << 4) & 0xf0; + pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, val); + if (mpu_port[dev]) { + val = mpu_port[dev]; + pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, val); + err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE, + val, 0, chip->irq, 0, + &chip->rmidi); + if (err < 0) snd_printk(KERN_WARNING "Riptide: Can't Allocate MPU at 0x%x\n", - addr); + val); else - chip->mpuaddr = addr; + chip->mpuaddr = val; } - if ((addr = opl3_port[dev]) != 0) { - pci_write_config_word(chip->pci, PCI_EXT_FM_Base, addr); - if ((err = snd_opl3_create(card, addr, addr + 2, - OPL3_HW_RIPTIDE, 0, - &chip->opl3)) < 0) + if (opl3_port[dev]) { + val = opl3_port[dev]; + pci_write_config_word(chip->pci, PCI_EXT_FM_Base, val); + err = snd_opl3_create(card, val, val + 2, + OPL3_HW_RIPTIDE, 0, &chip->opl3); + if (err < 0) snd_printk(KERN_WARNING "Riptide: Can't Allocate OPL3 at 0x%x\n", - addr); + val); else { - chip->opladdr = addr; - if ((err = - snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL)) < 0) + chip->opladdr = val; + err = snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL); + if (err < 0) snd_printk(KERN_WARNING "Riptide: Can't Allocate OPL3-HWDEP\n"); } } #ifdef SUPPORT_JOYSTICK - if ((addr = joystick_port[dev]) != 0) { - pci_write_config_word(chip->pci, PCI_EXT_Game_Base, addr); - chip->gameaddr = addr; + if (joystick_port[dev]) { + val = joystick_port[dev]; + pci_write_config_word(chip->pci, PCI_EXT_Game_Base, val); + chip->gameaddr = val; } #endif @@ -2178,13 +2157,16 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) chip->opladdr); #endif snd_riptide_proc_init(chip); - if ((err = snd_card_register(card)) < 0) { - snd_card_free(card); - return err; - } + err = snd_card_register(card); + if (err < 0) + goto error; pci_set_drvdata(pci, card); dev++; return 0; + + error: + snd_card_free(card); + return err; } static void __devexit snd_card_riptide_remove(struct pci_dev *pci) -- cgit v1.2.1 From db1005ec6ff8f250bccbc87387a955078891bfe4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 13:55:42 +0200 Subject: ALSA: riptide - Fix joystick resource handling The current code doesn't handle the multiple gameports properly, and uses unnecessary global static variables to store the data. This patch changes the probe / remove routines to use the driver data assigned to the dedicated pci device, and adds the support of multiple devices. Signed-off-by: Takashi Iwai --- sound/pci/riptide/riptide.c | 61 +++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 35 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 460be038f9c7..235a71e5ac8d 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -2014,14 +2014,12 @@ static int __devinit snd_riptide_mixer(struct snd_riptide *chip) } #ifdef SUPPORT_JOYSTICK -static int have_joystick; -static struct pci_dev *riptide_gameport_pci; -static struct gameport *riptide_gameport; static int __devinit snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id) { static int dev; + struct gameport *gameport; if (dev >= SNDRV_CARDS) return -ENODEV; @@ -2030,36 +2028,33 @@ snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id) return -ENOENT; } - if (joystick_port[dev]) { - riptide_gameport = gameport_allocate_port(); - if (riptide_gameport) { - if (!request_region - (joystick_port[dev], 8, "Riptide gameport")) { - snd_printk(KERN_WARNING - "Riptide: cannot grab gameport 0x%x\n", - joystick_port[dev]); - gameport_free_port(riptide_gameport); - riptide_gameport = NULL; - } else { - riptide_gameport_pci = pci; - riptide_gameport->io = joystick_port[dev]; - gameport_register_port(riptide_gameport); - } - } + if (!joystick_port[dev++]) + return 0; + + gameport = gameport_allocate_port(); + if (!gameport) + return -ENOMEM; + if (!request_region(joystick_port[dev], 8, "Riptide gameport")) { + snd_printk(KERN_WARNING + "Riptide: cannot grab gameport 0x%x\n", + joystick_port[dev]); + gameport_free_port(gameport); + return -EBUSY; } - dev++; + + gameport->io = joystick_port[dev]; + gameport_register_port(gameport); + pci_set_drvdata(pci, gameport); return 0; } static void __devexit snd_riptide_joystick_remove(struct pci_dev *pci) { - if (riptide_gameport) { - if (riptide_gameport_pci == pci) { - release_region(riptide_gameport->io, 8); - riptide_gameport_pci = NULL; - gameport_unregister_port(riptide_gameport); - riptide_gameport = NULL; - } + struct gameport *gameport = pci_get_drvdata(pci); + if (gameport) { + release_region(gameport->io, 8); + gameport_unregister_port(gameport); + pci_set_drvdata(pci, NULL); } } #endif @@ -2198,14 +2193,11 @@ static struct pci_driver joystick_driver = { static int __init alsa_card_riptide_init(void) { int err; - if ((err = pci_register_driver(&driver)) < 0) + err = pci_register_driver(&driver); + if (err < 0) return err; #if defined(SUPPORT_JOYSTICK) - if (pci_register_driver(&joystick_driver) < 0) { - have_joystick = 0; - snd_printk(KERN_INFO "no joystick found\n"); - } else - have_joystick = 1; + pci_register_driver(&joystick_driver); #endif return 0; } @@ -2214,8 +2206,7 @@ static void __exit alsa_card_riptide_exit(void) { pci_unregister_driver(&driver); #if defined(SUPPORT_JOYSTICK) - if (have_joystick) - pci_unregister_driver(&joystick_driver); + pci_unregister_driver(&joystick_driver); #endif } -- cgit v1.2.1 From 86d190e77c44cb057742dcc871b12ebd4633c387 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 15:18:58 +0200 Subject: ALSA: hda - Minor clean up of patch_sigmatel.c - Remove unneeded semicolons - Introduce spec->gpio_led to specify the GPIO bit for LED control Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 48 ++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 17310814f121..26d8707173b2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -193,6 +193,7 @@ struct sigmatel_spec { unsigned int gpio_dir; unsigned int gpio_data; unsigned int gpio_mute; + unsigned int gpio_led; /* stream */ unsigned int stream_delay; @@ -4651,22 +4652,13 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct sigmatel_spec *spec = codec->spec; - unsigned int gpio_bit = 0; /* gets rid of compiler warning */ - - switch (spec->board_config) { - case STAC_HP_DV4_1222NR: - gpio_bit = 0x01; - break; - case STAC_HP_HDX: - gpio_bit = 0x08; - } if (nid == 0x10) { if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) - spec->gpio_data &= ~gpio_bit; /* orange */ + spec->gpio_data |= spec->gpio_led; /* white */ else - spec->gpio_data |= gpio_bit; /* white */ + spec->gpio_data &= ~spec->gpio_led; /* orange */ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, @@ -5352,14 +5344,7 @@ again: */ spec->num_smuxes = 1; spec->num_dmuxes = 1; -#ifdef CONFIG_SND_HDA_POWER_SAVE - /* This controls MUTE LED */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - spec->gpio_data |= 0x01; - codec->patch_ops.check_power_status = - stac92xx_hp_check_power_status; -#endif + spec->gpio_led = 0x01; /* fallthrough */ case STAC_HP_DV5: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); @@ -5369,22 +5354,21 @@ again: spec->num_dmics = 1; spec->num_dmuxes = 1; spec->num_smuxes = 1; - /* - * For controlling MUTE LED on HP HDX16/HDX18 notebooks, - * the CONFIG_SND_HDA_POWER_SAVE is needed to be set. - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE /* orange/white mute led on GPIO3, orange=0, white=1 */ - spec->gpio_mask |= 0x08; - spec->gpio_dir |= 0x08; - spec->gpio_data |= 0x08; /* set to white */ + spec->gpio_led = 0x08; + break; + } +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (spec->gpio_led) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; /* register check_power_status callback. */ codec->patch_ops.check_power_status = - stac92xx_hp_check_power_status; + stac92xx_hp_check_power_status; + } #endif - break; - }; spec->multiout.dac_nids = spec->dac_nids; if (spec->dinput_mux) @@ -5409,7 +5393,7 @@ again: codec->proc_widget_hook = stac92hd7x_proc_hook; return 0; -}; +} static int patch_stac922x(struct hda_codec *codec) { @@ -5564,7 +5548,7 @@ static int patch_stac927x(struct hda_codec *codec) /* correct the device field to SPDIF out */ snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); break; - }; + } /* configure the analog microphone on some laptops */ snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); /* correct the front output jack as a hp out */ -- cgit v1.2.1 From 8174086167d43d0fd7b21928074145ae1d15bbab Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 15:22:00 +0200 Subject: ALSA: hda - Allow concurrent RIRB access in single_cmd mode In the single_cmd mode, the current driver code doesn't do any update for RIRB just for any safety reason. But, actually the RIRB and single_cmd mode don't conflict. Unsolicited events can be delivered even while using the single_cmd mode. This patch allows the handling of unsolicited events with single_cmd mode, just always checking RIRB independent from single_cmd flag. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 82d5218a0125..01d8d97dca4f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -901,8 +901,7 @@ static void azx_init_chip(struct azx *chip) azx_int_enable(chip); /* initialize the codec command I/O */ - if (!chip->single_cmd) - azx_init_cmd_io(chip); + azx_init_cmd_io(chip); /* program the position buffer */ azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); @@ -1018,7 +1017,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* clear rirb int */ status = azx_readb(chip, RIRBSTS); if (status & RIRB_INT_MASK) { - if (!chip->single_cmd && (status & RIRB_INT_RESPONSE)) + if (status & RIRB_INT_RESPONSE) azx_update_rirb(chip); azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); } @@ -2338,11 +2337,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, goto errout; } /* allocate CORB/RIRB */ - if (!chip->single_cmd) { - err = azx_alloc_cmd_io(chip); - if (err < 0) - goto errout; - } + err = azx_alloc_cmd_io(chip); + if (err < 0) + goto errout; /* initialize streams */ azx_init_stream(chip); -- cgit v1.2.1 From aa2936f5fe060e95ae06685149645b234085a468 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 16:07:57 +0200 Subject: ALSA: hda - Support sync after writing a verb This patch adds a debug mode to make the codec communication synchronous. Define SND_HDA_SUPPORT_SYNC_WRITE in hda_codec.c, and the call of snd_hda_codec_write*() will become synchronous, i.e. wait for the reply from the codec at each time issuing a verb. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 113 +++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 52 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 77385de01744..d1d5fb9d7afb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -158,6 +158,38 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, return val; } +/* + * Send and receive a verb + */ +static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, + unsigned int *res) +{ + struct hda_bus *bus = codec->bus; + int err, repeated = 0; + + if (res) + *res = -1; + snd_hda_power_up(codec); + mutex_lock(&bus->cmd_mutex); + again: + err = bus->ops.command(bus, cmd); + if (!err) { + if (res) { + *res = bus->ops.get_response(bus); + if (*res == -1 && bus->rirb_error) { + if (repeated++ < 1) { + snd_printd(KERN_WARNING "hda_codec: " + "Trying verb 0x%08x again\n", cmd); + goto again; + } + } + } + } + mutex_unlock(&bus->cmd_mutex); + snd_hda_power_down(codec); + return err; +} + /** * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec @@ -174,31 +206,18 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { - struct hda_bus *bus = codec->bus; - unsigned int cmd, res; - int repeated = 0; - - cmd = make_codec_cmd(codec, nid, direct, verb, parm); - snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); - again: - if (!bus->ops.command(bus, cmd)) { - res = bus->ops.get_response(bus); - if (res == -1 && bus->rirb_error) { - if (repeated++ < 1) { - snd_printd(KERN_WARNING "hda_codec: " - "Trying verb 0x%08x again\n", cmd); - goto again; - } - } - } else - res = (unsigned int)-1; - mutex_unlock(&bus->cmd_mutex); - snd_hda_power_down(codec); + unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm); + unsigned int res; + codec_exec_verb(codec, cmd, &res); return res; } EXPORT_SYMBOL_HDA(snd_hda_codec_read); +/* Define the below to send and receive verbs synchronously. + * If you often get any codec communication errors, this is worth to try. + */ +/* #define SND_HDA_SUPPORT_SYNC_WRITE */ + /** * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec @@ -214,17 +233,13 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read); int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { - struct hda_bus *bus = codec->bus; + unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); +#ifdef SND_HDA_SUPPORT_SYNC_WRITE unsigned int res; - int err; - - res = make_codec_cmd(codec, nid, direct, verb, parm); - snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); - err = bus->ops.command(bus, res); - mutex_unlock(&bus->cmd_mutex); - snd_hda_power_down(codec); - return err; + return codec_exec_verb(codec, cmd, &res); +#else + return codec_exec_verb(codec, cmd, NULL); +#endif } EXPORT_SYMBOL_HDA(snd_hda_codec_write); @@ -2281,28 +2296,22 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { - struct hda_bus *bus = codec->bus; - unsigned int res; - int err; + int err = snd_hda_codec_write(codec, nid, direct, verb, parm); + struct hda_cache_head *c; + u32 key; - res = make_codec_cmd(codec, nid, direct, verb, parm); - snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); - err = bus->ops.command(bus, res); - if (!err) { - struct hda_cache_head *c; - u32 key; - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - c = get_alloc_hash(&codec->cmd_cache, key); - if (c) - c->val = parm; - } - mutex_unlock(&bus->cmd_mutex); - snd_hda_power_down(codec); - return err; + if (err < 0) + return err; + /* parm may contain the verb stuff for get/set amp */ + verb = verb | (parm >> 8); + parm &= 0xff; + key = build_cmd_cache_key(nid, verb); + mutex_lock(&codec->bus->cmd_mutex); + c = get_alloc_hash(&codec->cmd_cache, key); + if (c) + c->val = parm; + mutex_unlock(&codec->bus->cmd_mutex); + return 0; } EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); -- cgit v1.2.1 From aae80dc24aeddec4e2b6e182a43491942f8667d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2009 18:35:27 +0200 Subject: ALSA: ctxfi - Add missing module parameter definitions Added missing module_param*() and MODULE_PARM*(). Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/xfi.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 19310ed26e88..e31e29e4ec7e 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -23,13 +23,22 @@ MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); static unsigned int reference_rate = 48000; static unsigned int multiple = 2; +MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)"); module_param(reference_rate, uint, S_IRUGO); +MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)"); module_param(multiple, uint, S_IRUGO); 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_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver"); + static struct pci_device_id ct_pci_dev_ids[] = { /* only X-Fi is supported, so... */ { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1) }, -- cgit v1.2.1 From 1812e67c7410c8d0d57f14a3dc81a99bd5b30e3e Mon Sep 17 00:00:00 2001 From: Tony Vroon Date: Wed, 27 May 2009 21:00:41 +0100 Subject: ALSA: hda - Compaq Presario CQ60 patching for Conexant A docking mic control is shown by default. The Compaq Presario CQ60 laptop has no docking connector, so designate it as a CXT5051_HP model. This makes the phantom mixer slider disappear. Signed-off-by: Tony Vroon Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 56ce19e68cb5..4fcbe21829ab 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1848,6 +1848,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = { static struct snd_pci_quirk cxt5051_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), + SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), -- cgit v1.2.1 From b05a7d4fed7e51dca37d0a31baf1466de30b1f01 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 28 May 2009 11:59:12 +0200 Subject: ALSA: hda - Always sync writes in single_cmd mode In the single_cmd mode, the hardware cannot store the multiple replies like on RIRB, thus each verb has to sync and wait for the response no matter whether the return value is needed or not. Otherwise it may result in a wrong return value from the previous verb. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 01d8d97dca4f..31a695e6e37d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -673,6 +673,27 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) * I left the codes, however, for debugging/testing purposes. */ +/* receive a response */ +static int azx_single_wait_for_response(struct azx *chip) +{ + int timeout = 50; + + while (timeout--) { + /* check IRV busy bit */ + if (azx_readw(chip, IRS) & ICH6_IRS_VALID) { + /* reuse rirb.res as the response return value */ + chip->rirb.res = azx_readl(chip, IR); + return 0; + } + udelay(1); + } + if (printk_ratelimit()) + snd_printd(SFX "get_response timeout: IRS=0x%x\n", + azx_readw(chip, IRS)); + chip->rirb.res = -1; + return -EIO; +} + /* send a command */ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) { @@ -688,7 +709,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) azx_writel(chip, IC, val); azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY); - return 0; + return azx_single_wait_for_response(chip); } udelay(1); } @@ -702,18 +723,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) static unsigned int azx_single_get_response(struct hda_bus *bus) { struct azx *chip = bus->private_data; - int timeout = 50; - - while (timeout--) { - /* check IRV busy bit */ - if (azx_readw(chip, IRS) & ICH6_IRS_VALID) - return azx_readl(chip, IR); - udelay(1); - } - if (printk_ratelimit()) - snd_printd(SFX "get_response timeout: IRS=0x%x\n", - azx_readw(chip, IRS)); - return (unsigned int)-1; + return chip->rirb.res; } /* -- cgit v1.2.1 From b21fadb9c1852c91622ca1dccfeb144bc535e36e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 28 May 2009 12:26:15 +0200 Subject: ALSA: hda - Add more register bits definitions Added some missing register bits definitions to reduce magic numbers. Also renamed some to follow the names on the datasheet. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 31a695e6e37d..f63bc6510e0f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -138,14 +138,23 @@ MODULE_DESCRIPTION("Intel HDA driver"); * registers */ #define ICH6_REG_GCAP 0x00 +#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */ +#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */ +#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */ +#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */ +#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */ #define ICH6_REG_VMIN 0x02 #define ICH6_REG_VMAJ 0x03 #define ICH6_REG_OUTPAY 0x04 #define ICH6_REG_INPAY 0x06 #define ICH6_REG_GCTL 0x08 +#define ICH6_GCTL_RESET (1 << 1) /* controller reset */ +#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */ +#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ #define ICH6_REG_WAKEEN 0x0c #define ICH6_REG_STATESTS 0x0e #define ICH6_REG_GSTS 0x10 +#define ICH6_GSTS_FSTS (1 << 1) /* flush status */ #define ICH6_REG_INTCTL 0x20 #define ICH6_REG_INTSTS 0x24 #define ICH6_REG_WALCLK 0x30 @@ -153,17 +162,27 @@ MODULE_DESCRIPTION("Intel HDA driver"); #define ICH6_REG_CORBLBASE 0x40 #define ICH6_REG_CORBUBASE 0x44 #define ICH6_REG_CORBWP 0x48 -#define ICH6_REG_CORBRP 0x4A +#define ICH6_REG_CORBRP 0x4a +#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */ #define ICH6_REG_CORBCTL 0x4c +#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */ +#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ #define ICH6_REG_CORBSTS 0x4d +#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */ #define ICH6_REG_CORBSIZE 0x4e #define ICH6_REG_RIRBLBASE 0x50 #define ICH6_REG_RIRBUBASE 0x54 #define ICH6_REG_RIRBWP 0x58 +#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */ #define ICH6_REG_RINTCNT 0x5a #define ICH6_REG_RIRBCTL 0x5c +#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ +#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */ +#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ #define ICH6_REG_RIRBSTS 0x5d +#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */ +#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */ #define ICH6_REG_RIRBSIZE 0x5e #define ICH6_REG_IC 0x60 @@ -260,16 +279,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ #define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ -/* GCTL unsolicited response enable bit */ -#define ICH6_GCTL_UREN (1<<8) - -/* GCTL reset bit */ -#define ICH6_GCTL_RESET (1<<0) - -/* CORB/RIRB control, read/write pointer */ -#define ICH6_RBCTL_DMA_EN 0x02 /* enable DMA */ -#define ICH6_RBCTL_IRQ_EN 0x01 /* enable IRQ */ -#define ICH6_RBRWP_CLR 0x8000 /* read/write pointer clear */ /* below are so far hardcoded - should read registers in future */ #define ICH6_MAX_CORB_ENTRIES 256 #define ICH6_MAX_RIRB_ENTRIES 256 @@ -515,9 +524,9 @@ static void azx_init_cmd_io(struct azx *chip) /* set the corb write pointer to 0 */ azx_writew(chip, CORBWP, 0); /* reset the corb hw read pointer */ - azx_writew(chip, CORBRP, ICH6_RBRWP_CLR); + azx_writew(chip, CORBRP, ICH6_CORBRP_RST); /* enable corb dma */ - azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN); + azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN); /* RIRB set up */ chip->rirb.addr = chip->rb.addr + 2048; @@ -529,7 +538,7 @@ static void azx_init_cmd_io(struct azx *chip) /* set the rirb size to 256 entries (ULI requires explicitly) */ azx_writeb(chip, RIRBSIZE, 0x02); /* reset the rirb hw write pointer */ - azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR); + azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST); /* set N=1, get RIRB response interrupt for new entry */ azx_writew(chip, RINTCNT, 1); /* enable rirb dma and response irq */ @@ -796,7 +805,7 @@ static int azx_reset(struct azx *chip) } /* Accept unsolicited responses */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN); + azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL); /* detect codecs */ if (!chip->codec_mask) { @@ -2284,10 +2293,10 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, /* ATI chips seems buggy about 64bit DMA addresses */ if (chip->driver_type == AZX_DRIVER_ATI) - gcap &= ~0x01; + gcap &= ~ICH6_GCAP_64OK; /* allow 64bit DMA address if supported by H/W */ - if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) + if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); else { pci_set_dma_mask(pci, DMA_BIT_MASK(32)); -- cgit v1.2.1 From 3fd43858c7937801134bd70ef1d411e44f9c0c1c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 28 May 2009 14:12:27 +0200 Subject: ALSA: au88x0: fix .pointer callback Appearently, the used mask in the .pointer callback is invalid. It should be in period_bytes range. The period_bytes is pow(2), so simple bitwise operation is used. Idea was taken from ALSA bug#4455. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/au88x0/au88x0_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 3906f5afe27a..f84bb19c7fdd 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -1255,8 +1255,8 @@ static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma) int temp; temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2)); - temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK); - return (temp); + temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1)); + return temp; } static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma) @@ -1504,8 +1504,7 @@ static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma) int temp; temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)); - //temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK)); - temp = (temp & POS_MASK) + ((dma->period_virt) * (dma->period_bytes)); + temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1)); return temp; } -- cgit v1.2.1 From e9ab33d03eb721a632214c0bbaa18652de88aa2d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 28 May 2009 14:20:00 +0200 Subject: ALSA: au88x0: fix wrong period_elapsed() call The period_elapsed() call should be called when position moves. The idea was taken from ALSA bug#4455. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/au88x0/au88x0_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index f84bb19c7fdd..23f49f356e0f 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2440,7 +2440,8 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) spin_lock(&vortex->lock); for (i = 0; i < NR_ADB; i++) { if (vortex->dma_adb[i].fifo_status == FIFO_START) { - if (vortex_adbdma_bufshift(vortex, i)) ; + if (!vortex_adbdma_bufshift(vortex, i)) + continue; spin_unlock(&vortex->lock); snd_pcm_period_elapsed(vortex->dma_adb[i]. substream); -- cgit v1.2.1 From 0528c7494e67c30329d086df141d2dd691f01556 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 26 May 2009 17:07:52 +0200 Subject: ALSA: clean up the logic for building sequencer modules Instead of mangling the CONFIG_* variables in the makefiles over and over, set a few helper variables in Kconfig. Signed-off-by: Michal Marek Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/Makefile | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile index cf2d5636d8be..fc5591e7777e 100644 --- a/sound/pci/emu10k1/Makefile +++ b/sound/pci/emu10k1/Makefile @@ -9,15 +9,7 @@ snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o snd-emu10k1x-objs := emu10k1x.o -# -# this function returns: -# "m" - CONFIG_SND_SEQUENCER is m -# - CONFIG_SND_SEQUENCER is undefined -# otherwise parameter #1 value -# -sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) - # Toplevel Module Dependency obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o -obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emu10k1-synth.o +obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emu10k1-synth.o obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o -- cgit v1.2.1 From ba84bfcd2b6fdc5a9ac53a4ab103088c99f84a12 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 30 May 2009 08:59:03 +0200 Subject: ALSA: hda - Fix reverted LED setup for HP The commit 86d190e77c44cb057742dcc871b12ebd4633c387 reverted the bit flip of LED GPIO for HP DX and DV4-1222nr. Fixed now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a1b4c9496d47..a915f404f654 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4666,9 +4666,9 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, if (nid == 0x10) { if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) - spec->gpio_data |= spec->gpio_led; /* white */ - else spec->gpio_data &= ~spec->gpio_led; /* orange */ + else + spec->gpio_data |= spec->gpio_led; /* white */ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, -- cgit v1.2.1 From 8a933ece41a59ce077eeffe5b9bf08b14d173c58 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 31 May 2009 09:28:12 +0200 Subject: ALSA: hda - Fix a typo in the previous patch ICH6_GCTL_RESET was wrongly set to another bit by the commit b21fadb9c1852c91622ca1dccfeb144bc535e36e. This caused a problem when the codec needs really a reset (e.g. recovering from the communication error at probe). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index f63bc6510e0f..b063d0e3d325 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -148,7 +148,7 @@ MODULE_DESCRIPTION("Intel HDA driver"); #define ICH6_REG_OUTPAY 0x04 #define ICH6_REG_INPAY 0x06 #define ICH6_REG_GCTL 0x08 -#define ICH6_GCTL_RESET (1 << 1) /* controller reset */ +#define ICH6_GCTL_RESET (1 << 0) /* controller reset */ #define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */ #define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ #define ICH6_REG_WAKEEN 0x0c -- cgit v1.2.1 From a3d6ab9723060e8d67e28a8d9142e6d08953d07b Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Mon, 1 Jun 2009 16:37:28 +0800 Subject: ALSA: hda - Support NVIDIA 8 channel HDMI audio Support 8 channel HDMI audio for MCP78/7A Signed-off-by: Wei Ni Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_nvhdmi.c | 279 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 248 insertions(+), 31 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index d57d8132a06e..f5792e2eea82 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -35,9 +35,28 @@ struct nvhdmi_spec { struct hda_pcm pcm_rec; }; +#define Nv_VERB_SET_Channel_Allocation 0xF79 +#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A +#define Nv_VERB_SET_Audio_Protection_On 0xF98 +#define Nv_VERB_SET_Audio_Protection_Off 0xF99 + +#define Nv_Master_Convert_nid 0x04 +#define Nv_Master_Pin_nid 0x05 + +static hda_nid_t nvhdmi_convert_nids[4] = { + /*front, rear, clfe, rear_surr */ + 0x6, 0x8, 0xa, 0xc, +}; + static struct hda_verb nvhdmi_basic_init[] = { + /* set audio protect on */ + { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, /* enable digital output on pin widget */ - { 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, {} /* terminator */ }; @@ -66,48 +85,205 @@ static int nvhdmi_init(struct hda_codec *codec) * Digital out */ static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) + struct hda_codec *codec, + struct snd_pcm_substream *substream) { struct nvhdmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_open(codec, &spec->multiout); } -static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { struct nvhdmi_spec *spec = codec->spec; + int i; + + snd_hda_codec_write(codec, Nv_Master_Convert_nid, + 0, AC_VERB_SET_CHANNEL_STREAMID, 0); + for (i = 0; i < 4; i++) { + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0, + AC_VERB_SET_STREAM_FORMAT, 0); + } + return snd_hda_multi_out_dig_close(codec, &spec->multiout); } -static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct nvhdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int chs; + unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id; + int i; + + mutex_lock(&codec->spdif_mutex); + + chs = substream->runtime->channels; + chan = chs ? (chs - 1) : 1; + + switch (chs) { + default: + case 0: + case 2: + chanmask = 0x00; + break; + case 4: + chanmask = 0x08; + break; + case 6: + chanmask = 0x0b; + break; + case 8: + chanmask = 0x13; + break; + } + dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT; + dataDCC2 = 0x2; + + /* set the Audio InforFrame Channel Allocation */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Channel_Allocation, chanmask); + + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ + if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + Nv_Master_Convert_nid, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + + /* set the stream id */ + snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); + + /* set the stream format */ + snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, + AC_VERB_SET_STREAM_FORMAT, format); + + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + Nv_Master_Convert_nid, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & 0xff); + snd_hda_codec_write(codec, + Nv_Master_Convert_nid, + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + + for (i = 0; i < 4; i++) { + if (chs == 2) + channel_id = 0; + else + channel_id = i * 2; + + /* turn off SPDIF once; + *otherwise the IEC958 bits won't be updated + */ + if (codec->spdif_status_reset && + (codec->spdif_ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_convert_nids[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); + /* set the stream id */ + snd_hda_codec_write(codec, + nvhdmi_convert_nids[i], + 0, + AC_VERB_SET_CHANNEL_STREAMID, + (stream_tag << 4) | channel_id); + /* set the stream format */ + snd_hda_codec_write(codec, + nvhdmi_convert_nids[i], + 0, + AC_VERB_SET_STREAM_FORMAT, + format); + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && + (codec->spdif_ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_convert_nids[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + codec->spdif_ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_convert_nids[i], + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + } + + /* set the Audio Info Frame Checksum */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Info_Frame_Checksum, + (0x71 - chan - chanmask)); + + mutex_unlock(&codec->spdif_mutex); + return 0; +} + +static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { struct nvhdmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); + format, substream); } -static struct hda_pcm_stream nvhdmi_pcm_digital_playback = { +static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = Nv_Master_Convert_nid, + .rates = SNDRV_PCM_RATE_48000, + .maxbps = 16, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .ops = { + .open = nvhdmi_dig_playback_pcm_open, + .close = nvhdmi_dig_playback_pcm_close_8ch, + .prepare = nvhdmi_dig_playback_pcm_prepare_8ch + }, +}; + +static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .nid = 0x4, /* NID to query formats and rates and setup streams */ + .nid = Nv_Master_Convert_nid, .rates = SNDRV_PCM_RATE_48000, .maxbps = 16, .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { .open = nvhdmi_dig_playback_pcm_open, - .close = nvhdmi_dig_playback_pcm_close, - .prepare = nvhdmi_dig_playback_pcm_prepare + .close = nvhdmi_dig_playback_pcm_close_2ch, + .prepare = nvhdmi_dig_playback_pcm_prepare_2ch }, }; -static int nvhdmi_build_pcms(struct hda_codec *codec) +static int nvhdmi_build_pcms_8ch(struct hda_codec *codec) { struct nvhdmi_spec *spec = codec->spec; struct hda_pcm *info = &spec->pcm_rec; @@ -117,7 +293,24 @@ static int nvhdmi_build_pcms(struct hda_codec *codec) info->name = "NVIDIA HDMI"; info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] + = nvhdmi_pcm_digital_playback_8ch; + + return 0; +} + +static int nvhdmi_build_pcms_2ch(struct hda_codec *codec) +{ + struct nvhdmi_spec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "NVIDIA HDMI"; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] + = nvhdmi_pcm_digital_playback_2ch; return 0; } @@ -127,14 +320,40 @@ static void nvhdmi_free(struct hda_codec *codec) kfree(codec->spec); } -static struct hda_codec_ops nvhdmi_patch_ops = { +static struct hda_codec_ops nvhdmi_patch_ops_8ch = { + .build_controls = nvhdmi_build_controls, + .build_pcms = nvhdmi_build_pcms_8ch, + .init = nvhdmi_init, + .free = nvhdmi_free, +}; + +static struct hda_codec_ops nvhdmi_patch_ops_2ch = { .build_controls = nvhdmi_build_controls, - .build_pcms = nvhdmi_build_pcms, + .build_pcms = nvhdmi_build_pcms_2ch, .init = nvhdmi_init, .free = nvhdmi_free, }; -static int patch_nvhdmi(struct hda_codec *codec) +static int patch_nvhdmi_8ch(struct hda_codec *codec) +{ + struct nvhdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 8; + spec->multiout.dig_out_nid = Nv_Master_Convert_nid; + + codec->patch_ops = nvhdmi_patch_ops_8ch; + + return 0; +} + +static int patch_nvhdmi_2ch(struct hda_codec *codec) { struct nvhdmi_spec *spec; @@ -144,13 +363,11 @@ static int patch_nvhdmi(struct hda_codec *codec) codec->spec = spec; - spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.num_dacs = 0; /* no analog */ spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital, - * seems to be unused in pure-digital - * case. */ + spec->multiout.dig_out_nid = Nv_Master_Convert_nid; - codec->patch_ops = nvhdmi_patch_ops; + codec->patch_ops = nvhdmi_patch_ops_2ch; return 0; } @@ -159,11 +376,11 @@ static int patch_nvhdmi(struct hda_codec *codec) * patch entries */ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { - { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, - { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, - { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi }, - { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi }, - { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi }, + { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, + { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, + { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch }, + { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, + { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, {} /* terminator */ }; -- cgit v1.2.1 From 7c922de709b90badc19705e4f998e6d5b44c419b Mon Sep 17 00:00:00 2001 From: Nickolas Lloyd Date: Mon, 1 Jun 2009 11:12:29 +0200 Subject: ALSA: hda - Jack Mode changes for Sigmatel boards This patch changes Line In as Out Switch and Mic In as Out Switch to enums for consistency, and causes all mic and line in ports to be probed and controls to be added appropriately. Signed-off-by: Nickolas Lloyd Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 219 ++++++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 79 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a915f404f654..48f4a36c4813 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -670,62 +670,6 @@ static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid) return vref; } -static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int new_vref; - unsigned int error; - - if (ucontrol->value.enumerated.item[0] == 0) - new_vref = AC_PINCTL_VREF_80; - else if (ucontrol->value.enumerated.item[0] == 1) - new_vref = AC_PINCTL_VREF_GRD; - else - new_vref = AC_PINCTL_VREF_HIZ; - - if (new_vref != stac92xx_vref_get(codec, kcontrol->private_value)) { - error = stac92xx_vref_set(codec, - kcontrol->private_value, new_vref); - return error; - } - - return 0; -} - -static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int vref = stac92xx_vref_get(codec, kcontrol->private_value); - if (vref == AC_PINCTL_VREF_80) - ucontrol->value.enumerated.item[0] = 0; - else if (vref == AC_PINCTL_VREF_GRD) - ucontrol->value.enumerated.item[0] = 1; - else if (vref == AC_PINCTL_VREF_HIZ) - ucontrol->value.enumerated.item[0] = 2; - - return 0; -} - -static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static char *texts[] = { - "Mic In", "Line In", "Line Out" - }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = 3; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= 3) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; -} - static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -2652,7 +2596,8 @@ static int stac92xx_build_pcms(struct hda_codec *codec) return 0; } -static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid) +static unsigned int stac92xx_get_default_vref(struct hda_codec *codec, + hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; @@ -2706,15 +2651,108 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, return 1; } -#define stac92xx_io_switch_info snd_ctl_boolean_mono_info +static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int i; + static char *texts[] = { + "Mic In", "Line In", "Line Out" + }; + + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value; + + if (nid == spec->mic_switch || nid == spec->line_switch) + i = 3; + else + i = 2; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = i; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= i) + uinfo->value.enumerated.item = i-1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref = stac92xx_vref_get(codec, nid); + + if (vref == stac92xx_get_default_vref(codec, nid)) + ucontrol->value.enumerated.item[0] = 0; + else if (vref == AC_PINCTL_VREF_GRD) + ucontrol->value.enumerated.item[0] = 1; + else if (vref == AC_PINCTL_VREF_HIZ) + ucontrol->value.enumerated.item[0] = 2; + + return 0; +} + +static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int new_vref = 0; + unsigned int error; + hda_nid_t nid = kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] == 0) + new_vref = stac92xx_get_default_vref(codec, nid); + else if (ucontrol->value.enumerated.item[0] == 1) + new_vref = AC_PINCTL_VREF_GRD; + else if (ucontrol->value.enumerated.item[0] == 2) + new_vref = AC_PINCTL_VREF_HIZ; + else + return 0; + + if (new_vref != stac92xx_vref_get(codec, nid)) { + error = stac92xx_vref_set(codec, nid, new_vref); + return error; + } + + return 0; +} + +static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2]; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + if (kcontrol->private_value == spec->line_switch) + texts[0] = "Line In"; + else + texts[0] = "Mic In"; + texts[1] = "Line Out"; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = 2; + uinfo->count = 1; + + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - int io_idx = kcontrol-> private_value & 0xff; + hda_nid_t nid = kcontrol->private_value; + int io_idx = (nid == spec->mic_switch) ? 1 : 0; - ucontrol->value.integer.value[0] = spec->io_switch[io_idx]; + ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx]; return 0; } @@ -2722,9 +2760,9 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value >> 8; - int io_idx = kcontrol-> private_value & 0xff; - unsigned short val = !!ucontrol->value.integer.value[0]; + hda_nid_t nid = kcontrol->private_value; + int io_idx = (nid == spec->mic_switch) ? 1 : 0; + unsigned short val = !!ucontrol->value.enumerated.item[0]; spec->io_switch[io_idx] = val; @@ -2733,7 +2771,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ else { unsigned int pinctl = AC_PINCTL_IN_EN; if (io_idx) /* set VREF for mic */ - pinctl |= stac92xx_get_vref(codec, nid); + pinctl |= stac92xx_get_default_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } @@ -2891,6 +2929,34 @@ static struct snd_kcontrol_new stac_input_src_temp = { .put = stac92xx_mux_enum_put, }; +static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, + hda_nid_t nid, int idx) +{ + int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int control = 0; + struct sigmatel_spec *spec = codec->spec; + char name[22]; + + if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) { + if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD + && nid == spec->line_switch) + control = STAC_CTL_WIDGET_IO_SWITCH; + else if (snd_hda_query_pin_caps(codec, nid) + & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT)) + control = STAC_CTL_WIDGET_DC_BIAS; + else if (nid == spec->mic_switch) + control = STAC_CTL_WIDGET_IO_SWITCH; + } + + if (control) { + strcpy(name, auto_pin_cfg_labels[idx]); + return stac92xx_add_control(codec->spec, control, + strcat(name, " Jack Mode"), nid); + } + + return 0; +} + static int stac92xx_add_input_source(struct sigmatel_spec *spec) { struct snd_kcontrol_new *knew; @@ -3253,7 +3319,9 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid; int err; + int idx; err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, spec->multiout.dac_nids, @@ -3270,20 +3338,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } - if (spec->line_switch) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, - "Line In as Output Switch", - spec->line_switch << 8); - if (err < 0) - return err; - } - - if (spec->mic_switch) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_DC_BIAS, - "Mic Jack Mode", - spec->mic_switch); - if (err < 0) - return err; + for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) { + nid = cfg->input_pins[idx]; + if (nid) { + err = stac92xx_add_jack_mode_control(codec, nid, idx); + if (err < 0) + return err; + } } return 0; @@ -4193,7 +4254,7 @@ static int stac92xx_init(struct hda_codec *codec) unsigned int pinctl, conf; if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { /* for mic pins, force to initialize */ - pinctl = stac92xx_get_vref(codec, nid); + pinctl = stac92xx_get_default_vref(codec, nid); pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); } else { -- cgit v1.2.1 From 0e4835c198e7dd9d814de25a17a1d6682107c394 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 25 May 2009 16:44:20 +0200 Subject: ALSA: hda-intel: improve initialization for ALC262_HP_BPC model Fix issues for 3 generations of HP workstations. The modest modifications do the following: 1. Change the second MIC from device 3 to device 1 2. Init the "boost" values to "0" by default From: John Brown Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index add920a925c7..d0786e9c3310 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10639,31 +10639,46 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = { {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */ + /* Input mixer1: only unmute Mic */ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, /* Input mixer2 */ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, /* Input mixer3 */ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))}, {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, -- cgit v1.2.1 From 92b9de8342c6ad0d930333851190ed25b88b190c Mon Sep 17 00:00:00 2001 From: Kacper Szczesniak Date: Tue, 2 Jun 2009 00:55:19 +0200 Subject: ALSA: hda - Macbook[Pro] 5 6ch support this is a patch against current snapshot that adds: 6 channels support for the MB5 model Signed-off-by: Kacper Szczesniak Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 80 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 15 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d0786e9c3310..12d6015e07fc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6266,6 +6266,34 @@ static struct hda_channel_mode alc885_mbp_6ch_modes[2] = { { 6, alc885_mbp_ch6_init }, }; +/* + * 2ch + * Speakers/Woofer/HP = Front + * LineIn = Input + */ +static struct hda_verb alc885_mb5_ch2_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } /* end */ +}; + +/* + * 6ch mode + * Speakers/HP = Front + * Woofer = LFE + * LineIn = Surround + */ +static struct hda_verb alc885_mb5_ch6_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + { } /* end */ +}; + +static struct hda_channel_mode alc885_mb5_6ch_modes[2] = { + { 2, alc885_mb5_ch2_init }, + { 6, alc885_mb5_ch6_init }, +}; /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b @@ -6310,10 +6338,14 @@ static struct snd_kcontrol_new alc885_mbp3_mixer[] = { }; static struct snd_kcontrol_new alc885_mb5_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0d, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Front Playback Switch", 0x0d, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("HP Playback Volume", 0x0f, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("HP Playback Switch", 0x0f, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), @@ -6322,6 +6354,7 @@ static struct snd_kcontrol_new alc885_mb5_mixer[] = { HDA_CODEC_VOLUME("Mic Boost", 0x19, 0x00, HDA_INPUT), { } /* end */ }; + static struct snd_kcontrol_new alc882_w2jc_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6551,22 +6584,39 @@ static struct hda_verb alc882_macpro_init_verbs[] = { /* Macbook 5,1 */ static struct hda_verb alc885_mb5_init_verbs[] = { + /* DACs */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Front mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* LineOut mixer */ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Front Pin: output 0 (0x0d) */ + /* Surround mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* LFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* HP mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin (0x0c) */ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* HP Pin: output 0 (0x0c) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* LFE Pin (0x0e) */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* HP Pin (0x0f) */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Front Mic pin: input vref at 80% */ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, @@ -6986,13 +7036,13 @@ static struct alc_config_preset alc882_presets[] = { .init_hook = alc885_mbp3_init_hook, }, [ALC885_MB5] = { - .mixers = { alc885_mb5_mixer }, + .mixers = { alc885_mb5_mixer, alc882_chmode_mixer }, .init_verbs = { alc885_mb5_init_verbs, alc880_gpio1_init_verbs }, .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mbp_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mbp_6ch_modes), + .channel_mode = alc885_mb5_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes), .input_mux = &mb5_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, -- cgit v1.2.1 From d22142aa1b23d64d01f87a94b5756ff6cbd1022f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ozan=20=C3=87a=C4=9Flayan?= Date: Mon, 1 Jun 2009 23:13:17 +0300 Subject: ALSA: hda - fix audio on LG R510 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, LG R510 is only able to produce sound on headphones, the internal speakers are not working. The user tested and confirmed that with model=Dell headphones, internal speakers and the microphone are working flawlessly. Tested-by: Serdar Soytetir Signed-off-by: Ozan Çağlayan Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 12d6015e07fc..56c2f8460409 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12111,6 +12111,7 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { ALC268_ACER_ASPIRE_ONE), SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL), + SND_PCI_QUIRK(0x1854, 0x1775, "LG R510", ALC268_DELL), SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), SND_PCI_QUIRK(0x103c, 0x30f1, "HP TX25xx series", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), -- cgit v1.2.1 From 8871e5b91518a47284b6bc2603b44dbc79c85446 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 01:02:50 +0200 Subject: ALSA: hda - Reorder and clean-up ALC268 quirk table Rearrange alc268_cfg_tbl[] in the order of vendor id, and group some entries using SND_PCI_QUIRK_MASK(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 56c2f8460409..ad6cc4218049 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12111,17 +12111,16 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { ALC268_ACER_ASPIRE_ONE), SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL), - SND_PCI_QUIRK(0x1854, 0x1775, "LG R510", ALC268_DELL), - SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x103c, 0x30f1, "HP TX25xx series", ALC268_TOSHIBA), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series", + ALC268_TOSHIBA), SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), - SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x1179, 0xff64, "TOSHIBA L305", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), + SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05", + ALC268_TOSHIBA), SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), - SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), + SND_PCI_QUIRK(0x1854, 0x1775, "LG R510", ALC268_DELL), {} }; -- cgit v1.2.1 From 8dd783304e6d0f7c2830365d63f75f08aa343e10 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 01:16:07 +0200 Subject: ALSA: hda - Add codec bus reset and verb-retry at critical errors Some machines machine cause a severe CORB/RIRB stall in certain weird conditions, such as PA access at the start up together with fglrx driver. This seems unable to be recovered without the controller reset. This patch allows the bus controller reset at critical errors so that the communication gets recovered again. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 32 ++++++++++++++++---------------- sound/pci/hda/hda_codec.h | 6 +++++- sound/pci/hda/hda_intel.c | 40 ++++++++++++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 23 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d1d5fb9d7afb..aa0e1c18b606 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -165,28 +165,29 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, unsigned int *res) { struct hda_bus *bus = codec->bus; - int err, repeated = 0; + int err; if (res) *res = -1; + again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - again: err = bus->ops.command(bus, cmd); - if (!err) { - if (res) { - *res = bus->ops.get_response(bus); - if (*res == -1 && bus->rirb_error) { - if (repeated++ < 1) { - snd_printd(KERN_WARNING "hda_codec: " - "Trying verb 0x%08x again\n", cmd); - goto again; - } - } - } - } + if (!err && res) + *res = bus->ops.get_response(bus); mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); + if (res && *res == -1 && bus->rirb_error) { + if (bus->response_reset) { + snd_printd("hda_codec: resetting BUS due to " + "fatal communication error\n"); + bus->ops.bus_reset(bus); + } + goto again; + } + /* clear reset-flag when the communication gets recovered */ + if (!err) + bus->response_reset = 0; return err; } @@ -3894,11 +3895,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); /** * snd_hda_suspend - suspend the codecs * @bus: the HDA bus - * @state: suspsend state * * Returns 0 if successful. */ -int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) +int snd_hda_suspend(struct hda_bus *bus) { struct hda_codec *codec; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c5bd40f77bb9..f5fa5d1f223c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -574,6 +574,8 @@ struct hda_bus_ops { /* attach a PCM stream */ int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, struct hda_pcm *pcm); + /* reset bus for retry verb */ + void (*bus_reset)(struct hda_bus *bus); #ifdef CONFIG_SND_HDA_POWER_SAVE /* notify power-up/down from codec to controller */ void (*pm_notify)(struct hda_bus *bus); @@ -624,6 +626,8 @@ struct hda_bus { unsigned int needs_damn_long_delay :1; unsigned int shutdown :1; /* being unloaded */ unsigned int rirb_error:1; /* error in codec communication */ + unsigned int response_reset:1; /* controller was reset */ + unsigned int in_reset:1; /* during reset operation */ }; /* @@ -907,7 +911,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); * power management */ #ifdef CONFIG_PM -int snd_hda_suspend(struct hda_bus *bus, pm_message_t state); +int snd_hda_suspend(struct hda_bus *bus); int snd_hda_resume(struct hda_bus *bus); #endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b063d0e3d325..44f9a0aa20c5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -661,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) return -1; } - snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): " - "last cmd=0x%08x\n", chip->last_cmd); - /* re-initialize CORB/RIRB */ - spin_lock_irq(&chip->reg_lock); + /* a fatal communication error; need either to reset or to fallback + * to the single_cmd mode + */ bus->rirb_error = 1; + if (!bus->response_reset && !bus->in_reset) { + bus->response_reset = 1; + return -1; /* give a chance to retry */ + } + + snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " + "switching to single_cmd mode: last cmd=0x%08x\n", + chip->last_cmd); + chip->single_cmd = 1; + bus->response_reset = 0; + /* re-initialize CORB/RIRB */ azx_free_cmd_io(chip); azx_init_cmd_io(chip); - spin_unlock_irq(&chip->reg_lock); return -1; } @@ -709,6 +718,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) struct azx *chip = bus->private_data; int timeout = 50; + bus->rirb_error = 0; while (timeout--) { /* check ICB busy bit */ if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { @@ -1247,6 +1257,23 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, struct hda_pcm *cpcm); static void azx_stop_chip(struct azx *chip); +static void azx_bus_reset(struct hda_bus *bus) +{ + struct azx *chip = bus->private_data; + int i; + + bus->in_reset = 1; + azx_stop_chip(chip); + azx_init_chip(chip); + if (chip->initialized) { + for (i = 0; i < AZX_MAX_PCMS; i++) + snd_pcm_suspend_all(chip->pcm[i]); + snd_hda_suspend(chip->bus); + snd_hda_resume(chip->bus); + } + bus->in_reset = 0; +} + /* * Codec initialization */ @@ -1270,6 +1297,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, bus_temp.ops.command = azx_send_cmd; bus_temp.ops.get_response = azx_get_response; bus_temp.ops.attach_pcm = azx_attach_pcm_stream; + bus_temp.ops.bus_reset = azx_bus_reset; #ifdef CONFIG_SND_HDA_POWER_SAVE bus_temp.power_save = &power_save; bus_temp.ops.pm_notify = azx_power_notify; @@ -1997,7 +2025,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) for (i = 0; i < AZX_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); if (chip->initialized) - snd_hda_suspend(chip->bus, state); + snd_hda_suspend(chip->bus); azx_stop_chip(chip); if (chip->irq >= 0) { free_irq(chip->irq, chip); -- cgit v1.2.1 From b20f3b834673be9ead83a3c6f07fa3881d1a990f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 01:20:22 +0200 Subject: ALSA: hda - Limit codec-verb retry to limited hardwares The reset of a BUS controller during operations is somehow risky and shouldn't be done inevitably for devices that have apparently no such codec-communication problems. This patch adds the check of the hardware and limits the bus-reset capability. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 12 ++---------- sound/pci/hda/hda_codec.h | 3 +++ sound/pci/hda/hda_intel.c | 2 +- sound/pci/hda/patch_sigmatel.c | 9 +++++++++ 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index aa0e1c18b606..562403a23488 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -214,11 +214,6 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_read); -/* Define the below to send and receive verbs synchronously. - * If you often get any codec communication errors, this is worth to try. - */ -/* #define SND_HDA_SUPPORT_SYNC_WRITE */ - /** * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec @@ -235,12 +230,9 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); -#ifdef SND_HDA_SUPPORT_SYNC_WRITE unsigned int res; - return codec_exec_verb(codec, cmd, &res); -#else - return codec_exec_verb(codec, cmd, NULL); -#endif + return codec_exec_verb(codec, cmd, + codec->bus->sync_write ? &res : NULL); } EXPORT_SYMBOL_HDA(snd_hda_codec_write); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f5fa5d1f223c..cad79efaabc9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -624,6 +624,9 @@ struct hda_bus { /* misc op flags */ unsigned int needs_damn_long_delay :1; + unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ + unsigned int sync_write:1; /* sync after verb write */ + /* status for codec/controller */ unsigned int shutdown :1; /* being unloaded */ unsigned int rirb_error:1; /* error in codec communication */ unsigned int response_reset:1; /* controller was reset */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 44f9a0aa20c5..9f44645a1d04 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -665,7 +665,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) * to the single_cmd mode */ bus->rirb_error = 1; - if (!bus->response_reset && !bus->in_reset) { + if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { bus->response_reset = 1; return -1; /* give a chance to retry */ } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 48f4a36c4813..42f944bb641d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5375,6 +5375,15 @@ again: if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); + /* Some HP machines seem to have unstable codec communications + * especially with ATI fglrx driver. For recovering from the + * CORB/RIRB stall, allow the BUS reset and keep always sync + */ + if (spec->board_config == STAC_HP_DV5) { + codec->bus->sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + spec->aloopback_ctl = stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; -- cgit v1.2.1 From 8a4259bf89d23bfd58d87e275ef6da29cea6b3c5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 08:40:51 +0200 Subject: ALSA: ctxfi - Fix Oops at mmapping Replace a spinlock with a mutex protecting the vm block list at mmap / munmap calls, which caused Oops like below: BUG: sleeping function called from invalid context at mm/slub.c:1599 in_atomic(): 0, irqs_disabled(): 1, pid: 32065, name: xine Pid: 32065, comm: xine Tainted: P 2.6.29.4-75.fc10.x86_64 #1 Call Trace: [] __might_sleep+0x105/0x10a [] kmem_cache_alloc+0x32/0xe2 [] ct_vm_map+0xfa/0x19e [snd_ctxfi] [] ct_map_audio_buffer+0x4c/0x76 [snd_ctxfi] [] atc_pcm_playback_prepare+0x1d7/0x2a8 [snd_ctxfi] [] ? up_read+0x9/0xb [] ? __up_read+0x7c/0x87 [] ct_pcm_playback_prepare+0x39/0x60 [snd_ctxfi] [] snd_pcm_do_prepare+0x16/0x28 [snd_pcm] [] snd_pcm_action_single+0x2d/0x5b [snd_pcm] [] snd_pcm_action_nonatomic+0x52/0x6a [snd_pcm] [] snd_pcm_common_ioctl1+0x404/0xc79 [snd_pcm] [] ? alloc_pages_current+0xb9/0xc2 [] ? new_slab+0x1a5/0x1cb [] ? vma_prio_tree_insert+0x23/0xc1 [] snd_pcm_playback_ioctl1+0x213/0x230 [snd_pcm] [] ? mmap_region+0x397/0x4c9 [] snd_pcm_playback_ioctl+0x2e/0x36 [snd_pcm] [] vfs_ioctl+0x2a/0x78 [] do_vfs_ioctl+0x462/0x4a2 [] ? default_spin_lock_flags+0x9/0xe [] ? trace_hardirqs_off_thunk+0x3a/0x6c [] sys_ioctl+0x55/0x77 [] system_call_fastpath+0x16/0x1b Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 11 ----------- sound/pci/ctxfi/ctatc.h | 1 - sound/pci/ctxfi/ctvmem.c | 14 +++++++++++--- sound/pci/ctxfi/ctvmem.h | 3 ++- 4 files changed, 13 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index ead104ea1e35..1a4bb357eaef 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -119,7 +119,6 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm); static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - unsigned long flags; struct snd_pcm_runtime *runtime; struct ct_vm *vm; @@ -129,9 +128,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) runtime = apcm->substream->runtime; vm = atc->vm; - spin_lock_irqsave(&atc->vm_lock, flags); apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); - spin_unlock_irqrestore(&atc->vm_lock, flags); if (NULL == apcm->vm_block) return -ENOENT; @@ -141,7 +138,6 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - unsigned long flags; struct ct_vm *vm; if (NULL == apcm->vm_block) @@ -149,9 +145,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) vm = atc->vm; - spin_lock_irqsave(&atc->vm_lock, flags); vm->unmap(vm, apcm->vm_block); - spin_unlock_irqrestore(&atc->vm_lock, flags); apcm->vm_block = NULL; } @@ -161,9 +155,7 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) struct ct_vm *vm; void *kvirt_addr; unsigned long phys_addr; - unsigned long flags; - spin_lock_irqsave(&atc->vm_lock, flags); vm = atc->vm; kvirt_addr = vm->get_ptp_virt(vm, index); if (kvirt_addr == NULL) @@ -171,8 +163,6 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) else phys_addr = virt_to_phys(kvirt_addr); - spin_unlock_irqrestore(&atc->vm_lock, flags); - return phys_addr; } @@ -1562,7 +1552,6 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, atc_set_ops(atc); spin_lock_init(&atc->atc_lock); - spin_lock_init(&atc->vm_lock); /* Find card model */ err = atc_identify_card(atc); diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 286c993d461a..a7b0ec24cd75 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -101,7 +101,6 @@ struct ct_atc { unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index); spinlock_t atc_lock; - spinlock_t vm_lock; int (*pcm_playback_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm); diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index cecf77e3ee86..363b67e3a9e7 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -35,25 +35,27 @@ get_vm_block(struct ct_vm *vm, unsigned int size) struct ct_vm_block *block = NULL, *entry = NULL; struct list_head *pos = NULL; + mutex_lock(&vm->lock); list_for_each(pos, &vm->unused) { entry = list_entry(pos, struct ct_vm_block, list); if (entry->size >= size) break; /* found a block that is big enough */ } if (pos == &vm->unused) - return NULL; + goto out; if (entry->size == size) { /* Move the vm node from unused list to used list directly */ list_del(&entry->list); list_add(&entry->list, &vm->used); vm->size -= size; - return entry; + block = entry; + goto out; } block = kzalloc(sizeof(*block), GFP_KERNEL); if (NULL == block) - return NULL; + goto out; block->addr = entry->addr; block->size = size; @@ -62,6 +64,8 @@ get_vm_block(struct ct_vm *vm, unsigned int size) entry->size -= size; vm->size -= size; + out: + mutex_unlock(&vm->lock); return block; } @@ -70,6 +74,7 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) struct ct_vm_block *entry = NULL, *pre_ent = NULL; struct list_head *pos = NULL, *pre = NULL; + mutex_lock(&vm->lock); list_del(&block->list); vm->size += block->size; @@ -106,6 +111,7 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) pos = pre; pre = pos->prev; } + mutex_unlock(&vm->lock); } /* Map host addr (kmalloced/vmalloced) to device logical addr. */ @@ -191,6 +197,8 @@ int ct_vm_create(struct ct_vm **rvm) if (NULL == vm) return -ENOMEM; + mutex_init(&vm->lock); + /* Allocate page table pages */ for (i = 0; i < CT_PTP_NUM; i++) { vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 4eb5bdd5cad4..618952efa5b3 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -20,7 +20,7 @@ #define CT_PTP_NUM 1 /* num of device page table pages */ -#include +#include #include struct ct_vm_block { @@ -35,6 +35,7 @@ struct ct_vm { unsigned int size; /* Available addr space in bytes */ struct list_head unused; /* List of unused blocks */ struct list_head used; /* List of used blocks */ + struct mutex lock; /* Map host addr (kmalloced/vmalloced) to device logical addr. */ struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, int size); -- cgit v1.2.1 From 67fbf880631bb4493ad8d23f25562abdf09dc01d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 09:18:26 +0200 Subject: ALSA: ctxfi - Fix a typo in MODULE_LICENSE A space has to be put between GPL and v2. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/xfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index e31e29e4ec7e..bf232e7299ac 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -18,7 +18,7 @@ MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); -MODULE_LICENSE("GPLv2"); +MODULE_LICENSE("GPL v2"); MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); static unsigned int reference_rate = 48000; -- cgit v1.2.1 From 3b315d70b094e8b439358756a9084438fd7a71c2 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 2 Jun 2009 10:54:19 +0200 Subject: ALSA: hda - Acer Aspire 8930G support Short story: this laptop has 5.1 built-in speakers which you *really* want to use (the not-so-"sub" woofer is what makes the audio above average for a laptop), so 6-channel support is important (plus a decent asound.conf to upmix stereo). It also has the 3 typical jacks that ought to have a selectable mode. And it's based on ALC889, which sucks. Rationale/explanations: The const_channel_count stuff was added because, for a laptop like this, you always have 6 channels available (internal speakers) but still need to set the mode for the 3 external jacks. Therefore, the device always needs to be in 6-channel mode but there still needs to be a mixer control for the jack mode. You could use line/mic-in at the same time as the 6 internal speakers, for example. You might be tempted to make it even smarter by dynamically switching the max channel count when headphones are plugged in (therefore muting the internal speakers and reducing the physical channel count to the jack channel mode), but as a user I consider this to be harmful because I want the audio to blow up to 6 channels / upmixed as soon as I unplug the headphones, and having opened the device while in 2-channel mode would prevent this from working (and always making 6-channel mode available doesn't do any harm). The hardware needs EAPD turned on and the DACs routed to the internal speaker pins, so the patch adds those verbs. The ALC889 CLFE and subsequent (side/aux, here unused) DACs do NOT work by default, at least here. I wasted much time trying to talk to Realtek/pshou about this, but they just kept sending me useless updates to patch_realtek.c that did nothing relevant. In the end I gave up and brute forced the issue by trying to flip every bit in the proprietary coefficient registers, and eventually found the two magic registers that need to be cleared to enable all DACs. I have only heard Acer users complain, but that might be because ALC889 is pretty new and using 5.1 (and noticing the missing center/lfe channels) might not be that common. If this is a generalized issue with all ALC889 systems then those verbs should probably be moved to a common verb array. The internal mic is untested and probably doesn't work. These settings will probably work for other Acer Gemstone laptops with the same 5.1 speaker config. When identified, those should be added to the PCI subsystem ID list. Signed-off-by: Hector Martin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 99 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ad6cc4218049..2bbb9e445cb1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -223,6 +223,7 @@ enum { ALC883_ACER, ALC883_ACER_ASPIRE, ALC888_ACER_ASPIRE_4930G, + ALC888_ACER_ASPIRE_8930G, ALC883_MEDION, ALC883_MEDION_MD2, ALC883_LAPTOP_EAPD, @@ -313,6 +314,8 @@ struct alc_spec { const struct hda_channel_mode *channel_mode; int num_channel_mode; int need_dac_fix; + int const_channel_count; + int ext_channel_count; /* PCM information */ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ @@ -368,6 +371,7 @@ struct alc_config_preset { unsigned int num_channel_mode; const struct hda_channel_mode *channel_mode; int need_dac_fix; + int const_channel_count; unsigned int num_mux_defs; const struct hda_input_mux *input_mux; void (*unsol_event)(struct hda_codec *, unsigned int); @@ -462,7 +466,7 @@ static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, spec->num_channel_mode, - spec->multiout.max_channels); + spec->ext_channel_count); } static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, @@ -472,9 +476,12 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, spec->num_channel_mode, - &spec->multiout.max_channels); - if (err >= 0 && spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; + &spec->ext_channel_count); + if (err >= 0 && !spec->const_channel_count) { + spec->multiout.max_channels = spec->ext_channel_count; + if (spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + } return err; } @@ -854,8 +861,13 @@ static void setup_preset(struct alc_spec *spec, spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; spec->need_dac_fix = preset->need_dac_fix; + spec->const_channel_count = preset->const_channel_count; - spec->multiout.max_channels = spec->channel_mode[0].channels; + if (preset->const_channel_count) + spec->multiout.max_channels = preset->const_channel_count; + else + spec->multiout.max_channels = spec->channel_mode[0].channels; + spec->ext_channel_count = spec->channel_mode[0].channels; spec->multiout.num_dacs = preset->num_dacs; spec->multiout.dac_nids = preset->dac_nids; @@ -1456,6 +1468,48 @@ static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { { } }; +/* + * ALC888 Acer Aspire 8930G model + */ + +static struct hda_verb alc888_acer_aspire_8930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal Front to Front */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect Internal Rear to Rear */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect Internal CLFE to CLFE */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect HP out to Front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable all DACs */ +/* DAC DISABLE/MUTE 1? */ +/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x03}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, +/* DAC DISABLE/MUTE 2? */ +/* some bit here disables the other DACs. Init=0x4900 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, +/* Enable amplifiers */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + { } +}; + static struct hda_input_mux alc888_2_capture_sources[2] = { /* Front mic only available on one ADC */ { @@ -1508,6 +1562,17 @@ static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec) alc_automute_amp(codec); } +static void alc888_acer_aspire_8930g_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x1b; + alc_automute_amp(codec); +} + /* * ALC880 3-stack model * @@ -8758,6 +8823,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_ACER] = "acer", [ALC883_ACER_ASPIRE] = "acer-aspire", [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", + [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g", [ALC883_MEDION] = "medion", [ALC883_MEDION_MD2] = "medion-md2", [ALC883_LAPTOP_EAPD] = "laptop-eapd", @@ -8790,6 +8856,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", ALC888_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", + ALC888_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO), SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO), SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", @@ -9009,6 +9077,27 @@ static struct alc_config_preset alc883_presets[] = { .unsol_event = alc_automute_amp_unsol_event, .init_hook = alc888_acer_aspire_4930g_init_hook, }, + [ALC888_ACER_ASPIRE_8930G] = { + .mixers = { alc888_base_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_8930g_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc888_acer_aspire_8930g_init_hook, + }, [ALC883_MEDION] = { .mixers = { alc883_fivestack_mixer, alc883_chmode_mixer }, -- cgit v1.2.1 From 601e1cc5df940b59e71c947726640811897d30df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 11:37:01 +0200 Subject: ALSA: ca0106 - Add missing registrations of vmaster controls Although the vmaster controls are created, they aren't registered thus they don't appear in the real world. Added the missing snd_ctl_add() calls. Signed-off-by: Takashi Iwai Cc: --- sound/pci/ca0106/ca0106_mixer.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index c111efe61c3c..f143f7185dd1 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -841,6 +841,9 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) snd_ca0106_master_db_scale); if (!vmaster) return -ENOMEM; + err = snd_ctl_add(card, vmaster); + if (err < 0) + return err; add_slaves(card, vmaster, slave_vols); if (emu->details->spi_dac == 1) { @@ -848,6 +851,9 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) NULL); if (!vmaster) return -ENOMEM; + err = snd_ctl_add(card, vmaster); + if (err < 0) + return err; add_slaves(card, vmaster, slave_sws); } return 0; -- cgit v1.2.1 From 9a83b7453c2c4db145666b653abe9d9f410d18a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:20:48 +0200 Subject: ALSA: Remove invalid GENERIC_MIX PCM sublass SNDRV_PCM_SUBCLASS_GENERIC_MIX is mostly for h/w multi-stream playback devices, but ca0106 and emu10k1x don't support it (unlike emu10k1). We shouldn't set that flag to avoid confusion. Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106_main.c | 1 - sound/pci/emu10k1/emu10k1x.c | 1 - 2 files changed, 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index bfac30f7929f..57b992a5c057 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1319,7 +1319,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device) } pcm->info_flags = 0; - pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "CA0106"); for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 1970f0e70f37..4d3ad793e98f 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -858,7 +858,6 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s } pcm->info_flags = 0; - pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; switch(device) { case 0: strcpy(pcm->name, "EMU10K1X Front"); -- cgit v1.2.1 From 822fa19b5c23746577687a0ec48eae0ec1cd22a0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:12:17 +0200 Subject: ALSA: ALSA: ctxfi - Release PCM resources at each prepare call The prepare callback can be called multiple times, thus it needs to release and acquire the resource again by itself at the second or later call. Simply add pcm_release_resources() at the beginning of each prepare callback in ctatc.c. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 1a4bb357eaef..e14ed714721b 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -254,6 +254,9 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) return 0; } + /* first release old resources */ + atc->pcm_release_resources(atc, apcm); + /* Get SRC resource */ desc.multi = apcm->substream->runtime->channels; desc.msr = atc->msr; @@ -496,6 +499,9 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; struct src_node_conf_t src_node_conf[2] = {{0} }; + /* first release old resources */ + atc->pcm_release_resources(atc, apcm); + /* The numbers of converting SRCs and SRCIMPs should be determined * by pitch value. */ @@ -767,6 +773,9 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, int n_amixer = apcm->substream->runtime->channels, i = 0; unsigned int pitch = 0, rsr = atc->pll_rate; + /* first release old resources */ + atc->pcm_release_resources(atc, apcm); + /* Get SRC resource */ desc.multi = apcm->substream->runtime->channels; desc.msr = 1; -- cgit v1.2.1 From 6585db943aade186d38eaab2720c18887b94c875 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:17:27 +0200 Subject: ALSA: ctxfi - Fix surround mixer names We usually pick up "Surround" mixer for the rear output, and "Side" for the extra surround. Fix the channel mapping to follow it. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctmixer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index b7950768c3f8..177c46e248db 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -168,7 +168,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVES_P] = { .ctl = 1, - .name = "Surround Playback Volume", + .name = "Side Playback Volume", }, [MIXER_WAVEC_P] = { .ctl = 1, @@ -176,7 +176,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVER_P] = { .ctl = 1, - .name = "Rear Playback Volume", + .name = "Surround Playback Volume", }, [MIXER_PCM_C_S] = { @@ -213,7 +213,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVES_P_S] = { .ctl = 1, - .name = "Surround Playback Switch", + .name = "Side Playback Switch", }, [MIXER_WAVEC_P_S] = { .ctl = 1, @@ -221,7 +221,7 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = { }, [MIXER_WAVER_P_S] = { .ctl = 1, - .name = "Rear Playback Switch", + .name = "Surround Playback Switch", }, [MIXER_DIGITAL_IO_S] = { .ctl = 0, -- cgit v1.2.1 From 8372d4980fbc2e403f0dc5457441c8c6b7c04915 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:27:56 +0200 Subject: ALSA: ctxfi - Fix PCM device naming PCM names for surround streams should be also fixed as well as the mixer element names. Also, a bit clean up for PCM name setup. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 8 ++++---- sound/pci/ctxfi/ctatc.h | 4 ++-- sound/pci/ctxfi/ctpcm.c | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index e14ed714721b..675dd4c8eaec 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -72,15 +72,15 @@ static struct { [FRONT] = { .create = ct_alsa_pcm_create, .destroy = NULL, .public_name = "Front/WaveIn"}, - [REAR] = { .create = ct_alsa_pcm_create, + [SURROUND] = { .create = ct_alsa_pcm_create, .destroy = NULL, - .public_name = "Rear"}, + .public_name = "Surround"}, [CLFE] = { .create = ct_alsa_pcm_create, .destroy = NULL, .public_name = "Center/LFE"}, - [SURROUND] = { .create = ct_alsa_pcm_create, + [SIDE] = { .create = ct_alsa_pcm_create, .destroy = NULL, - .public_name = "Surround"}, + .public_name = "Side"}, [IEC958] = { .create = ct_alsa_pcm_create, .destroy = NULL, .public_name = "IEC958 Non-audio"}, diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index a7b0ec24cd75..b86d12cd4a19 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -29,9 +29,9 @@ enum CTALSADEVS { /* Types of alsa devices */ FRONT, - REAR, - CLFE, SURROUND, + CLFE, + SIDE, IEC958, MIXER, NUM_CTALSADEVS /* This should always be the last */ diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index a64cb0ed8759..756d8b2776b3 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -469,12 +469,10 @@ int ct_alsa_pcm_create(struct ct_atc *atc, struct snd_pcm *pcm; int err; int playback_count, capture_count; - char name[128]; - strncpy(name, device_name, sizeof(name)); playback_count = (IEC958 == device) ? 1 : 8; capture_count = (FRONT == device) ? 1 : 0; - err = snd_pcm_new(atc->card, name, device, + err = snd_pcm_new(atc->card, "ctxfi", device, playback_count, capture_count, &pcm); if (err < 0) { printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err); @@ -484,7 +482,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc, pcm->private_data = atc; pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, device_name); + strlcpy(pcm->name, device_name, sizeof(pcm->name)); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); -- cgit v1.2.1 From d2b9b96c516d4d61663d92ab4ad4f15ca0134ef2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 14:39:05 +0200 Subject: ALSA: ctxfi - Fix supported PCM formats The device seems supporting only U8, S16, S24_3LE, S32. Other linear formats result in bad outputs. Also, added the support for 32bit float format, which wasn't listed in the original code. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 5 ++--- sound/pci/ctxfi/ctpcm.c | 15 +++++---------- 2 files changed, 7 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 675dd4c8eaec..268ecc4c2856 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -170,16 +170,15 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) { switch (snd_format) { case SNDRV_PCM_FORMAT_U8: - case SNDRV_PCM_FORMAT_S8: return SRC_SF_U8; case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_U16_LE: return SRC_SF_S16; case SNDRV_PCM_FORMAT_S24_3LE: return SRC_SF_S24; - case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S32_LE: return SRC_SF_S32; + case SNDRV_PCM_FORMAT_FLOAT_LE: + return SRC_SF_F32; default: printk(KERN_ERR "ctxfi: not recognized snd format is %d \n", snd_format); diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 756d8b2776b3..26d86dc35e52 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -26,12 +26,10 @@ static struct snd_pcm_hardware ct_pcm_playback_hw = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S24_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_FLOAT_LE), .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000), .rate_min = 8000, @@ -52,8 +50,7 @@ static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE), + .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000), @@ -77,12 +74,10 @@ static struct snd_pcm_hardware ct_pcm_capture_hw = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID), .formats = (SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S24_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_FLOAT_LE), .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000), .rate_min = 8000, -- cgit v1.2.1 From cd391e206f486955e216a61bd9ebcb0e142122e9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 15:04:29 +0200 Subject: ALSA: ctxfi - Remove PAGE_SIZE limitation Remove the limitation of PAGE_SIZE to be 4k by defining the own page size and macros for 4k. 8kb page size could be natively supported, but it's disabled right now for simplicity. Also, clean up using upper_32_bits() macro. Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 1 - sound/pci/ctxfi/cthw20k1.c | 16 ++++++---------- sound/pci/ctxfi/cthw20k2.c | 17 ++++------------- sound/pci/ctxfi/ctvmem.c | 25 ++++++++++++------------- sound/pci/ctxfi/ctvmem.h | 8 ++++++++ 5 files changed, 30 insertions(+), 37 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 2d7fef5df017..3a7640feaf92 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -277,7 +277,6 @@ config SND_CS5535AUDIO config SND_CTXFI tristate "Creative Sound Blaster X-Fi" - depends on X86 select SND_PCM help If you want to use soundcards based on Creative Sound Blastr X-Fi diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 44283bd7b2df..b7b8e6f41d0d 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1249,18 +1249,14 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) } trnctl = 0x13; /* 32-bit, 4k-size page */ -#if BITS_PER_LONG == 64 - ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); - ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); - trnctl |= (1<<2); -#elif BITS_PER_LONG == 32 - ptp_phys_low = info->vm_pgt_phys & (~0UL); - ptp_phys_high = 0; -#else -# error "Unknown BITS_PER_LONG!" -#endif + ptp_phys_low = (u32)info->vm_pgt_phys; + ptp_phys_high = upper_32_bits(info->vm_pgt_phys); + if (sizeof(void *) == 8) /* 64bit address */ + trnctl |= (1 << 2); +#if 0 /* Only 4k h/w pages for simplicitiy */ #if PAGE_SIZE == 8192 trnctl |= (1<<5); +#endif #endif hw_write_20kx(hw, PTPALX, ptp_phys_low); hw_write_20kx(hw, PTPAHX, ptp_phys_high); diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 7c24c2ca96bd..349728765f2c 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1203,19 +1203,10 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) } vmctl = 0x80000C0F; /* 32-bit, 4k-size page */ -#if BITS_PER_LONG == 64 - ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1); - ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1); - vmctl |= (3<<8); -#elif BITS_PER_LONG == 32 - ptp_phys_low = info->vm_pgt_phys & (~0UL); - ptp_phys_high = 0; -#else -# error "Unknown BITS_PER_LONG!" -#endif -#if PAGE_SIZE == 8192 -# error "Don't support 8k-page!" -#endif + ptp_phys_low = (u32)info->vm_pgt_phys; + ptp_phys_high = upper_32_bits(info->vm_pgt_phys); + if (sizeof(void *) == 8) /* 64bit address */ + vmctl |= (3 << 8); /* Write page table physical address to all PTPAL registers */ for (i = 0; i < 64; i++) { hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low); diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 363b67e3a9e7..74a03623d047 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -18,12 +18,11 @@ #include "ctvmem.h" #include #include -#include /* for PAGE_SIZE macro definition */ #include #include -#define CT_PTES_PER_PAGE (PAGE_SIZE / sizeof(void *)) -#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * PAGE_SIZE) +#define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *)) +#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE) /* * * Find or create vm block based on requested @size. @@ -138,24 +137,24 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) return NULL; } - start_phys = (virt_to_phys(host_addr) & PAGE_MASK); - pages = (PAGE_ALIGN(virt_to_phys(host_addr) + size) - - start_phys) >> PAGE_SHIFT; + start_phys = (virt_to_phys(host_addr) & CT_PAGE_MASK); + pages = (CT_PAGE_ALIGN(virt_to_phys(host_addr) + size) + - start_phys) >> CT_PAGE_SHIFT; ptp = vm->ptp[0]; - block = get_vm_block(vm, (pages << PAGE_SHIFT)); + block = get_vm_block(vm, (pages << CT_PAGE_SHIFT)); if (block == NULL) { printk(KERN_ERR "ctxfi: No virtual memory block that is big " "enough to allocate!\n"); return NULL; } - pte_start = (block->addr >> PAGE_SHIFT); + pte_start = (block->addr >> CT_PAGE_SHIFT); for (i = 0; i < pages; i++) - ptp[pte_start+i] = start_phys + (i << PAGE_SHIFT); + ptp[pte_start+i] = start_phys + (i << CT_PAGE_SHIFT); - block->addr += (virt_to_phys(host_addr) & (~PAGE_MASK)); + block->addr += (virt_to_phys(host_addr) & (~CT_PAGE_MASK)); block->size = size; return block; @@ -164,9 +163,9 @@ ct_vm_map(struct ct_vm *vm, void *host_addr, int size) static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) { /* do unmapping */ - block->size = ((block->addr + block->size + PAGE_SIZE - 1) - & PAGE_MASK) - (block->addr & PAGE_MASK); - block->addr &= PAGE_MASK; + block->size = ((block->addr + block->size + CT_PAGE_SIZE - 1) + & CT_PAGE_MASK) - (block->addr & CT_PAGE_MASK); + block->addr &= CT_PAGE_MASK; put_vm_block(vm, block); } diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 618952efa5b3..17d2d37a9ea7 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -23,6 +23,14 @@ #include #include +/* The chip can handle the page table of 4k pages + * (emu20k1 can handle even 8k pages, but we don't use it right now) + */ +#define CT_PAGE_SIZE 4096 +#define CT_PAGE_SHIFT 12 +#define CT_PAGE_MASK (~(PAGE_SIZE - 1)) +#define CT_PAGE_ALIGN(addr) ALIGN(addr, CT_PAGE_SIZE) + struct ct_vm_block { unsigned int addr; /* starting logical addr of this block */ unsigned int size; /* size of this device virtual mem block */ -- cgit v1.2.1 From c76157d9286ed598c241c212aa5a3c6e5107bd82 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 15:26:19 +0200 Subject: ALSA: ctxfi - Support SG-buffers Use SG-buffers instead of contiguous pages. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 2 +- sound/pci/ctxfi/ctpcm.c | 4 +++- sound/pci/ctxfi/ctvmem.c | 56 +++++++++++++++++++----------------------------- sound/pci/ctxfi/ctvmem.h | 5 ++++- 4 files changed, 30 insertions(+), 37 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 268ecc4c2856..684947546d81 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -128,7 +128,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) runtime = apcm->substream->runtime; vm = atc->vm; - apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes); + apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes); if (NULL == apcm->vm_block) return -ENOENT; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 26d86dc35e52..52ddf19d83bb 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -442,6 +442,7 @@ static struct snd_pcm_ops ct_pcm_playback_ops = { .prepare = ct_pcm_playback_prepare, .trigger = ct_pcm_playback_trigger, .pointer = ct_pcm_playback_pointer, + .page = snd_pcm_sgbuf_ops_page, }; /* PCM operators for capture */ @@ -454,6 +455,7 @@ static struct snd_pcm_ops ct_pcm_capture_ops = { .prepare = ct_pcm_capture_prepare, .trigger = ct_pcm_capture_trigger, .pointer = ct_pcm_capture_pointer, + .page = snd_pcm_sgbuf_ops_page, }; /* Create ALSA pcm device */ @@ -485,7 +487,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(atc->pci), 128*1024, 128*1024); return 0; diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 74a03623d047..b7f8e58ae07d 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *)) #define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE) @@ -34,6 +34,13 @@ get_vm_block(struct ct_vm *vm, unsigned int size) struct ct_vm_block *block = NULL, *entry = NULL; struct list_head *pos = NULL; + size = CT_PAGE_ALIGN(size); + if (size > vm->size) { + printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " + "memory space available!\n"); + return NULL; + } + mutex_lock(&vm->lock); list_for_each(pos, &vm->unused) { entry = list_entry(pos, struct ct_vm_block, list); @@ -73,6 +80,8 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) struct ct_vm_block *entry = NULL, *pre_ent = NULL; struct list_head *pos = NULL, *pre = NULL; + block->size = CT_PAGE_ALIGN(block->size); + mutex_lock(&vm->lock); list_del(&block->list); vm->size += block->size; @@ -115,57 +124,36 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) /* Map host addr (kmalloced/vmalloced) to device logical addr. */ static struct ct_vm_block * -ct_vm_map(struct ct_vm *vm, void *host_addr, int size) +ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size) { - struct ct_vm_block *block = NULL; - unsigned long pte_start; - unsigned long i; - unsigned long pages; - unsigned long start_phys; + struct ct_vm_block *block; + unsigned int pte_start; + unsigned i, pages; unsigned long *ptp; - /* do mapping */ - if ((unsigned long)host_addr >= VMALLOC_START) { - printk(KERN_ERR "ctxfi: " - "Fail! Not support vmalloced addr now!\n"); - return NULL; - } - - if (size > vm->size) { - printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural " - "memory space available!\n"); - return NULL; - } - - start_phys = (virt_to_phys(host_addr) & CT_PAGE_MASK); - pages = (CT_PAGE_ALIGN(virt_to_phys(host_addr) + size) - - start_phys) >> CT_PAGE_SHIFT; - - ptp = vm->ptp[0]; - - block = get_vm_block(vm, (pages << CT_PAGE_SHIFT)); + block = get_vm_block(vm, size); if (block == NULL) { printk(KERN_ERR "ctxfi: No virtual memory block that is big " "enough to allocate!\n"); return NULL; } + ptp = vm->ptp[0]; pte_start = (block->addr >> CT_PAGE_SHIFT); - for (i = 0; i < pages; i++) - ptp[pte_start+i] = start_phys + (i << CT_PAGE_SHIFT); + pages = block->size >> CT_PAGE_SHIFT; + for (i = 0; i < pages; i++) { + unsigned long addr; + addr = snd_pcm_sgbuf_get_addr(substream, i << CT_PAGE_SHIFT); + ptp[pte_start + i] = addr; + } - block->addr += (virt_to_phys(host_addr) & (~CT_PAGE_MASK)); block->size = size; - return block; } static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) { /* do unmapping */ - block->size = ((block->addr + block->size + CT_PAGE_SIZE - 1) - & CT_PAGE_MASK) - (block->addr & CT_PAGE_MASK); - block->addr &= CT_PAGE_MASK; put_vm_block(vm, block); } diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 17d2d37a9ea7..01e4fd0386a3 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -37,6 +37,8 @@ struct ct_vm_block { struct list_head list; }; +struct snd_pcm_substream; + /* Virtual memory management object for card device */ struct ct_vm { void *ptp[CT_PTP_NUM]; /* Device page table pages */ @@ -46,7 +48,8 @@ struct ct_vm { struct mutex lock; /* Map host addr (kmalloced/vmalloced) to device logical addr. */ - struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, int size); + struct ct_vm_block *(*map)(struct ct_vm *, struct snd_pcm_substream *, + int size); /* Unmap device logical addr area. */ void (*unmap)(struct ct_vm *, struct ct_vm_block *block); void *(*get_ptp_virt)(struct ct_vm *vm, int index); -- cgit v1.2.1 From eeaf100d25cefdbe9dc40d6e0f2025411474972a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 16:06:02 +0200 Subject: ALSA: ca0106 - Add missing card->mixername field setup Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106_mixer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index f143f7185dd1..7724252c1ec5 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -856,6 +856,8 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) return err; add_slaves(card, vmaster, slave_sws); } + + strcpy(card->mixername, "CA0106"); return 0; } -- cgit v1.2.1 From 3f08a0e4ab1ce65bb882f6425ff482e5db025f78 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 17:39:52 +0200 Subject: ALSA: bt87x - Add a quirk entry for Askey Computer Corp. MagicTView'99 Signed-off-by: Takashi Iwai --- sound/pci/bt87x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index ce3f2e90f4d7..24585c6c6d01 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -810,6 +810,8 @@ static struct pci_device_id snd_bt87x_ids[] = { BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC), /* Voodoo TV 200 */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC), + /* Askey Computer Corp. MagicTView'99 */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x144f, 0x3000, GENERIC), /* AVerMedia Studio No. 103, 203, ...? */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, AVPHONE98), /* Prolink PixelView PV-M4900 */ -- cgit v1.2.1 From 018df41861475595a51d327b83fb5830462f7a53 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 4 Jun 2009 00:13:40 +0200 Subject: ALSA: hda - More Aspire 8930G fixes Enable all three capture channels, including the missing nid 7 which is the only one capable of capturing DMIC input Enable Headphone amp for the HP jack. This causes a volume boost for headphones, but does not cause any noticeable effect for light loads like other amps, so there is no need to make it configurable. Add Input Mix capture mux setting to capture the output of the playback input mux (that is, what goes out the speakers except for PCM) Hack another coef register because the stereo DMIC for some reason produces a nonstandard sum/difference signal. I found a bit to make it just use the sum signal for both channels, which makes it behave like a standard mono microphone. The stereo is useless anyway (they're 1cm apart). Tested working: Three capture channels, mic in, line in, DMIC. Tested not working: CD. Not sure why, might be unconnected in the actual hardware or a CD drive issue. Also looked at SPDIF. It appears to work (emitter lights up inside the HP out jack) but I lack a proper miniTOSLINK cable to test it. Signed-off-by: Hector Martin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 65 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 11 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2bbb9e445cb1..8699b7fb45b9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1469,10 +1469,10 @@ static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { }; /* - * ALC888 Acer Aspire 8930G model + * ALC889 Acer Aspire 8930G model */ -static struct hda_verb alc888_acer_aspire_8930g_verbs[] = { +static struct hda_verb alc889_acer_aspire_8930g_verbs[] = { /* Front Mic: set to PIN_IN (empty by default) */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Unselect Front Mic by default in input mixer 3 */ @@ -1492,7 +1492,7 @@ static struct hda_verb alc888_acer_aspire_8930g_verbs[] = { {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect HP out to Front */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Enable all DACs */ @@ -1507,6 +1507,17 @@ static struct hda_verb alc888_acer_aspire_8930g_verbs[] = { /* Enable amplifiers */ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, +/* DMIC fix + * This laptop has a stereo digital microphone. The mics are only 1cm apart + * which makes the stereo useless. However, either the mic or the ALC889 + * makes the signal become a difference/sum signal instead of standard + * stereo, which is annoying. So instead we flip this bit which makes the + * codec replicate the sum signal to both channels, turning it into a + * normal mono mic. + */ +/* DMIC_CONTROL? Init value = 0x0001 */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0003}, { } }; @@ -1531,6 +1542,38 @@ static struct hda_input_mux alc888_2_capture_sources[2] = { } }; +static struct hda_input_mux alc889_capture_sources[3] = { + /* Digital mic only available on first "ADC" */ + { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + { "Input Mix", 0xa }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + }, + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Input Mix", 0xa }, + }, + } +}; + static struct snd_kcontrol_new alc888_base_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -1562,7 +1605,7 @@ static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec) alc_automute_amp(codec); } -static void alc888_acer_aspire_8930g_init_hook(struct hda_codec *codec) +static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -9081,22 +9124,22 @@ static struct alc_config_preset alc883_presets[] = { .mixers = { alc888_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc888_acer_aspire_8930g_verbs }, + alc889_acer_aspire_8930g_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), - .adc_nids = alc883_adc_nids_rev, - .capsrc_nids = alc883_capsrc_nids_rev, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .capsrc_nids = alc889_capsrc_nids, .dig_out_nid = ALC883_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, .const_channel_count = 6, .num_mux_defs = - ARRAY_SIZE(alc888_2_capture_sources), - .input_mux = alc888_2_capture_sources, + ARRAY_SIZE(alc889_capture_sources), + .input_mux = alc889_capture_sources, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_acer_aspire_8930g_init_hook, + .init_hook = alc889_acer_aspire_8930g_init_hook, }, [ALC883_MEDION] = { .mixers = { alc883_fivestack_mixer, -- cgit v1.2.1 From 65f759831179bb8922f2a91a989487161d476a94 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 4 Jun 2009 13:46:16 +0400 Subject: ALSA: hda_intel: fix build error when !PM Fix this build error when CONFIG_PM is not set: ound/pci/hda/hda_intel.c: In function 'azx_bus_reset': sound/pci/hda/hda_intel.c:1270: error: implicit declaration of function 'snd_pcm_suspend_all' sound/pci/hda/hda_intel.c:1271: error: implicit declaration of function 'snd_hda_suspend' sound/pci/hda/hda_intel.c:1272: error: implicit declaration of function 'snd_hda_resume' Signed-off-by: Alexander Beregalov Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9f44645a1d04..4e9ea7080270 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1260,17 +1260,20 @@ static void azx_stop_chip(struct azx *chip); static void azx_bus_reset(struct hda_bus *bus) { struct azx *chip = bus->private_data; - int i; bus->in_reset = 1; azx_stop_chip(chip); azx_init_chip(chip); +#ifdef CONFIG_PM if (chip->initialized) { + int i; + for (i = 0; i < AZX_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); snd_hda_suspend(chip->bus); snd_hda_resume(chip->bus); } +#endif bus->in_reset = 0; } -- cgit v1.2.1 From 6d74b86d3c0f9cfa949566a862aaad840e393249 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 09:26:41 +0200 Subject: ALSA: ctxfi - Allow 64bit DMA emu20kx chips support 64bit address PTE. Allow the DMA bit mask to accept 64bit address, too. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index b7b8e6f41d0d..1da1f82fe812 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -15,8 +15,6 @@ * */ -#include "cthw20k1.h" -#include "ct20k1reg.h" #include #include #include @@ -26,8 +24,14 @@ #include #include #include +#include "cthw20k1.h" +#include "ct20k1reg.h" -#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ +#if BITS_PER_LONG == 32 +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ +#else +#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ +#endif struct hw20k1 { struct hw hw; -- cgit v1.2.1 From 42a0b31827e4c555efebda7d347cf4ea6b82913a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 09:29:22 +0200 Subject: ALSA: ctxfi - Fix endian-dependent codes The UAA-mode check in hwct20k1.c is implemented with the endian-dependent codes. Fix to be more portable (and readable). Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 1da1f82fe812..fd5f454c4040 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1785,6 +1785,8 @@ static int hw_have_digit_io_switch(struct hw *hw) || ((subsys_id & 0xf000) == 0x6000)); } +#define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + #define UAA_CFG_PWRSTATUS 0x44 #define UAA_CFG_SPACE_FLAG 0xA0 #define UAA_CORE_CHANGE 0x3FFC @@ -1792,12 +1794,18 @@ static int uaa_to_xfi(struct pci_dev *pci) { unsigned int bar0, bar1, bar2, bar3, bar4, bar5; unsigned int cmd, irq, cl_size, l_timer, pwr; - unsigned int CTLA, CTLZ, CTLL, CTLX, CTL_, CTLF, CTLi; unsigned int is_uaa = 0; unsigned int data[4] = {0}; unsigned int io_base; void *mem_base; int i = 0; + const u32 CTLX = CTLBITS('C', 'T', 'L', 'X'); + const u32 CTL_ = CTLBITS('C', 'T', 'L', '-'); + const u32 CTLF = CTLBITS('C', 'T', 'L', 'F'); + const u32 CTLi = CTLBITS('C', 'T', 'L', 'i'); + const u32 CTLA = CTLBITS('C', 'T', 'L', 'A'); + const u32 CTLZ = CTLBITS('C', 'T', 'L', 'Z'); + const u32 CTLL = CTLBITS('C', 'T', 'L', 'L'); /* By default, Hendrix card UAA Bar0 should be using memory... */ io_base = pci_resource_start(pci, 0); @@ -1805,14 +1813,6 @@ static int uaa_to_xfi(struct pci_dev *pci) if (NULL == mem_base) return -ENOENT; - CTLX = ___constant_swab32(*((unsigned int *)"CTLX")); - CTL_ = ___constant_swab32(*((unsigned int *)"CTL-")); - CTLF = ___constant_swab32(*((unsigned int *)"CTLF")); - CTLi = ___constant_swab32(*((unsigned int *)"CTLi")); - CTLA = ___constant_swab32(*((unsigned int *)"CTLA")); - CTLZ = ___constant_swab32(*((unsigned int *)"CTLZ")); - CTLL = ___constant_swab32(*((unsigned int *)"CTLL")); - /* Read current mode from Mode Change Register */ for (i = 0; i < 4; i++) data[i] = readl(mem_base + UAA_CORE_CHANGE); -- cgit v1.2.1 From 3e1647c5b54a91a7182e121cfe569e6f0bf167ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Fri, 5 Jun 2009 00:47:26 +0200 Subject: ALSA: support Sony Vaio TT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit with BIOS probing only we offer a non functional headphone swith and volume slider. Signed-off-by: Guido Günther Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8699b7fb45b9..1ab1b92fdc1c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -243,6 +243,7 @@ enum { ALC888_ASUS_EEE1601, ALC889A_MB31, ALC1200_ASUS_P5Q, + ALC883_SONY_VAIO_TT, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -8134,6 +8135,16 @@ static struct snd_kcontrol_new alc889A_mb31_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_vaiott_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + static struct hda_bind_ctls alc883_bind_cap_vol = { .ops = &snd_hda_bind_vol, .values = { @@ -8410,6 +8421,17 @@ static struct hda_verb alc888_6st_dell_verbs[] = { { } }; +static struct hda_verb alc883_vaiott_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + static void alc888_3st_hp_init_hook(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -8669,6 +8691,16 @@ static void alc888_lenovo_sky_init_hook(struct hda_codec *codec) alc_automute_amp(codec); } +static void alc883_vaiott_init_hook(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x17; + alc_automute_amp(codec); +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -8884,6 +8916,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", [ALC1200_ASUS_P5Q] = "asus-p5q", [ALC889A_MB31] = "mb31", + [ALC883_SONY_VAIO_TT] = "sony-vaio-tt", [ALC883_AUTO] = "auto", }; @@ -8978,6 +9011,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL), SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), + SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), {} }; @@ -9373,6 +9407,17 @@ static struct alc_config_preset alc883_presets[] = { .unsol_event = alc889A_mb31_unsol_event, .init_hook = alc889A_mb31_automute, }, + [ALC883_SONY_VAIO_TT] = { + .mixers = { alc883_vaiott_mixer }, + .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_automute_amp_unsol_event, + .init_hook = alc883_vaiott_init_hook, + }, }; -- cgit v1.2.1 From 6bc5874a1ddf98ac0fe6c4eab7d286c11cb1c748 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 12:15:51 +0200 Subject: ALSA: ctxfi - Fix previous fix for 64bit DMA Remove unneeded substitution to 32bit int to make it really working. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k1.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index fd5f454c4040..e530a6d60422 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1883,18 +1883,17 @@ static int hw_card_start(struct hw *hw) int err = 0; struct pci_dev *pci = hw->pci; u16 subsys_id = 0; - unsigned int dma_mask = 0; err = pci_enable_device(pci); if (err < 0) return err; /* Set DMA transfer mask */ - dma_mask = CT_XFI_DMA_MASK; - if (pci_set_dma_mask(pci, dma_mask) < 0 || - pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || + pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { printk(KERN_ERR "architecture does not support PCI " - "busmaster DMA with mask 0x%x\n", dma_mask); + "busmaster DMA with mask 0x%llx\n", + CT_XFI_DMA_MASK); err = -ENXIO; goto error1; } -- cgit v1.2.1 From b7bbf876087e0e2c0ba723a8398083c9a9ac1dfd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:11:07 +0200 Subject: ALSA: ctxfi - Use native timer interrupt on emu20k1 emu20k1 has a native timer interrupt based on the audio clock, which is more accurate than the system timer (from the synchronization POV). This patch adds the code to handle this with multiple streams. The system timer is still used on emu20k2, and can be used also for emu20k1 easily by changing USE_SYSTEM_TIMER to 1 in cttimer.c. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/Makefile | 2 +- sound/pci/ctxfi/ct20k1reg.h | 2 + sound/pci/ctxfi/ctatc.c | 21 ++- sound/pci/ctxfi/ctatc.h | 9 +- sound/pci/ctxfi/cthardware.h | 19 ++ sound/pci/ctxfi/cthw20k1.c | 43 ++++- sound/pci/ctxfi/ctpcm.c | 106 ++--------- sound/pci/ctxfi/cttimer.c | 417 +++++++++++++++++++++++++++++++++++++++++++ sound/pci/ctxfi/cttimer.h | 29 +++ 9 files changed, 543 insertions(+), 105 deletions(-) create mode 100644 sound/pci/ctxfi/cttimer.c create mode 100644 sound/pci/ctxfi/cttimer.h (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile index 29043237f9f8..15075f89e98a 100644 --- a/sound/pci/ctxfi/Makefile +++ b/sound/pci/ctxfi/Makefile @@ -1,5 +1,5 @@ snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ - ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \ + ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \ cthw20k2.o cthw20k1.o obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h index c62e6775dab3..f2e34e3f27ee 100644 --- a/sound/pci/ctxfi/ct20k1reg.h +++ b/sound/pci/ctxfi/ct20k1reg.h @@ -589,6 +589,8 @@ #define WC 0x1C6000 #define TIMR 0x1C6004 +# define TIMR_IE (1<<15) +# define TIMR_IP (1<<14) #define GIP 0x1C6010 #define GIE 0x1C6014 diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 684947546d81..10b741977dd7 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -22,6 +22,7 @@ #include "ctsrc.h" #include "ctamixer.h" #include "ctdaio.h" +#include "cttimer.h" #include #include #include @@ -307,6 +308,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) src = apcm->src; } + ct_timer_prepare(apcm->timer); + return 0; error1: @@ -389,6 +392,7 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) src->ops->set_state(src, SRC_STATE_INIT); src->ops->commit_write(src); + ct_timer_start(apcm->timer); return 0; } @@ -397,6 +401,8 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct src *src = NULL; int i = 0; + ct_timer_stop(apcm->timer); + src = apcm->src; src->ops->set_bm(src, 0); src->ops->set_state(src, SRC_STATE_OFF); @@ -701,6 +707,8 @@ static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) } } + ct_timer_prepare(apcm->timer); + return 0; } @@ -749,6 +757,7 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) /* Enable relevant SRCs synchronously */ src_mgr->commit_write(src_mgr); + ct_timer_start(apcm->timer); return 0; } @@ -906,6 +915,8 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) dao->ops->set_right_input(dao, &amixer->rsc); spin_unlock_irqrestore(&atc->atc_lock, flags); + ct_timer_prepare(apcm->timer); + return 0; } @@ -1100,6 +1111,11 @@ static int ct_atc_destroy(struct ct_atc *atc) if (NULL == atc) return 0; + if (atc->timer) { + ct_timer_free(atc->timer); + atc->timer = NULL; + } + /* Stop hardware and disable all interrupts */ if (NULL != atc->hw) ((struct hw *)atc->hw)->card_stop(atc->hw); @@ -1586,6 +1602,10 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, /* Build topology */ atc_connect_resources(atc); + atc->timer = ct_timer_new(atc); + if (!atc->timer) + goto error1; + atc->create_alsa_devs = ct_create_alsa_devs; err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); @@ -1602,4 +1622,3 @@ error1: printk(KERN_ERR "ctxfi: Something wrong!!!\n"); return err; } - diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index b86d12cd4a19..a3f9b1bc7dcc 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -59,16 +59,15 @@ struct ct_atc_chip_details { }; struct ct_atc; +struct ct_timer; +struct ct_timer_instance; /* alsa pcm stream descriptor */ struct ct_atc_pcm { struct snd_pcm_substream *substream; void (*interrupt)(struct ct_atc_pcm *apcm); + struct ct_timer_instance *timer; unsigned int started:1; - unsigned int stop_timer:1; - struct timer_list timer; - spinlock_t timer_lock; - unsigned int position; /* Only mono and interleaved modes are supported now. */ struct ct_vm_block *vm_block; @@ -144,6 +143,8 @@ struct ct_atc { unsigned char n_src; unsigned char n_srcimp; unsigned char n_pcm; + + struct ct_timer *timer; }; diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index b0512df8b334..35350cf9d2f2 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -145,6 +145,12 @@ struct hw { int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); int (*daio_mgr_commit_write)(struct hw *hw, void *blk); + int (*set_timer_irq)(struct hw *hw, int enable); + int (*set_timer_tick)(struct hw *hw, unsigned int tick); + + void (*irq_callback)(void *data, unsigned int bit); + void *irq_callback_data; + struct pci_dev *pci; /* the pci kernel structure of this card */ int irq; unsigned long io_base; @@ -157,4 +163,17 @@ int destroy_hw_obj(struct hw *hw); unsigned int get_field(unsigned int data, unsigned int field); void set_field(unsigned int *data, unsigned int field, unsigned int value); +/* IRQ bits */ +#define PLL_INT (1 << 10) /* PLL input-clock out-of-range */ +#define FI_INT (1 << 9) /* forced interrupt */ +#define IT_INT (1 << 8) /* timer interrupt */ +#define PCI_INT (1 << 7) /* PCI bus error pending */ +#define URT_INT (1 << 6) /* UART Tx/Rx */ +#define GPI_INT (1 << 5) /* GPI pin */ +#define MIX_INT (1 << 4) /* mixer parameter segment FIFO channels */ +#define DAI_INT (1 << 3) /* DAI (SR-tracker or SPDIF-receiver) */ +#define TP_INT (1 << 2) /* transport priority queue */ +#define DSP_INT (1 << 1) /* DSP */ +#define SRC_INT (1 << 0) /* SRC channels */ + #endif /* CTHARDWARE_H */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index e530a6d60422..550b30a2bcf1 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1171,6 +1171,21 @@ static int daio_mgr_put_ctrl_blk(void *blk) return 0; } +/* Timer interrupt */ +static int set_timer_irq(struct hw *hw, int enable) +{ + hw_write_20kx(hw, GIE, enable ? IT_INT : 0); + return 0; +} + +static int set_timer_tick(struct hw *hw, unsigned int ticks) +{ + if (ticks) + ticks |= TIMR_IE | TIMR_IP; + hw_write_20kx(hw, TIMR, ticks); + return 0; +} + /* Card hardware initialization block */ struct dac_conf { unsigned int msr; /* master sample rate in rsrs */ @@ -1878,6 +1893,22 @@ static int uaa_to_xfi(struct pci_dev *pci) return 0; } +static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id) +{ + struct hw *hw = dev_id; + unsigned int status; + + status = hw_read_20kx(hw, GIP); + if (!status) + return IRQ_NONE; + + if (hw->irq_callback) + hw->irq_callback(hw->irq_callback_data, status); + + hw_write_20kx(hw, GIP, status); + return IRQ_HANDLED; +} + static int hw_card_start(struct hw *hw) { int err = 0; @@ -1914,12 +1945,13 @@ static int hw_card_start(struct hw *hw) hw->io_base = pci_resource_start(pci, 0); } - /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, - atc->chip_details->nm_card, hw))) { + err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED, + "ctxfi", hw); + if (err < 0) { + printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); goto error2; } hw->irq = pci->irq; - */ pci_set_master(pci); @@ -1936,6 +1968,8 @@ error1: static int hw_card_stop(struct hw *hw) { /* TODO: Disable interrupt and so on... */ + if (hw->irq >= 0) + synchronize_irq(hw->irq); return 0; } @@ -2215,6 +2249,9 @@ int create_20k1_hw_obj(struct hw **rhw) hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; hw->daio_mgr_commit_write = daio_mgr_commit_write; + hw->set_timer_irq = set_timer_irq; + hw->set_timer_tick = set_timer_tick; + *rhw = hw; return 0; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 52ddf19d83bb..32b742dcd538 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -16,6 +16,7 @@ */ #include "ctpcm.h" +#include "cttimer.h" #include /* Hardware descriptions for playback */ @@ -108,6 +109,7 @@ static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); atc->pcm_release_resources(atc, apcm); + ct_timer_instance_free(apcm->timer); kfree(apcm); runtime->private_data = NULL; } @@ -124,8 +126,6 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) if (NULL == apcm) return -ENOMEM; - spin_lock_init(&apcm->timer_lock); - apcm->stop_timer = 0; apcm->substream = substream; apcm->interrupt = ct_atc_pcm_interrupt; runtime->private_data = apcm; @@ -153,6 +153,10 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) return err; } + apcm->timer = ct_timer_instance_new(atc->timer, apcm); + if (!apcm->timer) + return -ENOMEM; + return 0; } @@ -182,89 +186,6 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } -static void ct_pcm_timer_callback(unsigned long data) -{ - struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; - struct snd_pcm_substream *substream = apcm->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int period_size = runtime->period_size; - unsigned int buffer_size = runtime->buffer_size; - unsigned long flags; - unsigned int position = 0, dist = 0, interval = 0; - - position = substream->ops->pointer(substream); - dist = (position + buffer_size - apcm->position) % buffer_size; - if ((dist >= period_size) || - (position/period_size != apcm->position/period_size)) { - apcm->interrupt(apcm); - apcm->position = position; - } - /* Add extra HZ*5/1000 to avoid overrun issue when recording - * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ - interval = ((period_size - (position % period_size)) - * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; - spin_lock_irqsave(&apcm->timer_lock, flags); - apcm->timer.expires = jiffies + interval; - if (!apcm->stop_timer) - add_timer(&apcm->timer); - - spin_unlock_irqrestore(&apcm->timer_lock, flags); -} - -static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) -{ - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - if (timer_pending(&apcm->timer)) { - /* The timer has already been started. */ - spin_unlock_irqrestore(&apcm->timer_lock, flags); - return 0; - } - - init_timer(&apcm->timer); - apcm->timer.data = (unsigned long)apcm; - apcm->timer.function = ct_pcm_timer_callback; - spin_unlock_irqrestore(&apcm->timer_lock, flags); - apcm->position = 0; - - return 0; -} - -static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) -{ - struct snd_pcm_runtime *runtime = apcm->substream->runtime; - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - if (timer_pending(&apcm->timer)) { - /* The timer has already been started. */ - spin_unlock_irqrestore(&apcm->timer_lock, flags); - return 0; - } - - apcm->timer.expires = jiffies + (runtime->period_size * HZ + - (runtime->rate - 1)) / runtime->rate; - apcm->stop_timer = 0; - add_timer(&apcm->timer); - spin_unlock_irqrestore(&apcm->timer_lock, flags); - - return 0; -} - -static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) -{ - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - apcm->stop_timer = 1; - del_timer(&apcm->timer); - spin_unlock_irqrestore(&apcm->timer_lock, flags); - - try_to_del_timer_sync(&apcm->timer); - - return 0; -} static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) { @@ -283,8 +204,6 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) return err; } - ct_pcm_timer_prepare(apcm); - return 0; } @@ -300,12 +219,10 @@ ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: atc->pcm_playback_start(atc, apcm); - ct_pcm_timer_start(apcm); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ct_pcm_timer_stop(apcm); atc->pcm_playback_stop(atc, apcm); break; default: @@ -341,9 +258,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) if (NULL == apcm) return -ENOMEM; - spin_lock_init(&apcm->timer_lock); apcm->started = 0; - apcm->stop_timer = 0; apcm->substream = substream; apcm->interrupt = ct_atc_pcm_interrupt; runtime->private_data = apcm; @@ -365,6 +280,10 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) return err; } + apcm->timer = ct_timer_instance_new(atc->timer, apcm); + if (!apcm->timer) + return -ENOMEM; + return 0; } @@ -388,8 +307,6 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) return err; } - ct_pcm_timer_prepare(apcm); - return 0; } @@ -403,14 +320,11 @@ ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: atc->pcm_capture_start(atc, apcm); - ct_pcm_timer_start(apcm); break; case SNDRV_PCM_TRIGGER_STOP: - ct_pcm_timer_stop(apcm); atc->pcm_capture_stop(atc, apcm); break; default: - ct_pcm_timer_stop(apcm); atc->pcm_capture_stop(atc, apcm); break; } diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c new file mode 100644 index 000000000000..3acb26d0c4cc --- /dev/null +++ b/sound/pci/ctxfi/cttimer.c @@ -0,0 +1,417 @@ +/* + * PCM timer handling on ctxfi + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + */ + +#include +#include +#include +#include "ctatc.h" +#include "cthardware.h" +#include "cttimer.h" + +struct ct_timer_ops { + void (*init)(struct ct_timer_instance *); + void (*prepare)(struct ct_timer_instance *); + void (*start)(struct ct_timer_instance *); + void (*stop)(struct ct_timer_instance *); + void (*free_instance)(struct ct_timer_instance *); + void (*interrupt)(struct ct_timer *); + void (*free_global)(struct ct_timer *); +}; + +/* timer instance -- assigned to each PCM stream */ +struct ct_timer_instance { + spinlock_t lock; + struct ct_timer *timer_base; + struct ct_atc_pcm *apcm; + struct snd_pcm_substream *substream; + struct timer_list timer; + struct list_head instance_list; + struct list_head running_list; + unsigned int position; + unsigned int frag_count; + unsigned int running:1; + unsigned int need_update:1; +}; + +/* timer instance manager */ +struct ct_timer { + spinlock_t lock; /* global timer lock (for xfitimer) */ + spinlock_t list_lock; /* lock for instance list */ + struct ct_atc *atc; + struct ct_timer_ops *ops; + struct list_head instance_head; + struct list_head running_head; + unsigned int irq_handling:1; /* in IRQ handling */ + unsigned int reprogram:1; /* need to reprogram the internval */ + unsigned int running:1; /* global timer running */ +}; + + +/* + * system-timer-based updates + */ + +static void ct_systimer_callback(unsigned long data) +{ + struct ct_timer_instance *ti = (struct ct_timer_instance *)data; + struct snd_pcm_substream *substream = ti->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = ti->apcm; + unsigned int period_size = runtime->period_size; + unsigned int buffer_size = runtime->buffer_size; + unsigned long flags; + unsigned int position, dist, interval; + + position = substream->ops->pointer(substream); + dist = (position + buffer_size - ti->position) % buffer_size; + if (dist >= period_size || + position / period_size != ti->position / period_size) { + apcm->interrupt(apcm); + ti->position = position; + } + /* Add extra HZ*5/1000 to avoid overrun issue when recording + * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ + interval = ((period_size - (position % period_size)) + * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; + spin_lock_irqsave(&ti->lock, flags); + if (ti->running) + mod_timer(&ti->timer, jiffies + interval); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_init(struct ct_timer_instance *ti) +{ + setup_timer(&ti->timer, ct_systimer_callback, + (unsigned long)ti); +} + +static void ct_systimer_start(struct ct_timer_instance *ti) +{ + struct snd_pcm_runtime *runtime = ti->substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&ti->lock, flags); + ti->running = 1; + mod_timer(&ti->timer, + jiffies + (runtime->period_size * HZ + + (runtime->rate - 1)) / runtime->rate); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_stop(struct ct_timer_instance *ti) +{ + unsigned long flags; + + spin_lock_irqsave(&ti->lock, flags); + ti->running = 0; + del_timer(&ti->timer); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_prepare(struct ct_timer_instance *ti) +{ + ct_systimer_stop(ti); + try_to_del_timer_sync(&ti->timer); +} + +#define ct_systimer_free ct_systimer_prepare + +static struct ct_timer_ops ct_systimer_ops = { + .init = ct_systimer_init, + .free_instance = ct_systimer_free, + .prepare = ct_systimer_prepare, + .start = ct_systimer_start, + .stop = ct_systimer_stop, +}; + + +/* + * Handling multiple streams using a global emu20k1 timer irq + */ + +#define CT_TIMER_FREQ 48000 +#define MAX_TICKS ((1 << 13) - 1) + +static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks) +{ + struct hw *hw = atimer->atc->hw; + if (ticks > MAX_TICKS) + ticks = MAX_TICKS; + hw->set_timer_tick(hw, ticks); + if (!atimer->running) + hw->set_timer_irq(hw, 1); + atimer->running = 1; +} + +static void ct_xfitimer_irq_stop(struct ct_timer *atimer) +{ + if (atimer->running) { + struct hw *hw = atimer->atc->hw; + hw->set_timer_irq(hw, 0); + hw->set_timer_tick(hw, 0); + atimer->running = 0; + } +} + +/* + * reprogram the timer interval; + * checks the running instance list and determines the next timer interval. + * also updates the each stream position, returns the number of streams + * to call snd_pcm_period_elapsed() appropriately + * + * call this inside the lock and irq disabled + */ +static int ct_xfitimer_reprogram(struct ct_timer *atimer) +{ + struct ct_timer_instance *ti; + int min_intr = -1; + int updates = 0; + + list_for_each_entry(ti, &atimer->running_head, running_list) { + struct snd_pcm_runtime *runtime; + unsigned int pos, diff; + int intr; + runtime = ti->substream->runtime; + pos = ti->substream->ops->pointer(ti->substream); + if (pos < ti->position) + diff = runtime->buffer_size - ti->position + pos; + else + diff = pos - ti->position; + ti->position = pos; + while (diff >= ti->frag_count) { + ti->frag_count += runtime->period_size; + ti->need_update = 1; + updates++; + } + ti->frag_count -= diff; + intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ, + runtime->rate); + if (min_intr < 0 || intr < min_intr) + min_intr = intr; + } + + if (min_intr > 0) + ct_xfitimer_irq_rearm(atimer, min_intr); + else + ct_xfitimer_irq_stop(atimer); + + atimer->reprogram = 0; /* clear flag */ + return updates; +} + +/* look through the instance list and call period_elapsed if needed */ +static void ct_xfitimer_check_period(struct ct_timer *atimer) +{ + struct ct_timer_instance *ti; + unsigned long flags; + + spin_lock_irqsave(&atimer->list_lock, flags); + list_for_each_entry(ti, &atimer->instance_head, instance_list) { + if (ti->need_update) { + ti->need_update = 0; + ti->apcm->interrupt(ti->apcm); + } + } + spin_unlock_irqrestore(&atimer->list_lock, flags); +} + +/* Handle timer-interrupt */ +static void ct_xfitimer_callback(struct ct_timer *atimer) +{ + int update; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + atimer->irq_handling = 1; + do { + update = ct_xfitimer_reprogram(atimer); + spin_unlock(&atimer->lock); + if (update) + ct_xfitimer_check_period(atimer); + spin_lock(&atimer->lock); + } while (atimer->reprogram); + atimer->irq_handling = 0; + spin_unlock_irqrestore(&atimer->lock, flags); +} + +static void ct_xfitimer_prepare(struct ct_timer_instance *ti) +{ + ti->frag_count = ti->substream->runtime->period_size; + ti->need_update = 0; +} + + +/* start/stop the timer */ +static void ct_xfitimer_update(struct ct_timer *atimer) +{ + unsigned long flags; + int update; + + if (atimer->irq_handling) { + /* reached from IRQ handler; let it handle later */ + atimer->reprogram = 1; + return; + } + + spin_lock_irqsave(&atimer->lock, flags); + ct_xfitimer_irq_stop(atimer); + update = ct_xfitimer_reprogram(atimer); + spin_unlock_irqrestore(&atimer->lock, flags); + if (update) + ct_xfitimer_check_period(atimer); +} + +static void ct_xfitimer_start(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + list_add(&ti->running_list, &atimer->running_head); + spin_unlock_irqrestore(&atimer->lock, flags); + ct_xfitimer_update(atimer); +} + +static void ct_xfitimer_stop(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + list_del_init(&ti->running_list); + ti->need_update = 0; + spin_unlock_irqrestore(&atimer->lock, flags); + ct_xfitimer_update(atimer); +} + +static void ct_xfitimer_free_global(struct ct_timer *atimer) +{ + ct_xfitimer_irq_stop(atimer); +} + +static struct ct_timer_ops ct_xfitimer_ops = { + .prepare = ct_xfitimer_prepare, + .start = ct_xfitimer_start, + .stop = ct_xfitimer_stop, + .interrupt = ct_xfitimer_callback, + .free_global = ct_xfitimer_free_global, +}; + +/* + * timer instance + */ + +struct ct_timer_instance * +ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm) +{ + struct ct_timer_instance *ti; + + ti = kzalloc(sizeof(*ti), GFP_KERNEL); + if (!ti) + return NULL; + spin_lock_init(&ti->lock); + INIT_LIST_HEAD(&ti->instance_list); + INIT_LIST_HEAD(&ti->running_list); + ti->timer_base = atimer; + ti->apcm = apcm; + ti->substream = apcm->substream; + if (atimer->ops->init) + atimer->ops->init(ti); + + spin_lock_irq(&atimer->list_lock); + list_add(&ti->instance_list, &atimer->instance_head); + spin_unlock_irq(&atimer->list_lock); + + return ti; +} + +void ct_timer_prepare(struct ct_timer_instance *ti) +{ + if (ti->timer_base->ops->prepare) + ti->timer_base->ops->prepare(ti); + ti->position = 0; + ti->running = 0; +} + +void ct_timer_start(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + atimer->ops->start(ti); +} + +void ct_timer_stop(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + atimer->ops->stop(ti); +} + +void ct_timer_instance_free(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + + atimer->ops->stop(ti); /* to be sure */ + if (atimer->ops->free_instance) + atimer->ops->free_instance(ti); + + spin_lock_irq(&atimer->list_lock); + list_del(&ti->instance_list); + spin_unlock_irq(&atimer->list_lock); + + kfree(ti); +} + +/* + * timer manager + */ + +#define USE_SYSTEM_TIMER 0 + +static void ct_timer_interrupt(void *data, unsigned int status) +{ + struct ct_timer *timer = data; + + /* Interval timer interrupt */ + if ((status & IT_INT) && timer->ops->interrupt) + timer->ops->interrupt(timer); +} + +struct ct_timer *ct_timer_new(struct ct_atc *atc) +{ + struct ct_timer *atimer; + struct hw *hw; + + atimer = kzalloc(sizeof(*atimer), GFP_KERNEL); + if (!atimer) + return NULL; + spin_lock_init(&atimer->lock); + spin_lock_init(&atimer->list_lock); + INIT_LIST_HEAD(&atimer->instance_head); + INIT_LIST_HEAD(&atimer->running_head); + atimer->atc = atc; + hw = atc->hw; + if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { + printk(KERN_INFO "ctxfi: Use xfi-native timer\n"); + atimer->ops = &ct_xfitimer_ops; + hw->irq_callback_data = atimer; + hw->irq_callback = ct_timer_interrupt; + } else { + printk(KERN_INFO "ctxfi: Use system timer\n"); + atimer->ops = &ct_systimer_ops; + } + return atimer; +} + +void ct_timer_free(struct ct_timer *atimer) +{ + struct hw *hw = atimer->atc->hw; + hw->irq_callback = NULL; + if (atimer->ops->free_global) + atimer->ops->free_global(atimer); + kfree(atimer); +} + diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h new file mode 100644 index 000000000000..979348229291 --- /dev/null +++ b/sound/pci/ctxfi/cttimer.h @@ -0,0 +1,29 @@ +/* + * Timer handling + */ + +#ifndef __CTTIMER_H +#define __CTTIMER_H + +#include +#include +#include + +struct snd_pcm_substream; +struct ct_atc; +struct ct_atc_pcm; + +struct ct_timer; +struct ct_timer_instance; + +struct ct_timer *ct_timer_new(struct ct_atc *atc); +void ct_timer_free(struct ct_timer *atimer); + +struct ct_timer_instance * +ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm); +void ct_timer_instance_free(struct ct_timer_instance *ti); +void ct_timer_start(struct ct_timer_instance *ti); +void ct_timer_stop(struct ct_timer_instance *ti); +void ct_timer_prepare(struct ct_timer_instance *ti); + +#endif /* __CTTIMER_H */ -- cgit v1.2.1 From 775ffa1d3e5a550dd2c9d947d773021c61531b36 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:12:16 +0200 Subject: ALSA: ctxfi - Set periods_min to 2 Set 2 to minimal periods of playback pcm setups, too. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctpcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 32b742dcd538..a0bd31c6090d 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -40,7 +40,7 @@ static struct snd_pcm_hardware ct_pcm_playback_hw = { .buffer_bytes_max = (128*1024), .period_bytes_min = (64), .period_bytes_max = (128*1024), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, .fifo_size = 0, }; @@ -62,7 +62,7 @@ static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = { .buffer_bytes_max = (128*1024), .period_bytes_min = (64), .period_bytes_max = (128*1024), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, .fifo_size = 0, }; -- cgit v1.2.1 From 2a36f67f8c81f0babda0e811c760b7bfa971010b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:34:10 +0200 Subject: ALSA: ctxfi - Clean up / optimize - Use static tables instead of assigining each funciton pointer - Add __devinit* to appropriate places; pcm, mixer and timer cannot be marked because they are kept in the function table that lives long - Move create_alsa_devs function out of struct ct_atc to mark it __devinit Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 82 ++++++++--------- sound/pci/ctxfi/ctatc.h | 3 +- sound/pci/ctxfi/cthardware.c | 4 +- sound/pci/ctxfi/cthw20k1.c | 205 +++++++++++++++++++++---------------------- sound/pci/ctxfi/cthw20k2.c | 195 ++++++++++++++++++++-------------------- sound/pci/ctxfi/xfi.c | 2 +- 6 files changed, 243 insertions(+), 248 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 10b741977dd7..9b1324544db0 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1202,7 +1202,7 @@ static int atc_dev_free(struct snd_device *dev) return ct_atc_destroy(atc); } -static int atc_identify_card(struct ct_atc *atc) +static int __devinit atc_identify_card(struct ct_atc *atc) { u16 subsys; u8 revision; @@ -1243,7 +1243,7 @@ static int atc_identify_card(struct ct_atc *atc) return 0; } -static int ct_create_alsa_devs(struct ct_atc *atc) +int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) { enum CTALSADEVS i; struct hw *hw = atc->hw; @@ -1277,7 +1277,7 @@ static int ct_create_alsa_devs(struct ct_atc *atc) return 0; } -static int atc_create_hw_devs(struct ct_atc *atc) +static int __devinit atc_create_hw_devs(struct ct_atc *atc) { struct hw *hw = NULL; struct card_conf info = {0}; @@ -1313,7 +1313,7 @@ static int atc_create_hw_devs(struct ct_atc *atc) return 0; } -static int atc_get_resources(struct ct_atc *atc) +static int __devinit atc_get_resources(struct ct_atc *atc) { struct daio_desc da_desc = {0}; struct daio_mgr *daio_mgr = NULL; @@ -1423,7 +1423,7 @@ static int atc_get_resources(struct ct_atc *atc) return 0; } -static void +static void __devinit atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, struct src **srcs, struct srcimp **srcimps) { @@ -1462,7 +1462,7 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */ } -static void atc_connect_resources(struct ct_atc *atc) +static void __devinit atc_connect_resources(struct ct_atc *atc) { struct dai *dai = NULL; struct dao *dao = NULL; @@ -1508,37 +1508,35 @@ static void atc_connect_resources(struct ct_atc *atc) } } -static void atc_set_ops(struct ct_atc *atc) -{ - /* Set operations */ - atc->map_audio_buffer = ct_map_audio_buffer; - atc->unmap_audio_buffer = ct_unmap_audio_buffer; - atc->pcm_playback_prepare = atc_pcm_playback_prepare; - atc->pcm_release_resources = atc_pcm_release_resources; - atc->pcm_playback_start = atc_pcm_playback_start; - atc->pcm_playback_stop = atc_pcm_stop; - atc->pcm_playback_position = atc_pcm_playback_position; - atc->pcm_capture_prepare = atc_pcm_capture_prepare; - atc->pcm_capture_start = atc_pcm_capture_start; - atc->pcm_capture_stop = atc_pcm_stop; - atc->pcm_capture_position = atc_pcm_capture_position; - atc->spdif_passthru_playback_prepare = spdif_passthru_playback_prepare; - atc->get_ptp_phys = atc_get_ptp_phys; - atc->select_line_in = atc_select_line_in; - atc->select_mic_in = atc_select_mic_in; - atc->select_digit_io = atc_select_digit_io; - atc->line_front_unmute = atc_line_front_unmute; - atc->line_surround_unmute = atc_line_surround_unmute; - atc->line_clfe_unmute = atc_line_clfe_unmute; - atc->line_rear_unmute = atc_line_rear_unmute; - atc->line_in_unmute = atc_line_in_unmute; - atc->spdif_out_unmute = atc_spdif_out_unmute; - atc->spdif_in_unmute = atc_spdif_in_unmute; - atc->spdif_out_get_status = atc_spdif_out_get_status; - atc->spdif_out_set_status = atc_spdif_out_set_status; - atc->spdif_out_passthru = atc_spdif_out_passthru; - atc->have_digit_io_switch = atc_have_digit_io_switch; -} +static struct ct_atc atc_preset __devinitdata = { + .map_audio_buffer = ct_map_audio_buffer, + .unmap_audio_buffer = ct_unmap_audio_buffer, + .pcm_playback_prepare = atc_pcm_playback_prepare, + .pcm_release_resources = atc_pcm_release_resources, + .pcm_playback_start = atc_pcm_playback_start, + .pcm_playback_stop = atc_pcm_stop, + .pcm_playback_position = atc_pcm_playback_position, + .pcm_capture_prepare = atc_pcm_capture_prepare, + .pcm_capture_start = atc_pcm_capture_start, + .pcm_capture_stop = atc_pcm_stop, + .pcm_capture_position = atc_pcm_capture_position, + .spdif_passthru_playback_prepare = spdif_passthru_playback_prepare, + .get_ptp_phys = atc_get_ptp_phys, + .select_line_in = atc_select_line_in, + .select_mic_in = atc_select_mic_in, + .select_digit_io = atc_select_digit_io, + .line_front_unmute = atc_line_front_unmute, + .line_surround_unmute = atc_line_surround_unmute, + .line_clfe_unmute = atc_line_clfe_unmute, + .line_rear_unmute = atc_line_rear_unmute, + .line_in_unmute = atc_line_in_unmute, + .spdif_out_unmute = atc_spdif_out_unmute, + .spdif_in_unmute = atc_spdif_in_unmute, + .spdif_out_get_status = atc_spdif_out_get_status, + .spdif_out_set_status = atc_spdif_out_set_status, + .spdif_out_passthru = atc_spdif_out_passthru, + .have_digit_io_switch = atc_have_digit_io_switch, +}; /** * ct_atc_create - create and initialize a hardware manager @@ -1552,7 +1550,7 @@ static void atc_set_ops(struct ct_atc *atc) * Returns 0 if suceeds, or negative error code if fails. */ -int ct_atc_create(struct snd_card *card, struct pci_dev *pci, +int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, struct ct_atc **ratc) { struct ct_atc *atc = NULL; @@ -1567,14 +1565,14 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, if (NULL == atc) return -ENOMEM; + /* Set operations */ + *atc = atc_preset; + atc->card = card; atc->pci = pci; atc->rsr = rsr; atc->msr = msr; - /* Set operations */ - atc_set_ops(atc); - spin_lock_init(&atc->atc_lock); /* Find card model */ @@ -1606,8 +1604,6 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, if (!atc->timer) goto error1; - atc->create_alsa_devs = ct_create_alsa_devs; - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); if (err < 0) goto error1; diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index a3f9b1bc7dcc..04459aa0d4d9 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -91,8 +91,6 @@ struct ct_atc { const struct ct_atc_chip_details *chip_details; enum CTCARDS model; - /* Create all alsa devices */ - int (*create_alsa_devs)(struct ct_atc *atc); struct ct_vm *vm; /* device virtual memory manager for this card */ int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); @@ -151,5 +149,6 @@ struct ct_atc { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, struct ct_atc **ratc); +int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); #endif /* CTATC_H */ diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 8e58860f641c..53d1acadc0e8 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -20,7 +20,7 @@ #include "cthw20k2.h" #include -static enum CHIPTYP get_chip_type(struct hw *hw) +static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) { enum CHIPTYP type = ATCNONE; @@ -39,7 +39,7 @@ static enum CHIPTYP get_chip_type(struct hw *hw) return type; } -int create_hw_obj(struct pci_dev *pci, struct hw **rhw) +int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) { int err = 0; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 550b30a2bcf1..df565c11fcca 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -2138,9 +2138,107 @@ static void hw_write_pci(struct hw *hw, u32 reg, u32 data) &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags); } -int create_20k1_hw_obj(struct hw **rhw) +static struct hw ct20k1_preset __devinitdata = { + .irq = -1, + + .card_init = hw_card_init, + .card_stop = hw_card_stop, + .pll_init = hw_pll_init, + .is_adc_source_selected = hw_is_adc_input_selected, + .select_adc_source = hw_adc_input_select, + .have_digit_io_switch = hw_have_digit_io_switch, + + .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk, + .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk, + .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk, + .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk, + .src_set_state = src_set_state, + .src_set_bm = src_set_bm, + .src_set_rsr = src_set_rsr, + .src_set_sf = src_set_sf, + .src_set_wr = src_set_wr, + .src_set_pm = src_set_pm, + .src_set_rom = src_set_rom, + .src_set_vo = src_set_vo, + .src_set_st = src_set_st, + .src_set_ie = src_set_ie, + .src_set_ilsz = src_set_ilsz, + .src_set_bp = src_set_bp, + .src_set_cisz = src_set_cisz, + .src_set_ca = src_set_ca, + .src_set_sa = src_set_sa, + .src_set_la = src_set_la, + .src_set_pitch = src_set_pitch, + .src_set_dirty = src_set_dirty, + .src_set_clear_zbufs = src_set_clear_zbufs, + .src_set_dirty_all = src_set_dirty_all, + .src_commit_write = src_commit_write, + .src_get_ca = src_get_ca, + .src_get_dirty = src_get_dirty, + .src_dirty_conj_mask = src_dirty_conj_mask, + .src_mgr_enbs_src = src_mgr_enbs_src, + .src_mgr_enb_src = src_mgr_enb_src, + .src_mgr_dsb_src = src_mgr_dsb_src, + .src_mgr_commit_write = src_mgr_commit_write, + + .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk, + .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk, + .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc, + .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser, + .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt, + .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr, + .srcimp_mgr_commit_write = srcimp_mgr_commit_write, + + .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk, + .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk, + .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk, + .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk, + .amixer_set_mode = amixer_set_mode, + .amixer_set_iv = amixer_set_iv, + .amixer_set_x = amixer_set_x, + .amixer_set_y = amixer_set_y, + .amixer_set_sadr = amixer_set_sadr, + .amixer_set_se = amixer_set_se, + .amixer_set_dirty = amixer_set_dirty, + .amixer_set_dirty_all = amixer_set_dirty_all, + .amixer_commit_write = amixer_commit_write, + .amixer_get_y = amixer_get_y, + .amixer_get_dirty = amixer_get_dirty, + + .dai_get_ctrl_blk = dai_get_ctrl_blk, + .dai_put_ctrl_blk = dai_put_ctrl_blk, + .dai_srt_set_srco = dai_srt_set_srcr, + .dai_srt_set_srcm = dai_srt_set_srcl, + .dai_srt_set_rsr = dai_srt_set_rsr, + .dai_srt_set_drat = dai_srt_set_drat, + .dai_srt_set_ec = dai_srt_set_ec, + .dai_srt_set_et = dai_srt_set_et, + .dai_commit_write = dai_commit_write, + + .dao_get_ctrl_blk = dao_get_ctrl_blk, + .dao_put_ctrl_blk = dao_put_ctrl_blk, + .dao_set_spos = dao_set_spos, + .dao_commit_write = dao_commit_write, + .dao_get_spos = dao_get_spos, + + .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk, + .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk, + .daio_mgr_enb_dai = daio_mgr_enb_dai, + .daio_mgr_dsb_dai = daio_mgr_dsb_dai, + .daio_mgr_enb_dao = daio_mgr_enb_dao, + .daio_mgr_dsb_dao = daio_mgr_dsb_dao, + .daio_mgr_dao_init = daio_mgr_dao_init, + .daio_mgr_set_imaparc = daio_mgr_set_imaparc, + .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, + .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, + .daio_mgr_commit_write = daio_mgr_commit_write, + + .set_timer_irq = set_timer_irq, + .set_timer_tick = set_timer_tick, +}; + +int __devinit create_20k1_hw_obj(struct hw **rhw) { - struct hw *hw; struct hw20k1 *hw20k1; *rhw = NULL; @@ -2151,108 +2249,9 @@ int create_20k1_hw_obj(struct hw **rhw) spin_lock_init(&hw20k1->reg_20k1_lock); spin_lock_init(&hw20k1->reg_pci_lock); - hw = &hw20k1->hw; + hw20k1->hw = ct20k1_preset; - hw->io_base = 0; - hw->mem_base = (unsigned long)NULL; - hw->irq = -1; - - hw->card_init = hw_card_init; - hw->card_stop = hw_card_stop; - hw->pll_init = hw_pll_init; - hw->is_adc_source_selected = hw_is_adc_input_selected; - hw->select_adc_source = hw_adc_input_select; - hw->have_digit_io_switch = hw_have_digit_io_switch; - - hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; - hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; - hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; - hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; - hw->src_set_state = src_set_state; - hw->src_set_bm = src_set_bm; - hw->src_set_rsr = src_set_rsr; - hw->src_set_sf = src_set_sf; - hw->src_set_wr = src_set_wr; - hw->src_set_pm = src_set_pm; - hw->src_set_rom = src_set_rom; - hw->src_set_vo = src_set_vo; - hw->src_set_st = src_set_st; - hw->src_set_ie = src_set_ie; - hw->src_set_ilsz = src_set_ilsz; - hw->src_set_bp = src_set_bp; - hw->src_set_cisz = src_set_cisz; - hw->src_set_ca = src_set_ca; - hw->src_set_sa = src_set_sa; - hw->src_set_la = src_set_la; - hw->src_set_pitch = src_set_pitch; - hw->src_set_dirty = src_set_dirty; - hw->src_set_clear_zbufs = src_set_clear_zbufs; - hw->src_set_dirty_all = src_set_dirty_all; - hw->src_commit_write = src_commit_write; - hw->src_get_ca = src_get_ca; - hw->src_get_dirty = src_get_dirty; - hw->src_dirty_conj_mask = src_dirty_conj_mask; - hw->src_mgr_enbs_src = src_mgr_enbs_src; - hw->src_mgr_enb_src = src_mgr_enb_src; - hw->src_mgr_dsb_src = src_mgr_dsb_src; - hw->src_mgr_commit_write = src_mgr_commit_write; - - hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; - hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; - hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; - hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; - hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; - hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; - hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; - - hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; - hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; - hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; - hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; - hw->amixer_set_mode = amixer_set_mode; - hw->amixer_set_iv = amixer_set_iv; - hw->amixer_set_x = amixer_set_x; - hw->amixer_set_y = amixer_set_y; - hw->amixer_set_sadr = amixer_set_sadr; - hw->amixer_set_se = amixer_set_se; - hw->amixer_set_dirty = amixer_set_dirty; - hw->amixer_set_dirty_all = amixer_set_dirty_all; - hw->amixer_commit_write = amixer_commit_write; - hw->amixer_get_y = amixer_get_y; - hw->amixer_get_dirty = amixer_get_dirty; - - hw->dai_get_ctrl_blk = dai_get_ctrl_blk; - hw->dai_put_ctrl_blk = dai_put_ctrl_blk; - hw->dai_srt_set_srco = dai_srt_set_srcr; - hw->dai_srt_set_srcm = dai_srt_set_srcl; - hw->dai_srt_set_rsr = dai_srt_set_rsr; - hw->dai_srt_set_drat = dai_srt_set_drat; - hw->dai_srt_set_ec = dai_srt_set_ec; - hw->dai_srt_set_et = dai_srt_set_et; - hw->dai_commit_write = dai_commit_write; - - hw->dao_get_ctrl_blk = dao_get_ctrl_blk; - hw->dao_put_ctrl_blk = dao_put_ctrl_blk; - hw->dao_set_spos = dao_set_spos; - hw->dao_commit_write = dao_commit_write; - hw->dao_get_spos = dao_get_spos; - - hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; - hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; - hw->daio_mgr_enb_dai = daio_mgr_enb_dai; - hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; - hw->daio_mgr_enb_dao = daio_mgr_enb_dao; - hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; - hw->daio_mgr_dao_init = daio_mgr_dao_init; - hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; - hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; - hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; - hw->daio_mgr_commit_write = daio_mgr_commit_write; - - hw->set_timer_irq = set_timer_irq; - hw->set_timer_tick = set_timer_tick; - - *rhw = hw; + *rhw = &hw20k1->hw; return 0; } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 349728765f2c..041199fbae16 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2006,7 +2006,103 @@ static void hw_write_20kx(struct hw *hw, u32 reg, u32 data) writel(data, (void *)(hw->mem_base + reg)); } -int create_20k2_hw_obj(struct hw **rhw) +static struct hw ct20k2_preset __devinitdata = { + .irq = -1, + + .card_init = hw_card_init, + .card_stop = hw_card_stop, + .pll_init = hw_pll_init, + .is_adc_source_selected = hw_is_adc_input_selected, + .select_adc_source = hw_adc_input_select, + .have_digit_io_switch = hw_have_digit_io_switch, + + .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk, + .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk, + .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk, + .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk, + .src_set_state = src_set_state, + .src_set_bm = src_set_bm, + .src_set_rsr = src_set_rsr, + .src_set_sf = src_set_sf, + .src_set_wr = src_set_wr, + .src_set_pm = src_set_pm, + .src_set_rom = src_set_rom, + .src_set_vo = src_set_vo, + .src_set_st = src_set_st, + .src_set_ie = src_set_ie, + .src_set_ilsz = src_set_ilsz, + .src_set_bp = src_set_bp, + .src_set_cisz = src_set_cisz, + .src_set_ca = src_set_ca, + .src_set_sa = src_set_sa, + .src_set_la = src_set_la, + .src_set_pitch = src_set_pitch, + .src_set_dirty = src_set_dirty, + .src_set_clear_zbufs = src_set_clear_zbufs, + .src_set_dirty_all = src_set_dirty_all, + .src_commit_write = src_commit_write, + .src_get_ca = src_get_ca, + .src_get_dirty = src_get_dirty, + .src_dirty_conj_mask = src_dirty_conj_mask, + .src_mgr_enbs_src = src_mgr_enbs_src, + .src_mgr_enb_src = src_mgr_enb_src, + .src_mgr_dsb_src = src_mgr_dsb_src, + .src_mgr_commit_write = src_mgr_commit_write, + + .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk, + .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk, + .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc, + .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser, + .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt, + .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr, + .srcimp_mgr_commit_write = srcimp_mgr_commit_write, + + .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk, + .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk, + .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk, + .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk, + .amixer_set_mode = amixer_set_mode, + .amixer_set_iv = amixer_set_iv, + .amixer_set_x = amixer_set_x, + .amixer_set_y = amixer_set_y, + .amixer_set_sadr = amixer_set_sadr, + .amixer_set_se = amixer_set_se, + .amixer_set_dirty = amixer_set_dirty, + .amixer_set_dirty_all = amixer_set_dirty_all, + .amixer_commit_write = amixer_commit_write, + .amixer_get_y = amixer_get_y, + .amixer_get_dirty = amixer_get_dirty, + + .dai_get_ctrl_blk = dai_get_ctrl_blk, + .dai_put_ctrl_blk = dai_put_ctrl_blk, + .dai_srt_set_srco = dai_srt_set_srco, + .dai_srt_set_srcm = dai_srt_set_srcm, + .dai_srt_set_rsr = dai_srt_set_rsr, + .dai_srt_set_drat = dai_srt_set_drat, + .dai_srt_set_ec = dai_srt_set_ec, + .dai_srt_set_et = dai_srt_set_et, + .dai_commit_write = dai_commit_write, + + .dao_get_ctrl_blk = dao_get_ctrl_blk, + .dao_put_ctrl_blk = dao_put_ctrl_blk, + .dao_set_spos = dao_set_spos, + .dao_commit_write = dao_commit_write, + .dao_get_spos = dao_get_spos, + + .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk, + .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk, + .daio_mgr_enb_dai = daio_mgr_enb_dai, + .daio_mgr_dsb_dai = daio_mgr_dsb_dai, + .daio_mgr_enb_dao = daio_mgr_enb_dao, + .daio_mgr_dsb_dao = daio_mgr_dsb_dao, + .daio_mgr_dao_init = daio_mgr_dao_init, + .daio_mgr_set_imaparc = daio_mgr_set_imaparc, + .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, + .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, + .daio_mgr_commit_write = daio_mgr_commit_write, +}; + +int __devinit create_20k2_hw_obj(struct hw **rhw) { struct hw *hw; @@ -2015,102 +2111,7 @@ int create_20k2_hw_obj(struct hw **rhw) if (NULL == hw) return -ENOMEM; - hw->io_base = 0; - hw->mem_base = (unsigned long)NULL; - hw->irq = -1; - - hw->card_init = hw_card_init; - hw->card_stop = hw_card_stop; - hw->pll_init = hw_pll_init; - hw->is_adc_source_selected = hw_is_adc_input_selected; - hw->select_adc_source = hw_adc_input_select; - hw->have_digit_io_switch = hw_have_digit_io_switch; - - hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk; - hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk; - hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk; - hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk; - hw->src_set_state = src_set_state; - hw->src_set_bm = src_set_bm; - hw->src_set_rsr = src_set_rsr; - hw->src_set_sf = src_set_sf; - hw->src_set_wr = src_set_wr; - hw->src_set_pm = src_set_pm; - hw->src_set_rom = src_set_rom; - hw->src_set_vo = src_set_vo; - hw->src_set_st = src_set_st; - hw->src_set_ie = src_set_ie; - hw->src_set_ilsz = src_set_ilsz; - hw->src_set_bp = src_set_bp; - hw->src_set_cisz = src_set_cisz; - hw->src_set_ca = src_set_ca; - hw->src_set_sa = src_set_sa; - hw->src_set_la = src_set_la; - hw->src_set_pitch = src_set_pitch; - hw->src_set_dirty = src_set_dirty; - hw->src_set_clear_zbufs = src_set_clear_zbufs; - hw->src_set_dirty_all = src_set_dirty_all; - hw->src_commit_write = src_commit_write; - hw->src_get_ca = src_get_ca; - hw->src_get_dirty = src_get_dirty; - hw->src_dirty_conj_mask = src_dirty_conj_mask; - hw->src_mgr_enbs_src = src_mgr_enbs_src; - hw->src_mgr_enb_src = src_mgr_enb_src; - hw->src_mgr_dsb_src = src_mgr_dsb_src; - hw->src_mgr_commit_write = src_mgr_commit_write; - - hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk; - hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk; - hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc; - hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser; - hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt; - hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr; - hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write; - - hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk; - hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk; - hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk; - hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk; - hw->amixer_set_mode = amixer_set_mode; - hw->amixer_set_iv = amixer_set_iv; - hw->amixer_set_x = amixer_set_x; - hw->amixer_set_y = amixer_set_y; - hw->amixer_set_sadr = amixer_set_sadr; - hw->amixer_set_se = amixer_set_se; - hw->amixer_set_dirty = amixer_set_dirty; - hw->amixer_set_dirty_all = amixer_set_dirty_all; - hw->amixer_commit_write = amixer_commit_write; - hw->amixer_get_y = amixer_get_y; - hw->amixer_get_dirty = amixer_get_dirty; - - hw->dai_get_ctrl_blk = dai_get_ctrl_blk; - hw->dai_put_ctrl_blk = dai_put_ctrl_blk; - hw->dai_srt_set_srco = dai_srt_set_srco; - hw->dai_srt_set_srcm = dai_srt_set_srcm; - hw->dai_srt_set_rsr = dai_srt_set_rsr; - hw->dai_srt_set_drat = dai_srt_set_drat; - hw->dai_srt_set_ec = dai_srt_set_ec; - hw->dai_srt_set_et = dai_srt_set_et; - hw->dai_commit_write = dai_commit_write; - - hw->dao_get_ctrl_blk = dao_get_ctrl_blk; - hw->dao_put_ctrl_blk = dao_put_ctrl_blk; - hw->dao_set_spos = dao_set_spos; - hw->dao_commit_write = dao_commit_write; - hw->dao_get_spos = dao_get_spos; - - hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk; - hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk; - hw->daio_mgr_enb_dai = daio_mgr_enb_dai; - hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai; - hw->daio_mgr_enb_dao = daio_mgr_enb_dao; - hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao; - hw->daio_mgr_dao_init = daio_mgr_dao_init; - hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc; - hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt; - hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; - hw->daio_mgr_commit_write = daio_mgr_commit_write; - + *hw = ct20k2_preset; *rhw = hw; return 0; diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index bf232e7299ac..279dac6c34dd 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -86,7 +86,7 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) card->private_data = atc; /* Create alsa devices supported by this card */ - err = atc->create_alsa_devs(atc); + err = ct_atc_create_alsa_devs(atc); if (err < 0) goto error; -- cgit v1.2.1 From 032abb519c23000f8a0fae78bb460047f9129270 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 16:37:19 +0200 Subject: ALSA: ctxfi - Set device 0 for mixer control elements Mixer control elements are usually assigned to device 0. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctmixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 177c46e248db..fac783f585fc 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -1097,7 +1097,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, int err = 0; /* Create snd kcontrol instances on demand */ - vol_ctl.device = swh_ctl.device = device; + /* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */ err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer); if (err) return err; -- cgit v1.2.1 From 3f7440a6b771169e1f11fa582e53a4259b682809 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 17:40:04 +0200 Subject: ALSA: Clean up 64bit division functions Replace the house-made div64_32() with the standard div_u64*() functions. Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdsp.c | 7 +++---- sound/pci/rme9652/hdspm.c | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 314e73531bd1..bcfdbb5ebc40 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1047,7 +1048,6 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames) static void hdsp_set_dds_value(struct hdsp *hdsp, int rate) { u64 n; - u32 r; if (rate >= 112000) rate /= 4; @@ -1055,7 +1055,7 @@ static void hdsp_set_dds_value(struct hdsp *hdsp, int rate) rate /= 2; n = DDS_NUMERATOR; - div64_32(&n, rate, &r); + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); /* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS @@ -3097,7 +3097,6 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn static int hdsp_dds_offset(struct hdsp *hdsp) { u64 n; - u32 r; unsigned int dds_value = hdsp->dds_value; int system_sample_rate = hdsp->system_sample_rate; @@ -3109,7 +3108,7 @@ static int hdsp_dds_offset(struct hdsp *hdsp) * dds_value = n / rate * rate = n / dds_value */ - div64_32(&n, dds_value, &r); + n = div_u64(n, dds_value); if (system_sample_rate >= 112000) n *= 4; else if (system_sample_rate >= 56000) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index bac2dc0c5d85..0dce331a2a3b 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -831,7 +832,6 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) { u64 n; - u32 r; if (rate >= 112000) rate /= 4; @@ -844,7 +844,7 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) */ /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */ n = 110100480000000ULL; /* Value checked for AES32 and MADI */ - div64_32(&n, rate, &r); + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); hdspm_write(hdspm, HDSPM_freqReg, (u32)n); -- cgit v1.2.1 From 28cd4aa43de2b6d3b1e3385d450bfb31cbe8d72a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 17:58:00 +0200 Subject: ALSA: ctxfi - Add missing inclusion of linux/math64.h Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cttimer.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 3acb26d0c4cc..ceda74e356cb 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include "ctatc.h" -- cgit v1.2.1 From 54de6bc8b2437f642844cecb8d183df2368ffceb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 10:21:07 +0200 Subject: ALSA: ctxfi - Optimize the native timer handling using wc counter Optimize the timer update routine to look up wall clock once instead of checking the position of each stream at each timer update. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthardware.h | 1 + sound/pci/ctxfi/cthw20k1.c | 6 ++++ sound/pci/ctxfi/cttimer.c | 76 ++++++++++++++++++++++++++++---------------- 3 files changed, 55 insertions(+), 28 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 35350cf9d2f2..8f11644ddc92 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -147,6 +147,7 @@ struct hw { int (*set_timer_irq)(struct hw *hw, int enable); int (*set_timer_tick)(struct hw *hw, unsigned int tick); + unsigned int (*get_wc)(struct hw *hw); void (*irq_callback)(void *data, unsigned int bit); void *irq_callback_data; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index df565c11fcca..b165466e1a54 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1186,6 +1186,11 @@ static int set_timer_tick(struct hw *hw, unsigned int ticks) return 0; } +static unsigned int get_wc(struct hw *hw) +{ + return hw_read_20kx(hw, WC); +} + /* Card hardware initialization block */ struct dac_conf { unsigned int msr; /* master sample rate in rsrs */ @@ -2235,6 +2240,7 @@ static struct hw ct20k1_preset __devinitdata = { .set_timer_irq = set_timer_irq, .set_timer_tick = set_timer_tick, + .get_wc = get_wc, }; int __devinit create_20k1_hw_obj(struct hw **rhw) diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index ceda74e356cb..ec869a4fe2b3 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -47,6 +47,7 @@ struct ct_timer { struct ct_timer_ops *ops; struct list_head instance_head; struct list_head running_head; + unsigned int wc; /* current wallclock */ unsigned int irq_handling:1; /* in IRQ handling */ unsigned int reprogram:1; /* need to reprogram the internval */ unsigned int running:1; /* global timer running */ @@ -136,6 +137,7 @@ static struct ct_timer_ops ct_systimer_ops = { */ #define CT_TIMER_FREQ 48000 +#define MIN_TICKS 1 #define MAX_TICKS ((1 << 13) - 1) static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks) @@ -159,6 +161,12 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer) } } +static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer) +{ + struct hw *hw = atimer->atc->hw; + return hw->get_wc(hw); +} + /* * reprogram the timer interval; * checks the running instance list and determines the next timer interval. @@ -170,37 +178,46 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer) static int ct_xfitimer_reprogram(struct ct_timer *atimer) { struct ct_timer_instance *ti; - int min_intr = -1; + unsigned int min_intr = (unsigned int)-1; int updates = 0; + unsigned int wc, diff; + if (list_empty(&atimer->running_head)) { + ct_xfitimer_irq_stop(atimer); + atimer->reprogram = 0; /* clear flag */ + return 0; + } + + wc = ct_xfitimer_get_wc(atimer); + diff = wc - atimer->wc; + atimer->wc = wc; list_for_each_entry(ti, &atimer->running_head, running_list) { - struct snd_pcm_runtime *runtime; - unsigned int pos, diff; - int intr; - runtime = ti->substream->runtime; - pos = ti->substream->ops->pointer(ti->substream); - if (pos < ti->position) - diff = runtime->buffer_size - ti->position + pos; - else - diff = pos - ti->position; - ti->position = pos; - while (diff >= ti->frag_count) { - ti->frag_count += runtime->period_size; - ti->need_update = 1; - updates++; + if (ti->frag_count > diff) + ti->frag_count -= diff; + else { + unsigned int pos; + unsigned int period_size, rate; + + period_size = ti->substream->runtime->period_size; + rate = ti->substream->runtime->rate; + pos = ti->substream->ops->pointer(ti->substream); + if (pos / period_size != ti->position / period_size) { + ti->need_update = 1; + ti->position = pos; + updates++; + } + pos %= period_size; + pos = period_size - pos; + ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ + + rate - 1, rate); } - ti->frag_count -= diff; - intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ, - runtime->rate); - if (min_intr < 0 || intr < min_intr) - min_intr = intr; + if (ti->frag_count < min_intr) + min_intr = ti->frag_count; } - if (min_intr > 0) - ct_xfitimer_irq_rearm(atimer, min_intr); - else - ct_xfitimer_irq_stop(atimer); - + if (min_intr < MIN_TICKS) + min_intr = MIN_TICKS; + ct_xfitimer_irq_rearm(atimer, min_intr); atimer->reprogram = 0; /* clear flag */ return updates; } @@ -253,13 +270,14 @@ static void ct_xfitimer_update(struct ct_timer *atimer) unsigned long flags; int update; + spin_lock_irqsave(&atimer->lock, flags); if (atimer->irq_handling) { /* reached from IRQ handler; let it handle later */ atimer->reprogram = 1; + spin_unlock_irqrestore(&atimer->lock, flags); return; } - spin_lock_irqsave(&atimer->lock, flags); ct_xfitimer_irq_stop(atimer); update = ct_xfitimer_reprogram(atimer); spin_unlock_irqrestore(&atimer->lock, flags); @@ -273,6 +291,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti) unsigned long flags; spin_lock_irqsave(&atimer->lock, flags); + if (list_empty(&ti->running_list)) + atimer->wc = ct_xfitimer_get_wc(atimer); list_add(&ti->running_list, &atimer->running_head); spin_unlock_irqrestore(&atimer->lock, flags); ct_xfitimer_update(atimer); @@ -396,12 +416,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) atimer->atc = atc; hw = atc->hw; if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { - printk(KERN_INFO "ctxfi: Use xfi-native timer\n"); + snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n"); atimer->ops = &ct_xfitimer_ops; hw->irq_callback_data = atimer; hw->irq_callback = ct_timer_interrupt; } else { - printk(KERN_INFO "ctxfi: Use system timer\n"); + snd_printd(KERN_INFO "ctxfi: Use system timer\n"); atimer->ops = &ct_systimer_ops; } return atimer; -- cgit v1.2.1 From c4865679df5453d1bc94c534f4d94a364c0546df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 12:57:17 +0200 Subject: ALSA: ca0106 - Fix master volume scale The master volume dB scale was wrongly defined as 0.50dB setp while it must be 0.25dB step. Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106_mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 7724252c1ec5..c8c6f437f5b3 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -739,7 +739,7 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch } while (0) static __devinitdata -DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1); +DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1); static char *slave_vols[] __devinitdata = { "Analog Front Playback Volume", -- cgit v1.2.1 From d436dd063be605dc29f17b2cb0b99a852db89bed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 13:50:18 +0200 Subject: ALSA: ctxfi - Make volume controls more intuitive Change the volume control to dB scale (as the raw data seems so). Also added the TLV dB-scale information. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctmixer.c | 49 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index fac783f585fc..796156e4bd38 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -18,11 +18,12 @@ #include "ctmixer.h" #include "ctamixer.h" +#include #include #include #include #include -#include +#include enum CT_SUM_CTL { SUM_IN_F, @@ -292,6 +293,7 @@ set_switch_state(struct ct_mixer *mixer, mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START)); } +#if 0 /* not used */ /* Map integer value ranging from 0 to 65535 to 14-bit float value ranging * from 2^-6 to (1+1023/1024) */ static unsigned int uint16_to_float14(unsigned int x) @@ -331,6 +333,12 @@ static unsigned int float14_to_uint16(unsigned int x) return x; } +#endif /* not used */ + +#define VOL_SCALE 0x1c +#define VOL_MAX 0x100 + +static const DECLARE_TLV_DB_SCALE(ct_vol_db_scale, -6400, 25, 1); static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -338,8 +346,7 @@ static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 43690; - uinfo->value.integer.step = 128; + uinfo->value.integer.max = VOL_MAX; return 0; } @@ -349,15 +356,18 @@ static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol, { struct ct_atc *atc = snd_kcontrol_chip(kcontrol); enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); - struct amixer *amixer = NULL; - int i = 0; + struct amixer *amixer; + int i, val; for (i = 0; i < 2; i++) { amixer = ((struct ct_mixer *)atc->mixer)-> amixers[type*CHN_NUM+i]; - /* Convert 14-bit float-point scale to 16-bit integer volume */ - ucontrol->value.integer.value[i] = - (float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xffff); + val = amixer->ops->get_scale(amixer) / VOL_SCALE; + if (val < 0) + val = 0; + else if (val > VOL_MAX) + val = VOL_MAX; + ucontrol->value.integer.value[i] = val; } return 0; @@ -369,16 +379,19 @@ static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, struct ct_atc *atc = snd_kcontrol_chip(kcontrol); struct ct_mixer *mixer = atc->mixer; enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value); - struct amixer *amixer = NULL; - int i = 0, j = 0, change = 0, val = 0; + struct amixer *amixer; + int i, j, val, oval, change = 0; for (i = 0; i < 2; i++) { - /* Convert 16-bit integer volume to 14-bit float-point scale */ - val = (ucontrol->value.integer.value[i] & 0xffff); + val = ucontrol->value.integer.value[i]; + if (val < 0) + val = 0; + else if (val > VOL_MAX) + val = VOL_MAX; + val *= VOL_SCALE; amixer = mixer->amixers[type*CHN_NUM+i]; - if ((float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xff80) - != (val & 0xff80)) { - val = uint16_to_float14(val); + oval = amixer->ops->get_scale(amixer); + if (val != oval) { amixer->ops->set_scale(amixer, val); amixer->ops->commit_write(amixer); change = 1; @@ -398,11 +411,13 @@ static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new vol_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = ct_alsa_mix_volume_info, .get = ct_alsa_mix_volume_get, - .put = ct_alsa_mix_volume_put + .put = ct_alsa_mix_volume_put, + .tlv = { .p = ct_vol_db_scale }, }; static void -- cgit v1.2.1 From 4836ac655410e7f126d316b0be062b38746f7529 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 14:49:26 +0200 Subject: ALSA: ctxfi - Fix DMA mask for emu20k2 chip Allow 64bit DMA mask for emu20k2 chip, too. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k2.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 041199fbae16..edbfb4827469 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -26,7 +26,11 @@ #include #include -#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bits */ +#if BITS_PER_LONG == 32 +#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ +#else +#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ +#endif static u32 hw_read_20kx(struct hw *hw, u32 reg); static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); @@ -1834,18 +1838,16 @@ static int hw_card_start(struct hw *hw) int err = 0; struct pci_dev *pci = hw->pci; unsigned int gctl; - unsigned int dma_mask = 0; err = pci_enable_device(pci); if (err < 0) return err; /* Set DMA transfer mask */ - dma_mask = CT_XFI_DMA_MASK; - if (pci_set_dma_mask(pci, dma_mask) < 0 || - pci_set_consistent_dma_mask(pci, dma_mask) < 0) { + if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || + pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { printk(KERN_ERR "ctxfi: architecture does not support PCI " - "busmaster DMA with mask 0x%x\n", dma_mask); + "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK); err = -ENXIO; goto error1; } -- cgit v1.2.1 From 514eef9c2a711b4c24b97bb456d39695a6fe1775 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 14:57:57 +0200 Subject: ALSA: ctxfi - Remove useless initializations and cast Remove useless variable initializations and cast at the beginning of functions. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctamixer.c | 62 ++++++++-------- sound/pci/ctxfi/ctatc.c | 164 ++++++++++++++++++++++--------------------- sound/pci/ctxfi/ctdaio.c | 40 +++++------ sound/pci/ctxfi/cthardware.c | 6 +- sound/pci/ctxfi/cthw20k1.c | 82 +++++++++++----------- sound/pci/ctxfi/cthw20k2.c | 52 +++++++------- sound/pci/ctxfi/ctimap.c | 4 +- sound/pci/ctxfi/ctmixer.c | 60 ++++++++-------- sound/pci/ctxfi/ctresource.c | 6 +- sound/pci/ctxfi/ctsrc.c | 110 ++++++++++++++--------------- sound/pci/ctxfi/ctvmem.c | 12 ++-- 11 files changed, 301 insertions(+), 297 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index 859e996ad728..a1db51b3ead8 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -58,9 +58,9 @@ static struct rsc_ops amixer_basic_rsc_ops = { static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); amixer->input = rsc; if (NULL == rsc) @@ -75,9 +75,9 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) /* y is a 14-bit immediate constant */ static int amixer_set_y(struct amixer *amixer, unsigned int y) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; hw->amixer_set_y(amixer->rsc.ctrl_blk, y); return 0; @@ -85,9 +85,9 @@ static int amixer_set_y(struct amixer *amixer, unsigned int y) static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv); return 0; @@ -95,9 +95,9 @@ static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) static int amixer_set_sum(struct amixer *amixer, struct sum *sum) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; amixer->sum = sum; if (NULL == sum) { hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); @@ -112,13 +112,13 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum) static int amixer_commit_write(struct amixer *amixer) { - struct hw *hw = NULL; - unsigned int index = 0; - int i = 0; - struct rsc *input = NULL; - struct sum *sum = NULL; + struct hw *hw; + unsigned int index; + int i; + struct rsc *input; + struct sum *sum; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; input = amixer->input; sum = amixer->sum; @@ -158,10 +158,10 @@ static int amixer_commit_write(struct amixer *amixer) static int amixer_commit_raw_write(struct amixer *amixer) { - struct hw *hw = NULL; - unsigned int index = 0; + struct hw *hw; + unsigned int index; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; index = amixer->rsc.ops->output_slot(&amixer->rsc); hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); @@ -170,9 +170,9 @@ static int amixer_commit_raw_write(struct amixer *amixer) static int amixer_get_y(struct amixer *amixer) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)amixer->rsc.hw; + hw = amixer->rsc.hw; return hw->amixer_get_y(amixer->rsc.ctrl_blk); } @@ -201,7 +201,7 @@ static int amixer_rsc_init(struct amixer *amixer, const struct amixer_desc *desc, struct amixer_mgr *mgr) { - int err = 0; + int err; err = rsc_init(&amixer->rsc, amixer->idx[0], AMIXER, desc->msr, mgr->mgr.hw); @@ -233,9 +233,9 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, const struct amixer_desc *desc, struct amixer **ramixer) { - int err = 0, i = 0; - unsigned int idx = 0; - struct amixer *amixer = NULL; + int err, i; + unsigned int idx; + struct amixer *amixer; unsigned long flags; *ramixer = NULL; @@ -284,7 +284,7 @@ error: static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) { unsigned long flags; - int i = 0; + int i; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < amixer->rsc.msr; i++) @@ -299,7 +299,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) { - int err = 0; + int err; struct amixer_mgr *amixer_mgr; *ramixer_mgr = NULL; @@ -367,7 +367,7 @@ static int sum_rsc_init(struct sum *sum, const struct sum_desc *desc, struct sum_mgr *mgr) { - int err = 0; + int err; err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); if (err) @@ -388,9 +388,9 @@ static int get_sum_rsc(struct sum_mgr *mgr, const struct sum_desc *desc, struct sum **rsum) { - int err = 0, i = 0; - unsigned int idx = 0; - struct sum *sum = NULL; + int err, i; + unsigned int idx; + struct sum *sum; unsigned long flags; *rsum = NULL; @@ -438,7 +438,7 @@ error: static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) { unsigned long flags; - int i = 0; + int i; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < sum->rsc.msr; i++) @@ -453,7 +453,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) { - int err = 0; + int err; struct sum_mgr *sum_mgr; *rsum_mgr = NULL; diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 9b1324544db0..7898a375df0e 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -190,8 +190,8 @@ static unsigned int convert_format(snd_pcm_format_t snd_format) static unsigned int atc_get_pitch(unsigned int input_rate, unsigned int output_rate) { - unsigned int pitch = 0; - int b = 0; + unsigned int pitch; + int b; /* get pitch and convert to fixed-point 8.24 format. */ pitch = (input_rate / output_rate) << 24; @@ -241,12 +241,12 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct src_desc desc = {0}; struct amixer_desc mix_dsc = {0}; - struct src *src = NULL; - struct amixer *amixer = NULL; - int err = 0; + struct src *src; + struct amixer *amixer; + int err; int n_amixer = apcm->substream->runtime->channels, i = 0; int device = apcm->substream->pcm->device; - unsigned int pitch = 0; + unsigned int pitch; unsigned long flags; if (NULL != apcm->src) { @@ -324,8 +324,8 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP]; struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; - struct srcimp *srcimp = NULL; - int i = 0; + struct srcimp *srcimp; + int i; if (NULL != apcm->srcimps) { for (i = 0; i < apcm->n_srcimp; i++) { @@ -377,7 +377,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - unsigned int max_cisz = 0; + unsigned int max_cisz; struct src *src = apcm->src; max_cisz = src->multi * src->rsc.msr; @@ -398,8 +398,8 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; - int i = 0; + struct src *src; + int i; ct_timer_stop(apcm->timer); @@ -426,8 +426,8 @@ static int atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct src *src = apcm->src; - u32 size = 0, max_cisz = 0; - int position = 0; + u32 size, max_cisz; + int position; position = src->ops->get_ca(src); @@ -449,7 +449,7 @@ struct src_node_conf_t { static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm, struct src_node_conf_t *conf, int *n_srcc) { - unsigned int pitch = 0; + unsigned int pitch; /* get pitch and convert to fixed-point 8.24 format. */ pitch = atc_get_pitch((atc->rsr * atc->msr), @@ -494,14 +494,14 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM]; struct src_desc src_dsc = {0}; - struct src *src = NULL; + struct src *src; struct srcimp_desc srcimp_dsc = {0}; - struct srcimp *srcimp = NULL; + struct srcimp *srcimp; struct amixer_desc mix_dsc = {0}; struct sum_desc sum_dsc = {0}; - unsigned int pitch = 0; - int multi = 0, err = 0, i = 0; - int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0; + unsigned int pitch; + int multi, err, i; + int n_srcimp, n_amixer, n_srcc, n_sum; struct src_node_conf_t src_node_conf[2] = {{0} }; /* first release old resources */ @@ -518,8 +518,8 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc); n_sum = (1 == multi) ? 1 : 0; - n_amixer += n_sum * 2 + n_srcc; - n_srcimp += n_srcc; + n_amixer = n_sum * 2 + n_srcc; + n_srcimp = n_srcc; if ((multi > 1) && (0x8000000 >= pitch)) { /* Need extra AMIXERs and SRCIMPs for special treatment * of interleaved recording of conjugate channels */ @@ -633,14 +633,14 @@ error1: static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; - struct amixer *amixer = NULL; - struct srcimp *srcimp = NULL; + struct src *src; + struct amixer *amixer; + struct srcimp *srcimp; struct ct_mixer *mixer = atc->mixer; - struct sum *mono = NULL; + struct sum *mono; struct rsc *out_ports[8] = {NULL}; - int err = 0, i = 0, j = 0, n_sum = 0, multi = 0; - unsigned int pitch = 0; + int err, i, j, n_sum, multi; + unsigned int pitch; int mix_base = 0, imp_base = 0; if (NULL != apcm->src) { @@ -714,9 +714,9 @@ static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; + struct src *src; struct src_mgr *src_mgr = atc->rsc_mgrs[SRC]; - int i = 0, multi = 0; + int i, multi; if (apcm->started) return 0; @@ -776,10 +776,10 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER]; struct src_desc desc = {0}; struct amixer_desc mix_dsc = {0}; - struct src *src = NULL; - int err = 0; - int n_amixer = apcm->substream->runtime->channels, i = 0; - unsigned int pitch = 0, rsr = atc->pll_rate; + struct src *src; + int err; + int n_amixer = apcm->substream->runtime->channels, i; + unsigned int pitch, rsr = atc->pll_rate; /* first release old resources */ atc->pcm_release_resources(atc, apcm); @@ -832,15 +832,24 @@ error1: return err; } +static int atc_pll_init(struct ct_atc *atc, int rate) +{ + struct hw *hw = atc->hw; + int err; + err = hw->pll_init(hw, rate); + atc->pll_rate = err ? 0 : rate; + return err; +} + static int spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio); unsigned long flags; unsigned int rate = apcm->substream->runtime->rate; - unsigned int status = 0; - int err = 0; - unsigned char iec958_con_fs = 0; + unsigned int status; + int err; + unsigned char iec958_con_fs; switch (rate) { case 48000: @@ -864,10 +873,8 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) dao->ops->set_spos(dao, status); dao->ops->commit_write(dao); } - if ((rate != atc->pll_rate) && (32000 != rate)) { - err = ((struct hw *)atc->hw)->pll_init(atc->hw, rate); - atc->pll_rate = err ? 0 : rate; - } + if ((rate != atc->pll_rate) && (32000 != rate)) + err = atc_pll_init(atc, rate); spin_unlock_irqrestore(&atc->atc_lock, flags); return err; @@ -876,11 +883,11 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) static int spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) { - struct src *src = NULL; - struct amixer *amixer = NULL; - struct dao *dao = NULL; - int err = 0; - int i = 0; + struct src *src; + struct amixer *amixer; + struct dao *dao; + int err; + int i; unsigned long flags; if (NULL != apcm->src) @@ -924,7 +931,7 @@ static int atc_select_line_in(struct ct_atc *atc) { struct hw *hw = atc->hw; struct ct_mixer *mixer = atc->mixer; - struct src *src = NULL; + struct src *src; if (hw->is_adc_source_selected(hw, ADC_LINEIN)) return 0; @@ -946,7 +953,7 @@ static int atc_select_mic_in(struct ct_atc *atc) { struct hw *hw = atc->hw; struct ct_mixer *mixer = atc->mixer; - struct src *src = NULL; + struct src *src; if (hw->is_adc_source_selected(hw, ADC_MICIN)) return 0; @@ -1063,8 +1070,8 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) { unsigned long flags; struct dao_desc da_dsc = {0}; - struct dao *dao = NULL; - int err = 0; + struct dao *dao; + int err; struct ct_mixer *mixer = atc->mixer; struct rsc *rscs[2] = {NULL}; unsigned int spos = 0; @@ -1082,11 +1089,8 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) dao->ops->set_left_input(dao, rscs[0]); dao->ops->set_right_input(dao, rscs[1]); /* Restore PLL to atc->rsr if needed. */ - if (atc->pll_rate != atc->rsr) { - err = ((struct hw *)atc->hw)->pll_init(atc->hw, - atc->rsr); - atc->pll_rate = err ? 0 : atc->rsr; - } + if (atc->pll_rate != atc->rsr) + err = atc_pll_init(atc, atc->rsr); } dao->ops->set_spos(dao, spos); dao->ops->commit_write(dao); @@ -1097,15 +1101,15 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state) static int ct_atc_destroy(struct ct_atc *atc) { - struct daio_mgr *daio_mgr = NULL; - struct dao *dao = NULL; - struct dai *dai = NULL; - struct daio *daio = NULL; - struct sum_mgr *sum_mgr = NULL; - struct src_mgr *src_mgr = NULL; - struct srcimp_mgr *srcimp_mgr = NULL; - struct srcimp *srcimp = NULL; - struct ct_mixer *mixer = NULL; + struct daio_mgr *daio_mgr; + struct dao *dao; + struct dai *dai; + struct daio *daio; + struct sum_mgr *sum_mgr; + struct src_mgr *src_mgr; + struct srcimp_mgr *srcimp_mgr; + struct srcimp *srcimp; + struct ct_mixer *mixer; int i = 0; if (NULL == atc) @@ -1279,9 +1283,9 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) static int __devinit atc_create_hw_devs(struct ct_atc *atc) { - struct hw *hw = NULL; + struct hw *hw; struct card_conf info = {0}; - int i = 0, err = 0; + int i, err; err = create_hw_obj(atc->pci, &hw); if (err) { @@ -1316,14 +1320,14 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) static int __devinit atc_get_resources(struct ct_atc *atc) { struct daio_desc da_desc = {0}; - struct daio_mgr *daio_mgr = NULL; + struct daio_mgr *daio_mgr; struct src_desc src_dsc = {0}; - struct src_mgr *src_mgr = NULL; + struct src_mgr *src_mgr; struct srcimp_desc srcimp_dsc = {0}; - struct srcimp_mgr *srcimp_mgr = NULL; + struct srcimp_mgr *srcimp_mgr; struct sum_desc sum_dsc = {0}; - struct sum_mgr *sum_mgr = NULL; - int err = 0, i = 0; + struct sum_mgr *sum_mgr; + int err, i; unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); @@ -1428,8 +1432,8 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, struct src **srcs, struct srcimp **srcimps) { struct rsc *rscs[2] = {NULL}; - struct src *src = NULL; - struct srcimp *srcimp = NULL; + struct src *src; + struct srcimp *srcimp; int i = 0; rscs[0] = &dai->daio.rscl; @@ -1464,13 +1468,13 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai, static void __devinit atc_connect_resources(struct ct_atc *atc) { - struct dai *dai = NULL; - struct dao *dao = NULL; - struct src *src = NULL; - struct sum *sum = NULL; - struct ct_mixer *mixer = NULL; + struct dai *dai; + struct dao *dao; + struct src *src; + struct sum *sum; + struct ct_mixer *mixer; struct rsc *rscs[2] = {NULL}; - int i = 0, j = 0; + int i, j; mixer = atc->mixer; @@ -1553,11 +1557,11 @@ static struct ct_atc atc_preset __devinitdata = { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, struct ct_atc **ratc) { - struct ct_atc *atc = NULL; + struct ct_atc *atc; static struct snd_device_ops ops = { .dev_free = atc_dev_free, }; - int err = 0; + int err; *ratc = NULL; diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index a2aea399eba1..befead4eeaab 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -168,9 +168,9 @@ static int dao_commit_write(struct dao *dao) static int dao_set_left_input(struct dao *dao, struct rsc *input) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); if (NULL == entry) @@ -196,9 +196,9 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) static int dao_set_right_input(struct dao *dao, struct rsc *input) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); if (NULL == entry) @@ -224,9 +224,9 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) static int dao_clear_left_input(struct dao *dao) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; if (NULL == dao->imappers[0]) return 0; @@ -248,9 +248,9 @@ static int dao_clear_left_input(struct dao *dao) static int dao_clear_right_input(struct dao *dao) { - struct imapper *entry = NULL; + struct imapper *entry; struct daio *daio = &dao->daio; - int i = 0; + int i; if (NULL == dao->imappers[daio->rscl.msr]) return 0; @@ -299,7 +299,7 @@ static int dai_set_srt_srcr(struct dai *dai, struct rsc *src) static int dai_set_srt_msr(struct dai *dai, unsigned int msr) { - unsigned int rsr = 0; + unsigned int rsr; for (rsr = 0; msr > 1; msr >>= 1) rsr++; @@ -340,8 +340,8 @@ static int daio_rsc_init(struct daio *daio, const struct daio_desc *desc, void *hw) { - int err = 0; - unsigned int idx_l = 0, idx_r = 0; + int err; + unsigned int idx_l, idx_r; switch (((struct hw *)hw)->get_chip_type(hw)) { case ATC20K1: @@ -400,8 +400,8 @@ static int dao_rsc_init(struct dao *dao, struct daio_mgr *mgr) { struct hw *hw = mgr->mgr.hw; - unsigned int conf = 0; - int err = 0; + unsigned int conf; + int err; err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw); if (err) @@ -423,7 +423,7 @@ static int dao_rsc_init(struct dao *dao, daio_device_index(dao->daio.type, hw)); hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk); - conf |= (desc->msr & 0x7) | (desc->passthru << 3); + conf = (desc->msr & 0x7) | (desc->passthru << 3); hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk, daio_device_index(dao->daio.type, hw), conf); hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, @@ -475,9 +475,9 @@ static int dai_rsc_init(struct dai *dai, const struct daio_desc *desc, struct daio_mgr *mgr) { - int err = 0; + int err; struct hw *hw = mgr->mgr.hw; - unsigned int rsr = 0, msr = 0; + unsigned int rsr, msr; err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw); if (err) @@ -536,7 +536,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, const struct daio_desc *desc, struct daio **rdaio) { - int err = 0; + int err; struct dai *dai = NULL; struct dao *dao = NULL; unsigned long flags; @@ -660,7 +660,7 @@ static int daio_map_op(void *data, struct imapper *entry) static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); if ((0 == entry->addr) && (mgr->init_imap_added)) { @@ -677,7 +677,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr); @@ -701,7 +701,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr) int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) { - int err = 0, i = 0; + int err, i; struct daio_mgr *daio_mgr; struct imapper *entry; diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 53d1acadc0e8..5ec6813d3911 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -22,7 +22,7 @@ static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) { - enum CHIPTYP type = ATCNONE; + enum CHIPTYP type; switch (hw->pci->device) { case 0x0005: /* 20k1 device */ @@ -41,7 +41,7 @@ static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) { - int err = 0; + int err; switch (pci->device) { case 0x0005: /* 20k1 device */ @@ -65,7 +65,7 @@ int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) int destroy_hw_obj(struct hw *hw) { - int err = 0; + int err; switch (hw->pci->device) { case 0x0005: /* 20k1 device */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index b165466e1a54..38b87b6ee6d4 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -369,7 +369,7 @@ static unsigned int src_param_pitch_mixer(unsigned int src_idx) static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) { struct src_rsc_ctrl_blk *ctl = blk; - int i = 0; + int i; if (ctl->dirty.bf.czbfs) { /* Clear Z-Buffer registers */ @@ -468,8 +468,8 @@ static int src_mgr_dsb_src(void *blk, unsigned int idx) static int src_mgr_commit_write(struct hw *hw, void *blk) { struct src_mgr_ctrl_blk *ctl = blk; - int i = 0; - unsigned int ret = 0; + int i; + unsigned int ret; if (ctl->dirty.bf.enbsa) { do { @@ -1108,7 +1108,7 @@ static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) static int daio_mgr_commit_write(struct hw *hw, void *blk) { struct daio_mgr_ctrl_blk *ctl = blk; - int i = 0; + int i; if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) { for (i = 0; i < 4; i++) { @@ -1212,8 +1212,8 @@ struct trn_conf { static int hw_daio_init(struct hw *hw, const struct daio_conf *info) { - u32 i2sorg = 0; - u32 spdorg = 0; + u32 i2sorg; + u32 spdorg; /* Read I2S CTL. Keep original value. */ /*i2sorg = hw_read_20kx(hw, I2SCTL);*/ @@ -1263,8 +1263,8 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) /* TRANSPORT operations */ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) { - u32 trnctl = 0; - unsigned long ptp_phys_low = 0, ptp_phys_high = 0; + u32 trnctl; + u32 ptp_phys_low, ptp_phys_high; /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { @@ -1316,7 +1316,7 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) static int hw_pll_init(struct hw *hw, unsigned int rsr) { unsigned int pllctl; - int i = 0; + int i; pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731; for (i = 0; i < 3; i++) { @@ -1384,7 +1384,7 @@ static void i2c_lock(struct hw *hw) static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) { - unsigned int ret = 0; + unsigned int ret; do { ret = hw_read_pci(hw, 0xEC); @@ -1397,9 +1397,9 @@ static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data) static int hw_reset_dac(struct hw *hw) { - u32 i = 0; - u16 gpioorg = 0; - unsigned int ret = 0; + u32 i; + u16 gpioorg; + unsigned int ret; if (i2c_unlock(hw)) return -1; @@ -1430,10 +1430,10 @@ static int hw_reset_dac(struct hw *hw) static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { - u32 data = 0; - u16 gpioorg = 0; - u16 subsys_id = 0; - unsigned int ret = 0; + u32 data; + u16 gpioorg; + u16 subsys_id; + unsigned int ret; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1494,13 +1494,12 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type) { - u32 data = 0; - return data; + return 0; } static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO); switch (type) { @@ -1521,7 +1520,7 @@ static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type) static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO); switch (type) { @@ -1539,7 +1538,7 @@ static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u16 subsys_id = 0; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1559,7 +1558,7 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) static int adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) { - u32 data = 0; + u32 data; /* * check and set the following GPIO bits accordingly @@ -1599,9 +1598,9 @@ adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost) static int adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) { - u32 data = 0; - u32 i2c_data = 0; - unsigned int ret = 0; + u32 data; + u32 i2c_data; + unsigned int ret; if (i2c_unlock(hw)) return -1; @@ -1649,9 +1648,9 @@ adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost) static int adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) { - u32 data = 0; - u32 i2c_data = 0; - unsigned int ret = 0; + u32 data; + u32 i2c_data; + unsigned int ret; if (i2c_unlock(hw)) return -1; @@ -1693,7 +1692,7 @@ adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u16 subsys_id = 0; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1719,8 +1718,8 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) { u16 gpioorg; u16 input_source; - u32 adcdata = 0; - unsigned int ret = 0; + u32 adcdata; + unsigned int ret; input_source = 0x100; /* default to analog */ switch (input) { @@ -1742,6 +1741,7 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) input_source = 0x0; /* set to Digital */ break; default: + adcdata = 0x0; break; } @@ -1781,8 +1781,8 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err = 0; - u16 subsys_id = 0; + int err; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { @@ -1797,7 +1797,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) static int hw_have_digit_io_switch(struct hw *hw) { - u16 subsys_id = 0; + u16 subsys_id; pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* SB073x and Vista compatible cards have no digit IO switch */ @@ -1814,11 +1814,11 @@ static int uaa_to_xfi(struct pci_dev *pci) { unsigned int bar0, bar1, bar2, bar3, bar4, bar5; unsigned int cmd, irq, cl_size, l_timer, pwr; - unsigned int is_uaa = 0; + unsigned int is_uaa; unsigned int data[4] = {0}; unsigned int io_base; void *mem_base; - int i = 0; + int i; const u32 CTLX = CTLBITS('C', 'T', 'L', 'X'); const u32 CTL_ = CTLBITS('C', 'T', 'L', '-'); const u32 CTLF = CTLBITS('C', 'T', 'L', 'F'); @@ -1916,9 +1916,9 @@ static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id) static int hw_card_start(struct hw *hw) { - int err = 0; + int err; struct pci_dev *pci = hw->pci; - u16 subsys_id = 0; + u16 subsys_id; err = pci_enable_device(pci); if (err < 0) @@ -2004,8 +2004,8 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) { int err; unsigned int gctl; - u16 subsys_id = 0; - u32 data = 0; + u16 subsys_id; + u32 data; struct dac_conf dac_info = {0}; struct adc_conf adc_info = {0}; struct daio_conf daio_info = {0}; diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index edbfb4827469..7d6dcbaf5244 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) static int src_put_rsc_ctrl_blk(void *blk) { - kfree((struct src_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -359,7 +359,7 @@ static unsigned int src_param_pitch_mixer(unsigned int src_idx) static int src_commit_write(struct hw *hw, unsigned int idx, void *blk) { struct src_rsc_ctrl_blk *ctl = blk; - int i = 0; + int i; if (ctl->dirty.bf.czbfs) { /* Clear Z-Buffer registers */ @@ -458,8 +458,8 @@ static int src_mgr_dsb_src(void *blk, unsigned int idx) static int src_mgr_commit_write(struct hw *hw, void *blk) { struct src_mgr_ctrl_blk *ctl = blk; - int i = 0; - unsigned int ret = 0; + int i; + unsigned int ret; if (ctl->dirty.bf.enbsa) { do { @@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) static int src_mgr_put_ctrl_blk(void *blk) { - kfree((struct src_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) static int srcimp_mgr_put_ctrl_blk(void *blk) { - kfree((struct srcimp_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -704,7 +704,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) static int amixer_rsc_put_ctrl_blk(void *blk) { - kfree((struct amixer_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -893,7 +893,7 @@ static int dai_get_ctrl_blk(void **rblk) static int dai_put_ctrl_blk(void *blk) { - kfree((struct dai_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -943,7 +943,7 @@ static int dao_get_ctrl_blk(void **rblk) static int dao_put_ctrl_blk(void *blk) { - kfree((struct dao_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -1051,8 +1051,8 @@ static int daio_mgr_set_imapaddr(void *blk, unsigned int addr) static int daio_mgr_commit_write(struct hw *hw, void *blk) { struct daio_mgr_ctrl_blk *ctl = blk; - unsigned int data = 0; - int i = 0; + unsigned int data; + int i; for (i = 0; i < 8; i++) { if ((ctl->dirty.bf.atxctl & (0x1 << i))) { @@ -1080,7 +1080,7 @@ static int daio_mgr_commit_write(struct hw *hw, void *blk) static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) { struct daio_mgr_ctrl_blk *blk; - int i = 0; + int i; *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); @@ -1099,7 +1099,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) static int daio_mgr_put_ctrl_blk(void *blk) { - kfree((struct daio_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -1125,7 +1125,7 @@ struct trn_conf { static int hw_daio_init(struct hw *hw, const struct daio_conf *info) { - u32 dwData = 0; + u32 dwData; int i; /* Program I2S with proper sample rate and enable the correct I2S @@ -1195,9 +1195,9 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) /* TRANSPORT operations */ static int hw_trn_init(struct hw *hw, const struct trn_conf *info) { - u32 vmctl = 0, data = 0; - unsigned long ptp_phys_low = 0, ptp_phys_high = 0; - int i = 0; + u32 vmctl, data; + u32 ptp_phys_low, ptp_phys_high; + int i; /* Set up device page table */ if ((~0UL) == info->vm_pgt_phys) { @@ -1433,7 +1433,7 @@ static int I2CLockChip(struct hw *hw) static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) { - int err = 0; + int err; unsigned int RegI2CStatus; unsigned int RegI2CAddress; @@ -1481,7 +1481,7 @@ static int I2CUninit(struct hw *hw) static int I2CWaitDataReady(struct hw *hw) { int i = 0x400000; - unsigned int ret = 0; + unsigned int ret; do { ret = hw_read_20kx(hw, I2C_IF_STATUS); @@ -1541,9 +1541,9 @@ static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { - int err = 0; - u32 dwData = 0; - int i = 0; + int err; + u32 dwData; + int i; struct REGS_CS4382 cs4382_Read = {0}; struct REGS_CS4382 cs4382_Def = { 0x00000001, /* Mode Control 1 */ @@ -1696,7 +1696,7 @@ End: static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO_DATA); switch (type) { @@ -1714,7 +1714,7 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u32 data = 0; + u32 data; data = hw_read_20kx(hw, GPIO_DATA); switch (type) { @@ -1747,8 +1747,8 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err = 0; - u32 dwMux = 2, dwData = 0, dwCtl = 0; + int err; + u32 dwMux = 2, dwData, dwCtl; /* Set ADC reset bit as output */ dwData = hw_read_20kx(hw, GPIO_CTRL); diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c index d34eacd902ce..0b73368a4df6 100644 --- a/sound/pci/ctxfi/ctimap.c +++ b/sound/pci/ctxfi/ctimap.c @@ -99,8 +99,8 @@ int input_mapper_delete(struct list_head *mappers, struct imapper *entry, void free_input_mapper_list(struct list_head *head) { - struct imapper *entry = NULL; - struct list_head *pos = NULL; + struct imapper *entry; + struct list_head *pos; while (!list_empty(head)) { pos = head->next; diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 796156e4bd38..666722d9de41 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -298,7 +298,7 @@ set_switch_state(struct ct_mixer *mixer, * from 2^-6 to (1+1023/1024) */ static unsigned int uint16_to_float14(unsigned int x) { - unsigned int i = 0; + unsigned int i; if (x < 17) return 0; @@ -318,7 +318,7 @@ static unsigned int uint16_to_float14(unsigned int x) static unsigned int float14_to_uint16(unsigned int x) { - unsigned int e = 0; + unsigned int e; if (!x) return x; @@ -491,7 +491,7 @@ static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol, struct ct_atc *atc = snd_kcontrol_chip(kcontrol); struct ct_mixer *mixer = atc->mixer; enum CTALSA_MIXER_CTL type = kcontrol->private_value; - int state = 0; + int state; state = ucontrol->value.integer.value[0]; if (get_switch_state(mixer, type) == state) @@ -574,7 +574,7 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ct_atc *atc = snd_kcontrol_chip(kcontrol); - unsigned int status = 0; + unsigned int status; atc->spdif_out_get_status(atc, &status); ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; @@ -589,8 +589,8 @@ static int ct_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ct_atc *atc = snd_kcontrol_chip(kcontrol); - int change = 1; - unsigned int status = 0, old_status = 0; + int change; + unsigned int status, old_status; status = (ucontrol->value.iec958.status[0] << 0) | (ucontrol->value.iec958.status[1] << 8) | @@ -641,8 +641,8 @@ static struct snd_kcontrol_new iec958_ctl = { static int ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) { - struct snd_kcontrol *kctl = NULL; - int err = 0; + struct snd_kcontrol *kctl; + int err; kctl = snd_ctl_new1(new, mixer->atc); if (NULL == kctl) @@ -669,9 +669,9 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) { - enum CTALSA_MIXER_CTL type = 0; + enum CTALSA_MIXER_CTL type; struct ct_atc *atc = mixer->atc; - int err = 0; + int err; /* Create snd kcontrol instances on demand */ for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) { @@ -733,9 +733,9 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer) static void ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) { - struct amixer *amix_d = NULL; - struct sum *sum_c = NULL; - int i = 0; + struct amixer *amix_d; + struct sum *sum_c; + int i; for (i = 0; i < 2; i++) { amix_d = mixer->amixers[type*CHN_NUM+i]; @@ -748,8 +748,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) static void ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) { - struct amixer *amix_d = NULL; - int i = 0; + struct amixer *amix_d; + int i; for (i = 0; i < 2; i++) { amix_d = mixer->amixers[type*CHN_NUM+i]; @@ -760,14 +760,14 @@ ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type) static int ct_mixer_get_resources(struct ct_mixer *mixer) { - struct sum_mgr *sum_mgr = NULL; - struct sum *sum = NULL; + struct sum_mgr *sum_mgr; + struct sum *sum; struct sum_desc sum_desc = {0}; - struct amixer_mgr *amixer_mgr = NULL; - struct amixer *amixer = NULL; + struct amixer_mgr *amixer_mgr; + struct amixer *amixer; struct amixer_desc am_desc = {0}; - int err = 0; - int i = 0; + int err; + int i; /* Allocate sum resources for mixer obj */ sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; @@ -822,8 +822,8 @@ error1: static int ct_mixer_get_mem(struct ct_mixer **rmixer) { - struct ct_mixer *mixer = NULL; - int err = 0; + struct ct_mixer *mixer; + int err; *rmixer = NULL; /* Allocate mem for mixer obj */ @@ -855,9 +855,9 @@ error1: static int ct_mixer_topology_build(struct ct_mixer *mixer) { - struct sum *sum = NULL; - struct amixer *amix_d = NULL, *amix_s = NULL; - enum CT_AMIXER_CTL i = 0, j = 0; + struct sum *sum; + struct amixer *amix_d, *amix_s; + enum CT_AMIXER_CTL i, j; /* Build topology from destination to source */ @@ -1044,7 +1044,7 @@ int ct_mixer_destroy(struct ct_mixer *mixer) struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM]; struct amixer_mgr *amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER]; - struct amixer *amixer = NULL; + struct amixer *amixer; int i = 0; /* Release amixer resources */ @@ -1071,8 +1071,8 @@ int ct_mixer_destroy(struct ct_mixer *mixer) int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer) { - struct ct_mixer *mixer = NULL; - int err = 0; + struct ct_mixer *mixer; + int err; *rmixer = NULL; @@ -1109,7 +1109,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, enum CTALSADEVS device, const char *device_name) { - int err = 0; + int err; /* Create snd kcontrol instances on demand */ /* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */ diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c index da21a717a07a..889c495bb7d1 100644 --- a/sound/pci/ctxfi/ctresource.c +++ b/sound/pci/ctxfi/ctresource.c @@ -27,7 +27,7 @@ static int get_resource(u8 *rscs, unsigned int amount, unsigned int multi, unsigned int *ridx) { - int i = 0, j = 0, k = 0, n = 0; + int i, j, k, n; /* Check whether there are sufficient resources to meet request. */ for (i = 0, n = multi; i < amount; i++) { @@ -61,7 +61,7 @@ get_resource(u8 *rscs, unsigned int amount, static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) { - unsigned int i = 0, j = 0, k = 0, n = 0; + unsigned int i, j, k, n; /* Mark the contiguous bits in resource bit-map as used */ for (n = multi, i = idx; n > 0; n--) { @@ -76,7 +76,7 @@ static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx) int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx) { - int err = 0; + int err; if (n > mgr->avail) return -ENOENT; diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 77e118c5bc97..e1c145d8b702 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -37,9 +37,9 @@ static int (*src_default_config[3])(struct src *) = { static int src_set_state(struct src *src, unsigned int state) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_state(src->rsc.ctrl_blk, state); return 0; @@ -47,9 +47,9 @@ static int src_set_state(struct src *src, unsigned int state) static int src_set_bm(struct src *src, unsigned int bm) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_bm(src->rsc.ctrl_blk, bm); return 0; @@ -57,9 +57,9 @@ static int src_set_bm(struct src *src, unsigned int bm) static int src_set_sf(struct src *src, unsigned int sf) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_sf(src->rsc.ctrl_blk, sf); return 0; @@ -67,9 +67,9 @@ static int src_set_sf(struct src *src, unsigned int sf) static int src_set_pm(struct src *src, unsigned int pm) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_pm(src->rsc.ctrl_blk, pm); return 0; @@ -77,9 +77,9 @@ static int src_set_pm(struct src *src, unsigned int pm) static int src_set_rom(struct src *src, unsigned int rom) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_rom(src->rsc.ctrl_blk, rom); return 0; @@ -87,9 +87,9 @@ static int src_set_rom(struct src *src, unsigned int rom) static int src_set_vo(struct src *src, unsigned int vo) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_vo(src->rsc.ctrl_blk, vo); return 0; @@ -97,9 +97,9 @@ static int src_set_vo(struct src *src, unsigned int vo) static int src_set_st(struct src *src, unsigned int st) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_st(src->rsc.ctrl_blk, st); return 0; @@ -107,9 +107,9 @@ static int src_set_st(struct src *src, unsigned int st) static int src_set_bp(struct src *src, unsigned int bp) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_bp(src->rsc.ctrl_blk, bp); return 0; @@ -117,9 +117,9 @@ static int src_set_bp(struct src *src, unsigned int bp) static int src_set_cisz(struct src *src, unsigned int cisz) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_cisz(src->rsc.ctrl_blk, cisz); return 0; @@ -127,9 +127,9 @@ static int src_set_cisz(struct src *src, unsigned int cisz) static int src_set_ca(struct src *src, unsigned int ca) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_ca(src->rsc.ctrl_blk, ca); return 0; @@ -137,9 +137,9 @@ static int src_set_ca(struct src *src, unsigned int ca) static int src_set_sa(struct src *src, unsigned int sa) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_sa(src->rsc.ctrl_blk, sa); return 0; @@ -147,9 +147,9 @@ static int src_set_sa(struct src *src, unsigned int sa) static int src_set_la(struct src *src, unsigned int la) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_la(src->rsc.ctrl_blk, la); return 0; @@ -157,9 +157,9 @@ static int src_set_la(struct src *src, unsigned int la) static int src_set_pitch(struct src *src, unsigned int pitch) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_pitch(src->rsc.ctrl_blk, pitch); return 0; @@ -167,9 +167,9 @@ static int src_set_pitch(struct src *src, unsigned int pitch) static int src_set_clear_zbufs(struct src *src) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1); return 0; @@ -177,11 +177,11 @@ static int src_set_clear_zbufs(struct src *src) static int src_commit_write(struct src *src) { - struct hw *hw = NULL; - int i = 0; + struct hw *hw; + int i; unsigned int dirty = 0; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; src->rsc.ops->master(&src->rsc); if (src->rsc.msr > 1) { /* Save dirty flags for conjugate resource programming */ @@ -207,9 +207,9 @@ static int src_commit_write(struct src *src) static int src_get_ca(struct src *src) { - struct hw *hw = NULL; + struct hw *hw; - hw = (struct hw *)src->rsc.hw; + hw = src->rsc.hw; return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc), src->rsc.ctrl_blk); } @@ -229,7 +229,7 @@ static struct src *src_next_interleave(struct src *src) static int src_default_config_memrd(struct src *src) { struct hw *hw = src->rsc.hw; - unsigned int rsr = 0, msr = 0; + unsigned int rsr, msr; hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); hw->src_set_bm(src->rsc.ctrl_blk, 1); @@ -297,7 +297,7 @@ static int src_default_config_memwr(struct src *src) static int src_default_config_arcrw(struct src *src) { struct hw *hw = src->rsc.hw; - unsigned int rsr = 0, msr = 0; + unsigned int rsr, msr; unsigned int dirty; hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF); @@ -360,8 +360,8 @@ static int src_rsc_init(struct src *src, u32 idx, const struct src_desc *desc, struct src_mgr *mgr) { - int err = 0; - int i = 0, n = 0; + int err; + int i, n; struct src *p; n = (MEMRD == desc->mode) ? desc->multi : 1; @@ -395,7 +395,7 @@ error1: static int src_rsc_uninit(struct src *src, struct src_mgr *mgr) { - int i = 0, n = 0; + int i, n; struct src *p; n = (MEMRD == src->mode) ? src->multi : 1; @@ -416,8 +416,8 @@ static int get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) { unsigned int idx = SRC_RESOURCE_NUM; - int err = 0; - struct src *src = NULL; + int err; + struct src *src; unsigned long flags; *rsrc = NULL; @@ -489,7 +489,7 @@ static int put_src_rsc(struct src_mgr *mgr, struct src *src) static int src_enable_s(struct src_mgr *mgr, struct src *src) { struct hw *hw = mgr->mgr.hw; - int i = 0; + int i; src->rsc.ops->master(&src->rsc); for (i = 0; i < src->rsc.msr; i++) { @@ -505,7 +505,7 @@ static int src_enable_s(struct src_mgr *mgr, struct src *src) static int src_enable(struct src_mgr *mgr, struct src *src) { struct hw *hw = mgr->mgr.hw; - int i = 0; + int i; src->rsc.ops->master(&src->rsc); for (i = 0; i < src->rsc.msr; i++) { @@ -521,7 +521,7 @@ static int src_enable(struct src_mgr *mgr, struct src *src) static int src_disable(struct src_mgr *mgr, struct src *src) { struct hw *hw = mgr->mgr.hw; - int i = 0; + int i; src->rsc.ops->master(&src->rsc); for (i = 0; i < src->rsc.msr; i++) { @@ -545,7 +545,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr) int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) { - int err = 0, i = 0; + int err, i; struct src_mgr *src_mgr; *rsrc_mgr = NULL; @@ -618,8 +618,8 @@ static struct rsc_ops srcimp_basic_rsc_ops = { static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input) { - struct imapper *entry = NULL; - int i = 0; + struct imapper *entry; + int i; srcimp->rsc.ops->master(&srcimp->rsc); src->rsc.ops->master(&src->rsc); @@ -646,7 +646,7 @@ static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input) static int srcimp_unmap(struct srcimp *srcimp) { - int i = 0; + int i; /* Program master and conjugate resources */ for (i = 0; i < srcimp->rsc.msr; i++) { @@ -669,7 +669,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp, const struct srcimp_desc *desc, struct srcimp_mgr *mgr) { - int err = 0; + int err; err = rsc_init(&srcimp->rsc, srcimp->idx[0], SRCIMP, desc->msr, mgr->mgr.hw); @@ -715,9 +715,9 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr, const struct srcimp_desc *desc, struct srcimp **rsrcimp) { - int err = 0, i = 0; - unsigned int idx = 0; - struct srcimp *srcimp = NULL; + int err, i; + unsigned int idx; + struct srcimp *srcimp; unsigned long flags; *rsrcimp = NULL; @@ -765,7 +765,7 @@ error1: static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp) { unsigned long flags; - int i = 0; + int i; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < srcimp->rsc.msr; i++) @@ -795,7 +795,7 @@ static int srcimp_map_op(void *data, struct imapper *entry) static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); if ((0 == entry->addr) && (mgr->init_imap_added)) { @@ -812,7 +812,7 @@ static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry) static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) { unsigned long flags; - int err = 0; + int err; spin_lock_irqsave(&mgr->imap_lock, flags); err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr); @@ -828,7 +828,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) { - int err = 0; + int err; struct srcimp_mgr *srcimp_mgr; struct imapper *entry; diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index b7f8e58ae07d..67665a7e43c6 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -31,8 +31,8 @@ static struct ct_vm_block * get_vm_block(struct ct_vm *vm, unsigned int size) { - struct ct_vm_block *block = NULL, *entry = NULL; - struct list_head *pos = NULL; + struct ct_vm_block *block = NULL, *entry; + struct list_head *pos; size = CT_PAGE_ALIGN(size); if (size > vm->size) { @@ -77,8 +77,8 @@ get_vm_block(struct ct_vm *vm, unsigned int size) static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block) { - struct ct_vm_block *entry = NULL, *pre_ent = NULL; - struct list_head *pos = NULL, *pre = NULL; + struct ct_vm_block *entry, *pre_ent; + struct list_head *pos, *pre; block->size = CT_PAGE_ALIGN(block->size); @@ -223,8 +223,8 @@ int ct_vm_create(struct ct_vm **rvm) void ct_vm_destroy(struct ct_vm *vm) { int i; - struct list_head *pos = NULL; - struct ct_vm_block *entry = NULL; + struct list_head *pos; + struct ct_vm_block *entry; /* free used and unused list nodes */ while (!list_empty(&vm->used)) { -- cgit v1.2.1 From af8500bbbd18438495d2f91ad07bda49fff3b770 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 15:07:46 +0200 Subject: ALSA: ctxfi - Fix possible buffer pointer overrun Fix possible buffer pointer overruns. Back to zero when it's equal or over the buffer size. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctpcm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index a0bd31c6090d..870fa170f046 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -243,6 +243,8 @@ ct_pcm_playback_pointer(struct snd_pcm_substream *substream) /* Read out playback position */ position = atc->pcm_playback_position(atc, apcm); position = bytes_to_frames(runtime, position); + if (position >= runtime->buffer_size) + position = 0; return position; } @@ -343,6 +345,8 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream) /* Read out playback position */ position = atc->pcm_capture_position(atc, apcm); position = bytes_to_frames(runtime, position); + if (position >= runtime->buffer_size) + position = 0; return position; } -- cgit v1.2.1 From d362af62ed98f58c64a2b3dd58c79d25ad181b0b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 15:31:22 +0200 Subject: ALSA: ctxfi - Fix / clean up hw20k2 chip code - Clean up Hungarian coding style - Don't use static variables for I2C information; this unables to use multiple instances. Now they are stored in struct hw20k2 fields. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cthw20k2.c | 346 +++++++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 169 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 7d6dcbaf5244..4493a51c6b01 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -15,8 +15,6 @@ * */ -#include "cthw20k2.h" -#include "ct20k2reg.h" #include #include #include @@ -25,6 +23,8 @@ #include #include #include +#include "cthw20k2.h" +#include "ct20k2reg.h" #if BITS_PER_LONG == 32 #define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */ @@ -32,6 +32,14 @@ #define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */ #endif +struct hw20k2 { + struct hw hw; + /* for i2c */ + unsigned char dev_id; + unsigned char addr_size; + unsigned char data_size; +}; + static u32 hw_read_20kx(struct hw *hw, u32 reg); static void hw_write_20kx(struct hw *hw, u32 reg, u32 data); @@ -1125,7 +1133,7 @@ struct trn_conf { static int hw_daio_init(struct hw *hw, const struct daio_conf *info) { - u32 dwData; + u32 data; int i; /* Program I2S with proper sample rate and enable the correct I2S @@ -1156,12 +1164,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) if (i <= 3) { /* 1st 3 channels are SPDIFs (SB0960) */ if (i == 3) - dwData = 0x1001001; + data = 0x1001001; else - dwData = 0x1000001; + data = 0x1000001; - hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), dwData); - hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), dwData); + hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data); + hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data); /* Initialize the SPDIF Out Channel status registers. * The value specified here is based on the typical @@ -1179,13 +1187,13 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info) hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B); } else { /* Next 5 channels are I2S (SB0960) */ - dwData = 0x11; - hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), dwData); + data = 0x11; + hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data); if (2 == info->msr) { /* Four channels per sample period */ - dwData |= 0x1000; + data |= 0x1000; } - hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), dwData); + hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data); } } @@ -1377,34 +1385,32 @@ static int hw_auto_init(struct hw *hw) #define I2C_ADDRESS_PTAD 0x0000FFFF #define I2C_ADDRESS_SLAD 0x007F0000 -struct REGS_CS4382 { - u32 dwModeControl_1; - u32 dwModeControl_2; - u32 dwModeControl_3; +struct regs_cs4382 { + u32 mode_control_1; + u32 mode_control_2; + u32 mode_control_3; - u32 dwFilterControl; - u32 dwInvertControl; + u32 filter_control; + u32 invert_control; - u32 dwMixControl_P1; - u32 dwVolControl_A1; - u32 dwVolControl_B1; + u32 mix_control_P1; + u32 vol_control_A1; + u32 vol_control_B1; - u32 dwMixControl_P2; - u32 dwVolControl_A2; - u32 dwVolControl_B2; + u32 mix_control_P2; + u32 vol_control_A2; + u32 vol_control_B2; - u32 dwMixControl_P3; - u32 dwVolControl_A3; - u32 dwVolControl_B3; + u32 mix_control_P3; + u32 vol_control_A3; + u32 vol_control_B3; - u32 dwMixControl_P4; - u32 dwVolControl_A4; - u32 dwVolControl_B4; + u32 mix_control_P4; + u32 vol_control_A4; + u32 vol_control_B4; }; -static u8 m_bAddressSize, m_bDataSize, m_bDeviceID; - -static int I2CUnlockFullAccess(struct hw *hw) +static int hw20k2_i2c_unlock_full_access(struct hw *hw) { u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] = {0xB3, 0xD4}; @@ -1420,7 +1426,7 @@ static int I2CUnlockFullAccess(struct hw *hw) return -1; } -static int I2CLockChip(struct hw *hw) +static int hw20k2_i2c_lock_chip(struct hw *hw) { /* Write twice */ hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED); @@ -1431,54 +1437,55 @@ static int I2CLockChip(struct hw *hw) return -1; } -static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize) +static int hw20k2_i2c_init(struct hw *hw, u8 dev_id, u8 addr_size, u8 data_size) { + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; int err; - unsigned int RegI2CStatus; - unsigned int RegI2CAddress; + unsigned int i2c_status; + unsigned int i2c_addr; - err = I2CUnlockFullAccess(hw); + err = hw20k2_i2c_unlock_full_access(hw); if (err < 0) return err; - m_bAddressSize = bAddressSize; - m_bDataSize = bDataSize; - m_bDeviceID = bDeviceID; + hw20k2->addr_size = addr_size; + hw20k2->data_size = data_size; + hw20k2->dev_id = dev_id; - RegI2CAddress = 0; - set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, bDeviceID); + i2c_addr = 0; + set_field(&i2c_addr, I2C_ADDRESS_SLAD, dev_id); - hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr); - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_DCM, 1); /* Direct control mode */ + set_field(&i2c_status, I2C_STATUS_DCM, 1); /* Direct control mode */ - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); return 0; } -static int I2CUninit(struct hw *hw) +static int hw20k2_i2c_uninit(struct hw *hw) { - unsigned int RegI2CStatus; - unsigned int RegI2CAddress; + unsigned int i2c_status; + unsigned int i2c_addr; - RegI2CAddress = 0; - set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ + i2c_addr = 0; + set_field(&i2c_addr, I2C_ADDRESS_SLAD, 0x57); /* I2C id */ - hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress); + hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr); - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_DCM, 0); /* I2C mode */ + set_field(&i2c_status, I2C_STATUS_DCM, 0); /* I2C mode */ - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); - return I2CLockChip(hw); + return hw20k2_i2c_lock_chip(hw); } -static int I2CWaitDataReady(struct hw *hw) +static int hw20k2_i2c_wait_data_ready(struct hw *hw) { int i = 0x400000; unsigned int ret; @@ -1490,51 +1497,53 @@ static int I2CWaitDataReady(struct hw *hw) return i; } -static int I2CRead(struct hw *hw, u16 wAddress, u32 *pdwData) +static int hw20k2_i2c_read(struct hw *hw, u16 addr, u32 *datap) { - unsigned int RegI2CStatus; + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + unsigned int i2c_status; - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_BC, - (4 == m_bAddressSize) ? 0 : m_bAddressSize); - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); - if (!I2CWaitDataReady(hw)) + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); + set_field(&i2c_status, I2C_STATUS_BC, + (4 == hw20k2->addr_size) ? 0 : hw20k2->addr_size); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); + if (!hw20k2_i2c_wait_data_ready(hw)) return -1; - hw_write_20kx(hw, I2C_IF_WDATA, (u32)wAddress); - if (!I2CWaitDataReady(hw)) + hw_write_20kx(hw, I2C_IF_WDATA, addr); + if (!hw20k2_i2c_wait_data_ready(hw)) return -1; /* Force a read operation */ hw_write_20kx(hw, I2C_IF_RDATA, 0); - if (!I2CWaitDataReady(hw)) + if (!hw20k2_i2c_wait_data_ready(hw)) return -1; - *pdwData = hw_read_20kx(hw, I2C_IF_RDATA); + *datap = hw_read_20kx(hw, I2C_IF_RDATA); return 0; } -static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) +static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data) { - unsigned int dwI2CData = (dwData << (m_bAddressSize * 8)) | wAddress; - unsigned int RegI2CStatus; + struct hw20k2 *hw20k2 = (struct hw20k2 *)hw; + unsigned int i2c_data = (data << (hw20k2->addr_size * 8)) | addr; + unsigned int i2c_status; - RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS); + i2c_status = hw_read_20kx(hw, I2C_IF_STATUS); - set_field(&RegI2CStatus, I2C_STATUS_BC, - (4 == (m_bAddressSize + m_bDataSize)) ? - 0 : (m_bAddressSize + m_bDataSize)); + set_field(&i2c_status, I2C_STATUS_BC, + (4 == (hw20k2->addr_size + hw20k2->data_size)) ? + 0 : (hw20k2->addr_size + hw20k2->data_size)); - hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus); - I2CWaitDataReady(hw); + hw_write_20kx(hw, I2C_IF_STATUS, i2c_status); + hw20k2_i2c_wait_data_ready(hw); /* Dummy write to trigger the write oprtation */ hw_write_20kx(hw, I2C_IF_WDATA, 0); - I2CWaitDataReady(hw); + hw20k2_i2c_wait_data_ready(hw); /* This is the real data */ - hw_write_20kx(hw, I2C_IF_WDATA, dwI2CData); - I2CWaitDataReady(hw); + hw_write_20kx(hw, I2C_IF_WDATA, i2c_data); + hw20k2_i2c_wait_data_ready(hw); return 0; } @@ -1542,10 +1551,10 @@ static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData) static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { int err; - u32 dwData; + u32 data; int i; - struct REGS_CS4382 cs4382_Read = {0}; - struct REGS_CS4382 cs4382_Def = { + struct regs_cs4382 cs_read = {0}; + struct regs_cs4382 cs_def = { 0x00000001, /* Mode Control 1 */ 0x00000000, /* Mode Control 2 */ 0x00000084, /* Mode Control 3 */ @@ -1566,87 +1575,86 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) }; /* Set DAC reset bit as output */ - dwData = hw_read_20kx(hw, GPIO_CTRL); - dwData |= 0x02; - hw_write_20kx(hw, GPIO_CTRL, dwData); + data = hw_read_20kx(hw, GPIO_CTRL); + data |= 0x02; + hw_write_20kx(hw, GPIO_CTRL, data); - err = I2CInit(hw, 0x18, 1, 1); + err = hw20k2_i2c_init(hw, 0x18, 1, 1); if (err < 0) goto End; for (i = 0; i < 2; i++) { /* Reset DAC twice just in-case the chip * didn't initialized properly */ - dwData = hw_read_20kx(hw, GPIO_DATA); + data = hw_read_20kx(hw, GPIO_DATA); /* GPIO data bit 1 */ - dwData &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, dwData); + data &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(10); - dwData |= 0x2; - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= 0x2; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); /* Reset the 2nd time */ - dwData &= 0xFFFFFFFD; - hw_write_20kx(hw, GPIO_DATA, dwData); + data &= 0xFFFFFFFD; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(10); - dwData |= 0x2; - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= 0x2; + hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); - if (I2CRead(hw, CS4382_MC1, &cs4382_Read.dwModeControl_1)) + if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1)) continue; - if (I2CRead(hw, CS4382_MC2, &cs4382_Read.dwModeControl_2)) + if (hw20k2_i2c_read(hw, CS4382_MC2, &cs_read.mode_control_2)) continue; - if (I2CRead(hw, CS4382_MC3, &cs4382_Read.dwModeControl_3)) + if (hw20k2_i2c_read(hw, CS4382_MC3, &cs_read.mode_control_3)) continue; - if (I2CRead(hw, CS4382_FC, &cs4382_Read.dwFilterControl)) + if (hw20k2_i2c_read(hw, CS4382_FC, &cs_read.filter_control)) continue; - if (I2CRead(hw, CS4382_IC, &cs4382_Read.dwInvertControl)) + if (hw20k2_i2c_read(hw, CS4382_IC, &cs_read.invert_control)) continue; - if (I2CRead(hw, CS4382_XC1, &cs4382_Read.dwMixControl_P1)) + if (hw20k2_i2c_read(hw, CS4382_XC1, &cs_read.mix_control_P1)) continue; - if (I2CRead(hw, CS4382_VCA1, &cs4382_Read.dwVolControl_A1)) + if (hw20k2_i2c_read(hw, CS4382_VCA1, &cs_read.vol_control_A1)) continue; - if (I2CRead(hw, CS4382_VCB1, &cs4382_Read.dwVolControl_B1)) + if (hw20k2_i2c_read(hw, CS4382_VCB1, &cs_read.vol_control_B1)) continue; - if (I2CRead(hw, CS4382_XC2, &cs4382_Read.dwMixControl_P2)) + if (hw20k2_i2c_read(hw, CS4382_XC2, &cs_read.mix_control_P2)) continue; - if (I2CRead(hw, CS4382_VCA2, &cs4382_Read.dwVolControl_A2)) + if (hw20k2_i2c_read(hw, CS4382_VCA2, &cs_read.vol_control_A2)) continue; - if (I2CRead(hw, CS4382_VCB2, &cs4382_Read.dwVolControl_B2)) + if (hw20k2_i2c_read(hw, CS4382_VCB2, &cs_read.vol_control_B2)) continue; - if (I2CRead(hw, CS4382_XC3, &cs4382_Read.dwMixControl_P3)) + if (hw20k2_i2c_read(hw, CS4382_XC3, &cs_read.mix_control_P3)) continue; - if (I2CRead(hw, CS4382_VCA3, &cs4382_Read.dwVolControl_A3)) + if (hw20k2_i2c_read(hw, CS4382_VCA3, &cs_read.vol_control_A3)) continue; - if (I2CRead(hw, CS4382_VCB3, &cs4382_Read.dwVolControl_B3)) + if (hw20k2_i2c_read(hw, CS4382_VCB3, &cs_read.vol_control_B3)) continue; - if (I2CRead(hw, CS4382_XC4, &cs4382_Read.dwMixControl_P4)) + if (hw20k2_i2c_read(hw, CS4382_XC4, &cs_read.mix_control_P4)) continue; - if (I2CRead(hw, CS4382_VCA4, &cs4382_Read.dwVolControl_A4)) + if (hw20k2_i2c_read(hw, CS4382_VCA4, &cs_read.vol_control_A4)) continue; - if (I2CRead(hw, CS4382_VCB4, &cs4382_Read.dwVolControl_B4)) + if (hw20k2_i2c_read(hw, CS4382_VCB4, &cs_read.vol_control_B4)) continue; - if (memcmp(&cs4382_Read, &cs4382_Def, - sizeof(struct REGS_CS4382))) + if (memcmp(&cs_read, &cs_def, sizeof(cs_read))) continue; else break; @@ -1657,29 +1665,29 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) /* Note: Every I2C write must have some delay. * This is not a requirement but the delay works here... */ - I2CWrite(hw, CS4382_MC1, 0x80); - I2CWrite(hw, CS4382_MC2, 0x10); + hw20k2_i2c_write(hw, CS4382_MC1, 0x80); + hw20k2_i2c_write(hw, CS4382_MC2, 0x10); if (1 == info->msr) { - I2CWrite(hw, CS4382_XC1, 0x24); - I2CWrite(hw, CS4382_XC2, 0x24); - I2CWrite(hw, CS4382_XC3, 0x24); - I2CWrite(hw, CS4382_XC4, 0x24); + hw20k2_i2c_write(hw, CS4382_XC1, 0x24); + hw20k2_i2c_write(hw, CS4382_XC2, 0x24); + hw20k2_i2c_write(hw, CS4382_XC3, 0x24); + hw20k2_i2c_write(hw, CS4382_XC4, 0x24); } else if (2 == info->msr) { - I2CWrite(hw, CS4382_XC1, 0x25); - I2CWrite(hw, CS4382_XC2, 0x25); - I2CWrite(hw, CS4382_XC3, 0x25); - I2CWrite(hw, CS4382_XC4, 0x25); + hw20k2_i2c_write(hw, CS4382_XC1, 0x25); + hw20k2_i2c_write(hw, CS4382_XC2, 0x25); + hw20k2_i2c_write(hw, CS4382_XC3, 0x25); + hw20k2_i2c_write(hw, CS4382_XC4, 0x25); } else { - I2CWrite(hw, CS4382_XC1, 0x26); - I2CWrite(hw, CS4382_XC2, 0x26); - I2CWrite(hw, CS4382_XC3, 0x26); - I2CWrite(hw, CS4382_XC4, 0x26); + hw20k2_i2c_write(hw, CS4382_XC1, 0x26); + hw20k2_i2c_write(hw, CS4382_XC2, 0x26); + hw20k2_i2c_write(hw, CS4382_XC3, 0x26); + hw20k2_i2c_write(hw, CS4382_XC4, 0x26); } return 0; End: - I2CUninit(hw); + hw20k2_i2c_uninit(hw); return -1; } @@ -1721,21 +1729,21 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) case ADC_MICIN: data |= (0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), MAKE_WM8775_DATA(0x101)); /* Mic-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ break; case ADC_LINEIN: data &= ~(0x1 << 14); hw_write_20kx(hw, GPIO_DATA, data); - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), MAKE_WM8775_DATA(0x102)); /* Line-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ break; default: @@ -1748,34 +1756,34 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { int err; - u32 dwMux = 2, dwData, dwCtl; + u32 mux = 2, data, ctl; /* Set ADC reset bit as output */ - dwData = hw_read_20kx(hw, GPIO_CTRL); - dwData |= (0x1 << 15); - hw_write_20kx(hw, GPIO_CTRL, dwData); + data = hw_read_20kx(hw, GPIO_CTRL); + data |= (0x1 << 15); + hw_write_20kx(hw, GPIO_CTRL, data); /* Initialize I2C */ - err = I2CInit(hw, 0x1A, 1, 1); + err = hw20k2_i2c_init(hw, 0x1A, 1, 1); if (err < 0) { printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n"); goto error; } /* Make ADC in normal operation */ - dwData = hw_read_20kx(hw, GPIO_DATA); - dwData &= ~(0x1 << 15); + data = hw_read_20kx(hw, GPIO_DATA); + data &= ~(0x1 << 15); mdelay(10); - dwData |= (0x1 << 15); - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= (0x1 << 15); + hw_write_20kx(hw, GPIO_DATA, data); mdelay(50); /* Set the master mode (256fs) */ if (1 == info->msr) { - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02), MAKE_WM8775_DATA(0x02)); } else if (2 == info->msr) { - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A), MAKE_WM8775_DATA(0x0A)); } else { printk(KERN_ALERT "ctxfi: Invalid master sampling " @@ -1785,35 +1793,35 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) } /* Configure GPIO bit 14 change to line-in/mic-in */ - dwCtl = hw_read_20kx(hw, GPIO_CTRL); - dwCtl |= 0x1<<14; - hw_write_20kx(hw, GPIO_CTRL, dwCtl); + ctl = hw_read_20kx(hw, GPIO_CTRL); + ctl |= 0x1 << 14; + hw_write_20kx(hw, GPIO_CTRL, ctl); /* Check using Mic-in or Line-in */ - dwData = hw_read_20kx(hw, GPIO_DATA); + data = hw_read_20kx(hw, GPIO_DATA); - if (dwMux == 1) { + if (mux == 1) { /* Configures GPIO data to select Mic-in */ - dwData |= 0x1<<14; - hw_write_20kx(hw, GPIO_DATA, dwData); + data |= 0x1 << 14; + hw_write_20kx(hw, GPIO_DATA, data); - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), MAKE_WM8775_DATA(0x101)); /* Mic-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - } else if (dwMux == 2) { + } else if (mux == 2) { /* Configures GPIO data to select Line-in */ - dwData &= ~(0x1<<14); - hw_write_20kx(hw, GPIO_DATA, dwData); + data &= ~(0x1 << 14); + hw_write_20kx(hw, GPIO_DATA, data); /* Setup ADC */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102), MAKE_WM8775_DATA(0x102)); /* Line-in */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ - I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), + hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF), MAKE_WM8775_DATA(0xCF)); /* No boost */ } else { printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n"); @@ -1824,7 +1832,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) return 0; error: - I2CUninit(hw); + hw20k2_i2c_uninit(hw); return err; } @@ -2106,15 +2114,15 @@ static struct hw ct20k2_preset __devinitdata = { int __devinit create_20k2_hw_obj(struct hw **rhw) { - struct hw *hw; + struct hw20k2 *hw20k2; *rhw = NULL; - hw = kzalloc(sizeof(*hw), GFP_KERNEL); - if (NULL == hw) + hw20k2 = kzalloc(sizeof(*hw20k2), GFP_KERNEL); + if (!hw20k2) return -ENOMEM; - *hw = ct20k2_preset; - *rhw = hw; + hw20k2->hw = ct20k2_preset; + *rhw = &hw20k2->hw; return 0; } -- cgit v1.2.1 From 806d31d73843a018cb239fc16ba53d82f4d8dc33 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 15:50:42 +0200 Subject: ALSA: emu10k1 - Fix minimum periods for efx playback EFX playback stream should have periods_min = 2 to avoid the buffer position overflow (due to restrictions of the pcm-indirect helper). Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emupcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 78f62fd404c2..55b83ef73c63 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1736,7 +1736,7 @@ static struct snd_pcm_hardware snd_emu10k1_fx8010_playback = .buffer_bytes_max = (128*1024), .period_bytes_min = 1024, .period_bytes_max = (128*1024), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, .fifo_size = 0, }; -- cgit v1.2.1 From 64a8be74357477558183b43156c5536b642de134 Mon Sep 17 00:00:00 2001 From: David Heidelberger Date: Mon, 8 Jun 2009 16:15:18 +0200 Subject: ALSA: hda - Add 7.1 support for MSI GX620 Added 7.1 support for MSI GX620 and jack quirk. Reference: kernel bug#13430 http://bugzilla.kernel.org/show_bug.cgi?id=13430 Signed-off-by: David Heidelberger Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 115 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1ab1b92fdc1c..cfe4808c3a4c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -220,6 +220,7 @@ enum { ALC883_6ST_DIG, ALC883_TARGA_DIG, ALC883_TARGA_2ch_DIG, + ALC883_TARGA_8ch_DIG, ALC883_ACER, ALC883_ACER_ASPIRE, ALC888_ACER_ASPIRE_4930G, @@ -2722,6 +2723,7 @@ static struct hda_verb alc880_pin_asus_init_verbs[] = { /* Enable GPIO mask and set output */ #define alc880_gpio1_init_verbs alc_gpio1_init_verbs #define alc880_gpio2_init_verbs alc_gpio2_init_verbs +#define alc880_gpio3_init_verbs alc_gpio3_init_verbs /* Clevo m520g init */ static struct hda_verb alc880_pin_clevo_init_verbs[] = { @@ -7719,6 +7721,73 @@ static struct hda_channel_mode alc883_3ST_6ch_modes[3] = { { 6, alc883_3ST_ch6_init }, }; + +/* + * 2ch mode + */ +static struct hda_verb alc883_4ST_ch2_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PhIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static struct hda_verb alc883_4ST_ch4_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_4ST_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc883_4ST_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_4ST_8ch_modes[4] = { + { 2, alc883_4ST_ch2_init }, + { 4, alc883_4ST_ch4_init }, + { 6, alc883_4ST_ch6_init }, + { 8, alc883_4ST_ch8_init }, +}; + + /* * 2ch mode */ @@ -8355,14 +8424,24 @@ static struct hda_verb alc883_tagra_verbs[] = { {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */ - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ +/* Connect Line-Out side jack (SPDIF) to Side */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, { } /* end */ }; @@ -8607,8 +8686,8 @@ static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); @@ -8895,6 +8974,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_6ST_DIG] = "6stack-dig", [ALC883_TARGA_DIG] = "targa-dig", [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", + [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig", [ALC883_ACER] = "acer", [ALC883_ACER_ASPIRE] = "acer-aspire", [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", @@ -8979,6 +9059,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG), SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), @@ -9108,6 +9189,24 @@ static struct alc_config_preset alc883_presets[] = { .unsol_event = alc883_tagra_unsol_event, .init_hook = alc883_tagra_init_hook, }, + [ALC883_TARGA_8ch_DIG] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, + alc883_tagra_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes), + .channel_mode = alc883_4ST_8ch_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_tagra_unsol_event, + .init_hook = alc883_tagra_automute, + }, [ALC883_ACER] = { .mixers = { alc883_base_mixer }, /* On TravelMate laptops, GPIO 0 enables the internal speaker -- cgit v1.2.1 From f03ecf50534a81b06544c58a713076d59d54baf9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 16:38:17 +0200 Subject: ALSA: hda - Fix the previous tagra-8ch patch - Fix a typo in the patch - Adapted to follow the recent change for unsol event handling Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cfe4808c3a4c..337d2a59c67e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7726,7 +7726,7 @@ static struct hda_channel_mode alc883_3ST_6ch_modes[3] = { * 2ch mode */ static struct hda_verb alc883_4ST_ch2_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PhIN_OUT }, + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, @@ -9205,7 +9205,7 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, .unsol_event = alc883_tagra_unsol_event, - .init_hook = alc883_tagra_automute, + .init_hook = alc883_tagra_init_hook, }, [ALC883_ACER] = { .mixers = { alc883_base_mixer }, -- cgit v1.2.1 From 9470195a9cd13e6d90221b8b5d897e9232da8d28 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 18:10:32 +0200 Subject: ALSA: ctxfi - Clean up probe routines Clean up probe routines and model detection routines so that the driver won't call and check the PCI subsystem id at each time. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 132 +++++++++++++++++++------------------------ sound/pci/ctxfi/ctatc.h | 17 ++---- sound/pci/ctxfi/ctdaio.c | 6 +- sound/pci/ctxfi/cthardware.c | 31 +++------- sound/pci/ctxfi/cthardware.h | 20 ++++++- sound/pci/ctxfi/cthw20k1.c | 88 +++++++++++------------------ sound/pci/ctxfi/xfi.c | 15 +++-- 7 files changed, 135 insertions(+), 174 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 7898a375df0e..002a70e0b13a 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -39,29 +39,40 @@ | (0x10 << 16) \ | ((IEC958_AES3_CON_FS_48000) << 24)) -static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = { - [CTSB0760] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB0760, - .nm_model = "SB076x"}, - [CTHENDRIX] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, - .nm_model = "Hendrix"}, - [CTSB08801] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08801, - .nm_model = "SB0880"}, - [CTSB08802] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08802, - .nm_model = "SB0880"}, - [CTSB08803] = {.subsys = PCI_SUBDEVICE_ID_CREATIVE_SB08803, - .nm_model = "SB0880"} +static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTUAA), + SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE, + "Unknown", CT20K1_UNKNOWN), + { } /* terminator */ }; -static struct ct_atc_chip_details atc_chip_details[] = { - {.vendor = PCI_VENDOR_ID_CREATIVE, - .device = PCI_DEVICE_ID_CREATIVE_20K1, - .sub_details = NULL, - .nm_card = "X-Fi 20k1"}, - {.vendor = PCI_VENDOR_ID_CREATIVE, - .device = PCI_DEVICE_ID_CREATIVE_20K2, - .sub_details = atc_sub_details, - .nm_card = "X-Fi 20k2"}, - {} /* terminator */ +static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760, + "SB0760", CTSB0760), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801, + "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802, + "SB0880", CTSB0880), + SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, + "SB0880", CTSB0880), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTHENDRIX), + { } /* terminator */ +}; + +static const char *ct_subsys_name[NUM_CTCARDS] = { + [CTSB055X] = "SB055x", + [CTSB073X] = "SB073x", + [CTSB0760] = "SB076x", + [CTUAA] = "UAA", + [CT20K1_UNKNOWN] = "Unknown", + [CTHENDRIX] = "Hendrix", + [CTSB0880] = "SB0880", }; static struct { @@ -1208,62 +1219,39 @@ static int atc_dev_free(struct snd_device *dev) static int __devinit atc_identify_card(struct ct_atc *atc) { - u16 subsys; - u8 revision; - struct pci_dev *pci = atc->pci; - const struct ct_atc_chip_details *d; - enum CTCARDS i; - - subsys = pci->subsystem_device; - revision = pci->revision; - atc->chip_details = NULL; - atc->model = NUM_CTCARDS; - for (d = atc_chip_details; d->vendor; d++) { - if (d->vendor != pci->vendor || d->device != pci->device) - continue; + const struct snd_pci_quirk *p; + const struct snd_pci_quirk *list; - if (NULL == d->sub_details) { - atc->chip_details = d; - break; - } - for (i = 0; i < NUM_CTCARDS; i++) { - if ((d->sub_details[i].subsys == subsys) || - (((subsys & 0x6000) == 0x6000) && - ((d->sub_details[i].subsys & 0x6000) == 0x6000))) { - atc->model = i; - break; - } - } - if (i >= NUM_CTCARDS) - continue; - - atc->chip_details = d; + switch (atc->chip_type) { + case ATC20K1: + atc->chip_name = "20K1"; + list = subsys_20k1_list; + break; + case ATC20K2: + atc->chip_name = "20K2"; + list = subsys_20k2_list; break; - /* not take revision into consideration now */ + default: + return -ENOENT; } - if (!d->vendor) + p = snd_pci_quirk_lookup(atc->pci, list); + if (!p) return -ENOENT; - + atc->model = p->value; + atc->model_name = ct_subsys_name[atc->model]; + snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", + atc->chip_name, atc->model_name, + atc->pci->subsystem_vendor, + atc->pci->subsystem_device); return 0; } int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) { enum CTALSADEVS i; - struct hw *hw = atc->hw; int err; - switch (hw->get_chip_type(hw)) { - case ATC20K1: - alsa_dev_funcs[MIXER].public_name = "20K1"; - break; - case ATC20K2: - alsa_dev_funcs[MIXER].public_name = "20K2"; - break; - default: - alsa_dev_funcs[MIXER].public_name = "Unknown"; - break; - } + alsa_dev_funcs[MIXER].public_name = atc->chip_name; for (i = 0; i < NUM_CTALSADEVS; i++) { if (NULL == alsa_dev_funcs[i].create) @@ -1287,7 +1275,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) struct card_conf info = {0}; int i, err; - err = create_hw_obj(atc->pci, &hw); + err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw); if (err) { printk(KERN_ERR "Failed to create hw obj!!!\n"); return err; @@ -1328,7 +1316,6 @@ static int __devinit atc_get_resources(struct ct_atc *atc) struct sum_desc sum_dsc = {0}; struct sum_mgr *sum_mgr; int err, i; - unsigned short subsys_id; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); if (NULL == atc->daios) @@ -1359,13 +1346,10 @@ static int __devinit atc_get_resources(struct ct_atc *atc) } atc->n_daio++; } - subsys_id = atc->pci->subsystem_device; - if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + if (atc->model == CTSB073X) da_desc.type = SPDIFI1; - } else { + else da_desc.type = SPDIFIO; - } err = daio_mgr->get_daio(daio_mgr, &da_desc, (struct daio **)&atc->daios[i]); if (err) { @@ -1555,7 +1539,8 @@ static struct ct_atc atc_preset __devinitdata = { */ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, - unsigned int rsr, unsigned int msr, struct ct_atc **ratc) + unsigned int rsr, unsigned int msr, + int chip_type, struct ct_atc **ratc) { struct ct_atc *atc; static struct snd_device_ops ops = { @@ -1576,6 +1561,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, atc->pci = pci; atc->rsr = rsr; atc->msr = msr; + atc->chip_type = chip_type; spin_lock_init(&atc->atc_lock); diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 04459aa0d4d9..a03347232e84 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -37,15 +37,6 @@ enum CTALSADEVS { /* Types of alsa devices */ NUM_CTALSADEVS /* This should always be the last */ }; -enum CTCARDS { - CTSB0760, - CTHENDRIX, - CTSB08801, - CTSB08802, - CTSB08803, - NUM_CTCARDS /* This should always be the last */ -}; - struct ct_atc_chip_sub_details { u16 subsys; const char *nm_model; @@ -89,8 +80,10 @@ struct ct_atc { unsigned int msr; /* master sample rate in rsr */ unsigned int pll_rate; /* current rate of Phase Lock Loop */ - const struct ct_atc_chip_details *chip_details; - enum CTCARDS model; + int chip_type; + int model; + const char *chip_name; + const char *model_name; struct ct_vm *vm; /* device virtual memory manager for this card */ int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm); @@ -147,7 +140,7 @@ struct ct_atc { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, - unsigned int rsr, unsigned int msr, + unsigned int rsr, unsigned int msr, int chip_type, struct ct_atc **ratc); int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index befead4eeaab..082e35c08c02 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -116,7 +116,7 @@ static struct rsc_ops daio_in_rsc_ops_20k2 = { static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw) { - switch (hw->get_chip_type(hw)) { + switch (hw->chip_type) { case ATC20K1: switch (type) { case SPDIFOO: return 0; @@ -343,7 +343,7 @@ static int daio_rsc_init(struct daio *daio, int err; unsigned int idx_l, idx_r; - switch (((struct hw *)hw)->get_chip_type(hw)) { + switch (((struct hw *)hw)->chip_type) { case ATC20K1: idx_l = idx_20k1[desc->type].left; idx_r = idx_20k1[desc->type].right; @@ -367,7 +367,7 @@ static int daio_rsc_init(struct daio *daio, if (desc->type <= DAIO_OUT_MAX) { daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops; } else { - switch (((struct hw *)hw)->get_chip_type(hw)) { + switch (((struct hw *)hw)->chip_type) { case ATC20K1: daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1; break; diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c index 5ec6813d3911..8e64f4862e85 100644 --- a/sound/pci/ctxfi/cthardware.c +++ b/sound/pci/ctxfi/cthardware.c @@ -20,34 +20,16 @@ #include "cthw20k2.h" #include -static enum CHIPTYP __devinitdata get_chip_type(struct hw *hw) -{ - enum CHIPTYP type; - - switch (hw->pci->device) { - case 0x0005: /* 20k1 device */ - type = ATC20K1; - break; - case 0x000B: /* 20k2 device */ - type = ATC20K2; - break; - default: - type = ATCNONE; - break; - } - - return type; -} - -int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) +int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, + enum CTCARDS model, struct hw **rhw) { int err; - switch (pci->device) { - case 0x0005: /* 20k1 device */ + switch (chip_type) { + case ATC20K1: err = create_20k1_hw_obj(rhw); break; - case 0x000B: /* 20k2 device */ + case ATC20K2: err = create_20k2_hw_obj(rhw); break; default: @@ -58,7 +40,8 @@ int __devinit create_hw_obj(struct pci_dev *pci, struct hw **rhw) return err; (*rhw)->pci = pci; - (*rhw)->get_chip_type = get_chip_type; + (*rhw)->chip_type = chip_type; + (*rhw)->model = model; return 0; } diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 8f11644ddc92..4a8e04f090a4 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -27,6 +27,19 @@ enum CHIPTYP { ATCNONE }; +enum CTCARDS { + /* 20k1 models */ + CTSB055X, + CTSB073X, + CTUAA, + CT20K1_UNKNOWN, + /* 20k2 models */ + CTSB0760, + CTHENDRIX, + CTSB0880, + NUM_CTCARDS /* This should always be the last */ +}; + /* Type of input source for ADC */ enum ADCSRC{ ADC_MICIN, @@ -48,7 +61,6 @@ struct hw { int (*card_init)(struct hw *hw, struct card_conf *info); int (*card_stop)(struct hw *hw); int (*pll_init)(struct hw *hw, unsigned int rsr); - enum CHIPTYP (*get_chip_type)(struct hw *hw); int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); int (*select_adc_source)(struct hw *hw, enum ADCSRC source); int (*have_digit_io_switch)(struct hw *hw); @@ -156,9 +168,13 @@ struct hw { int irq; unsigned long io_base; unsigned long mem_base; + + enum CHIPTYP chip_type; + enum CTCARDS model; }; -int create_hw_obj(struct pci_dev *pci, struct hw **rhw); +int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type, + enum CTCARDS model, struct hw **rhw); int destroy_hw_obj(struct hw *hw); unsigned int get_field(unsigned int data, unsigned int field); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 38b87b6ee6d4..5d58650beb73 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1432,11 +1432,9 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info) { u32 data; u16 gpioorg; - u16 subsys_id; unsigned int ret; - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { + if (hw->model == CTSB055X) { /* SB055x, unmute outputs */ gpioorg = (u16)hw_read_20kx(hw, GPIO); gpioorg &= 0xffbf; /* set GPIO6 to low */ @@ -1538,19 +1536,14 @@ static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type) static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ + switch (hw->model) { + case CTSB055X: return is_adc_input_selected_SB055x(hw, type); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + case CTSB073X: return is_adc_input_selected_hendrix(hw, type); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ + case CTHENDRIX: return is_adc_input_selected_hendrix(hw, type); - } else { + default: return is_adc_input_selected_SBx(hw, type); } } @@ -1692,20 +1685,17 @@ adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost) static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ - return adc_input_select_SB055x(hw, type, (ADC_MICIN == type)); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ - return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ - return adc_input_select_hendrix(hw, type, (ADC_MICIN == type)); - } else { - return adc_input_select_SBx(hw, type, (ADC_MICIN == type)); + int state = type == ADC_MICIN; + + switch (hw->model) { + case CTSB055X: + return adc_input_select_SB055x(hw, type, state); + case CTSB073X: + return adc_input_select_hendrix(hw, type, state); + case CTHENDRIX: + return adc_input_select_hendrix(hw, type, state); + default: + return adc_input_select_SBx(hw, type, state); } } @@ -1781,28 +1771,16 @@ static int adc_init_SBx(struct hw *hw, int input, int mic20db) static int hw_adc_init(struct hw *hw, const struct adc_conf *info) { - int err; - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* Sb055x card */ - err = adc_init_SB055x(hw, info->input, info->mic20db); - } else { - err = adc_init_SBx(hw, info->input, info->mic20db); - } - - return err; + if (hw->model == CTSB055X) + return adc_init_SB055x(hw, info->input, info->mic20db); + else + return adc_init_SBx(hw, info->input, info->mic20db); } static int hw_have_digit_io_switch(struct hw *hw) { - u16 subsys_id; - - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* SB073x and Vista compatible cards have no digit IO switch */ - return !((subsys_id == 0x0029) || (subsys_id == 0x0031) - || ((subsys_id & 0xf000) == 0x6000)); + return !(hw->model == CTSB073X || hw->model == CTHENDRIX); } #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -1918,7 +1896,6 @@ static int hw_card_start(struct hw *hw) { int err; struct pci_dev *pci = hw->pci; - u16 subsys_id; err = pci_enable_device(pci); if (err < 0) @@ -1939,8 +1916,7 @@ static int hw_card_start(struct hw *hw) goto error1; /* Switch to X-Fi mode from UAA mode if neeeded */ - pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id); - if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) { + if (hw->model == CTHENDRIX) { err = uaa_to_xfi(pci); if (err) goto error2; @@ -2004,7 +1980,6 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) { int err; unsigned int gctl; - u16 subsys_id; u32 data; struct dac_conf dac_info = {0}; struct adc_conf adc_info = {0}; @@ -2044,19 +2019,20 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) hw_write_20kx(hw, SRCIP, 0); mdelay(30); - pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id); /* Detect the card ID and configure GPIO accordingly. */ - if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) { - /* SB055x cards */ + switch (hw->model) { + case CTSB055X: hw_write_20kx(hw, GPIOCTL, 0x13fe); - } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) { - /* SB073x cards */ + break; + case CTSB073X: hw_write_20kx(hw, GPIOCTL, 0x00e6); - } else if ((subsys_id & 0xf000) == 0x6000) { - /* Vista compatible cards */ + break; + case CTHENDRIX: /* Vista compatible cards */ hw_write_20kx(hw, GPIOCTL, 0x00c2); - } else { + break; + default: hw_write_20kx(hw, GPIOCTL, 0x01e6); + break; } trn_info.vm_pgt_phys = info->vm_pgt_phys; diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 279dac6c34dd..2d3dd89af151 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -15,6 +15,7 @@ #include #include #include "ctatc.h" +#include "cthardware.h" MODULE_AUTHOR("Creative Technology Ltd"); MODULE_DESCRIPTION("X-Fi driver version 1.03"); @@ -41,8 +42,12 @@ MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver"); static struct pci_device_id ct_pci_dev_ids[] = { /* only X-Fi is supported, so... */ - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1) }, - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2) }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1), + .driver_data = ATC20K1, + }, + { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2), + .driver_data = ATC20K2, + }, { 0, } }; MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids); @@ -79,7 +84,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) "1 and 2, Value 2 is assumed.\n"); multiple = 2; } - err = ct_atc_create(card, pci, reference_rate, multiple, &atc); + err = ct_atc_create(card, pci, reference_rate, multiple, + pci_id->driver_data, &atc); if (err < 0) goto error; @@ -92,7 +98,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) strcpy(card->driver, "SB-XFi"); strcpy(card->shortname, "Creative X-Fi"); - strcpy(card->longname, "Creative ALSA Driver X-Fi"); + snprintf(card->longname, sizeof(card->longname), "%s %s %s", + card->shortname, atc->chip_name, atc->model_name); err = snd_card_register(card); if (err < 0) -- cgit v1.2.1 From 09521d2e3edd0bf02b66e5b8c13f1559f2d6958a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 18:29:38 +0200 Subject: ALSA: ctxfi - Fix wrong model id for UAA CTUAA should be checked instead of CTHENDRIX. The latter is for 20k2 chip. Also, fixed the detection of UAA/HENDRIX models by fixing the mask bits. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 9 +++++---- sound/pci/ctxfi/cthw20k1.c | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 002a70e0b13a..4e25b24848bd 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -44,8 +44,8 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, - PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTUAA), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, 0x6000, + "UAA", CTUAA), SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE, "Unknown", CT20K1_UNKNOWN), { } /* terminator */ @@ -60,8 +60,9 @@ static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = { "SB0880", CTSB0880), SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803, "SB0880", CTSB0880), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0x6000, - PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "UAA", CTHENDRIX), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, + PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX", + CTHENDRIX), { } /* terminator */ }; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 5d58650beb73..cb69d9ddfbe3 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1541,7 +1541,7 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) return is_adc_input_selected_SB055x(hw, type); case CTSB073X: return is_adc_input_selected_hendrix(hw, type); - case CTHENDRIX: + case CTUAA: return is_adc_input_selected_hendrix(hw, type); default: return is_adc_input_selected_SBx(hw, type); @@ -1692,7 +1692,7 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) return adc_input_select_SB055x(hw, type, state); case CTSB073X: return adc_input_select_hendrix(hw, type, state); - case CTHENDRIX: + case CTUAA: return adc_input_select_hendrix(hw, type, state); default: return adc_input_select_SBx(hw, type, state); @@ -1780,7 +1780,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) static int hw_have_digit_io_switch(struct hw *hw) { /* SB073x and Vista compatible cards have no digit IO switch */ - return !(hw->model == CTSB073X || hw->model == CTHENDRIX); + return !(hw->model == CTSB073X || hw->model == CTUAA); } #define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -1916,7 +1916,7 @@ static int hw_card_start(struct hw *hw) goto error1; /* Switch to X-Fi mode from UAA mode if neeeded */ - if (hw->model == CTHENDRIX) { + if (hw->model == CTUAA) { err = uaa_to_xfi(pci); if (err) goto error2; @@ -2027,7 +2027,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) case CTSB073X: hw_write_20kx(hw, GPIOCTL, 0x00e6); break; - case CTHENDRIX: /* Vista compatible cards */ + case CTUAA: hw_write_20kx(hw, GPIOCTL, 0x00c2); break; default: -- cgit v1.2.1 From 04145f2bfbc3ac4dbb71fa085b82380444d50b4f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 21:31:50 +0200 Subject: ALSA: ctxfi - Add use_system_timer module option Added use_system_timer module option to force to use the system timer instead of emu20k1 timer irq for debugging. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/cttimer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index ec869a4fe2b3..779c6c3591a5 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -8,12 +8,17 @@ #include #include +#include #include #include #include "ctatc.h" #include "cthardware.h" #include "cttimer.h" +static int use_system_timer; +MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer"); +module_param(use_system_timer, bool, S_IRUGO); + struct ct_timer_ops { void (*init)(struct ct_timer_instance *); void (*prepare)(struct ct_timer_instance *); @@ -390,8 +395,6 @@ void ct_timer_instance_free(struct ct_timer_instance *ti) * timer manager */ -#define USE_SYSTEM_TIMER 0 - static void ct_timer_interrupt(void *data, unsigned int status) { struct ct_timer *timer = data; @@ -415,7 +418,7 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) INIT_LIST_HEAD(&atimer->running_head); atimer->atc = atc; hw = atc->hw; - if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { + if (!use_system_timer && hw->set_timer_irq) { snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n"); atimer->ops = &ct_xfitimer_ops; hw->irq_callback_data = atimer; -- cgit v1.2.1 From c399f3be82e1249221a7f410855ab1aa747fa82b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2009 08:16:20 +0200 Subject: ALSA: ctxfi - Add missing start check in atc_pcm_playback_start() Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 4e25b24848bd..799eb98e7794 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -392,6 +392,10 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) unsigned int max_cisz; struct src *src = apcm->src; + if (apcm->started) + return 0; + apcm->started = 1; + max_cisz = src->multi * src->rsc.msr; max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8); -- cgit v1.2.1 From 5242bc7613311aa1a3d5ed41e9cf81015b65563f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2009 08:17:14 +0200 Subject: ALSA: ctxfi - Check the presence of SRC instance in PCM pointer callbacks The SRC instances may not exist when PCM pointer callback is called at the state before initialization is finished. Add the NULL check just to be sure. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 799eb98e7794..e54006ef83cf 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -445,6 +445,8 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) u32 size, max_cisz; int position; + if (!src) + return 0; position = src->ops->get_ca(src); size = apcm->vm_block->size; @@ -782,6 +784,8 @@ atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct src *src = apcm->src; + if (!src) + return 0; return src->ops->get_ca(src) - apcm->vm_block->addr; } -- cgit v1.2.1 From a5990dc5b96f537618b0f057c8723a6a0b0cdc74 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2009 08:19:02 +0200 Subject: ALSA: ctxfi - Clear PCM resources at hw_params and hw_free Currently the PCM resources are allocated only once and ever in prepare callback, assuming that the PCM parameters are never changed. But it's not true. This patch adds the call of atc->pcm_release_resources() at hw_params and hw_free callbacks to assure that the PCM setup is done correctly for each h/w parameter changes. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 4 ++-- sound/pci/ctxfi/ctpcm.c | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index e54006ef83cf..80fb2baed7a7 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -523,7 +523,7 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct src_node_conf_t src_node_conf[2] = {{0} }; /* first release old resources */ - atc->pcm_release_resources(atc, apcm); + atc_pcm_release_resources(atc, apcm); /* The numbers of converting SRCs and SRCIMPs should be determined * by pitch value. */ @@ -802,7 +802,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, unsigned int pitch, rsr = atc->pll_rate; /* first release old resources */ - atc->pcm_release_resources(atc, apcm); + atc_pcm_release_resources(atc, apcm); /* Get SRC resource */ desc.multi = apcm->substream->runtime->channels; diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 870fa170f046..9e5c0c4da726 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -176,12 +176,26 @@ static int ct_pcm_playback_close(struct snd_pcm_substream *substream) static int ct_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - return snd_pcm_lib_malloc_pages(substream, + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct ct_atc_pcm *apcm = substream->runtime->private_data; + int err; + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) + return err; + /* clear previous resources */ + atc->pcm_release_resources(atc, apcm); + return err; } static int ct_pcm_hw_free(struct snd_pcm_substream *substream) { + struct ct_atc *atc = snd_pcm_substream_chip(substream); + struct ct_atc_pcm *apcm = substream->runtime->private_data; + + /* clear previous resources */ + atc->pcm_release_resources(atc, apcm); /* Free snd-allocated pages */ return snd_pcm_lib_free_pages(substream); } -- cgit v1.2.1 From a5c0f88678cd2fb1f649f7d366d756f2b2f97f0c Mon Sep 17 00:00:00 2001 From: Simos Xenitellis Date: Wed, 10 Jun 2009 16:33:31 +0100 Subject: ALSA: hda - add quirk for STAC92xx (SigmaTel STAC9205) A quirk is required for 8086:284b (rev 03) [Subsystem: 161f:2073]. The following has been tested with Alsa 1.0.20 (git master). Background details can be found at https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4561 http://forum.ubuntu-gr.org/viewtopic.php?f=38&t=5290 Tested-by: Theodora Iliopoulou Signed-off-by: Simos Xenitellis Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 42f944bb641d..93e47c96a38b 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1590,6 +1590,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30, + "SigmaTel",STAC_9205_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), /* Dell laptops have BIOS problem */ @@ -5938,6 +5940,7 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 }, { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 }, { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 }, + { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 }, { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 }, { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 }, { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 }, -- cgit v1.2.1 From de0525ca34aa7a65faf6902e3e00956ab80d2ede Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Thu, 11 Jun 2009 16:03:34 +0200 Subject: ALSA: lx6464es - support standard alsa module parameters trivial patch to support the alsa module parameters `index', `id' and `enable' Signed-off-by: Tim Blechmann Signed-off-by: Takashi Iwai --- sound/pci/lx6464es/lx6464es.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 870bfc58c697..ccf1b38c88ea 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -43,6 +43,13 @@ 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_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Digigram LX6464ES interface."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards."); + static const char card_name[] = "LX6464ES"; -- cgit v1.2.1 From 4b512d26f425be1c779c8319249b42ce3c3424d2 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 14 Apr 2009 23:14:10 -0300 Subject: trivial: typo (en|dis|avail|remove)bale -> (en|dis|avail|remove)able Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Jiri Kosina --- sound/pci/aw2/aw2-saa7146.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c index 6a3891ab69dd..296123ab74f7 100644 --- a/sound/pci/aw2/aw2-saa7146.c +++ b/sound/pci/aw2/aw2-saa7146.c @@ -108,7 +108,7 @@ void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip, #endif /* WS0_CTRL, WS0_SYNC: input TSL1, I2S */ - /* At initialization WS1 and WS2 are disbaled (configured as input */ + /* At initialization WS1 and WS2 are disabled (configured as input) */ acon1 |= 0 * WS1_CTRL; acon1 |= 0 * WS2_CTRL; -- cgit v1.2.1 From 98a1708de1bfa5fe1c490febba850d6043d3c7fa Mon Sep 17 00:00:00 2001 From: Martin Olsson Date: Wed, 22 Apr 2009 18:21:29 +0200 Subject: trivial: fix typos s/paramter/parameter/ and s/excute/execute/ in documentation and source comments. Signed-off-by: Martin Olsson Signed-off-by: Jiri Kosina --- sound/pci/sis7019.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h index 013b6739a742..bc8c76819408 100644 --- a/sound/pci/sis7019.h +++ b/sound/pci/sis7019.h @@ -203,7 +203,7 @@ #define SIS_WEISR_B 0xac -/* Playback DMA parameters (paramter RAM) */ +/* Playback DMA parameters (parameter RAM) */ #define SIS_PLAY_DMA_OFFSET 0x0000 #define SIS_PLAY_DMA_SIZE 0x10 #define SIS_PLAY_DMA_ADDR(addr, num) \ @@ -228,7 +228,7 @@ #define SIS_PLAY_DMA_SSO_MASK 0xffff0000 #define SIS_PLAY_DMA_ESO_MASK 0x0000ffff -/* Capture DMA parameters (paramter RAM) */ +/* Capture DMA parameters (parameter RAM) */ #define SIS_CAPTURE_DMA_OFFSET 0x0800 #define SIS_CAPTURE_DMA_SIZE 0x10 #define SIS_CAPTURE_DMA_ADDR(addr, num) \ -- cgit v1.2.1 From 19af5cdb7c79ff5ec96a99893ffb7f894f4a3dc1 Mon Sep 17 00:00:00 2001 From: Martin Olsson Date: Thu, 23 Apr 2009 11:37:37 +0200 Subject: trivial: fix typo milisecond/millisecond for documentation and source comments. Signed-off-by: Martin Olsson Signed-off-by: Jiri Kosina --- sound/pci/vx222/vx222_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index c0efe4491116..6416d3f0c7be 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -367,7 +367,7 @@ static int vx2_load_xilinx_binary(struct vx_core *chip, const struct firmware *x unsigned int port; const unsigned char *image; - /* XILINX reset (wait at least 1 milisecond between reset on and off). */ + /* XILINX reset (wait at least 1 millisecond between reset on and off). */ vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK); vx_inl(chip, CNTRL); msleep(10); -- cgit v1.2.1