summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/aoa/codecs/tas.c2
-rw-r--r--sound/core/control.c64
-rw-r--r--sound/core/init.c2
-rw-r--r--sound/core/pcm_lib.c14
-rw-r--r--sound/firewire/Kconfig11
-rw-r--r--sound/firewire/Makefile2
-rw-r--r--sound/firewire/isight.c755
-rw-r--r--sound/firewire/iso-resources.c5
-rw-r--r--sound/firewire/packets-buffer.c2
-rw-r--r--sound/i2c/other/Makefile2
-rw-r--r--sound/i2c/other/tea575x-tuner.c153
-rw-r--r--sound/oss/Kconfig4
-rw-r--r--sound/oss/Makefile1
-rw-r--r--sound/oss/ac97_codec.c1203
-rw-r--r--sound/oss/au1550_ac97.c2147
-rw-r--r--sound/pci/Kconfig27
-rw-r--r--sound/pci/Makefile1
-rw-r--r--sound/pci/asihpi/asihpi.c328
-rw-r--r--sound/pci/asihpi/hpi6000.c39
-rw-r--r--sound/pci/asihpi/hpi6205.c95
-rw-r--r--sound/pci/asihpi/hpi_internal.h19
-rw-r--r--sound/pci/asihpi/hpicmn.c10
-rw-r--r--sound/pci/asihpi/hpicmn.h2
-rw-r--r--sound/pci/asihpi/hpifunc.c27
-rw-r--r--sound/pci/asihpi/hpimsgx.c31
-rw-r--r--sound/pci/asihpi/hpioctl.c63
-rw-r--r--sound/pci/au88x0/au8810.h2
-rw-r--r--sound/pci/au88x0/au8820.h2
-rw-r--r--sound/pci/au88x0/au8830.h2
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c20
-rw-r--r--sound/pci/emu10k1/emufx.c5
-rw-r--r--sound/pci/emu10k1/emumixer.c10
-rw-r--r--sound/pci/es1968.c78
-rw-r--r--sound/pci/fm801.c371
-rw-r--r--sound/pci/hda/hda_codec.c4
-rw-r--r--sound/pci/hda/patch_realtek.c55
-rw-r--r--sound/pci/hda/patch_via.c10
-rw-r--r--sound/pci/intel8x0m.c4
-rw-r--r--sound/pci/lola/Makefile4
-rw-r--r--sound/pci/lola/lola.c791
-rw-r--r--sound/pci/lola/lola.h527
-rw-r--r--sound/pci/lola/lola_clock.c323
-rw-r--r--sound/pci/lola/lola_mixer.c839
-rw-r--r--sound/pci/lola/lola_pcm.c706
-rw-r--r--sound/pci/lola/lola_proc.c222
-rw-r--r--sound/ppc/tumbler.c2
-rw-r--r--sound/usb/6fire/control.c105
-rw-r--r--sound/usb/6fire/control.h17
-rw-r--r--sound/usb/6fire/firmware.c73
-rw-r--r--sound/usb/6fire/pcm.c97
-rw-r--r--sound/usb/Kconfig10
-rw-r--r--sound/usb/clock.c11
-rw-r--r--sound/usb/debug.h2
-rw-r--r--sound/usb/format.c5
-rw-r--r--sound/usb/mixer.c10
-rw-r--r--sound/usb/mixer_quirks.c12
-rw-r--r--sound/usb/quirks-table.h47
-rw-r--r--sound/usb/quirks.c2
58 files changed, 5175 insertions, 4202 deletions
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
index 58804c7acfcf..fd2188c3df2b 100644
--- a/sound/aoa/codecs/tas.c
+++ b/sound/aoa/codecs/tas.c
@@ -170,7 +170,7 @@ static void tas_set_volume(struct tas *tas)
/* analysing the volume and mixer tables shows
* that they are similar enough when we shift
* the mixer table down by 4 bits. The error
- * is minuscule, in just one item the error
+ * is miniscule, in just one item the error
* is 1, at a value of 0x07f17b (mixer table
* value is 0x07f17a) */
tmp = tas_gaintable[left];
diff --git a/sound/core/control.c b/sound/core/control.c
index a08ad57c49b6..5d98194bcad5 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -366,6 +366,70 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
EXPORT_SYMBOL(snd_ctl_add);
/**
+ * snd_ctl_replace - replace the control instance of the card
+ * @card: the card instance
+ * @kcontrol: the control instance to replace
+ * @add_on_replace: add the control if not already added
+ *
+ * Replaces the given control. If the given control does not exist
+ * and the add_on_replace flag is set, the control is added. If the
+ * control exists, it is destroyed first.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ *
+ * It frees automatically the control which cannot be added or replaced.
+ */
+int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
+ bool add_on_replace)
+{
+ struct snd_ctl_elem_id id;
+ unsigned int idx;
+ struct snd_kcontrol *old;
+ int ret;
+
+ if (!kcontrol)
+ return -EINVAL;
+ if (snd_BUG_ON(!card || !kcontrol->info)) {
+ ret = -EINVAL;
+ goto error;
+ }
+ id = kcontrol->id;
+ down_write(&card->controls_rwsem);
+ old = snd_ctl_find_id(card, &id);
+ if (!old) {
+ if (add_on_replace)
+ goto add;
+ up_write(&card->controls_rwsem);
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = snd_ctl_remove(card, old);
+ if (ret < 0) {
+ up_write(&card->controls_rwsem);
+ goto error;
+ }
+add:
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
+ up_write(&card->controls_rwsem);
+ ret = -ENOMEM;
+ goto error;
+ }
+ list_add_tail(&kcontrol->list, &card->controls);
+ card->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->last_numid + 1;
+ card->last_numid += kcontrol->count;
+ up_write(&card->controls_rwsem);
+ for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+ return 0;
+
+error:
+ snd_ctl_free_one(kcontrol);
+ return ret;
+}
+EXPORT_SYMBOL(snd_ctl_replace);
+
+/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
* @kcontrol: the control instance to remove
diff --git a/sound/core/init.c b/sound/core/init.c
index a0080aa45ae9..30ecad41403c 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -514,7 +514,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
id = card->id;
if (*id == '\0')
- strcpy(id, "default");
+ strcpy(id, "Default");
while (1) {
if (loops-- == 0) {
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 64449cb8f873..abfeff1611ce 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -189,6 +189,7 @@ static void xrun(struct snd_pcm_substream *substream)
#define XRUN_LOG_CNT 10
struct hwptr_log_entry {
+ unsigned int in_interrupt;
unsigned long jiffies;
snd_pcm_uframes_t pos;
snd_pcm_uframes_t period_size;
@@ -204,7 +205,7 @@ struct snd_pcm_hwptr_log {
};
static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos)
+ snd_pcm_uframes_t pos, int in_interrupt)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
@@ -220,6 +221,7 @@ static void xrun_log(struct snd_pcm_substream *substream,
return;
}
entry = &log->entries[log->idx];
+ entry->in_interrupt = in_interrupt;
entry->jiffies = jiffies;
entry->pos = pos;
entry->period_size = runtime->period_size;
@@ -246,9 +248,11 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
entry = &log->entries[idx];
if (entry->period_size == 0)
break;
- snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
+ snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
"hwptr=%ld/%ld\n",
- name, entry->jiffies, (unsigned long)entry->pos,
+ name, entry->in_interrupt ? "[Q] " : "",
+ entry->jiffies,
+ (unsigned long)entry->pos,
(unsigned long)entry->period_size,
(unsigned long)entry->buffer_size,
(unsigned long)entry->old_hw_ptr,
@@ -262,7 +266,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos) do { } while (0)
+#define xrun_log(substream, pos, in_interrupt) do { } while (0)
#define xrun_log_show(substream) do { } while (0)
#endif
@@ -326,7 +330,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
pos -= pos % runtime->min_align;
if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos);
+ xrun_log(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index e486f48660fb..26071489970b 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -22,4 +22,15 @@ config SND_FIREWIRE_SPEAKERS
To compile this driver as a module, choose M here: the module
will be called snd-firewire-speakers.
+config SND_ISIGHT
+ tristate "Apple iSight microphone"
+ select SND_PCM
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for the front and rear microphones
+ of the Apple iSight web camera.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-isight.
+
endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index e5b1634d9ad4..d71ed8935f76 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,6 +1,8 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp.o
snd-firewire-speakers-objs := speakers.o
+snd-isight-objs := isight.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
+obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
new file mode 100644
index 000000000000..86ee16ca365e
--- /dev/null
+++ b/sound/firewire/isight.c
@@ -0,0 +1,755 @@
+/*
+ * Apple iSight audio driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "packets-buffer.h"
+
+#define OUI_APPLE 0x000a27
+#define MODEL_APPLE_ISIGHT 0x000008
+#define SW_ISIGHT_AUDIO 0x000010
+
+#define REG_AUDIO_ENABLE 0x000
+#define AUDIO_ENABLE 0x80000000
+#define REG_DEF_AUDIO_GAIN 0x204
+#define REG_GAIN_RAW_START 0x210
+#define REG_GAIN_RAW_END 0x214
+#define REG_GAIN_DB_START 0x218
+#define REG_GAIN_DB_END 0x21c
+#define REG_SAMPLE_RATE_INQUIRY 0x280
+#define REG_ISO_TX_CONFIG 0x300
+#define SPEED_SHIFT 16
+#define REG_SAMPLE_RATE 0x400
+#define RATE_48000 0x80000000
+#define REG_GAIN 0x500
+#define REG_MUTE 0x504
+
+#define MAX_FRAMES_PER_PACKET 475
+
+#define QUEUE_LENGTH 20
+
+struct isight {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct fw_device *device;
+ u64 audio_base;
+ struct fw_address_handler iris_handler;
+ struct snd_pcm_substream *pcm;
+ struct mutex mutex;
+ struct iso_packets_buffer buffer;
+ struct fw_iso_resources resources;
+ struct fw_iso_context *context;
+ bool pcm_active;
+ bool pcm_running;
+ bool first_packet;
+ int packet_index;
+ u32 total_samples;
+ unsigned int buffer_pointer;
+ unsigned int period_counter;
+ s32 gain_min, gain_max;
+ unsigned int gain_tlv[4];
+};
+
+struct audio_payload {
+ __be32 sample_count;
+ __be32 signature;
+ __be32 sample_total;
+ __be32 reserved;
+ __be16 samples[2 * MAX_FRAMES_PER_PACKET];
+};
+
+MODULE_DESCRIPTION("iSight audio driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static struct fw_iso_packet audio_packet = {
+ .payload_length = sizeof(struct audio_payload),
+ .interrupt = 1,
+ .header_length = 4,
+};
+
+static void isight_update_pointers(struct isight *isight, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime = isight->pcm->runtime;
+ unsigned int ptr;
+
+ smp_wmb(); /* update buffer data before buffer pointer */
+
+ ptr = isight->buffer_pointer;
+ ptr += count;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ ACCESS_ONCE(isight->buffer_pointer) = ptr;
+
+ isight->period_counter += count;
+ if (isight->period_counter >= runtime->period_size) {
+ isight->period_counter -= runtime->period_size;
+ snd_pcm_period_elapsed(isight->pcm);
+ }
+}
+
+static void isight_samples(struct isight *isight,
+ const __be16 *samples, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int count1;
+
+ if (!ACCESS_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ if (isight->buffer_pointer + count <= runtime->buffer_size) {
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count1 * 4);
+ samples += count1 * 2;
+ memcpy(runtime->dma_area, samples, (count - count1) * 4);
+ }
+
+ isight_update_pointers(isight, count);
+}
+
+static void isight_pcm_abort(struct isight *isight)
+{
+ unsigned long flags;
+
+ if (ACCESS_ONCE(isight->pcm_active)) {
+ snd_pcm_stream_lock_irqsave(isight->pcm, flags);
+ if (snd_pcm_running(isight->pcm))
+ snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(isight->pcm, flags);
+ }
+}
+
+static void isight_dropped_samples(struct isight *isight, unsigned int total)
+{
+ struct snd_pcm_runtime *runtime;
+ u32 dropped;
+ unsigned int count1;
+
+ if (!ACCESS_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ dropped = total - isight->total_samples;
+ if (dropped < runtime->buffer_size) {
+ if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, dropped * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, count1 * 4);
+ memset(runtime->dma_area, 0, (dropped - count1) * 4);
+ }
+ isight_update_pointers(isight, dropped);
+ } else {
+ isight_pcm_abort(isight);
+ }
+}
+
+static void isight_packet(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct isight *isight = data;
+ const struct audio_payload *payload;
+ unsigned int index, length, count, total;
+ int err;
+
+ if (isight->packet_index < 0)
+ return;
+ index = isight->packet_index;
+ payload = isight->buffer.packets[index].buffer;
+ length = be32_to_cpup(header) >> 16;
+
+ if (likely(length >= 16 &&
+ payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
+ count = be32_to_cpu(payload->sample_count);
+ if (likely(count <= (length - 16) / 4)) {
+ total = be32_to_cpu(payload->sample_total);
+ if (unlikely(total != isight->total_samples)) {
+ if (!isight->first_packet)
+ isight_dropped_samples(isight, total);
+ isight->first_packet = false;
+ isight->total_samples = total;
+ }
+
+ isight_samples(isight, payload->samples, count);
+ isight->total_samples += count;
+ }
+ }
+
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[index].offset);
+ if (err < 0) {
+ dev_err(&isight->unit->device, "queueing error: %d\n", err);
+ isight_pcm_abort(isight);
+ isight->packet_index = -1;
+ return;
+ }
+
+ if (++index >= QUEUE_LENGTH)
+ index = 0;
+ isight->packet_index = index;
+}
+
+static int isight_connect(struct isight *isight)
+{
+ int ch, err, rcode, errors = 0;
+ __be32 value;
+
+retry_after_bus_reset:
+ ch = fw_iso_resources_allocate(&isight->resources,
+ sizeof(struct audio_payload),
+ isight->device->max_speed);
+ if (ch < 0) {
+ err = ch;
+ goto error;
+ }
+
+ value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
+ for (;;) {
+ rcode = fw_run_transaction(
+ isight->device->card,
+ TCODE_WRITE_QUADLET_REQUEST,
+ isight->device->node_id,
+ isight->resources.generation,
+ isight->device->max_speed,
+ isight->audio_base + REG_ISO_TX_CONFIG,
+ &value, 4);
+ if (rcode == RCODE_COMPLETE) {
+ return 0;
+ } else if (rcode == RCODE_GENERATION) {
+ fw_iso_resources_free(&isight->resources);
+ goto retry_after_bus_reset;
+ } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+ err = -EIO;
+ goto err_resources;
+ }
+ msleep(5);
+ }
+
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+error:
+ return err;
+}
+
+static int isight_open(struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 4 * 1024 * 1024,
+ .period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct isight *isight = substream->private_data;
+
+ substream->runtime->hw = hardware;
+
+ return iso_packets_buffer_init(&isight->buffer, isight->unit,
+ QUEUE_LENGTH,
+ sizeof(struct audio_payload),
+ DMA_FROM_DEVICE);
+}
+
+static int isight_close(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ iso_packets_buffer_destroy(&isight->buffer, isight->unit);
+
+ return 0;
+}
+
+static int isight_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ ACCESS_ONCE(isight->pcm_active) = true;
+
+ return 0;
+}
+
+static int reg_read(struct isight *isight, int offset, __be32 *value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
+ isight->audio_base + offset, value, 4);
+}
+
+static int reg_write(struct isight *isight, int offset, __be32 value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + offset, &value, 4);
+}
+
+static void isight_stop_streaming(struct isight *isight)
+{
+ if (!isight->context)
+ return;
+
+ fw_iso_context_stop(isight->context);
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+}
+
+static int isight_hw_free(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ ACCESS_ONCE(isight->pcm_active) = false;
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int isight_start_streaming(struct isight *isight)
+{
+ unsigned int i;
+ int err;
+
+ if (isight->context) {
+ if (isight->packet_index < 0)
+ isight_stop_streaming(isight);
+ else
+ return 0;
+ }
+
+ err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
+ if (err < 0)
+ goto error;
+
+ err = isight_connect(isight);
+ if (err < 0)
+ goto error;
+
+ err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
+ if (err < 0)
+ goto err_resources;
+
+ isight->context = fw_iso_context_create(isight->device->card,
+ FW_ISO_CONTEXT_RECEIVE,
+ isight->resources.channel,
+ isight->device->max_speed,
+ 4, isight_packet, isight);
+ if (IS_ERR(isight->context)) {
+ err = PTR_ERR(isight->context);
+ isight->context = NULL;
+ goto err_resources;
+ }
+
+ for (i = 0; i < QUEUE_LENGTH; ++i) {
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[i].offset);
+ if (err < 0)
+ goto err_context;
+ }
+
+ isight->first_packet = true;
+ isight->packet_index = 0;
+
+ err = fw_iso_context_start(isight->context, -1, 0,
+ FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
+ if (err < 0)
+ goto err_context;
+
+ return 0;
+
+err_context:
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+error:
+ return err;
+}
+
+static int isight_prepare(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ isight->buffer_pointer = 0;
+ isight->period_counter = 0;
+
+ mutex_lock(&isight->mutex);
+ err = isight_start_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return err;
+}
+
+static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct isight *isight = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ACCESS_ONCE(isight->pcm_running) = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ACCESS_ONCE(isight->pcm_running) = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ return ACCESS_ONCE(isight->buffer_pointer);
+}
+
+static int isight_create_pcm(struct isight *isight)
+{
+ static struct snd_pcm_ops ops = {
+ .open = isight_open,
+ .close = isight_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = isight_hw_params,
+ .hw_free = isight_hw_free,
+ .prepare = isight_prepare,
+ .trigger = isight_trigger,
+ .pointer = isight_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = isight;
+ strcpy(pcm->name, "iSight");
+ isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ isight->pcm->ops = &ops;
+
+ return 0;
+}
+
+static int isight_gain_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct isight *isight = ctl->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = isight->gain_min;
+ info->value.integer.max = isight->gain_max;
+
+ return 0;
+}
+
+static int isight_gain_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 gain;
+ int err;
+
+ err = reg_read(isight, REG_GAIN, &gain);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = (s32)be32_to_cpu(gain);
+
+ return 0;
+}
+
+static int isight_gain_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ if (value->value.integer.value[0] < isight->gain_min ||
+ value->value.integer.value[0] > isight->gain_max)
+ return -EINVAL;
+
+ return reg_write(isight, REG_GAIN,
+ cpu_to_be32(value->value.integer.value[0]));
+}
+
+static int isight_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 mute;
+ int err;
+
+ err = reg_read(isight, REG_MUTE, &mute);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = !mute;
+
+ return 0;
+}
+
+static int isight_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ return reg_write(isight, REG_MUTE,
+ (__force __be32)!value->value.integer.value[0]);
+}
+
+static int isight_create_mixer(struct isight *isight)
+{
+ static const struct snd_kcontrol_new gain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = isight_gain_info,
+ .get = isight_gain_get,
+ .put = isight_gain_put,
+ };
+ static const struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = isight_mute_get,
+ .put = isight_mute_put,
+ };
+ __be32 value;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ err = reg_read(isight, REG_GAIN_RAW_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_min = be32_to_cpu(value);
+
+ err = reg_read(isight, REG_GAIN_RAW_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_max = be32_to_cpu(value);
+
+ isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
+ isight->gain_tlv[1] = 2 * sizeof(unsigned int);
+
+ err = reg_read(isight, REG_GAIN_DB_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100;
+
+ err = reg_read(isight, REG_GAIN_DB_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100;
+
+ ctl = snd_ctl_new1(&gain_control, isight);
+ if (ctl)
+ ctl->tlv.p = isight->gain_tlv;
+ err = snd_ctl_add(isight->card, ctl);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void isight_card_free(struct snd_card *card)
+{
+ struct isight *isight = card->private_data;
+
+ fw_iso_resources_destroy(&isight->resources);
+ fw_unit_put(isight->unit);
+ fw_device_put(isight->device);
+ mutex_destroy(&isight->mutex);
+}
+
+static u64 get_unit_base(struct fw_unit *unit)
+{
+ struct fw_csr_iterator i;
+ int key, value;
+
+ fw_csr_iterator_init(&i, unit->directory);
+ while (fw_csr_iterator_next(&i, &key, &value))
+ if (key == CSR_OFFSET)
+ return CSR_REGISTER_BASE + value * 4;
+ return 0;
+}
+
+static int isight_probe(struct device *unit_dev)
+{
+ struct fw_unit *unit = fw_unit(unit_dev);
+ struct fw_device *fw_dev = fw_parent_device(unit);
+ struct snd_card *card;
+ struct isight *isight;
+ int err;
+
+ err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card);
+ if (err < 0)
+ return err;
+ snd_card_set_dev(card, unit_dev);
+
+ isight = card->private_data;
+ isight->card = card;
+ mutex_init(&isight->mutex);
+ isight->unit = fw_unit_get(unit);
+ isight->device = fw_device_get(fw_dev);
+ isight->audio_base = get_unit_base(unit);
+ if (!isight->audio_base) {
+ dev_err(&unit->device, "audio unit base not found\n");
+ err = -ENXIO;
+ goto err_unit;
+ }
+ fw_iso_resources_init(&isight->resources, unit);
+
+ card->private_free = isight_card_free;
+
+ strcpy(card->driver, "iSight");
+ strcpy(card->shortname, "Apple iSight");
+ snprintf(card->longname, sizeof(card->longname),
+ "Apple iSight (GUID %08x%08x) at %s, S%d",
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&unit->device), 100 << fw_dev->max_speed);
+ strcpy(card->mixername, "iSight");
+
+ err = isight_create_pcm(isight);
+ if (err < 0)
+ goto error;
+
+ err = isight_create_mixer(isight);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(unit_dev, isight);
+
+ return 0;
+
+err_unit:
+ fw_unit_put(isight->unit);
+ fw_device_put(isight->device);
+ mutex_destroy(&isight->mutex);
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static int isight_remove(struct device *dev)
+{
+ struct isight *isight = dev_get_drvdata(dev);
+
+ isight_pcm_abort(isight);
+
+ snd_card_disconnect(isight->card);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ snd_card_free_when_closed(isight->card);
+
+ return 0;
+}
+
+static void isight_bus_reset(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ if (fw_iso_resources_update(&isight->resources) < 0) {
+ isight_pcm_abort(isight);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+ }
+}
+
+static const struct ieee1394_device_id isight_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .specifier_id = OUI_APPLE,
+ .version = SW_ISIGHT_AUDIO,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
+
+static struct fw_driver isight_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ .probe = isight_probe,
+ .remove = isight_remove,
+ },
+ .update = isight_bus_reset,
+ .id_table = isight_id_table,
+};
+
+static int __init alsa_isight_init(void)
+{
+ return driver_register(&isight_driver.driver);
+}
+
+static void __exit alsa_isight_exit(void)
+{
+ driver_unregister(&isight_driver.driver);
+}
+
+module_init(alsa_isight_init);
+module_exit(alsa_isight_exit);
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
index 775dbd5f3445..9d4a6714f9ec 100644
--- a/sound/firewire/iso-resources.c
+++ b/sound/firewire/iso-resources.c
@@ -36,6 +36,7 @@ int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
return 0;
}
+EXPORT_SYMBOL(fw_iso_resources_init);
/**
* fw_iso_resources_destroy - destroy a resource manager
@@ -48,6 +49,7 @@ void fw_iso_resources_destroy(struct fw_iso_resources *r)
mutex_destroy(&r->mutex);
fw_unit_put(r->unit);
}
+EXPORT_SYMBOL(fw_iso_resources_destroy);
static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
{
@@ -152,6 +154,7 @@ retry_after_bus_reset:
return channel;
}
+EXPORT_SYMBOL(fw_iso_resources_allocate);
/**
* fw_iso_resources_update - update resource allocations after a bus reset
@@ -203,6 +206,7 @@ int fw_iso_resources_update(struct fw_iso_resources *r)
return channel;
}
+EXPORT_SYMBOL(fw_iso_resources_update);
/**
* fw_iso_resources_free - frees allocated resources
@@ -230,3 +234,4 @@ void fw_iso_resources_free(struct fw_iso_resources *r)
mutex_unlock(&r->mutex);
}
+EXPORT_SYMBOL(fw_iso_resources_free);
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c
index 1e20e60ba6a6..3c61ca2e6152 100644
--- a/sound/firewire/packets-buffer.c
+++ b/sound/firewire/packets-buffer.c
@@ -60,6 +60,7 @@ err_packets:
error:
return err;
}
+EXPORT_SYMBOL(iso_packets_buffer_init);
/**
* iso_packets_buffer_destroy - frees packet buffer resources
@@ -72,3 +73,4 @@ void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
kfree(b->packets);
}
+EXPORT_SYMBOL(iso_packets_buffer_destroy);
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 2dad40f3f622..c95d8f1aae87 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -14,4 +14,4 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
-obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
+obj-$(CONFIG_SND_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index ee538f1ae846..4831800239d3 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -37,8 +37,8 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0);
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
+#define FREQ_LO (50UL * 16000)
+#define FREQ_HI (150UL * 16000)
/*
* definitions
@@ -77,27 +77,95 @@ static struct v4l2_queryctrl radio_qctrl[] = {
* lowlevel part
*/
+static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
+{
+ u16 l;
+ u8 data;
+
+ tea->ops->set_direction(tea, 1);
+ udelay(16);
+
+ for (l = 25; l > 0; l--) {
+ data = (val >> 24) & TEA575X_DATA;
+ val <<= 1; /* shift data */
+ tea->ops->set_pins(tea, data | TEA575X_WREN);
+ udelay(2);
+ tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
+ udelay(2);
+ tea->ops->set_pins(tea, data | TEA575X_WREN);
+ udelay(2);
+ }
+
+ if (!tea->mute)
+ tea->ops->set_pins(tea, 0);
+}
+
+static unsigned int snd_tea575x_read(struct snd_tea575x *tea)
+{
+ u16 l, rdata;
+ u32 data = 0;
+
+ tea->ops->set_direction(tea, 0);
+ tea->ops->set_pins(tea, 0);
+ udelay(16);
+
+ for (l = 24; l--;) {
+ tea->ops->set_pins(tea, TEA575X_CLK);
+ udelay(2);
+ if (!l)
+ tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
+ tea->ops->set_pins(tea, 0);
+ udelay(2);
+ data <<= 1; /* shift data */
+ rdata = tea->ops->get_pins(tea);
+ if (!l)
+ tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1;
+ if (rdata & TEA575X_DATA)
+ data++;
+ udelay(2);
+ }
+
+ if (tea->mute)
+ tea->ops->set_pins(tea, TEA575X_WREN);
+
+ return data;
+}
+
+static void snd_tea575x_get_freq(struct snd_tea575x *tea)
+{
+ unsigned long freq;
+
+ freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
+ /* freq *= 12.5 */
+ freq *= 125;
+ freq /= 10;
+ /* crystal fixup */
+ if (tea->tea5759)
+ freq += TEA575X_FMIF;
+ else
+ freq -= TEA575X_FMIF;
+
+ tea->freq = freq * 16; /* from kHz */
+}
+
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
{
unsigned long freq;
- freq = tea->freq / 16; /* to kHz */
- if (freq > 108000)
- freq = 108000;
- if (freq < 87000)
- freq = 87000;
+ freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
+ freq /= 16; /* to kHz */
/* crystal fixup */
if (tea->tea5759)
- freq -= tea->freq_fixup;
+ freq -= TEA575X_FMIF;
else
- freq += tea->freq_fixup;
+ freq += TEA575X_FMIF;
/* freq /= 12.5 */
freq *= 10;
freq /= 125;
tea->val &= ~TEA575X_BIT_FREQ_MASK;
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
- tea->ops->write(tea, tea->val);
+ snd_tea575x_write(tea, tea->val);
}
/*
@@ -109,29 +177,34 @@ static int vidioc_querycap(struct file *file, void *priv,
{
struct snd_tea575x *tea = video_drvdata(file);
- strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
- strlcpy(v->card, "Maestro Radio", sizeof(v->card));
- sprintf(v->bus_info, "PCI");
+ strlcpy(v->card, tea->card, sizeof(v->card));
+ strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+ strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER;
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct snd_tea575x *tea = video_drvdata(file);
+
if (v->index > 0)
return -EINVAL;
+ snd_tea575x_read(tea);
+
strcpy(v->name, "FM");
v->type = V4L2_TUNER_RADIO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
v->rangelow = FREQ_LO;
v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+ v->signal = tea->tuned ? 0xffff : 0;
+
return 0;
}
@@ -148,7 +221,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct snd_tea575x *tea = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
+ snd_tea575x_get_freq(tea);
f->frequency = tea->freq;
return 0;
}
@@ -158,6 +234,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct snd_tea575x *tea = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
return -EINVAL;
@@ -209,10 +288,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- ctrl->value = tea->mute;
- return 0;
- }
+ ctrl->value = tea->mute;
+ return 0;
}
return -EINVAL;
}
@@ -224,11 +301,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- tea->ops->mute(tea, ctrl->value);
+ if (tea->mute != ctrl->value) {
tea->mute = ctrl->value;
- return 0;
+ snd_tea575x_set_freq(tea);
}
+ return 0;
}
return -EINVAL;
}
@@ -293,18 +370,16 @@ static struct video_device tea575x_radio = {
/*
* initialize all the tea575x chips
*/
-void snd_tea575x_init(struct snd_tea575x *tea)
+int snd_tea575x_init(struct snd_tea575x *tea)
{
int retval;
- unsigned int val;
struct video_device *tea575x_radio_inst;
- val = tea->ops->read(tea);
- if (val == 0x1ffffff || val == 0) {
- snd_printk(KERN_ERR
- "tea575x-tuner: Cannot find TEA575x chip\n");
- return;
- }
+ tea->mute = 1;
+
+ snd_tea575x_write(tea, 0x55AA);
+ if (snd_tea575x_read(tea) != 0x55AA)
+ return -ENODEV;
tea->in_use = 0;
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
@@ -313,7 +388,7 @@ void snd_tea575x_init(struct snd_tea575x *tea)
tea575x_radio_inst = video_device_alloc();
if (tea575x_radio_inst == NULL) {
printk(KERN_ERR "tea575x-tuner: not enough memory\n");
- return;
+ return -ENOMEM;
}
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
@@ -328,17 +403,13 @@ void snd_tea575x_init(struct snd_tea575x *tea)
if (retval) {
printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
kfree(tea575x_radio_inst);
- return;
+ return retval;
}
snd_tea575x_set_freq(tea);
-
- /* mute on init */
- if (tea->ops->mute) {
- tea->ops->mute(tea, 1);
- tea->mute = 1;
- }
tea->vd = tea575x_radio_inst;
+
+ return 0;
}
void snd_tea575x_exit(struct snd_tea575x *tea)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 76c090218073..6c93e051f9ae 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -22,10 +22,6 @@ config SOUND_VWSND
<file:Documentation/sound/oss/vwsnd> for more info on this driver's
capabilities.
-config SOUND_AU1550_AC97
- tristate "Au1550/Au1200 AC97 Sound"
- depends on SOC_AU1550 || SOC_AU1200
-
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
depends on (m || !STANDALONE) && ISA
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index 90ffb99c6b17..77f21b68bf0f 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -25,7 +25,6 @@ obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
-obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_DMASOUND) += dmasound/
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
deleted file mode 100644
index 0cd23d94888f..000000000000
--- a/sound/oss/ac97_codec.c
+++ /dev/null
@@ -1,1203 +0,0 @@
-/*
- * ac97_codec.c: Generic AC97 mixer/modem module
- *
- * Derived from ac97 mixer in maestro and trident driver.
- *
- * Copyright 2000 Silicon Integrated System Corporation
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- **************************************************************************
- *
- * The Intel Audio Codec '97 specification is available at:
- * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
- *
- **************************************************************************
- *
- * History
- * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
- * Removed non existent WM9700
- * Added support for WM9705, WM9708, WM9709, WM9710, WM9711
- * WM9712 and WM9717
- * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
- * corrections to support WM9707 in ViewPad 1000
- * v0.4 Mar 15 2000 Ollie Lho
- * dual codecs support verified with 4 channels output
- * v0.3 Feb 22 2000 Ollie Lho
- * bug fix for record mask setting
- * v0.2 Feb 10 2000 Ollie Lho
- * add ac97_read_proc for /proc/driver/{vendor}/ac97
- * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
- * Isolated from trident.c to support multiple ac97 codec
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ac97_codec.h>
-#include <asm/uaccess.h>
-#include <linux/mutex.h>
-
-#define CODEC_ID_BUFSZ 14
-
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right);
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-
-static int ac97_init_mixer(struct ac97_codec *codec);
-
-static int wolfson_init03(struct ac97_codec * codec);
-static int wolfson_init04(struct ac97_codec * codec);
-static int wolfson_init05(struct ac97_codec * codec);
-static int wolfson_init11(struct ac97_codec * codec);
-static int wolfson_init13(struct ac97_codec * codec);
-static int tritech_init(struct ac97_codec * codec);
-static int tritech_maestro_init(struct ac97_codec * codec);
-static int sigmatel_9708_init(struct ac97_codec *codec);
-static int sigmatel_9721_init(struct ac97_codec *codec);
-static int sigmatel_9744_init(struct ac97_codec *codec);
-static int ad1886_init(struct ac97_codec *codec);
-static int eapd_control(struct ac97_codec *codec, int);
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int cmedia_init(struct ac97_codec * codec);
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-
-
-/*
- * AC97 operations.
- *
- * If you are adding a codec then you should be able to use
- * eapd_ops - any codec that supports EAPD amp control (most)
- * null_ops - any ancient codec that supports nothing
- *
- * The three functions are
- * init - used for non AC97 standard initialisation
- * amplifier - used to do amplifier control (1=on 0=off)
- * digital - switch to digital modes (0 = analog)
- *
- * Not all codecs support all features, not all drivers use all the
- * operations yet
- */
-
-static struct ac97_ops null_ops = { NULL, NULL, NULL };
-static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
-static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
-static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
-static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
-static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
-static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
-static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
-static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
-static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
-static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
-static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
-static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
-static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
-static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
-static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
-
-/* sorted by vendor/device id */
-static const struct {
- u32 id;
- char *name;
- struct ac97_ops *ops;
- int flags;
-} ac97_codec_ids[] = {
- {0x41445303, "Analog Devices AD1819", &null_ops},
- {0x41445340, "Analog Devices AD1881", &null_ops},
- {0x41445348, "Analog Devices AD1881A", &null_ops},
- {0x41445360, "Analog Devices AD1885", &default_ops},
- {0x41445361, "Analog Devices AD1886", &ad1886_ops},
- {0x41445370, "Analog Devices AD1981", &null_ops},
- {0x41445372, "Analog Devices AD1981A", &null_ops},
- {0x41445374, "Analog Devices AD1981B", &null_ops},
- {0x41445460, "Analog Devices AD1885", &default_ops},
- {0x41445461, "Analog Devices AD1886", &ad1886_ops},
- {0x414B4D00, "Asahi Kasei AK4540", &null_ops},
- {0x414B4D01, "Asahi Kasei AK4542", &null_ops},
- {0x414B4D02, "Asahi Kasei AK4543", &null_ops},
- {0x414C4326, "ALC100P", &null_ops},
- {0x414C4710, "ALC200/200P", &null_ops},
- {0x414C4720, "ALC650", &default_digital_ops},
- {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME },
- {0x43525900, "Cirrus Logic CS4297", &default_ops},
- {0x43525903, "Cirrus Logic CS4297", &default_ops},
- {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
- {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
- {0x43525923, "Cirrus Logic CS4298", &null_ops},
- {0x4352592B, "Cirrus Logic CS4294", &null_ops},
- {0x4352592D, "Cirrus Logic CS4294", &null_ops},
- {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
- {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
- {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
- {0x43585430, "CXT48", &default_ops, AC97_DELUDED_MODEM },
- {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM },
- {0x44543031, "Diamond Technology DT0893", &default_ops},
- {0x45838308, "ESS Allegro ES1988", &null_ops},
- {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */
- {0x4e534331, "National Semiconductor LM4549", &null_ops},
- {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
- {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
- {0x545200FF, "TriTech TR?????", &tritech_m_ops},
- {0x54524102, "TriTech TR28022", &null_ops},
- {0x54524103, "TriTech TR28023", &null_ops},
- {0x54524106, "TriTech TR28026", &null_ops},
- {0x54524108, "TriTech TR28028", &tritech_ops},
- {0x54524123, "TriTech TR A5", &null_ops},
- {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03},
- {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04},
- {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05},
- {0x574D4C09, "Wolfson WM9709", &null_ops},
- {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11},
- {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF},
- {0x83847600, "SigmaTel STAC????", &null_ops},
- {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
- {0x83847605, "SigmaTel STAC9704", &null_ops},
- {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops},
- {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops},
- {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops},
- {0x83847652, "SigmaTel STAC9752/53", &default_ops},
- {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops},
- {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops},
- {0x83847684, "SigmaTel STAC9783/84?", &null_ops},
- {0x57454301, "Winbond 83971D", &null_ops},
-};
-
-/* this table has default mixer values for all OSS mixers. */
-static struct mixer_defaults {
- int mixer;
- unsigned int value;
-} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
- /* all values 0 -> 100 in bytes */
- {SOUND_MIXER_VOLUME, 0x4343},
- {SOUND_MIXER_BASS, 0x4343},
- {SOUND_MIXER_TREBLE, 0x4343},
- {SOUND_MIXER_PCM, 0x4343},
- {SOUND_MIXER_SPEAKER, 0x4343},
- {SOUND_MIXER_LINE, 0x4343},
- {SOUND_MIXER_MIC, 0x0000},
- {SOUND_MIXER_CD, 0x4343},
- {SOUND_MIXER_ALTPCM, 0x4343},
- {SOUND_MIXER_IGAIN, 0x4343},
- {SOUND_MIXER_LINE1, 0x4343},
- {SOUND_MIXER_PHONEIN, 0x4343},
- {SOUND_MIXER_PHONEOUT, 0x4343},
- {SOUND_MIXER_VIDEO, 0x4343},
- {-1,0}
-};
-
-/* table to scale scale from OSS mixer value to AC97 mixer register value */
-static struct ac97_mixer_hw {
- unsigned char offset;
- int scale;
-} ac97_hw[SOUND_MIXER_NRDEVICES]= {
- [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64},
- [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32},
- [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16},
- [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32},
- [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32},
- [SOUND_MIXER_CD] = {AC97_CD_VOL, 32},
- [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64},
- [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16},
- [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32},
- [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32},
- [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64},
- [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32},
-};
-
-/* the following tables allow us to go from OSS <-> ac97 quickly. */
-enum ac97_recsettings {
- AC97_REC_MIC=0,
- AC97_REC_CD,
- AC97_REC_VIDEO,
- AC97_REC_AUX,
- AC97_REC_LINE,
- AC97_REC_STEREO, /* combination of all enabled outputs.. */
- AC97_REC_MONO, /*.. or the mono equivalent */
- AC97_REC_PHONE
-};
-
-static const unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
- [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
- [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
-};
-
-/* indexed by bit position */
-static const unsigned int ac97_oss_rm[] = {
- [SOUND_MIXER_MIC] = AC97_REC_MIC,
- [SOUND_MIXER_CD] = AC97_REC_CD,
- [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
- [SOUND_MIXER_LINE1] = AC97_REC_AUX,
- [SOUND_MIXER_LINE] = AC97_REC_LINE,
- [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
- [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
-};
-
-static LIST_HEAD(codecs);
-static LIST_HEAD(codec_drivers);
-static DEFINE_MUTEX(codec_mutex);
-
-/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
- about that given mixer, and should be holding a spinlock for the card */
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel)
-{
- u16 val;
- int ret = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
- val = codec->codec_read(codec , mh->offset);
-
- if (val & AC97_MUTE) {
- ret = 0;
- } else if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* nice stereo mixers .. */
- int left,right;
-
- left = (val >> 8) & 0x7f;
- right = val & 0x7f;
-
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * 100) / mh->scale;
- left = (left * 100) / mh->scale;
- } else {
- /* these may have 5 or 6 bit resolution */
- if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = 100 - ((right * 100) / scale);
- left = 100 - ((left * 100) / scale);
- }
- ret = left | (right << 8);
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- ret = 100 - (((val & 0x1f) * 100) / scale);
- } else if (oss_channel == SOUND_MIXER_MIC) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (oss_channel == SOUND_MIXER_BASS) {
- ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- ret = 100 - (((val & 0xe) * 100) / mh->scale);
- }
-
-#ifdef DEBUG
- printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, val, ret);
-#endif
-
- return ret;
-}
-
-/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
- make sure all is well in arg land, call with spinlock held */
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right)
-{
- u16 val = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
-#ifdef DEBUG
- printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
- "left vol:%2d, right vol:%2d:",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, left, right);
-#endif
-
- if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* stereo mixers */
- if (left == 0 && right == 0) {
- val = AC97_MUTE;
- } else {
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * mh->scale) / 100;
- left = (left * mh->scale) / 100;
- if (right >= mh->scale)
- right = mh->scale-1;
- if (left >= mh->scale)
- left = mh->scale-1;
- } else {
- /* these may have 5 or 6 bit resolution */
- if (oss_channel == SOUND_MIXER_VOLUME ||
- oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = ((100 - right) * scale) / 100;
- left = ((100 - left) * scale) / 100;
- if (right >= scale)
- right = scale-1;
- if (left >= scale)
- left = scale-1;
- }
- val = (left << 8) | right;
- }
- } else if (oss_channel == SOUND_MIXER_BASS) {
- val = codec->codec_read(codec , mh->offset) & ~0x0f00;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= (left << 8) & 0x0e00;
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- val = codec->codec_read(codec , mh->offset) & ~0x000f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left & 0x000e;
- } else if(left == 0) {
- val = AC97_MUTE;
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left << 1;
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- left = ((100 - left) * scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_MIC) {
- val = codec->codec_read(codec , mh->offset) & ~0x801f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left;
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- }
-#ifdef DEBUG
- printk(" 0x%04x", val);
-#endif
-
- codec->codec_write(codec, mh->offset, val);
-
-#ifdef DEBUG
- val = codec->codec_read(codec, mh->offset);
- printk(" -> 0x%04x\n", val);
-#endif
-}
-
-/* a thin wrapper for write_mixer */
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val )
-{
- unsigned int left,right;
-
- /* cleanse input a little */
- right = ((val >> 8) & 0xff) ;
- left = (val & 0xff) ;
-
- if (right > 100) right = 100;
- if (left > 100) left = 100;
-
- codec->mixer_state[oss_mixer] = (right << 8) | left;
- codec->write_mixer(codec, oss_mixer, left, right);
-}
-
-/* read or write the recmask, the ac97 can really have left and right recording
- inputs independently set, but OSS doesn't seem to want us to express that to
- the user. the caller guarantees that we have a supported bit set, and they
- must be holding the card's spinlock */
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
-{
- unsigned int val;
-
- if (rw) {
- /* read it from the card */
- val = codec->codec_read(codec, AC97_RECORD_SELECT);
-#ifdef DEBUG
- printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
-#endif
- return (1 << ac97_rm2oss[val & 0x07]);
- }
-
- /* else, write the first set in the mask as the
- output */
- /* clear out current set value first (AC97 supports only 1 input!) */
- val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
- if (mask != val)
- mask &= ~val;
-
- val = ffs(mask);
- val = ac97_oss_rm[val-1];
- val |= val << 8; /* set both channels */
-
-#ifdef DEBUG
- printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
-#endif
-
- codec->codec_write(codec, AC97_RECORD_SELECT, val);
-
- return 0;
-};
-
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
-{
- int i, val = 0;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- info.modify_counter = codec->modcnt;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
-
- if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int __user *)arg);
-
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* give them the current record source */
- if (!codec->recmask_io) {
- val = 0;
- } else {
- val = codec->recmask_io(codec, 1, 0);
- }
- break;
-
- case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
- val = codec->supported_mixers;
- break;
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- val = codec->record_sources;
- break;
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- val = codec->stereo_mixers;
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default: /* read a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- /* do we ever want to touch the hardware? */
- /* val = codec->read_mixer(codec, i); */
- val = codec->mixer_state[i];
- break;
- }
- return put_user(val, (int __user *)arg);
- }
-
- if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
- codec->modcnt++;
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (!codec->recmask_io) return -EINVAL;
- if (!val) return 0;
- if (!(val &= codec->record_sources)) return -EINVAL;
-
- codec->recmask_io(codec, 0, val);
-
- return 0;
- default: /* write a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- ac97_set_mixer(codec, i, val);
-
- return 0;
- }
- }
- return -EINVAL;
-}
-
-/**
- * codec_id - Turn id1/id2 into a PnP string
- * @id1: Vendor ID1
- * @id2: Vendor ID2
- * @buf: CODEC_ID_BUFSZ byte buffer
- *
- * Fills buf with a zero terminated PnP ident string for the id1/id2
- * pair. For convenience the return is the passed in buffer pointer.
- */
-
-static char *codec_id(u16 id1, u16 id2, char *buf)
-{
- if(id1&0x8080) {
- snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
- } else {
- buf[0] = (id1 >> 8);
- buf[1] = (id1 & 0xFF);
- buf[2] = (id2 >> 8);
- snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
- }
- return buf;
-}
-
-/**
- * ac97_check_modem - Check if the Codec is a modem
- * @codec: codec to check
- *
- * Return true if the device is an AC97 1.0 or AC97 2.0 modem
- */
-
-static int ac97_check_modem(struct ac97_codec *codec)
-{
- /* Check for an AC97 1.0 soft modem (ID1) */
- if(codec->codec_read(codec, AC97_RESET) & 2)
- return 1;
- /* Check for an AC97 2.x soft modem */
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
- if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
- return 1;
- return 0;
-}
-
-
-/**
- * ac97_alloc_codec - Allocate an AC97 codec
- *
- * Returns a new AC97 codec structure. AC97 codecs may become
- * refcounted soon so this interface is needed. Returns with
- * one reference taken.
- */
-
-struct ac97_codec *ac97_alloc_codec(void)
-{
- struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL);
- if(!codec)
- return NULL;
-
- spin_lock_init(&codec->lock);
- INIT_LIST_HEAD(&codec->list);
- return codec;
-}
-
-EXPORT_SYMBOL(ac97_alloc_codec);
-
-/**
- * ac97_release_codec - Release an AC97 codec
- * @codec: codec to release
- *
- * Release an allocated AC97 codec. This will be refcounted in
- * time but for the moment is trivial. Calls the unregister
- * handler if the codec is now defunct.
- */
-
-void ac97_release_codec(struct ac97_codec *codec)
-{
- /* Remove from the list first, we don't want to be
- "rediscovered" */
- mutex_lock(&codec_mutex);
- list_del(&codec->list);
- mutex_unlock(&codec_mutex);
- /*
- * The driver needs to deal with internal
- * locking to avoid accidents here.
- */
- if(codec->driver)
- codec->driver->remove(codec, codec->driver);
- kfree(codec);
-}
-
-EXPORT_SYMBOL(ac97_release_codec);
-
-/**
- * ac97_probe_codec - Initialize and setup AC97-compatible codec
- * @codec: (in/out) Kernel info for a single AC97 codec
- *
- * Reset the AC97 codec, then initialize the mixer and
- * the rest of the @codec structure.
- *
- * The codec_read and codec_write fields of @codec are
- * required to be setup and working when this function
- * is called. All other fields are set by this function.
- *
- * codec_wait field of @codec can optionally be provided
- * when calling this function. If codec_wait is not %NULL,
- * this function will call codec_wait any time it is
- * necessary to wait for the audio chip to reach the
- * codec-ready state. If codec_wait is %NULL, then
- * the default behavior is to call schedule_timeout.
- * Currently codec_wait is used to wait for AC97 codec
- * reset to complete.
- *
- * Some codecs will power down when a register reset is
- * performed. We now check for such codecs.
- *
- * Returns 1 (true) on success, or 0 (false) on failure.
- */
-
-int ac97_probe_codec(struct ac97_codec *codec)
-{
- u16 id1, id2;
- u16 audio;
- int i;
- char cidbuf[CODEC_ID_BUFSZ];
- u16 f;
- struct list_head *l;
- struct ac97_driver *d;
-
- /* wait for codec-ready state */
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
-
- /* will the codec power down if register reset ? */
- id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
- id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
- codec->name = NULL;
- codec->codec_ops = &null_ops;
- for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
- if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
- codec->type = ac97_codec_ids[i].id;
- codec->name = ac97_codec_ids[i].name;
- codec->codec_ops = ac97_codec_ids[i].ops;
- codec->flags = ac97_codec_ids[i].flags;
- break;
- }
- }
-
- codec->model = (id1 << 16) | id2;
- if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
- /* reset codec and wait for the ready bit before we continue */
- codec->codec_write(codec, AC97_RESET, 0L);
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
- }
-
- /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
- * be read zero.
- *
- * FIXME: is the following comment outdated? -jgarzik
- * Probing of AC97 in this way is not reliable, it is not even SAFE !!
- */
- if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
- printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
- (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")
- : (codec->id&1 ? "Secondary": "Primary"));
- return 0;
- }
-
- /* probe for Modem Codec */
- codec->modem = ac97_check_modem(codec);
-
- /* enable SPDIF */
- f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if((codec->codec_ops == &null_ops) && (f & 4))
- codec->codec_ops = &default_digital_ops;
-
- /* A device which thinks its a modem but isn't */
- if(codec->flags & AC97_DELUDED_MODEM)
- codec->modem = 0;
-
- if (codec->name == NULL)
- codec->name = "Unknown";
- printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n",
- codec->modem ? "Modem" : (audio ? "Audio" : ""),
- codec_id(id1, id2, cidbuf), codec->name);
-
- if(!ac97_init_mixer(codec))
- return 0;
-
- /*
- * Attach last so the caller can override the mixer
- * callbacks.
- */
-
- mutex_lock(&codec_mutex);
- list_add(&codec->list, &codecs);
-
- list_for_each(l, &codec_drivers) {
- d = list_entry(l, struct ac97_driver, list);
- if ((codec->model ^ d->codec_id) & d->codec_mask)
- continue;
- if(d->probe(codec, d) == 0)
- {
- codec->driver = d;
- break;
- }
- }
-
- mutex_unlock(&codec_mutex);
- return 1;
-}
-
-static int ac97_init_mixer(struct ac97_codec *codec)
-{
- u16 cap;
- int i;
-
- cap = codec->codec_read(codec, AC97_RESET);
-
- /* mixer masks */
- codec->supported_mixers = AC97_SUPPORTED_MASK;
- codec->stereo_mixers = AC97_STEREO_MASK;
- codec->record_sources = AC97_RECORD_MASK;
- if (!(cap & 0x04))
- codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
- if (!(cap & 0x10))
- codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
-
-
- /* detect bit resolution */
- codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
- if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
- codec->bit_resolution = 6;
- else
- codec->bit_resolution = 5;
-
- /* generic OSS to AC97 wrapper */
- codec->read_mixer = ac97_read_mixer;
- codec->write_mixer = ac97_write_mixer;
- codec->recmask_io = ac97_recmask_io;
- codec->mixer_ioctl = ac97_mixer_ioctl;
-
- /* initialize mixer channel volumes */
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- struct mixer_defaults *md = &mixer_defaults[i];
- if (md->mixer == -1)
- break;
- if (!supported_mixer(codec, md->mixer))
- continue;
- ac97_set_mixer(codec, md->mixer, md->value);
- }
-
- /* codec specific initialization for 4-6 channel output or secondary codec stuff */
- if (codec->codec_ops->init != NULL) {
- codec->codec_ops->init(codec);
- }
-
- /*
- * Volume is MUTE only on this device. We have to initialise
- * it but its useless beyond that.
- */
- if(codec->flags & AC97_NO_PCM_VOLUME)
- {
- codec->supported_mixers &= ~SOUND_MASK_PCM;
- printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
- }
- return 1;
-}
-
-#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
-#define AC97_SIGMATEL_DAC2INVERT 0x6e
-#define AC97_SIGMATEL_BIAS1 0x70
-#define AC97_SIGMATEL_BIAS2 0x72
-#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */
-#define AC97_SIGMATEL_CIC1 0x76
-#define AC97_SIGMATEL_CIC2 0x78
-
-
-static int sigmatel_9708_init(struct ac97_codec * codec)
-{
- u16 codec72, codec6c;
-
- codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
- codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
-
- if ((codec72==0) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
- } else if ((codec72==0x8000) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
- codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
- } else if ((codec72==0x8000) && (codec6c==0x0080)) {
- /* nothing */
- }
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-
-static int sigmatel_9721_init(struct ac97_codec * codec)
-{
- /* Only set up secondary codec */
- if (codec->id == 0)
- return 0;
-
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
-
- /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
- sloc 3,4 = 0x01, slot 7,8 = 0x00, */
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
-
- /* we don't have the crystal when we are on an AMR card, so use
- BIT_CLK as our clock source. Write the magic word ABBA and read
- back to enable register 0x78 */
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_read(codec, AC97_SIGMATEL_CIC1);
-
- /* sync all the clocks*/
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
-
- return 0;
-}
-
-
-static int sigmatel_9744_init(struct ac97_codec * codec)
-{
- // patch for SigmaTel
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-static int cmedia_init(struct ac97_codec *codec)
-{
- /* Initialise the CMedia 9739 */
- /*
- We could set various options here
- Register 0x20 bit 0x100 sets mic as center bass
- Also do multi_channel_ctrl &=~0x3000 |=0x1000
-
- For now we set up the GPIO and PC beep
- */
-
- u16 v;
-
- /* MIC */
- codec->codec_write(codec, 0x64, 0x3000);
- v = codec->codec_read(codec, 0x64);
- v &= ~0x8000;
- codec->codec_write(codec, 0x64, v);
- codec->codec_write(codec, 0x70, 0x0100);
- codec->codec_write(codec, 0x72, 0x0020);
- return 0;
-}
-
-#define AC97_WM97XX_FMIXER_VOL 0x72
-#define AC97_WM97XX_RMIXER_VOL 0x74
-#define AC97_WM97XX_TEST 0x5a
-#define AC97_WM9704_RPCM_VOL 0x70
-#define AC97_WM9711_OUT3VOL 0x16
-
-static int wolfson_init03(struct ac97_codec * codec)
-{
- /* this is known to work for the ViewSonic ViewPad 1000 */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
- return 0;
-}
-
-static int wolfson_init04(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
-
- // patch for DVD noise
- codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
-
- // init vol as PCM vol
- codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
- codec->codec_read(codec, AC97_PCMOUT_VOL));
-
- /* set rear surround volume */
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- return 0;
-}
-
-/* WM9705, WM9710 */
-static int wolfson_init05(struct ac97_codec * codec)
-{
- /* set front mixer volume */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- return 0;
-}
-
-/* WM9711, WM9712 */
-static int wolfson_init11(struct ac97_codec * codec)
-{
- /* stop pop's during suspend/resume */
- codec->codec_write(codec, AC97_WM97XX_TEST,
- codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
-
- /* set out3 volume */
- codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
- return 0;
-}
-
-/* WM9713 */
-static int wolfson_init13(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);
- codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
- codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
- codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
- codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
-
- return 0;
-}
-
-static int tritech_init(struct ac97_codec * codec)
-{
- codec->codec_write(codec, 0x26, 0x0300);
- codec->codec_write(codec, 0x26, 0x0000);
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-static int tritech_maestro_init(struct ac97_codec * codec)
-{
- /* no idea what this does */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0XFFFF);
- return 0;
-}
-
-
-
-/*
- * Presario700 workaround
- * for Jack Sense/SPDIF Register mis-setting causing
- * no audible output
- * by Santiago Nullo 04/05/2002
- */
-
-#define AC97_AD1886_JACK_SENSE 0x72
-
-static int ad1886_init(struct ac97_codec * codec)
-{
- /* from AD1886 Specs */
- codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010);
- return 0;
-}
-
-
-
-
-/*
- * This is basically standard AC97. It should work as a default for
- * almost all modern codecs. Note that some cards wire EAPD *backwards*
- * That side of it is up to the card driver not us to cope with.
- *
- */
-
-static int eapd_control(struct ac97_codec * codec, int on)
-{
- if(on)
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
- else
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
- return 0;
-}
-
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 reg;
-
- reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
-
- switch(rate)
- {
- /* Off by default */
- default:
- case 0:
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
- if(rate == 0)
- return 0;
- return -EINVAL;
- case 1:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
- break;
- case 2:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
- break;
- case 3:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
- break;
- }
-
- reg &= ~AC97_SC_CC_MASK;
- reg |= (mode & AUDIO_CCMASK) << 6;
-
- if(mode & AUDIO_DIGITAL)
- reg |= 2;
- if(mode & AUDIO_PRO)
- reg |= 1;
- if(mode & AUDIO_DRS)
- reg |= 0x4000;
-
- codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- reg &= (AC97_EA_SLOT_MASK);
- reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if(!(reg & 0x0400))
- {
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Crystal digital audio control (CS4299)
- */
-
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0; break; /* SPEN off */
- case 48000: cv = 0x8004; break; /* 48KHz digital */
- case 44100: cv = 0x8104; break; /* 44.1KHz digital */
- case 32768: /* 32Khz */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x68, cv);
- return 0;
-}
-
-/*
- * CMedia digital audio control
- * Needs more work.
- */
-
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0001; break; /* SPEN off */
- case 48000: cv = 0x0009; break; /* 48KHz digital */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x2A, 0x05c4);
- codec->codec_write(codec, 0x6C, cv);
-
- /* Switch on mix to surround */
- cv = codec->codec_read(codec, 0x64);
- cv &= ~0x0200;
- if(mode)
- cv |= 0x0200;
- codec->codec_write(codec, 0x64, cv);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-#if 0 /* there has been 1 person on the planet with a pt101 that we
- know of. If they care, they can put this back in :) */
-static int pt101_init(struct ac97_codec * codec)
-{
- printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
- /* who knows.. */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0xFFFF);
- codec->codec_write(codec, 0x10, 0x9F1F);
- codec->codec_write(codec, 0x12, 0x0808);
- codec->codec_write(codec, 0x14, 0x9F1F);
- codec->codec_write(codec, 0x16, 0x9F1F);
- codec->codec_write(codec, 0x18, 0x0404);
- codec->codec_write(codec, 0x1A, 0x0000);
- codec->codec_write(codec, 0x1C, 0x0000);
- codec->codec_write(codec, 0x02, 0x0404);
- codec->codec_write(codec, 0x04, 0x0808);
- codec->codec_write(codec, 0x0C, 0x801F);
- codec->codec_write(codec, 0x0E, 0x801F);
- return 0;
-}
-#endif
-
-
-EXPORT_SYMBOL(ac97_probe_codec);
-
-MODULE_LICENSE("GPL");
-
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
deleted file mode 100644
index a8f626d99c5b..000000000000
--- a/sound/oss/au1550_ac97.c
+++ /dev/null
@@ -1,2147 +0,0 @@
-/*
- * au1550_ac97.c -- Sound driver for Alchemy Au1550 MIPS Internet Edge
- * Processor.
- *
- * Copyright 2004 Embedded Edge, LLC
- * dan@embeddededge.com
- *
- * Mostly copied from the au1000.c driver and some from the
- * PowerMac dbdma driver.
- * We assume the processor can do memory coherent DMA.
- *
- * Ported to 2.6 by Matt Porter <mporter@kernel.crashing.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * 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.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#undef DEBUG
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/sound.h>
-#include <linux/slab.h>
-#include <linux/soundcard.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/hardirq.h>
-#include <asm/mach-au1x00/au1xxx_psc.h>
-#include <asm/mach-au1x00/au1xxx_dbdma.h>
-#include <asm/mach-au1x00/au1xxx.h>
-
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-/* misc stuff */
-#define POLL_COUNT 0x50000
-#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
-
-/* The number of DBDMA ring descriptors to allocate. No sense making
- * this too large....if you can't keep up with a few you aren't likely
- * to be able to with lots of them, either.
- */
-#define NUM_DBDMA_DESCRIPTORS 4
-
-#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)
-
-/* Boot options
- * 0 = no VRA, 1 = use VRA if codec supports it
- */
-static DEFINE_MUTEX(au1550_ac97_mutex);
-static int vra = 1;
-module_param(vra, bool, 0);
-MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
-
-static struct au1550_state {
- /* soundcore stuff */
- int dev_audio;
-
- struct ac97_codec *codec;
- unsigned codec_base_caps; /* AC'97 reg 00h, "Reset Register" */
- unsigned codec_ext_caps; /* AC'97 reg 28h, "Extended Audio ID" */
- int no_vra; /* do not use VRA */
-
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex sem;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- struct dmabuf {
- u32 dmanr;
- unsigned sample_rate;
- unsigned src_factor;
- unsigned sample_size;
- int num_channels;
- int dma_bytes_per_sample;
- int user_bytes_per_sample;
- int cnt_factor;
-
- void *rawbuf;
- unsigned buforder;
- unsigned numfrag;
- unsigned fragshift;
- void *nextIn;
- void *nextOut;
- int count;
- unsigned total_bytes;
- unsigned error;
- wait_queue_head_t wait;
-
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dma_fragsize;
- unsigned dmasize;
- unsigned dma_qcount;
-
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned stopped:1;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
- } dma_dac, dma_adc;
-} au1550_state;
-
-static unsigned
-ld2(unsigned int x)
-{
- unsigned r = 0;
-
- if (x >= 0x10000) {
- x >>= 16;
- r += 16;
- }
- if (x >= 0x100) {
- x >>= 8;
- r += 8;
- }
- if (x >= 0x10) {
- x >>= 4;
- r += 4;
- }
- if (x >= 4) {
- x >>= 2;
- r += 2;
- }
- if (x >= 2)
- r++;
- return r;
-}
-
-static void
-au1550_delay(int msec)
-{
- if (in_interrupt())
- return;
-
- schedule_timeout_uninterruptible(msecs_to_jiffies(msec));
-}
-
-static u16
-rdcodec(struct ac97_codec *codec, u8 addr)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- u16 data;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("rdcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= PSC_AC97CDC_RD; /* read command */
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- /* now wait for the data
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read poll expired!");
- data = 0;
- goto out;
- }
-
- /* wait for command done?
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read cmdwait expired!");
- data = 0;
- goto out;
- }
-
- data = au_readl(PSC_AC97CDC) & 0xffff;
- au_sync();
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- out:
- spin_unlock_irqrestore(&s->lock, flags);
-
- return data;
-}
-
-
-static void
-wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= (u32)data;
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: read cmdwait expired!");
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-waitcodec(struct ac97_codec *codec)
-{
- u16 temp;
- u32 val;
- int i;
-
- /* codec_wait is used to wait for a ready state after
- * an AC97C_RESET.
- */
- au1550_delay(10);
-
- /* first poll the CODEC_READY tag bit
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (val & PSC_AC97STAT_CR)
- break;
- }
- if (i == POLL_COUNT) {
- err("waitcodec: CODEC_READY poll expired!");
- return;
- }
-
- /* get AC'97 powerdown control/status register
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
-
- /* If anything is powered down, power'em up
- */
- if (temp & 0x7f00) {
- /* Power on
- */
- wrcodec(codec, AC97_POWER_CONTROL, 0);
- au1550_delay(100);
-
- /* Reread
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
- }
-
- /* Check if Codec REF,ANL,DAC,ADC ready
- */
- if ((temp & 0x7f0f) != 0x000f)
- err("codec reg 26 status (0x%x) not ready!!", temp);
-}
-
-/* stop the ADC before calling */
-static void
-set_adc_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *adc = &s->dma_adc;
- struct dmabuf *dac = &s->dma_dac;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- adc->src_factor = ((96000 / rate) + 1) >> 1;
- adc->sample_rate = 48000 / adc->src_factor;
- return;
- }
-
- adc->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
-
- /* read it back for actual supported rate
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- pr_debug("set_adc_rate: set to %d Hz\n", adc_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
-
- adc->sample_rate = adc_rate;
- dac->sample_rate = dac_rate;
-}
-
-/* stop the DAC before calling */
-static void
-set_dac_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *dac = &s->dma_dac;
- struct dmabuf *adc = &s->dma_adc;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- dac->src_factor = ((96000 / rate) + 1) >> 1;
- dac->sample_rate = 48000 / dac->src_factor;
- return;
- }
-
- dac->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
-
- /* I don't support different sample rates for multichannel,
- * so make these channels the same.
- */
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
- /* read it back for actual supported rate
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
-
- pr_debug("set_dac_rate: set to %d Hz\n", dac_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- dac->sample_rate = dac_rate;
- adc->sample_rate = adc_rate;
-}
-
-static void
-stop_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
- u32 stat;
- unsigned long flags;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_TP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Transmit Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_TB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-stop_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- unsigned long flags;
- u32 stat;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_RP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Receive Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_RB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void
-set_xmit_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- switch (num_channels) {
- case 6: /* stereo with surround and center/LFE,
- * slots 3,4,6,7,8,9
- */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9);
-
- case 4: /* stereo with surround, slots 3,4,7,8 */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);
-
- case 2: /* stereo, slots 3,4 */
- case 1: /* mono */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);
- }
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-static void
-set_recv_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Always enable slots 3 and 4 (stereo). Slot 6 is
- * optional Mic ADC, which we don't support yet.
- */
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-/* Hold spinlock for both start_dac() and start_adc() calls */
-static void
-start_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
-
- if (!db->stopped)
- return;
-
- set_xmit_slots(db->num_channels);
- au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
- au_sync();
-
- au1xxx_dbdma_start(db->dmanr);
-
- db->stopped = 0;
-}
-
-static void
-start_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- int i;
-
- if (!db->stopped)
- return;
-
- /* Put two buffers on the ring to get things started.
- */
- for (i=0; i<2; i++) {
- au1xxx_dbdma_put_dest(db->dmanr, virt_to_phys(db->nextIn),
- db->dma_fragsize, DDMA_FLAGS_IE);
-
- db->nextIn += db->dma_fragsize;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
- }
-
- set_recv_slots(db->num_channels);
- au1xxx_dbdma_start(db->dmanr);
- au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
- au_sync();
-
- db->stopped = 0;
-}
-
-static int
-prog_dmabuf(struct au1550_state *s, struct dmabuf *db)
-{
- unsigned user_bytes_per_sec;
- unsigned bufs;
- unsigned rate = db->sample_rate;
-
- if (!db->rawbuf) {
- db->ready = db->mapped = 0;
- db->buforder = 5; /* 32 * PAGE_SIZE */
- db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
- if (!db->rawbuf)
- return -ENOMEM;
- }
-
- db->cnt_factor = 1;
- if (db->sample_size == 8)
- db->cnt_factor *= 2;
- if (db->num_channels == 1)
- db->cnt_factor *= 2;
- db->cnt_factor *= db->src_factor;
-
- db->count = 0;
- db->dma_qcount = 0;
- db->nextIn = db->nextOut = db->rawbuf;
-
- db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
- db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
- 2 : db->num_channels);
-
- user_bytes_per_sec = rate * db->user_bytes_per_sample;
- bufs = PAGE_SIZE << db->buforder;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < user_bytes_per_sec)
- db->fragshift = ld2(user_bytes_per_sec/1000);
- else
- db->fragshift = db->ossfragshift;
- } else {
- db->fragshift = ld2(user_bytes_per_sec / 100 /
- (db->subdivision ? db->subdivision : 1));
- if (db->fragshift < 3)
- db->fragshift = 3;
- }
-
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
-
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
- }
-
- if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
- db->numfrag = db->ossmaxfrags;
-
- db->dmasize = db->dma_fragsize * db->numfrag;
- memset(db->rawbuf, 0, bufs);
-
- pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",
- rate, db->sample_size, db->num_channels);
- pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",
- db->fragsize, db->cnt_factor, db->dma_fragsize);
- pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);
-
- db->ready = 1;
- return 0;
-}
-
-static int
-prog_dmabuf_adc(struct au1550_state *s)
-{
- stop_adc(s);
- return prog_dmabuf(s, &s->dma_adc);
-
-}
-
-static int
-prog_dmabuf_dac(struct au1550_state *s)
-{
- stop_dac(s);
- return prog_dmabuf(s, &s->dma_dac);
-}
-
-
-static void dac_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *) dev_id;
- struct dmabuf *db = &s->dma_dac;
- u32 ac97c_stat;
-
- spin_lock(&s->lock);
-
- ac97c_stat = au_readl(PSC_AC97STAT);
- if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
- pr_debug("AC97C status = 0x%08x\n", ac97c_stat);
- db->dma_qcount--;
-
- if (db->count >= db->fragsize) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->count -= db->fragsize;
- db->total_bytes += db->dma_fragsize;
- db->dma_qcount++;
- }
-
- /* wake up anybody listening */
- if (waitqueue_active(&db->wait))
- wake_up(&db->wait);
-
- spin_unlock(&s->lock);
-}
-
-
-static void adc_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *)dev_id;
- struct dmabuf *dp = &s->dma_adc;
- u32 obytes;
- char *obuf;
-
- spin_lock(&s->lock);
-
- /* Pull the buffer from the dma queue.
- */
- au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
-
- if ((dp->count + obytes) > dp->dmasize) {
- /* Overrun. Stop ADC and log the error
- */
- spin_unlock(&s->lock);
- stop_adc(s);
- dp->error++;
- err("adc overrun");
- return;
- }
-
- /* Put a new empty buffer on the destination DMA.
- */
- au1xxx_dbdma_put_dest(dp->dmanr, virt_to_phys(dp->nextIn),
- dp->dma_fragsize, DDMA_FLAGS_IE);
-
- dp->nextIn += dp->dma_fragsize;
- if (dp->nextIn >= dp->rawbuf + dp->dmasize)
- dp->nextIn -= dp->dmasize;
-
- dp->count += obytes;
- dp->total_bytes += obytes;
-
- /* wake up anybody listening
- */
- if (waitqueue_active(&dp->wait))
- wake_up(&dp->wait);
-
- spin_unlock(&s->lock);
-}
-
-static loff_t
-au1550_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-
-static int
-au1550_open_mixdev(struct inode *inode, struct file *file)
-{
- mutex_lock(&au1550_ac97_mutex);
- file->private_data = &au1550_state;
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static int
-au1550_release_mixdev(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int
-mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
- unsigned long arg)
-{
- return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static long
-au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- struct ac97_codec *codec = s->codec;
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = mixdev_ioctl(codec, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static /*const */ struct file_operations au1550_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .unlocked_ioctl = au1550_ioctl_mixdev,
- .open = au1550_open_mixdev,
- .release = au1550_release_mixdev,
-};
-
-static int
-drain_dac(struct au1550_state *s, int nonblock)
-{
- unsigned long flags;
- int count, tmo;
-
- if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
- return 0;
-
- for (;;) {
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (count <= s->dma_dac.fragsize)
- break;
- if (signal_pending(current))
- break;
- if (nonblock)
- return -EBUSY;
- tmo = 1000 * count / (s->no_vra ?
- 48000 : s->dma_dac.sample_rate);
- tmo /= s->dma_dac.dma_bytes_per_sample;
- au1550_delay(tmo);
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-static inline u8 S16_TO_U8(s16 ch)
-{
- return (u8) (ch >> 8) + 0x80;
-}
-static inline s16 U8_TO_S16(u8 ch)
-{
- return (s16) (ch - 0x80) << 8;
-}
-
-/*
- * Translates user samples to dma buffer suitable for AC'97 DAC data:
- * If mono, copy left channel to right channel in dma buffer.
- * If 8 bit samples, cvt to 16-bit before writing to dma buffer.
- * If interpolating (no VRA), duplicate every audio frame src_factor times.
- */
-static int
-translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
- s16 ch, dmasample[6];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_from_user(dmabuf, userbuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- if (copy_from_user(usersample, userbuf,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- ch = U8_TO_S16(usersample[i]);
- else
- ch = *((s16 *) (&usersample[i * 2]));
- dmasample[i] = ch;
- if (mono)
- dmasample[i + 1] = ch; /* right channel */
- }
-
- /* duplicate every audio frame src_factor times
- */
- for (i = 0; i < db->src_factor; i++)
- memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Translates AC'97 ADC samples to user buffer:
- * If mono, send only left channel to user buffer.
- * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
- * If decimating (no VRA), skip over src_factor audio frames.
- */
-static int
-translate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_to_user(userbuf, dmabuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- usersample[i] =
- S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
- else
- *((s16 *) (&usersample[i * 2])) =
- *((s16 *) (&dmabuf[i * 2]));
- }
-
- if (copy_to_user(userbuf, usersample,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Copy audio data to/from user buffer from/to dma buffer, taking care
- * that we wrap when reading/writing the dma buffer. Returns actual byte
- * count written to or read from the dma buffer.
- */
-static int
-copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
-{
- char *bufptr = to_user ? db->nextOut : db->nextIn;
- char *bufend = db->rawbuf + db->dmasize;
- int cnt, ret;
-
- if (bufptr + count > bufend) {
- int partial = (int) (bufend - bufptr);
- if (to_user) {
- if ((cnt = translate_to_user(db, userbuf,
- bufptr, partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_to_user(db, userbuf + partial,
- db->rawbuf,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- } else {
- if ((cnt = translate_from_user(db, bufptr, userbuf,
- partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_from_user(db, db->rawbuf,
- userbuf + partial,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- }
- } else {
- if (to_user)
- ret = translate_to_user(db, userbuf, bufptr, count);
- else
- ret = translate_from_user(db, bufptr, userbuf, count);
- }
-
- return ret;
-}
-
-
-static ssize_t
-au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_adc;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for samples in ADC dma buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- if (db->stopped)
- start_adc(s);
- avail = db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from nextOut to user
- */
- if ((cnt = copy_dmabuf_user(db, buffer,
- count > avail ?
- avail : count, 1)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count -= cnt;
- db->nextOut += cnt;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-static ssize_t
-au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_dac;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- pr_debug("write: count=%d\n", count);
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for space in playback buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- avail = (int) db->dmasize - db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from user to nextIn
- */
- if ((cnt = copy_dmabuf_user(db, (char *) buffer,
- count > avail ?
- avail : count, 0)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count += cnt;
- db->nextIn += cnt;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
-
- /* If the data is available, we want to keep two buffers
- * on the dma queue. If the queue count reaches zero,
- * we know the dma has stopped.
- */
- while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->total_bytes += db->dma_fragsize;
- if (db->dma_qcount == 0)
- start_dac(s);
- db->dma_qcount++;
- }
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-
-/* No kernel lock - we have our own spinlock */
-static unsigned int
-au1550_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!s->dma_dac.ready)
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!s->dma_adc.ready)
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
-
- spin_lock_irqsave(&s->lock, flags);
-
- if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed) s->dma_dac.dmasize >=
- s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return mask;
-}
-
-static int
-au1550_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db;
- unsigned long size;
- int ret = 0;
-
- mutex_lock(&au1550_ac97_mutex);
- mutex_lock(&s->sem);
- if (vma->vm_flags & VM_WRITE)
- db = &s->dma_dac;
- else if (vma->vm_flags & VM_READ)
- db = &s->dma_adc;
- else {
- ret = -EINVAL;
- goto out;
- }
- if (vma->vm_pgoff != 0) {
- ret = -EINVAL;
- goto out;
- }
- size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << db->buforder)) {
- ret = -EINVAL;
- goto out;
- }
- if (remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page(db->rawbuf)),
- size, vma->vm_page_prot)) {
- ret = -EAGAIN;
- goto out;
- }
- vma->vm_flags &= ~VM_IO;
- db->mapped = 1;
-out:
- mutex_unlock(&s->sem);
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-#ifdef DEBUG
-static struct ioctl_str_t {
- unsigned int cmd;
- const char *str;
-} ioctl_str[] = {
- {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
- {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
- {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
- {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
- {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
- {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
- {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
- {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
- {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
- {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
- {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
- {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
- {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
- {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
- {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
- {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
- {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
- {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
- {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
- {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
- {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
- {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
- {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
- {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
- {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
- {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
- {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
- {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
- {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
- {OSS_GETVERSION, "OSS_GETVERSION"},
- {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
- {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
- {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
- {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
-};
-#endif
-
-static int
-dma_count_done(struct dmabuf *db)
-{
- if (db->stopped)
- return 0;
-
- return db->dma_fragsize - au1xxx_get_dma_residue(db->dmanr);
-}
-
-
-static int
-au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int count;
- int val, mapped, ret, diff;
-
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
-
-#ifdef DEBUG
- for (count = 0; count < ARRAY_SIZE(ioctl_str); count++) {
- if (ioctl_str[count].cmd == cmd)
- break;
- }
- if (count < ARRAY_SIZE(ioctl_str))
- pr_debug("ioctl %s, arg=0x%lxn", ioctl_str[count].str, arg);
- else
- pr_debug("ioctl 0x%x unknown, arg=0x%lx\n", cmd, arg);
-#endif
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s, file->f_flags & O_NONBLOCK);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq();
- s->dma_dac.count = s->dma_dac.total_bytes = 0;
- s->dma_dac.nextIn = s->dma_dac.nextOut =
- s->dma_dac.rawbuf;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq();
- s->dma_adc.count = s->dma_adc.total_bytes = 0;
- s->dma_adc.nextIn = s->dma_adc.nextOut =
- s->dma_adc.rawbuf;
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val >= 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- set_adc_rate(s, val);
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- set_dac_rate(s, val);
- }
- if (s->open_mode & FMODE_READ)
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- if (s->open_mode & FMODE_WRITE)
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.num_channels = val ? 2 : 1;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.num_channels = val ? 2 : 1;
- if (s->codec_ext_caps & AC97_EXT_DACS) {
- /* disable surround and center/lfe in AC'97
- */
- u16 ext_stat = rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- if (val < 0 || val > 2)
- return -EINVAL;
- stop_adc(s);
- s->dma_adc.num_channels = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- switch (val) {
- case 1:
- case 2:
- break;
- case 3:
- case 5:
- return -EINVAL;
- case 4:
- if (!(s->codec_ext_caps &
- AC97_EXTID_SDAC))
- return -EINVAL;
- break;
- case 6:
- if ((s->codec_ext_caps &
- AC97_EXT_DACS) != AC97_EXT_DACS)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- stop_dac(s);
- if (val <= 2 &&
- (s->codec_ext_caps & AC97_EXT_DACS)) {
- /* disable surround and center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- } else if (val >= 4) {
- /* enable surround, center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- ext_stat &= ~AC97_EXTSTAT_PRJ;
- if (val == 6)
- ext_stat &=
- ~(AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRK);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat);
- }
-
- s->dma_dac.num_channels = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- if (val == AFMT_S16_LE)
- s->dma_adc.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_adc.sample_size = 8;
- }
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- if (val == AFMT_S16_LE)
- s->dma_dac.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_dac.sample_size = 8;
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- } else {
- if (file->f_mode & FMODE_READ)
- val = (s->dma_adc.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- else
- val = (s->dma_dac.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- spin_lock_irqsave(&s->lock, flags);
- if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
- val |= PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_adc(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_dac(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- abinfo.fragsize = s->dma_dac.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = (s->dma_dac.dmasize - count) /
- s->dma_dac.cnt_factor;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- pr_debug("ioctl SNDCTL_DSP_GETOSPACE: bytes=%d, fragments=%d\n", abinfo.bytes, abinfo.fragments);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- abinfo.fragsize = s->dma_adc.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_adc.count;
- count += dma_count_done(&s->dma_adc);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = count / s->dma_adc.cnt_factor;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- count /= s->dma_dac.cnt_factor;
- return put_user(count, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_adc.total_bytes;
- count = s->dma_adc.count;
- if (!s->dma_adc.stopped) {
- diff = dma_count_done(&s->dma_adc);
- count += diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff -
- virt_to_phys(s->dma_adc.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) -
- virt_to_phys(s->dma_adc.rawbuf);
- if (s->dma_adc.mapped)
- s->dma_adc.count &= (s->dma_adc.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_dac.total_bytes;
- count = s->dma_dac.count;
- if (!s->dma_dac.stopped) {
- diff = dma_count_done(&s->dma_dac);
- count -= diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff -
- virt_to_phys(s->dma_dac.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) -
- virt_to_phys(s->dma_dac.rawbuf);
- if (s->dma_dac.mapped)
- s->dma_dac.count &= (s->dma_dac.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_dac.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE)
- return put_user(s->dma_dac.fragsize, (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ossfragshift = val & 0xffff;
- s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_adc.ossfragshift < 4)
- s->dma_adc.ossfragshift = 4;
- if (s->dma_adc.ossfragshift > 15)
- s->dma_adc.ossfragshift = 15;
- if (s->dma_adc.ossmaxfrags < 4)
- s->dma_adc.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ossfragshift = val & 0xffff;
- s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_dac.ossfragshift < 4)
- s->dma_dac.ossfragshift = 4;
- if (s->dma_dac.ossfragshift > 15)
- s->dma_dac.ossfragshift = 15;
- if (s->dma_dac.ossmaxfrags < 4)
- s->dma_dac.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.subdivision = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.subdivision = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.num_channels, (int *)arg);
- else
- return put_user(s->dma_dac.num_channels, (int *)arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.sample_size, (int *)arg);
- else
- return put_user(s->dma_dac.sample_size, (int *)arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
-
- return mixdev_ioctl(s->codec, cmd, arg);
-}
-
-static long
-au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = au1550_ioctl(file, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static int
-au1550_open(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- DECLARE_WAITQUEUE(wait, current);
- struct au1550_state *s = &au1550_state;
- int ret;
-
-#ifdef DEBUG
- if (file->f_flags & O_NONBLOCK)
- pr_debug("open: non-blocking\n");
- else
- pr_debug("open: blocking\n");
-#endif
-
- file->private_data = s;
- mutex_lock(&au1550_ac97_mutex);
- /* wait for device to become free */
- mutex_lock(&s->open_mutex);
- while (s->open_mode & file->f_mode) {
- ret = -EBUSY;
- if (file->f_flags & O_NONBLOCK)
- goto out;
- add_wait_queue(&s->open_wait, &wait);
- __set_current_state(TASK_INTERRUPTIBLE);
- mutex_unlock(&s->open_mutex);
- schedule();
- remove_wait_queue(&s->open_wait, &wait);
- set_current_state(TASK_RUNNING);
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- goto out2;
- mutex_lock(&s->open_mutex);
- }
-
- stop_dac(s);
- stop_adc(s);
-
- if (file->f_mode & FMODE_READ) {
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
- s->dma_adc.num_channels = 1;
- s->dma_adc.sample_size = 8;
- set_adc_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_adc.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_WRITE) {
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
- s->dma_dac.num_channels = 1;
- s->dma_dac.sample_size = 8;
- set_dac_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_dac.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_READ) {
- if ((ret = prog_dmabuf_adc(s)))
- goto out;
- }
- if (file->f_mode & FMODE_WRITE) {
- if ((ret = prog_dmabuf_dac(s)))
- goto out;
- }
-
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_init(&s->sem);
- ret = 0;
-out:
- mutex_unlock(&s->open_mutex);
-out2:
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-static int
-au1550_release(struct inode *inode, struct file *file)
-{
- struct au1550_state *s = file->private_data;
-
- mutex_lock(&au1550_ac97_mutex);
-
- if (file->f_mode & FMODE_WRITE) {
- mutex_unlock(&au1550_ac97_mutex);
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&au1550_ac97_mutex);
- }
-
- mutex_lock(&s->open_mutex);
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- kfree(s->dma_dac.rawbuf);
- s->dma_dac.rawbuf = NULL;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- kfree(s->dma_adc.rawbuf);
- s->dma_adc.rawbuf = NULL;
- }
- s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
- mutex_unlock(&s->open_mutex);
- wake_up(&s->open_wait);
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static /*const */ struct file_operations au1550_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .read = au1550_read,
- .write = au1550_write,
- .poll = au1550_poll,
- .unlocked_ioctl = au1550_unlocked_ioctl,
- .mmap = au1550_mmap,
- .open = au1550_open,
- .release = au1550_release,
-};
-
-MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
-MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
-MODULE_LICENSE("GPL");
-
-
-static int __devinit
-au1550_probe(void)
-{
- struct au1550_state *s = &au1550_state;
- int val;
-
- memset(s, 0, sizeof(struct au1550_state));
-
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->open_wait);
- mutex_init(&s->open_mutex);
- spin_lock_init(&s->lock);
-
- s->codec = ac97_alloc_codec();
- if(s->codec == NULL) {
- err("Out of memory");
- return -1;
- }
- s->codec->private_data = s;
- s->codec->id = 0;
- s->codec->codec_read = rdcodec;
- s->codec->codec_write = wrcodec;
- s->codec->codec_wait = waitcodec;
-
- if (!request_mem_region(CPHYSADDR(AC97_PSC_SEL),
- 0x30, "Au1550 AC97")) {
- err("AC'97 ports in use");
- }
-
- /* Allocate the DMA Channels
- */
- if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
- DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
- err("Can't get DAC DMA");
- goto err_dma1;
- }
- au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get DAC DMA descriptors");
- goto err_dma1;
- }
-
- if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
- DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
- err("Can't get ADC DMA");
- goto err_dma2;
- }
- au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get ADC DMA descriptors");
- goto err_dma2;
- }
-
- pr_info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
-
- /* register devices */
-
- if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
- goto err_dev1;
- if ((s->codec->dev_mixer =
- register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
- goto err_dev2;
-
- /* The GPIO for the appropriate PSC was configured by the
- * board specific start up.
- *
- * configure PSC for AC'97
- */
- au_writel(0, AC97_PSC_CTRL); /* Disable PSC */
- au_sync();
- au_writel((PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE), AC97_PSC_SEL);
- au_sync();
-
- /* cold reset the AC'97
- */
- au_writel(PSC_AC97RST_RST, PSC_AC97RST);
- au_sync();
- au1550_delay(10);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* need to delay around 500msec(bleech) to give
- some CODECs enough time to wakeup */
- au1550_delay(500);
-
- /* warm reset the AC'97 to start the bitclk
- */
- au_writel(PSC_AC97RST_SNC, PSC_AC97RST);
- au_sync();
- udelay(100);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* Enable PSC
- */
- au_writel(PSC_CTRL_ENABLE, AC97_PSC_CTRL);
- au_sync();
-
- /* Wait for PSC ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_SR) == 0);
-
- /* Configure AC97 controller.
- * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
- */
- val = PSC_AC97CFG_SET_LEN(16);
- val |= PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8;
-
- /* Enable device so we can at least
- * talk over the AC-link.
- */
- au_writel(val, PSC_AC97CFG);
- au_writel(PSC_AC97MSK_ALLMASK, PSC_AC97MSK);
- au_sync();
- val |= PSC_AC97CFG_DE_ENABLE;
- au_writel(val, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_DR) == 0);
-
- /* codec init */
- if (!ac97_probe_codec(s->codec))
- goto err_dev3;
-
- s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
- s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
- pr_info("AC'97 Base/Extended ID = %04x/%04x",
- s->codec_base_caps, s->codec_ext_caps);
-
- if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
- /* codec does not support VRA
- */
- s->no_vra = 1;
- } else if (!vra) {
- /* Boot option says disable VRA
- */
- u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat & ~AC97_EXTSTAT_VRA);
- s->no_vra = 1;
- }
- if (s->no_vra)
- pr_info("no VRA, interpolating and decimating");
-
- /* set mic to be the recording source */
- val = SOUND_MASK_MIC;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
- (unsigned long) &val);
-
- return 0;
-
- err_dev3:
- unregister_sound_mixer(s->codec->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- err_dma2:
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- err_dma1:
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
-
- ac97_release_codec(s->codec);
- return -1;
-}
-
-static void __devinit
-au1550_remove(void)
-{
- struct au1550_state *s = &au1550_state;
-
- if (!s)
- return;
- synchronize_irq();
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
- unregister_sound_dsp(s->dev_audio);
- unregister_sound_mixer(s->codec->dev_mixer);
- ac97_release_codec(s->codec);
-}
-
-static int __init
-init_au1550(void)
-{
- return au1550_probe();
-}
-
-static void __exit
-cleanup_au1550(void)
-{
- au1550_remove();
-}
-
-module_init(init_au1550);
-module_exit(cleanup_au1550);
-
-#ifndef MODULE
-
-static int __init
-au1550_setup(char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep(&options, ","))) {
- if (!*this_opt)
- continue;
- if (!strncmp(this_opt, "vra", 3)) {
- vra = 1;
- }
- }
-
- return 1;
-}
-
-__setup("au1550_audio=", au1550_setup);
-
-#endif /* MODULE */
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 389cd7931668..e90d103e177e 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -534,6 +534,14 @@ config SND_ES1968_INPUT
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
+config SND_ES1968_RADIO
+ bool "Enable TEA5757 radio tuner support for es1968"
+ depends on SND_ES1968
+ depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968
+ help
+ Say Y here to include support for TEA5757 radio tuner integrated on
+ some MediaForte cards (e.g. SF64-PCE2).
+
config SND_FM801
tristate "ForteMedia FM801"
select SND_OPL3_LIB
@@ -552,13 +560,13 @@ config SND_FM801_TEA575X_BOOL
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
help
Say Y here to include support for soundcards based on the ForteMedia
- FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
- Forte SF256-PCS-02) into the snd-fm801 driver.
+ FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
+ SF64-PCR) into the snd-fm801 driver.
-config SND_FM801_TEA575X
+config SND_TEA575X
tristate
- depends on SND_FM801_TEA575X_BOOL
- default SND_FM801
+ depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
+ default SND_FM801 || SND_ES1968
source "sound/pci/hda/Kconfig"
@@ -658,6 +666,15 @@ config SND_KORG1212
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
+config SND_LOLA
+ tristate "Digigram Lola"
+ select SND_PCM
+ help
+ Say Y to include support for Digigram Lola boards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-lola.
+
config SND_LX6464ES
tristate "Digigram LX6464ES"
select SND_PCM
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 9cf4348ec137..54fe325e3aa5 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_SND) += \
ca0106/ \
cs46xx/ \
cs5535audio/ \
+ lola/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index f8ccc9677c6f..2ca6f4f85b41 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -42,10 +42,29 @@
#include <sound/tlv.h>
#include <sound/hwdep.h>
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
+#if defined CONFIG_SND_DEBUG
+/* copied from pcm_lib.c, hope later patch will make that version public
+and this copy can be removed */
+static void pcm_debug_name(struct snd_pcm_substream *substream,
+ char *name, size_t len)
+{
+ snprintf(name, len, "pcmC%dD%d%c:%d",
+ substream->pcm->card->number,
+ substream->pcm->device,
+ substream->stream ? 'c' : 'p',
+ substream->number);
+}
+#define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name))
+#else
+#define pcm_debug_name(s, n, l) do { } while (0)
+#define DEBUG_NAME(name, substream) do { } while (0)
+#endif
+
#if defined CONFIG_SND_DEBUG_VERBOSE
/**
* snd_printddd - very verbose debug printk
@@ -58,7 +77,7 @@ MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
#define snd_printddd(format, args...) \
__snd_printk(3, __FILE__, __LINE__, format, ##args)
#else
-#define snd_printddd(format, args...) do { } while (0)
+#define snd_printddd(format, args...) do { } while (0)
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
@@ -101,13 +120,6 @@ static int adapter_fs = DEFAULT_SAMPLERATE;
#define PERIOD_BYTES_MIN 2048
#define BUFFER_BYTES_MAX (512 * 1024)
-/* convert stream to character */
-#define SCHR(s) ((s == SNDRV_PCM_STREAM_PLAYBACK) ? 'P' : 'C')
-
-/*#define TIMER_MILLISECONDS 20
-#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
-*/
-
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
struct clk_source {
@@ -136,7 +148,7 @@ struct snd_card_asihpi {
u32 h_mixer;
struct clk_cache cc;
- u16 support_mmap;
+ u16 can_dma;
u16 support_grouping;
u16 support_mrx;
u16 update_interval_frames;
@@ -155,6 +167,7 @@ struct snd_card_asihpi_pcm {
unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int drained_count;
struct snd_pcm_substream *substream;
u32 h_stream;
struct hpi_format format;
@@ -288,19 +301,26 @@ static u16 handle_error(u16 err, int line, char *filename)
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
/***************************** GENERAL PCM ****************/
-static void print_hwparams(struct snd_pcm_hw_params *p)
+
+static void print_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *p)
{
- snd_printd("HWPARAMS \n");
- snd_printd("samplerate %d \n", params_rate(p));
- snd_printd("Channels %d \n", params_channels(p));
- snd_printd("Format %d \n", params_format(p));
- snd_printd("subformat %d \n", params_subformat(p));
- snd_printd("Buffer bytes %d \n", params_buffer_bytes(p));
- snd_printd("Period bytes %d \n", params_period_bytes(p));
- snd_printd("access %d \n", params_access(p));
- snd_printd("period_size %d \n", params_period_size(p));
- snd_printd("periods %d \n", params_periods(p));
- snd_printd("buffer_size %d \n", params_buffer_size(p));
+ DEBUG_NAME(substream, name);
+ snd_printd("%s HWPARAMS\n", name);
+ snd_printd(" samplerate %d Hz\n", params_rate(p));
+ snd_printd(" channels %d\n", params_channels(p));
+ snd_printd(" format %d\n", params_format(p));
+ snd_printd(" subformat %d\n", params_subformat(p));
+ snd_printd(" buffer %d B\n", params_buffer_bytes(p));
+ snd_printd(" period %d B\n", params_period_bytes(p));
+ snd_printd(" access %d\n", params_access(p));
+ snd_printd(" period_size %d\n", params_period_size(p));
+ snd_printd(" periods %d\n", params_periods(p));
+ snd_printd(" buffer_size %d\n", params_buffer_size(p));
+ snd_printd(" %d B/s\n", params_rate(p) *
+ params_channels(p) *
+ snd_pcm_format_width(params_format(p)) / 8);
+
}
static snd_pcm_format_t hpi_to_alsa_formats[] = {
@@ -451,7 +471,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
int width;
unsigned int bytes_per_sec;
- print_hwparams(params);
+ print_hwparams(substream, params);
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
@@ -459,10 +479,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
if (err)
return err;
- snd_printdd("format %d, %d chans, %d_hz\n",
- format, params_channels(params),
- params_rate(params));
-
hpi_handle_error(hpi_format_create(&dpcm->format,
params_channels(params),
format, params_rate(params), 0, 0));
@@ -477,8 +493,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
}
dpcm->hpi_buffer_attached = 0;
- if (card->support_mmap) {
-
+ if (card->can_dma) {
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
@@ -509,8 +524,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
dpcm->bytes_per_sec = bytes_per_sec;
dpcm->buffer_bytes = params_buffer_bytes(params);
dpcm->period_bytes = params_period_bytes(params);
- snd_printdd("buffer_bytes=%d, period_bytes=%d, bps=%d\n",
- dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec);
return 0;
}
@@ -564,9 +577,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
u16 e;
+ DEBUG_NAME(substream, name);
+
+ snd_printdd("%s trigger\n", name);
- snd_printdd("%c%d trigger\n",
- SCHR(substream->stream), substream->number);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_pcm_group_for_each_entry(s, substream) {
@@ -580,8 +594,8 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
if (substream->stream != s->stream)
continue;
- if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
- (card->support_mmap)) {
+ ds->drained_count = 0;
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* How do I know how much valid data is present
* in buffer? Must be at least one period!
* Guessing 2 periods, but if
@@ -599,9 +613,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
}
if (card->support_grouping) {
- snd_printdd("\t%c%d group\n",
- SCHR(s->stream),
- s->number);
+ snd_printdd("%d group\n", s->number);
e = hpi_stream_group_add(
dpcm->h_stream,
ds->h_stream);
@@ -618,7 +630,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
/* start the master stream */
snd_card_asihpi_pcm_timer_start(substream);
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
- !card->support_mmap)
+ !card->can_dma)
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
@@ -636,9 +648,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
if (card->support_grouping) {
- snd_printdd("\t%c%d group\n",
- SCHR(s->stream),
- s->number);
+ snd_printdd("%d group\n", s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
@@ -732,9 +742,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
int loops = 0;
u16 state;
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
+ DEBUG_NAME(substream, name);
- snd_printdd("%c%d snd_card_asihpi_timer_function\n",
- SCHR(substream->stream), substream->number);
+ snd_printdd("%s snd_card_asihpi_timer_function\n", name);
/* find minimum newdata and buffer pos in group */
snd_pcm_group_for_each_entry(s, substream) {
@@ -756,6 +766,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
/* number of bytes in on-card buffer */
runtime->delay = on_card_bytes;
+ if (!card->can_dma)
+ on_card_bytes = bytes_avail;
+
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
if (state == HPI_STATE_STOPPED) {
@@ -763,12 +776,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
(on_card_bytes < ds->pcm_buf_host_rw_ofs)) {
hpi_handle_error(hpi_stream_start(ds->h_stream));
snd_printdd("P%d start\n", s->number);
+ ds->drained_count = 0;
}
} else if (state == HPI_STATE_DRAINED) {
snd_printd(KERN_WARNING "P%d drained\n",
s->number);
- /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
- continue; */
+ ds->drained_count++;
+ if (ds->drained_count > 2) {
+ snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
+ continue;
+ }
+ } else {
+ ds->drained_count = 0;
}
} else
pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
@@ -786,16 +805,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
newdata);
}
- snd_printdd("hw_ptr x%04lX, appl_ptr x%04lX\n",
+ snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n",
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
runtime->control->appl_ptr));
- snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X,"
- " aux=x%04X space=x%04X\n",
- loops, SCHR(s->stream), s->number,
- state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail,
+ snd_printdd("%d S=%d, "
+ "rw=0x%04X, dma=0x%04X, left=0x%04X, "
+ "aux=0x%04X space=0x%04X\n",
+ s->number, state,
+ ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs,
+ (int)bytes_avail,
(int)on_card_bytes, buffer_size-bytes_avail);
loops++;
}
@@ -814,7 +835,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- snd_printdd("jif %d buf pos x%04X newdata x%04X xfer x%04X\n",
+ snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n",
next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
snd_pcm_group_for_each_entry(s, substream) {
@@ -826,30 +847,63 @@ static void snd_card_asihpi_timer_function(unsigned long data)
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
- if (xfercount && (on_card_bytes <= ds->period_bytes)) {
- if (card->support_mmap) {
- if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_printddd("P%d write x%04x\n",
+ if (xfercount &&
+ /* Limit use of on card fifo for playback */
+ ((on_card_bytes <= ds->period_bytes) ||
+ (s->stream == SNDRV_PCM_STREAM_CAPTURE)))
+
+ {
+
+ unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
+ unsigned int xfer1, xfer2;
+ char *pd = &s->runtime->dma_area[buf_ofs];
+
+ if (card->can_dma) { /* buffer wrap is handled at lower level */
+ xfer1 = xfercount;
+ xfer2 = 0;
+ } else {
+ xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
+ xfer2 = xfercount - xfer1;
+ }
+
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_printddd("P%d write1 0x%04X 0x%04X\n",
+ s->number, xfer1, buf_ofs);
+ hpi_handle_error(
+ hpi_outstream_write_buf(
+ ds->h_stream, pd, xfer1,
+ &ds->format));
+
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+
+ snd_printddd("P%d write2 0x%04X 0x%04X\n",
s->number,
- ds->period_bytes);
+ xfercount - xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
- ds->h_stream,
- &s->runtime->
- dma_area[0],
- xfercount,
+ ds->h_stream, pd,
+ xfercount - xfer1,
&ds->format));
- } else {
- snd_printddd("C%d read x%04x\n",
- s->number,
- xfercount);
+ }
+ } else {
+ snd_printddd("C%d read1 0x%04x\n",
+ s->number, xfer1);
+ hpi_handle_error(
+ hpi_instream_read_buf(
+ ds->h_stream,
+ pd, xfer1));
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+ snd_printddd("C%d read2 0x%04x\n",
+ s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
ds->h_stream,
- NULL, xfercount));
+ pd, xfer2));
}
- ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
- } /* else R/W will be handled by read/write callbacks */
+ }
+ ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs;
snd_pcm_period_elapsed(s);
}
@@ -863,7 +917,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
- snd_printdd(KERN_INFO "Playback ioctl %d\n", cmd);
+ snd_printddd(KERN_INFO "P%d ioctl %d\n", substream->number, cmd);
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
@@ -873,7 +927,7 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- snd_printdd("playback prepare %d\n", substream->number);
+ snd_printdd("P%d prepare\n", substream->number);
hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
dpcm->pcm_buf_host_rw_ofs = 0;
@@ -890,7 +944,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t ptr;
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
- snd_printddd("playback_pointer=x%04lx\n", (unsigned long)ptr);
+ snd_printddd("P%d pointer = 0x%04lx\n", substream->number, (unsigned long)ptr);
return ptr;
}
@@ -986,11 +1040,9 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE;
-
- if (card->support_mmap)
- snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
@@ -998,7 +1050,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
/* struct is copied, so can create initializer dynamically */
runtime->hw = snd_card_asihpi_playback;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -1028,58 +1080,6 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- unsigned int len;
-
- len = frames_to_bytes(runtime, count);
-
- if (copy_from_user(runtime->dma_area, src, len))
- return -EFAULT;
-
- snd_printddd("playback copy%d %u bytes\n",
- substream->number, len);
-
- hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
-
- dpcm->pcm_buf_host_rw_ofs += len;
-
- return 0;
-}
-
-static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
- substream, int channel,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
-{
- /* Usually writes silence to DMA buffer, which should be overwritten
- by real audio later. Our fifos cannot be overwritten, and are not
- free-running DMAs. Silence is output on fifo underflow.
- This callback is still required to allow the copy callback to be used.
- */
- return 0;
-}
-
-static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
- .open = snd_card_asihpi_playback_open,
- .close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_playback_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_playback_pointer,
- .copy = snd_card_asihpi_playback_copy,
- .silence = snd_card_asihpi_playback_silence,
-};
-
static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
@@ -1211,18 +1211,16 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
snd_card_asihpi_capture_format(card, dpcm->h_stream,
&snd_card_asihpi_capture);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
- snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
-
- if (card->support_mmap)
- snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
runtime->hw = snd_card_asihpi_capture;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -1246,28 +1244,6 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- u32 len;
-
- len = frames_to_bytes(runtime, count);
-
- snd_printddd("capture copy%d %d bytes\n", substream->number, len);
- hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream,
- runtime->dma_area, len));
-
- dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len;
-
- if (copy_to_user(dst, runtime->dma_area, len))
- return -EFAULT;
-
- return 0;
-}
-
static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
@@ -1279,18 +1255,6 @@ static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.pointer = snd_card_asihpi_capture_pointer,
};
-static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
- .open = snd_card_asihpi_capture_open,
- .close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_capture_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_capture_pointer,
- .copy = snd_card_asihpi_capture_copy
-};
-
static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
int device, int substreams)
{
@@ -1303,17 +1267,10 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
if (err < 0)
return err;
/* pointer to ops struct is stored, dont change ops afterwards! */
- if (asihpi->support_mmap) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_asihpi_playback_mmap_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_asihpi_capture_mmap_ops);
- } else {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_ops);
- }
pcm->private_data = asihpi;
pcm->info_flags = 0;
@@ -1413,14 +1370,16 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
struct hpi_control *hpi_ctl,
char *name)
{
- char *dir = "";
+ char *dir;
memset(snd_control, 0, sizeof(*snd_control));
snd_control->name = hpi_ctl->name;
snd_control->private_value = hpi_ctl->h_control;
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
snd_control->index = 0;
- if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
+ if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
+ dir = ""; /* clock is neither capture nor playback */
+ else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
dir = "Capture "; /* On or towards a PCM capture destination*/
else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
(!hpi_ctl->dst_node_type))
@@ -1433,7 +1392,7 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
dir = "Playback "; /* PCM Playback source, or output node */
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
- sprintf(hpi_ctl->name, "%s%d %s%d %s%s",
+ sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
asihpi_dst_names[hpi_ctl->dst_node_type],
@@ -2875,14 +2834,14 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
if (err)
asihpi->update_interval_frames = 512;
- if (!asihpi->support_mmap)
+ if (!asihpi->can_dma)
asihpi->update_interval_frames *= 2;
hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
0, &h_stream));
err = hpi_instream_host_buffer_free(h_stream);
- asihpi->support_mmap = (!err);
+ asihpi->can_dma = (!err);
hpi_handle_error(hpi_instream_close(h_stream));
@@ -2894,8 +2853,8 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
asihpi->out_max_chans = 2;
}
- snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
- asihpi->support_mmap,
+ snd_printk(KERN_INFO "has dma:%d, grouping:%d, mrx:%d\n",
+ asihpi->can_dma,
asihpi->support_grouping,
asihpi->support_mrx
);
@@ -2925,10 +2884,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
by enable_hwdep module param*/
snd_asihpi_hpi_new(asihpi, 0, NULL);
- if (asihpi->support_mmap)
- strcpy(card->driver, "ASIHPI-MMAP");
- else
- strcpy(card->driver, "ASIHPI");
+ strcpy(card->driver, "ASIHPI");
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
sprintf(card->longname, "%s %i",
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index 8c8aac4c567e..df4aed5295dd 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -200,8 +200,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -222,9 +222,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -279,6 +276,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
adapter_get_asserts(pao, phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
+
default:
hw_message(pao, phm, phr);
break;
@@ -333,26 +334,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
- /* subsytem messages get executed by every HPI. */
- /* All other messages are ignored unless the adapter index matches */
- /* an adapter in the HPI */
- /*HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->wObject, phm->wFunction); */
-
- /* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+ HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
+ phm->adapter_index);
return;
}
+ /* Don't even try to communicate with crashed DSP */
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
+ HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
+ phm->adapter_index);
return;
}
}
@@ -463,15 +460,9 @@ static void subsys_create_adapter(struct hpi_message *phm,
phr->error = 0;
}
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- pao = hpi_find_adapter(phm->obj_index);
- if (!pao)
- return;
-
delete_adapter_obj(pao);
hpi_delete_adapter(pao);
phr->error = 0;
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 22e9f08dea6d..9d5df54a6b46 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -152,8 +152,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
@@ -223,15 +223,13 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index);
/*****************************************************************************/
-static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
+static void subsys_message(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -279,6 +277,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
+
default:
hw_message(pao, phm, phr);
break;
@@ -371,36 +373,17 @@ static void instream_message(struct hpi_adapter_obj *pao,
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
-void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
+ struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- /* subsytem messages are processed by every HPI.
- * All other messages are ignored unless the adapter index matches
- * an adapter in the HPI
- */
- /* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject,
- phm->wFunction); */
-
- /* if Dsp has crashed then do not communicate with it any more */
- if (phm->object != HPI_OBJ_SUBSYSTEM) {
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
- return;
- }
-
- if ((pao->dsp_crashed >= 10)
- && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
- /* allow last resort debug read even after crash */
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
- return;
- }
+ if (pao && (pao->dsp_crashed >= 10)
+ && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
+ /* allow last resort debug read even after crash */
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_DSP_HARDWARE);
+ HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object,
+ phm->function);
+ return;
}
/* Init default response */
@@ -412,7 +395,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
- subsys_message(phm, phr);
+ subsys_message(pao, phm, phr);
break;
case HPI_OBJ_ADAPTER:
@@ -444,6 +427,26 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
}
}
+void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_adapter_obj *pao = NULL;
+
+ if (phm->object != HPI_OBJ_SUBSYSTEM) {
+ /* normal messages must have valid adapter index */
+ pao = hpi_find_adapter(phm->adapter_index);
+ } else {
+ /* subsys messages don't address an adapter */
+ _HPI_6205(NULL, phm, phr);
+ return;
+ }
+
+ if (pao)
+ _HPI_6205(pao, phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+}
+
/*****************************************************************************/
/* SUBSYSTEM */
@@ -491,13 +494,11 @@ static void subsys_create_adapter(struct hpi_message *phm,
}
/** delete an adapter - required by WDM driver */
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao;
struct hpi_hw_obj *phw;
- pao = hpi_find_adapter(phm->obj_index);
if (!pao) {
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
@@ -563,11 +564,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
}
err = adapter_boot_load_dsp(pao, pos_error_code);
- if (err)
+ if (err) {
+ HPI_DEBUG_LOG(ERROR, "DSP code load failed\n");
/* no need to clean up as SubSysCreateAdapter */
/* calls DeleteAdapter on error. */
return err;
-
+ }
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
/* allow boot load even if mem alloc wont work */
@@ -604,6 +606,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
control_cache.number_of_controls,
interface->control_cache.size_in_bytes,
p_control_cache_virtual);
+
if (!phw->p_cache)
err = HPI_ERROR_MEMORY_ALLOC;
}
@@ -675,16 +678,14 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
}
/** Free memory areas allocated by adapter
- * this routine is called from SubSysDeleteAdapter,
+ * this routine is called from AdapterDelete,
* and SubSysCreateAdapter if duplicate index
*/
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
{
- struct hpi_hw_obj *phw;
+ struct hpi_hw_obj *phw = pao->priv;
int i;
- phw = pao->priv;
-
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
hpios_locked_mem_free(&phw->h_control_cache);
hpi_free_control_cache(phw->p_cache);
@@ -1275,6 +1276,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
case HPI_ADAPTER_FAMILY_ASI(0x6300):
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
break;
+ case HPI_ADAPTER_FAMILY_ASI(0x5500):
case HPI_ADAPTER_FAMILY_ASI(0x5600):
case HPI_ADAPTER_FAMILY_ASI(0x6500):
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
@@ -2059,7 +2061,6 @@ static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us)
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
-
u32 r;
interface->host_cmd = cmd;
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index 3b9fd115da36..bf5eced76bac 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -294,7 +294,7 @@ enum HPI_CONTROL_ATTRIBUTES {
/* These defines are used to fill in protocol information for an Ethernet packet
sent using HMI on CS18102 */
-/** ID supplied by Cirrius for ASI packets. */
+/** ID supplied by Cirrus for ASI packets. */
#define HPI_ETHERNET_PACKET_ID 0x85
/** Simple packet - no special routing required */
#define HPI_ETHERNET_PACKET_V1 0x01
@@ -307,7 +307,7 @@ enum HPI_CONTROL_ATTRIBUTES {
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
-#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
+#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */
/** Default network timeout in milli-seconds. */
#define HPI_ETHERNET_TIMEOUT_MS 500
@@ -397,14 +397,14 @@ enum HPI_FUNCTION_IDS {
HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
- HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4),
+ /* HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), */
HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
- HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7),
+ /* HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), */
HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
- HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10),
- HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11),
+ /* HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), */
+ /* HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), */
HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
@@ -433,7 +433,8 @@ enum HPI_FUNCTION_IDS {
HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
-#define HPI_ADAPTER_FUNCTION_COUNT 20
+ HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21),
+#define HPI_ADAPTER_FUNCTION_COUNT 21
HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
@@ -1561,8 +1562,6 @@ void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
u16 *pw_adapter_index);
-u16 hpi_subsys_delete_adapter(u16 adapter_index);
-
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
@@ -1584,9 +1583,7 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for individual HPI entry points */
-hpi_handler_func HPI_1000;
hpi_handler_func HPI_6000;
hpi_handler_func HPI_6205;
-hpi_handler_func HPI_COMMON;
#endif /* _HPI_INTERNAL_H_ */
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index 3e9c5c289764..b15a02e91f82 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -227,8 +227,9 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
if (info->control_type) {
pC->p_info[info->control_index] = info;
cached++;
- } else /* dummy cache entry */
+ } else { /* dummy cache entry */
pC->p_info[info->control_index] = NULL;
+ }
byte_count += info->size_in32bit_words * 4;
@@ -298,7 +299,7 @@ struct pad_ofs_size {
unsigned int field_size;
};
-static struct pad_ofs_size pad_desc[] = {
+static const struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
@@ -617,6 +618,10 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
}
}
+/** Allocate control cache.
+
+\return Cache pointer, or NULL if allocation fails.
+*/
struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
const u32 size_in_bytes, u8 *p_dsp_control_buffer)
{
@@ -667,7 +672,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
phr->u.s.num_adapters = adapters.gw_num_adapters;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h
index 590f0b69e655..d53cdf6e535f 100644
--- a/sound/pci/asihpi/hpicmn.h
+++ b/sound/pci/asihpi/hpicmn.h
@@ -60,3 +60,5 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
+
+hpi_handler_func HPI_COMMON;
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index c38fc9487560..7397b169b89f 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -105,33 +105,6 @@ u16 hpi_subsys_get_version_ex(u32 *pversion_ex)
return hr.error;
}
-u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
- u16 *pw_adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CREATE_ADAPTER);
- hm.u.s.resource = *p_resource;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_adapter_index = hr.u.s.adapter_index;
- return hr.error;
-}
-
-u16 hpi_subsys_delete_adapter(u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.obj_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
{
struct hpi_message hm;
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index 360028b9abf5..7352a5f7b4f7 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -211,24 +211,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
HPIMSGX__init(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- HPIMSGX__cleanup(phm->obj_index, h_owner);
- {
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_CLOSE);
- hm.adapter_index = phm->obj_index;
- hw_entry_point(&hm, &hr);
- }
- if ((phm->obj_index < HPI_MAX_ADAPTERS)
- && hpi_entry_points[phm->obj_index]) {
- hpi_entry_points[phm->obj_index] (phm, phr);
- hpi_entry_points[phm->obj_index] = NULL;
- } else
- phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
-
- break;
default:
/* Must explicitly handle every subsys message in this switch */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
@@ -247,6 +229,19 @@ static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ HPIMSGX__cleanup(phm->adapter_index, h_owner);
+ {
+ struct hpi_message hm;
+ struct hpi_response hr;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_CLOSE);
+ hm.adapter_index = phm->adapter_index;
+ hw_entry_point(&hm, &hr);
+ }
+ hw_entry_point(phm, phr);
+ break;
+
default:
hw_entry_point(phm, phr);
break;
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index cd624f13ff8e..d8e7047512f8 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -25,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/fs.h>
#include <linux/slab.h>
@@ -161,26 +162,24 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- pa = &adapters[hm->h.adapter_index];
+ switch (hm->h.function) {
+ case HPI_SUBSYS_CREATE_ADAPTER:
+ case HPI_ADAPTER_DELETE:
+ /* Application must not use these functions! */
+ hr->h.size = sizeof(hr->h);
+ hr->h.error = HPI_ERROR_INVALID_OPERATION;
+ hr->h.function = hm->h.function;
+ uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
+ if (uncopied_bytes)
+ err = -EFAULT;
+ else
+ err = 0;
+ goto out;
+ }
+
hr->h.size = res_max_size;
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
- switch (hm->h.function) {
- case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- /* Application must not use these functions! */
- hr->h.size = sizeof(hr->h);
- hr->h.error = HPI_ERROR_INVALID_OPERATION;
- hr->h.function = hm->h.function;
- uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
- if (uncopied_bytes)
- err = -EFAULT;
- else
- err = 0;
- goto out;
-
- default:
- hpi_send_recv_f(&hm->m0, &hr->r0, file);
- }
+ hpi_send_recv_f(&hm->m0, &hr->r0, file);
} else {
u16 __user *ptr = NULL;
u32 size = 0;
@@ -188,8 +187,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
u32 adapter = hm->h.adapter_index;
+ pa = &adapters[adapter];
- if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
+ if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) {
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN,
HPI_ERROR_BAD_ADAPTER_NUMBER);
@@ -317,7 +317,7 @@ out:
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
- int err, idx, nm;
+ int idx, nm;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
@@ -351,11 +351,8 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
- HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
- idx, pci_dev->resource[idx].name,
- (unsigned long long)pci_resource_start(pci_dev, idx),
- (unsigned long long)pci_resource_end(pci_dev, idx),
- (unsigned long long)pci_resource_flags(pci_dev, idx));
+ HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
+ &pci_dev->resource[idx]);
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
@@ -395,17 +392,20 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
adapter.index = hr.u.s.adapter_index;
adapter.type = hr.u.s.adapter_type;
+
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_OPEN);
hm.adapter_index = adapter.index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- err = hpi_adapter_open(adapter.index);
- if (err)
+ if (hr.error)
goto err;
adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
- adapters[hr.u.s.adapter_index] = adapter;
+ adapters[adapter.index] = adapter;
mutex_init(&adapters[adapter.index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
@@ -440,10 +440,9 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
struct hpi_adapter *pa;
pa = pci_get_drvdata(pci_dev);
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.obj_index = pa->index;
- hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_DELETE);
+ hm.adapter_index = pa->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
index 5d69c31fe3f4..79fbee3845eb 100644
--- a/sound/pci/au88x0/au8810.h
+++ b/sound/pci/au88x0/au8810.h
@@ -4,7 +4,7 @@
#define CHIP_AU8810
-#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME "Aureal Advantage"
#define CARD_NAME_SHORT "au8810"
#define NR_ADB 0x10
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
index abbe85e4f7a9..cafdb9668a34 100644
--- a/sound/pci/au88x0/au8820.h
+++ b/sound/pci/au88x0/au8820.h
@@ -11,7 +11,7 @@
#define CHIP_AU8820
-#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex"
#define CARD_NAME_SHORT "au8820"
/* Number of ADB and WT channels */
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
index 04ece1b1c218..999b29ab34ad 100644
--- a/sound/pci/au88x0/au8830.h
+++ b/sound/pci/au88x0/au8830.h
@@ -11,7 +11,7 @@
#define CHIP_AU8830
-#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex 2"
#define CARD_NAME_SHORT "au8830"
#define NR_ADB 0x20
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index 33f0ba5559a7..c5f7ae46afef 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -44,10 +44,10 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 0x10000,
- .period_bytes_min = 0x1,
+ .period_bytes_min = 0x20,
.period_bytes_max = 0x1000,
.periods_min = 2,
- .periods_max = 32,
+ .periods_max = 1024,
};
#ifndef CHIP_AU8820
@@ -140,6 +140,9 @@ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err;
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 64);
+
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
#ifndef CHIP_AU8820
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) {
@@ -423,11 +426,11 @@ static struct snd_pcm_ops snd_vortex_playback_ops = {
*/
static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
- "AU88x0 ADB",
- "AU88x0 SPDIF",
- "AU88x0 A3D",
- "AU88x0 WT",
- "AU88x0 I2S",
+ CARD_NAME " ADB",
+ CARD_NAME " SPDIF",
+ CARD_NAME " A3D",
+ CARD_NAME " WT",
+ CARD_NAME " I2S",
};
static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
"adb",
@@ -524,7 +527,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
nr_capt, &pcm);
if (err < 0)
return err;
- strcpy(pcm->name, vortex_pcm_name[idx]);
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
chip->pcm[idx] = pcm;
// This is an evil hack, but it saves a lot of duplicated code.
VORTEX_PCM_TYPE(pcm) = idx;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 7a9401462c1c..dae4050ede5c 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -303,6 +303,9 @@ static const u32 db_table[101] = {
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
+/* EMU10K1 bass/treble db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
+
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
};
@@ -2163,6 +2166,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[i + 1];
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -2172,6 +2176,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
#define BASS_GPR 0x8c
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 05afe06e353a..9d890a5aec5a 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1729,8 +1729,6 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Master Mono Playback Volume",
"PCM Out Path & Mute",
"Mono Output Select",
- "Front Playback Switch",
- "Front Playback Volume",
"Surround Playback Switch",
"Surround Playback Volume",
"Center Playback Switch",
@@ -1879,6 +1877,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
emu->rear_ac97 = 1;
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
+ remove_ctl(card,"Front Playback Volume");
+ remove_ctl(card,"Front Playback Switch");
}
/* remove unused AC97 controls */
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
@@ -1913,6 +1913,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
+ if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
+ remove_ctl(card, "Center Playback Volume");
+ remove_ctl(card, "LFE Playback Volume");
+ remove_ctl(card, "Wave Center Playback Volume");
+ remove_ctl(card, "Wave LFE Playback Volume");
+ }
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 7c17f45d876d..ab0a6156a704 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -112,6 +112,10 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#ifdef CONFIG_SND_ES1968_RADIO
+#include <sound/tea575x-tuner.h>
+#endif
+
#define CARD_NAME "ESS Maestro1/2"
#define DRIVER_NAME "ES1968"
@@ -553,6 +557,10 @@ struct es1968 {
spinlock_t ac97_lock;
struct tasklet_struct hwvol_tq;
#endif
+
+#ifdef CONFIG_SND_ES1968_RADIO
+ struct snd_tea575x tea;
+#endif
};
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
@@ -2571,6 +2579,63 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip)
}
#endif /* CONFIG_SND_ES1968_INPUT */
+#ifdef CONFIG_SND_ES1968_RADIO
+#define GPIO_DATA 0x60
+#define IO_MASK 4 /* mask register offset from GPIO_DATA
+ bits 1=unmask write to given bit */
+#define IO_DIR 8 /* direction register offset from GPIO_DATA
+ bits 0/1=read/write direction */
+/* mask bits for GPIO lines */
+#define STR_DATA 0x0040 /* GPIO6 */
+#define STR_CLK 0x0080 /* GPIO7 */
+#define STR_WREN 0x0100 /* GPIO8 */
+#define STR_MOST 0x0200 /* GPIO9 */
+
+static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 val = 0;
+
+ val |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+ val |= (pins & TEA575X_CLK) ? STR_CLK : 0;
+ val |= (pins & TEA575X_WREN) ? STR_WREN : 0;
+
+ outw(val, io);
+}
+
+static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 val = inw(io);
+
+ return (val & STR_DATA) ? TEA575X_DATA : 0 |
+ (val & STR_MOST) ? TEA575X_MOST : 0;
+}
+
+static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 odir = inw(io + IO_DIR);
+
+ if (output) {
+ outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+ outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR);
+ } else {
+ outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK);
+ outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR);
+ }
+}
+
+static struct snd_tea575x_ops snd_es1968_tea_ops = {
+ .set_pins = snd_es1968_tea575x_set_pins,
+ .get_pins = snd_es1968_tea575x_get_pins,
+ .set_direction = snd_es1968_tea575x_set_direction,
+};
+#endif
+
static int snd_es1968_free(struct es1968 *chip)
{
#ifdef CONFIG_SND_ES1968_INPUT
@@ -2585,6 +2650,10 @@ static int snd_es1968_free(struct es1968 *chip)
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
}
+#ifdef CONFIG_SND_ES1968_RADIO
+ snd_tea575x_exit(&chip->tea);
+#endif
+
if (chip->irq >= 0)
free_irq(chip->irq, chip);
snd_es1968_free_gameport(chip);
@@ -2723,6 +2792,15 @@ static int __devinit snd_es1968_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
+#ifdef CONFIG_SND_ES1968_RADIO
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_es1968_tea_ops;
+ strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ if (!snd_tea575x_init(&chip->tea))
+ printk(KERN_INFO "es1968: detected TEA575x radio\n");
+#endif
+
*chip_ret = chip;
return 0;
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index e1baad74ea4b..eacd4901a308 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -38,7 +38,6 @@
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <sound/tea575x-tuner.h>
-#define TEA575X_RADIO 1
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -53,7 +52,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
/*
* Enable TEA575x tuner
* 1 = MediaForte 256-PCS
- * 2 = MediaForte 256-PCPR
+ * 2 = MediaForte 256-PCP
* 3 = MediaForte 64-PCR
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
@@ -67,7 +66,7 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
#define TUNER_ONLY (1<<4)
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
@@ -196,7 +195,7 @@ struct fm801 {
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
struct snd_tea575x tea;
#endif
@@ -715,310 +714,89 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
* TEA5757 radio
*/
-#ifdef TEA575X_RADIO
-
-/* 256PCS GPIO numbers */
-#define TEA_256PCS_DATA 1
-#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
-#define TEA_256PCS_BUS_CLOCK 3
-
-static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
+/* GPIO to TEA575x maps */
+struct snd_fm801_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
+static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
+ { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
+ { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
+ { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
+};
-static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
+static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
- val |= 1;
- }
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_unlock_irq(&chip->reg_lock);
+ reg &= ~(FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
- return val;
-}
+ reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
+ reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0;
+ /* WRITE_ENABLE is inverted */
+ reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
-/* 256PCPR GPIO numbers */
-#define TEA_256PCPR_BUS_CLOCK 0
-#define TEA_256PCPR_DATA 1
-#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
-
-static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
-
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
}
-static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
+static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
- val |= 1;
- }
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ return (reg & FM801_GPIO_GP(gpio.data)) ? TEA575X_DATA : 0 |
+ (reg & FM801_GPIO_GP(gpio.most)) ? TEA575X_MOST : 0;
}
-/* 64PCR GPIO numbers */
-#define TEA_64PCR_BUS_CLOCK 0
-#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
-#define TEA_64PCR_DATA 2
-
-static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
+static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
/* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
+ reg |= FM801_GPIO_GS(gpio.data) |
+ FM801_GPIO_GS(gpio.wren) |
+ FM801_GPIO_GS(gpio.clk) |
+ FM801_GPIO_GS(gpio.most);
+ if (output) {
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
+ } else {
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.most) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.most) |
+ FM801_GPIO_GP(gpio.wren);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.clk));
}
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_64PCR_DATA);
outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
- val |= 1;
- }
-
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
}
-static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
- unsigned int mute)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
-
- spin_lock_irq(&chip->reg_lock);
-
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- if (mute)
- /* 0xf800 (mute) */
- reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- else
- /* 0xf802 (unmute) */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
- {
- /* 1 = MediaForte 256-PCS */
- .write = snd_fm801_tea575x_256pcs_write,
- .read = snd_fm801_tea575x_256pcs_read,
- },
- {
- /* 2 = MediaForte 256-PCPR */
- .write = snd_fm801_tea575x_256pcpr_write,
- .read = snd_fm801_tea575x_256pcpr_read,
- },
- {
- /* 3 = MediaForte 64-PCR */
- .write = snd_fm801_tea575x_64pcr_write,
- .read = snd_fm801_tea575x_64pcr_read,
- .mute = snd_fm801_tea575x_64pcr_mute,
- }
+static struct snd_tea575x_ops snd_fm801_tea_ops = {
+ .set_pins = snd_fm801_tea575x_set_pins,
+ .get_pins = snd_fm801_tea575x_get_pins,
+ .set_direction = snd_fm801_tea575x_set_direction,
};
#endif
@@ -1371,7 +1149,7 @@ static int snd_fm801_free(struct fm801 *chip)
outw(cmdw, FM801_REG(chip, IRQ_MASK));
__end_hw:
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
snd_tea575x_exit(&chip->tea);
#endif
if (chip->irq >= 0)
@@ -1450,16 +1228,25 @@ static int __devinit snd_fm801_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_fm801_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
- chip->tea.dev_nr = tea575x_tuner >> 16;
- chip->tea.card = card;
- chip->tea.freq_fixup = 10700;
- chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
- snd_tea575x_init(&chip->tea);
- }
+ if (snd_tea575x_init(&chip->tea))
+ snd_printk(KERN_ERR "TEA575x radio not found\n");
+ } else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0)
+ /* autodetect tuner connection */
+ for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
+ chip->tea575x_tuner = tea575x_tuner;
+ if (!snd_tea575x_init(&chip->tea)) {
+ snd_printk(KERN_INFO "detected TEA575x radio type %s\n",
+ snd_fm801_tea575x_gpios[tea575x_tuner - 1].name);
+ break;
+ }
+ }
+ strlcpy(chip->tea.card, snd_fm801_tea575x_gpios[(tea575x_tuner & TUNER_TYPE_MASK) - 1].name, sizeof(chip->tea.card));
#endif
*rchip = chip;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 430f41db6044..759ade12e758 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -937,6 +937,7 @@ void snd_hda_shutup_pins(struct hda_codec *codec)
}
EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
+#ifdef SND_HDA_NEEDS_RESUME
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
static void restore_shutup_pins(struct hda_codec *codec)
{
@@ -953,6 +954,7 @@ static void restore_shutup_pins(struct hda_codec *codec)
}
codec->pins_shutup = 0;
}
+#endif
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
@@ -1329,6 +1331,7 @@ static void purify_inactive_streams(struct hda_codec *codec)
}
}
+#ifdef SND_HDA_NEEDS_RESUME
/* clean up all streams; called from suspend */
static void hda_cleanup_all_streams(struct hda_codec *codec)
{
@@ -1340,6 +1343,7 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
really_cleanup_stream(codec, p);
}
}
+#endif
/*
* amp access functions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 52928d9a72da..c82979a8cd09 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1704,11 +1704,11 @@ static void alc_apply_fixup(struct hda_codec *codec, int action)
codec->chip_name, fix->type);
break;
}
- if (!fix[id].chained)
+ if (!fix->chained)
break;
if (++depth > 10)
break;
- id = fix[id].chain_id;
+ id = fix->chain_id;
}
}
@@ -5645,6 +5645,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
static struct snd_pci_quirk beep_white_list[] = {
SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1),
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
{}
};
@@ -9863,6 +9864,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = {
SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
@@ -10699,7 +10701,6 @@ enum {
PINFIX_LENOVO_Y530,
PINFIX_PB_M5210,
PINFIX_ACER_ASPIRE_7736,
- PINFIX_GIGABYTE_880GM,
};
static const struct alc_fixup alc882_fixups[] = {
@@ -10731,13 +10732,6 @@ static const struct alc_fixup alc882_fixups[] = {
.type = ALC_FIXUP_SKU,
.v.sku = ALC_FIXUP_SKU_IGNORE,
},
- [PINFIX_GIGABYTE_880GM] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x14, 0x1114410 }, /* set as speaker */
- { }
- }
- },
};
static struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -10745,7 +10739,6 @@ static struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM),
{}
};
@@ -14868,6 +14861,23 @@ static void alc269_fixup_hweq(struct hda_codec *codec,
alc_write_coef_idx(codec, 0x1e, coef | 0x80);
}
+static void alc271_fixup_dmic(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ static struct hda_verb verbs[] = {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
+ {}
+ };
+ unsigned int cfg;
+
+ if (strcmp(codec->chip_name, "ALC271X"))
+ return;
+ cfg = snd_hda_codec_get_pincfg(codec, 0x12);
+ if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED)
+ snd_hda_sequence_write(codec, verbs);
+}
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -14876,6 +14886,7 @@ enum {
ALC269_FIXUP_ASUS_G73JW,
ALC269_FIXUP_LENOVO_EAPD,
ALC275_FIXUP_SONY_HWEQ,
+ ALC271_FIXUP_DMIC,
};
static const struct alc_fixup alc269_fixups[] = {
@@ -14929,7 +14940,11 @@ static const struct alc_fixup alc269_fixups[] = {
.v.func = alc269_fixup_hweq,
.chained = true,
.chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
- }
+ },
+ [ALC271_FIXUP_DMIC] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc271_fixup_dmic,
+ },
};
static struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -14938,6 +14953,7 @@ static struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
@@ -18782,6 +18798,8 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO),
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(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13),
SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA),
@@ -19455,7 +19473,7 @@ enum {
ALC662_FIXUP_IDEAPAD,
ALC272_FIXUP_MARIO,
ALC662_FIXUP_CZC_P10T,
- ALC662_FIXUP_GIGABYTE,
+ ALC662_FIXUP_SKU_IGNORE,
};
static const struct alc_fixup alc662_fixups[] = {
@@ -19484,20 +19502,17 @@ static const struct alc_fixup alc662_fixups[] = {
{}
}
},
- [ALC662_FIXUP_GIGABYTE] = {
- .type = ALC_FIXUP_PINS,
- .v.pins = (const struct alc_pincfg[]) {
- { 0x14, 0x1114410 }, /* set as speaker */
- { }
- }
+ [ALC662_FIXUP_SKU_IGNORE] = {
+ .type = ALC_FIXUP_SKU,
+ .v.sku = ALC_FIXUP_SKU_IGNORE,
},
};
static struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 1371b57c11e8..0997031c48d2 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1292,14 +1292,18 @@ static void notify_aa_path_ctls(struct hda_codec *codec)
{
int i;
struct snd_ctl_elem_id id;
- const char *labels[] = {"Mic", "Front Mic", "Line"};
+ const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
+ struct snd_kcontrol *ctl;
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
for (i = 0; i < ARRAY_SIZE(labels); i++) {
sprintf(id.name, "%s Playback Volume", labels[i]);
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
+ ctl = snd_hda_find_mixer_ctl(codec, id.name);
+ if (ctl)
+ snd_ctl_notify(codec->bus->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &ctl->id);
}
}
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 27709f0cd2a6..f3353b49c785 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -235,8 +235,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
+ { PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
#if 0
- { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
#endif
{ 0, }
@@ -1261,9 +1261,9 @@ static struct shortname_table {
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
+ { 0x746e, "AMD AMD8111" },
#if 0
{ 0x5455, "ALi M5455" },
- { 0x746d, "AMD AMD8111" },
#endif
{ 0 },
};
diff --git a/sound/pci/lola/Makefile b/sound/pci/lola/Makefile
new file mode 100644
index 000000000000..8178a2a59d00
--- /dev/null
+++ b/sound/pci/lola/Makefile
@@ -0,0 +1,4 @@
+snd-lola-y := lola.o lola_pcm.o lola_clock.o lola_mixer.o
+snd-lola-$(CONFIG_SND_DEBUG) += lola_proc.o
+
+obj-$(CONFIG_SND_LOLA) += snd-lola.o
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
new file mode 100644
index 000000000000..34b24286d279
--- /dev/null
+++ b/sound/pci/lola/lola.c
@@ -0,0 +1,791 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include "lola.h"
+
+/* Standard options */
+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 Lola driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram Lola driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram Lola driver.");
+
+/* Lola-specific options */
+
+/* for instance use always max granularity which is compatible
+ * with all sample rates
+ */
+static int granularity[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1)] = LOLA_GRANULARITY_MAX
+};
+
+/* below a sample_rate of 16kHz the analogue audio quality is NOT excellent */
+static int sample_rate_min[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1) ] = 16000
+};
+
+module_param_array(granularity, int, NULL, 0444);
+MODULE_PARM_DESC(granularity, "Granularity value");
+module_param_array(sample_rate_min, int, NULL, 0444);
+MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
+
+/*
+ */
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Digigram, Lola}}");
+MODULE_DESCRIPTION("Digigram Lola driver");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int debug;
+module_param(debug, int, 0644);
+#define verbose_debug(fmt, args...) \
+ do { if (debug > 1) printk(KERN_DEBUG SFX fmt, ##args); } while (0)
+#else
+#define verbose_debug(fmt, args...)
+#endif
+
+/*
+ * pseudo-codec read/write via CORB/RIRB
+ */
+
+static int corb_send_verb(struct lola *chip, unsigned int nid,
+ unsigned int verb, unsigned int data,
+ unsigned int extdata)
+{
+ unsigned long flags;
+ int ret = -EIO;
+
+ chip->last_cmd_nid = nid;
+ chip->last_verb = verb;
+ chip->last_data = data;
+ chip->last_extdata = extdata;
+ data |= (nid << 20) | (verb << 8);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
+ unsigned int wp = chip->corb.wp + 1;
+ wp %= LOLA_CORB_ENTRIES;
+ chip->corb.wp = wp;
+ chip->corb.buf[wp * 2] = cpu_to_le32(data);
+ chip->corb.buf[wp * 2 + 1] = cpu_to_le32(extdata);
+ lola_writew(chip, BAR0, CORBWP, wp);
+ chip->rirb.cmds++;
+ smp_wmb();
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return ret;
+}
+
+static void lola_queue_unsol_event(struct lola *chip, unsigned int res,
+ unsigned int res_ex)
+{
+ lola_update_ext_clock_freq(chip, res);
+}
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void lola_update_rirb(struct lola *chip)
+{
+ unsigned int rp, wp;
+ u32 res, res_ex;
+
+ wp = lola_readw(chip, BAR0, RIRBWP);
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= LOLA_CORB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ if (res_ex & LOLA_RIRB_EX_UNSOL_EV)
+ lola_queue_unsol_event(chip, res, res_ex);
+ else if (chip->rirb.cmds) {
+ chip->res = res;
+ chip->res_ex = res_ex;
+ smp_wmb();
+ chip->rirb.cmds--;
+ }
+ }
+}
+
+static int rirb_get_response(struct lola *chip, unsigned int *val,
+ unsigned int *extval)
+{
+ unsigned long timeout;
+
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+ for (;;) {
+ if (chip->polling_mode) {
+ spin_lock_irq(&chip->reg_lock);
+ lola_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ if (!chip->rirb.cmds) {
+ *val = chip->res;
+ if (extval)
+ *extval = chip->res_ex;
+ verbose_debug("get_response: %x, %x\n",
+ chip->res, chip->res_ex);
+ if (chip->res_ex & LOLA_RIRB_EX_ERROR) {
+ printk(KERN_WARNING SFX "RIRB ERROR: "
+ "NID=%x, verb=%x, data=%x, ext=%x\n",
+ chip->last_cmd_nid,
+ chip->last_verb, chip->last_data,
+ chip->last_extdata);
+ return -EIO;
+ }
+ return 0;
+ }
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(20);
+ cond_resched();
+ }
+ printk(KERN_WARNING SFX "RIRB response error\n");
+ if (!chip->polling_mode) {
+ printk(KERN_WARNING SFX "switching to polling mode\n");
+ chip->polling_mode = 1;
+ goto again;
+ }
+ return -EIO;
+}
+
+/* aynchronous write of a codec verb with data */
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata)
+{
+ verbose_debug("codec_write NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ return corb_send_verb(chip, nid, verb, data, extdata);
+}
+
+/* write a codec verb with data and read the returned status */
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval)
+{
+ int err;
+
+ verbose_debug("codec_read NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ err = corb_send_verb(chip, nid, verb, data, extdata);
+ if (err < 0)
+ return err;
+ err = rirb_get_response(chip, val, extval);
+ return err;
+}
+
+/* flush all pending codec writes */
+int lola_codec_flush(struct lola *chip)
+{
+ unsigned int tmp;
+ return rirb_get_response(chip, &tmp, NULL);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t lola_interrupt(int irq, void *dev_id)
+{
+ struct lola *chip = dev_id;
+ unsigned int notify_ins, notify_outs, error_ins, error_outs;
+ int handled = 0;
+ int i;
+
+ notify_ins = notify_outs = error_ins = error_outs = 0;
+ spin_lock(&chip->reg_lock);
+ for (;;) {
+ unsigned int status, in_sts, out_sts;
+ unsigned int reg;
+
+ status = lola_readl(chip, BAR1, DINTSTS);
+ if (!status || status == -1)
+ break;
+
+ in_sts = lola_readl(chip, BAR1, DIINTSTS);
+ out_sts = lola_readl(chip, BAR1, DOINTSTS);
+
+ /* clear Input Interrupts */
+ for (i = 0; in_sts && i < chip->pcm[CAPT].num_streams; i++) {
+ if (!(in_sts & (1 << i)))
+ continue;
+ in_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_ins |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_ins |= (1 << i);
+ /* clear */
+ lola_dsd_write(chip, i, STS, reg);
+ }
+
+ /* clear Output Interrupts */
+ for (i = 0; out_sts && i < chip->pcm[PLAY].num_streams; i++) {
+ if (!(out_sts & (1 << i)))
+ continue;
+ out_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i + MAX_STREAM_IN_COUNT, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_outs |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_outs |= (1 << i);
+ lola_dsd_write(chip, i + MAX_STREAM_IN_COUNT, STS, reg);
+ }
+
+ if (status & LOLA_DINT_CTRL) {
+ unsigned char rbsts; /* ring status is byte access */
+ rbsts = lola_readb(chip, BAR0, RIRBSTS);
+ rbsts &= LOLA_RIRB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, RIRBSTS, rbsts);
+ rbsts = lola_readb(chip, BAR0, CORBSTS);
+ rbsts &= LOLA_CORB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, CORBSTS, rbsts);
+
+ lola_update_rirb(chip);
+ }
+
+ if (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)) {
+ /* clear global fifo error interrupt */
+ lola_writel(chip, BAR1, DINTSTS,
+ (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)));
+ }
+ handled = 1;
+ }
+ spin_unlock(&chip->reg_lock);
+
+ lola_pcm_update(chip, &chip->pcm[CAPT], notify_ins);
+ lola_pcm_update(chip, &chip->pcm[PLAY], notify_outs);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * controller
+ */
+static int reset_controller(struct lola *chip)
+{
+ unsigned int gctl = lola_readl(chip, BAR0, GCTL);
+ unsigned long end_time;
+
+ if (gctl) {
+ /* to be sure */
+ lola_writel(chip, BAR1, BOARD_MODE, 0);
+ return 0;
+ }
+
+ chip->cold_reset = 1;
+ lola_writel(chip, BAR0, GCTL, LOLA_GCTL_RESET);
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ msleep(1);
+ gctl = lola_readl(chip, BAR0, GCTL);
+ if (gctl)
+ break;
+ } while (time_before(jiffies, end_time));
+ if (!gctl) {
+ printk(KERN_ERR SFX "cannot reset controller\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void lola_irq_enable(struct lola *chip)
+{
+ unsigned int val;
+
+ /* enalbe all I/O streams */
+ val = (1 << chip->pcm[PLAY].num_streams) - 1;
+ lola_writel(chip, BAR1, DOINTCTL, val);
+ val = (1 << chip->pcm[CAPT].num_streams) - 1;
+ lola_writel(chip, BAR1, DIINTCTL, val);
+
+ /* enable global irqs */
+ val = LOLA_DINT_GLOBAL | LOLA_DINT_CTRL | LOLA_DINT_FIFOERR |
+ LOLA_DINT_MUERR;
+ lola_writel(chip, BAR1, DINTCTL, val);
+}
+
+static void lola_irq_disable(struct lola *chip)
+{
+ lola_writel(chip, BAR1, DINTCTL, 0);
+ lola_writel(chip, BAR1, DIINTCTL, 0);
+ lola_writel(chip, BAR1, DOINTCTL, 0);
+}
+
+static int setup_corb_rirb(struct lola *chip)
+{
+ int err;
+ unsigned char tmp;
+ unsigned long end_time;
+
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->rb);
+ if (err < 0)
+ return err;
+
+ chip->corb.addr = chip->rb.addr;
+ chip->corb.buf = (u32 *)chip->rb.area;
+ chip->rirb.addr = chip->rb.addr + 2048;
+ chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ if (!lola_readb(chip, BAR0, RIRBCTL) &&
+ !lola_readb(chip, BAR0, CORBCTL))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_time));
+
+ /* CORB set up */
+ lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
+ lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
+ /* set the corb size to 256 entries */
+ lola_writeb(chip, BAR0, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ lola_writew(chip, BAR0, CORBWP, 0);
+ /* reset the corb hw read pointer */
+ lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
+ /* enable corb dma */
+ lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, CORBSTS, tmp);
+ chip->corb.wp = 0;
+
+ /* RIRB set up */
+ lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
+ lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
+ /* set the rirb size to 256 entries */
+ lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
+ /* set N=1, get RIRB response interrupt for new entry */
+ lola_writew(chip, BAR0, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, RIRBSTS, tmp);
+ chip->rirb.rp = chip->rirb.cmds = 0;
+
+ return 0;
+}
+
+static void stop_corb_rirb(struct lola *chip)
+{
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+}
+
+static void lola_reset_setups(struct lola *chip)
+{
+ /* update the granularity */
+ lola_set_granularity(chip, chip->granularity, true);
+ /* update the sample clock */
+ lola_set_clock_index(chip, chip->clock.cur_index);
+ /* enable unsolicited events of the clock widget */
+ lola_enable_clock_events(chip);
+ /* update the analog gains */
+ lola_setup_all_analog_gains(chip, CAPT, false); /* input, update */
+ /* update SRC configuration if applicable */
+ lola_set_src_config(chip, chip->input_src_mask, false);
+ /* update the analog outputs */
+ lola_setup_all_analog_gains(chip, PLAY, false); /* output, update */
+}
+
+static int lola_parse_tree(struct lola *chip)
+{
+ unsigned int val;
+ int nid, err;
+
+ err = lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read VENDOR_ID\n");
+ return err;
+ }
+ val >>= 16;
+ if (val != 0x1369) {
+ printk(KERN_ERR SFX "Unknown codec vendor 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid);
+ return err;
+ }
+ if (val != 1) {
+ printk(KERN_ERR SFX "Unknown function type %d\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read SPECCAPS\n");
+ return err;
+ }
+ chip->lola_caps = val;
+ chip->pin[CAPT].num_pins = LOLA_AFG_INPUT_PIN_COUNT(chip->lola_caps);
+ chip->pin[PLAY].num_pins = LOLA_AFG_OUTPUT_PIN_COUNT(chip->lola_caps);
+ snd_printdd(SFX "speccaps=0x%x, pins in=%d, out=%d\n",
+ chip->lola_caps,
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+
+ if (chip->pin[CAPT].num_pins > MAX_AUDIO_INOUT_COUNT ||
+ chip->pin[PLAY].num_pins > MAX_AUDIO_INOUT_COUNT) {
+ printk(KERN_ERR SFX "Invalid Lola-spec caps 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ nid = 0x02;
+ err = lola_init_pcm(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pcm(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ err = lola_init_pins(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pins(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_clock_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_mixer_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+
+ /* enable unsolicited events of the clock widget */
+ err = lola_enable_clock_events(chip);
+ if (err < 0)
+ return err;
+
+ /* if last ResetController was not a ColdReset, we don't know
+ * the state of the card; initialize here again
+ */
+ if (!chip->cold_reset) {
+ lola_reset_setups(chip);
+ chip->cold_reset = 1;
+ } else {
+ /* set the granularity if it is not the default */
+ if (chip->granularity != LOLA_GRANULARITY_MIN)
+ lola_set_granularity(chip, chip->granularity, true);
+ }
+
+ return 0;
+}
+
+static void lola_stop_hw(struct lola *chip)
+{
+ stop_corb_rirb(chip);
+ lola_irq_disable(chip);
+}
+
+static void lola_free(struct lola *chip)
+{
+ if (chip->initialized)
+ lola_stop_hw(chip);
+ lola_free_pcm(chip);
+ lola_free_mixer(chip);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->bar[0].remap_addr)
+ iounmap(chip->bar[0].remap_addr);
+ if (chip->bar[1].remap_addr)
+ iounmap(chip->bar[1].remap_addr);
+ if (chip->rb.area)
+ snd_dma_free_pages(&chip->rb);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+}
+
+static int lola_dev_free(struct snd_device *device)
+{
+ lola_free(device->device_data);
+ return 0;
+}
+
+static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci,
+ int dev, struct lola **rchip)
+{
+ struct lola *chip;
+ int err;
+ unsigned int dever;
+ static struct snd_device_ops ops = {
+ .dev_free = lola_dev_free,
+ };
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ chip->granularity = granularity[dev];
+ switch (chip->granularity) {
+ case 8:
+ chip->sample_rate_max = 48000;
+ break;
+ case 16:
+ chip->sample_rate_max = 96000;
+ break;
+ case 32:
+ chip->sample_rate_max = 192000;
+ break;
+ default:
+ snd_printk(KERN_WARNING SFX
+ "Invalid granularity %d, reset to %d\n",
+ chip->granularity, LOLA_GRANULARITY_MAX);
+ chip->granularity = LOLA_GRANULARITY_MAX;
+ chip->sample_rate_max = 192000;
+ break;
+ }
+ chip->sample_rate_min = sample_rate_min[dev];
+ if (chip->sample_rate_min > chip->sample_rate_max) {
+ snd_printk(KERN_WARNING SFX
+ "Invalid sample_rate_min %d, reset to 16000\n",
+ chip->sample_rate_min);
+ chip->sample_rate_min = 16000;
+ }
+
+ err = pci_request_regions(pci, DRVNAME);
+ if (err < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ chip->bar[0].addr = pci_resource_start(pci, 0);
+ chip->bar[0].remap_addr = pci_ioremap_bar(pci, 0);
+ chip->bar[1].addr = pci_resource_start(pci, 2);
+ chip->bar[1].remap_addr = pci_ioremap_bar(pci, 2);
+ if (!chip->bar[0].remap_addr || !chip->bar[1].remap_addr) {
+ snd_printk(KERN_ERR SFX "ioremap error\n");
+ err = -ENXIO;
+ goto errout;
+ }
+
+ pci_set_master(pci);
+
+ err = reset_controller(chip);
+ if (err < 0)
+ goto errout;
+
+ if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED,
+ DRVNAME, chip)) {
+ printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
+ err = -EBUSY;
+ goto errout;
+ }
+ chip->irq = pci->irq;
+ synchronize_irq(chip->irq);
+
+ dever = lola_readl(chip, BAR1, DEVER);
+ chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
+ chip->pcm[PLAY].num_streams = (dever >> 10) & 0x3ff;
+ chip->version = (dever >> 24) & 0xff;
+ snd_printdd(SFX "streams in=%d, out=%d, version=0x%x\n",
+ chip->pcm[CAPT].num_streams, chip->pcm[PLAY].num_streams,
+ chip->version);
+
+ /* Test LOLA_BAR1_DEVER */
+ if (chip->pcm[CAPT].num_streams > MAX_STREAM_IN_COUNT ||
+ chip->pcm[PLAY].num_streams > MAX_STREAM_OUT_COUNT ||
+ (!chip->pcm[CAPT].num_streams &&
+ !chip->pcm[PLAY].num_streams)) {
+ printk(KERN_ERR SFX "invalid DEVER = %x\n", dever);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ err = setup_corb_rirb(chip);
+ if (err < 0)
+ goto errout;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
+ goto errout;
+ }
+
+ strcpy(card->driver, "Lola");
+ strlcpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->bar[0].addr, chip->irq);
+ strcpy(card->mixername, card->shortname);
+
+ lola_irq_enable(chip);
+
+ chip->initialized = 1;
+ *rchip = chip;
+ return 0;
+
+ errout:
+ lola_free(chip);
+ return err;
+}
+
+static int __devinit lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct lola *chip;
+ 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 < 0) {
+ snd_printk(KERN_ERR SFX "Error creating card!\n");
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ err = lola_create(card, pci, dev, &chip);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ err = lola_parse_tree(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = lola_create_pcm(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = lola_create_mixer(chip);
+ if (err < 0)
+ goto out_free;
+
+ lola_proc_debug_new(chip);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto out_free;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return err;
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit lola_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static DEFINE_PCI_DEVICE_TABLE(lola_ids) = {
+ { PCI_VDEVICE(DIGIGRAM, 0x0001) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lola_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+ .name = DRVNAME,
+ .id_table = lola_ids,
+ .probe = lola_probe,
+ .remove = __devexit_p(lola_remove),
+};
+
+static int __init alsa_card_lola_init(void)
+{
+ return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_lola_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_lola_init)
+module_exit(alsa_card_lola_exit)
diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h
new file mode 100644
index 000000000000..d5708e29b16d
--- /dev/null
+++ b/sound/pci/lola/lola.h
@@ -0,0 +1,527 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOLA_H
+#define _LOLA_H
+
+#define DRVNAME "snd-lola"
+#define SFX DRVNAME ": "
+
+/*
+ * Lola HD Audio Registers BAR0
+ */
+#define LOLA_BAR0_GCAP 0x00
+#define LOLA_BAR0_VMIN 0x02
+#define LOLA_BAR0_VMAJ 0x03
+#define LOLA_BAR0_OUTPAY 0x04
+#define LOLA_BAR0_INPAY 0x06
+#define LOLA_BAR0_GCTL 0x08
+#define LOLA_BAR0_WAKEEN 0x0c
+#define LOLA_BAR0_STATESTS 0x0e
+#define LOLA_BAR0_GSTS 0x10
+#define LOLA_BAR0_OUTSTRMPAY 0x18
+#define LOLA_BAR0_INSTRMPAY 0x1a
+#define LOLA_BAR0_INTCTL 0x20
+#define LOLA_BAR0_INTSTS 0x24
+#define LOLA_BAR0_WALCLK 0x30
+#define LOLA_BAR0_SSYNC 0x38
+
+#define LOLA_BAR0_CORBLBASE 0x40
+#define LOLA_BAR0_CORBUBASE 0x44
+#define LOLA_BAR0_CORBWP 0x48 /* no ULONG access */
+#define LOLA_BAR0_CORBRP 0x4a /* no ULONG access */
+#define LOLA_BAR0_CORBCTL 0x4c /* no ULONG access */
+#define LOLA_BAR0_CORBSTS 0x4d /* UCHAR access only */
+#define LOLA_BAR0_CORBSIZE 0x4e /* no ULONG access */
+
+#define LOLA_BAR0_RIRBLBASE 0x50
+#define LOLA_BAR0_RIRBUBASE 0x54
+#define LOLA_BAR0_RIRBWP 0x58
+#define LOLA_BAR0_RINTCNT 0x5a /* no ULONG access */
+#define LOLA_BAR0_RIRBCTL 0x5c
+#define LOLA_BAR0_RIRBSTS 0x5d /* UCHAR access only */
+#define LOLA_BAR0_RIRBSIZE 0x5e /* no ULONG access */
+
+#define LOLA_BAR0_ICW 0x60
+#define LOLA_BAR0_IRR 0x64
+#define LOLA_BAR0_ICS 0x68
+#define LOLA_BAR0_DPLBASE 0x70
+#define LOLA_BAR0_DPUBASE 0x74
+
+/* stream register offsets from stream base 0x80 */
+#define LOLA_BAR0_SD0_OFFSET 0x80
+#define LOLA_REG0_SD_CTL 0x00
+#define LOLA_REG0_SD_STS 0x03
+#define LOLA_REG0_SD_LPIB 0x04
+#define LOLA_REG0_SD_CBL 0x08
+#define LOLA_REG0_SD_LVI 0x0c
+#define LOLA_REG0_SD_FIFOW 0x0e
+#define LOLA_REG0_SD_FIFOSIZE 0x10
+#define LOLA_REG0_SD_FORMAT 0x12
+#define LOLA_REG0_SD_BDLPL 0x18
+#define LOLA_REG0_SD_BDLPU 0x1c
+
+/*
+ * Lola Digigram Registers BAR1
+ */
+#define LOLA_BAR1_FPGAVER 0x00
+#define LOLA_BAR1_DEVER 0x04
+#define LOLA_BAR1_UCBMV 0x08
+#define LOLA_BAR1_JTAG 0x0c
+#define LOLA_BAR1_UARTRX 0x10
+#define LOLA_BAR1_UARTTX 0x14
+#define LOLA_BAR1_UARTCR 0x18
+#define LOLA_BAR1_NVRAMVER 0x1c
+#define LOLA_BAR1_CTRLSPI 0x20
+#define LOLA_BAR1_DSPI 0x24
+#define LOLA_BAR1_AISPI 0x28
+#define LOLA_BAR1_GRAN 0x2c
+
+#define LOLA_BAR1_DINTCTL 0x80
+#define LOLA_BAR1_DIINTCTL 0x84
+#define LOLA_BAR1_DOINTCTL 0x88
+#define LOLA_BAR1_LRC 0x90
+#define LOLA_BAR1_DINTSTS 0x94
+#define LOLA_BAR1_DIINTSTS 0x98
+#define LOLA_BAR1_DOINTSTS 0x9c
+
+#define LOLA_BAR1_DSD0_OFFSET 0xa0
+#define LOLA_BAR1_DSD_SIZE 0x18
+
+#define LOLA_BAR1_DSDnSTS 0x00
+#define LOLA_BAR1_DSDnLPIB 0x04
+#define LOLA_BAR1_DSDnCTL 0x08
+#define LOLA_BAR1_DSDnLVI 0x0c
+#define LOLA_BAR1_DSDnBDPL 0x10
+#define LOLA_BAR1_DSDnBDPU 0x14
+
+#define LOLA_BAR1_SSYNC 0x03e8
+
+#define LOLA_BAR1_BOARD_CTRL 0x0f00
+#define LOLA_BAR1_BOARD_MODE 0x0f02
+
+#define LOLA_BAR1_SOURCE_GAIN_ENABLE 0x1000
+#define LOLA_BAR1_DEST00_MIX_GAIN_ENABLE 0x1004
+#define LOLA_BAR1_DEST31_MIX_GAIN_ENABLE 0x1080
+#define LOLA_BAR1_SOURCE00_01_GAIN 0x1084
+#define LOLA_BAR1_SOURCE30_31_GAIN 0x10c0
+#define LOLA_BAR1_SOURCE_GAIN(src) \
+ (LOLA_BAR1_SOURCE00_01_GAIN + (src) * 2)
+#define LOLA_BAR1_DEST00_MIX00_01_GAIN 0x10c4
+#define LOLA_BAR1_DEST00_MIX30_31_GAIN 0x1100
+#define LOLA_BAR1_DEST01_MIX00_01_GAIN 0x1104
+#define LOLA_BAR1_DEST01_MIX30_31_GAIN 0x1140
+#define LOLA_BAR1_DEST31_MIX00_01_GAIN 0x1884
+#define LOLA_BAR1_DEST31_MIX30_31_GAIN 0x18c0
+#define LOLA_BAR1_MIX_GAIN(dest, mix) \
+ (LOLA_BAR1_DEST00_MIX00_01_GAIN + (dest) * 0x40 + (mix) * 2)
+#define LOLA_BAR1_ANALOG_CLIP_IN 0x18c4
+#define LOLA_BAR1_PEAKMETERS_SOURCE00_01 0x18c8
+#define LOLA_BAR1_PEAKMETERS_SOURCE30_31 0x1904
+#define LOLA_BAR1_PEAKMETERS_SOURCE(src) \
+ (LOLA_BAR1_PEAKMETERS_SOURCE00_01 + (src) * 2)
+#define LOLA_BAR1_PEAKMETERS_DEST00_01 0x1908
+#define LOLA_BAR1_PEAKMETERS_DEST30_31 0x1944
+#define LOLA_BAR1_PEAKMETERS_DEST(dest) \
+ (LOLA_BAR1_PEAKMETERS_DEST00_01 + (dest) * 2)
+#define LOLA_BAR1_PEAKMETERS_AGC00_01 0x1948
+#define LOLA_BAR1_PEAKMETERS_AGC14_15 0x1964
+#define LOLA_BAR1_PEAKMETERS_AGC(x) \
+ (LOLA_BAR1_PEAKMETERS_AGC00_01 + (x) * 2)
+
+/* GCTL reset bit */
+#define LOLA_GCTL_RESET (1 << 0)
+/* GCTL unsolicited response enable bit */
+#define LOLA_GCTL_UREN (1 << 8)
+
+/* CORB/RIRB control, read/write pointer */
+#define LOLA_RBCTL_DMA_EN 0x02 /* enable DMA */
+#define LOLA_RBCTL_IRQ_EN 0x01 /* enable IRQ */
+#define LOLA_RBRWP_CLR 0x8000 /* read/write pointer clear */
+
+#define LOLA_RIRB_EX_UNSOL_EV 0x40000000
+#define LOLA_RIRB_EX_ERROR 0x80000000
+
+/* CORB int mask: CMEI[0] */
+#define LOLA_CORB_INT_CMEI 0x01
+#define LOLA_CORB_INT_MASK LOLA_CORB_INT_CMEI
+
+/* RIRB int mask: overrun[2], response[0] */
+#define LOLA_RIRB_INT_RESPONSE 0x01
+#define LOLA_RIRB_INT_OVERRUN 0x04
+#define LOLA_RIRB_INT_MASK (LOLA_RIRB_INT_RESPONSE | LOLA_RIRB_INT_OVERRUN)
+
+/* DINTCTL and DINTSTS */
+#define LOLA_DINT_GLOBAL 0x80000000 /* global interrupt enable bit */
+#define LOLA_DINT_CTRL 0x40000000 /* controller interrupt enable bit */
+#define LOLA_DINT_FIFOERR 0x20000000 /* global fifo error enable bit */
+#define LOLA_DINT_MUERR 0x10000000 /* global microcontroller underrun error */
+
+/* DSDnCTL bits */
+#define LOLA_DSD_CTL_SRST 0x01 /* stream reset bit */
+#define LOLA_DSD_CTL_SRUN 0x02 /* stream DMA start bit */
+#define LOLA_DSD_CTL_IOCE 0x04 /* interrupt on completion enable */
+#define LOLA_DSD_CTL_DEIE 0x10 /* descriptor error interrupt enable */
+#define LOLA_DSD_CTL_VLRCV 0x20 /* valid LRCountValue information in bits 8..31 */
+#define LOLA_LRC_MASK 0xffffff00
+
+/* DSDnSTS */
+#define LOLA_DSD_STS_BCIS 0x04 /* buffer completion interrupt status */
+#define LOLA_DSD_STS_DESE 0x10 /* descriptor error interrupt */
+#define LOLA_DSD_STS_FIFORDY 0x20 /* fifo ready */
+
+#define LOLA_CORB_ENTRIES 256
+
+#define MAX_STREAM_IN_COUNT 16
+#define MAX_STREAM_OUT_COUNT 16
+#define MAX_STREAM_COUNT 16
+#define MAX_PINS MAX_STREAM_COUNT
+#define MAX_STREAM_BUFFER_COUNT 16
+#define MAX_AUDIO_INOUT_COUNT 16
+
+#define LOLA_CLOCK_TYPE_INTERNAL 0
+#define LOLA_CLOCK_TYPE_AES 1
+#define LOLA_CLOCK_TYPE_AES_SYNC 2
+#define LOLA_CLOCK_TYPE_WORDCLOCK 3
+#define LOLA_CLOCK_TYPE_ETHERSOUND 4
+#define LOLA_CLOCK_TYPE_VIDEO 5
+
+#define LOLA_CLOCK_FORMAT_NONE 0
+#define LOLA_CLOCK_FORMAT_NTSC 1
+#define LOLA_CLOCK_FORMAT_PAL 2
+
+#define MAX_SAMPLE_CLOCK_COUNT 48
+
+/* parameters used with mixer widget's mixer capabilities */
+#define LOLA_PEAK_METER_CAN_AGC_MASK 1
+#define LOLA_PEAK_METER_CAN_ANALOG_CLIP_MASK 2
+
+struct lola_bar {
+ unsigned long addr;
+ void __iomem *remap_addr;
+};
+
+/* CORB/RIRB */
+struct lola_rb {
+ u32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds; /* number of pending requests */
+};
+
+/* Pin widget setup */
+struct lola_pin {
+ unsigned int nid;
+ bool is_analog;
+ unsigned int amp_mute;
+ unsigned int amp_step_size;
+ unsigned int amp_num_steps;
+ unsigned int amp_offset;
+ unsigned int max_level;
+ unsigned int config_default_reg;
+ unsigned int fixed_gain_list_len;
+ unsigned int cur_gain_step;
+};
+
+struct lola_pin_array {
+ unsigned int num_pins;
+ unsigned int num_analog_pins;
+ struct lola_pin pins[MAX_PINS];
+};
+
+/* Clock widget setup */
+struct lola_sample_clock {
+ unsigned int type;
+ unsigned int format;
+ unsigned int freq;
+};
+
+struct lola_clock_widget {
+ unsigned int nid;
+ unsigned int items;
+ unsigned int cur_index;
+ unsigned int cur_freq;
+ bool cur_valid;
+ struct lola_sample_clock sample_clock[MAX_SAMPLE_CLOCK_COUNT];
+ unsigned int idx_lookup[MAX_SAMPLE_CLOCK_COUNT];
+};
+
+#define LOLA_MIXER_DIM 32
+struct lola_mixer_array {
+ u32 src_gain_enable;
+ u32 dest_mix_gain_enable[LOLA_MIXER_DIM];
+ u16 src_gain[LOLA_MIXER_DIM];
+ u16 dest_mix_gain[LOLA_MIXER_DIM][LOLA_MIXER_DIM];
+};
+
+/* Mixer widget setup */
+struct lola_mixer_widget {
+ unsigned int nid;
+ unsigned int caps;
+ struct lola_mixer_array __user *array;
+ struct lola_mixer_array *array_saved;
+ unsigned int src_stream_outs;
+ unsigned int src_phys_ins;
+ unsigned int dest_stream_ins;
+ unsigned int dest_phys_outs;
+ unsigned int src_stream_out_ofs;
+ unsigned int dest_phys_out_ofs;
+ unsigned int src_mask;
+ unsigned int dest_mask;
+};
+
+/* Audio stream */
+struct lola_stream {
+ unsigned int nid; /* audio widget NID */
+ unsigned int index; /* array index */
+ unsigned int dsd; /* DSD index */
+ bool can_float;
+ struct snd_pcm_substream *substream; /* assigned PCM substream */
+ struct lola_stream *master; /* master stream (for multi-channel) */
+
+ /* buffer setup */
+ unsigned int bufsize;
+ unsigned int period_bytes;
+ unsigned int frags;
+
+ /* format + channel setup */
+ unsigned int format_verb;
+
+ /* flags */
+ unsigned int opened:1;
+ unsigned int prepared:1;
+ unsigned int paused:1;
+ unsigned int running:1;
+};
+
+#define PLAY SNDRV_PCM_STREAM_PLAYBACK
+#define CAPT SNDRV_PCM_STREAM_CAPTURE
+
+struct lola_pcm {
+ unsigned int num_streams;
+ struct snd_dma_buffer bdl; /* BDL buffer */
+ struct lola_stream streams[MAX_STREAM_COUNT];
+};
+
+/* card instance */
+struct lola {
+ struct snd_card *card;
+ struct pci_dev *pci;
+
+ /* pci resources */
+ struct lola_bar bar[2];
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct mutex open_mutex;
+
+ /* CORB/RIRB */
+ struct lola_rb corb;
+ struct lola_rb rirb;
+ unsigned int res, res_ex; /* last read values */
+ /* last command (for debugging) */
+ unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
+
+ /* CORB/RIRB buffers */
+ struct snd_dma_buffer rb;
+
+ /* unsolicited events */
+ unsigned int last_unsol_res;
+
+ /* streams */
+ struct lola_pcm pcm[2];
+
+ /* input src */
+ unsigned int input_src_caps_mask;
+ unsigned int input_src_mask;
+
+ /* pins */
+ struct lola_pin_array pin[2];
+
+ /* clock */
+ struct lola_clock_widget clock;
+ int ref_count_rate;
+ unsigned int sample_rate;
+
+ /* mixer */
+ struct lola_mixer_widget mixer;
+
+ /* hw info */
+ unsigned int version;
+ unsigned int lola_caps;
+
+ /* parameters */
+ unsigned int granularity;
+ unsigned int sample_rate_min;
+ unsigned int sample_rate_max;
+
+ /* flags */
+ unsigned int initialized:1;
+ unsigned int cold_reset:1;
+ unsigned int polling_mode:1;
+
+ /* for debugging */
+ unsigned int debug_res;
+ unsigned int debug_res_ex;
+};
+
+#define BAR0 0
+#define BAR1 1
+
+/* Helper macros */
+#define lola_readl(chip, idx, name) \
+ readl((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readw(chip, idx, name) \
+ readw((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readb(chip, idx, name) \
+ readb((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writel(chip, idx, name, val) \
+ writel((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writew(chip, idx, name, val) \
+ writew((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writeb(chip, idx, name, val) \
+ writeb((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+
+#define lola_dsd_read(chip, dsd, name) \
+ readl((chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+#define lola_dsd_write(chip, dsd, name, val) \
+ writel((val), (chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+
+/* GET verbs HDAudio */
+#define LOLA_VERB_GET_STREAM_FORMAT 0xa00
+#define LOLA_VERB_GET_AMP_GAIN_MUTE 0xb00
+#define LOLA_VERB_PARAMETERS 0xf00
+#define LOLA_VERB_GET_POWER_STATE 0xf05
+#define LOLA_VERB_GET_CONV 0xf06
+#define LOLA_VERB_GET_UNSOLICITED_RESPONSE 0xf08
+#define LOLA_VERB_GET_DIGI_CONVERT_1 0xf0d
+#define LOLA_VERB_GET_CONFIG_DEFAULT 0xf1c
+#define LOLA_VERB_GET_SUBSYSTEM_ID 0xf20
+/* GET verbs Digigram */
+#define LOLA_VERB_GET_FIXED_GAIN 0xfc0
+#define LOLA_VERB_GET_GAIN_SELECT 0xfc1
+#define LOLA_VERB_GET_MAX_LEVEL 0xfc2
+#define LOLA_VERB_GET_CLOCK_LIST 0xfc3
+#define LOLA_VERB_GET_CLOCK_SELECT 0xfc4
+#define LOLA_VERB_GET_CLOCK_STATUS 0xfc5
+
+/* SET verbs HDAudio */
+#define LOLA_VERB_SET_STREAM_FORMAT 0x200
+#define LOLA_VERB_SET_AMP_GAIN_MUTE 0x300
+#define LOLA_VERB_SET_POWER_STATE 0x705
+#define LOLA_VERB_SET_CHANNEL_STREAMID 0x706
+#define LOLA_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define LOLA_VERB_SET_DIGI_CONVERT_1 0x70d
+/* SET verbs Digigram */
+#define LOLA_VERB_SET_GAIN_SELECT 0xf81
+#define LOLA_VERB_SET_CLOCK_SELECT 0xf84
+#define LOLA_VERB_SET_GRANULARITY_STEPS 0xf86
+#define LOLA_VERB_SET_SOURCE_GAIN 0xf87
+#define LOLA_VERB_SET_MIX_GAIN 0xf88
+#define LOLA_VERB_SET_DESTINATION_GAIN 0xf89
+#define LOLA_VERB_SET_SRC 0xf8a
+
+/* Parameter IDs used with LOLA_VERB_PARAMETERS */
+#define LOLA_PAR_VENDOR_ID 0x00
+#define LOLA_PAR_FUNCTION_TYPE 0x05
+#define LOLA_PAR_AUDIO_WIDGET_CAP 0x09
+#define LOLA_PAR_PCM 0x0a
+#define LOLA_PAR_STREAM_FORMATS 0x0b
+#define LOLA_PAR_PIN_CAP 0x0c
+#define LOLA_PAR_AMP_IN_CAP 0x0d
+#define LOLA_PAR_CONNLIST_LEN 0x0e
+#define LOLA_PAR_POWER_STATE 0x0f
+#define LOLA_PAR_GPIO_CAP 0x11
+#define LOLA_PAR_AMP_OUT_CAP 0x12
+#define LOLA_PAR_SPECIFIC_CAPS 0x80
+#define LOLA_PAR_FIXED_GAIN_LIST 0x81
+
+/* extract results of LOLA_PAR_SPECIFIC_CAPS */
+#define LOLA_AFG_MIXER_WIDGET_PRESENT(res) ((res & (1 << 21)) != 0)
+#define LOLA_AFG_CLOCK_WIDGET_PRESENT(res) ((res & (1 << 20)) != 0)
+#define LOLA_AFG_INPUT_PIN_COUNT(res) ((res >> 10) & 0x2ff)
+#define LOLA_AFG_OUTPUT_PIN_COUNT(res) ((res) & 0x2ff)
+
+/* extract results of LOLA_PAR_AMP_IN_CAP / LOLA_PAR_AMP_OUT_CAP */
+#define LOLA_AMP_MUTE_CAPABLE(res) ((res & (1 << 31)) != 0)
+#define LOLA_AMP_STEP_SIZE(res) ((res >> 24) & 0x7f)
+#define LOLA_AMP_NUM_STEPS(res) ((res >> 12) & 0x3ff)
+#define LOLA_AMP_OFFSET(res) ((res) & 0x3ff)
+
+#define LOLA_GRANULARITY_MIN 8
+#define LOLA_GRANULARITY_MAX 32
+#define LOLA_GRANULARITY_STEP 8
+
+/* parameters used with unsolicited command/response */
+#define LOLA_UNSOLICITED_TAG_MASK 0x3f
+#define LOLA_UNSOLICITED_TAG 0x1a
+#define LOLA_UNSOLICITED_ENABLE 0x80
+#define LOLA_UNSOL_RESP_TAG_OFFSET 26
+
+/* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */
+#define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f)
+#define LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(res) ((res >> 7) & 0x1f)
+
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata);
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval);
+int lola_codec_flush(struct lola *chip);
+#define lola_read_param(chip, nid, param, val) \
+ lola_codec_read(chip, nid, LOLA_VERB_PARAMETERS, param, 0, val, NULL)
+
+/* PCM */
+int lola_create_pcm(struct lola *chip);
+void lola_free_pcm(struct lola *chip);
+int lola_init_pcm(struct lola *chip, int dir, int *nidp);
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits);
+
+/* clock */
+int lola_init_clock_widget(struct lola *chip, int nid);
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force);
+int lola_enable_clock_events(struct lola *chip);
+int lola_set_clock_index(struct lola *chip, unsigned int idx);
+int lola_set_clock(struct lola *chip, int idx);
+int lola_set_sample_rate(struct lola *chip, int rate);
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val);
+unsigned int lola_sample_rate_convert(unsigned int coded);
+
+/* mixer */
+int lola_init_pins(struct lola *chip, int dir, int *nidp);
+int lola_init_mixer_widget(struct lola *chip, int nid);
+void lola_free_mixer(struct lola *chip);
+int lola_create_mixer(struct lola *chip);
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute);
+void lola_save_mixer(struct lola *chip);
+void lola_restore_mixer(struct lola *chip);
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update);
+
+/* proc */
+#ifdef CONFIG_SND_DEBUG
+void lola_proc_debug_new(struct lola *chip);
+#else
+#define lola_proc_debug_new(chip)
+#endif
+
+#endif /* _LOLA_H */
diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c
new file mode 100644
index 000000000000..72f8ef0ac865
--- /dev/null
+++ b/sound/pci/lola/lola_clock.c
@@ -0,0 +1,323 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+unsigned int lola_sample_rate_convert(unsigned int coded)
+{
+ unsigned int freq;
+
+ /* base frequency */
+ switch (coded & 0x3) {
+ case 0: freq = 48000; break;
+ case 1: freq = 44100; break;
+ case 2: freq = 32000; break;
+ default: return 0; /* error */
+ }
+
+ /* multiplier / devisor */
+ switch (coded & 0x1c) {
+ case (0 << 2): break;
+ case (4 << 2): break;
+ case (1 << 2): freq *= 2; break;
+ case (2 << 2): freq *= 4; break;
+ case (5 << 2): freq /= 2; break;
+ case (6 << 2): freq /= 4; break;
+ default: return 0; /* error */
+ }
+
+ /* ajustement */
+ switch (coded & 0x60) {
+ case (0 << 5): break;
+ case (1 << 5): freq = (freq * 999) / 1000; break;
+ case (2 << 5): freq = (freq * 1001) / 1000; break;
+ default: return 0; /* error */
+ }
+ return freq;
+}
+
+/*
+ * Granualrity
+ */
+
+#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000
+#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000
+
+static bool check_gran_clock_compatibility(struct lola *chip,
+ unsigned int val,
+ unsigned int freq)
+{
+ if (!chip->granularity)
+ return true;
+
+ if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
+ (val % LOLA_GRANULARITY_STEP) != 0)
+ return false;
+
+ if (val == LOLA_GRANULARITY_MIN) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
+ return false;
+ } else if (val < LOLA_GRANULARITY_MAX) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
+ return false;
+ }
+ return true;
+}
+
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
+{
+ int err;
+
+ if (!force) {
+ if (val == chip->granularity)
+ return 0;
+#if 0
+ /* change Gran only if there are no streams allocated ! */
+ if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
+ return -EBUSY;
+#endif
+ if (!check_gran_clock_compatibility(chip, val,
+ chip->clock.cur_freq))
+ return -EINVAL;
+ }
+
+ chip->granularity = val;
+ val /= LOLA_GRANULARITY_STEP;
+
+ /* audio function group */
+ err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
+ val, 0);
+ if (err < 0)
+ return err;
+ /* this can be a very slow function !!! */
+ usleep_range(400 * val, 20000);
+ return lola_codec_flush(chip);
+}
+
+/*
+ * Clock widget handling
+ */
+
+int __devinit lola_init_clock_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int i, j, nitems, nb_verbs, idx, idx_list;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
+ snd_printdd("No valid clock widget\n");
+ return 0;
+ }
+
+ chip->clock.nid = nid;
+ chip->clock.items = val & 0xff;
+ snd_printdd("clock_list nid=%x, entries=%d\n", nid,
+ chip->clock.items);
+ if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
+ printk(KERN_ERR SFX "CLOCK_LIST too big: %d\n",
+ chip->clock.items);
+ return -EINVAL;
+ }
+
+ nitems = chip->clock.items;
+ nb_verbs = (nitems + 3) / 4;
+ idx = 0;
+ idx_list = 0;
+ for (i = 0; i < nb_verbs; i++) {
+ unsigned int res_ex;
+ unsigned short items[4];
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ idx, 0, &val, &res_ex);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read CLOCK_LIST\n");
+ return -EINVAL;
+ }
+
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ int format = LOLA_CLOCK_FORMAT_NONE;
+ bool add_clock = true;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ else if (freq == 48000) {
+ chip->clock.cur_index = idx_list;
+ chip->clock.cur_freq = 48000;
+ chip->clock.cur_valid = true;
+ }
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ /* video clock has a format (0:NTSC, 1:PAL)*/
+ if (items[j] & 0x80)
+ format = LOLA_CLOCK_FORMAT_NTSC;
+ else
+ format = LOLA_CLOCK_FORMAT_PAL;
+ }
+ if (add_clock) {
+ struct lola_sample_clock *sc;
+ sc = &chip->clock.sample_clock[idx_list];
+ sc->type = type;
+ sc->format = format;
+ sc->freq = freq;
+ /* keep the index used with the board */
+ chip->clock.idx_lookup[idx_list] = idx;
+ idx_list++;
+ } else {
+ chip->clock.items--;
+ }
+ if (++idx >= nitems)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* enable unsolicited events of the clock widget */
+int lola_enable_clock_events(struct lola *chip)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_UNSOLICITED_ENABLE,
+ LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ printk(KERN_WARNING SFX "error in enable_clock_events %d\n",
+ res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_set_clock_index(struct lola *chip, unsigned int idx)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_CLOCK_SELECT,
+ chip->clock.idx_lookup[idx],
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ printk(KERN_WARNING SFX "error in set_clock %d\n", res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
+{
+ unsigned int tag;
+
+ /* the current EXTERNAL clock information gets updated by interrupt
+ * with an unsolicited response
+ */
+ if (!val)
+ return false;
+ tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
+ if (tag != LOLA_UNSOLICITED_TAG)
+ return false;
+
+ /* only for current = external clocks */
+ if (chip->clock.sample_clock[chip->clock.cur_index].type !=
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
+ chip->clock.cur_valid = (val & 0x100) != 0;
+ }
+ return true;
+}
+
+int lola_set_clock(struct lola *chip, int idx)
+{
+ int freq = 0;
+ bool valid = false;
+
+ if (idx == chip->clock.cur_index) {
+ /* current clock is allowed */
+ freq = chip->clock.cur_freq;
+ valid = chip->clock.cur_valid;
+ } else if (chip->clock.sample_clock[idx].type ==
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ /* internal clocks allowed */
+ freq = chip->clock.sample_clock[idx].freq;
+ valid = true;
+ }
+
+ if (!freq || !valid)
+ return -EINVAL;
+
+ if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
+ return -EINVAL;
+
+ if (idx != chip->clock.cur_index) {
+ int err = lola_set_clock_index(chip, idx);
+ if (err < 0)
+ return err;
+ /* update new settings */
+ chip->clock.cur_index = idx;
+ chip->clock.cur_freq = freq;
+ chip->clock.cur_valid = true;
+ }
+ return 0;
+}
+
+int lola_set_sample_rate(struct lola *chip, int rate)
+{
+ int i;
+
+ if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
+ return 0;
+ /* search for new dwClockIndex */
+ for (i = 0; i < chip->clock.items; i++) {
+ if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
+ chip->clock.sample_clock[i].freq == rate)
+ break;
+ }
+ if (i >= chip->clock.items)
+ return -EINVAL;
+ return lola_set_clock(chip, i);
+}
+
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
new file mode 100644
index 000000000000..5d518f1a712c
--- /dev/null
+++ b/sound/pci/lola/lola_mixer.c
@@ -0,0 +1,839 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lola.h"
+
+static int __devinit lola_init_pin(struct lola *chip, struct lola_pin *pin,
+ int dir, int nid)
+{
+ unsigned int val;
+ int err;
+
+ pin->nid = nid;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ val &= 0x00f00fff; /* test TYPE and bits 0..11 */
+ if (val == 0x00400200) /* Type = 4, Digital = 1 */
+ pin->is_analog = false;
+ else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
+ pin->is_analog = true;
+ else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
+ pin->is_analog = true;
+ else {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", val, nid);
+ return -EINVAL;
+ }
+
+ /* analog parameters only following, so continue in case of Digital pin
+ */
+ if (!pin->is_analog)
+ return 0;
+
+ if (dir == PLAY)
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
+ else
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read AMP-caps for 0x%x\n", nid);
+ return err;
+ }
+
+ pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
+ pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
+ pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
+ if (pin->amp_num_steps) {
+ /* zero as mute state */
+ pin->amp_num_steps++;
+ pin->amp_step_size++;
+ }
+ pin->amp_offset = LOLA_AMP_OFFSET(val);
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
+ NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't get MAX_LEVEL 0x%x\n", nid);
+ return err;
+ }
+ pin->max_level = val & 0x3ff; /* 10 bits */
+
+ pin->config_default_reg = 0;
+ pin->fixed_gain_list_len = 0;
+ pin->cur_gain_step = 0;
+
+ return 0;
+}
+
+int __devinit lola_init_pins(struct lola *chip, int dir, int *nidp)
+{
+ int i, err, nid;
+ nid = *nidp;
+ for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
+ err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
+ if (err < 0)
+ return err;
+ if (chip->pin[dir].pins[i].is_analog)
+ chip->pin[dir].num_analog_pins++;
+ }
+ *nidp = nid;
+ return 0;
+}
+
+void lola_free_mixer(struct lola *chip)
+{
+ if (chip->mixer.array_saved)
+ vfree(chip->mixer.array_saved);
+}
+
+int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
+ snd_printdd("No valid mixer widget\n");
+ return 0;
+ }
+
+ chip->mixer.nid = nid;
+ chip->mixer.caps = val;
+ chip->mixer.array = (struct lola_mixer_array __iomem *)
+ (chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
+
+ /* reserve memory to copy mixer data for sleep mode transitions */
+ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
+
+ /* mixer matrix sources are physical input data and play streams */
+ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
+ chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
+
+ /* mixer matrix destinations are record streams and physical output */
+ chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
+ chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
+
+ /* mixer matrix can have unused areas between PhysIn and
+ * Play or Record and PhysOut zones
+ */
+ chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
+ LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
+ chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
+ LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(val);
+
+ /* example : MixerMatrix of LoLa881
+ * 0-------8------16-------8------16
+ * | | | | |
+ * | INPUT | | INPUT | |
+ * | -> |unused | -> |unused |
+ * | RECORD| | OUTPUT| |
+ * | | | | |
+ * 8--------------------------------
+ * | | | | |
+ * | | | | |
+ * |unused |unused |unused |unused |
+ * | | | | |
+ * | | | | |
+ * 16-------------------------------
+ * | | | | |
+ * | PLAY | | PLAY | |
+ * | -> |unused | -> |unused |
+ * | RECORD| | OUTPUT| |
+ * | | | | |
+ * 8--------------------------------
+ * | | | | |
+ * | | | | |
+ * |unused |unused |unused |unused |
+ * | | | | |
+ * | | | | |
+ * 16-------------------------------
+ */
+ if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
+ chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
+ printk(KERN_ERR SFX "Invalid mixer widget size\n");
+ return -EINVAL;
+ }
+
+ chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
+ (((1U << chip->mixer.src_stream_outs) - 1)
+ << chip->mixer.src_stream_out_ofs);
+ chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
+ (((1U << chip->mixer.dest_phys_outs) - 1)
+ << chip->mixer.dest_phys_out_ofs);
+
+ return 0;
+}
+
+static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
+ unsigned short gain, bool on)
+{
+ unsigned int oldval, val;
+
+ if (!(chip->mixer.src_mask & (1 << id)))
+ return -EINVAL;
+ writew(gain, &chip->mixer.array->src_gain[id]);
+ oldval = val = readl(&chip->mixer.array->src_gain_enable);
+ if (on)
+ val |= (1 << id);
+ else
+ val &= ~(1 << id);
+ writel(val, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ /* inform micro-controller about the new source gain */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, id, 0);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
+ unsigned short *gains)
+{
+ int i;
+
+ if ((chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->src_gain[i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
+ /* update for all srcs at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
+ }
+ /* update manually */
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, i, 0);
+ }
+ }
+ return 0;
+}
+#endif /* not used */
+
+static int lola_mixer_set_mapping_gain(struct lola *chip,
+ unsigned int src, unsigned int dest,
+ unsigned short gain, bool on)
+{
+ unsigned int val;
+
+ if (!(chip->mixer.src_mask & (1 << src)) ||
+ !(chip->mixer.dest_mask & (1 << dest)))
+ return -EINVAL;
+ if (on)
+ writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
+ val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
+ if (on)
+ val |= (1 << src);
+ else
+ val &= ~(1 << src);
+ writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
+ lola_codec_flush(chip);
+ return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
+ src, dest);
+}
+
+static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
+ unsigned int mask, unsigned short *gains)
+{
+ int i;
+
+ if (!(chip->mixer.dest_mask & (1 << id)) ||
+ (chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
+ lola_codec_flush(chip);
+ /* update for all dests at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call);
+
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
+{
+ struct lola_pin *pin;
+ int idx, max_idx;
+
+ pin = chip->pin[dir].pins;
+ max_idx = chip->pin[dir].num_pins;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (pin[idx].is_analog) {
+ unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
+ /* set volume and do not save the value */
+ set_analog_volume(chip, dir, idx, val, false);
+ }
+ }
+ return lola_codec_flush(chip);
+}
+
+void lola_save_mixer(struct lola *chip)
+{
+ /* mute analog output */
+ if (chip->mixer.array_saved) {
+ /* store contents of mixer array */
+ memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
+ sizeof(*chip->mixer.array));
+ }
+ lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
+}
+
+void lola_restore_mixer(struct lola *chip)
+{
+ int i;
+
+ /*lola_reset_setups(chip);*/
+ if (chip->mixer.array_saved) {
+ /* restore contents of mixer array */
+ memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
+ sizeof(*chip->mixer.array));
+ /* inform micro-controller about all restored values
+ * and ignore return values
+ */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ chip->mixer.src_stream_out_ofs + i, 0);
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.dest_phys_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ chip->mixer.dest_phys_out_ofs + i, 0);
+ lola_codec_flush(chip);
+ }
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call)
+{
+ struct lola_pin *pin;
+ int err;
+
+ if (idx >= chip->pin[dir].num_pins)
+ return -EINVAL;
+ pin = &chip->pin[dir].pins[idx];
+ if (!pin->is_analog || pin->amp_num_steps <= val)
+ return -EINVAL;
+ if (external_call && pin->cur_gain_step == val)
+ return 0;
+ if (external_call)
+ lola_codec_flush(chip);
+ err = lola_codec_write(chip, pin->nid,
+ LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
+ if (err < 0)
+ return err;
+ if (external_call)
+ pin->cur_gain_step = val;
+ return 0;
+}
+
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
+{
+ int ret = 0;
+ int success = 0;
+ int n, err;
+
+ /* SRC can be activated and the dwInputSRCMask is valid? */
+ if ((chip->input_src_caps_mask & src_mask) != src_mask)
+ return -EINVAL;
+ /* handle all even Inputs - SRC is a stereo setting !!! */
+ for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
+ unsigned int mask = 3U << n; /* handle the stereo case */
+ unsigned int new_src, src_state;
+ if (!(chip->input_src_caps_mask & mask))
+ continue;
+ /* if one IO needs SRC, both stereo IO will get SRC */
+ new_src = (src_mask & mask) != 0;
+ if (update) {
+ src_state = (chip->input_src_mask & mask) != 0;
+ if (src_state == new_src)
+ continue; /* nothing to change for this IO */
+ }
+ err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
+ LOLA_VERB_SET_SRC, new_src, 0);
+ if (!err)
+ success++;
+ else
+ ret = err;
+ }
+ if (success)
+ ret = lola_codec_flush(chip);
+ if (!ret)
+ chip->input_src_mask = src_mask;
+ return ret;
+}
+
+/*
+ */
+static int init_mixer_values(struct lola *chip)
+{
+ int i;
+
+ /* all src on */
+ lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
+
+ /* clear all matrix */
+ memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
+ /* set src gain to 0dB */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_mixer_set_src_gain(chip,
+ i + chip->mixer.src_stream_out_ofs,
+ 336, true); /* 0dB */
+ /* set 1:1 dest gain */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
+ int src = i % chip->mixer.src_phys_ins;
+ lola_mixer_set_mapping_gain(chip, src, i, 336, true);
+ }
+ for (i = 0; i < chip->mixer.src_stream_outs; i++) {
+ int src = chip->mixer.src_stream_out_ofs + i;
+ int dst = chip->mixer.dest_phys_out_ofs +
+ i % chip->mixer.dest_phys_outs;
+ lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
+ }
+ return 0;
+}
+
+/*
+ * analog mixer control element
+ */
+static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chip->pin[dir].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
+ return 0;
+}
+
+static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ chip->pin[dir].pins[i].cur_gain_step;
+ return 0;
+}
+
+static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i, err;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++) {
+ err = set_analog_volume(chip, dir, i,
+ ucontrol->value.integer.value[i],
+ true);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ unsigned int val1, val2;
+ struct lola_pin *pin;
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ pin = &chip->pin[dir].pins[0];
+
+ val2 = pin->amp_step_size * 25;
+ val1 = -1 * (int)pin->amp_offset * (int)val2;
+#ifdef TLV_DB_SCALE_MUTE
+ val2 |= TLV_DB_SCALE_MUTE;
+#endif
+ if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), tlv + 1))
+ return -EFAULT;
+ if (put_user(val1, tlv + 2))
+ return -EFAULT;
+ if (put_user(val2, tlv + 3))
+ return -EFAULT;
+ return 0;
+}
+
+static struct snd_kcontrol_new lola_analog_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = lola_analog_vol_info,
+ .get = lola_analog_vol_get,
+ .put = lola_analog_vol_put,
+ .tlv.c = lola_analog_vol_tlv,
+};
+
+static int __devinit create_analog_mixer(struct lola *chip, int dir, char *name)
+{
+ if (!chip->pin[dir].num_pins)
+ return 0;
+ /* no analog volumes on digital only adapters */
+ if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
+ return 0;
+ lola_analog_mixer.name = name;
+ lola_analog_mixer.private_value = dir;
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_analog_mixer, chip));
+}
+
+/*
+ * Hardware sample rate converter on digital input
+ */
+static int lola_input_src_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chip->pin[CAPT].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int lola_input_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ !!(chip->input_src_mask & (1 << i));
+ return 0;
+}
+
+static int lola_input_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+ unsigned int mask;
+
+ mask = 0;
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ if (ucontrol->value.integer.value[i])
+ mask |= 1 << i;
+ return lola_set_src_config(chip, mask, true);
+}
+
+static struct snd_kcontrol_new lola_input_src_mixer __devinitdata = {
+ .name = "Digital SRC Capture Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = lola_input_src_info,
+ .get = lola_input_src_get,
+ .put = lola_input_src_put,
+};
+
+/*
+ * Lola16161 or Lola881 can have Hardware sample rate converters
+ * on its digital input pins
+ */
+static int __devinit create_input_src_mixer(struct lola *chip)
+{
+ if (!chip->input_src_caps_mask)
+ return 0;
+
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_input_src_mixer, chip));
+}
+
+/*
+ * src gain mixer
+ */
+static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 409;
+ return 0;
+}
+
+static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int mask, i;
+
+ mask = readl(&chip->mixer.array->src_gain_enable);
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << idx)))
+ return -EINVAL;
+ if (mask & (1 << idx))
+ val = readw(&chip->mixer.array->src_gain[idx]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ int i, err;
+
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val)
+ val--;
+ err = lola_mixer_set_src_gain(chip, idx, val, !!val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
+static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_src_gain_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_src_gain_info,
+ .get = lola_src_gain_get,
+ .put = lola_src_gain_put,
+ .tlv.p = lola_src_gain_tlv,
+};
+
+static int __devinit create_src_gain_mixer(struct lola *chip,
+ int num, int ofs, char *name)
+{
+ lola_src_gain_mixer.name = name;
+ lola_src_gain_mixer.private_value = ofs + (num << 8);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_src_gain_mixer, chip));
+}
+
+/*
+ * destination gain (matrix-like) mixer
+ */
+static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = src_num;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 433;
+ return 0;
+}
+
+static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask, i;
+
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
+ for (i = 0; i < src_num; i++) {
+ unsigned int src = src_ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << src)))
+ return -EINVAL;
+ if (mask & (1 << dst))
+ val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask;
+ unsigned short gains[MAX_STREAM_COUNT];
+ int i, num;
+
+ mask = 0;
+ num = 0;
+ for (i = 0; i < src_num; i++) {
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val) {
+ gains[num++] = val - 1;
+ mask |= 1 << i;
+ }
+ }
+ mask <<= src_ofs;
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ return lola_mixer_set_dest_gains(chip, dst, mask, gains);
+}
+
+static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_dest_gain_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_dest_gain_info,
+ .get = lola_dest_gain_get,
+ .put = lola_dest_gain_put,
+ .tlv.p = lola_dest_gain_tlv,
+};
+
+static int __devinit create_dest_gain_mixer(struct lola *chip,
+ int src_num, int src_ofs,
+ int num, int ofs, char *name)
+{
+ lola_dest_gain_mixer.count = num;
+ lola_dest_gain_mixer.name = name;
+ lola_dest_gain_mixer.private_value =
+ src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_dest_gain_mixer, chip));
+}
+
+/*
+ */
+int __devinit lola_create_mixer(struct lola *chip)
+{
+ int err;
+
+ err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_input_src_mixer(chip);
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
+ "Line Source Gain Volume");
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ "Stream Source Gain Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_stream_ins, 0,
+ "Line Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_stream_ins, 0,
+ "Stream-Loopback Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Line-Loopback Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Stream Playback Volume");
+ if (err < 0)
+ return err;
+
+ return init_mixer_values(chip);
+}
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
new file mode 100644
index 000000000000..c44db68eecb5
--- /dev/null
+++ b/sound/pci/lola/lola_pcm.c
@@ -0,0 +1,706 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+#define LOLA_MAX_BDL_ENTRIES 8
+#define LOLA_MAX_BUF_SIZE (1024*1024*1024)
+#define LOLA_BDL_ENTRY_SIZE (16 * 16)
+
+static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ return &chip->pcm[substream->stream];
+}
+
+static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream)
+{
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ unsigned int idx = substream->number;
+ return &pcm->streams[idx];
+}
+
+static unsigned int lola_get_lrc(struct lola *chip)
+{
+ return lola_readl(chip, BAR1, LRC);
+}
+
+static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync)
+{
+ unsigned int tstamp = lola_get_lrc(chip) >> 8;
+ if (chip->granularity) {
+ unsigned int wait_banks = quick_no_sync ? 0 : 8;
+ tstamp += (wait_banks + 1) * chip->granularity - 1;
+ tstamp -= tstamp % chip->granularity;
+ }
+ return tstamp << 8;
+}
+
+/* clear any pending interrupt status */
+static void lola_stream_clear_pending_irq(struct lola *chip,
+ struct lola_stream *str)
+{
+ unsigned int val = lola_dsd_read(chip, str->dsd, STS);
+ val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS;
+ if (val)
+ lola_dsd_write(chip, str->dsd, STS, val);
+}
+
+static void lola_stream_start(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+}
+
+static void lola_stream_stop(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+ lola_stream_clear_pending_irq(chip, str);
+}
+
+static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int val;
+ val = lola_dsd_read(chip, str->dsd, CTL);
+ if (!(val & LOLA_DSD_CTL_SRST))
+ return;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "SRST not clear (stream %d)\n", str->dsd);
+}
+
+static int lola_stream_wait_for_fifo(struct lola *chip,
+ struct lola_stream *str,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) == val)
+ return 0;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "FIFO not ready (stream %d)\n", str->dsd);
+ return -EIO;
+}
+
+/* sync for FIFO ready/empty for all linked streams;
+ * clear paused flag when FIFO gets ready again
+ */
+static int lola_sync_wait_for_fifo(struct lola *chip,
+ struct snd_pcm_substream *substream,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ struct snd_pcm_substream *s;
+ int pending = 0;
+
+ while (time_before(jiffies, end_time)) {
+ pending = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->prepared && str->paused) {
+ unsigned int reg;
+ reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) != val) {
+ pending = str->dsd + 1;
+ break;
+ }
+ if (ready)
+ str->paused = 0;
+ }
+ }
+ if (!pending)
+ return 0;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "FIFO not ready (pending %d)\n", pending - 1);
+ return -EIO;
+}
+
+/* finish pause - prepare for a new resume */
+static void lola_sync_pause(struct lola *chip,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_substream *s;
+
+ lola_sync_wait_for_fifo(chip, substream, false);
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->paused && str->prepared)
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ }
+ lola_sync_wait_for_fifo(chip, substream, true);
+}
+
+static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
+{
+ if (str->prepared) {
+ if (str->paused)
+ lola_sync_pause(chip, str->substream);
+ str->prepared = 0;
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ lola_stream_wait_for_fifo(chip, str, false);
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST);
+ lola_dsd_write(chip, str->dsd, LVI, 0);
+ lola_dsd_write(chip, str->dsd, BDPU, 0);
+ lola_dsd_write(chip, str->dsd, BDPL, 0);
+ wait_for_srst_clear(chip, str);
+ }
+}
+
+static struct snd_pcm_hardware lola_pcm_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_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = LOLA_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = LOLA_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = LOLA_MAX_BDL_ENTRIES,
+ .fifo_size = 0,
+};
+
+static int lola_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ mutex_lock(&chip->open_mutex);
+ if (str->opened) {
+ mutex_unlock(&chip->open_mutex);
+ return -EBUSY;
+ }
+ str->substream = substream;
+ str->master = NULL;
+ str->opened = 1;
+ runtime->hw = lola_pcm_hw;
+ runtime->hw.channels_max = pcm->num_streams - str->index;
+ if (chip->sample_rate) {
+ /* sample rate is locked */
+ runtime->hw.rate_min = chip->sample_rate;
+ runtime->hw.rate_max = chip->sample_rate;
+ } else {
+ runtime->hw.rate_min = chip->sample_rate_min;
+ runtime->hw.rate_max = chip->sample_rate_max;
+ }
+ chip->ref_count_rate++;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ chip->granularity);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ chip->granularity);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static void lola_cleanup_slave_streams(struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ int i;
+ for (i = str->index + 1; i < pcm->num_streams; i++) {
+ struct lola_stream *s = &pcm->streams[i];
+ if (s->master != str)
+ break;
+ s->master = NULL;
+ s->opened = 0;
+ }
+}
+
+static int lola_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ if (str->substream == substream) {
+ str->substream = NULL;
+ str->opened = 0;
+ }
+ if (--chip->ref_count_rate == 0) {
+ /* release sample rate */
+ chip->sample_rate = 0;
+ }
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct lola_stream *str = lola_get_stream(substream);
+
+ str->bufsize = 0;
+ str->period_bytes = 0;
+ str->format_verb = 0;
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ mutex_unlock(&chip->open_mutex);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+ struct lola_stream *str, u32 **bdlp,
+ int ofs, int size)
+{
+ u32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (str->frags >= LOLA_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = size ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ str->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
+ struct snd_pcm_substream *substream,
+ struct lola_stream *str)
+{
+ u32 *bdl;
+ int i, ofs, periods, period_bytes;
+
+ period_bytes = str->period_bytes;
+ periods = str->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index);
+ ofs = 0;
+ str->frags = 0;
+ for (i = 0; i < periods; i++) {
+ ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
+ str->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream)
+{
+ unsigned int verb;
+
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ verb = 0x00000000;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ verb = 0x00000200;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ verb = 0x00000300;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ verb = 0x00001300;
+ break;
+ default:
+ return 0;
+ }
+ verb |= substream->runtime->channels;
+ return verb;
+}
+
+static int lola_set_stream_config(struct lola *chip,
+ struct lola_stream *str,
+ int channels)
+{
+ int i, err;
+ unsigned int verb, val;
+
+ /* set format info for all channels
+ * (with only one command for the first channel)
+ */
+ err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT,
+ str->format_verb, 0, &val, NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Cannot set stream format 0x%x\n",
+ str->format_verb);
+ return err;
+ }
+
+ /* update stream - channel config */
+ for (i = 0; i < channels; i++) {
+ verb = (str->index << 6) | i;
+ err = lola_codec_read(chip, str[i].nid,
+ LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb,
+ &val, NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Cannot set stream channel %d\n", i);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ dma_addr_t bdl;
+
+ if (str->prepared)
+ return -EINVAL;
+
+ /* set up BDL */
+ bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index;
+ lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl);
+ lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl));
+ /* program the stream LVI (last valid index) of the BDL */
+ lola_dsd_write(chip, str->dsd, LVI, str->frags - 1);
+ lola_stream_clear_pending_irq(chip, str);
+
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN);
+
+ str->prepared = 1;
+
+ return lola_stream_wait_for_fifo(chip, str, true);
+}
+
+static int lola_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_verb;
+ int i, err;
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ if (str->index + runtime->channels > pcm->num_streams) {
+ mutex_unlock(&chip->open_mutex);
+ return -EINVAL;
+ }
+ for (i = 1; i < runtime->channels; i++) {
+ str[i].master = str;
+ str[i].opened = 1;
+ }
+ mutex_unlock(&chip->open_mutex);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ format_verb = lola_get_format_verb(substream);
+
+ str->bufsize = bufsize;
+ str->period_bytes = period_bytes;
+ str->format_verb = format_verb;
+
+ err = lola_setup_periods(chip, pcm, substream, str);
+ if (err < 0)
+ return err;
+
+ err = lola_set_sample_rate(chip, runtime->rate);
+ if (err < 0)
+ return err;
+ chip->sample_rate = runtime->rate; /* sample rate gets locked */
+
+ err = lola_set_stream_config(chip, str, runtime->channels);
+ if (err < 0)
+ return err;
+
+ err = lola_setup_controller(chip, pcm, str);
+ if (err < 0) {
+ lola_stream_reset(chip, str);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str;
+ struct snd_pcm_substream *s;
+ unsigned int start;
+ unsigned int tstamp;
+ bool sync_streams;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * sample correct synchronization is only needed starting several
+ * streams. On stop or if only one stream do as quick as possible
+ */
+ sync_streams = (start && snd_pcm_stream_linked(substream));
+ tstamp = lola_get_tstamp(chip, !sync_streams);
+ spin_lock(&chip->reg_lock);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (start)
+ lola_stream_start(chip, str, tstamp);
+ else
+ lola_stream_stop(chip, str, tstamp);
+ str->running = start;
+ str->paused = !start;
+ snd_pcm_trigger_done(s, substream);
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB);
+
+ if (pos >= str->bufsize)
+ pos = 0;
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
+{
+ int i;
+
+ for (i = 0; bits && i < pcm->num_streams; i++) {
+ if (bits & (1 << i)) {
+ struct lola_stream *str = &pcm->streams[i];
+ if (str->substream && str->running)
+ snd_pcm_period_elapsed(str->substream);
+ bits &= ~(1 << i);
+ }
+ }
+}
+
+static struct snd_pcm_ops lola_pcm_ops = {
+ .open = lola_pcm_open,
+ .close = lola_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = lola_pcm_hw_params,
+ .hw_free = lola_pcm_hw_free,
+ .prepare = lola_pcm_prepare,
+ .trigger = lola_pcm_trigger,
+ .pointer = lola_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+int __devinit lola_create_pcm(struct lola *chip)
+{
+ struct snd_pcm *pcm;
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->pcm[i].bdl);
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_pcm_new(chip->card, "Digigram Lola", 0,
+ chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
+ chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strlcpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
+ pcm->private_data = chip;
+ for (i = 0; i < 2; i++) {
+ if (chip->pcm[i].num_streams)
+ snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 1024 * 64, 32 * 1024 * 1024);
+ return 0;
+}
+
+void lola_free_pcm(struct lola *chip)
+{
+ snd_dma_free_pages(&chip->pcm[0].bdl);
+ snd_dma_free_pages(&chip->pcm[1].bdl);
+}
+
+/*
+ */
+
+static int lola_init_stream(struct lola *chip, struct lola_stream *str,
+ int idx, int nid, int dir)
+{
+ unsigned int val;
+ int err;
+
+ str->nid = nid;
+ str->index = idx;
+ str->dsd = idx;
+ if (dir == PLAY)
+ str->dsd += MAX_STREAM_IN_COUNT;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ if (dir == PLAY) {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */
+ if ((val & 0x00f00dff) != 0x00000010) {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ } else {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
+ * (bug : ignore bit8: Conn list = 0/1)
+ */
+ if ((val & 0x00f00cff) != 0x00100010) {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ /* test bit9:DIGITAL and bit12:SRC_PRESENT*/
+ if ((val & 0x00001200) == 0x00001200)
+ chip->input_src_caps_mask |= (1 << idx);
+ }
+
+ err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read FORMATS 0x%x\n", nid);
+ return err;
+ }
+ val &= 3;
+ if (val == 3)
+ str->can_float = true;
+ if (!(val & 1)) {
+ printk(KERN_ERR SFX "Invalid formats 0x%x for 0x%x", val, nid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int __devinit lola_init_pcm(struct lola *chip, int dir, int *nidp)
+{
+ struct lola_pcm *pcm = &chip->pcm[dir];
+ int i, nid, err;
+
+ nid = *nidp;
+ for (i = 0; i < pcm->num_streams; i++, nid++) {
+ err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir);
+ if (err < 0)
+ return err;
+ }
+ *nidp = nid;
+ return 0;
+}
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
new file mode 100644
index 000000000000..9d7daf897c9d
--- /dev/null
+++ b/sound/pci/lola/lola_proc.c
@@ -0,0 +1,222 @@
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+static void print_audio_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ snd_iprintf(buffer, " Formats: 0x%x\n", val);
+}
+
+static void print_pin_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, unsigned int ampcap,
+ const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ if (val == 0x00400200)
+ return;
+ lola_read_param(chip, nid, ampcap, &val);
+ snd_iprintf(buffer, " Amp-Caps: 0x%x\n", val);
+ snd_iprintf(buffer, " mute=%d, step-size=%d, steps=%d, ofs=%d\n",
+ LOLA_AMP_MUTE_CAPABLE(val),
+ LOLA_AMP_STEP_SIZE(val),
+ LOLA_AMP_NUM_STEPS(val),
+ LOLA_AMP_OFFSET(val));
+ lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val, NULL);
+ snd_iprintf(buffer, " Max-level: 0x%x\n", val);
+}
+
+static void print_clock_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ int i, j, num_clocks;
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Clock] wcaps 0x%x\n", nid, val);
+ num_clocks = val & 0xff;
+ for (i = 0; i < num_clocks; i += 4) {
+ unsigned int res_ex;
+ unsigned short items[4];
+ const char *name;
+
+ lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ i, 0, &val, &res_ex);
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ if (i + j >= num_clocks)
+ break;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ name = "Internal";
+ freq = lola_sample_rate_convert(freq);
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ name = "Video";
+ freq = lola_sample_rate_convert(freq);
+ } else {
+ name = "Other";
+ }
+ snd_iprintf(buffer, " Clock %d: Type %d:%s, freq=%d\n",
+ i + j, type, name, freq);
+ }
+ }
+}
+
+static void print_mixer_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Mixer] wcaps 0x%x\n", nid, val);
+}
+
+static void lola_proc_codec_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ unsigned int val;
+ int i, nid;
+
+ lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ snd_iprintf(buffer, "Vendor: 0x%08x\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ snd_iprintf(buffer, "Function Type: %d\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ snd_iprintf(buffer, "Specific-Caps: 0x%08x\n", val);
+ snd_iprintf(buffer, " Pins-In %d, Pins-Out %d\n",
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+ nid = 2;
+ for (i = 0; i < chip->pcm[CAPT].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-In]");
+ for (i = 0; i < chip->pcm[PLAY].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-Out]");
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_IN_CAP,
+ "[Pin-In]");
+ for (i = 0; i < chip->pin[PLAY].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_OUT_CAP,
+ "[Pin-Out]");
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ print_clock_widget(buffer, chip, nid);
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ print_mixer_widget(buffer, chip, nid);
+ nid++;
+ }
+}
+
+/* direct codec access for debugging */
+static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ char line[64];
+ unsigned int id, verb, data, extdata;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4)
+ continue;
+ lola_codec_read(chip, id, verb, data, extdata,
+ &chip->debug_res,
+ &chip->debug_res_ex);
+ }
+}
+
+static void lola_proc_codec_rw_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ snd_iprintf(buffer, "0x%x 0x%x\n", chip->debug_res, chip->debug_res_ex);
+}
+
+/*
+ * dump some registers
+ */
+static void lola_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 0x40; i += 4) {
+ snd_iprintf(buffer, "BAR0 %02x: %08x\n", i,
+ readl(chip->bar[BAR0].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 0x30; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0x80; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 32; i++) {
+ snd_iprintf(buffer, "DSD %02x STS %08x\n", i,
+ lola_dsd_read(chip, i, STS));
+ snd_iprintf(buffer, "DSD %02x LPIB %08x\n", i,
+ lola_dsd_read(chip, i, LPIB));
+ snd_iprintf(buffer, "DSD %02x CTL %08x\n", i,
+ lola_dsd_read(chip, i, CTL));
+ snd_iprintf(buffer, "DSD %02x LVIL %08x\n", i,
+ lola_dsd_read(chip, i, LVI));
+ snd_iprintf(buffer, "DSD %02x BDPL %08x\n", i,
+ lola_dsd_read(chip, i, BDPL));
+ snd_iprintf(buffer, "DSD %02x BDPU %08x\n", i,
+ lola_dsd_read(chip, i, BDPU));
+ }
+}
+
+void __devinit lola_proc_debug_new(struct lola *chip)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(chip->card, "codec", &entry))
+ snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
+ if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
+ snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
+ entry->mode |= S_IWUSR;
+ entry->c.text.write = lola_proc_codec_rw_write;
+ }
+ if (!snd_card_proc_new(chip->card, "regs", &entry))
+ snd_info_set_text_ops(entry, chip, lola_proc_regs_read);
+}
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 961d98297695..9cea84c3e0c6 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1000,7 +1000,7 @@ static void device_change_handler(struct work_struct *work)
chip->lineout_sw_ctl);
if (mix->anded_reset)
msleep(10);
- check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
+ check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify,
chip->speaker_sw_ctl);
} else {
/* unmute speaker, mute others */
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c
index 248463511186..ac828eff1a63 100644
--- a/sound/usb/6fire/control.c
+++ b/sound/usb/6fire/control.c
@@ -65,6 +65,15 @@ init_data[] = {
{ 0 } /* TERMINATING ENTRY */
};
+static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
+/* values to write to soundcard register for all samplerates */
+static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
+
+enum {
+ DIGITAL_THRU_ONLY_SAMPLERATE = 3
+};
+
static void usb6fire_control_master_vol_update(struct control_runtime *rt)
{
struct comm_runtime *comm_rt = rt->chip->comm;
@@ -95,6 +104,67 @@ static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
}
}
+static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
+{
+ int ret;
+ struct usb_device *device = rt->chip->dev;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (rate < 0 || rate >= CONTROL_N_RATES)
+ return -EINVAL;
+
+ ret = usb_set_interface(device, 1, rates_altsetting[rate]);
+ if (ret < 0)
+ return ret;
+
+ /* set soundcard clock */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
+ rates_6fire_vh[rate]);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_set_channels(
+ struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in)
+{
+ int ret;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ /* enable analog inputs and outputs
+ * (one bit per stereo-channel) */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x02,
+ (1 << (n_analog_out / 2)) - 1,
+ (1 << (n_analog_in / 2)) - 1);
+ if (ret < 0)
+ return ret;
+
+ /* disable digital inputs and outputs */
+ /* TODO: use spdif_x to enable/disable digital channels */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_streaming_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (comm_rt) {
+ if (!rt->usb_streaming && rt->digital_thru_switch)
+ usb6fire_control_set_rate(rt,
+ DIGITAL_THRU_ONLY_SAMPLERATE);
+ return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
+ (rt->usb_streaming ? 0x01 : 0x00) |
+ (rt->digital_thru_switch ? 0x08 : 0x00));
+ }
+ return -EINVAL;
+}
+
static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -195,6 +265,28 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
+ rt->digital_thru_switch = ucontrol->value.integer.value[0];
+ usb6fire_control_streaming_update(rt);
+ changed = 1;
+ }
+ return changed;
+}
+
+static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = rt->digital_thru_switch;
+ return 0;
+}
+
static struct __devinitdata snd_kcontrol_new elements[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -223,6 +315,15 @@ static struct __devinitdata snd_kcontrol_new elements[] = {
.get = usb6fire_control_opt_coax_get,
.put = usb6fire_control_opt_coax_put
},
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Thru Playback Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = usb6fire_control_digital_thru_get,
+ .put = usb6fire_control_digital_thru_put
+ },
{}
};
@@ -238,6 +339,9 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
return -ENOMEM;
rt->chip = chip;
+ rt->update_streaming = usb6fire_control_streaming_update;
+ rt->set_rate = usb6fire_control_set_rate;
+ rt->set_channels = usb6fire_control_set_channels;
i = 0;
while (init_data[i].type) {
@@ -249,6 +353,7 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
usb6fire_control_opt_coax_update(rt);
usb6fire_control_line_phono_update(rt);
usb6fire_control_master_vol_update(rt);
+ usb6fire_control_streaming_update(rt);
i = 0;
while (elements[i].name) {
diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h
index b534c777ab02..8f5aeead2e3d 100644
--- a/sound/usb/6fire/control.h
+++ b/sound/usb/6fire/control.h
@@ -21,12 +21,29 @@ enum {
CONTROL_MAX_ELEMENTS = 32
};
+enum {
+ CONTROL_RATE_44KHZ,
+ CONTROL_RATE_48KHZ,
+ CONTROL_RATE_88KHZ,
+ CONTROL_RATE_96KHZ,
+ CONTROL_RATE_176KHZ,
+ CONTROL_RATE_192KHZ,
+ CONTROL_N_RATES
+};
+
struct control_runtime {
+ int (*update_streaming)(struct control_runtime *rt);
+ int (*set_rate)(struct control_runtime *rt, int rate);
+ int (*set_channels)(struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in);
+
struct sfire_chip *chip;
struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
bool opt_coax_switch;
bool line_phono_switch;
+ bool digital_thru_switch;
+ bool usb_streaming;
u8 master_vol;
};
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
index 86c1a3103760..d47beffedb0f 100644
--- a/sound/usb/6fire/firmware.c
+++ b/sound/usb/6fire/firmware.c
@@ -3,12 +3,6 @@
*
* Firmware loader
*
- * Currently not working for all devices. To be able to use the device
- * in linux, it is also possible to let the windows driver upload the firmware.
- * For that, start the computer in windows and reboot.
- * As long as the device is connected to the power supply, no firmware reload
- * needs to be performed.
- *
* Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011
* Version: 0.3.0
@@ -21,6 +15,7 @@
*/
#include <linux/firmware.h>
+#include <linux/bitrev.h>
#include "firmware.h"
#include "chip.h"
@@ -33,32 +28,6 @@ enum {
FPGA_BUFSIZE = 512, FPGA_EP = 2
};
-static const u8 BIT_REVERSE_TABLE[256] = {
- 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50,
- 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8,
- 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04,
- 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
- 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c,
- 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82,
- 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32,
- 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
- 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46,
- 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6,
- 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e,
- 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
- 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71,
- 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99,
- 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25,
- 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
- 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d,
- 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3,
- 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b,
- 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb,
- 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67,
- 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f,
- 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f,
- 0xbf, 0x7f, 0xff };
-
/*
* wMaxPacketSize of pcm endpoints.
* keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
@@ -72,6 +41,10 @@ static const u8 ep_w_max_packet_size[] = {
0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
};
+static const u8 known_fw_versions[][4] = {
+ { 0x03, 0x01, 0x0b, 0x00 }
+};
+
struct ihex_record {
u16 address;
u8 len;
@@ -340,7 +313,7 @@ static int usb6fire_fw_fpga_upload(
while (c != end) {
for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
- buffer[i] = BIT_REVERSE_TABLE[(u8) *c];
+ buffer[i] = byte_rev_table[(u8) *c];
ret = usb6fire_fw_fpga_write(device, buffer, i);
if (ret < 0) {
@@ -363,6 +336,25 @@ static int usb6fire_fw_fpga_upload(
return 0;
}
+/* check, if the firmware version the devices has currently loaded
+ * is known by this driver. 'version' needs to have 4 bytes version
+ * info data. */
+static int usb6fire_fw_check(u8 *version)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)
+ if (!memcmp(version, known_fw_versions + i, 4))
+ return 0;
+
+ snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
+ "%02x %02x %02x %02x. "
+ "please reconnect to power. if this failure "
+ "still happens, check your firmware installation.",
+ version[0], version[1], version[2], version[3]);
+ return -EINVAL;
+}
+
int usb6fire_fw_init(struct usb_interface *intf)
{
int i;
@@ -378,9 +370,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
"firmware state.\n");
return ret;
}
- if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55
- || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7]
- != 0x00) {
+ if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
"received from device: ");
for (i = 0; i < 8; i++)
@@ -389,7 +379,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
return -EIO;
}
/* do we need fpga loader ezusb firmware? */
- if (buffer[3] == 0x01 && buffer[6] == 0x19) {
+ if (buffer[3] == 0x01) {
ret = usb6fire_fw_ezusb_upload(intf,
"6fire/dmx6firel2.ihx", 0, NULL, 0);
if (ret < 0)
@@ -397,7 +387,10 @@ int usb6fire_fw_init(struct usb_interface *intf)
return FW_NOT_READY;
}
/* do we need fpga firmware and application ezusb firmware? */
- else if (buffer[3] == 0x02 && buffer[6] == 0x0b) {
+ else if (buffer[3] == 0x02) {
+ ret = usb6fire_fw_check(buffer + 4);
+ if (ret < 0)
+ return ret;
ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
if (ret < 0)
return ret;
@@ -410,8 +403,8 @@ int usb6fire_fw_init(struct usb_interface *intf)
return FW_NOT_READY;
}
/* all fw loaded? */
- else if (buffer[3] == 0x03 && buffer[6] == 0x0b)
- return 0;
+ else if (buffer[3] == 0x03)
+ return usb6fire_fw_check(buffer + 4);
/* unknown data? */
else {
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
index ba62c7468ba8..b137b25865cc 100644
--- a/sound/usb/6fire/pcm.c
+++ b/sound/usb/6fire/pcm.c
@@ -17,26 +17,23 @@
#include "pcm.h"
#include "chip.h"
#include "comm.h"
+#include "control.h"
enum {
OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
};
/* keep next two synced with
- * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */
+ * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE
+ * and CONTROL_RATE_XXX in control.h */
static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
-static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
static const int rates_alsaid[] = {
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
-/* values to write to soundcard register for all samplerates */
-static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
-static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
-
enum { /* settings for pcm */
OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
};
@@ -48,15 +45,6 @@ enum { /* pcm streaming states */
STREAM_STOPPING
};
-enum { /* pcm sample rates (also index into RATES_XXX[]) */
- RATE_44KHZ,
- RATE_48KHZ,
- RATE_88KHZ,
- RATE_96KHZ,
- RATE_176KHZ,
- RATE_192KHZ
-};
-
static const struct snd_pcm_hardware pcm_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -64,7 +52,7 @@ static const struct snd_pcm_hardware pcm_hw = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
@@ -87,57 +75,34 @@ static const struct snd_pcm_hardware pcm_hw = {
static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
{
int ret;
- struct usb_device *device = rt->chip->dev;
- struct comm_runtime *comm_rt = rt->chip->comm;
+ struct control_runtime *ctrl_rt = rt->chip->control;
- if (rt->rate >= ARRAY_SIZE(rates))
- return -EINVAL;
- /* disable streaming */
- ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00);
+ ctrl_rt->usb_streaming = false;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error stopping streaming while "
"setting samplerate %d.\n", rates[rt->rate]);
return ret;
}
- ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]);
- if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error setting interface "
- "altsetting %d for samplerate %d.\n",
- rates_altsetting[rt->rate], rates[rt->rate]);
- return ret;
- }
-
- /* set soundcard clock */
- ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate],
- rates_6fire_vh[rt->rate]);
+ ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
rates[rt->rate]);
return ret;
}
- /* enable analog inputs and outputs
- * (one bit per stereo-channel) */
- ret = comm_rt->write16(comm_rt, 0x02, 0x02,
- (1 << (OUT_N_CHANNELS / 2)) - 1,
- (1 << (IN_N_CHANNELS / 2)) - 1);
+ ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
+ false, false);
if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error initializing analog channels "
+ snd_printk(KERN_ERR PREFIX "error initializing channels "
"while setting samplerate %d.\n",
rates[rt->rate]);
return ret;
}
- /* disable digital inputs and outputs */
- ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
- if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error initializing digital "
- "channels while setting samplerate %d.\n",
- rates[rt->rate]);
- return ret;
- }
- ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01);
+ ctrl_rt->usb_streaming = true;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error starting streaming while "
"setting samplerate %d.\n", rates[rt->rate]);
@@ -168,12 +133,15 @@ static struct pcm_substream *usb6fire_pcm_get_substream(
static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
{
int i;
+ struct control_runtime *ctrl_rt = rt->chip->control;
if (rt->stream_state != STREAM_DISABLED) {
for (i = 0; i < PCM_N_URBS; i++) {
usb_kill_urb(&rt->in_urbs[i].instance);
usb_kill_urb(&rt->out_urbs[i].instance);
}
+ ctrl_rt->usb_streaming = false;
+ ctrl_rt->update_streaming(ctrl_rt);
rt->stream_state = STREAM_DISABLED;
}
}
@@ -228,7 +196,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
unsigned int total_length = 0;
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
- u32 *src = (u32 *) urb->buffer;
+ u32 *src = NULL;
u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
* (alsa_rt->frame_bits >> 3));
u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
@@ -244,7 +212,12 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
else
frame_count = 0;
- src = (u32 *) (urb->buffer + total_length);
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ src = (u32 *) (urb->buffer + total_length);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ src = (u32 *) (urb->buffer - 1 + total_length);
+ else
+ return;
src++; /* skip leading 4 bytes of every packet */
total_length += urb->packets[i].length;
for (frame = 0; frame < frame_count; frame++) {
@@ -274,9 +247,18 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub,
* (alsa_rt->frame_bits >> 3));
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
* (alsa_rt->frame_bits >> 3));
- u32 *dest = (u32 *) urb->buffer;
+ u32 *dest;
int bytes_per_frame = alsa_rt->channels << 2;
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ dest = (u32 *) (urb->buffer - 1);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ dest = (u32 *) (urb->buffer);
+ else {
+ snd_printk(KERN_ERR PREFIX "Unknown sample format.");
+ return;
+ }
+
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
/* at least 4 header bytes for valid packet.
* after that: 32 bits per sample for analog channels */
@@ -456,7 +438,7 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
/* all substreams closed? if so, stop streaming */
if (!rt->playback.instance && !rt->capture.instance) {
usb6fire_pcm_stream_stop(rt);
- rt->rate = -1;
+ rt->rate = ARRAY_SIZE(rates);
}
}
mutex_unlock(&rt->stream_mutex);
@@ -480,7 +462,6 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
- int i;
int ret;
if (rt->panic)
@@ -493,12 +474,10 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
sub->period_off = 0;
if (rt->stream_state == STREAM_DISABLED) {
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (alsa_rt->rate == rates[i]) {
- rt->rate = i;
+ for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
+ if (alsa_rt->rate == rates[rt->rate])
break;
- }
- if (i == ARRAY_SIZE(rates)) {
+ if (rt->rate == ARRAY_SIZE(rates)) {
mutex_unlock(&rt->stream_mutex);
snd_printk("invalid rate %d in prepare.\n",
alsa_rt->rate);
@@ -613,7 +592,7 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip)
rt->chip = chip;
rt->stream_state = STREAM_DISABLED;
- rt->rate = -1;
+ rt->rate = ARRAY_SIZE(rates);
init_waitqueue_head(&rt->stream_wait_queue);
mutex_init(&rt->stream_mutex);
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 97724d8fa9f6..8beb77563da2 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -100,19 +100,17 @@ config SND_USB_US122L
config SND_USB_6FIRE
tristate "TerraTec DMX 6Fire USB"
- depends on EXPERIMENTAL
select FW_LOADER
+ select BITREVERSE
select SND_RAWMIDI
select SND_PCM
help
Say Y here to include support for TerraTec 6fire DMX USB interface.
You will need firmware files in order to be able to use the device
- after it has been coldstarted. This driver currently does not support
- firmware loading for all devices. If you own such a device,
- you could start windows and let the windows driver upload
- the firmware. As long as you do not unplug your device from power,
- it should be usable.
+ after it has been coldstarted. An install script for the firmware
+ and further help can be found at
+ http://sixfireusb.sourceforge.net
endif # SND_USB
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 7754a1034545..075195e8661a 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -104,6 +104,15 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
int err;
unsigned char data;
struct usb_device *dev = chip->dev;
+ struct uac_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return 0;
+
+ /* If a clock source can't tell us whether it's valid, we assume it is */
+ if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID))
+ return 1;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
@@ -114,7 +123,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
if (err < 0) {
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
__func__, source_id);
- return err;
+ return 0;
}
return !!data;
diff --git a/sound/usb/debug.h b/sound/usb/debug.h
index 343ec2d9ee66..58030176f008 100644
--- a/sound/usb/debug.h
+++ b/sound/usb/debug.h
@@ -8,7 +8,7 @@
#ifdef HW_CONST_DEBUG
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
-#define hwc_debug(fmt, args...) /**/
+#define hwc_debug(fmt, args...) do { } while(0)
#endif
#endif /* __USBAUDIO_DEBUG_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 5b792d2c8061..8d042dce0d16 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -30,6 +30,7 @@
#include "helper.h"
#include "debug.h"
#include "clock.h"
+#include "format.h"
/*
* parse the audio format type I descriptor
@@ -176,9 +177,11 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
if (!rate)
continue;
/* C-Media CM6501 mislabels its 96 kHz altsetting */
+ /* Terratec Aureon 7.1 USB C-Media 6206, too */
if (rate == 48000 && nr_rates == 1 &&
(chip->usb_id == USB_ID(0x0d8c, 0x0201) ||
- chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
+ chip->usb_id == USB_ID(0x0d8c, 0x0102) ||
+ chip->usb_id == USB_ID(0x0ccd, 0x00b1)) &&
fp->altsetting == 5 && fp->maxpacksize == 392)
rate = 96000;
/* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 6ec33b62e6cf..eab06edcc9b7 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1097,11 +1097,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
append_ctl_name(kctl, control == UAC_FU_MUTE ?
" Switch" : " Volume");
if (control == UAC_FU_VOLUME) {
- kctl->tlv.c = mixer_vol_tlv;
- kctl->vd[0].access |=
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
check_mapped_dB(map, cval);
+ if (cval->dBmin < cval->dBmax) {
+ kctl->tlv.c = mixer_vol_tlv;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
}
break;
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 73dcc8256bc0..9146cffa6ede 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -61,6 +61,7 @@ static const struct rc_config {
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
+ { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
@@ -188,6 +189,12 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
!value, 0, NULL, 0, 100);
+ /* USB X-Fi S51 Pro */
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ !value, 0, NULL, 0, 100);
else
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
@@ -234,9 +241,13 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
/* USB X-Fi S51 doesn't have a CMSS LED */
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
continue;
+ /* USB X-Fi S51 Pro doesn't have one either */
+ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
+ continue;
if (i > 1 && /* Live24ext has 2 LEDs only */
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
break;
err = snd_ctl_add(mixer->chip->card,
@@ -512,6 +523,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x041e, 0x3020):
case USB_ID(0x041e, 0x3040):
case USB_ID(0x041e, 0x3042):
+ case USB_ID(0x041e, 0x30df):
case USB_ID(0x041e, 0x3048):
err = snd_audigy2nx_controls_create(mixer);
if (err < 0)
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index c66d3f64dcf8..78792a8900c3 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -1651,6 +1651,32 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ USB_DEVICE(0x0582, 0x0127),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "GR-55", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
@@ -1953,7 +1979,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE(0x0763, 0x2080),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra", */
@@ -2020,7 +2046,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE(0x0763, 0x2081),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8R", */
@@ -2179,6 +2205,17 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* KORG devices */
+{
+ USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "KORG, Inc.",
+ /* .product_name = "PANDORA PX5D", */
+ .ifnum = 3,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ }
+},
+
/* AKAI devices */
{
USB_DEVICE(0x09e8, 0x0062),
@@ -2332,6 +2369,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Native Instruments MK2 series */
{
+ /* Komplete Audio 6 */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x17cc,
+ .idProduct = 0x1000,
+},
+{
/* Traktor Audio 6 */
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = 0x17cc,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index ec07e62e53f3..bd13d7257240 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -533,12 +533,14 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
case USB_ID(0x0d8c, 0x0102):
/* C-Media CM6206 / CM106-Like Sound Device */
+ case USB_ID(0x0ccd, 0x00b1): /* Terratec Aureon 7.1 USB */
return snd_usb_cm6206_boot_quirk(dev);
case USB_ID(0x133e, 0x0815):
/* Access Music VirusTI Desktop */
return snd_usb_accessmusic_boot_quirk(dev);
+ case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
return snd_usb_nativeinstruments_boot_quirk(dev);