aboutsummaryrefslogtreecommitdiff
path: root/sound/pci
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-05-20 09:41:44 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-05-20 09:41:44 -0700
commit7f06a8b26aba1dc03b42272dc0089a800372c575 (patch)
tree7c67198f83d069eb13fd417e022d111b7e4c82a1 /sound/pci
parentc3ad33c9bcb6616999953af76f16318120fe3691 (diff)
parentd71f4cece4bd97d05592836202fc04ff2e7817e3 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (250 commits) ALSA: hda: Storage class should be before const qualifier ASoC: tpa6130a2: Remove CPVSS and HPVdd supplies ASoC: tpa6130a2: Define output pins with SND_SOC_DAPM_OUTPUT ASoC: sdp4430 - add sdp4430 pcm ops to DAI. ASoC: TWL6040: Enable earphone path in codec ASoC: SDP4430: Add support for Earphone speaker ASoC: SDP4430: Add sdp4430 machine driver ASoC: tlv320dac33: Avoid powering off while in BIAS_OFF ASoC: tlv320dac33: Use dev_dbg in dac33_hard_power function ALSA: sound/pci/asihpi: Use kzalloc ALSA: hdmi - dont fail on extra nodes ALSA: intelhdmi - add id for the CougarPoint chipset ALSA: intelhdmi - user friendly codec name ALSA: intelhdmi - add dependency on SND_DYNAMIC_MINORS ALSA: asihpi: incorrect range check ALSA: asihpi: testing the wrong variable ALSA: es1688: add pedantic range checks ARM: McBSP: Add support for omap4 in McBSP driver ARM: McBSP: Fix request for irq in OMAP4 OMAP: McBSP: Add 32-bit mode support ...
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/Kconfig32
-rw-r--r--sound/pci/Makefile1
-rw-r--r--sound/pci/asihpi/Makefile5
-rw-r--r--sound/pci/asihpi/asihpi.c3002
-rw-r--r--sound/pci/asihpi/hpi.h2001
-rw-r--r--sound/pci/asihpi/hpi6000.c1840
-rw-r--r--sound/pci/asihpi/hpi6000.h70
-rw-r--r--sound/pci/asihpi/hpi6205.c2331
-rw-r--r--sound/pci/asihpi/hpi6205.h93
-rw-r--r--sound/pci/asihpi/hpi_internal.h1641
-rw-r--r--sound/pci/asihpi/hpicmn.c643
-rw-r--r--sound/pci/asihpi/hpicmn.h64
-rw-r--r--sound/pci/asihpi/hpidebug.c225
-rw-r--r--sound/pci/asihpi/hpidebug.h385
-rw-r--r--sound/pci/asihpi/hpidspcd.c172
-rw-r--r--sound/pci/asihpi/hpidspcd.h104
-rw-r--r--sound/pci/asihpi/hpifunc.c3864
-rw-r--r--sound/pci/asihpi/hpimsginit.c130
-rw-r--r--sound/pci/asihpi/hpimsginit.h40
-rw-r--r--sound/pci/asihpi/hpimsgx.c907
-rw-r--r--sound/pci/asihpi/hpimsgx.h36
-rw-r--r--sound/pci/asihpi/hpioctl.c484
-rw-r--r--sound/pci/asihpi/hpioctl.h38
-rw-r--r--sound/pci/asihpi/hpios.c114
-rw-r--r--sound/pci/asihpi/hpios.h178
-rw-r--r--sound/pci/asihpi/hpipcida.h37
-rw-r--r--sound/pci/cs4281.c40
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c19
-rw-r--r--sound/pci/emu10k1/emuproc.c51
-rw-r--r--sound/pci/es1968.c129
-rw-r--r--sound/pci/hda/Kconfig1
-rw-r--r--sound/pci/hda/hda_codec.c76
-rw-r--r--sound/pci/hda/hda_codec.h3
-rw-r--r--sound/pci/hda/hda_intel.c109
-rw-r--r--sound/pci/hda/patch_analog.c21
-rw-r--r--sound/pci/hda/patch_conexant.c157
-rw-r--r--sound/pci/hda/patch_hdmi.c10
-rw-r--r--sound/pci/hda/patch_intelhdmi.c21
-rw-r--r--sound/pci/hda/patch_realtek.c238
-rw-r--r--sound/pci/ice1712/aureon.c89
-rw-r--r--sound/pci/maestro3.c139
-rw-r--r--sound/pci/mixart/mixart.c79
42 files changed, 19305 insertions, 314 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 1298c68d6bf..e7a8cd058ef 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -58,6 +58,18 @@ config SND_ALI5451
To compile this driver as a module, choose M here: the module
will be called snd-ali5451.
+config SND_ASIHPI
+ tristate "AudioScience ASIxxxx"
+ depends on X86
+ select FW_LOADER
+ select SND_PCM
+ select SND_HWDEP
+ help
+ Say Y here to include support for AudioScience ASI sound cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-asihpi.
+
config SND_ATIIXP
tristate "ATI IXP AC97 Controller"
select SND_AC97_CODEC
@@ -501,6 +513,16 @@ config SND_ES1968
To compile this driver as a module, choose M here: the module
will be called snd-es1968.
+config SND_ES1968_INPUT
+ bool "Enable input device for es1968 volume buttons"
+ depends on SND_ES1968
+ depends on INPUT=y || INPUT=SND_ES1968
+ help
+ If you say Y here, you will get an input device which reports
+ keypresses for the volume buttons connected to the es1968 chip.
+ If you say N the buttons will directly control the master volume.
+ It is recommended to say Y.
+
config SND_FM801
tristate "ForteMedia FM801"
select SND_OPL3_LIB
@@ -655,6 +677,16 @@ config SND_MAESTRO3
To compile this driver as a module, choose M here: the module
will be called snd-maestro3.
+config SND_MAESTRO3_INPUT
+ bool "Enable input device for maestro3 volume buttons"
+ depends on SND_MAESTRO3
+ depends on INPUT=y || INPUT=SND_MAESTRO3
+ help
+ If you say Y here, you will get an input device which reports
+ keypresses for the volume buttons connected to the maestro3 chip.
+ If you say N the buttons will directly control the master volume.
+ It is recommended to say Y.
+
config SND_MIXART
tristate "Digigram miXart"
select SND_HWDEP
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index ecfc609d2b9..9cf4348ec13 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
obj-$(CONFIG_SND) += \
ac97/ \
ali5451/ \
+ asihpi/ \
au88x0/ \
aw2/ \
ctxfi/ \
diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile
new file mode 100644
index 00000000000..391830a4556
--- /dev/null
+++ b/sound/pci/asihpi/Makefile
@@ -0,0 +1,5 @@
+snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
+ hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
+ hpios.o hpi6000.o hpi6205.o hpimsgx.o
+
+obj-$(CONFIG_SND_ASIHPI) += snd-asihpi.o
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
new file mode 100644
index 00000000000..f74c7372b3d
--- /dev/null
+++ b/sound/pci/asihpi/asihpi.c
@@ -0,0 +1,3002 @@
+/*
+ * Asihpi soundcard
+ * Copyright (c) by AudioScience Inc <alsa@audioscience.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation;
+ *
+ * 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
+ *
+ *
+ * The following is not a condition of use, merely a request:
+ * If you modify this program, particularly if you fix errors, AudioScience Inc
+ * would appreciate it if you grant us the right to use those modifications
+ * for any purpose including commercial applications.
+ */
+/* >0: print Hw params, timer vars. >1: print stream write/copy sizes */
+#define REALLY_VERBOSE_LOGGING 0
+
+#if REALLY_VERBOSE_LOGGING
+#define VPRINTK1 snd_printd
+#else
+#define VPRINTK1(...)
+#endif
+
+#if REALLY_VERBOSE_LOGGING > 1
+#define VPRINTK2 snd_printd
+#else
+#define VPRINTK2(...)
+#endif
+
+#ifndef ASI_STYLE_NAMES
+/* not sure how ALSA style name should look */
+#define ASI_STYLE_NAMES 1
+#endif
+
+#include "hpi_internal.h"
+#include "hpimsginit.h"
+#include "hpioctl.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#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");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int enable_hpi_hwdep = 1;
+
+module_param_array(index, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
+
+module_param_array(id, charp, NULL, S_IRUGO);
+MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
+
+module_param_array(enable, bool, NULL, S_IRUGO);
+MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
+
+module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(enable_hpi_hwdep,
+ "ALSA enable HPI hwdep for AudioScience soundcard ");
+
+/* identify driver */
+#ifdef KERNEL_ALSA_BUILD
+static char *build_info = "built using headers from kernel source";
+module_param(build_info, charp, S_IRUGO);
+MODULE_PARM_DESC(build_info, "built using headers from kernel source");
+#else
+static char *build_info = "built within ALSA source";
+module_param(build_info, charp, S_IRUGO);
+MODULE_PARM_DESC(build_info, "built within ALSA source");
+#endif
+
+/* set to 1 to dump every control from adapter to log */
+static const int mixer_dump;
+
+#define DEFAULT_SAMPLERATE 44100
+static int adapter_fs = DEFAULT_SAMPLERATE;
+
+static struct hpi_hsubsys *ss; /* handle to HPI audio subsystem */
+
+/* defaults */
+#define PERIODS_MIN 2
+#define PERIOD_BYTES_MIN 2304
+#define BUFFER_BYTES_MAX (512 * 1024)
+
+/*#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 {
+ int source;
+ int index;
+ char *name;
+};
+
+struct clk_cache {
+ int count;
+ int has_local;
+ struct clk_source s[MAX_CLOCKSOURCES];
+};
+
+/* Per card data */
+struct snd_card_asihpi {
+ struct snd_card *card;
+ struct pci_dev *pci;
+ u16 adapter_index;
+ u32 serial_number;
+ u16 type;
+ u16 version;
+ u16 num_outstreams;
+ u16 num_instreams;
+
+ u32 h_mixer;
+ struct clk_cache cc;
+
+ u16 support_mmap;
+ u16 support_grouping;
+ u16 support_mrx;
+ u16 update_interval_frames;
+ u16 in_max_chans;
+ u16 out_max_chans;
+};
+
+/* Per stream data */
+struct snd_card_asihpi_pcm {
+ struct timer_list timer;
+ unsigned int respawn_timer;
+ unsigned int hpi_buffer_attached;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int bytes_per_sec;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ unsigned int pcm_buf_pos; /* position in buffer */
+ struct snd_pcm_substream *substream;
+ u32 h_stream;
+ struct hpi_format format;
+};
+
+/* universal stream verbs work with out or in stream handles */
+
+/* Functions to allow driver to give a buffer to HPI for busmastering */
+
+static u16 hpi_stream_host_buffer_attach(
+ struct hpi_hsubsys *hS,
+ u32 h_stream, /* handle to outstream. */
+ u32 size_in_bytes, /* size in bytes of bus mastering buffer */
+ u32 pci_address
+)
+{
+ struct hpi_message hm;
+ struct hpi_response hr;
+ unsigned int obj = hpi_handle_object(h_stream);
+
+ if (!h_stream)
+ return HPI_ERROR_INVALID_OBJ;
+ hpi_init_message_response(&hm, &hr, obj,
+ obj == HPI_OBJ_OSTREAM ?
+ HPI_OSTREAM_HOSTBUFFER_ALLOC :
+ HPI_ISTREAM_HOSTBUFFER_ALLOC);
+
+ hpi_handle_to_indexes(h_stream, &hm.adapter_index,
+ &hm.obj_index);
+
+ hm.u.d.u.buffer.buffer_size = size_in_bytes;
+ hm.u.d.u.buffer.pci_address = pci_address;
+ hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER;
+ hpi_send_recv(&hm, &hr);
+ return hr.error;
+}
+
+static u16 hpi_stream_host_buffer_detach(
+ struct hpi_hsubsys *hS,
+ u32 h_stream
+)
+{
+ struct hpi_message hm;
+ struct hpi_response hr;
+ unsigned int obj = hpi_handle_object(h_stream);
+
+ if (!h_stream)
+ return HPI_ERROR_INVALID_OBJ;
+
+ hpi_init_message_response(&hm, &hr, obj,
+ obj == HPI_OBJ_OSTREAM ?
+ HPI_OSTREAM_HOSTBUFFER_FREE :
+ HPI_ISTREAM_HOSTBUFFER_FREE);
+
+ hpi_handle_to_indexes(h_stream, &hm.adapter_index,
+ &hm.obj_index);
+ hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER;
+ hpi_send_recv(&hm, &hr);
+ return hr.error;
+}
+
+static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream)
+{
+ if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
+ return hpi_outstream_start(hS, h_stream);
+ else
+ return hpi_instream_start(hS, h_stream);
+}
+
+static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream)
+{
+ if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
+ return hpi_outstream_stop(hS, h_stream);
+ else
+ return hpi_instream_stop(hS, h_stream);
+}
+
+static inline u16 hpi_stream_get_info_ex(
+ struct hpi_hsubsys *hS,
+ u32 h_stream,
+ u16 *pw_state,
+ u32 *pbuffer_size,
+ u32 *pdata_in_buffer,
+ u32 *psample_count,
+ u32 *pauxiliary_data
+)
+{
+ if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
+ return hpi_outstream_get_info_ex(hS, h_stream, pw_state,
+ pbuffer_size, pdata_in_buffer,
+ psample_count, pauxiliary_data);
+ else
+ return hpi_instream_get_info_ex(hS, h_stream, pw_state,
+ pbuffer_size, pdata_in_buffer,
+ psample_count, pauxiliary_data);
+}
+
+static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS,
+ u32 h_master,
+ u32 h_stream)
+{
+ if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM)
+ return hpi_outstream_group_add(hS, h_master, h_stream);
+ else
+ return hpi_instream_group_add(hS, h_master, h_stream);
+}
+
+static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS,
+ u32 h_stream)
+{
+ if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
+ return hpi_outstream_group_reset(hS, h_stream);
+ else
+ return hpi_instream_group_reset(hS, h_stream);
+}
+
+static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS,
+ u32 h_stream, u32 *mo, u32 *mi)
+{
+ if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
+ return hpi_outstream_group_get_map(hS, h_stream, mo, mi);
+ else
+ return hpi_instream_group_get_map(hS, h_stream, mo, mi);
+}
+
+static u16 handle_error(u16 err, int line, char *filename)
+{
+ if (err)
+ printk(KERN_WARNING
+ "in file %s, line %d: HPI error %d\n",
+ filename, line, err);
+ return err;
+}
+
+#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
+
+/***************************** GENERAL PCM ****************/
+#if REALLY_VERBOSE_LOGGING
+static void print_hwparams(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));
+}
+#else
+#define print_hwparams(x)
+#endif
+
+static snd_pcm_format_t hpi_to_alsa_formats[] = {
+ -1, /* INVALID */
+ SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */
+ SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */
+ -1, /* HPI_FORMAT_MPEG_L1 3 */
+ SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */
+ SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */
+ -1, /* HPI_FORMAT_DOLBY_AC2 6 */
+ -1, /* HPI_FORMAT_DOLBY_AC3 7 */
+ SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */
+ -1, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */
+ -1, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */
+ SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */
+ -1, /* HPI_FORMAT_RAW_BITSTREAM 12 */
+ -1, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */
+ SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */
+#if 1
+ /* ALSA can't handle 3 byte sample size together with power-of-2
+ * constraint on buffer_bytes, so disable this format
+ */
+ -1
+#else
+ /* SNDRV_PCM_FORMAT_S24_3LE */ /* { HPI_FORMAT_PCM24_SIGNED 15 */
+#endif
+};
+
+
+static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
+ u16 *hpi_format)
+{
+ u16 format;
+
+ for (format = HPI_FORMAT_PCM8_UNSIGNED;
+ format <= HPI_FORMAT_PCM24_SIGNED; format++) {
+ if (hpi_to_alsa_formats[format] == alsa_format) {
+ *hpi_format = format;
+ return 0;
+ }
+ }
+
+ snd_printd(KERN_WARNING "failed match for alsa format %d\n",
+ alsa_format);
+ *hpi_format = 0;
+ return -EINVAL;
+}
+
+static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
+ struct snd_pcm_hardware *pcmhw)
+{
+ u16 err;
+ u32 h_control;
+ u32 sample_rate;
+ int idx;
+ unsigned int rate_min = 200000;
+ unsigned int rate_max = 0;
+ unsigned int rates = 0;
+
+ if (asihpi->support_mrx) {
+ rates |= SNDRV_PCM_RATE_CONTINUOUS;
+ rates |= SNDRV_PCM_RATE_8000_96000;
+ rate_min = 8000;
+ rate_max = 100000;
+ } else {
+ /* on cards without SRC,
+ valid rates are determined by sampleclock */
+ err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
+ HPI_CONTROL_SAMPLECLOCK, &h_control);
+ if (err) {
+ snd_printk(KERN_ERR
+ "no local sampleclock, err %d\n", err);
+ }
+
+ for (idx = 0; idx < 100; idx++) {
+ if (hpi_sample_clock_query_local_rate(ss,
+ h_control, idx, &sample_rate)) {
+ if (!idx)
+ snd_printk(KERN_ERR
+ "local rate query failed\n");
+
+ break;
+ }
+
+ rate_min = min(rate_min, sample_rate);
+ rate_max = max(rate_max, sample_rate);
+
+ switch (sample_rate) {
+ case 5512:
+ rates |= SNDRV_PCM_RATE_5512;
+ break;
+ case 8000:
+ rates |= SNDRV_PCM_RATE_8000;
+ break;
+ case 11025:
+ rates |= SNDRV_PCM_RATE_11025;
+ break;
+ case 16000:
+ rates |= SNDRV_PCM_RATE_16000;
+ break;
+ case 22050:
+ rates |= SNDRV_PCM_RATE_22050;
+ break;
+ case 32000:
+ rates |= SNDRV_PCM_RATE_32000;
+ break;
+ case 44100:
+ rates |= SNDRV_PCM_RATE_44100;
+ break;
+ case 48000:
+ rates |= SNDRV_PCM_RATE_48000;
+ break;
+ case 64000:
+ rates |= SNDRV_PCM_RATE_64000;
+ break;
+ case 88200:
+ rates |= SNDRV_PCM_RATE_88200;
+ break;
+ case 96000:
+ rates |= SNDRV_PCM_RATE_96000;
+ break;
+ case 176400:
+ rates |= SNDRV_PCM_RATE_176400;
+ break;
+ case 192000:
+ rates |= SNDRV_PCM_RATE_192000;
+ break;
+ default: /* some other rate */
+ rates |= SNDRV_PCM_RATE_KNOT;
+ }
+ }
+ }
+
+ /* printk(KERN_INFO "Supported rates %X %d %d\n",
+ rates, rate_min, rate_max); */
+ pcmhw->rates = rates;
+ pcmhw->rate_min = rate_min;
+ pcmhw->rate_max = rate_max;
+}
+
+static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
+ int err;
+ u16 format;
+ unsigned int bytes_per_sec;
+
+ print_hwparams(params);
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (err < 0)
+ return err;
+ err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
+ if (err)
+ return err;
+
+ VPRINTK1(KERN_INFO "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));
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (hpi_instream_reset(ss, dpcm->h_stream) != 0)
+ return -EINVAL;
+
+ if (hpi_instream_set_format(ss,
+ dpcm->h_stream, &dpcm->format) != 0)
+ return -EINVAL;
+ }
+
+ dpcm->hpi_buffer_attached = 0;
+ if (card->support_mmap) {
+
+ err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream,
+ params_buffer_bytes(params), runtime->dma_addr);
+ if (err == 0) {
+ snd_printd(KERN_INFO
+ "stream_host_buffer_attach succeeded %u %lu\n",
+ params_buffer_bytes(params),
+ (unsigned long)runtime->dma_addr);
+ } else {
+ snd_printd(KERN_INFO
+ "stream_host_buffer_attach error %d\n",
+ err);
+ return -ENOMEM;
+ }
+
+ err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL,
+ &dpcm->hpi_buffer_attached,
+ NULL, NULL, NULL);
+
+ snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n",
+ dpcm->hpi_buffer_attached);
+ }
+ bytes_per_sec = params_rate(params) * params_channels(params);
+ bytes_per_sec *= snd_pcm_format_width(params_format(params));
+ bytes_per_sec /= 8;
+ if (bytes_per_sec <= 0)
+ return -EINVAL;
+
+ dpcm->bytes_per_sec = bytes_per_sec;
+ dpcm->pcm_size = params_buffer_bytes(params);
+ dpcm->pcm_count = params_period_bytes(params);
+ snd_printd(KERN_INFO "pcm_size=%d, pcm_count=%d, bps=%d\n",
+ dpcm->pcm_size, dpcm->pcm_count, bytes_per_sec);
+
+ dpcm->pcm_irq_pos = 0;
+ dpcm->pcm_buf_pos = 0;
+ return 0;
+}
+
+static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
+ substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ int expiry;
+
+ expiry = (dpcm->pcm_count * HZ / dpcm->bytes_per_sec);
+ /* wait longer the first time, for samples to propagate */
+ expiry = max(expiry, 20);
+ dpcm->timer.expires = jiffies + expiry;
+ dpcm->respawn_timer = 1;
+ add_timer(&dpcm->timer);
+}
+
+static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+
+ dpcm->respawn_timer = 0;
+ del_timer(&dpcm->timer);
+}
+
+static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
+ struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ u16 e;
+
+ snd_printd("trigger %dstream %d\n",
+ substream->stream, substream->number);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct snd_card_asihpi_pcm *ds;
+ ds = s->runtime->private_data;
+
+ if (snd_pcm_substream_chip(s) != card)
+ continue;
+
+ if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (card->support_mmap)) {
+ /* How do I know how much valid data is present
+ * in buffer? Just guessing 2 periods, but if
+ * buffer is bigger it may contain even more
+ * data??
+ */
+ unsigned int preload = ds->pcm_count * 2;
+ VPRINTK2("preload %d\n", preload);
+ hpi_handle_error(hpi_outstream_write_buf(
+ ss, ds->h_stream,
+ &s->runtime->dma_area[0],
+ preload,
+ &ds->format));
+ }
+
+ if (card->support_grouping) {
+ VPRINTK1("\t_group %dstream %d\n", s->stream,
+ s->number);
+ e = hpi_stream_group_add(ss,
+ dpcm->h_stream,
+ ds->h_stream);
+ if (!e) {
+ snd_pcm_trigger_done(s, substream);
+ } else {
+ hpi_handle_error(e);
+ break;
+ }
+ } else
+ break;
+ }
+ snd_printd("start\n");
+ /* start the master stream */
+ snd_card_asihpi_pcm_timer_start(substream);
+ hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_card_asihpi_pcm_timer_stop(substream);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (snd_pcm_substream_chip(s) != card)
+ continue;
+
+ /*? workaround linked streams don't
+ transition to SETUP 20070706*/
+ s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
+
+ if (card->support_grouping) {
+ VPRINTK1("\t_group %dstream %d\n", s->stream,
+ s->number);
+ snd_pcm_trigger_done(s, substream);
+ } else
+ break;
+ }
+ snd_printd("stop\n");
+
+ /* _prepare and _hwparams reset the stream */
+ hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ hpi_handle_error(
+ hpi_outstream_reset(ss, dpcm->h_stream));
+
+ if (card->support_grouping)
+ hpi_handle_error(hpi_stream_group_reset(ss,
+ dpcm->h_stream));
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_printd("pause release\n");
+ hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
+ snd_card_asihpi_pcm_timer_start(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_printd("pause\n");
+ snd_card_asihpi_pcm_timer_stop(substream);
+ hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+ break;
+ default:
+ snd_printd("\tINVALID\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ if (dpcm->hpi_buffer_attached)
+ hpi_stream_host_buffer_detach(ss, dpcm->h_stream);
+
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ kfree(dpcm);
+}
+
+/*algorithm outline
+ Without linking degenerates to getting single stream pos etc
+ Without mmap 2nd loop degenerates to snd_pcm_period_elapsed
+*/
+/*
+buf_pos=get_buf_pos(s);
+for_each_linked_stream(s) {
+ buf_pos=get_buf_pos(s);
+ min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size)
+ new_data = min(new_data, calc_new_data(buf_pos,irq_pos)
+}
+timer.expires = jiffies + predict_next_period_ready(min_buf_pos);
+for_each_linked_stream(s) {