]> nv-tegra.nvidia Code Review - linux-3.10.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Mar 2011 17:46:37 +0000 (10:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Mar 2011 17:46:37 +0000 (10:46 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (308 commits)
  ALSA: sound/pci/asihpi: check adapter index in hpi_ioctl
  ALSA: aloop - Fix possible IRQ lock inversion
  ALSA: sound/core: merge list_del()/list_add_tail() to list_move_tail()
  ALSA: ctxfi - use list_move() instead of list_del()/list_add() combination
  ALSA: firewire - msleep needs delay.h
  ALSA: firewire-lib, firewire-speakers: handle packet queueing errors
  ALSA: firewire-lib: allocate DMA buffer separately
  ALSA: firewire-lib: use no-info SYT for packets without SYT sample
  ALSA: add LaCie FireWire Speakers/Griffin FireWave Surround driver
  ALSA: hda - Remove an unused variable in patch_realtek.c
  ALSA: hda - pin-adc-mux-dmic auto-configuration of 92HD8X codecs
  ALSA: hda - fix digital mic selection in mixer on 92HD8X codecs
  ALSA: hda - Move default input-src selection to init part
  ALSA: hda - Initialize special cases for input src in init phase
  ALSA: ctxfi - Clear input settings before initialization
  ALSA: ctxfi - Fix SPDIF status retrieval
  ALSA: ctxfi - Fix incorrect SPDIF status bit mask
  ALSA: ctxfi - Fix microphone boost codes/comments
  ALSA: atiixp - Fix wrong time-out checks during ac-link reset
  ALSA: intel8x0m: append 'm' to "r_intel8x0"
  ...

254 files changed:
arch/arm/mach-shmobile/board-ag5evm.c
arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-mackerel.c
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boards/mach-se/7724/setup.c
drivers/firewire/core-iso.c
drivers/firewire/core.h
include/linux/firewire.h
include/linux/mfd/wm8994/pdata.h
include/linux/mfd/wm8994/registers.h
include/sound/ac97_codec.h
include/sound/control.h
include/sound/cs4271.h [new file with mode: 0644]
include/sound/hdspm.h
include/sound/mixer_oss.h
include/sound/pcm.h
include/sound/sh_fsi.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/tlv320aic32x4.h [new file with mode: 0644]
include/sound/version.h
include/sound/wm8903.h
include/sound/wm9081.h
include/trace/events/asoc.h
sound/Kconfig
sound/Makefile
sound/core/control.c
sound/core/device.c
sound/core/memalloc.c
sound/core/oss/linear.c
sound/core/oss/mixer_oss.c
sound/core/oss/mulaw.c
sound/core/oss/pcm_oss.c
sound/core/oss/pcm_plugin.c
sound/core/oss/pcm_plugin.h
sound/core/oss/route.c
sound/core/pcm.c
sound/core/pcm_misc.c
sound/core/pcm_native.c
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_memory.c
sound/core/seq/seq_memory.h
sound/core/seq/seq_ports.c
sound/core/timer.c
sound/core/vmaster.c
sound/drivers/aloop.c
sound/firewire/Kconfig [new file with mode: 0644]
sound/firewire/Makefile [new file with mode: 0644]
sound/firewire/amdtp.c [new file with mode: 0644]
sound/firewire/amdtp.h [new file with mode: 0644]
sound/firewire/cmp.c [new file with mode: 0644]
sound/firewire/cmp.h [new file with mode: 0644]
sound/firewire/fcp.c [new file with mode: 0644]
sound/firewire/fcp.h [new file with mode: 0644]
sound/firewire/iso-resources.c [new file with mode: 0644]
sound/firewire/iso-resources.h [new file with mode: 0644]
sound/firewire/lib.c [new file with mode: 0644]
sound/firewire/lib.h [new file with mode: 0644]
sound/firewire/packets-buffer.c [new file with mode: 0644]
sound/firewire/packets-buffer.h [new file with mode: 0644]
sound/firewire/speakers.c [new file with mode: 0644]
sound/oss/soundcard.c
sound/pci/Kconfig
sound/pci/ac97/ac97_codec.c
sound/pci/ac97/ac97_patch.c
sound/pci/asihpi/asihpi.c
sound/pci/asihpi/hpi.h
sound/pci/asihpi/hpi6000.c
sound/pci/asihpi/hpi6205.c
sound/pci/asihpi/hpi6205.h
sound/pci/asihpi/hpi_internal.h
sound/pci/asihpi/hpicmn.c
sound/pci/asihpi/hpicmn.h
sound/pci/asihpi/hpidebug.c
sound/pci/asihpi/hpidebug.h
sound/pci/asihpi/hpidspcd.c
sound/pci/asihpi/hpidspcd.h
sound/pci/asihpi/hpifunc.c
sound/pci/asihpi/hpimsginit.c
sound/pci/asihpi/hpimsginit.h
sound/pci/asihpi/hpimsgx.c
sound/pci/asihpi/hpioctl.c
sound/pci/asihpi/hpios.h
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/au88x0/au88x0_eq.c
sound/pci/azt3328.c
sound/pci/ctxfi/ctatc.c
sound/pci/ctxfi/ctdaio.c
sound/pci/ctxfi/cthw20k2.c
sound/pci/ctxfi/ctmixer.c
sound/pci/ctxfi/ctvmem.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/intel8x0m.c
sound/pci/rme9652/hdspm.c
sound/ppc/pmac.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4104.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/cs4271.c [new file with mode: 0644]
sound/soc/codecs/dfbmcs320.c [new file with mode: 0644]
sound/soc/codecs/lm4857.c [new file with mode: 0644]
sound/soc/codecs/max98088.c
sound/soc/codecs/max9850.c [new file with mode: 0644]
sound/soc/codecs/max9850.h [new file with mode: 0644]
sound/soc/codecs/sgtl5000.c [new file with mode: 0644]
sound/soc/codecs/sgtl5000.h [new file with mode: 0644]
sound/soc/codecs/sn95031.c [new file with mode: 0644]
sound/soc/codecs/sn95031.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic32x4.c [new file with mode: 0644]
sound/soc/codecs/tlv320aic32x4.h [new file with mode: 0644]
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm8523.c
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8903.h
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8955.c
sound/soc/codecs/wm8961.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8978.c
sound/soc/codecs/wm8991.c [new file with mode: 0644]
sound/soc/codecs/wm8991.h [new file with mode: 0644]
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm8994-tables.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9090.c
sound/soc/codecs/wm_hubs.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/ep93xx/Kconfig
sound/soc/ep93xx/Makefile
sound/soc/ep93xx/edb93xx.c [new file with mode: 0644]
sound/soc/ep93xx/ep93xx-ac97.c
sound/soc/ep93xx/ep93xx-i2s.c
sound/soc/ep93xx/ep93xx-pcm.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/p1022_ds.c
sound/soc/imx/Kconfig
sound/soc/imx/Makefile
sound/soc/imx/imx-ssi.c
sound/soc/imx/mx27vis-aic32x4.c [new file with mode: 0644]
sound/soc/mid-x86/Kconfig [new file with mode: 0644]
sound/soc/mid-x86/Makefile [new file with mode: 0644]
sound/soc/mid-x86/mfld_machine.c [new file with mode: 0644]
sound/soc/mid-x86/sst_platform.c [new file with mode: 0644]
sound/soc/mid-x86/sst_platform.h [new file with mode: 0644]
sound/soc/omap/Kconfig
sound/soc/omap/rx51.c
sound/soc/pxa/raumfeld.c
sound/soc/pxa/tosa.c
sound/soc/pxa/z2.c
sound/soc/pxa/zylonite.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/ac97.c
sound/soc/samsung/ac97.h [deleted file]
sound/soc/samsung/dma.c
sound/soc/samsung/dma.h
sound/soc/samsung/goni_wm8994.c
sound/soc/samsung/h1940_uda1380.c
sound/soc/samsung/i2s.c
sound/soc/samsung/jive_wm8750.c
sound/soc/samsung/lm4857.h [deleted file]
sound/soc/samsung/ln2440sbc_alc650.c
sound/soc/samsung/neo1973_gta02_wm8753.c [deleted file]
sound/soc/samsung/neo1973_wm8753.c
sound/soc/samsung/pcm.c
sound/soc/samsung/pcm.h
sound/soc/samsung/rx1950_uda1380.c
sound/soc/samsung/s3c-i2s-v2.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/s3c24xx_simtec.c
sound/soc/samsung/s3c24xx_simtec_hermes.c
sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
sound/soc/samsung/s3c24xx_uda134x.c
sound/soc/samsung/smartq_wm8987.c
sound/soc/samsung/smdk2443_wm9710.c
sound/soc/samsung/smdk_spdif.c
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/smdk_wm9713.c
sound/soc/samsung/spdif.c
sound/soc/sh/fsi-ak4642.c
sound/soc/sh/fsi-da7210.c
sound/soc/sh/fsi-hdmi.c
sound/soc/sh/fsi.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-utils.c
sound/soc/tegra/Kconfig [new file with mode: 0644]
sound/soc/tegra/Makefile [new file with mode: 0644]
sound/soc/tegra/harmony.c [new file with mode: 0644]
sound/soc/tegra/tegra_asoc_utils.c [new file with mode: 0644]
sound/soc/tegra/tegra_asoc_utils.h [new file with mode: 0644]
sound/soc/tegra/tegra_das.c [new file with mode: 0644]
sound/soc/tegra/tegra_das.h [new file with mode: 0644]
sound/soc/tegra/tegra_i2s.c [new file with mode: 0644]
sound/soc/tegra/tegra_i2s.h [new file with mode: 0644]
sound/soc/tegra/tegra_pcm.c [new file with mode: 0644]
sound/soc/tegra/tegra_pcm.h [new file with mode: 0644]
sound/sound_core.c
sound/usb/6fire/Makefile [new file with mode: 0644]
sound/usb/6fire/chip.c [new file with mode: 0644]
sound/usb/6fire/chip.h [new file with mode: 0644]
sound/usb/6fire/comm.c [new file with mode: 0644]
sound/usb/6fire/comm.h [new file with mode: 0644]
sound/usb/6fire/common.h [new file with mode: 0644]
sound/usb/6fire/control.c [new file with mode: 0644]
sound/usb/6fire/control.h [new file with mode: 0644]
sound/usb/6fire/firmware.c [new file with mode: 0644]
sound/usb/6fire/firmware.h [new file with mode: 0644]
sound/usb/6fire/midi.c [new file with mode: 0644]
sound/usb/6fire/midi.h [new file with mode: 0644]
sound/usb/6fire/pcm.c [new file with mode: 0644]
sound/usb/6fire/pcm.h [new file with mode: 0644]
sound/usb/Kconfig
sound/usb/Makefile
sound/usb/caiaq/audio.c
sound/usb/caiaq/device.c
sound/usb/caiaq/device.h
sound/usb/card.c
sound/usb/midi.c
sound/usb/mixer.c
sound/usb/mixer.h
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/power.h [new file with mode: 0644]
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/usbaudio.h

index 4303a86e6e3848624a9ac1f6261a037c7021c6d9..3e6f0aab460bd2382552a5723c738a739fbc4610 100644 (file)
@@ -119,13 +119,6 @@ static struct platform_device keysc_device = {
 };
 
 /* FSI A */
-static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags = SH_FSI_OUT_SLAVE_MODE    |
-                      SH_FSI_IN_SLAVE_MODE     |
-                      SH_FSI_OFMT(I2S)         |
-                      SH_FSI_IFMT(I2S),
-};
-
 static struct resource fsi_resources[] = {
        [0] = {
                .name   = "FSI",
@@ -144,9 +137,6 @@ static struct platform_device fsi_device = {
        .id             = -1,
        .num_resources  = ARRAY_SIZE(fsi_resources),
        .resource       = fsi_resources,
-       .dev    = {
-               .platform_data  = &fsi_info,
-       },
 };
 
 static struct resource sh_mmcif_resources[] = {
index 81d6536552a97c61eec6976fe19c53b47432c20c..1a8118c929be26ebee043d90bb170b8677fa0157 100644 (file)
@@ -673,16 +673,12 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable)
 }
 
 static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags = SH_FSI_BRS_INV |
-                      SH_FSI_OUT_SLAVE_MODE |
-                      SH_FSI_IN_SLAVE_MODE |
-                      SH_FSI_OFMT(PCM) |
-                      SH_FSI_IFMT(PCM),
+       .porta_flags = SH_FSI_BRS_INV,
 
        .portb_flags = SH_FSI_BRS_INV |
                       SH_FSI_BRM_INV |
                       SH_FSI_LRS_INV |
-                      SH_FSI_OFMT(SPDIF),
+                      SH_FSI_FMT_SPDIF,
        .set_rate = fsi_set_rate,
 };
 
@@ -783,6 +779,10 @@ static struct platform_device hdmi_device = {
        },
 };
 
+static struct platform_device fsi_hdmi_device = {
+       .name           = "sh_fsi2_b_hdmi",
+};
+
 static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
                                unsigned long *parent_freq)
 {
@@ -936,6 +936,7 @@ static struct platform_device *ap4evb_devices[] __initdata = {
        &usb1_host_device,
        &fsi_device,
        &fsi_ak4643_device,
+       &fsi_hdmi_device,
        &sh_mmcif_device,
        &lcdc1_device,
        &lcdc_device,
index 1657eac5dde2c1bfee1a3a4bd799e47a884d0c76..1a63c213e45dfa96957a00cd402fca815327fd5a 100644 (file)
@@ -399,6 +399,10 @@ static struct platform_device hdmi_device = {
        },
 };
 
+static struct platform_device fsi_hdmi_device = {
+       .name           = "sh_fsi2_b_hdmi",
+};
+
 static int __init hdmi_init_pm_clock(void)
 {
        struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick");
@@ -609,16 +613,12 @@ fsi_set_rate_end:
 }
 
 static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags =  SH_FSI_BRS_INV          |
-                       SH_FSI_OUT_SLAVE_MODE   |
-                       SH_FSI_IN_SLAVE_MODE    |
-                       SH_FSI_OFMT(PCM)        |
-                       SH_FSI_IFMT(PCM),
+       .porta_flags =  SH_FSI_BRS_INV,
 
        .portb_flags =  SH_FSI_BRS_INV  |
                        SH_FSI_BRM_INV  |
                        SH_FSI_LRS_INV  |
-                       SH_FSI_OFMT(SPDIF),
+                       SH_FSI_FMT_SPDIF,
 
        .set_rate = fsi_set_rate,
 };
@@ -921,6 +921,7 @@ static struct platform_device *mackerel_devices[] __initdata = {
        &leds_device,
        &fsi_device,
        &fsi_ak4643_device,
+       &fsi_hdmi_device,
        &sdhi0_device,
 #if !defined(CONFIG_MMC_SH_MMCIF)
        &sdhi1_device,
index 3b71d2190de1b1782af836c668cb5f85da669d6f..e44480ce2ea8c32555c84e4f7caf3e033f65a850 100644 (file)
@@ -725,11 +725,7 @@ static struct platform_device camera_devices[] = {
 
 /* FSI */
 static struct sh_fsi_platform_info fsi_info = {
-       .portb_flags = SH_FSI_BRS_INV |
-                      SH_FSI_OUT_SLAVE_MODE |
-                      SH_FSI_IN_SLAVE_MODE |
-                      SH_FSI_OFMT(I2S) |
-                      SH_FSI_IFMT(I2S),
+       .portb_flags = SH_FSI_BRS_INV,
 };
 
 static struct resource fsi_resources[] = {
index 527679394a256ceed4e43789f03978a5d2274629..c8bcf6a19b55d7ce8f6a9425602f07be470e8d6a 100644 (file)
@@ -286,11 +286,7 @@ static struct platform_device ceu1_device = {
 /* FSI */
 /* change J20, J21, J22 pin to 1-2 connection to use slave mode */
 static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags = SH_FSI_BRS_INV |
-                      SH_FSI_OUT_SLAVE_MODE |
-                      SH_FSI_IN_SLAVE_MODE |
-                      SH_FSI_OFMT(PCM) |
-                      SH_FSI_IFMT(PCM),
+       .porta_flags = SH_FSI_BRS_INV,
 };
 
 static struct resource fsi_resources[] = {
index c003fa4e2db1f3ff85d0d6a17cf92cbdaca90000..c8658888e67bac701df8cf603b52220f5d219009 100644 (file)
@@ -362,3 +362,4 @@ void fw_iso_resource_manage(struct fw_card *card, int generation,
                *channel = ret;
        }
 }
+EXPORT_SYMBOL(fw_iso_resource_manage);
index f8dfcf1c6cbebe913b7fbef7a24f1da3d5e00dad..25e729cde2f74262b6caae39a4f51e6138e64b66 100644 (file)
@@ -147,9 +147,6 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
 /* -iso */
 
 int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
-void fw_iso_resource_manage(struct fw_card *card, int generation,
-                           u64 channels_mask, int *channel, int *bandwidth,
-                           bool allocate, __be32 buffer[2]);
 
 
 /* -topology */
index 9a3f5f9383f69748ff4cb5857694de9c6fe9e296..fc023d67676f6e18be83551c8543806bc05e08b8 100644 (file)
 #define CSR_BROADCAST_CHANNEL          0x234
 #define CSR_CONFIG_ROM                 0x400
 #define CSR_CONFIG_ROM_END             0x800
+#define CSR_OMPR                       0x900
+#define CSR_OPCR(i)                    (0x904 + (i) * 4)
+#define CSR_IMPR                       0x980
+#define CSR_IPCR(i)                    (0x984 + (i) * 4)
 #define CSR_FCP_COMMAND                        0xB00
 #define CSR_FCP_RESPONSE               0xD00
 #define CSR_FCP_END                    0xF00
@@ -441,5 +445,8 @@ int fw_iso_context_start(struct fw_iso_context *ctx,
                         int cycle, int sync, int tags);
 int fw_iso_context_stop(struct fw_iso_context *ctx);
 void fw_iso_context_destroy(struct fw_iso_context *ctx);
+void fw_iso_resource_manage(struct fw_card *card, int generation,
+                           u64 channels_mask, int *channel, int *bandwidth,
+                           bool allocate, __be32 buffer[2]);
 
 #endif /* _LINUX_FIREWIRE_H */
index 9eab263658beb99b043b2cc2c2f441cc25e120d8..466b1c777aff09033d449b92f6244bd701ff52f9 100644 (file)
@@ -103,13 +103,21 @@ struct wm8994_pdata {
         unsigned int lineout1fb:1;
         unsigned int lineout2fb:1;
 
-        /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
+       /* IRQ for microphone detection if brought out directly as a
+        * signal.
+        */
+       int micdet_irq;
+
+        /* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
         unsigned int micbias1_lvl:1;
         unsigned int micbias2_lvl:1;
 
-        /* Jack detect threashold levels, see datasheet for values */
+        /* WM8994 jack detect threashold levels, see datasheet for values */
         unsigned int jd_scthr:2;
         unsigned int jd_thr:2;
+
+       /* WM8958 microphone bias configuration */
+       int micbias[2];
 };
 
 #endif
index be072faec6f03e618f06b9ea206e82180adb2b01..f3ee84284670f5e42fc4871cdfc18bc2f7171d87 100644 (file)
@@ -63,6 +63,8 @@
 #define WM8994_MICBIAS                          0x3A
 #define WM8994_LDO_1                            0x3B
 #define WM8994_LDO_2                            0x3C
+#define WM8958_MICBIAS1                                0x3D
+#define WM8958_MICBIAS2                                0x3E
 #define WM8994_CHARGE_PUMP_1                    0x4C
 #define WM8958_CHARGE_PUMP_2                    0x4D
 #define WM8994_CLASS_W_1                        0x51
index b602f475cdbb9a4acd94d9870c083d29eeff43ff..f1dcefe4532bce8bccaab9ca896f447888983eec 100644 (file)
 #define AC97_FUNC_INFO         0x68    /* Function Information */
 #define AC97_SENSE_INFO                0x6a    /* Sense Details */
 
+/* volume controls */
+#define AC97_MUTE_MASK_MONO    0x8000
+#define AC97_MUTE_MASK_STEREO  0x8080
+
 /* slot allocation */
 #define AC97_SLOT_TAG          0
 #define AC97_SLOT_CMD_ADDR     1
 #define AC97_BC_18BIT_ADC      0x0100  /* 18-bit ADC resolution */
 #define AC97_BC_20BIT_ADC      0x0200  /* 20-bit ADC resolution */
 #define AC97_BC_ADC_MASK       0x0300
+#define AC97_BC_3D_TECH_ID_MASK        0x7c00  /* Per-vendor ID of 3D enhancement */
 
 /* general purpose */
 #define AC97_GP_DRSS_MASK      0x0c00  /* double rate slot select */
index 7715e6f00d3821af9d89085b08446d06a75763d5..e67db2869360292e68b7a38dcfd2f7e6d8c5404a 100644 (file)
@@ -115,6 +115,8 @@ int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
 int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
 int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
 int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+                       int active);
 struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid);
 struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id);
 
diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h
new file mode 100644 (file)
index 0000000..50a059e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Definitions for CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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.
+ */
+
+#ifndef __CS4271_H
+#define __CS4271_H
+
+struct cs4271_platform_data {
+       int gpio_nreset;        /* GPIO driving Reset pin, if any */
+};
+
+#endif /* __CS4271_H */
index 81990b2bcc981bb4f31cae44616aa052c5f1066e..1774ff5ff6321eb44eeaa68b51481898e3187d1f 100644 (file)
@@ -3,8 +3,8 @@
 /*
  *   Copyright (C) 2003 Winfried Ritsch (IEM)
  *   based on hdsp.h from Thomas Charbonnel (thomas@undata.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
 /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
 #define HDSPM_MAX_CHANNELS      64
 
-/* -------------------- IOCTL Peak/RMS Meters -------------------- */
-
-/* peam rms level structure like we get from hardware 
-  
-   maybe in future we can memory map it so I just copy it
-   to user on ioctl call now an dont change anything
-   rms are made out of low and high values
-   where (long) ????_rms = (????_rms_l >> 8) + ((????_rms_h & 0xFFFFFF00)<<24)
-   (i asume so from the code)
-*/
-
-struct hdspm_peak_rms {
-
-       unsigned int level_offset[1024];
+enum hdspm_io_type {
+       MADI,
+       MADIface,
+       AIO,
+       AES32,
+       RayDAT
+};
 
-       unsigned int input_peak[64];
-       unsigned int playback_peak[64];
-       unsigned int output_peak[64];
-       unsigned int xxx_peak[64];      /* not used */
+enum hdspm_speed {
+       ss,
+       ds,
+       qs
+};
 
-       unsigned int reserved[256];     /* not used */
+/* -------------------- IOCTL Peak/RMS Meters -------------------- */
 
-       unsigned int input_rms_l[64];
-       unsigned int playback_rms_l[64];
-       unsigned int output_rms_l[64];
-       unsigned int xxx_rms_l[64];     /* not used */
+struct hdspm_peak_rms {
+       uint32_t input_peaks[64];
+       uint32_t playback_peaks[64];
+       uint32_t output_peaks[64];
 
-       unsigned int input_rms_h[64];
-       unsigned int playback_rms_h[64];
-       unsigned int output_rms_h[64];
-       unsigned int xxx_rms_h[64];     /* not used */
-};
+       uint64_t input_rms[64];
+       uint64_t playback_rms[64];
+       uint64_t output_rms[64];
 
-struct hdspm_peak_rms_ioctl {
-       struct hdspm_peak_rms *peak;
+       uint8_t speed; /* enum {ss, ds, qs} */
+       int status2;
 };
 
-/* use indirect access due to the limit of ioctl bit size */
 #define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS \
-       _IOR('H', 0x40, struct hdspm_peak_rms_ioctl)
+       _IOR('H', 0x42, struct hdspm_peak_rms)
 
 /* ------------ CONFIG block IOCTL ---------------------- */
 
-struct hdspm_config_info {
+struct hdspm_config {
        unsigned char pref_sync_ref;
        unsigned char wordclock_sync_check;
        unsigned char madi_sync_check;
@@ -80,18 +71,121 @@ struct hdspm_config_info {
        unsigned int analog_out;
 };
 
-#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO \
-       _IOR('H', 0x41, struct hdspm_config_info)
+#define SNDRV_HDSPM_IOCTL_GET_CONFIG \
+       _IOR('H', 0x41, struct hdspm_config)
+
+/**
+ * If there's a TCO (TimeCode Option) board installed,
+ * there are further options and status data available.
+ * The hdspm_ltc structure contains the current SMPTE
+ * timecode and some status information and can be
+ * obtained via SNDRV_HDSPM_IOCTL_GET_LTC or in the
+ * hdspm_status struct.
+ **/
+
+enum hdspm_ltc_format {
+       format_invalid,
+       fps_24,
+       fps_25,
+       fps_2997,
+       fps_30
+};
+
+enum hdspm_ltc_frame {
+       frame_invalid,
+       drop_frame,
+       full_frame
+};
+
+enum hdspm_ltc_input_format {
+       ntsc,
+       pal,
+       no_video
+};
+
+struct hdspm_ltc {
+       unsigned int ltc;
 
+       enum hdspm_ltc_format format;
+       enum hdspm_ltc_frame frame;
+       enum hdspm_ltc_input_format input_format;
+};
+
+#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl)
+
+/**
+ * The status data reflects the device's current state
+ * as determined by the card's configuration and
+ * connection status.
+ **/
+
+enum hdspm_sync {
+       hdspm_sync_no_lock = 0,
+       hdspm_sync_lock = 1,
+       hdspm_sync_sync = 2
+};
 
-/* get Soundcard Version */
+enum hdspm_madi_input {
+       hdspm_input_optical = 0,
+       hdspm_input_coax = 1
+};
+
+enum hdspm_madi_channel_format {
+       hdspm_format_ch_64 = 0,
+       hdspm_format_ch_56 = 1
+};
+
+enum hdspm_madi_frame_format {
+       hdspm_frame_48 = 0,
+       hdspm_frame_96 = 1
+};
+
+enum hdspm_syncsource {
+       syncsource_wc = 0,
+       syncsource_madi = 1,
+       syncsource_tco = 2,
+       syncsource_sync = 3,
+       syncsource_none = 4
+};
+
+struct hdspm_status {
+       uint8_t card_type; /* enum hdspm_io_type */
+       enum hdspm_syncsource autosync_source;
+
+       uint64_t card_clock;
+       uint32_t master_period;
+
+       union {
+               struct {
+                       uint8_t sync_wc; /* enum hdspm_sync */
+                       uint8_t sync_madi; /* enum hdspm_sync */
+                       uint8_t sync_tco; /* enum hdspm_sync */
+                       uint8_t sync_in; /* enum hdspm_sync */
+                       uint8_t madi_input; /* enum hdspm_madi_input */
+                       uint8_t channel_format; /* enum hdspm_madi_channel_format */
+                       uint8_t frame_format; /* enum hdspm_madi_frame_format */
+               } madi;
+       } card_specific;
+};
+
+#define SNDRV_HDSPM_IOCTL_GET_STATUS \
+       _IOR('H', 0x47, struct hdspm_status)
+
+/**
+ * Get information about the card and its add-ons.
+ **/
+
+#define HDSPM_ADDON_TCO 1
 
 struct hdspm_version {
+       uint8_t card_type; /* enum hdspm_io_type */
+       char cardname[20];
+       unsigned int serial;
        unsigned short firmware_rev;
+       int addons;
 };
 
-#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x43, struct hdspm_version)
-
+#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x48, struct hdspm_version)
 
 /* ------------- get Matrix Mixer IOCTL --------------- */
 
@@ -103,7 +197,7 @@ struct hdspm_version {
 /* equivalent to hardware definition, maybe for future feature of mmap of
  * them
  */
-/* each of 64 outputs has 64 infader and 64 outfader: 
+/* each of 64 outputs has 64 infader and 64 outfader:
    Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */
 
 #define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS
@@ -131,4 +225,5 @@ typedef struct hdspm_version hdspm_version_t;
 typedef struct hdspm_channelfader snd_hdspm_channelfader_t;
 typedef struct hdspm_mixer hdspm_mixer_t;
 
-#endif                         /* __SOUND_HDSPM_H */
+
+#endif
index 51fbcb4a277a611fc836d96bb3a714f5fc60b01f..13cb0b430a1b755e108e1734fdcb5a013170f018 100644 (file)
@@ -73,6 +73,9 @@ struct snd_mixer_oss_file {
        struct snd_mixer_oss *mixer;
 };
 
+int snd_mixer_oss_ioctl_card(struct snd_card *card,
+                            unsigned int cmd, unsigned long arg);
+
 #endif /* CONFIG_SND_MIXER_OSS */
 
 #endif /* __SOUND_MIXER_OSS_H */
index e731f8d7193472e359323b4d0eda86428c340d61..430a9cc045e27e69cc7ea7bcff67468be22f0c5f 100644 (file)
@@ -136,48 +136,49 @@ struct snd_pcm_ops {
                                         SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
 #define SNDRV_PCM_RATE_8000_192000     (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
                                         SNDRV_PCM_RATE_192000)
-#define SNDRV_PCM_FMTBIT_S8            (1ULL << SNDRV_PCM_FORMAT_S8)
-#define SNDRV_PCM_FMTBIT_U8            (1ULL << SNDRV_PCM_FORMAT_U8)
-#define SNDRV_PCM_FMTBIT_S16_LE                (1ULL << SNDRV_PCM_FORMAT_S16_LE)
-#define SNDRV_PCM_FMTBIT_S16_BE                (1ULL << SNDRV_PCM_FORMAT_S16_BE)
-#define SNDRV_PCM_FMTBIT_U16_LE                (1ULL << SNDRV_PCM_FORMAT_U16_LE)
-#define SNDRV_PCM_FMTBIT_U16_BE                (1ULL << SNDRV_PCM_FORMAT_U16_BE)
-#define SNDRV_PCM_FMTBIT_S24_LE                (1ULL << SNDRV_PCM_FORMAT_S24_LE)
-#define SNDRV_PCM_FMTBIT_S24_BE                (1ULL << SNDRV_PCM_FORMAT_S24_BE)
-#define SNDRV_PCM_FMTBIT_U24_LE                (1ULL << SNDRV_PCM_FORMAT_U24_LE)
-#define SNDRV_PCM_FMTBIT_U24_BE                (1ULL << SNDRV_PCM_FORMAT_U24_BE)
-#define SNDRV_PCM_FMTBIT_S32_LE                (1ULL << SNDRV_PCM_FORMAT_S32_LE)
-#define SNDRV_PCM_FMTBIT_S32_BE                (1ULL << SNDRV_PCM_FORMAT_S32_BE)
-#define SNDRV_PCM_FMTBIT_U32_LE                (1ULL << SNDRV_PCM_FORMAT_U32_LE)
-#define SNDRV_PCM_FMTBIT_U32_BE                (1ULL << SNDRV_PCM_FORMAT_U32_BE)
-#define SNDRV_PCM_FMTBIT_FLOAT_LE      (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE)
-#define SNDRV_PCM_FMTBIT_FLOAT_BE      (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE)
-#define SNDRV_PCM_FMTBIT_FLOAT64_LE    (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE)
-#define SNDRV_PCM_FMTBIT_FLOAT64_BE    (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE)
-#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
-#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE)
-#define SNDRV_PCM_FMTBIT_MU_LAW                (1ULL << SNDRV_PCM_FORMAT_MU_LAW)
-#define SNDRV_PCM_FMTBIT_A_LAW         (1ULL << SNDRV_PCM_FORMAT_A_LAW)
-#define SNDRV_PCM_FMTBIT_IMA_ADPCM     (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM)
-#define SNDRV_PCM_FMTBIT_MPEG          (1ULL << SNDRV_PCM_FORMAT_MPEG)
-#define SNDRV_PCM_FMTBIT_GSM           (1ULL << SNDRV_PCM_FORMAT_GSM)
-#define SNDRV_PCM_FMTBIT_SPECIAL       (1ULL << SNDRV_PCM_FORMAT_SPECIAL)
-#define SNDRV_PCM_FMTBIT_S24_3LE       (1ULL << SNDRV_PCM_FORMAT_S24_3LE)
-#define SNDRV_PCM_FMTBIT_U24_3LE       (1ULL << SNDRV_PCM_FORMAT_U24_3LE)
-#define SNDRV_PCM_FMTBIT_S24_3BE       (1ULL << SNDRV_PCM_FORMAT_S24_3BE)
-#define SNDRV_PCM_FMTBIT_U24_3BE       (1ULL << SNDRV_PCM_FORMAT_U24_3BE)
-#define SNDRV_PCM_FMTBIT_S20_3LE       (1ULL << SNDRV_PCM_FORMAT_S20_3LE)
-#define SNDRV_PCM_FMTBIT_U20_3LE       (1ULL << SNDRV_PCM_FORMAT_U20_3LE)
-#define SNDRV_PCM_FMTBIT_S20_3BE       (1ULL << SNDRV_PCM_FORMAT_S20_3BE)
-#define SNDRV_PCM_FMTBIT_U20_3BE       (1ULL << SNDRV_PCM_FORMAT_U20_3BE)
-#define SNDRV_PCM_FMTBIT_S18_3LE       (1ULL << SNDRV_PCM_FORMAT_S18_3LE)
-#define SNDRV_PCM_FMTBIT_U18_3LE       (1ULL << SNDRV_PCM_FORMAT_U18_3LE)
-#define SNDRV_PCM_FMTBIT_S18_3BE       (1ULL << SNDRV_PCM_FORMAT_S18_3BE)
-#define SNDRV_PCM_FMTBIT_U18_3BE       (1ULL << SNDRV_PCM_FORMAT_U18_3BE)
-#define SNDRV_PCM_FMTBIT_G723_24       (1ULL << SNDRV_PCM_FORMAT_G723_24)
-#define SNDRV_PCM_FMTBIT_G723_24_1B    (1ULL << SNDRV_PCM_FORMAT_G723_24_1B)
-#define SNDRV_PCM_FMTBIT_G723_40       (1ULL << SNDRV_PCM_FORMAT_G723_40)
-#define SNDRV_PCM_FMTBIT_G723_40_1B    (1ULL << SNDRV_PCM_FORMAT_G723_40_1B)
+#define _SNDRV_PCM_FMTBIT(fmt)         (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
+#define SNDRV_PCM_FMTBIT_S8            _SNDRV_PCM_FMTBIT(S8)
+#define SNDRV_PCM_FMTBIT_U8            _SNDRV_PCM_FMTBIT(U8)
+#define SNDRV_PCM_FMTBIT_S16_LE                _SNDRV_PCM_FMTBIT(S16_LE)
+#define SNDRV_PCM_FMTBIT_S16_BE                _SNDRV_PCM_FMTBIT(S16_BE)
+#define SNDRV_PCM_FMTBIT_U16_LE                _SNDRV_PCM_FMTBIT(U16_LE)
+#define SNDRV_PCM_FMTBIT_U16_BE                _SNDRV_PCM_FMTBIT(U16_BE)
+#define SNDRV_PCM_FMTBIT_S24_LE                _SNDRV_PCM_FMTBIT(S24_LE)
+#define SNDRV_PCM_FMTBIT_S24_BE                _SNDRV_PCM_FMTBIT(S24_BE)
+#define SNDRV_PCM_FMTBIT_U24_LE                _SNDRV_PCM_FMTBIT(U24_LE)
+#define SNDRV_PCM_FMTBIT_U24_BE                _SNDRV_PCM_FMTBIT(U24_BE)
+#define SNDRV_PCM_FMTBIT_S32_LE                _SNDRV_PCM_FMTBIT(S32_LE)
+#define SNDRV_PCM_FMTBIT_S32_BE                _SNDRV_PCM_FMTBIT(S32_BE)
+#define SNDRV_PCM_FMTBIT_U32_LE                _SNDRV_PCM_FMTBIT(U32_LE)
+#define SNDRV_PCM_FMTBIT_U32_BE                _SNDRV_PCM_FMTBIT(U32_BE)
+#define SNDRV_PCM_FMTBIT_FLOAT_LE      _SNDRV_PCM_FMTBIT(FLOAT_LE)
+#define SNDRV_PCM_FMTBIT_FLOAT_BE      _SNDRV_PCM_FMTBIT(FLOAT_BE)
+#define SNDRV_PCM_FMTBIT_FLOAT64_LE    _SNDRV_PCM_FMTBIT(FLOAT64_LE)
+#define SNDRV_PCM_FMTBIT_FLOAT64_BE    _SNDRV_PCM_FMTBIT(FLOAT64_BE)
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_LE)
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_BE)
+#define SNDRV_PCM_FMTBIT_MU_LAW                _SNDRV_PCM_FMTBIT(MU_LAW)
+#define SNDRV_PCM_FMTBIT_A_LAW         _SNDRV_PCM_FMTBIT(A_LAW)
+#define SNDRV_PCM_FMTBIT_IMA_ADPCM     _SNDRV_PCM_FMTBIT(IMA_ADPCM)
+#define SNDRV_PCM_FMTBIT_MPEG          _SNDRV_PCM_FMTBIT(MPEG)
+#define SNDRV_PCM_FMTBIT_GSM           _SNDRV_PCM_FMTBIT(GSM)
+#define SNDRV_PCM_FMTBIT_SPECIAL       _SNDRV_PCM_FMTBIT(SPECIAL)
+#define SNDRV_PCM_FMTBIT_S24_3LE       _SNDRV_PCM_FMTBIT(S24_3LE)
+#define SNDRV_PCM_FMTBIT_U24_3LE       _SNDRV_PCM_FMTBIT(U24_3LE)
+#define SNDRV_PCM_FMTBIT_S24_3BE       _SNDRV_PCM_FMTBIT(S24_3BE)
+#define SNDRV_PCM_FMTBIT_U24_3BE       _SNDRV_PCM_FMTBIT(U24_3BE)
+#define SNDRV_PCM_FMTBIT_S20_3LE       _SNDRV_PCM_FMTBIT(S20_3LE)
+#define SNDRV_PCM_FMTBIT_U20_3LE       _SNDRV_PCM_FMTBIT(U20_3LE)
+#define SNDRV_PCM_FMTBIT_S20_3BE       _SNDRV_PCM_FMTBIT(S20_3BE)
+#define SNDRV_PCM_FMTBIT_U20_3BE       _SNDRV_PCM_FMTBIT(U20_3BE)
+#define SNDRV_PCM_FMTBIT_S18_3LE       _SNDRV_PCM_FMTBIT(S18_3LE)
+#define SNDRV_PCM_FMTBIT_U18_3LE       _SNDRV_PCM_FMTBIT(U18_3LE)
+#define SNDRV_PCM_FMTBIT_S18_3BE       _SNDRV_PCM_FMTBIT(S18_3BE)
+#define SNDRV_PCM_FMTBIT_U18_3BE       _SNDRV_PCM_FMTBIT(U18_3BE)
+#define SNDRV_PCM_FMTBIT_G723_24       _SNDRV_PCM_FMTBIT(G723_24)
+#define SNDRV_PCM_FMTBIT_G723_24_1B    _SNDRV_PCM_FMTBIT(G723_24_1B)
+#define SNDRV_PCM_FMTBIT_G723_40       _SNDRV_PCM_FMTBIT(G723_40)
+#define SNDRV_PCM_FMTBIT_G723_40_1B    _SNDRV_PCM_FMTBIT(G723_40_1B)
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define SNDRV_PCM_FMTBIT_S16           SNDRV_PCM_FMTBIT_S16_LE
@@ -490,7 +491,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
 int snd_pcm_status(struct snd_pcm_substream *substream,
                   struct snd_pcm_status *status);
 int snd_pcm_start(struct snd_pcm_substream *substream);
-int snd_pcm_stop(struct snd_pcm_substream *substream, int status);
+int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
 int snd_pcm_drain_done(struct snd_pcm_substream *substream);
 #ifdef CONFIG_PM
 int snd_pcm_suspend(struct snd_pcm_substream *substream);
@@ -748,8 +749,8 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc
        return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
 }
 
-#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS))
-#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT))
+#define params_access(p) ((__force snd_pcm_access_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)))
+#define params_format(p) ((__force snd_pcm_format_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)))
 #define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT))
 #define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min
 #define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min
index d79894192ae3e10cb5f072c51f2ed640c7ba76fd..9a155f9d0a12a627f968821d25f890cb71efc403 100644 (file)
 #define FSI_PORT_A     0
 #define FSI_PORT_B     1
 
-/* flags format
-
- * 0xABCDEEFF
- *
- * A:  channel size for TDM (input)
- * B:  channel size for TDM (ooutput)
- * C:  inversion
- * D:  mode
- * E:  input format
- * F:  output format
- */
-
 #include <linux/clk.h>
 #include <sound/soc.h>
 
-/* TDM channel */
-#define SH_FSI_SET_CH_I(x)     ((x & 0xF) << 28)
-#define SH_FSI_SET_CH_O(x)     ((x & 0xF) << 24)
-
-#define SH_FSI_CH_IMASK                0xF0000000
-#define SH_FSI_CH_OMASK                0x0F000000
-#define SH_FSI_GET_CH_I(x)     ((x & SH_FSI_CH_IMASK) >> 28)
-#define SH_FSI_GET_CH_O(x)     ((x & SH_FSI_CH_OMASK) >> 24)
-
-/* clock inversion */
-#define SH_FSI_INVERSION_MASK  0x00F00000
-#define SH_FSI_LRM_INV         (1 << 20)
-#define SH_FSI_BRM_INV         (1 << 21)
-#define SH_FSI_LRS_INV         (1 << 22)
-#define SH_FSI_BRS_INV         (1 << 23)
-
-/* mode */
-#define SH_FSI_MODE_MASK       0x000F0000
-#define SH_FSI_IN_SLAVE_MODE   (1 << 16)  /* default master mode */
-#define SH_FSI_OUT_SLAVE_MODE  (1 << 17)  /* default master mode */
-
-/* DI format */
-#define SH_FSI_FMT_MASK                0x000000FF
-#define SH_FSI_IFMT(x)         (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
-#define SH_FSI_OFMT(x)         (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
-#define SH_FSI_GET_IFMT(x)     ((x >> 8) & SH_FSI_FMT_MASK)
-#define SH_FSI_GET_OFMT(x)     ((x >> 0) & SH_FSI_FMT_MASK)
-
-#define SH_FSI_FMT_MONO                0
-#define SH_FSI_FMT_MONO_DELAY  1
-#define SH_FSI_FMT_PCM         2
-#define SH_FSI_FMT_I2S         3
-#define SH_FSI_FMT_TDM         4
-#define SH_FSI_FMT_TDM_DELAY   5
-#define SH_FSI_FMT_SPDIF       6
-
-
-#define SH_FSI_IFMT_TDM_CH(x) \
-       (SH_FSI_IFMT(TDM)       | SH_FSI_SET_CH_I(x))
-#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
-       (SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
+/*
+ * flags format
+ *
+ * 0x000000BA
+ *
+ * A:  inversion
+ * B:  format mode
+ */
 
-#define SH_FSI_OFMT_TDM_CH(x) \
-       (SH_FSI_OFMT(TDM)       | SH_FSI_SET_CH_O(x))
-#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
-       (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
+/* A: clock inversion */
+#define SH_FSI_INVERSION_MASK  0x0000000F
+#define SH_FSI_LRM_INV         (1 << 0)
+#define SH_FSI_BRM_INV         (1 << 1)
+#define SH_FSI_LRS_INV         (1 << 2)
+#define SH_FSI_BRS_INV         (1 << 3)
+
+/* B: format mode */
+#define SH_FSI_FMT_MASK                0x000000F0
+#define SH_FSI_FMT_DAI         (0 << 4)
+#define SH_FSI_FMT_SPDIF       (1 << 4)
 
 
 /*
index 8031769ac48584c3ae91c30261fe8f6099bfb35b..979ed84e07d65a78fb24798a72e2bd3976d5ec10 100644 (file)
        .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
 
+/* additional sequencing control within an event type */
+#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .event = wevent, .event_flags = wflags, \
+       .subseq = wsubseq}
+#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
+       wflags) \
+{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
+       .shift = wshift, .invert = winvert, .event = wevent, \
+       .event_flags = wflags, .subseq = wsubseq}
+
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
@@ -450,6 +462,7 @@ struct snd_soc_dapm_widget {
        unsigned char ext:1;                    /* has external widgets */
        unsigned char force:1;                  /* force state */
        unsigned char ignore_suspend:1;         /* kept enabled over suspend */
+       int subseq;                             /* sort within widget type */
 
        int (*power_check)(struct snd_soc_dapm_widget *w);
 
@@ -487,6 +500,9 @@ struct snd_soc_dapm_context {
 
        struct snd_soc_dapm_update *update;
 
+       void (*seq_notifier)(struct snd_soc_dapm_context *,
+                            enum snd_soc_dapm_type, int);
+
        struct device *dev; /* from parent - for debug */
        struct snd_soc_codec *codec; /* parent codec */
        struct snd_soc_card *card; /* parent card */
index 74921f20a1d84fb7bac1e3a693fd9710dcee6aab..bfa4836ea107bfc50daba2fe4bdb640c26049927 100644 (file)
@@ -234,6 +234,7 @@ struct snd_soc_codec;
 struct snd_soc_codec_driver;
 struct soc_enum;
 struct snd_soc_jack;
+struct snd_soc_jack_zone;
 struct snd_soc_jack_pin;
 struct snd_soc_cache_ops;
 #include <sound/soc-dapm.h>
@@ -258,6 +259,16 @@ enum snd_soc_compress_type {
        SND_SOC_RBTREE_COMPRESSION
 };
 
+int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+                            unsigned int freq, int dir);
+int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+                         unsigned int freq_in, unsigned int freq_out);
+
+int snd_soc_register_card(struct snd_soc_card *card);
+int snd_soc_unregister_card(struct snd_soc_card *card);
+int snd_soc_suspend(struct device *dev);
+int snd_soc_resume(struct device *dev);
+int snd_soc_poweroff(struct device *dev);
 int snd_soc_register_platform(struct device *dev,
                struct snd_soc_platform_driver *platform_drv);
 void snd_soc_unregister_platform(struct device *dev);
@@ -265,7 +276,8 @@ int snd_soc_register_codec(struct device *dev,
                const struct snd_soc_codec_driver *codec_drv,
                struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_codec(struct device *dev);
-int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
+                                   unsigned int reg);
 int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
                               int addr_bits, int data_bits,
                               enum snd_soc_control_type control);
@@ -276,6 +288,10 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
                        unsigned int reg, unsigned int value);
 int snd_soc_cache_read(struct snd_soc_codec *codec,
                       unsigned int reg, unsigned int *value);
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+                                     unsigned int reg);
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+                                     unsigned int reg);
 
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@@ -297,6 +313,9 @@ void snd_soc_jack_notifier_register(struct snd_soc_jack *jack,
                                    struct notifier_block *nb);
 void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack,
                                      struct notifier_block *nb);
+int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count,
+                         struct snd_soc_jack_zone *zones);
+int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage);
 #ifdef CONFIG_GPIOLIB
 int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        struct snd_soc_jack_gpio *gpios);
@@ -321,7 +340,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
  *Controls
  */
 struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
-       void *data, char *long_name);
+                                 void *data, char *long_name,
+                                 const char *prefix);
 int snd_soc_add_controls(struct snd_soc_codec *codec,
        const struct snd_kcontrol_new *controls, int num_controls);
 int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
@@ -366,6 +386,22 @@ int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
 int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 
+/**
+ * struct snd_soc_reg_access - Describes whether a given register is
+ * readable, writable or volatile.
+ *
+ * @reg: the register number
+ * @read: whether this register is readable
+ * @write: whether this register is writable
+ * @vol: whether this register is volatile
+ */
+struct snd_soc_reg_access {
+       u16 reg;
+       u16 read;
+       u16 write;
+       u16 vol;
+};
+
 /**
  * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
  *
@@ -380,6 +416,24 @@ struct snd_soc_jack_pin {
        bool invert;
 };
 
+/**
+ * struct snd_soc_jack_zone - Describes voltage zones of jack detection
+ *
+ * @min_mv: start voltage in mv
+ * @max_mv: end voltage in mv
+ * @jack_type: type of jack that is expected for this voltage
+ * @debounce_time: debounce_time for jack, codec driver should wait for this
+ *             duration before reading the adc for voltages
+ * @:list: list container
+ */
+struct snd_soc_jack_zone {
+       unsigned int min_mv;
+       unsigned int max_mv;
+       unsigned int jack_type;
+       unsigned int debounce_time;
+       struct list_head list;
+};
+
 /**
  * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
  *
@@ -388,6 +442,10 @@ struct snd_soc_jack_pin {
  * @report:       value to report when jack detected
  * @invert:       report presence in low state
  * @debouce_time: debouce time in ms
+ * @wake:        enable as wake source
+ * @jack_status_check: callback function which overrides the detection
+ *                    to provide more complex checks (eg, reading an
+ *                    ADC).
  */
 #ifdef CONFIG_GPIOLIB
 struct snd_soc_jack_gpio {
@@ -396,6 +454,8 @@ struct snd_soc_jack_gpio {
        int report;
        int invert;
        int debounce_time;
+       bool wake;
+
        struct snd_soc_jack *jack;
        struct delayed_work work;
 
@@ -409,6 +469,7 @@ struct snd_soc_jack {
        struct list_head pins;
        int status;
        struct blocking_notifier_head notifier;
+       struct list_head jack_zones;
 };
 
 /* SoC PCM stream information */
@@ -459,18 +520,22 @@ struct snd_soc_codec {
        struct list_head card_list;
        int num_dai;
        enum snd_soc_compress_type compress_type;
+       size_t reg_size;        /* reg_cache_size * reg_word_size */
+       int (*volatile_register)(struct snd_soc_codec *, unsigned int);
+       int (*readable_register)(struct snd_soc_codec *, unsigned int);
 
        /* runtime */
        struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
        unsigned int active;
-       unsigned int cache_only:1;  /* Suppress writes to hardware */
-       unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
+       unsigned int cache_bypass:1; /* Suppress access to the cache */
        unsigned int suspended:1; /* Codec is in suspend PM state */
        unsigned int probed:1; /* Codec has been probed */
        unsigned int ac97_registered:1; /* Codec has been AC97 registered */
        unsigned int ac97_created:1; /* Codec has been created by SoC */
        unsigned int sysfs_registered:1; /* codec has been sysfs registered */
        unsigned int cache_init:1; /* codec cache has been initialized */
+       u32 cache_only;  /* Suppress writes to hardware */
+       u32 cache_sync; /* Cache needs to be synced to hardware */
 
        /* codec IO */
        void *control_data; /* codec control (i2c/3wire) data */
@@ -503,22 +568,39 @@ struct snd_soc_codec_driver {
                        pm_message_t state);
        int (*resume)(struct snd_soc_codec *);
 
+       /* Default DAPM setup, added after probe() is run */
+       const struct snd_soc_dapm_widget *dapm_widgets;
+       int num_dapm_widgets;
+       const struct snd_soc_dapm_route *dapm_routes;
+       int num_dapm_routes;
+
+       /* codec wide operations */
+       int (*set_sysclk)(struct snd_soc_codec *codec,
+                         int clk_id, unsigned int freq, int dir);
+       int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
+               unsigned int freq_in, unsigned int freq_out);
+
        /* codec IO */
        unsigned int (*read)(struct snd_soc_codec *, unsigned int);
        int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
        int (*display_register)(struct snd_soc_codec *, char *,
                                size_t, unsigned int);
-       int (*volatile_register)(unsigned int);
-       int (*readable_register)(unsigned int);
+       int (*volatile_register)(struct snd_soc_codec *, unsigned int);
+       int (*readable_register)(struct snd_soc_codec *, unsigned int);
        short reg_cache_size;
        short reg_cache_step;
        short reg_word_size;
        const void *reg_cache_default;
+       short reg_access_size;
+       const struct snd_soc_reg_access *reg_access_default;
        enum snd_soc_compress_type compress_type;
 
        /* codec bias level */
        int (*set_bias_level)(struct snd_soc_codec *,
                              enum snd_soc_bias_level level);
+
+       void (*seq_notifier)(struct snd_soc_dapm_context *,
+                            enum snd_soc_dapm_type, int);
 };
 
 /* SoC platform interface */
@@ -617,15 +699,16 @@ struct snd_soc_card {
 
        bool instantiated;
 
-       int (*probe)(struct platform_device *pdev);
-       int (*remove)(struct platform_device *pdev);
+       int (*probe)(struct snd_soc_card *card);
+       int (*late_probe)(struct snd_soc_card *card);
+       int (*remove)(struct snd_soc_card *card);
 
        /* the pre and post PM functions are used to do any PM work before and
         * after the codec and DAI's do any PM work. */
-       int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
-       int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
-       int (*resume_pre)(struct platform_device *pdev);
-       int (*resume_post)(struct platform_device *pdev);
+       int (*suspend_pre)(struct snd_soc_card *card);
+       int (*suspend_post)(struct snd_soc_card *card);
+       int (*resume_pre)(struct snd_soc_card *card);
+       int (*resume_post)(struct snd_soc_card *card);
 
        /* callbacks */
        int (*set_bias_level)(struct snd_soc_card *,
@@ -654,6 +737,14 @@ struct snd_soc_card {
        struct snd_soc_pcm_runtime *rtd_aux;
        int num_aux_rtd;
 
+       /*
+        * Card-specific routes and widgets.
+        */
+       struct snd_soc_dapm_widget *dapm_widgets;
+       int num_dapm_widgets;
+       struct snd_soc_dapm_route *dapm_routes;
+       int num_dapm_routes;
+
        struct work_struct deferred_resume_work;
 
        /* lists of probed devices belonging to this card */
@@ -665,11 +756,16 @@ struct snd_soc_card {
        struct list_head paths;
        struct list_head dapm_list;
 
+       /* Generic DAPM context for the card */
+       struct snd_soc_dapm_context dapm;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_card_root;
        struct dentry *debugfs_pop_time;
 #endif
        u32 pop_time;
+
+       void *drvdata;
 };
 
 /* SoC machine DAI configuration, glues a codec and cpu DAI together */
@@ -721,6 +817,17 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec,
 
 /* device driver data */
 
+static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
+               void *data)
+{
+       card->drvdata = data;
+}
+
+static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
+{
+       return card->drvdata;
+}
+
 static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
                void *data)
 {
@@ -754,6 +861,22 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd)
        return dev_get_drvdata(&rtd->dev);
 }
 
+static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
+{
+       INIT_LIST_HEAD(&card->dai_dev_list);
+       INIT_LIST_HEAD(&card->codec_dev_list);
+       INIT_LIST_HEAD(&card->platform_dev_list);
+       INIT_LIST_HEAD(&card->widgets);
+       INIT_LIST_HEAD(&card->paths);
+       INIT_LIST_HEAD(&card->dapm_list);
+}
+
 #include <sound/soc-dai.h>
 
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *snd_soc_debugfs_root;
+#endif
+
+extern const struct dev_pm_ops snd_soc_pm_ops;
+
 #endif
diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h
new file mode 100644 (file)
index 0000000..c009f70
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * tlv320aic32x4.h  --  TLV320AIC32X4 Soc Audio driver platform data
+ *
+ * Copyright 2011 Vista Silicon S.L.
+ *
+ * Author: Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AIC32X4_PDATA_H
+#define _AIC32X4_PDATA_H
+
+#define AIC32X4_PWR_MICBIAS_2075_LDOIN         0x00000001
+#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE     0x00000002
+#define AIC32X4_PWR_AIC32X4_LDO_ENABLE         0x00000004
+#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36   0x00000008
+#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED    0x00000010
+
+#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K     0x00000001
+#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K     0x00000002
+
+struct aic32x4_pdata {
+       u32 power_cfg;
+       u32 micpga_routing;
+       bool swapdacs;
+};
+
+#endif
index bf69a5b7e65f5f9e4eeb9fd011dc9f0804f0589e..8fc5321e1ecc739fa7bd0cdb30c7e63ac0fc1e43 100644 (file)
@@ -1,3 +1,3 @@
 /* include/version.h */
-#define CONFIG_SND_VERSION "1.0.23"
+#define CONFIG_SND_VERSION "1.0.24"
 #define CONFIG_SND_DATE ""
index 1eeebd534f7e7feea81e1bac43843847fc49e337..cf7ccb76a8de8ad7f7a409aad3a703addd574d06 100644 (file)
 #define WM8903_MICBIAS_ENA_SHIFT                     0  /* MICBIAS_ENA */
 #define WM8903_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */
 
+/*
+ * WM8903_GPn_FN values
+ *
+ * See datasheets for list of valid values per pin
+ */
+#define WM8903_GPn_FN_GPIO_OUTPUT                    0
+#define WM8903_GPn_FN_BCLK                           1
+#define WM8903_GPn_FN_IRQ_OUTPT                      2
+#define WM8903_GPn_FN_GPIO_INPUT                     3
+#define WM8903_GPn_FN_MICBIAS_CURRENT_DETECT         4
+#define WM8903_GPn_FN_MICBIAS_SHORT_DETECT           5
+#define WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT             6
+#define WM8903_GPn_FN_FLL_LOCK_OUTPUT                8
+#define WM8903_GPn_FN_FLL_CLOCK_OUTPUT               9
+
 /*
  * R116 (0x74) - GPIO Control 1
  */
 #define WM8903_GP5_DB_SHIFT                          0  /* GP5_DB */
 #define WM8903_GP5_DB_WIDTH                          1  /* GP5_DB */
 
+#define WM8903_NUM_GPIO 5
+
 struct wm8903_platform_data {
        bool irq_active_low;   /* Set if IRQ active low, default high */
 
@@ -239,7 +256,8 @@ struct wm8903_platform_data {
 
        int micdet_delay;      /* Delay after microphone detection (ms) */
 
-       u32 gpio_cfg[5];       /* Default register values for GPIO pin mux */
+       int gpio_base;
+       u32 gpio_cfg[WM8903_NUM_GPIO]; /* Default register values for GPIO pin mux */
 };
 
 #endif
index e173ddbf6bd4f7dafd4680caf402c852c0011dcb..f34b0b1716d845911271c222f74382fac12d135d 100644 (file)
@@ -17,9 +17,12 @@ struct wm9081_retune_mobile_setting {
        u16 config[20];
 };
 
-struct wm9081_retune_mobile_config {
-       struct wm9081_retune_mobile_setting *configs;
-       int num_configs;
+struct wm9081_pdata {
+       bool irq_high;   /* IRQ is active high */
+       bool irq_cmos;   /* IRQ is in CMOS mode */
+
+       struct wm9081_retune_mobile_setting *retune_configs;
+       int num_retune_configs;
 };
 
 #endif
index 186e84db4b548215c623cd0241db16a1209e0a28..ae973d2e27a11fa30ee926f8a72ed6b66b8138d1 100644 (file)
@@ -229,6 +229,31 @@ TRACE_EVENT(snd_soc_jack_notify,
        TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
 );
 
+TRACE_EVENT(snd_soc_cache_sync,
+
+       TP_PROTO(struct snd_soc_codec *codec, const char *type,
+                const char *status),
+
+       TP_ARGS(codec, type, status),
+
+       TP_STRUCT__entry(
+               __string(       name,           codec->name     )
+               __string(       status,         status          )
+               __string(       type,           type            )
+               __field(        int,            id              )
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, codec->name);
+               __assign_str(status, status);
+               __assign_str(type, type);
+               __entry->id = codec->id;
+       ),
+
+       TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
+                 (int)__entry->id, __get_str(type), __get_str(status))
+);
+
 #endif /* _TRACE_ASOC_H */
 
 /* This part must be outside protection */
index fcad760f56919906bfe4f43867c9a2cac2f7aca5..1fef141ef8e720ea5f93a6849db18a282f5708d4 100644 (file)
@@ -97,6 +97,8 @@ source "sound/sh/Kconfig"
 # here assuming USB is defined before ALSA
 source "sound/usb/Kconfig"
 
+source "sound/firewire/Kconfig"
+
 # the following will depend on the order of config.
 # here assuming PCMCIA is defined before ALSA
 source "sound/pcmcia/Kconfig"
index ec467decfa7913d19b663fc57ecde40be21093d2..ce9132b1c395220a3f71c4ed460256a0ace97221 100644 (file)
@@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
 obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
-       sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/
+       firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/
 obj-$(CONFIG_SND_AOA) += aoa/
 
 # This one must be compilable even if sound is configured out
index 9ce00ed20fba3d9141cb13dd045230412ee86e4c..a08ad57c49b6adf48a6fcc516dd02d62c159f406 100644 (file)
@@ -279,33 +279,31 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
 
 EXPORT_SYMBOL(snd_ctl_free_one);
 
-static unsigned int snd_ctl_hole_check(struct snd_card *card,
-                                      unsigned int count)
+static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
+                                         unsigned int count)
 {
        struct snd_kcontrol *kctl;
 
        list_for_each_entry(kctl, &card->controls, list) {
-               if ((kctl->id.numid <= card->last_numid &&
-                    kctl->id.numid + kctl->count > card->last_numid) ||
-                   (kctl->id.numid <= card->last_numid + count - 1 &&
-                    kctl->id.numid + kctl->count > card->last_numid + count - 1))
-                       return card->last_numid = kctl->id.numid + kctl->count - 1;
+               if (kctl->id.numid < card->last_numid + 1 + count &&
+                   kctl->id.numid + kctl->count > card->last_numid + 1) {
+                       card->last_numid = kctl->id.numid + kctl->count - 1;
+                       return true;
+               }
        }
-       return card->last_numid;
+       return false;
 }
 
 static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
 {
-       unsigned int last_numid, iter = 100000;
+       unsigned int iter = 100000;
 
-       last_numid = card->last_numid;
-       while (last_numid != snd_ctl_hole_check(card, count)) {
+       while (snd_ctl_remove_numid_conflict(card, count)) {
                if (--iter == 0) {
                        /* this situation is very unlikely */
                        snd_printk(KERN_ERR "unable to allocate new control numid\n");
                        return -ENOMEM;
                }
-               last_numid = card->last_numid;
        }
        return 0;
 }
@@ -465,6 +463,52 @@ error:
        return ret;
 }
 
+/**
+ * snd_ctl_activate_id - activate/inactivate the control of the given id
+ * @card: the card instance
+ * @id: the control id to activate/inactivate
+ * @active: non-zero to activate
+ *
+ * Finds the control instance with the given id, and activate or
+ * inactivate the control together with notification, if changed.
+ *
+ * Returns 0 if unchanged, 1 if changed, or a negative error code on failure.
+ */
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+                       int active)
+{
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_volatile *vd;
+       unsigned int index_offset;
+       int ret;
+
+       down_write(&card->controls_rwsem);
+       kctl = snd_ctl_find_id(card, id);
+       if (kctl == NULL) {
+               ret = -ENOENT;
+               goto unlock;
+       }
+       index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+       vd = &kctl->vd[index_offset];
+       ret = 0;
+       if (active) {
+               if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+                       goto unlock;
+               vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       } else {
+               if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
+                       goto unlock;
+               vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       }
+       ret = 1;
+ unlock:
+       up_write(&card->controls_rwsem);
+       if (ret > 0)
+               snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
+
 /**
  * snd_ctl_rename_id - replace the id of a control on the card
  * @card: the card instance
index a67dfac08c035aa1c41938d150c7ce333336faf0..2d1ad4b0cd653171c7d175b1b3edadc29b10abf6 100644 (file)
@@ -225,15 +225,16 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
 {
        struct snd_device *dev;
        int err;
-       unsigned int range_low, range_high;
+       unsigned int range_low, range_high, type;
 
        if (snd_BUG_ON(!card))
                return -ENXIO;
-       range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
+       range_low = (__force unsigned int)cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
        range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
       __again:
        list_for_each_entry(dev, &card->devices, list) {
-               if (dev->type >= range_low && dev->type <= range_high) {
+               type = (__force unsigned int)dev->type;
+               if (type >= range_low && type <= range_high) {
                        if ((err = snd_device_free(card, dev->device_data)) < 0)
                                return err;
                        goto __again;
index 9e92441f9b78fb3b51c5924ae1052e980629d1cc..16bd9c03679b16dd18d119495a7ab0e530fcbd0c 100644 (file)
@@ -192,7 +192,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
        dmab->bytes = 0;
        switch (type) {
        case SNDRV_DMA_TYPE_CONTINUOUS:
-               dmab->area = snd_malloc_pages(size, (unsigned long)device);
+               dmab->area = snd_malloc_pages(size,
+                                       (__force gfp_t)(unsigned long)device);
                dmab->addr = 0;
                break;
 #ifdef CONFIG_HAS_DMA
index 4c1d168271990a7a76bc70c921fe31ca4b5968f9..13b3f6f49fae3c8624b8d47e44b24268be211b3c 100644 (file)
@@ -114,7 +114,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
        return frames;
 }
 
-static void init_data(struct linear_priv *data, int src_format, int dst_format)
+static void init_data(struct linear_priv *data,
+                     snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
 {
        int src_le, dst_le, src_bytes, dst_bytes;
 
@@ -140,9 +141,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format)
        if (snd_pcm_format_signed(src_format) !=
            snd_pcm_format_signed(dst_format)) {
                if (dst_le)
-                       data->flip = cpu_to_le32(0x80000000);
+                       data->flip = (__force u32)cpu_to_le32(0x80000000);
                else
-                       data->flip = cpu_to_be32(0x80000000);
+                       data->flip = (__force u32)cpu_to_be32(0x80000000);
        }
 }
 
index 822dd56993ca2a54de249d38e5827be11be9b7ac..d8359cfeca15a43484eb3bdc710123bfefe234a2 100644 (file)
@@ -190,9 +190,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
                return -EIO;
        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
                int err;
-               if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
+               unsigned int index;
+               if ((err = mixer->get_recsrc(fmixer, &index)) < 0)
                        return err;
-               result = 1 << result;
+               result = 1 << index;
        } else {
                struct snd_mixer_oss_slot *pslot;
                int chn;
@@ -214,6 +215,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
        struct snd_mixer_oss *mixer = fmixer->mixer;
        struct snd_mixer_oss_slot *pslot;
        int chn, active;
+       unsigned int index;
        int result = 0;
 
        if (mixer == NULL)
@@ -222,8 +224,8 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
                if (recsrc & ~mixer->oss_recsrc)
                        recsrc &= ~mixer->oss_recsrc;
                mixer->put_recsrc(fmixer, ffz(~recsrc));
-               mixer->get_recsrc(fmixer, &result);
-               result = 1 << result;
+               mixer->get_recsrc(fmixer, &index);
+               result = 1 << index;
        }
        for (chn = 0; chn < 31; chn++) {
                pslot = &mixer->slots[chn];
index f7649d4d950bcdd2555a3633019428536a87fbbd..7915564bd39445fd45e8cff34330ea269c6e9476 100644 (file)
@@ -274,7 +274,7 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
        return frames;
 }
 
-static void init_data(struct mulaw_priv *data, int format)
+static void init_data(struct mulaw_priv *data, snd_pcm_format_t format)
 {
 #ifdef SNDRV_LITTLE_ENDIAN
        data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
index a2e4eb324699ace38148bd087d59e50d7d173e44..23c34a02894b77831638e89a65ae17f7df607633 100644 (file)
@@ -41,6 +41,7 @@
 #include <sound/info.h>
 #include <linux/soundcard.h>
 #include <sound/initval.h>
+#include <sound/mixer_oss.h>
 
 #define OSS_ALSAEMULVER                _SIOR ('M', 249, int)
 
@@ -60,7 +61,6 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
 MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
 MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);
 
-extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg);
 static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file);
 static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file);
 static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file);
@@ -656,7 +656,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
 #define AFMT_AC3         0x00000400
 #define AFMT_VORBIS      0x00000800
 
-static int snd_pcm_oss_format_from(int format)
+static snd_pcm_format_t snd_pcm_oss_format_from(int format)
 {
        switch (format) {
        case AFMT_MU_LAW:       return SNDRV_PCM_FORMAT_MU_LAW;
@@ -680,7 +680,7 @@ static int snd_pcm_oss_format_from(int format)
        }
 }
 
-static int snd_pcm_oss_format_to(int format)
+static int snd_pcm_oss_format_to(snd_pcm_format_t format)
 {
        switch (format) {
        case SNDRV_PCM_FORMAT_MU_LAW:   return AFMT_MU_LAW;
@@ -843,7 +843,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
        size_t oss_frame_size;
        int err;
        int direct;
-       int format, sformat, n;
+       snd_pcm_format_t format, sformat;
+       int n;
        struct snd_mask sformat_mask;
        struct snd_mask mask;
 
@@ -868,11 +869,11 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
        _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
        snd_mask_none(&mask);
        if (atomic_read(&substream->mmap_count))
-               snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+               snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
        else {
-               snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+               snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED);
                if (!direct)
-                       snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+                       snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
        }
        err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
        if (err < 0) {
@@ -891,19 +892,22 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
        else
                sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
 
-       if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
-               for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
-                       if (snd_mask_test(&sformat_mask, sformat) &&
+       if ((__force int)sformat < 0 ||
+           !snd_mask_test(&sformat_mask, (__force int)sformat)) {
+               for (sformat = (__force snd_pcm_format_t)0;
+                    (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST;
+                    sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) {
+                       if (snd_mask_test(&sformat_mask, (__force int)sformat) &&
                            snd_pcm_oss_format_to(sformat) >= 0)
                                break;
                }
-               if (sformat > SNDRV_PCM_FORMAT_LAST) {
+               if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) {
                        snd_printd("Cannot find a format!!!\n");
                        err = -EINVAL;
                        goto failure;
                }
        }
-       err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
+       err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
        if (err < 0)
                goto failure;
 
@@ -912,9 +916,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
        } else {
                _snd_pcm_hw_params_any(params);
                _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
-                                     SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+                                     (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
                _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
-                                     snd_pcm_oss_format_from(runtime->oss.format), 0);
+                                     (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0);
                _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
                                      runtime->oss.channels, 0);
                _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
@@ -1185,10 +1189,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const
                if (in_kernel) {
                        mm_segment_t fs;
                        fs = snd_enter_user();
-                       ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
+                       ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames);
                        snd_leave_user(fs);
                } else {
-                       ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
+                       ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames);
                }
                if (ret != -EPIPE && ret != -ESTRPIPE)
                        break;
@@ -1230,10 +1234,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
                if (in_kernel) {
                        mm_segment_t fs;
                        fs = snd_enter_user();
-                       ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
+                       ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames);
                        snd_leave_user(fs);
                } else {
-                       ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
+                       ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames);
                }
                if (ret == -EPIPE) {
                        if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
@@ -1333,7 +1337,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha
                struct snd_pcm_plugin_channel *channels;
                size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
                if (!in_kernel) {
-                       if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes))
+                       if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes))
                                return -EFAULT;
                        buf = runtime->oss.buffer;
                }
@@ -1429,7 +1433,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf,
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_sframes_t frames, frames1;
 #ifdef CONFIG_SND_PCM_OSS_PLUGINS
-       char __user *final_dst = (char __user *)buf;
+       char __user *final_dst = (char __force __user *)buf;
        if (runtime->oss.plugin_first) {
                struct snd_pcm_plugin_channel *channels;
                size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
@@ -1549,6 +1553,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
 {
        struct snd_pcm_runtime *runtime;
        ssize_t result = 0;
+       snd_pcm_state_t state;
        long res;
        wait_queue_t wait;
 
@@ -1570,9 +1575,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
                result = 0;
                set_current_state(TASK_INTERRUPTIBLE);
                snd_pcm_stream_lock_irq(substream);
-               res = runtime->status->state;
+               state = runtime->status->state;
                snd_pcm_stream_unlock_irq(substream);
-               if (res != SNDRV_PCM_STATE_RUNNING) {
+               if (state != SNDRV_PCM_STATE_RUNNING) {
                        set_current_state(TASK_RUNNING);
                        break;
                }
@@ -1658,7 +1663,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
                                                                   size1);
                                        size1 /= runtime->channels; /* frames */
                                        fs = snd_enter_user();
-                                       snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
+                                       snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1);
                                        snd_leave_user(fs);
                                }
                        } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
index 6751daa3bb507fe9dd3007e2013dff63a4a701ee..71cc3ddf5c156075be1bfbee065e9899af8f806d 100644 (file)
@@ -264,7 +264,7 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
        return frames;
 }
 
-static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
+static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format)
 {
        struct snd_mask formats = *mask;
        u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
@@ -276,16 +276,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
                       SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
                       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
                       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
-       snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
+       snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);
        
        if (formats.bits[0] & (u32)linfmts)
                formats.bits[0] |= (u32)linfmts;
        if (formats.bits[1] & (u32)(linfmts >> 32))
                formats.bits[1] |= (u32)(linfmts >> 32);
-       return snd_mask_test(&formats, format);
+       return snd_mask_test(&formats, (__force int)format);
 }
 
-static int preferred_formats[] = {
+static snd_pcm_format_t preferred_formats[] = {
        SNDRV_PCM_FORMAT_S16_LE,
        SNDRV_PCM_FORMAT_S16_BE,
        SNDRV_PCM_FORMAT_U16_LE,
@@ -306,24 +306,25 @@ static int preferred_formats[] = {
        SNDRV_PCM_FORMAT_U8
 };
 
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+                                          struct snd_mask *format_mask)
 {
        int i;
 
-       if (snd_mask_test(format_mask, format))
+       if (snd_mask_test(format_mask, (__force int)format))
                return format;
-       if (! snd_pcm_plug_formats(format_mask, format))
-               return -EINVAL;
+       if (!snd_pcm_plug_formats(format_mask, format))
+               return (__force snd_pcm_format_t)-EINVAL;
        if (snd_pcm_format_linear(format)) {
                unsigned int width = snd_pcm_format_width(format);
                int unsignd = snd_pcm_format_unsigned(format) > 0;
                int big = snd_pcm_format_big_endian(format) > 0;
                unsigned int badness, best = -1;
-               int best_format = -1;
+               snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
                for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
-                       int f = preferred_formats[i];
+                       snd_pcm_format_t f = preferred_formats[i];
                        unsigned int w;
-                       if (!snd_mask_test(format_mask, f))
+                       if (!snd_mask_test(format_mask, (__force int)f))
                                continue;
                        w = snd_pcm_format_width(f);
                        if (w >= width)
@@ -337,17 +338,20 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
                                best = badness;
                        }
                }
-               return best_format >= 0 ? best_format : -EINVAL;
+               if ((__force int)best_format >= 0)
+                       return best_format;
+               else
+                       return (__force snd_pcm_format_t)-EINVAL;
        } else {
                switch (format) {
                case SNDRV_PCM_FORMAT_MU_LAW:
                        for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
-                               int format1 = preferred_formats[i];
-                               if (snd_mask_test(format_mask, format1))
+                               snd_pcm_format_t format1 = preferred_formats[i];
+                               if (snd_mask_test(format_mask, (__force int)format1))
                                        return format1;
                        }
                default:
-                       return -EINVAL;
+                       return (__force snd_pcm_format_t)-EINVAL;
                }
        }
 }
@@ -359,7 +363,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,
        struct snd_pcm_plugin_format tmpformat;
        struct snd_pcm_plugin_format dstformat;
        struct snd_pcm_plugin_format srcformat;
-       int src_access, dst_access;
+       snd_pcm_access_t src_access, dst_access;
        struct snd_pcm_plugin *plugin = NULL;
        int err;
        int stream = snd_pcm_plug_stream(plug);
@@ -641,7 +645,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
 }
 
 int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
-                        size_t samples, int format)
+                        size_t samples, snd_pcm_format_t format)
 {
        /* FIXME: sub byte resolution and odd dst_offset */
        unsigned char *dst;
@@ -688,7 +692,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst
 
 int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,
                      const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
-                     size_t samples, int format)
+                     size_t samples, snd_pcm_format_t format)
 {
        /* FIXME: sub byte resolution and odd dst_offset */
        char *src, *dst;
index b9afab6037115db1e5f6ab08f9836c4262047e2a..a5035c2369a62fccaf55c727b1ef41f70d07f32c 100644 (file)
@@ -46,7 +46,7 @@ struct snd_pcm_plugin_channel {
 };
 
 struct snd_pcm_plugin_format {
-       int format;
+       snd_pcm_format_t format;
        unsigned int rate;
        unsigned int channels;
 };
@@ -58,7 +58,7 @@ struct snd_pcm_plugin {
        struct snd_pcm_plugin_format dst_format;        /* destination format */
        int src_width;                  /* sample width in bits */
        int dst_width;                  /* sample width in bits */
-       int access;
+       snd_pcm_access_t access;
        snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames);
        snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames);
        snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin,
@@ -125,7 +125,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_pcm_hw_params *slave_params);
 
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask);
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+                                          struct snd_mask *format_mask);
 
 int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
 
@@ -146,12 +147,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
 
 int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel,
                         size_t dst_offset,
-                        size_t samples, int format);
+                        size_t samples, snd_pcm_format_t format);
 int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel,
                      size_t src_offset,
                      const struct snd_pcm_channel_area *dst_channel,
                      size_t dst_offset,
-                     size_t samples, int format);
+                     size_t samples, snd_pcm_format_t format);
 
 void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size);
 void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr);
index bbe25d8c450ad5e9e3d653b94215fc050fab521c..c8171f5783c873c49eaa86d61420b99e30adfac8 100644 (file)
@@ -25,7 +25,7 @@
 #include "pcm_plugin.h"
 
 static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
-                      snd_pcm_uframes_t frames, int format)
+                      snd_pcm_uframes_t frames, snd_pcm_format_t format)
 {
        int dst = 0;
        for (; dst < ndsts; ++dst) {
@@ -38,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
 
 static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel,
                             struct snd_pcm_plugin_channel *dst_channel,
-                            snd_pcm_uframes_t frames, int format)
+                            snd_pcm_uframes_t frames, snd_pcm_format_t format)
 {
        dst_channel->enabled = 1;
        snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format);
@@ -51,7 +51,7 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
 {
        int nsrcs, ndsts, dst;
        struct snd_pcm_plugin_channel *dvp;
-       int format;
+       snd_pcm_format_t format;
 
        if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
                return -ENXIO;
index 6b4b1287b314455111c5853334a4aded9425edd3..ee9abb2d9001153110fc021131aebc24ab86dfe0 100644 (file)
@@ -211,9 +211,9 @@ static char *snd_pcm_format_names[] = {
 
 const char *snd_pcm_format_name(snd_pcm_format_t format)
 {
-       if (format >= ARRAY_SIZE(snd_pcm_format_names))
+       if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
                return "Unknown";
-       return snd_pcm_format_names[format];
+       return snd_pcm_format_names[(__force unsigned int)format];
 }
 EXPORT_SYMBOL_GPL(snd_pcm_format_name);
 
@@ -269,12 +269,12 @@ static const char *snd_pcm_stream_name(int stream)
 
 static const char *snd_pcm_access_name(snd_pcm_access_t access)
 {
-       return snd_pcm_access_names[access];
+       return snd_pcm_access_names[(__force int)access];
 }
 
 static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
 {
-       return snd_pcm_subformat_names[subformat];
+       return snd_pcm_subformat_names[(__force int)subformat];
 }
 
 static const char *snd_pcm_tstamp_mode_name(int mode)
@@ -284,7 +284,7 @@ static const char *snd_pcm_tstamp_mode_name(int mode)
 
 static const char *snd_pcm_state_name(snd_pcm_state_t state)
 {
-       return snd_pcm_state_names[state];
+       return snd_pcm_state_names[(__force int)state];
 }
 
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
index 434af3c56d52111c486301b3a7e483ab17806eb8..88f02e3866e0f355fde874e94099e990dcd79f4d 100644 (file)
@@ -35,7 +35,10 @@ struct pcm_format_data {
        unsigned char silence[8];       /* silence data to fill */
 };
 
-static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
+/* we do lots of calculations on snd_pcm_format_t; shut up sparse */
+#define INT    __force int
+
+static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
        [SNDRV_PCM_FORMAT_S8] = {
                .width = 8, .phys = 8, .le = -1, .signd = 1,
                .silence = {},
@@ -215,9 +218,9 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
 int snd_pcm_format_signed(snd_pcm_format_t format)
 {
        int val;
-       if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+       if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
                return -EINVAL;
-       if ((val = pcm_formats[format].signd) < 0)
+       if ((val = pcm_formats[(INT)format].signd) < 0)
                return -EINVAL;
        return val;
 }
@@ -266,9 +269,9 @@ EXPORT_SYMBOL(snd_pcm_format_linear);
 int snd_pcm_format_little_endian(snd_pcm_format_t format)
 {
        int val;
-       if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+       if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
                return -EINVAL;
-       if ((val = pcm_formats[format].le) < 0)
+       if ((val = pcm_formats[(INT)format].le) < 0)
                return -EINVAL;
        return val;
 }
@@ -304,9 +307,9 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian);
 int snd_pcm_format_width(snd_pcm_format_t format)
 {
        int val;
-       if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+       if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
                return -EINVAL;
-       if ((val = pcm_formats[format].width) == 0)
+       if ((val = pcm_formats[(INT)format].width) == 0)
                return -EINVAL;
        return val;
 }
@@ -323,9 +326,9 @@ EXPORT_SYMBOL(snd_pcm_format_width);
 int snd_pcm_format_physical_width(snd_pcm_format_t format)
 {
        int val;
-       if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+       if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
                return -EINVAL;
-       if ((val = pcm_formats[format].phys) == 0)
+       if ((val = pcm_formats[(INT)format].phys) == 0)
                return -EINVAL;
        return val;
 }
@@ -358,11 +361,11 @@ EXPORT_SYMBOL(snd_pcm_format_size);
  */
 const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
 {
-       if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+       if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
                return NULL;
-       if (! pcm_formats[format].phys)
+       if (! pcm_formats[(INT)format].phys)
                return NULL;
-       return pcm_formats[format].silence;
+       return pcm_formats[(INT)format].silence;
 }
 
 EXPORT_SYMBOL(snd_pcm_format_silence_64);
@@ -382,16 +385,16 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
        int width;
        unsigned char *dst, *pat;
 
-       if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+       if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST)
                return -EINVAL;
        if (samples == 0)
                return 0;
-       width = pcm_formats[format].phys; /* physical width */
-       pat = pcm_formats[format].silence;
+       width = pcm_formats[(INT)format].phys; /* physical width */
+       pat = pcm_formats[(INT)format].silence;
        if (! width)
                return -EINVAL;
        /* signed or 1 byte data */
-       if (pcm_formats[format].signd == 1 || width <= 8) {
+       if (pcm_formats[(INT)format].signd == 1 || width <= 8) {
                unsigned int bytes = samples * width / 8;
                memset(data, *pat, bytes);
                return 0;
index 4be45e7be8ad0ab7385102f46817e45d402abb55..ae42b6509ce4cabeda3d9beeeaea39f814be5d35 100644 (file)
@@ -941,7 +941,7 @@ static struct action_ops snd_pcm_action_stop = {
  *
  * The state of each stream is then changed to the given state unconditionally.
  */
-int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
+int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state)
 {
        return snd_pcm_action(&snd_pcm_action_stop, substream, state);
 }
index 99a485f136484ed41c8588eae9a4464af4814070..f2436d33fbf7182a62ae1bc1744a41f8bdb385ce 100644 (file)
@@ -1052,7 +1052,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                } else {
 #ifdef CONFIG_COMPAT
                        if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
-                               void *ptr = compat_ptr(event.data.raw32.d[1]);
+                               void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]);
                                event.data.ext.ptr = ptr;
                        }
 #endif
@@ -2407,7 +2407,7 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
        if (client == NULL)
                return -ENXIO;
        fs = snd_enter_user();
-       result = snd_seq_do_ioctl(client, cmd, (void __user *)arg);
+       result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg);
        snd_leave_user(fs);
        return result;
 }
@@ -2497,9 +2497,6 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
 }
 
 
-void snd_seq_info_pool(struct snd_info_buffer *buffer,
-                      struct snd_seq_pool *pool, char *space);
-
 /* exported to seq_info.c */
 void snd_seq_info_clients_read(struct snd_info_entry *entry, 
                               struct snd_info_buffer *buffer)
index 7fb55436287fe7ad807e8e841965610489ca0109..7f50c1437675c8882b7ba943c31f6ae96b460f31 100644 (file)
@@ -86,7 +86,7 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
 
        if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
                char buf[32];
-               char __user *curptr = (char __user *)event->data.ext.ptr;
+               char __user *curptr = (char __force __user *)event->data.ext.ptr;
                while (len > 0) {
                        int size = sizeof(buf);
                        if (len < size)
@@ -157,7 +157,7 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
        if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
                if (! in_kernel)
                        return -EINVAL;
-               if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
+               if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len))
                        return -EFAULT;
                return newlen;
        }
@@ -343,7 +343,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
                                tmp->event = src->event;
                                src = src->next;
                        } else if (is_usrptr) {
-                               if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
+                               if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) {
                                        err = -EFAULT;
                                        goto __error;
                                }
index 63e91431a29f3f1093e9097a52fcd9c137b4fe11..4a2ec779b8a701b1aba2402e9de583878f7a39ca 100644 (file)
@@ -24,6 +24,8 @@
 #include <sound/seq_kernel.h>
 #include <linux/poll.h>
 
+struct snd_info_buffer;
+
 /* container for sequencer event (internal use) */
 struct snd_seq_event_cell {
        struct snd_seq_event event;
@@ -99,5 +101,7 @@ void snd_sequencer_memory_done(void);
 /* polling */
 int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait);
 
+void snd_seq_info_pool(struct snd_info_buffer *buffer,
+                      struct snd_seq_pool *pool, char *space);
 
 #endif
index 3bf7d73ac52ebbed66858c695050f25cde25156b..e12bcd94b6db29c074b3a66b692a5094cab11581 100644 (file)
@@ -412,7 +412,7 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
  * initialization or termination of devices (see seq_midi.c).
  *
  * If callback_all option is set, the callback function is invoked
- * at each connnection/disconnection. 
+ * at each connection/disconnection. 
  */
 
 static int subscribe_port(struct snd_seq_client *client,
index ed016329e911e9ab77d95b3abb7e2a44693363df..7c1cbf0a0dc4098326e587ee30b1ed268c460a45 100644 (file)
@@ -186,9 +186,8 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
                list_for_each_entry(master, &timer->open_list_head, open_list) {
                        if (slave->slave_class == master->slave_class &&
                            slave->slave_id == master->slave_id) {
-                               list_del(&slave->open_list);
-                               list_add_tail(&slave->open_list,
-                                             &master->slave_list_head);
+                               list_move_tail(&slave->open_list,
+                                              &master->slave_list_head);
                                spin_lock_irq(&slave_active_lock);
                                slave->master = master;
                                slave->timer = master->timer;
@@ -414,8 +413,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
 static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
                            unsigned long sticks)
 {
-       list_del(&timeri->active_list);
-       list_add_tail(&timeri->active_list, &timer->active_list_head);
+       list_move_tail(&timeri->active_list, &timer->active_list_head);
        if (timer->running) {
                if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                        goto __start_now;
index 3b9b550109cb9fe4e59b60db5ee436546029867c..a89948ae9e8d4dd6cf8859f4045a025cab92becd 100644 (file)
@@ -18,7 +18,7 @@
  * a subset of information returned via ctl info callback
  */
 struct link_ctl_info {
-       int type;               /* value type */
+       snd_ctl_elem_type_t type; /* value type */
        int count;              /* item count */
        int min_val, max_val;   /* min, max values */
 };
index 12b44b0b677712642a57be2f5554a4fb86347f2a..a0da7755fceaad0236c5ef013164df8770e1a5df 100644 (file)
@@ -482,8 +482,9 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
                        cable->streams[SNDRV_PCM_STREAM_CAPTURE];
        unsigned long delta_play = 0, delta_capt = 0;
        unsigned int running;
+       unsigned long flags;
 
-       spin_lock(&cable->lock);        
+       spin_lock_irqsave(&cable->lock, flags);
        running = cable->running ^ cable->pause;
        if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
                delta_play = jiffies - dpcm_play->last_jiffies;
@@ -495,10 +496,8 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
                dpcm_capt->last_jiffies += delta_capt;
        }
 
-       if (delta_play == 0 && delta_capt == 0) {
-               spin_unlock(&cable->lock);
-               return running;
-       }
+       if (delta_play == 0 && delta_capt == 0)
+               goto unlock;
                
        if (delta_play > delta_capt) {
                loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
@@ -510,14 +509,14 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
                delta_capt = delta_play;
        }
 
-       if (delta_play == 0 && delta_capt == 0) {
-               spin_unlock(&cable->lock);
-               return running;
-       }
+       if (delta_play == 0 && delta_capt == 0)
+               goto unlock;
+
        /* note delta_capt == delta_play at this moment */
        loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
        loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
-       spin_unlock(&cable->lock);
+ unlock:
+       spin_unlock_irqrestore(&cable->lock, flags);
        return running;
 }
 
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
new file mode 100644 (file)
index 0000000..e486f48
--- /dev/null
@@ -0,0 +1,25 @@
+menuconfig SND_FIREWIRE
+       bool "FireWire sound devices"
+       depends on FIREWIRE
+       default y
+       help
+         Support for IEEE-1394/FireWire/iLink sound devices.
+
+if SND_FIREWIRE && FIREWIRE
+
+config SND_FIREWIRE_LIB
+       tristate
+       depends on SND_PCM
+
+config SND_FIREWIRE_SPEAKERS
+       tristate "FireWire speakers"
+       select SND_PCM
+       select SND_FIREWIRE_LIB
+       help
+         Say Y here to include support for the Griffin FireWave Surround
+         and the LaCie FireWire Speakers.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-firewire-speakers.
+
+endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
new file mode 100644 (file)
index 0000000..e5b1634
--- /dev/null
@@ -0,0 +1,6 @@
+snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
+                        fcp.o cmp.o amdtp.o
+snd-firewire-speakers-objs := speakers.o
+
+obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
+obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
new file mode 100644 (file)
index 0000000..b18140f
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
+ * with Common Isochronous Packet (IEC 61883-1) headers
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include "amdtp.h"
+
+#define TICKS_PER_CYCLE                3072
+#define CYCLES_PER_SECOND      8000
+#define TICKS_PER_SECOND       (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 Âµs */
+
+#define TAG_CIP                        1
+
+#define CIP_EOH                        (1u << 31)
+#define CIP_FMT_AM             (0x10 << 24)
+#define AMDTP_FDF_AM824                (0 << 19)
+#define AMDTP_FDF_SFC_SHIFT    16
+
+/* TODO: make these configurable */
+#define INTERRUPT_INTERVAL     16
+#define QUEUE_LENGTH           48
+
+/**
+ * amdtp_out_stream_init - initialize an AMDTP output stream structure
+ * @s: the AMDTP output stream to initialize
+ * @unit: the target of the stream
+ * @flags: the packet transmission method to use
+ */
+int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
+                         enum cip_out_flags flags)
+{
+       if (flags != CIP_NONBLOCKING)
+               return -EINVAL;
+
+       s->unit = fw_unit_get(unit);
+       s->flags = flags;
+       s->context = ERR_PTR(-1);
+       mutex_init(&s->mutex);
+       s->packet_index = 0;
+
+       return 0;
+}
+EXPORT_SYMBOL(amdtp_out_stream_init);
+
+/**
+ * amdtp_out_stream_destroy - free stream resources
+ * @s: the AMDTP output stream to destroy
+ */
+void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
+{
+       WARN_ON(!IS_ERR(s->context));
+       mutex_destroy(&s->mutex);
+       fw_unit_put(s->unit);
+}
+EXPORT_SYMBOL(amdtp_out_stream_destroy);
+
+/**
+ * amdtp_out_stream_set_rate - set the sample rate
+ * @s: the AMDTP output stream to configure
+ * @rate: the sample rate
+ *
+ * The sample rate must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate)
+{
+       static const struct {
+               unsigned int rate;
+               unsigned int syt_interval;
+       } rate_info[] = {
+               [CIP_SFC_32000]  = {  32000,  8, },
+               [CIP_SFC_44100]  = {  44100,  8, },
+               [CIP_SFC_48000]  = {  48000,  8, },
+               [CIP_SFC_88200]  = {  88200, 16, },
+               [CIP_SFC_96000]  = {  96000, 16, },
+               [CIP_SFC_176400] = { 176400, 32, },
+               [CIP_SFC_192000] = { 192000, 32, },
+       };
+       unsigned int sfc;
+
+       if (WARN_ON(!IS_ERR(s->context)))
+               return;
+
+       for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc)
+               if (rate_info[sfc].rate == rate) {
+                       s->sfc = sfc;
+                       s->syt_interval = rate_info[sfc].syt_interval;
+                       return;
+               }
+       WARN_ON(1);
+}
+EXPORT_SYMBOL(amdtp_out_stream_set_rate);
+
+/**
+ * amdtp_out_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP output stream
+ *
+ * This function must not be called before the stream has been configured
+ * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and
+ * amdtp_out_stream_set_midi().
+ */
+unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
+{
+       static const unsigned int max_data_blocks[] = {
+               [CIP_SFC_32000]  =  4,
+               [CIP_SFC_44100]  =  6,
+               [CIP_SFC_48000]  =  6,
+               [CIP_SFC_88200]  = 12,
+               [CIP_SFC_96000]  = 12,
+               [CIP_SFC_176400] = 23,
+               [CIP_SFC_192000] = 24,
+       };
+
+       s->data_block_quadlets = s->pcm_channels;
+       s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
+
+       return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets;
+}
+EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
+
+static void amdtp_write_s16(struct amdtp_out_stream *s,
+                           struct snd_pcm_substream *pcm,
+                           __be32 *buffer, unsigned int frames);
+static void amdtp_write_s32(struct amdtp_out_stream *s,
+                           struct snd_pcm_substream *pcm,
+                           __be32 *buffer, unsigned int frames);
+
+/**
+ * amdtp_out_stream_set_pcm_format - set the PCM format
+ * @s: the AMDTP output stream to configure
+ * @format: the format of the ALSA PCM device
+ *
+ * The sample format must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
+                                    snd_pcm_format_t format)
+{
+       if (WARN_ON(!IS_ERR(s->context)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               s->transfer_samples = amdtp_write_s16;
+               break;
+       case SNDRV_PCM_FORMAT_S32:
+               s->transfer_samples = amdtp_write_s32;
+               break;
+       }
+}
+EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
+
+static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
+{
+       unsigned int phase, data_blocks;
+
+       if (!cip_sfc_is_base_44100(s->sfc)) {
+               /* Sample_rate / 8000 is an integer, and precomputed. */
+               data_blocks = s->data_block_state;
+       } else {
+               phase = s->data_block_state;
+
+               /*
+                * This calculates the number of data blocks per packet so that
+                * 1) the overall rate is correct and exactly synchronized to
+                *    the bus clock, and
+                * 2) packets with a rounded-up number of blocks occur as early
+                *    as possible in the sequence (to prevent underruns of the
+                *    device's buffer).
+                */
+               if (s->sfc == CIP_SFC_44100)
+                       /* 6 6 5 6 5 6 5 ... */
+                       data_blocks = 5 + ((phase & 1) ^
+                                          (phase == 0 || phase >= 40));
+               else
+                       /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+                       data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
+               if (++phase >= (80 >> (s->sfc >> 1)))
+                       phase = 0;
+               s->data_block_state = phase;
+       }
+
+       return data_blocks;
+}
+
+static unsigned int calculate_syt(struct amdtp_out_stream *s,
+                                 unsigned int cycle)
+{
+       unsigned int syt_offset, phase, index, syt;
+
+       if (s->last_syt_offset < TICKS_PER_CYCLE) {
+               if (!cip_sfc_is_base_44100(s->sfc))
+                       syt_offset = s->last_syt_offset + s->syt_offset_state;
+               else {
+               /*
+                * The time, in ticks, of the n'th SYT_INTERVAL sample is:
+                *   n * SYT_INTERVAL * 24576000 / sample_rate
+                * Modulo TICKS_PER_CYCLE, the difference between successive
+                * elements is about 1386.23.  Rounding the results of this
+                * formula to the SYT precision results in a sequence of
+                * differences that begins with:
+                *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
+                * This code generates _exactly_ the same sequence.
+                */
+                       phase = s->syt_offset_state;
+                       index = phase % 13;
+                       syt_offset = s->last_syt_offset;
+                       syt_offset += 1386 + ((index && !(index & 3)) ||
+                                             phase == 146);
+                       if (++phase >= 147)
+                               phase = 0;
+                       s->syt_offset_state = phase;
+               }
+       } else
+               syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
+       s->last_syt_offset = syt_offset;
+
+       if (syt_offset < TICKS_PER_CYCLE) {
+               syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+               syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
+               syt += syt_offset % TICKS_PER_CYCLE;
+
+               return syt & 0xffff;
+       } else {
+               return 0xffff; /* no info */
+       }
+}
+
+static void amdtp_write_s32(struct amdtp_out_stream *s,
+                           struct snd_pcm_substream *pcm,
+                           __be32 *buffer, unsigned int frames)
+{
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, frame_step, i, c;
+       const u32 *src;
+
+       channels = s->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+       frame_step = s->data_block_quadlets - channels;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+                       src++;
+                       buffer++;
+               }
+               buffer += frame_step;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void amdtp_write_s16(struct amdtp_out_stream *s,
+                           struct snd_pcm_substream *pcm,
+                           __be32 *buffer, unsigned int frames)
+{
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, frame_step, i, c;
+       const u16 *src;
+
+       channels = s->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+       frame_step = s->data_block_quadlets - channels;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *buffer = cpu_to_be32((*src << 8) | 0x40000000);
+                       src++;
+                       buffer++;
+               }
+               buffer += frame_step;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
+                                  __be32 *buffer, unsigned int frames)
+{
+       unsigned int i, c;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < s->pcm_channels; ++c)
+                       buffer[c] = cpu_to_be32(0x40000000);
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static void amdtp_fill_midi(struct amdtp_out_stream *s,
+                           __be32 *buffer, unsigned int frames)
+{
+       unsigned int i;
+
+       for (i = 0; i < frames; ++i)
+               buffer[s->pcm_channels + i * s->data_block_quadlets] =
+                                               cpu_to_be32(0x80000000);
+}
+
+static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
+{
+       __be32 *buffer;
+       unsigned int index, data_blocks, syt, ptr;
+       struct snd_pcm_substream *pcm;
+       struct fw_iso_packet packet;
+       int err;
+
+       if (s->packet_index < 0)
+               return;
+       index = s->packet_index;
+
+       data_blocks = calculate_data_blocks(s);
+       syt = calculate_syt(s, cycle);
+
+       buffer = s->buffer.packets[index].buffer;
+       buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
+                               (s->data_block_quadlets << 16) |
+                               s->data_block_counter);
+       buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
+                               (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt);
+       buffer += 2;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm)
+               s->transfer_samples(s, pcm, buffer, data_blocks);
+       else
+               amdtp_fill_pcm_silence(s, buffer, data_blocks);
+       if (s->midi_ports)
+               amdtp_fill_midi(s, buffer, data_blocks);
+
+       s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+       packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
+       packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL);
+       packet.skip = 0;
+       packet.tag = TAG_CIP;
+       packet.sy = 0;
+       packet.header_length = 0;
+
+       err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer,
+                                  s->buffer.packets[index].offset);
+       if (err < 0) {
+               dev_err(&s->unit->device, "queueing error: %d\n", err);
+               s->packet_index = -1;
+               amdtp_out_stream_pcm_abort(s);
+               return;
+       }
+
+       if (++index >= QUEUE_LENGTH)
+               index = 0;
+       s->packet_index = index;
+
+       if (pcm) {
+               ptr = s->pcm_buffer_pointer + data_blocks;
+               if (ptr >= pcm->runtime->buffer_size)
+                       ptr -= pcm->runtime->buffer_size;
+               ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
+
+               s->pcm_period_pointer += data_blocks;
+               if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+                       s->pcm_period_pointer -= pcm->runtime->period_size;
+                       snd_pcm_period_elapsed(pcm);
+               }
+       }
+}
+
+static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
+                               size_t header_length, void *header, void *data)
+{
+       struct amdtp_out_stream *s = data;
+       unsigned int i, packets = header_length / 4;
+
+       /*
+        * Compute the cycle of the last queued packet.
+        * (We need only the four lowest bits for the SYT, so we can ignore
+        * that bits 0-11 must wrap around at 3072.)
+        */
+       cycle += QUEUE_LENGTH - packets;
+
+       for (i = 0; i < packets; ++i)
+               queue_out_packet(s, ++cycle);
+}
+
+static int queue_initial_skip_packets(struct amdtp_out_stream *s)
+{
+       struct fw_iso_packet skip_packet = {
+               .skip = 1,
+       };
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < QUEUE_LENGTH; ++i) {
+               skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1,
+                                                  INTERRUPT_INTERVAL);
+               err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0);
+               if (err < 0)
+                       return err;
+               if (++s->packet_index >= QUEUE_LENGTH)
+                       s->packet_index = 0;
+       }
+
+       return 0;
+}
+
+/**
+ * amdtp_out_stream_start - start sending packets
+ * @s: the AMDTP output stream to start
+ * @channel: the isochronous channel on the bus
+ * @speed: firewire speed code
+ *
+ * The stream cannot be started until it has been configured with
+ * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and
+ * amdtp_out_stream_set_midi(); and it must be started before any
+ * PCM or MIDI device can be started.
+ */
+int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
+{
+       static const struct {
+               unsigned int data_block;
+               unsigned int syt_offset;
+       } initial_state[] = {
+               [CIP_SFC_32000]  = {  4, 3072 },
+               [CIP_SFC_48000]  = {  6, 1024 },
+               [CIP_SFC_96000]  = { 12, 1024 },
+               [CIP_SFC_192000] = { 24, 1024 },
+               [CIP_SFC_44100]  = {  0,   67 },
+               [CIP_SFC_88200]  = {  0,   67 },
+               [CIP_SFC_176400] = {  0,   67 },
+       };
+       int err;
+
+       mutex_lock(&s->mutex);
+
+       if (WARN_ON(!IS_ERR(s->context) ||
+                   (!s->pcm_channels && !s->midi_ports))) {
+               err = -EBADFD;
+               goto err_unlock;
+       }
+
+       s->data_block_state = initial_state[s->sfc].data_block;
+       s->syt_offset_state = initial_state[s->sfc].syt_offset;
+       s->last_syt_offset = TICKS_PER_CYCLE;
+
+       err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
+                                     amdtp_out_stream_get_max_payload(s),
+                                     DMA_TO_DEVICE);
+       if (err < 0)
+               goto err_unlock;
+
+       s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
+                                          FW_ISO_CONTEXT_TRANSMIT,
+                                          channel, speed, 0,
+                                          out_packet_callback, s);
+       if (IS_ERR(s->context)) {
+               err = PTR_ERR(s->context);
+               if (err == -EBUSY)
+                       dev_err(&s->unit->device,
+                               "no free output stream on this controller\n");
+               goto err_buffer;
+       }
+
+       amdtp_out_stream_update(s);
+
+       s->packet_index = 0;
+       s->data_block_counter = 0;
+       err = queue_initial_skip_packets(s);
+       if (err < 0)
+               goto err_context;
+
+       err = fw_iso_context_start(s->context, -1, 0, 0);
+       if (err < 0)
+               goto err_context;
+
+       mutex_unlock(&s->mutex);
+
+       return 0;
+
+err_context:
+       fw_iso_context_destroy(s->context);
+       s->context = ERR_PTR(-1);
+err_buffer:
+       iso_packets_buffer_destroy(&s->buffer, s->unit);
+err_unlock:
+       mutex_unlock(&s->mutex);
+
+       return err;
+}
+EXPORT_SYMBOL(amdtp_out_stream_start);
+
+/**
+ * amdtp_out_stream_update - update the stream after a bus reset
+ * @s: the AMDTP output stream
+ */
+void amdtp_out_stream_update(struct amdtp_out_stream *s)
+{
+       ACCESS_ONCE(s->source_node_id_field) =
+               (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
+}
+EXPORT_SYMBOL(amdtp_out_stream_update);
+
+/**
+ * amdtp_out_stream_stop - stop sending packets
+ * @s: the AMDTP output stream to stop
+ *
+ * All PCM and MIDI devices of the stream must be stopped before the stream
+ * itself can be stopped.
+ */
+void amdtp_out_stream_stop(struct amdtp_out_stream *s)
+{
+       mutex_lock(&s->mutex);
+
+       if (IS_ERR(s->context)) {
+               mutex_unlock(&s->mutex);
+               return;
+       }
+
+       fw_iso_context_stop(s->context);
+       fw_iso_context_destroy(s->context);
+       s->context = ERR_PTR(-1);
+       iso_packets_buffer_destroy(&s->buffer, s->unit);
+
+       mutex_unlock(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_out_stream_stop);
+
+/**
+ * amdtp_out_stream_pcm_abort - abort the running PCM device
+ * @s: the AMDTP stream about to be stopped
+ *
+ * If the isochronous stream needs to be stopped asynchronously, call this
+ * function first to stop the PCM device.
+ */
+void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
+{
+       struct snd_pcm_substream *pcm;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm) {
+               snd_pcm_stream_lock_irq(pcm);
+               if (snd_pcm_running(pcm))
+                       snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN);
+               snd_pcm_stream_unlock_irq(pcm);
+       }
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_abort);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
new file mode 100644 (file)
index 0000000..537a9cb
--- /dev/null
@@ -0,0 +1,169 @@
+#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include "packets-buffer.h"
+
+/**
+ * enum cip_out_flags - describes details of the streaming protocol
+ * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
+ *     sample_rate/8000 samples, with rounding up or down to adjust
+ *     for clock skew and left-over fractional samples.  This should
+ *     be used if supported by the device.
+ */
+enum cip_out_flags {
+       CIP_NONBLOCKING = 0,
+};
+
+/**
+ * enum cip_sfc - a stream's sample rate
+ */
+enum cip_sfc {
+       CIP_SFC_32000  = 0,
+       CIP_SFC_44100  = 1,
+       CIP_SFC_48000  = 2,
+       CIP_SFC_88200  = 3,
+       CIP_SFC_96000  = 4,
+       CIP_SFC_176400 = 5,
+       CIP_SFC_192000 = 6,
+};
+
+#define AMDTP_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
+                                        SNDRV_PCM_FMTBIT_S32)
+
+struct fw_unit;
+struct fw_iso_context;
+struct snd_pcm_substream;
+
+struct amdtp_out_stream {
+       struct fw_unit *unit;
+       enum cip_out_flags flags;
+       struct fw_iso_context *context;
+       struct mutex mutex;
+
+       enum cip_sfc sfc;
+       unsigned int data_block_quadlets;
+       unsigned int pcm_channels;
+       unsigned int midi_ports;
+       void (*transfer_samples)(struct amdtp_out_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+
+       unsigned int syt_interval;
+       unsigned int source_node_id_field;
+       struct iso_packets_buffer buffer;
+
+       struct snd_pcm_substream *pcm;
+
+       int packet_index;
+       unsigned int data_block_counter;
+
+       unsigned int data_block_state;
+
+       unsigned int last_syt_offset;
+       unsigned int syt_offset_state;
+
+       unsigned int pcm_buffer_pointer;
+       unsigned int pcm_period_pointer;
+};
+
+int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
+                         enum cip_out_flags flags);
+void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
+
+void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate);
+unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
+
+int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
+void amdtp_out_stream_update(struct amdtp_out_stream *s);
+void amdtp_out_stream_stop(struct amdtp_out_stream *s);
+
+void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
+                                    snd_pcm_format_t format);
+void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
+
+/**
+ * amdtp_out_stream_set_pcm - configure format of PCM samples
+ * @s: the AMDTP output stream to be configured
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ *                as AM824 multi-bit linear audio
+ *
+ * This function must not be called while the stream is running.
+ */
+static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s,
+                                           unsigned int pcm_channels)
+{
+       s->pcm_channels = pcm_channels;
+}
+
+/**
+ * amdtp_out_stream_set_midi - configure format of MIDI data
+ * @s: the AMDTP output stream to be configured
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ *
+ * This function must not be called while the stream is running.
+ */
+static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s,
+                                            unsigned int midi_ports)
+{
+       s->midi_ports = midi_ports;
+}
+
+/**
+ * amdtp_out_streaming_error - check for streaming error
+ * @s: the AMDTP output stream
+ *
+ * If this function returns true, the stream's packet queue has stopped due to
+ * an asynchronous error.
+ */
+static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
+{
+       return s->packet_index < 0;
+}
+
+/**
+ * amdtp_out_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP output stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+{
+       s->pcm_buffer_pointer = 0;
+       s->pcm_period_pointer = 0;
+}
+
+/**
+ * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP output stream
+ * @pcm: the PCM device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of PCM data.  This function should be called from the PCM
+ * device's .trigger callback.
+ */
+static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
+                                               struct snd_pcm_substream *pcm)
+{
+       ACCESS_ONCE(s->pcm) = pcm;
+}
+
+/**
+ * amdtp_out_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP output stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+static inline unsigned long
+amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+{
+       return ACCESS_ONCE(s->pcm_buffer_pointer);
+}
+
+static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
+{
+       return sfc & 1;
+}
+
+#endif
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
new file mode 100644 (file)
index 0000000..4a37f3a
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Connection Management Procedures (IEC 61883-1) helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "cmp.h"
+
+#define IMPR_SPEED_MASK                0xc0000000
+#define IMPR_SPEED_SHIFT       30
+#define IMPR_XSPEED_MASK       0x00000060
+#define IMPR_XSPEED_SHIFT      5
+#define IMPR_PLUGS_MASK                0x0000001f
+
+#define IPCR_ONLINE            0x80000000
+#define IPCR_BCAST_CONN                0x40000000
+#define IPCR_P2P_CONN_MASK     0x3f000000
+#define IPCR_P2P_CONN_SHIFT    24
+#define IPCR_CHANNEL_MASK      0x003f0000
+#define IPCR_CHANNEL_SHIFT     16
+
+enum bus_reset_handling {
+       ABORT_ON_BUS_RESET,
+       SUCCEED_ON_BUS_RESET,
+};
+
+static __attribute__((format(printf, 2, 3)))
+void cmp_error(struct cmp_connection *c, const char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
+               'i', c->pcr_index, &(struct va_format){ fmt, &va });
+       va_end(va);
+}
+
+static int pcr_modify(struct cmp_connection *c,
+                     __be32 (*modify)(struct cmp_connection *c, __be32 old),
+                     int (*check)(struct cmp_connection *c, __be32 pcr),
+                     enum bus_reset_handling bus_reset_handling)
+{
+       struct fw_device *device = fw_parent_device(c->resources.unit);
+       __be32 *buffer = c->resources.buffer;
+       int generation = c->resources.generation;
+       int rcode, errors = 0;
+       __be32 old_arg;
+       int err;
+
+       buffer[0] = c->last_pcr_value;
+       for (;;) {
+               old_arg = buffer[0];
+               buffer[1] = modify(c, buffer[0]);
+
+               rcode = fw_run_transaction(
+                               device->card, TCODE_LOCK_COMPARE_SWAP,
+                               device->node_id, generation, device->max_speed,
+                               CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
+                               buffer, 8);
+
+               if (rcode == RCODE_COMPLETE) {
+                       if (buffer[0] == old_arg) /* success? */
+                               break;
+
+                       if (check) {
+                               err = check(c, buffer[0]);
+                               if (err < 0)
+                                       return err;
+                       }
+               } else if (rcode == RCODE_GENERATION)
+                       goto bus_reset;
+               else if (rcode_is_permanent_error(rcode) || ++errors >= 3)
+                       goto io_error;
+       }
+       c->last_pcr_value = buffer[1];
+
+       return 0;
+
+io_error:
+       cmp_error(c, "transaction failed: %s\n", rcode_string(rcode));
+       return -EIO;
+
+bus_reset:
+       return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0;
+}
+
+
+/**
+ * cmp_connection_init - initializes a connection manager
+ * @c: the connection manager to initialize
+ * @unit: a unit of the target device
+ * @ipcr_index: the index of the iPCR on the target device
+ */
+int cmp_connection_init(struct cmp_connection *c,
+                       struct fw_unit *unit,
+                       unsigned int ipcr_index)
+{
+       __be32 impr_be;
+       u32 impr;
+       int err;
+
+       err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+                                CSR_REGISTER_BASE + CSR_IMPR,
+                                &impr_be, 4);
+       if (err < 0)
+               return err;
+       impr = be32_to_cpu(impr_be);
+
+       if (ipcr_index >= (impr & IMPR_PLUGS_MASK))
+               return -EINVAL;
+
+       err = fw_iso_resources_init(&c->resources, unit);
+       if (err < 0)
+               return err;
+
+       c->connected = false;
+       mutex_init(&c->mutex);
+       c->last_pcr_value = cpu_to_be32(0x80000000);
+       c->pcr_index = ipcr_index;
+       c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT;
+       if (c->max_speed == SCODE_BETA)
+               c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT;
+
+       return 0;
+}
+EXPORT_SYMBOL(cmp_connection_init);
+
+/**
+ * cmp_connection_destroy - free connection manager resources
+ * @c: the connection manager
+ */
+void cmp_connection_destroy(struct cmp_connection *c)
+{
+       WARN_ON(c->connected);
+       mutex_destroy(&c->mutex);
+       fw_iso_resources_destroy(&c->resources);
+}
+EXPORT_SYMBOL(cmp_connection_destroy);
+
+
+static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
+{
+       ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN |
+                            IPCR_P2P_CONN_MASK |
+                            IPCR_CHANNEL_MASK);
+       ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT);
+       ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT);
+
+       return ipcr;
+}
+
+static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
+{
+       if (ipcr & cpu_to_be32(IPCR_BCAST_CONN |
+                              IPCR_P2P_CONN_MASK)) {
+               cmp_error(c, "plug is already in use\n");
+               return -EBUSY;
+       }
+       if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) {
+               cmp_error(c, "plug is not on-line\n");
+               return -ECONNREFUSED;
+       }
+
+       return 0;
+}
+
+/**
+ * cmp_connection_establish - establish a connection to the target
+ * @c: the connection manager
+ * @max_payload_bytes: the amount of data (including CIP headers) per packet
+ *
+ * This function establishes a point-to-point connection from the local
+ * computer to the target by allocating isochronous resources (channel and
+ * bandwidth) and setting the target's input plug control register.  When this
+ * function succeeds, the caller is responsible for starting transmitting
+ * packets.
+ */
+int cmp_connection_establish(struct cmp_connection *c,
+                            unsigned int max_payload_bytes)
+{
+       int err;
+
+       if (WARN_ON(c->connected))
+               return -EISCONN;
+
+       c->speed = min(c->max_speed,
+                      fw_parent_device(c->resources.unit)->max_speed);
+
+       mutex_lock(&c->mutex);
+
+retry_after_bus_reset:
+       err = fw_iso_resources_allocate(&c->resources,
+                                       max_payload_bytes, c->speed);
+       if (err < 0)
+               goto err_mutex;
+
+       err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
+                        ABORT_ON_BUS_RESET);
+       if (err == -EAGAIN) {
+               fw_iso_resources_free(&c->resources);
+               goto retry_after_bus_reset;
+       }
+       if (err < 0)
+               goto err_resources;
+
+       c->connected = true;
+
+       mutex_unlock(&c->mutex);
+
+       return 0;
+
+err_resources:
+       fw_iso_resources_free(&c->resources);
+err_mutex:
+       mutex_unlock(&c->mutex);
+
+       return err;
+}
+EXPORT_SYMBOL(cmp_connection_establish);
+
+/**
+ * cmp_connection_update - update the connection after a bus reset
+ * @c: the connection manager
+ *
+ * This function must be called from the driver's .update handler to reestablish
+ * any connection that might have been active.
+ *
+ * Returns zero on success, or a negative error code.  On an error, the
+ * connection is broken and the caller must stop transmitting iso packets.
+ */
+int cmp_connection_update(struct cmp_connection *c)
+{
+       int err;
+
+       mutex_lock(&c->mutex);
+
+       if (!c->connected) {
+               mutex_unlock(&c->mutex);
+               return 0;
+       }
+
+       err = fw_iso_resources_update(&c->resources);
+       if (err < 0)
+               goto err_unconnect;
+
+       err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
+                        SUCCEED_ON_BUS_RESET);
+       if (err < 0)
+               goto err_resources;
+
+       mutex_unlock(&c->mutex);
+
+       return 0;
+
+err_resources:
+       fw_iso_resources_free(&c->resources);
+err_unconnect:
+       c->connected = false;
+       mutex_unlock(&c->mutex);
+
+       return err;
+}
+EXPORT_SYMBOL(cmp_connection_update);
+
+
+static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
+{
+       return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK);
+}
+
+/**
+ * cmp_connection_break - break the connection to the target
+ * @c: the connection manager
+ *
+ * This function deactives the connection in the target's input plug control
+ * register, and frees the isochronous resources of the connection.  Before
+ * calling this function, the caller should cease transmitting packets.
+ */
+void cmp_connection_break(struct cmp_connection *c)
+{
+       int err;
+
+       mutex_lock(&c->mutex);
+
+       if (!c->connected) {
+               mutex_unlock(&c->mutex);
+               return;
+       }
+
+       err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
+       if (err < 0)
+               cmp_error(c, "plug is still connected\n");
+
+       fw_iso_resources_free(&c->resources);
+
+       c->connected = false;
+
+       mutex_unlock(&c->mutex);
+}
+EXPORT_SYMBOL(cmp_connection_break);
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
new file mode 100644 (file)
index 0000000..f47de08
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef SOUND_FIREWIRE_CMP_H_INCLUDED
+#define SOUND_FIREWIRE_CMP_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include "iso-resources.h"
+
+struct fw_unit;
+
+/**
+ * struct cmp_connection - manages an isochronous connection to a device
+ * @speed: the connection's actual speed
+ *
+ * This structure manages (using CMP) an isochronous stream from the local
+ * computer to a device's input plug (iPCR).
+ *
+ * There is no corresponding oPCR created on the local computer, so it is not
+ * possible to overlay connections on top of this one.
+ */
+struct cmp_connection {
+       int speed;
+       /* private: */
+       bool connected;
+       struct mutex mutex;
+       struct fw_iso_resources resources;
+       __be32 last_pcr_value;
+       unsigned int pcr_index;
+       unsigned int max_speed;
+};
+
+int cmp_connection_init(struct cmp_connection *connection,
+                       struct fw_unit *unit,
+                       unsigned int ipcr_index);
+void cmp_connection_destroy(struct cmp_connection *connection);
+
+int cmp_connection_establish(struct cmp_connection *connection,
+                            unsigned int max_payload);
+int cmp_connection_update(struct cmp_connection *connection);
+void cmp_connection_break(struct cmp_connection *connection);
+
+#endif
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
new file mode 100644 (file)
index 0000000..ec578b5
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Function Control Protocol (IEC 61883-1) helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include "fcp.h"
+#include "lib.h"
+
+#define CTS_AVC 0x00
+
+#define ERROR_RETRIES  3
+#define ERROR_DELAY_MS 5
+#define FCP_TIMEOUT_MS 125
+
+static DEFINE_SPINLOCK(transactions_lock);
+static LIST_HEAD(transactions);
+
+enum fcp_state {
+       STATE_PENDING,
+       STATE_BUS_RESET,
+       STATE_COMPLETE,
+};
+
+struct fcp_transaction {
+       struct list_head list;
+       struct fw_unit *unit;
+       void *response_buffer;
+       unsigned int response_size;
+       unsigned int response_match_bytes;
+       enum fcp_state state;
+       wait_queue_head_t wait;
+};
+
+/**
+ * fcp_avc_transaction - send an AV/C command and wait for its response
+ * @unit: a unit on the target device
+ * @command: a buffer containing the command frame; must be DMA-able
+ * @command_size: the size of @command
+ * @response: a buffer for the response frame
+ * @response_size: the maximum size of @response
+ * @response_match_bytes: a bitmap specifying the bytes used to detect the
+ *                        correct response frame
+ *
+ * This function sends a FCP command frame to the target and waits for the
+ * corresponding response frame to be returned.
+ *
+ * Because it is possible for multiple FCP transactions to be active at the
+ * same time, the correct response frame is detected by the value of certain
+ * bytes.  These bytes must be set in @response before calling this function,
+ * and the corresponding bits must be set in @response_match_bytes.
+ *
+ * @command and @response can point to the same buffer.
+ *
+ * Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment.
+ *
+ * Returns the actual size of the response frame, or a negative error code.
+ */
+int fcp_avc_transaction(struct fw_unit *unit,
+                       const void *command, unsigned int command_size,
+                       void *response, unsigned int response_size,
+                       unsigned int response_match_bytes)
+{
+       struct fcp_transaction t;
+       int tcode, ret, tries = 0;
+
+       t.unit = unit;
+       t.response_buffer = response;
+       t.response_size = response_size;
+       t.response_match_bytes = response_match_bytes;
+       t.state = STATE_PENDING;
+       init_waitqueue_head(&t.wait);
+
+       spin_lock_irq(&transactions_lock);
+       list_add_tail(&t.list, &transactions);
+       spin_unlock_irq(&transactions_lock);
+
+       for (;;) {
+               tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST
+                                         : TCODE_WRITE_BLOCK_REQUEST;
+               ret = snd_fw_transaction(t.unit, tcode,
+                                        CSR_REGISTER_BASE + CSR_FCP_COMMAND,
+                                        (void *)command, command_size);
+               if (ret < 0)
+                       break;
+
+               wait_event_timeout(t.wait, t.state != STATE_PENDING,
+                                  msecs_to_jiffies(FCP_TIMEOUT_MS));
+
+               if (t.state == STATE_COMPLETE) {
+                       ret = t.response_size;
+                       break;
+               } else if (t.state == STATE_BUS_RESET) {
+                       msleep(ERROR_DELAY_MS);
+               } else if (++tries >= ERROR_RETRIES) {
+                       dev_err(&t.unit->device, "FCP command timed out\n");
+                       ret = -EIO;
+                       break;
+               }
+       }
+
+       spin_lock_irq(&transactions_lock);
+       list_del(&t.list);
+       spin_unlock_irq(&transactions_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(fcp_avc_transaction);
+
+/**
+ * fcp_bus_reset - inform the target handler about a bus reset
+ * @unit: the unit that might be used by fcp_avc_transaction()
+ *
+ * This function must be called from the driver's .update handler to inform
+ * the FCP transaction handler that a bus reset has happened.  Any pending FCP
+ * transactions are retried.
+ */
+void fcp_bus_reset(struct fw_unit *unit)
+{
+       struct fcp_transaction *t;
+
+       spin_lock_irq(&transactions_lock);
+       list_for_each_entry(t, &transactions, list) {
+               if (t->unit == unit &&
+                   t->state == STATE_PENDING) {
+                       t->state = STATE_BUS_RESET;
+                       wake_up(&t->wait);
+               }
+       }
+       spin_unlock_irq(&transactions_lock);
+}
+EXPORT_SYMBOL(fcp_bus_reset);
+
+/* checks whether the response matches the masked bytes in response_buffer */
+static bool is_matching_response(struct fcp_transaction *transaction,
+                                const void *response, size_t length)
+{
+       const u8 *p1, *p2;
+       unsigned int mask, i;
+
+       p1 = response;
+       p2 = transaction->response_buffer;
+       mask = transaction->response_match_bytes;
+
+       for (i = 0; ; ++i) {
+               if ((mask & 1) && p1[i] != p2[i])
+                       return false;
+               mask >>= 1;
+               if (!mask)
+                       return true;
+               if (--length == 0)
+                       return false;
+       }
+}
+
+static void fcp_response(struct fw_card *card, struct fw_request *request,
+                        int tcode, int destination, int source,
+                        int generation, unsigned long long offset,
+                        void *data, size_t length, void *callback_data)
+{
+       struct fcp_transaction *t;
+       unsigned long flags;
+
+       if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC)
+               return;
+
+       spin_lock_irqsave(&transactions_lock, flags);
+       list_for_each_entry(t, &transactions, list) {
+               struct fw_device *device = fw_parent_device(t->unit);
+               if (device->card != card ||
+                   device->generation != generation)
+                       continue;
+               smp_rmb(); /* node_id vs. generation */
+               if (device->node_id != source)
+                       continue;
+
+               if (t->state == STATE_PENDING &&
+                   is_matching_response(t, data, length)) {
+                       t->state = STATE_COMPLETE;
+                       t->response_size = min((unsigned int)length,
+                                              t->response_size);
+                       memcpy(t->response_buffer, data, t->response_size);
+                       wake_up(&t->wait);
+               }
+       }
+       spin_unlock_irqrestore(&transactions_lock, flags);
+}
+
+static struct fw_address_handler response_register_handler = {
+       .length = 0x200,
+       .address_callback = fcp_response,
+};
+
+static int __init fcp_module_init(void)
+{
+       static const struct fw_address_region response_register_region = {
+               .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
+               .end = CSR_REGISTER_BASE + CSR_FCP_END,
+       };
+
+       fw_core_add_address_handler(&response_register_handler,
+                                   &response_register_region);
+
+       return 0;
+}
+
+static void __exit fcp_module_exit(void)
+{
+       WARN_ON(!list_empty(&transactions));
+       fw_core_remove_address_handler(&response_register_handler);
+}
+
+module_init(fcp_module_init);
+module_exit(fcp_module_exit);
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h
new file mode 100644 (file)
index 0000000..8659568
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
+#define SOUND_FIREWIRE_FCP_H_INCLUDED
+
+struct fw_unit;
+
+int fcp_avc_transaction(struct fw_unit *unit,
+                       const void *command, unsigned int command_size,
+                       void *response, unsigned int response_size,
+                       unsigned int response_match_bytes);
+void fcp_bus_reset(struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
new file mode 100644 (file)
index 0000000..775dbd5
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * isochronous resources helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "iso-resources.h"
+
+/**
+ * fw_iso_resources_init - initializes a &struct fw_iso_resources
+ * @r: the resource manager to initialize
+ * @unit: the device unit for which the resources will be needed
+ *
+ * If the device does not support all channel numbers, change @r->channels_mask
+ * after calling this function.
+ */
+int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
+{
+       r->buffer = kmalloc(2 * 4, GFP_KERNEL);
+       if (!r->buffer)
+               return -ENOMEM;
+
+       r->channels_mask = ~0uLL;
+       r->unit = fw_unit_get(unit);
+       mutex_init(&r->mutex);
+       r->allocated = false;
+
+       return 0;
+}
+
+/**
+ * fw_iso_resources_destroy - destroy a resource manager
+ * @r: the resource manager that is no longer needed
+ */
+void fw_iso_resources_destroy(struct fw_iso_resources *r)
+{
+       WARN_ON(r->allocated);
+       kfree(r->buffer);
+       mutex_destroy(&r->mutex);
+       fw_unit_put(r->unit);
+}
+
+static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
+{
+       unsigned int bytes, s400_bytes;
+
+       /* iso packets have three header quadlets and quadlet-aligned payload */
+       bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
+
+       /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
+       if (speed <= SCODE_400)
+               s400_bytes = bytes * (1 << (SCODE_400 - speed));
+       else
+               s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
+
+       return s400_bytes;
+}
+
+static int current_bandwidth_overhead(struct fw_card *card)
+{
+       /*
+        * Under the usual pessimistic assumption (cable length 4.5 m), the
+        * isochronous overhead for N cables is 1.797 Âµs + N * 0.494 Âµs, or
+        * 88.3 + N * 24.3 in bandwidth units.
+        *
+        * The calculation below tries to deduce N from the current gap count.
+        * If the gap count has been optimized by measuring the actual packet
+        * transmission time, this derived overhead should be near the actual
+        * overhead as well.
+        */
+       return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
+}
+
+static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
+{
+       for (;;) {
+               s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
+               if (delay <= 0)
+                       return 0;
+               if (schedule_timeout_interruptible(delay) > 0)
+                       return -ERESTARTSYS;
+       }
+}
+
+/**
+ * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
+ * @r: the resource manager
+ * @max_payload_bytes: the amount of data (including CIP headers) per packet
+ * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
+ *
+ * This function allocates one isochronous channel and enough bandwidth for the
+ * specified packet size.
+ *
+ * Returns the channel number that the caller must use for streaming, or
+ * a negative error code.  Due to potentionally long delays, this function is
+ * interruptible and can return -ERESTARTSYS.  On success, the caller is
+ * responsible for calling fw_iso_resources_update() on bus resets, and
+ * fw_iso_resources_free() when the resources are not longer needed.
+ */
+int fw_iso_resources_allocate(struct fw_iso_resources *r,
+                             unsigned int max_payload_bytes, int speed)
+{
+       struct fw_card *card = fw_parent_device(r->unit)->card;
+       int bandwidth, channel, err;
+
+       if (WARN_ON(r->allocated))
+               return -EBADFD;
+
+       r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
+
+retry_after_bus_reset:
+       spin_lock_irq(&card->lock);
+       r->generation = card->generation;
+       r->bandwidth_overhead = current_bandwidth_overhead(card);
+       spin_unlock_irq(&card->lock);
+
+       err = wait_isoch_resource_delay_after_bus_reset(card);
+       if (err < 0)
+               return err;
+
+       mutex_lock(&r->mutex);
+
+       bandwidth = r->bandwidth + r->bandwidth_overhead;
+       fw_iso_resource_manage(card, r->generation, r->channels_mask,
+                              &channel, &bandwidth, true, r->buffer);
+       if (channel == -EAGAIN) {
+               mutex_unlock(&r->mutex);
+               goto retry_after_bus_reset;
+       }
+       if (channel >= 0) {
+               r->channel = channel;
+               r->allocated = true;
+       } else {
+               if (channel == -EBUSY)
+                       dev_err(&r->unit->device,
+                               "isochronous resources exhausted\n");
+               else
+                       dev_err(&r->unit->device,
+                               "isochronous resource allocation failed\n");
+       }
+
+       mutex_unlock(&r->mutex);
+
+       return channel;
+}
+
+/**
+ * fw_iso_resources_update - update resource allocations after a bus reset
+ * @r: the resource manager
+ *
+ * This function must be called from the driver's .update handler to reallocate
+ * any resources that were allocated before the bus reset.  It is safe to call
+ * this function if no resources are currently allocated.
+ *
+ * Returns a negative error code on failure.  If this happens, the caller must
+ * stop streaming.
+ */
+int fw_iso_resources_update(struct fw_iso_resources *r)
+{
+       struct fw_card *card = fw_parent_device(r->unit)->card;
+       int bandwidth, channel;
+
+       mutex_lock(&r->mutex);
+
+       if (!r->allocated) {
+               mutex_unlock(&r->mutex);
+               return 0;
+       }
+
+       spin_lock_irq(&card->lock);
+       r->generation = card->generation;
+       r->bandwidth_overhead = current_bandwidth_overhead(card);
+       spin_unlock_irq(&card->lock);
+
+       bandwidth = r->bandwidth + r->bandwidth_overhead;
+
+       fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
+                              &channel, &bandwidth, true, r->buffer);
+       /*
+        * When another bus reset happens, pretend that the allocation
+        * succeeded; we will try again for the new generation later.
+        */
+       if (channel < 0 && channel != -EAGAIN) {
+               r->allocated = false;
+               if (channel == -EBUSY)
+                       dev_err(&r->unit->device,
+                               "isochronous resources exhausted\n");
+               else
+                       dev_err(&r->unit->device,
+                               "isochronous resource allocation failed\n");
+       }
+
+       mutex_unlock(&r->mutex);
+
+       return channel;
+}
+
+/**
+ * fw_iso_resources_free - frees allocated resources
+ * @r: the resource manager
+ *
+ * This function deallocates the channel and bandwidth, if allocated.
+ */
+void fw_iso_resources_free(struct fw_iso_resources *r)
+{
+       struct fw_card *card = fw_parent_device(r->unit)->card;
+       int bandwidth, channel;
+
+       mutex_lock(&r->mutex);
+
+       if (r->allocated) {
+               bandwidth = r->bandwidth + r->bandwidth_overhead;
+               fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
+                                      &channel, &bandwidth, false, r->buffer);
+               if (channel < 0)
+                       dev_err(&r->unit->device,
+                               "isochronous resource deallocation failed\n");
+
+               r->allocated = false;
+       }
+
+       mutex_unlock(&r->mutex);
+}
diff --git a/sound/firewire/iso-resources.h b/sound/firewire/iso-resources.h
new file mode 100644 (file)
index 0000000..3f0730e
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED
+#define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+struct fw_unit;
+
+/**
+ * struct fw_iso_resources - manages channel/bandwidth allocation
+ * @channels_mask: if the device does not support all channel numbers, set this
+ *                 bit mask to something else than the default (all ones)
+ *
+ * This structure manages (de)allocation of isochronous resources (channel and
+ * bandwidth) for one isochronous stream.
+ */
+struct fw_iso_resources {
+       u64 channels_mask;
+       /* private: */
+       struct fw_unit *unit;
+       struct mutex mutex;
+       unsigned int channel;
+       unsigned int bandwidth; /* in bandwidth units, without overhead */
+       unsigned int bandwidth_overhead;
+       int generation; /* in which allocation is valid */
+       bool allocated;
+       __be32 *buffer;
+};
+
+int fw_iso_resources_init(struct fw_iso_resources *r,
+                         struct fw_unit *unit);
+void fw_iso_resources_destroy(struct fw_iso_resources *r);
+
+int fw_iso_resources_allocate(struct fw_iso_resources *r,
+                             unsigned int max_payload_bytes, int speed);
+int fw_iso_resources_update(struct fw_iso_resources *r);
+void fw_iso_resources_free(struct fw_iso_resources *r);
+
+#endif
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
new file mode 100644 (file)
index 0000000..4750cea
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * miscellaneous helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include "lib.h"
+
+#define ERROR_RETRY_DELAY_MS   5
+
+/**
+ * rcode_string - convert a firewire result code to a string
+ * @rcode: the result
+ */
+const char *rcode_string(unsigned int rcode)
+{
+       static const char *const names[] = {
+               [RCODE_COMPLETE]        = "complete",
+               [RCODE_CONFLICT_ERROR]  = "conflict error",
+               [RCODE_DATA_ERROR]      = "data error",
+               [RCODE_TYPE_ERROR]      = "type error",
+               [RCODE_ADDRESS_ERROR]   = "address error",
+               [RCODE_SEND_ERROR]      = "send error",
+               [RCODE_CANCELLED]       = "cancelled",
+               [RCODE_BUSY]            = "busy",
+               [RCODE_GENERATION]      = "generation",
+               [RCODE_NO_ACK]          = "no ack",
+       };
+
+       if (rcode < ARRAY_SIZE(names) && names[rcode])
+               return names[rcode];
+       else
+               return "unknown";
+}
+EXPORT_SYMBOL(rcode_string);
+
+/**
+ * snd_fw_transaction - send a request and wait for its completion
+ * @unit: the driver's unit on the target device
+ * @tcode: the transaction code
+ * @offset: the address in the target's address space
+ * @buffer: input/output data
+ * @length: length of @buffer
+ *
+ * Submits an asynchronous request to the target device, and waits for the
+ * response.  The node ID and the current generation are derived from @unit.
+ * On a bus reset or an error, the transaction is retried a few times.
+ * Returns zero on success, or a negative error code.
+ */
+int snd_fw_transaction(struct fw_unit *unit, int tcode,
+                      u64 offset, void *buffer, size_t length)
+{
+       struct fw_device *device = fw_parent_device(unit);
+       int generation, rcode, tries = 0;
+
+       for (;;) {
+               generation = device->generation;
+               smp_rmb(); /* node_id vs. generation */
+               rcode = fw_run_transaction(device->card, tcode,
+                                          device->node_id, generation,
+                                          device->max_speed, offset,
+                                          buffer, length);
+
+               if (rcode == RCODE_COMPLETE)
+                       return 0;
+
+               if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
+                       dev_err(&unit->device, "transaction failed: %s\n",
+                               rcode_string(rcode));
+                       return -EIO;
+               }
+
+               msleep(ERROR_RETRY_DELAY_MS);
+       }
+}
+EXPORT_SYMBOL(snd_fw_transaction);
+
+MODULE_DESCRIPTION("FireWire audio helper functions");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
new file mode 100644 (file)
index 0000000..064f3fd
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef SOUND_FIREWIRE_LIB_H_INCLUDED
+#define SOUND_FIREWIRE_LIB_H_INCLUDED
+
+#include <linux/firewire-constants.h>
+#include <linux/types.h>
+
+struct fw_unit;
+
+int snd_fw_transaction(struct fw_unit *unit, int tcode,
+                      u64 offset, void *buffer, size_t length);
+const char *rcode_string(unsigned int rcode);
+
+/* returns true if retrying the transaction would not make sense */
+static inline bool rcode_is_permanent_error(int rcode)
+{
+       return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
+}
+
+#endif
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c
new file mode 100644 (file)
index 0000000..1e20e60
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * helpers for managing a buffer for many packets
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/firewire.h>
+#include <linux/slab.h>
+#include "packets-buffer.h"
+
+/**
+ * iso_packets_buffer_init - allocates the memory for packets
+ * @b: the buffer structure to initialize
+ * @unit: the device at the other end of the stream
+ * @count: the number of packets
+ * @packet_size: the (maximum) size of a packet, in bytes
+ * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE
+ */
+int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
+                           unsigned int count, unsigned int packet_size,
+                           enum dma_data_direction direction)
+{
+       unsigned int packets_per_page, pages;
+       unsigned int i, page_index, offset_in_page;
+       void *p;
+       int err;
+
+       b->packets = kmalloc(count * sizeof(*b->packets), GFP_KERNEL);
+       if (!b->packets) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       packet_size = L1_CACHE_ALIGN(packet_size);
+       packets_per_page = PAGE_SIZE / packet_size;
+       if (WARN_ON(!packets_per_page)) {
+               err = -EINVAL;
+               goto error;
+       }
+       pages = DIV_ROUND_UP(count, packets_per_page);
+
+       err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card,
+                                pages, direction);
+       if (err < 0)
+               goto err_packets;
+
+       for (i = 0; i < count; ++i) {
+               page_index = i / packets_per_page;
+               p = page_address(b->iso_buffer.pages[page_index]);
+               offset_in_page = (i % packets_per_page) * packet_size;
+               b->packets[i].buffer = p + offset_in_page;
+               b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page;
+       }
+
+       return 0;
+
+err_packets:
+       kfree(b->packets);
+error:
+       return err;
+}
+
+/**
+ * iso_packets_buffer_destroy - frees packet buffer resources
+ * @b: the buffer structure to free
+ * @unit: the device at the other end of the stream
+ */
+void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
+                               struct fw_unit *unit)
+{
+       fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
+       kfree(b->packets);
+}
diff --git a/sound/firewire/packets-buffer.h b/sound/firewire/packets-buffer.h
new file mode 100644 (file)
index 0000000..6513c5c
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED
+#define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED
+
+#include <linux/dma-mapping.h>
+#include <linux/firewire.h>
+
+/**
+ * struct iso_packets_buffer - manages a buffer for many packets
+ * @iso_buffer: the memory containing the packets
+ * @packets: an array, with each element pointing to one packet
+ */
+struct iso_packets_buffer {
+       struct fw_iso_buffer iso_buffer;
+       struct {
+               void *buffer;
+               unsigned int offset;
+       } *packets;
+};
+
+int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
+                           unsigned int count, unsigned int packet_size,
+                           enum dma_data_direction direction);
+void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
+                               struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
new file mode 100644 (file)
index 0000000..0fce921
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ * OXFW970-based speakers driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#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/slab.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "cmp.h"
+#include "fcp.h"
+#include "amdtp.h"
+#include "lib.h"
+
+#define OXFORD_FIRMWARE_ID_ADDRESS     (CSR_REGISTER_BASE + 0x50000)
+/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
+
+#define OXFORD_HARDWARE_ID_ADDRESS     (CSR_REGISTER_BASE + 0x90020)
+#define OXFORD_HARDWARE_ID_OXFW970     0x39443841
+#define OXFORD_HARDWARE_ID_OXFW971     0x39373100
+
+#define VENDOR_GRIFFIN         0x001292
+#define VENDOR_LACIE           0x00d04b
+
+#define SPECIFIER_1394TA       0x00a02d
+#define VERSION_AVC            0x010001
+
+struct device_info {
+       const char *driver_name;
+       const char *short_name;
+       const char *long_name;
+       int (*pcm_constraints)(struct snd_pcm_runtime *runtime);
+       unsigned int mixer_channels;
+       u8 mute_fb_id;
+       u8 volume_fb_id;
+};
+
+struct fwspk {
+       struct snd_card *card;
+       struct fw_unit *unit;
+       const struct device_info *device_info;
+       struct snd_pcm_substream *pcm;
+       struct mutex mutex;
+       struct cmp_connection connection;
+       struct amdtp_out_stream stream;
+       bool stream_running;
+       bool mute;
+       s16 volume[6];
+       s16 volume_min;
+       s16 volume_max;
+};
+
+MODULE_DESCRIPTION("FireWire speakers driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static int firewave_rate_constraint(struct snd_pcm_hw_params *params,
+                                   struct snd_pcm_hw_rule *rule)
+{
+       static unsigned int stereo_rates[] = { 48000, 96000 };
+       struct snd_interval *channels =
+                       hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *rate =
+                       hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+       /* two channels work only at 48/96 kHz */
+       if (snd_interval_max(channels) < 6)
+               return snd_interval_list(rate, 2, stereo_rates, 0);
+       return 0;
+}
+
+static int firewave_channels_constraint(struct snd_pcm_hw_params *params,
+                                       struct snd_pcm_hw_rule *rule)
+{
+       static const struct snd_interval all_channels = { .min = 6, .max = 6 };
+       struct snd_interval *rate =
+                       hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels =
+                       hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* 32/44.1 kHz work only with all six channels */
+       if (snd_interval_max(rate) < 48000)
+               return snd_interval_refine(channels, &all_channels);
+       return 0;
+}
+
+static int firewave_constraints(struct snd_pcm_runtime *runtime)
+{
+       static unsigned int channels_list[] = { 2, 6 };
+       static struct snd_pcm_hw_constraint_list channels_list_constraint = {
+               .count = 2,
+               .list = channels_list,
+       };
+       int err;
+
+       runtime->hw.rates = SNDRV_PCM_RATE_32000 |
+                           SNDRV_PCM_RATE_44100 |
+                           SNDRV_PCM_RATE_48000 |
+                           SNDRV_PCM_RATE_96000;
+       runtime->hw.channels_max = 6;
+
+       err = snd_pcm_hw_constraint_list(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_CHANNELS,
+                                        &channels_list_constraint);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 firewave_rate_constraint, NULL,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 firewave_channels_constraint, NULL,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime)
+{
+       runtime->hw.rates = SNDRV_PCM_RATE_32000 |
+                           SNDRV_PCM_RATE_44100 |
+                           SNDRV_PCM_RATE_48000 |
+                           SNDRV_PCM_RATE_88200 |
+                           SNDRV_PCM_RATE_96000;
+
+       return 0;
+}
+
+static int fwspk_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 = AMDTP_OUT_PCM_FORMAT_BITS,
+               .channels_min = 2,
+               .channels_max = 2,
+               .buffer_bytes_max = 4 * 1024 * 1024,
+               .period_bytes_min = 1,
+               .period_bytes_max = UINT_MAX,
+               .periods_min = 1,
+               .periods_max = UINT_MAX,
+       };
+       struct fwspk *fwspk = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       runtime->hw = hardware;
+
+       err = fwspk->device_info->pcm_constraints(runtime);
+       if (err < 0)
+               return err;
+       err = snd_pcm_limit_hw_rates(runtime);
+       if (err < 0)
+               return err;
+
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                          5000, 8192000);
+       if (err < 0)
+               return err;
+
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int fwspk_close(struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
+static void fwspk_stop_stream(struct fwspk *fwspk)
+{
+       if (fwspk->stream_running) {
+               amdtp_out_stream_stop(&fwspk->stream);
+               cmp_connection_break(&fwspk->connection);
+               fwspk->stream_running = false;
+       }
+}
+
+static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc)
+{
+       u8 *buf;
+       int err;
+
+       buf = kmalloc(8, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       buf[0] = 0x00;          /* AV/C, CONTROL */
+       buf[1] = 0xff;          /* unit */
+       buf[2] = 0x19;          /* INPUT PLUG SIGNAL FORMAT */
+       buf[3] = 0x00;          /* plug 0 */
+       buf[4] = 0x90;          /* format: audio */
+       buf[5] = 0x00 | sfc;    /* AM824, frequency */
+       buf[6] = 0xff;          /* SYT (not used) */
+       buf[7] = 0xff;
+
+       err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+       if (err < 0)
+               goto error;
+       if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) {
+               dev_err(&fwspk->unit->device, "failed to set sample rate\n");
+               err = -EIO;
+               goto error;
+       }
+
+       err = 0;
+
+error:
+       kfree(buf);
+
+       return err;
+}
+
+static int fwspk_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *hw_params)
+{
+       struct fwspk *fwspk = substream->private_data;
+       int err;
+
+       mutex_lock(&fwspk->mutex);
+       fwspk_stop_stream(fwspk);
+       mutex_unlock(&fwspk->mutex);
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               goto error;
+
+       amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params));
+       amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params));
+
+       amdtp_out_stream_set_pcm_format(&fwspk->stream,
+                                       params_format(hw_params));
+
+       err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
+       if (err < 0)
+               goto err_buffer;
+
+       return 0;
+
+err_buffer:
+       snd_pcm_lib_free_vmalloc_buffer(substream);
+error:
+       return err;
+}
+
+static int fwspk_hw_free(struct snd_pcm_substream *substream)
+{
+       struct fwspk *fwspk = substream->private_data;
+
+       mutex_lock(&fwspk->mutex);
+       fwspk_stop_stream(fwspk);
+       mutex_unlock(&fwspk->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int fwspk_prepare(struct snd_pcm_substream *substream)
+{
+       struct fwspk *fwspk = substream->private_data;
+       int err;
+
+       mutex_lock(&fwspk->mutex);
+
+       if (amdtp_out_streaming_error(&fwspk->stream))
+               fwspk_stop_stream(fwspk);
+
+       if (!fwspk->stream_running) {
+               err = cmp_connection_establish(&fwspk->connection,
+                       amdtp_out_stream_get_max_payload(&fwspk->stream));
+               if (err < 0)
+                       goto err_mutex;
+
+               err = amdtp_out_stream_start(&fwspk->stream,
+                                       fwspk->connection.resources.channel,
+                                       fwspk->connection.speed);
+               if (err < 0)
+                       goto err_connection;
+
+               fwspk->stream_running = true;
+       }
+
+       mutex_unlock(&fwspk->mutex);
+
+       amdtp_out_stream_pcm_prepare(&fwspk->stream);
+
+       return 0;
+
+err_connection:
+       cmp_connection_break(&fwspk->connection);
+err_mutex:
+       mutex_unlock(&fwspk->mutex);
+
+       return err;
+}
+
+static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct fwspk *fwspk = substream->private_data;
+       struct snd_pcm_substream *pcm;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               pcm = substream;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               pcm = NULL;
+               break;
+       default:
+               return -EINVAL;
+       }
+       amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm);
+       return 0;
+}
+
+static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
+{
+       struct fwspk *fwspk = substream->private_data;
+
+       return amdtp_out_stream_pcm_pointer(&fwspk->stream);
+}
+
+static int fwspk_create_pcm(struct fwspk *fwspk)
+{
+       static struct snd_pcm_ops ops = {
+               .open      = fwspk_open,
+               .close     = fwspk_close,
+               .ioctl     = snd_pcm_lib_ioctl,
+               .hw_params = fwspk_hw_params,
+               .hw_free   = fwspk_hw_free,
+               .prepare   = fwspk_prepare,
+               .trigger   = fwspk_trigger,
+               .pointer   = fwspk_pointer,
+               .page      = snd_pcm_lib_get_vmalloc_page,
+               .mmap      = snd_pcm_lib_mmap_vmalloc,
+       };
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm);
+       if (err < 0)
+               return err;
+       pcm->private_data = fwspk;
+       strcpy(pcm->name, fwspk->device_info->short_name);
+       fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       fwspk->pcm->ops = &ops;
+       return 0;
+}
+
+enum control_action { CTL_READ, CTL_WRITE };
+enum control_attribute {
+       CTL_MIN         = 0x02,
+       CTL_MAX         = 0x03,
+       CTL_CURRENT     = 0x10,
+};
+
+static int fwspk_mute_command(struct fwspk *fwspk, bool *value,
+                             enum control_action action)
+{
+       u8 *buf;
+       u8 response_ok;
+       int err;
+
+       buf = kmalloc(11, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (action == CTL_READ) {
+               buf[0] = 0x01;          /* AV/C, STATUS */
+               response_ok = 0x0c;     /*       STABLE */
+       } else {
+               buf[0] = 0x00;          /* AV/C, CONTROL */
+               response_ok = 0x09;     /*       ACCEPTED */
+       }
+       buf[1] = 0x08;                  /* audio unit 0 */
+       buf[2] = 0xb8;                  /* FUNCTION BLOCK */
+       buf[3] = 0x81;                  /* function block type: feature */
+       buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */
+       buf[5] = 0x10;                  /* control attribute: current */
+       buf[6] = 0x02;                  /* selector length */
+       buf[7] = 0x00;                  /* audio channel number */
+       buf[8] = 0x01;                  /* control selector: mute */
+       buf[9] = 0x01;                  /* control data length */
+       if (action == CTL_READ)
+               buf[10] = 0xff;
+       else
+               buf[10] = *value ? 0x70 : 0x60;
+
+       err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe);
+       if (err < 0)
+               goto error;
+       if (err < 11) {
+               dev_err(&fwspk->unit->device, "short FCP response\n");
+               err = -EIO;
+               goto error;
+       }
+       if (buf[0] != response_ok) {
+               dev_err(&fwspk->unit->device, "mute command failed\n");
+               err = -EIO;
+               goto error;
+       }
+       if (action == CTL_READ)
+               *value = buf[10] == 0x70;
+
+       err = 0;
+
+error:
+       kfree(buf);
+
+       return err;
+}
+
+static int fwspk_volume_command(struct fwspk *fwspk, s16 *value,
+                               unsigned int channel,
+                               enum control_attribute attribute,
+                               enum control_action action)
+{
+       u8 *buf;
+       u8 response_ok;
+       int err;
+
+       buf = kmalloc(12, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (action == CTL_READ) {
+               buf[0] = 0x01;          /* AV/C, STATUS */
+               response_ok = 0x0c;     /*       STABLE */
+       } else {
+               buf[0] = 0x00;          /* AV/C, CONTROL */
+               response_ok = 0x09;     /*       ACCEPTED */
+       }
+       buf[1] = 0x08;                  /* audio unit 0 */
+       buf[2] = 0xb8;                  /* FUNCTION BLOCK */
+       buf[3] = 0x81;                  /* function block type: feature */
+       buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */
+       buf[5] = attribute;             /* control attribute */
+       buf[6] = 0x02;                  /* selector length */
+       buf[7] = channel;               /* audio channel number */
+       buf[8] = 0x02;                  /* control selector: volume */
+       buf[9] = 0x02;                  /* control data length */
+       if (action == CTL_READ) {
+               buf[10] = 0xff;
+               buf[11] = 0xff;
+       } else {
+               buf[10] = *value >> 8;
+               buf[11] = *value;
+       }
+
+       err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe);
+       if (err < 0)
+               goto error;
+       if (err < 12) {
+               dev_err(&fwspk->unit->device, "short FCP response\n");
+               err = -EIO;
+               goto error;
+       }
+       if (buf[0] != response_ok) {
+               dev_err(&fwspk->unit->device, "volume command failed\n");
+               err = -EIO;
+               goto error;
+       }
+       if (action == CTL_READ)
+               *value = (buf[10] << 8) | buf[11];
+
+       err = 0;
+
+error:
+       kfree(buf);
+
+       return err;
+}
+
+static int fwspk_mute_get(struct snd_kcontrol *control,
+                         struct snd_ctl_elem_value *value)
+{
+       struct fwspk *fwspk = control->private_data;
+
+       value->value.integer.value[0] = !fwspk->mute;
+
+       return 0;
+}
+
+static int fwspk_mute_put(struct snd_kcontrol *control,
+                         struct snd_ctl_elem_value *value)
+{
+       struct fwspk *fwspk = control->private_data;
+       bool mute;
+       int err;
+
+       mute = !value->value.integer.value[0];
+
+       if (mute == fwspk->mute)
+               return 0;
+
+       err = fwspk_mute_command(fwspk, &mute, CTL_WRITE);
+       if (err < 0)
+               return err;
+       fwspk->mute = mute;
+
+       return 1;
+}
+
+static int fwspk_volume_info(struct snd_kcontrol *control,
+                            struct snd_ctl_elem_info *info)
+{
+       struct fwspk *fwspk = control->private_data;
+
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = fwspk->device_info->mixer_channels;
+       info->value.integer.min = fwspk->volume_min;
+       info->value.integer.max = fwspk->volume_max;
+
+       return 0;
+}
+
+static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
+
+static int fwspk_volume_get(struct snd_kcontrol *control,
+                           struct snd_ctl_elem_value *value)
+{
+       struct fwspk *fwspk = control->private_data;
+       unsigned int i;
+
+       for (i = 0; i < fwspk->device_info->mixer_channels; ++i)
+               value->value.integer.value[channel_map[i]] = fwspk->volume[i];
+
+       return 0;
+}
+
+static int fwspk_volume_put(struct snd_kcontrol *control,
+                         struct snd_ctl_elem_value *value)
+{
+       struct fwspk *fwspk = control->private_data;
+       unsigned int i, changed_channels;
+       bool equal_values = true;
+       s16 volume;
+       int err;
+
+       for (i = 0; i < fwspk->device_info->mixer_channels; ++i) {
+               if (value->value.integer.value[i] < fwspk->volume_min ||
+                   value->value.integer.value[i] > fwspk->volume_max)
+                       return -EINVAL;
+               if (value->value.integer.value[i] !=
+                   value->value.integer.value[0])
+                       equal_values = false;
+       }
+
+       changed_channels = 0;
+       for (i = 0; i < fwspk->device_info->mixer_channels; ++i)
+               if (value->value.integer.value[channel_map[i]] !=
+                                                       fwspk->volume[i])
+                       changed_channels |= 1 << (i + 1);
+
+       if (equal_values && changed_channels != 0)
+               changed_channels = 1 << 0;
+
+       for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) {
+               volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
+               if (changed_channels & (1 << i)) {
+                       err = fwspk_volume_command(fwspk, &volume, i,
+                                                  CTL_CURRENT, CTL_WRITE);
+                       if (err < 0)
+                               return err;
+               }
+               if (i > 0)
+                       fwspk->volume[i - 1] = volume;
+       }
+
+       return changed_channels != 0;
+}
+
+static int fwspk_create_mixer(struct fwspk *fwspk)
+{
+       static const struct snd_kcontrol_new controls[] = {
+               {
+                       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+                       .name = "PCM Playback Switch",
+                       .info = snd_ctl_boolean_mono_info,
+                       .get = fwspk_mute_get,
+                       .put = fwspk_mute_put,
+               },
+               {
+                       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+                       .name = "PCM Playback Volume",
+                       .info = fwspk_volume_info,
+                       .get = fwspk_volume_get,
+                       .put = fwspk_volume_put,
+               },
+       };
+       unsigned int i, first_ch;
+       int err;
+
+       err = fwspk_volume_command(fwspk, &fwspk->volume_min,
+                                  0, CTL_MIN, CTL_READ);
+       if (err < 0)
+               return err;
+       err = fwspk_volume_command(fwspk, &fwspk->volume_max,
+                                  0, CTL_MAX, CTL_READ);
+       if (err < 0)
+               return err;
+
+       err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ);
+       if (err < 0)
+               return err;
+
+       first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1;
+       for (i = 0; i < fwspk->device_info->mixer_channels; ++i) {
+               err = fwspk_volume_command(fwspk, &fwspk->volume[i],
+                                          first_ch + i, CTL_CURRENT, CTL_READ);
+               if (err < 0)
+                       return err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(controls); ++i) {
+               err = snd_ctl_add(fwspk->card,
+                                 snd_ctl_new1(&controls[i], fwspk));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static u32 fwspk_read_firmware_version(struct fw_unit *unit)
+{
+       __be32 data;
+       int err;
+
+       err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+                                OXFORD_FIRMWARE_ID_ADDRESS, &data, 4);
+       return err >= 0 ? be32_to_cpu(data) : 0;
+}
+
+static void fwspk_card_free(struct snd_card *card)
+{
+       struct fwspk *fwspk = card->private_data;
+       struct fw_device *dev = fw_parent_device(fwspk->unit);
+
+       amdtp_out_stream_destroy(&fwspk->stream);
+       cmp_connection_destroy(&fwspk->connection);
+       fw_unit_put(fwspk->unit);
+       fw_device_put(dev);
+       mutex_destroy(&fwspk->mutex);
+}
+
+static const struct device_info *__devinit fwspk_detect(struct fw_device *dev)
+{
+       static const struct device_info griffin_firewave = {
+               .driver_name = "FireWave",
+               .short_name  = "FireWave",
+               .long_name   = "Griffin FireWave Surround",
+               .pcm_constraints = firewave_constraints,
+               .mixer_channels = 6,
+               .mute_fb_id   = 0x01,
+               .volume_fb_id = 0x02,
+       };
+       static const struct device_info lacie_speakers = {
+               .driver_name = "FWSpeakers",
+               .short_name  = "FireWire Speakers",
+               .long_name   = "LaCie FireWire Speakers",
+               .pcm_constraints = lacie_speakers_constraints,
+               .mixer_channels = 1,
+               .mute_fb_id   = 0x01,
+               .volume_fb_id = 0x01,
+       };
+       struct fw_csr_iterator i;
+       int key, value;
+
+       fw_csr_iterator_init(&i, dev->config_rom);
+       while (fw_csr_iterator_next(&i, &key, &value))
+               if (key == CSR_VENDOR)
+                       switch (value) {
+                       case VENDOR_GRIFFIN:
+                               return &griffin_firewave;
+                       case VENDOR_LACIE:
+                               return &lacie_speakers;
+                       }
+
+       return NULL;
+}
+
+static int __devinit fwspk_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 fwspk *fwspk;
+       u32 firmware;
+       int err;
+
+       err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*fwspk), &card);
+       if (err < 0)
+               return err;
+       snd_card_set_dev(card, unit_dev);
+
+       fwspk = card->private_data;
+       fwspk->card = card;
+       mutex_init(&fwspk->mutex);
+       fw_device_get(fw_dev);
+       fwspk->unit = fw_unit_get(unit);
+       fwspk->device_info = fwspk_detect(fw_dev);
+       if (!fwspk->device_info) {
+               err = -ENODEV;
+               goto err_unit;
+       }
+
+       err = cmp_connection_init(&fwspk->connection, unit, 0);
+       if (err < 0)
+               goto err_unit;
+
+       err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING);
+       if (err < 0)
+               goto err_connection;
+
+       card->private_free = fwspk_card_free;
+
+       strcpy(card->driver, fwspk->device_info->driver_name);
+       strcpy(card->shortname, fwspk->device_info->short_name);
+       firmware = fwspk_read_firmware_version(unit);
+       snprintf(card->longname, sizeof(card->longname),
+                "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
+                fwspk->device_info->long_name,
+                firmware >> 20, firmware & 0xffff,
+                fw_dev->config_rom[3], fw_dev->config_rom[4],
+                dev_name(&unit->device), 100 << fw_dev->max_speed);
+       strcpy(card->mixername, "OXFW970");
+
+       err = fwspk_create_pcm(fwspk);
+       if (err < 0)
+               goto error;
+
+       err = fwspk_create_mixer(fwspk);
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(unit_dev, fwspk);
+
+       return 0;
+
+err_connection:
+       cmp_connection_destroy(&fwspk->connection);
+err_unit:
+       fw_unit_put(fwspk->unit);
+       fw_device_put(fw_dev);
+       mutex_destroy(&fwspk->mutex);
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static int __devexit fwspk_remove(struct device *dev)
+{
+       struct fwspk *fwspk = dev_get_drvdata(dev);
+
+       snd_card_disconnect(fwspk->card);
+
+       mutex_lock(&fwspk->mutex);
+       amdtp_out_stream_pcm_abort(&fwspk->stream);
+       fwspk_stop_stream(fwspk);
+       mutex_unlock(&fwspk->mutex);
+
+       snd_card_free_when_closed(fwspk->card);
+
+       return 0;
+}
+
+static void fwspk_bus_reset(struct fw_unit *unit)
+{
+       struct fwspk *fwspk = dev_get_drvdata(&unit->device);
+
+       fcp_bus_reset(fwspk->unit);
+
+       if (cmp_connection_update(&fwspk->connection) < 0) {
+               mutex_lock(&fwspk->mutex);
+               amdtp_out_stream_pcm_abort(&fwspk->stream);
+               fwspk_stop_stream(fwspk);
+               mutex_unlock(&fwspk->mutex);
+               return;
+       }
+
+       amdtp_out_stream_update(&fwspk->stream);
+}
+
+static const struct ieee1394_device_id fwspk_id_table[] = {
+       {
+               .match_flags  = IEEE1394_MATCH_VENDOR_ID |
+                               IEEE1394_MATCH_MODEL_ID |
+                               IEEE1394_MATCH_SPECIFIER_ID |
+                               IEEE1394_MATCH_VERSION,
+               .vendor_id    = VENDOR_GRIFFIN,
+               .model_id     = 0x00f970,
+               .specifier_id = SPECIFIER_1394TA,
+               .version      = VERSION_AVC,
+       },
+       {
+               .match_flags  = IEEE1394_MATCH_VENDOR_ID |
+                               IEEE1394_MATCH_MODEL_ID |
+                               IEEE1394_MATCH_SPECIFIER_ID |
+                               IEEE1394_MATCH_VERSION,
+               .vendor_id    = VENDOR_LACIE,
+               .model_id     = 0x00f970,
+               .specifier_id = SPECIFIER_1394TA,
+               .version      = VERSION_AVC,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table);
+
+static struct fw_driver fwspk_driver = {
+       .driver   = {
+               .owner  = THIS_MODULE,
+               .name   = KBUILD_MODNAME,
+               .bus    = &fw_bus_type,
+               .probe  = fwspk_probe,
+               .remove = __devexit_p(fwspk_remove),
+       },
+       .update   = fwspk_bus_reset,
+       .id_table = fwspk_id_table,
+};
+
+static int __init alsa_fwspk_init(void)
+{
+       return driver_register(&fwspk_driver.driver);
+}
+
+static void __exit alsa_fwspk_exit(void)
+{
+       driver_unregister(&fwspk_driver.driver);
+}
+
+module_init(alsa_fwspk_init);
+module_exit(alsa_fwspk_exit);
index fcb14a09982262d473842aeb55f365b031ebeb70..7c7793a0eb25ad29c0c7f724ada99b48cdfa98c9 100644 (file)
@@ -526,31 +526,21 @@ bad:
 }
 
 
-/* These device names follow the official Linux device list,
- * Documentation/devices.txt.  Let us know if there are other
- * common names we should support for compatibility.
- * Only those devices not created by the generic code in sound_core.c are
- * registered here.
- */
-static const struct {
-       unsigned short minor;
-       char *name;
-       umode_t mode;
-       int *num;
-} dev_list[] = { /* list of minor devices */
-/* seems to be some confusion here -- this device is not in the device list */
-       {SND_DEV_DSP16,     "dspW",      S_IWUGO | S_IRUSR | S_IRGRP,
-        &num_audiodevs},
-       {SND_DEV_AUDIO,     "audio",     S_IWUGO | S_IRUSR | S_IRGRP,
-        &num_audiodevs},
-};
-
 static int dmabuf;
 static int dmabug;
 
 module_param(dmabuf, int, 0444);
 module_param(dmabug, int, 0444);
 
+/* additional minors for compatibility */
+struct oss_minor_dev {
+       unsigned short minor;
+       unsigned int enabled;
+} dev_list[] = {
+       { SND_DEV_DSP16 },
+       { SND_DEV_AUDIO },
+};
+
 static int __init oss_init(void)
 {
        int             err;
@@ -571,18 +561,12 @@ static int __init oss_init(void)
        sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
 
        for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
-               device_create(sound_class, NULL,
-                             MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL,
-                             "%s", dev_list[i].name);
-
-               if (!dev_list[i].num)
-                       continue;
-
-               for (j = 1; j < *dev_list[i].num; j++)
-                       device_create(sound_class, NULL,
-                                     MKDEV(SOUND_MAJOR,
-                                           dev_list[i].minor + (j*0x10)),
-                                     NULL, "%s%d", dev_list[i].name, j);
+               j = 0;
+               do {
+                       unsigned short minor = dev_list[i].minor + j * 0x10;
+                       if (!register_sound_special(&oss_sound_fops, minor))
+                               dev_list[i].enabled = (1 << j);
+               } while (++j < num_audiodevs);
        }
 
        if (sound_nblocks >= MAX_MEM_BLOCKS - 1)
@@ -596,11 +580,11 @@ static void __exit oss_cleanup(void)
        int i, j;
 
        for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
-               device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor));
-               if (!dev_list[i].num)
-                       continue;
-               for (j = 1; j < *dev_list[i].num; j++)
-                       device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)));
+               j = 0;
+               do {
+                       if (dev_list[i].enabled & (1 << j))
+                               unregister_sound_special(dev_list[i].minor);
+               } while (++j < num_audiodevs);
        }
        
        unregister_sound_special(1);
index 9823d59d7ad747a4e3a649a8bbabe30c272a75d7..389cd7931668a233199555dfb19fb0dac7521a28 100644 (file)
@@ -152,10 +152,16 @@ config SND_AZT3328
        select SND_MPU401_UART
        select SND_PCM
        select SND_RAWMIDI
+       select SND_AC97_CODEC
        help
          Say Y here to include support for Aztech AZF3328 (PCI168)
          soundcards.
 
+         Supported features: AC97-"conformant" mixer, MPU401/OPL3, analog I/O
+         (16bit/8bit, many sample rates [<= 66.2kHz], NO hardware mixing),
+         Digital Enhanced Game Port, 1.024MHz multimedia sequencer timer,
+         ext. codec (I2S port), onboard amp (4W/4Ohms/ch), suspend/resume.
+
          To compile this driver as a module, choose M here: the module
          will be called snd-azt3328.
 
@@ -572,13 +578,13 @@ comment "Don't forget to add built-in firmwares for HDSP driver"
        depends on SND_HDSP=y
 
 config SND_HDSPM
-       tristate "RME Hammerfall DSP MADI"
+       tristate "RME Hammerfall DSP MADI/RayDAT/AIO"
        select SND_HWDEP
        select SND_RAWMIDI
        select SND_PCM
        help
-         Say Y here to include support for RME Hammerfall DSP MADI
-         soundcards.
+         Say Y here to include support for RME Hammerfall DSP MADI,
+         RayDAT and AIO soundcards.
 
          To compile this driver as a module, choose M here: the module
          will be called snd-hdspm.
index cb62d178b3e02d3ce3648ec5846068fa9c4ddfd6..7f4d619f4ddb06f8be256f256879ebe590ba1088 100644 (file)
@@ -71,6 +71,12 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
 { 0x414b4d00, 0xffffff00, "Asahi Kasei",       NULL,   NULL },
 { 0x414c4300, 0xffffff00, "Realtek",           NULL,   NULL },
 { 0x414c4700, 0xffffff00, "Realtek",           NULL,   NULL },
+/*
+ * This is an _inofficial_ Aztech Labs entry
+ * (value might differ from unknown official Aztech ID),
+ * currently used by the AC97 emulation of the almost-AC97 PCI168 card.
+ */
+{ 0x415a5400, 0xffffff00, "Aztech Labs (emulated)",    NULL,   NULL },
 { 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL },
 { 0x43525900, 0xffffff00, "Cirrus Logic",      NULL,   NULL },
 { 0x43585400, 0xffffff00, "Conexant",           NULL,  NULL },
@@ -127,6 +133,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x414c4781, 0xffffffff, "ALC658D",           NULL,   NULL }, /* already patched */
 { 0x414c4780, 0xfffffff0, "ALC658",            patch_alc655,   NULL },
 { 0x414c4790, 0xfffffff0, "ALC850",            patch_alc850,   NULL },
+{ 0x415a5401, 0xffffffff, "AZF3328",           patch_aztech_azf3328,   NULL },
 { 0x434d4941, 0xffffffff, "CMI9738",           patch_cm9738,   NULL },
 { 0x434d4961, 0xffffffff, "CMI9739",           patch_cm9739,   NULL },
 { 0x434d4969, 0xffffffff, "CMI9780",           patch_cm9780,   NULL },
@@ -590,9 +597,9 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
        snd_ac97_page_restore(ac97, page_save);
 #ifdef CONFIG_SND_AC97_POWER_SAVE
        /* check analog mixer power-down */
-       if ((val_mask & 0x8000) &&
+       if ((val_mask & AC97_PD_EAPD) &&
            (kcontrol->private_value & (1<<30))) {
-               if (val & 0x8000)
+               if (val & AC97_PD_EAPD)
                        ac97->power_up &= ~(1 << (reg>>1));
                else
                        ac97->power_up |= 1 << (reg>>1);
@@ -1035,20 +1042,20 @@ static int snd_ac97_dev_free(struct snd_device *device)
 
 static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg)
 {
-       unsigned short val, mask = 0x8000;
+       unsigned short val, mask = AC97_MUTE_MASK_MONO;
 
        if (! snd_ac97_valid_reg(ac97, reg))
                return 0;
 
        switch (reg) {
        case AC97_MASTER_TONE:
-               return ac97->caps & 0x04 ? 1 : 0;
+               return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0;
        case AC97_HEADPHONE:
-               return ac97->caps & 0x10 ? 1 : 0;
+               return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0;
        case AC97_REC_GAIN_MIC:
-               return ac97->caps & 0x01 ? 1 : 0;
+               return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0;
        case AC97_3D_CONTROL:
-               if (ac97->caps & 0x7c00) {
+               if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) {
                        val = snd_ac97_read(ac97, reg);
                        /* if nonzero - fixed and we can't set it */
                        return val == 0;
@@ -1104,7 +1111,10 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha
        *lo_max = *hi_max = 0;
        for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
                unsigned short val;
-               snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
+               snd_ac97_write(
+                       ac97, reg,
+                       AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8)
+               );
                /* Do the read twice due to buffers on some ac97 codecs.
                 * e.g. The STAC9704 returns exactly what you wrote to the register
                 * if you read it immediately. This causes the detect routine to fail.
@@ -1139,14 +1149,14 @@ static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int
        unsigned short val, val1;
 
        *max = 63;
-       val = 0x8080 | (0x20 << shift);
+       val = AC97_MUTE_MASK_STEREO | (0x20 << shift);
        snd_ac97_write(ac97, reg, val);
        val1 = snd_ac97_read(ac97, reg);
        if (val != val1) {
                *max = 31;
        }
        /* reset volume to zero */
-       snd_ac97_write_cache(ac97, reg, 0x8080);
+       snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO);
 }
 
 static inline int printable(unsigned int x)
@@ -1183,16 +1193,16 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
        if (! snd_ac97_valid_reg(ac97, reg))
                return 0;
 
-       mute_mask = 0x8000;
+       mute_mask = AC97_MUTE_MASK_MONO;
        val = snd_ac97_read(ac97, reg);
        if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) {
                /* check whether both mute bits work */
-               val1 = val | 0x8080;
+               val1 = val | AC97_MUTE_MASK_STEREO;
                snd_ac97_write(ac97, reg, val1);
                if (val1 == snd_ac97_read(ac97, reg))
-                       mute_mask = 0x8080;
+                       mute_mask = AC97_MUTE_MASK_STEREO;
        }
-       if (mute_mask == 0x8080) {
+       if (mute_mask == AC97_MUTE_MASK_STEREO) {
                struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
                if (check_amix)
                        tmp.private_value |= (1 << 30);
@@ -1268,9 +1278,11 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
        err = snd_ctl_add(card, kctl);
        if (err < 0)
                return err;
-       snd_ac97_write_cache(ac97, reg,
-                            (snd_ac97_read(ac97, reg) & 0x8080) |
-                            lo_max | (hi_max << 8));
+       snd_ac97_write_cache(
+               ac97, reg,
+               (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO)
+               | lo_max | (hi_max << 8)
+       );
        return 0;
 }
 
@@ -1332,7 +1344,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                        return err;
        }
 
-       ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
+       ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO;
 
        /* build center controls */
        if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) 
@@ -1410,8 +1422,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                        if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
                                return err;
                set_tlv_db_scale(kctl, db_scale_4bit);
-               snd_ac97_write_cache(ac97, AC97_PC_BEEP,
-                                    snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
+               snd_ac97_write_cache(
+                       ac97,
+                       AC97_PC_BEEP,
+                       (snd_ac97_read(ac97, AC97_PC_BEEP)
+                               | AC97_MUTE_MASK_MONO | 0x001e)
+               );
        }
        
        /* build Phone controls */
@@ -1545,7 +1561,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        }
 
        /* build Simulated Stereo Enhancement control */
-       if (ac97->caps & 0x0008) {
+       if (ac97->caps & AC97_BC_SIM_STEREO) {
                if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
                        return err;
        }
@@ -1557,7 +1573,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        }
 
        /* build Loudness control */
-       if (ac97->caps & 0x0020) {
+       if (ac97->caps & AC97_BC_LOUDNESS) {
                if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
                        return err;
        }
@@ -2542,8 +2558,8 @@ void snd_ac97_resume(struct snd_ac97 *ac97)
                        schedule_timeout_uninterruptible(1);
                } while (time_after_eq(end_time, jiffies));
                /* FIXME: extra delay */
-               ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000);
-               if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000)
+               ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO);
+               if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO)
                        msleep(250);
        } else {
                end_time = jiffies + msecs_to_jiffies(100);
@@ -2747,12 +2763,12 @@ static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
                int rshift = (kcontrol->private_value >> 12) & 0x0f;
                unsigned short mask;
                if (shift != rshift)
-                       mask = 0x8080;
+                       mask = AC97_MUTE_MASK_STEREO;
                else
-                       mask = 0x8000;
-               snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+                       mask = AC97_MUTE_MASK_MONO;
+               snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
                                     (ac97->regs[AC97_MASTER] & mask) == mask ?
-                                    0x8000 : 0);
+                                    AC97_PD_EAPD : 0);
        }
        return err;
 }
@@ -2765,7 +2781,10 @@ static int tune_mute_led(struct snd_ac97 *ac97)
                return -ENOENT;
        msw->put = master_mute_sw_put;
        snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
-       snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+       snd_ac97_update_bits(
+               ac97, AC97_POWERDOWN,
+               AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
+       );
        ac97->scaps |= AC97_SCAP_EAPD_LED;
        return 0;
 }
@@ -2780,12 +2799,12 @@ static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol,
                int rshift = (kcontrol->private_value >> 12) & 0x0f;
                unsigned short mask;
                if (shift != rshift)
-                       mask = 0x8080;
+                       mask = AC97_MUTE_MASK_STEREO;
                else
-                       mask = 0x8000;
-               snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+                       mask = AC97_MUTE_MASK_MONO;
+               snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
                                     (ac97->regs[AC97_MASTER] & mask) == mask ?
-                                    0x8000 : 0);
+                                    AC97_PD_EAPD : 0);
        }
        return err;
 }
@@ -2801,7 +2820,10 @@ static int tune_hp_mute_led(struct snd_ac97 *ac97)
        snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
        snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
        snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
-       snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+       snd_ac97_update_bits(
+               ac97, AC97_POWERDOWN,
+               AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
+       );
        return 0;
 }
 
index bf47574ca1f0c9240023c5a71579218a851bda73..200c9a1d48b7c7d8dc238edf0113730d4c0a5919 100644 (file)
 #include "ac97_local.h"
 #include "ac97_patch.h"
 
+/*
+ *  Forward declarations
+ */
+
+static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
+                                                   const char *name);
+static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+                               const unsigned int *tlv, const char **slaves);
+
 /*
  *  Chip specific initialization
  */
@@ -2940,6 +2949,49 @@ static int patch_alc850(struct snd_ac97 *ac97)
        return 0;
 }
 
+static int patch_aztech_azf3328_specific(struct snd_ac97 *ac97)
+{
+       struct snd_kcontrol *kctl_3d_center =
+               snd_ac97_find_mixer_ctl(ac97, "3D Control - Center");
+       struct snd_kcontrol *kctl_3d_depth =
+               snd_ac97_find_mixer_ctl(ac97, "3D Control - Depth");
+
+       /*
+        * 3D register is different from AC97 standard layout
+        * (also do some renaming, to resemble Windows driver naming)
+        */
+       if (kctl_3d_center) {
+               kctl_3d_center->private_value =
+                       AC97_SINGLE_VALUE(AC97_3D_CONTROL, 1, 0x07, 0);
+               snd_ac97_rename_vol_ctl(ac97,
+                       "3D Control - Center", "3D Control - Width"
+               );
+       }
+       if (kctl_3d_depth)
+               kctl_3d_depth->private_value =
+                       AC97_SINGLE_VALUE(AC97_3D_CONTROL, 8, 0x03, 0);
+
+       /* Aztech Windows driver calls the
+          equivalent control "Modem Playback", thus rename it: */
+       snd_ac97_rename_vol_ctl(ac97,
+               "Master Mono Playback", "Modem Playback"
+       );
+       snd_ac97_rename_vol_ctl(ac97,
+               "Headphone Playback", "FM Synth Playback"
+       );
+
+       return 0;
+}
+
+static const struct snd_ac97_build_ops patch_aztech_azf3328_ops = {
+       .build_specific = patch_aztech_azf3328_specific
+};
+
+static int patch_aztech_azf3328(struct snd_ac97 *ac97)
+{
+       ac97->build_ops = &patch_aztech_azf3328_ops;
+       return 0;
+}
 
 /*
  * C-Media CM97xx codecs
index c80b0b863c5493dfa54561b201a5259a660fd2ee..0ac1f98d91a1f2196fb9cd4aa93b4c79c2d57aad 100644 (file)
@@ -21,6 +21,7 @@
  *  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
 
 #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/version.h>
 #include <linux/init.h>
 #include <linux/jiffies.h>
 #include <linux/slab.h>
@@ -85,11 +82,11 @@ MODULE_PARM_DESC(enable_hpi_hwdep,
 
 /* identify driver */
 #ifdef KERNEL_ALSA_BUILD
-static char *build_info = "built using headers from kernel source";
+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";
+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
@@ -100,13 +97,14 @@ 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 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)
 */
@@ -152,11 +150,12 @@ 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 buffer_bytes;
+       unsigned int period_bytes;
        unsigned int bytes_per_sec;
-       unsigned int pcm_irq_pos;       /* IRQ position */
-       unsigned int pcm_buf_pos;       /* position in buffer */
+       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 */
        struct snd_pcm_substream *substream;
        u32 h_stream;
        struct hpi_format format;
@@ -167,7 +166,6 @@ struct snd_card_asihpi_pcm {
 /* 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
@@ -194,10 +192,7 @@ static u16 hpi_stream_host_buffer_attach(
        return hr.error;
 }
 
-static u16 hpi_stream_host_buffer_detach(
-       struct hpi_hsubsys *hS,
-       u32  h_stream
-)
+static u16 hpi_stream_host_buffer_detach(u32  h_stream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -218,24 +213,23 @@ static u16 hpi_stream_host_buffer_detach(
        return hr.error;
 }
 
-static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream)
+static inline u16 hpi_stream_start(u32 h_stream)
 {
        if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
-               return hpi_outstream_start(hS, h_stream);
+               return hpi_outstream_start(h_stream);
        else
-               return hpi_instream_start(hS, h_stream);
+               return hpi_instream_start(h_stream);
 }
 
-static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream)
+static inline u16 hpi_stream_stop(u32 h_stream)
 {
        if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
-               return hpi_outstream_stop(hS, h_stream);
+               return hpi_outstream_stop(h_stream);
        else
-               return hpi_instream_stop(hS, h_stream);
+               return hpi_instream_stop(h_stream);
 }
 
 static inline u16 hpi_stream_get_info_ex(
-    struct hpi_hsubsys *hS,
     u32 h_stream,
     u16        *pw_state,
     u32        *pbuffer_size,
@@ -244,42 +238,43 @@ static inline u16 hpi_stream_get_info_ex(
     u32        *pauxiliary_data
 )
 {
+       u16 e;
        if (hpi_handle_object(h_stream)  ==  HPI_OBJ_OSTREAM)
-               return hpi_outstream_get_info_ex(hS, h_stream, pw_state,
+               e = hpi_outstream_get_info_ex(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,
+               e = hpi_instream_get_info_ex(h_stream, pw_state,
                                        pbuffer_size, pdata_in_buffer,
                                        psample_count, pauxiliary_data);
+       return e;
 }
 
-static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS,
+static inline u16 hpi_stream_group_add(
                                        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);
+               return hpi_outstream_group_add(h_master, h_stream);
        else
-               return hpi_instream_group_add(hS, h_master, h_stream);
+               return hpi_instream_group_add(h_master, h_stream);
 }
 
-static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS,
-                                               u32 h_stream)
+static inline u16 hpi_stream_group_reset(u32 h_stream)
 {
        if (hpi_handle_object(h_stream) ==  HPI_OBJ_OSTREAM)
-               return hpi_outstream_group_reset(hS, h_stream);
+               return hpi_outstream_group_reset(h_stream);
        else
-               return hpi_instream_group_reset(hS, h_stream);
+               return hpi_instream_group_reset(h_stream);
 }
 
-static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS,
+static inline u16 hpi_stream_group_get_map(
                                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);
+               return hpi_outstream_group_get_map(h_stream, mo, mi);
        else
-               return hpi_instream_group_get_map(hS, h_stream, mo, mi);
+               return hpi_instream_group_get_map(h_stream, mo, mi);
 }
 
 static u16 handle_error(u16 err, int line, char *filename)
@@ -299,11 +294,11 @@ 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("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("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));
@@ -335,7 +330,7 @@ static snd_pcm_format_t hpi_to_alsa_formats[] = {
         */
        -1
 #else
-       /* SNDRV_PCM_FORMAT_S24_3LE */  /* { HPI_FORMAT_PCM24_SIGNED        15 */
+       /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */
 #endif
 };
 
@@ -378,20 +373,20 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
        } else {
                /* on cards without SRC,
                   valid rates are determined by sampleclock */
-               err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+               err = hpi_mixer_get_control(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);
+                               "No local sampleclock, err %d\n", err);
                }
 
                for (idx = 0; idx < 100; idx++) {
-                       if (hpi_sample_clock_query_local_rate(ss,
+                       if (hpi_sample_clock_query_local_rate(
                                h_control, idx, &sample_rate)) {
                                if (!idx)
                                        snd_printk(KERN_ERR
-                                               "local rate query failed\n");
+                                               "Local rate query failed\n");
 
                                break;
                        }
@@ -480,10 +475,10 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
                        format, params_rate(params), 0, 0));
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               if (hpi_instream_reset(ss, dpcm->h_stream) != 0)
+               if (hpi_instream_reset(dpcm->h_stream) != 0)
                        return -EINVAL;
 
-               if (hpi_instream_set_format(ss,
+               if (hpi_instream_set_format(
                        dpcm->h_stream, &dpcm->format) != 0)
                        return -EINVAL;
        }
@@ -491,10 +486,10 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
        dpcm->hpi_buffer_attached = 0;
        if (card->support_mmap) {
 
-               err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream,
+               err = hpi_stream_host_buffer_attach(dpcm->h_stream,
                        params_buffer_bytes(params),  runtime->dma_addr);
                if (err == 0) {
-                       snd_printd(KERN_INFO
+                       VPRINTK1(KERN_INFO
                                "stream_host_buffer_attach succeeded %u %lu\n",
                                params_buffer_bytes(params),
                                (unsigned long)runtime->dma_addr);
@@ -505,11 +500,11 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
                        return -ENOMEM;
                }
 
-               err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL,
+               err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
                                                &dpcm->hpi_buffer_attached,
                                                NULL, NULL, NULL);
 
-               snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n",
+               VPRINTK1(KERN_INFO "stream_host_buffer_attach status 0x%x\n",
                                dpcm->hpi_buffer_attached);
        }
        bytes_per_sec = params_rate(params) * params_channels(params);
@@ -520,16 +515,32 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
                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->buffer_bytes = params_buffer_bytes(params);
+       dpcm->period_bytes = params_period_bytes(params);
+       VPRINTK1(KERN_INFO "buffer_bytes=%d, period_bytes=%d, bps=%d\n",
+                       dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec);
 
-       dpcm->pcm_irq_pos = 0;
-       dpcm->pcm_buf_pos = 0;
        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(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);
+}
+
 static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
                                            substream)
 {
@@ -537,9 +548,9 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
        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);
+       expiry = HZ / 200;
+       /*? (dpcm->period_bytes * HZ / dpcm->bytes_per_sec); */
+       expiry = max(expiry, 1); /* don't let it be zero! */
        dpcm->timer.expires = jiffies + expiry;
        dpcm->respawn_timer = 1;
        add_timer(&dpcm->timer);
@@ -562,37 +573,44 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
        struct snd_pcm_substream *s;
        u16 e;
 
-       snd_printd("trigger %dstream %d\n",
-                       substream->stream, substream->number);
+       VPRINTK1(KERN_INFO "%c%d trigger\n",
+                       SCHR(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;
+                       struct snd_pcm_runtime *runtime = s->runtime;
+                       struct snd_card_asihpi_pcm *ds = runtime->private_data;
 
                        if (snd_pcm_substream_chip(s) != card)
                                continue;
 
+                       /* don't link Cap and Play */
+                       if (substream->stream != s->stream)
+                               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
+                               * in buffer? Must be at least one period!
+                               * 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);
+                               unsigned int preload = ds->period_bytes * 1;
+                               VPRINTK2(KERN_INFO "%d preload x%x\n", s->number, preload);
                                hpi_handle_error(hpi_outstream_write_buf(
-                                               ss, ds->h_stream,
-                                               &s->runtime->dma_area[0],
+                                               ds->h_stream,
+                                               &runtime->dma_area[0],
                                                preload,
                                                &ds->format));
+                               ds->pcm_buf_host_rw_ofs = preload;
                        }
 
                        if (card->support_grouping) {
-                               VPRINTK1("\t_group %dstream %d\n", s->stream,
+                               VPRINTK1(KERN_INFO "\t%c%d group\n",
+                                               SCHR(s->stream),
                                                s->number);
-                               e = hpi_stream_group_add(ss,
+                               e = hpi_stream_group_add(
                                        dpcm->h_stream,
                                        ds->h_stream);
                                if (!e) {
@@ -604,10 +622,12 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
                        } else
                                break;
                }
-               snd_printd("start\n");
+               VPRINTK1(KERN_INFO "start\n");
                /* start the master stream */
                snd_card_asihpi_pcm_timer_start(substream);
-               hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
+               if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
+                       !card->support_mmap)
+                       hpi_handle_error(hpi_stream_start(dpcm->h_stream));
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
@@ -615,88 +635,73 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
                snd_pcm_group_for_each_entry(s, substream) {
                        if (snd_pcm_substream_chip(s) != card)
                                continue;
+                       /* don't link Cap and Play */
+                       if (substream->stream != s->stream)
+                               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,
+                               VPRINTK1(KERN_INFO "\t%c%d group\n",
+                               SCHR(s->stream),
                                        s->number);
                                snd_pcm_trigger_done(s, substream);
                        } else
                                break;
                }
-               snd_printd("stop\n");
+               VPRINTK1(KERN_INFO "stop\n");
 
                /* _prepare and _hwparams reset the stream */
-               hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+               hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        hpi_handle_error(
-                               hpi_outstream_reset(ss, dpcm->h_stream));
+                               hpi_outstream_reset(dpcm->h_stream));
 
                if (card->support_grouping)
-                       hpi_handle_error(hpi_stream_group_reset(ss,
-                                               dpcm->h_stream));
+                       hpi_handle_error(hpi_stream_group_reset(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));
+               VPRINTK1(KERN_INFO "pause release\n");
+               hpi_handle_error(hpi_stream_start(dpcm->h_stream));
                snd_card_asihpi_pcm_timer_start(substream);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               snd_printd("pause\n");
+               VPRINTK1(KERN_INFO "pause\n");
                snd_card_asihpi_pcm_timer_stop(substream);
-               hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+               hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
                break;
        default:
-               snd_printd("\tINVALID\n");
+               snd_printd(KERN_ERR "\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);
+pcm_buf_dma_ofs=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)
+       pcm_buf_dma_ofs=get_buf_pos(s);
+       min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes)
+       new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos)
 }
 timer.expires = jiffies + predict_next_period_ready(min_buf_pos);
 for_each_linked_stream(s) {
-       s->buf_pos = min_buf_pos;
-       if (new_data > pcm_count) {
+       s->pcm_buf_dma_ofs = min_buf_pos;
+       if (new_data > period_bytes) {
                if (mmap) {
-                       irq_pos = (irq_pos + pcm_count) % pcm_size;
+                       irq_pos = (irq_pos + period_bytes) % buffer_bytes;
                        if (playback) {
-                               write(pcm_count);
+                               write(period_bytes);
                        } else {
-                               read(pcm_count);
+                               read(period_bytes);
                        }
                }
                snd_pcm_period_elapsed(s);
@@ -724,105 +729,136 @@ static inline unsigned int modulo_min(unsigned int a, unsigned int b,
 static void snd_card_asihpi_timer_function(unsigned long data)
 {
        struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data;
-       struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream);
+       struct snd_pcm_substream *substream = dpcm->substream;
+       struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_substream *s;
        unsigned int newdata = 0;
-       unsigned int buf_pos, min_buf_pos = 0;
+       unsigned int pcm_buf_dma_ofs, min_buf_pos = 0;
        unsigned int remdata, xfercount, next_jiffies;
        int first = 1;
+       int loops = 0;
        u16 state;
-       u32 buffer_size, data_avail, samples_played, aux;
+       u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
+
+       VPRINTK1(KERN_INFO "%c%d snd_card_asihpi_timer_function\n",
+                               SCHR(substream->stream), substream->number);
 
        /* find minimum newdata and buffer pos in group */
-       snd_pcm_group_for_each_entry(s, dpcm->substream) {
+       snd_pcm_group_for_each_entry(s, substream) {
                struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
                runtime = s->runtime;
 
                if (snd_pcm_substream_chip(s) != card)
                        continue;
 
-               hpi_handle_error(hpi_stream_get_info_ex(ss,
+               /* don't link Cap and Play */
+               if (substream->stream != s->stream)
+                       continue;
+
+               hpi_handle_error(hpi_stream_get_info_ex(
                                        ds->h_stream, &state,
-                                       &buffer_size, &data_avail,
-                                       &samples_played, &aux));
+                                       &buffer_size, &bytes_avail,
+                                       &samples_played, &on_card_bytes));
 
                /* number of bytes in on-card buffer */
-               runtime->delay = aux;
-
-               if (state == HPI_STATE_DRAINED) {
-                       snd_printd(KERN_WARNING  "outstream %d drained\n",
-                                       s->number);
-                       snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
-                       return;
-               }
+               runtime->delay = on_card_bytes;
 
                if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       buf_pos = frames_to_bytes(runtime, samples_played);
-               } else {
-                       buf_pos = data_avail + ds->pcm_irq_pos;
-               }
+                       pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
+                       if (state == HPI_STATE_STOPPED) {
+                               if ((bytes_avail == 0) &&
+                                   (on_card_bytes < ds->pcm_buf_host_rw_ofs)) {
+                                       hpi_handle_error(hpi_stream_start(ds->h_stream));
+                                       VPRINTK1(KERN_INFO "P%d start\n", s->number);
+                               }
+                       } else if (state == HPI_STATE_DRAINED) {
+                               VPRINTK1(KERN_WARNING "P%d drained\n",
+                                               s->number);
+                               /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
+                               continue; */
+                       }
+               } else
+                       pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
 
                if (first) {
                        /* can't statically init min when wrap is involved */
-                       min_buf_pos = buf_pos;
-                       newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size;
+                       min_buf_pos = pcm_buf_dma_ofs;
+                       newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes;
                        first = 0;
                } else {
                        min_buf_pos =
-                               modulo_min(min_buf_pos, buf_pos, UINT_MAX+1L);
+                               modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L);
                        newdata = min(
-                               (buf_pos - ds->pcm_irq_pos) % ds->pcm_size,
+                               (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes,
                                newdata);
                }
 
-               VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX\n",
+               VPRINTK1(KERN_INFO "PB timer hw_ptr x%04lX, appl_ptr x%04lX\n",
                        (unsigned long)frames_to_bytes(runtime,
                                                runtime->status->hw_ptr),
                        (unsigned long)frames_to_bytes(runtime,
                                                runtime->control->appl_ptr));
-               VPRINTK1("%d S=%d, irq=%04X, pos=x%04X, left=x%04X,"
-                       " aux=x%04X space=x%04X\n", s->number,
-                       state,  ds->pcm_irq_pos, buf_pos, (int)data_avail,
-                       (int)aux, buffer_size-data_avail);
+
+               VPRINTK1(KERN_INFO "%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,
+                       (int)on_card_bytes, buffer_size-bytes_avail);
+               loops++;
        }
+       pcm_buf_dma_ofs = min_buf_pos;
 
-       remdata = newdata % dpcm->pcm_count;
-       xfercount = newdata - remdata; /* a multiple of pcm_count */
-       next_jiffies = ((dpcm->pcm_count-remdata) * HZ / dpcm->bytes_per_sec)+1;
-       next_jiffies = max(next_jiffies, 2U * HZ / 1000U);
+       remdata = newdata % dpcm->period_bytes;
+       xfercount = newdata - remdata; /* a multiple of period_bytes */
+       /* come back when on_card_bytes has decreased enough to allow
+          write to happen, or when data has been consumed to make another
+          period
+       */
+       if (xfercount && (on_card_bytes  > dpcm->period_bytes))
+               next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec);
+       else
+               next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec);
+
+       next_jiffies = max(next_jiffies, 1U);
        dpcm->timer.expires = jiffies + next_jiffies;
-       VPRINTK1("jif %d buf pos x%04X newdata x%04X xc x%04X\n",
-                       next_jiffies, min_buf_pos, newdata, xfercount);
+       VPRINTK1(KERN_INFO "jif %d buf pos x%04X newdata x%04X xfer x%04X\n",
+                       next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
 
-       snd_pcm_group_for_each_entry(s, dpcm->substream) {
+       snd_pcm_group_for_each_entry(s, substream) {
                struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
-               ds->pcm_buf_pos = min_buf_pos;
 
-               if (xfercount) {
+               /* don't link Cap and Play */
+               if (substream->stream != s->stream)
+                       continue;
+
+               ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
+
+               if (xfercount && (on_card_bytes <= ds->period_bytes)) {
                        if (card->support_mmap) {
-                               ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount;
                                if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                                       VPRINTK2("write OS%d x%04x\n",
+                                       VPRINTK2(KERN_INFO "P%d write x%04x\n",
                                                        s->number,
-                                                       ds->pcm_count);
+                                                       ds->period_bytes);
                                        hpi_handle_error(
                                                hpi_outstream_write_buf(
-                                                       ss, ds->h_stream,
+                                                       ds->h_stream,
                                                        &s->runtime->
                                                                dma_area[0],
                                                        xfercount,
                                                        &ds->format));
                                } else {
-                                       VPRINTK2("read IS%d x%04x\n",
+                                       VPRINTK2(KERN_INFO "C%d read x%04x\n",
                                                s->number,
-                                               dpcm->pcm_count);
+                                               xfercount);
                                        hpi_handle_error(
                                                hpi_instream_read_buf(
-                                                       ss, ds->h_stream,
+                                                       ds->h_stream,
                                                        NULL, xfercount));
                                }
+                               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_elapsed_dma_ofs = pcm_buf_dma_ofs;
                        snd_pcm_period_elapsed(s);
                }
        }
@@ -845,12 +881,12 @@ 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_printd(KERN_INFO "playback prepare %d\n", substream->number);
-
-       hpi_handle_error(hpi_outstream_reset(ss, dpcm->h_stream));
-       dpcm->pcm_irq_pos = 0;
-       dpcm->pcm_buf_pos = 0;
+       VPRINTK1(KERN_INFO "playback prepare %d\n", substream->number);
 
+       hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
+       dpcm->pcm_buf_host_rw_ofs = 0;
+       dpcm->pcm_buf_dma_ofs = 0;
+       dpcm->pcm_buf_elapsed_dma_ofs = 0;
        return 0;
 }
 
@@ -861,27 +897,8 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
        struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
        snd_pcm_uframes_t ptr;
 
-       u32 samples_played;
-       u16 err;
-
-       if (!snd_pcm_stream_linked(substream)) {
-               /* NOTE, can use samples played for playback position here and
-               * in timer fn because it LAGS the actual read pointer, and is a
-               * better representation of actual playout position
-               */
-               err = hpi_outstream_get_info_ex(ss, dpcm->h_stream, NULL,
-                                       NULL, NULL,
-                                       &samples_played, NULL);
-               hpi_handle_error(err);
-
-               dpcm->pcm_buf_pos = frames_to_bytes(runtime, samples_played);
-       }
-       /* else must return most conservative value found in timer func
-        * by looping over all streams
-        */
-
-       ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos  % dpcm->pcm_size);
-       VPRINTK2("playback_pointer=%04ld\n", (unsigned long)ptr);
+       ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs  % dpcm->buffer_bytes);
+       /* VPRINTK2(KERN_INFO "playback_pointer=x%04lx\n", (unsigned long)ptr); */
        return ptr;
 }
 
@@ -898,12 +915,12 @@ static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi,
        /* on cards without SRC, must query at valid rate,
        * maybe set by external sync
        */
-       err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+       err = hpi_mixer_get_control(asihpi->h_mixer,
                                  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
                                  HPI_CONTROL_SAMPLECLOCK, &h_control);
 
        if (!err)
-               err = hpi_sample_clock_get_sample_rate(ss, h_control,
+               err = hpi_sample_clock_get_sample_rate(h_control,
                                &sample_rate);
 
        for (format = HPI_FORMAT_PCM8_UNSIGNED;
@@ -911,7 +928,7 @@ static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi,
                err = hpi_format_create(&hpi_format,
                                        2, format, sample_rate, 128000, 0);
                if (!err)
-                       err = hpi_outstream_query_format(ss, h_stream,
+                       err = hpi_outstream_query_format(h_stream,
                                                        &hpi_format);
                if (!err && (hpi_to_alsa_formats[format] != -1))
                        pcmhw->formats |=
@@ -942,7 +959,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
                return -ENOMEM;
 
        err =
-           hpi_outstream_open(ss, card->adapter_index,
+           hpi_outstream_open(card->adapter_index,
                              substream->number, &dpcm->h_stream);
        hpi_handle_error(err);
        if (err)
@@ -998,11 +1015,11 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
        snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                card->update_interval_frames);
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-               card->update_interval_frames * 4, UINT_MAX);
+               card->update_interval_frames * 2, UINT_MAX);
 
        snd_pcm_set_sync(substream);
 
-       snd_printd(KERN_INFO "playback open\n");
+       VPRINTK1(KERN_INFO "playback open\n");
 
        return 0;
 }
@@ -1012,8 +1029,8 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
 
-       hpi_handle_error(hpi_outstream_close(ss, dpcm->h_stream));
-       snd_printd(KERN_INFO "playback close\n");
+       hpi_handle_error(hpi_outstream_close(dpcm->h_stream));
+       VPRINTK1(KERN_INFO "playback close\n");
 
        return 0;
 }
@@ -1036,9 +1053,11 @@ static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
        VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n",
                        substream->number, len);
 
-       hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
+       hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream,
                                runtime->dma_area, len, &dpcm->format));
 
+       dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len;
+
        return 0;
 }
 
@@ -1052,10 +1071,10 @@ static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
        struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
 
        len = frames_to_bytes(runtime, count);
-       snd_printd(KERN_INFO "playback silence  %u bytes\n", len);
+       VPRINTK1(KERN_INFO "playback silence  %u bytes\n", len);
 
        memset(runtime->dma_area, 0, len);
-       hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
+       hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream,
                                runtime->dma_area, len, &dpcm->format));
        return 0;
 }
@@ -1091,13 +1110,13 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
 
-       VPRINTK2("capture pointer %d=%d\n",
-                       substream->number, dpcm->pcm_buf_pos);
-       /* NOTE Unlike playback can't use actual dwSamplesPlayed
+       VPRINTK2(KERN_INFO "capture pointer %d=%d\n",
+                       substream->number, dpcm->pcm_buf_dma_ofs);
+       /* NOTE Unlike playback can't use actual samples_played
                for the capture position, because those samples aren't yet in
                the local buffer available for reading.
        */
-       return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
+       return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
 }
 
 static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream,
@@ -1111,11 +1130,12 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
 
-       hpi_handle_error(hpi_instream_reset(ss, dpcm->h_stream));
-       dpcm->pcm_irq_pos = 0;
-       dpcm->pcm_buf_pos = 0;
+       hpi_handle_error(hpi_instream_reset(dpcm->h_stream));
+       dpcm->pcm_buf_host_rw_ofs = 0;
+       dpcm->pcm_buf_dma_ofs = 0;
+       dpcm->pcm_buf_elapsed_dma_ofs = 0;
 
-       snd_printd("capture prepare %d\n", substream->number);
+       VPRINTK1("Capture Prepare %d\n", substream->number);
        return 0;
 }
 
@@ -1133,12 +1153,12 @@ static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi,
 
        /* on cards without SRC, must query at valid rate,
                maybe set by external sync */
-       err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+       err = hpi_mixer_get_control(asihpi->h_mixer,
                                  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
                                  HPI_CONTROL_SAMPLECLOCK, &h_control);
 
        if (!err)
-               err = hpi_sample_clock_get_sample_rate(ss, h_control,
+               err = hpi_sample_clock_get_sample_rate(h_control,
                        &sample_rate);
 
        for (format = HPI_FORMAT_PCM8_UNSIGNED;
@@ -1147,7 +1167,7 @@ static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi,
                err = hpi_format_create(&hpi_format, 2, format,
                                sample_rate, 128000, 0);
                if (!err)
-                       err = hpi_instream_query_format(ss, h_stream,
+                       err = hpi_instream_query_format(h_stream,
                                            &hpi_format);
                if (!err)
                        pcmhw->formats |=
@@ -1178,11 +1198,11 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
        if (dpcm == NULL)
                return -ENOMEM;
 
-       snd_printd("hpi_instream_open adapter %d stream %d\n",
+       VPRINTK1("hpi_instream_open adapter %d stream %d\n",
                   card->adapter_index, substream->number);
 
        err = hpi_handle_error(
-           hpi_instream_open(ss, card->adapter_index,
+           hpi_instream_open(card->adapter_index,
                             substream->number, &dpcm->h_stream));
        if (err)
                kfree(dpcm);
@@ -1209,6 +1229,9 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
                snd_card_asihpi_capture.info |= 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)
@@ -1231,7 +1254,7 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
 {
        struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
 
-       hpi_handle_error(hpi_instream_close(ss, dpcm->h_stream));
+       hpi_handle_error(hpi_instream_close(dpcm->h_stream));
        return 0;
 }
 
@@ -1241,18 +1264,17 @@ static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
-       u32 data_size;
+       u32 len;
 
-       data_size = frames_to_bytes(runtime, count);
+       len = frames_to_bytes(runtime, count);
 
-       VPRINTK2("capture copy%d %d bytes\n", substream->number, data_size);
-       hpi_handle_error(hpi_instream_read_buf(ss, dpcm->h_stream,
-                               runtime->dma_area, data_size));
+       VPRINTK2(KERN_INFO "capture copy%d %d bytes\n", substream->number, len);
+       hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream,
+                               runtime->dma_area, len));
 
-       /* Used by capture_pointer */
-       dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + data_size;
+       dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len;
 
-       if (copy_to_user(dst, runtime->dma_area, data_size))
+       if (copy_to_user(dst, runtime->dma_area, len))
                return -EFAULT;
 
        return 0;
@@ -1287,7 +1309,7 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
        struct snd_pcm *pcm;
        int err;
 
-       err = snd_pcm_new(asihpi->card, "asihpi PCM", device,
+       err = snd_pcm_new(asihpi->card, "Asihpi PCM", device,
                         asihpi->num_outstreams, asihpi->num_instreams,
                         &pcm);
        if (err < 0)
@@ -1307,7 +1329,7 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
 
        pcm->private_data = asihpi;
        pcm->info_flags = 0;
-       strcpy(pcm->name, "asihpi PCM");
+       strcpy(pcm->name, "Asihpi PCM");
 
        /*? do we want to emulate MMAP for non-BBM cards?
        Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
@@ -1330,8 +1352,7 @@ struct hpi_control {
        char name[44]; /* copied to snd_ctl_elem_id.name[44]; */
 };
 
-static char *asihpi_tuner_band_names[] =
-{
+static const char * const asihpi_tuner_band_names[] = {
        "invalid",
        "AM",
        "FM mono",
@@ -1349,70 +1370,36 @@ compile_time_assert(
                (HPI_TUNER_BAND_LAST+1)),
        assert_tuner_band_names_size);
 
-#if ASI_STYLE_NAMES
-static char *asihpi_src_names[] =
-{
+static const char * const asihpi_src_names[] = {
        "no source",
-       "outstream",
-       "line_in",
-       "aes_in",
-       "tuner",
+       "PCM",
+       "Line",
+       "Digital",
+       "Tuner",
        "RF",
-       "clock",
-       "bitstr",
-       "mic",
-       "cobranet",
-       "analog_in",
-       "adapter",
+       "Clock",
+       "Bitstream",
+       "Microphone",
+       "Cobranet",
+       "Analog",
+       "Adapter",
 };
-#else
-static char *asihpi_src_names[] =
-{
-       "no source",
-       "PCM playback",
-       "line in",
-       "digital in",
-       "tuner",
-       "RF",
-       "clock",
-       "bitstream",
-       "mic",
-       "cobranet in",
-       "analog in",
-       "adapter",
-};
-#endif
 
 compile_time_assert(
        (ARRAY_SIZE(asihpi_src_names) ==
                (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
        assert_src_names_size);
 
-#if ASI_STYLE_NAMES
-static char *asihpi_dst_names[] =
-{
-       "no destination",
-       "instream",
-       "line_out",
-       "aes_out",
-       "RF",
-       "speaker" ,
-       "cobranet",
-       "analog_out",
-};
-#else
-static char *asihpi_dst_names[] =
-{
+static const char * const asihpi_dst_names[] = {
        "no destination",
-       "PCM capture",
-       "line out",
-       "digital out",
+       "PCM",
+       "Line",
+       "Digital",
        "RF",
-       "speaker",
-       "cobranet out",
-       "analog out"
+       "Speaker",
+       "Cobranet Out",
+       "Analog"
 };
-#endif
 
 compile_time_assert(
        (ARRAY_SIZE(asihpi_dst_names) ==
@@ -1438,30 +1425,45 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
                                struct hpi_control *hpi_ctl,
                                char *name)
 {
+       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)
+               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))
+               dir = "Capture "; /* On a source node that is not PCM playback */
+       else if (hpi_ctl->src_node_type &&
+               (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
+               (hpi_ctl->dst_node_type))
+               dir = "Monitor Playback "; /* Between an input and an output */
+       else
+               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 to %s%d %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],
                        hpi_ctl->dst_node_index,
-                       name);
+                       dir, name);
        else if (hpi_ctl->dst_node_type) {
-               sprintf(hpi_ctl->name, "%s%d %s",
+               sprintf(hpi_ctl->name, "%s %d %s%s",
                asihpi_dst_names[hpi_ctl->dst_node_type],
                hpi_ctl->dst_node_index,
-               name);
+               dir, name);
        } else {
-               sprintf(hpi_ctl->name, "%s%d %s",
+               sprintf(hpi_ctl->name, "%s %d %s%s",
                asihpi_src_names[hpi_ctl->src_node_type],
                hpi_ctl->src_node_index,
-               name);
+               dir, name);
        }
+       /* printk(KERN_INFO "Adding %s %d to %d ",  hpi_ctl->name,
+               hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */
 }
 
 /*------------------------------------------------------------
@@ -1478,7 +1480,7 @@ static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
        short max_gain_mB;
        short step_gain_mB;
 
-       err = hpi_volume_query_range(ss, h_control,
+       err = hpi_volume_query_range(h_control,
                        &min_gain_mB, &max_gain_mB, &step_gain_mB);
        if (err) {
                max_gain_mB = 0;
@@ -1500,7 +1502,7 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
        u32 h_control = kcontrol->private_value;
        short an_gain_mB[HPI_MAX_CHANNELS];
 
-       hpi_handle_error(hpi_volume_get_gain(ss, h_control, an_gain_mB));
+       hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB));
        ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB;
        ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB;
 
@@ -1522,7 +1524,7 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
           asihpi->mixer_volume[addr][1] != right;
         */
        change = 1;
-       hpi_handle_error(hpi_volume_set_gain(ss, h_control, an_gain_mB));
+       hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
        return change;
 }
 
@@ -1534,7 +1536,7 @@ static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
        struct snd_card *card = asihpi->card;
        struct snd_kcontrol_new snd_control;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "volume");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Volume");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
                                SNDRV_CTL_ELEM_ACCESS_TLV_READ;
        snd_control.info = snd_asihpi_volume_info;
@@ -1558,7 +1560,7 @@ static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol,
        short step_gain_mB;
 
        err =
-           hpi_level_query_range(ss, h_control, &min_gain_mB,
+           hpi_level_query_range(h_control, &min_gain_mB,
                               &max_gain_mB, &step_gain_mB);
        if (err) {
                max_gain_mB = 2400;
@@ -1580,7 +1582,7 @@ static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol,
        u32 h_control = kcontrol->private_value;
        short an_gain_mB[HPI_MAX_CHANNELS];
 
-       hpi_handle_error(hpi_level_get_gain(ss, h_control, an_gain_mB));
+       hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB));
        ucontrol->value.integer.value[0] =
            an_gain_mB[0] / HPI_UNITS_PER_dB;
        ucontrol->value.integer.value[1] =
@@ -1604,7 +1606,7 @@ static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol,
           asihpi->mixer_level[addr][1] != right;
         */
        change = 1;
-       hpi_handle_error(hpi_level_set_gain(ss, h_control, an_gain_mB));
+       hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB));
        return change;
 }
 
@@ -1617,7 +1619,7 @@ static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
        struct snd_kcontrol_new snd_control;
 
        /* can't use 'volume' cos some nodes have volume as well */
-       asihpi_ctl_init(&snd_control, hpi_ctl, "level");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Level");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
                                SNDRV_CTL_ELEM_ACCESS_TLV_READ;
        snd_control.info = snd_asihpi_level_info;
@@ -1633,12 +1635,8 @@ static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
  ------------------------------------------------------------*/
 
 /* AESEBU format */
-static char *asihpi_aesebu_format_names[] =
-{
-       "N/A",
-       "S/PDIF",
-       "AES/EBU",
-};
+static const char * const asihpi_aesebu_format_names[] = {
+       "N/A", "S/PDIF", "AES/EBU" };
 
 static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
@@ -1659,12 +1657,12 @@ static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
 
 static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol,
-                       u16 (*func)(const struct hpi_hsubsys *, u32, u16 *))
+                       u16 (*func)(u32, u16 *))
 {
        u32 h_control = kcontrol->private_value;
        u16 source, err;
 
-       err = func(ss, h_control, &source);
+       err = func(h_control, &source);
 
        /* default to N/A */
        ucontrol->value.enumerated.item[0] = 0;
@@ -1681,7 +1679,7 @@ static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
 
 static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol,
-                        u16 (*func)(const struct hpi_hsubsys *, u32, u16))
+                        u16 (*func)(u32, u16))
 {
        u32 h_control = kcontrol->private_value;
 
@@ -1693,7 +1691,7 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
        if (ucontrol->value.enumerated.item[0] == 2)
                source = HPI_AESEBU_FORMAT_AESEBU;
 
-       if (func(ss, h_control, source) != 0)
+       if (func(h_control, source) != 0)
                return -EINVAL;
 
        return 1;
@@ -1702,13 +1700,13 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
 static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol) {
        return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
-                                       HPI_AESEBU__receiver_get_format);
+                                       hpi_aesebu_receiver_get_format);
 }
 
 static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol) {
        return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
-                                       HPI_AESEBU__receiver_set_format);
+                                       hpi_aesebu_receiver_set_format);
 }
 
 static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol,
@@ -1730,8 +1728,8 @@ static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol,
        u32 h_control = kcontrol->private_value;
        u16 status;
 
-       hpi_handle_error(HPI_AESEBU__receiver_get_error_status(
-                               ss, h_control, &status));
+       hpi_handle_error(hpi_aesebu_receiver_get_error_status(
+                                        h_control, &status));
        ucontrol->value.integer.value[0] = status;
        return 0;
 }
@@ -1742,7 +1740,7 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
        struct snd_card *card = asihpi->card;
        struct snd_kcontrol_new snd_control;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "format");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
        snd_control.info = snd_asihpi_aesebu_format_info;
        snd_control.get = snd_asihpi_aesebu_rx_format_get;
@@ -1752,7 +1750,7 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
        if (ctl_add(card, &snd_control, asihpi) < 0)
                return -EINVAL;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "status");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Status");
        snd_control.access =
            SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
        snd_control.info = snd_asihpi_aesebu_rxstatus_info;
@@ -1764,13 +1762,13 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
 static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol) {
        return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
-                                       HPI_AESEBU__transmitter_get_format);
+                                       hpi_aesebu_transmitter_get_format);
 }
 
 static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol) {
        return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
-                                       HPI_AESEBU__transmitter_set_format);
+                                       hpi_aesebu_transmitter_set_format);
 }
 
 
@@ -1780,7 +1778,7 @@ static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
        struct snd_card *card = asihpi->card;
        struct snd_kcontrol_new snd_control;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "format");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
        snd_control.info = snd_asihpi_aesebu_format_info;
        snd_control.get = snd_asihpi_aesebu_tx_format_get;
@@ -1804,7 +1802,7 @@ static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol,
        u16 gain_range[3];
 
        for (idx = 0; idx < 3; idx++) {
-               err = hpi_tuner_query_gain(ss, h_control,
+               err = hpi_tuner_query_gain(h_control,
                                          idx, &gain_range[idx]);
                if (err != 0)
                        return err;
@@ -1827,7 +1825,7 @@ static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol,
        u32 h_control = kcontrol->private_value;
        short gain;
 
-       hpi_handle_error(hpi_tuner_get_gain(ss, h_control, &gain));
+       hpi_handle_error(hpi_tuner_get_gain(h_control, &gain));
        ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB;
 
        return 0;
@@ -1843,7 +1841,7 @@ static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol,
        short gain;
 
        gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
-       hpi_handle_error(hpi_tuner_set_gain(ss, h_control, gain));
+       hpi_handle_error(hpi_tuner_set_gain(h_control, gain));
 
        return 1;
 }
@@ -1857,7 +1855,7 @@ static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol,
        u32 i;
 
        for (i = 0; i < len; i++) {
-               err = hpi_tuner_query_band(ss,
+               err = hpi_tuner_query_band(
                                h_control, i, &band_list[i]);
                if (err != 0)
                        break;
@@ -1913,7 +1911,7 @@ static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
        num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
                                HPI_TUNER_BAND_LAST);
 
-       hpi_handle_error(hpi_tuner_get_band(ss, h_control, &band));
+       hpi_handle_error(hpi_tuner_get_band(h_control, &band));
 
        ucontrol->value.enumerated.item[0] = -1;
        for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++)
@@ -1940,7 +1938,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
                        HPI_TUNER_BAND_LAST);
 
        band = tuner_bands[ucontrol->value.enumerated.item[0]];
-       hpi_handle_error(hpi_tuner_set_band(ss, h_control, band));
+       hpi_handle_error(hpi_tuner_set_band(h_control, band));
 
        return 1;
 }
@@ -1965,7 +1963,7 @@ static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol,
 
        for (band_iter = 0; band_iter < num_bands; band_iter++) {
                for (idx = 0; idx < 3; idx++) {
-                       err = hpi_tuner_query_frequency(ss, h_control,
+                       err = hpi_tuner_query_frequency(h_control,
                                idx, tuner_bands[band_iter],
                                &temp_freq_range[idx]);
                        if (err != 0)
@@ -1998,7 +1996,7 @@ static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol,
        u32 h_control = kcontrol->private_value;
        u32 freq;
 
-       hpi_handle_error(hpi_tuner_get_frequency(ss, h_control, &freq));
+       hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq));
        ucontrol->value.integer.value[0] = freq;
 
        return 0;
@@ -2011,7 +2009,7 @@ static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol,
        u32 freq;
 
        freq = ucontrol->value.integer.value[0];
-       hpi_handle_error(hpi_tuner_set_frequency(ss, h_control, freq));
+       hpi_handle_error(hpi_tuner_set_frequency(h_control, freq));
 
        return 1;
 }
@@ -2026,8 +2024,8 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
        snd_control.private_value = hpi_ctl->h_control;
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
 
-       if (!hpi_tuner_get_gain(ss, hpi_ctl->h_control, NULL)) {
-               asihpi_ctl_init(&snd_control, hpi_ctl, "gain");
+       if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) {
+               asihpi_ctl_init(&snd_control, hpi_ctl, "Gain");
                snd_control.info = snd_asihpi_tuner_gain_info;
                snd_control.get = snd_asihpi_tuner_gain_get;
                snd_control.put = snd_asihpi_tuner_gain_put;
@@ -2036,7 +2034,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
                        return -EINVAL;
        }
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "band");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Band");
        snd_control.info = snd_asihpi_tuner_band_info;
        snd_control.get = snd_asihpi_tuner_band_get;
        snd_control.put = snd_asihpi_tuner_band_put;
@@ -2044,7 +2042,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
        if (ctl_add(card, &snd_control, asihpi) < 0)
                return -EINVAL;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "freq");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Freq");
        snd_control.info = snd_asihpi_tuner_freq_info;
        snd_control.get = snd_asihpi_tuner_freq_get;
        snd_control.put = snd_asihpi_tuner_freq_put;
@@ -2095,7 +2093,7 @@ static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
        short an_gain_mB[HPI_MAX_CHANNELS], i;
        u16 err;
 
-       err = hpi_meter_get_peak(ss, h_control, an_gain_mB);
+       err = hpi_meter_get_peak(h_control, an_gain_mB);
 
        for (i = 0; i < HPI_MAX_CHANNELS; i++) {
                if (err) {
@@ -2120,7 +2118,7 @@ static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
        struct snd_card *card = asihpi->card;
        struct snd_kcontrol_new snd_control;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "meter");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Meter");
        snd_control.access =
            SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
        snd_control.info = snd_asihpi_meter_info;
@@ -2140,7 +2138,7 @@ static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
        struct hpi_control hpi_ctl;
        int s, err;
        for (s = 0; s < 32; s++) {
-               err = hpi_multiplexer_query_source(ss, h_control, s,
+               err = hpi_multiplexer_query_source(h_control, s,
                                                  &hpi_ctl.
                                                  src_node_type,
                                                  &hpi_ctl.
@@ -2168,7 +2166,7 @@ static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
                    uinfo->value.enumerated.items - 1;
 
        err =
-           hpi_multiplexer_query_source(ss, h_control,
+           hpi_multiplexer_query_source(h_control,
                                        uinfo->value.enumerated.item,
                                        &src_node_type, &src_node_index);
 
@@ -2186,11 +2184,11 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
        u16 src_node_type, src_node_index;
        int s;
 
-       hpi_handle_error(hpi_multiplexer_get_source(ss, h_control,
+       hpi_handle_error(hpi_multiplexer_get_source(h_control,
                                &source_type, &source_index));
        /* Should cache this search result! */
        for (s = 0; s < 256; s++) {
-               if (hpi_multiplexer_query_source(ss, h_control, s,
+               if (hpi_multiplexer_query_source(h_control, s,
                                            &src_node_type, &src_node_index))
                        break;
 
@@ -2201,7 +2199,7 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
                }
        }
        snd_printd(KERN_WARNING
-               "control %x failed to match mux source %hu %hu\n",
+               "Control %x failed to match mux source %hu %hu\n",
                h_control, source_type, source_index);
        ucontrol->value.enumerated.item[0] = 0;
        return 0;
@@ -2217,12 +2215,12 @@ static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol,
 
        change = 1;
 
-       e = hpi_multiplexer_query_source(ss, h_control,
+       e = hpi_multiplexer_query_source(h_control,
                                    ucontrol->value.enumerated.item[0],
                                    &source_type, &source_index);
        if (!e)
                hpi_handle_error(
-                       hpi_multiplexer_set_source(ss, h_control,
+                       hpi_multiplexer_set_source(h_control,
                                                source_type, source_index));
        return change;
 }
@@ -2234,11 +2232,7 @@ static int  __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
        struct snd_card *card = asihpi->card;
        struct snd_kcontrol_new snd_control;
 
-#if ASI_STYLE_NAMES
-       asihpi_ctl_init(&snd_control, hpi_ctl, "multiplexer");
-#else
-       asihpi_ctl_init(&snd_control, hpi_ctl, "route");
-#endif
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Route");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
        snd_control.info = snd_asihpi_mux_info;
        snd_control.get = snd_asihpi_mux_get;
@@ -2254,33 +2248,38 @@ static int  __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
 static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_info *uinfo)
 {
-       static char *mode_names[HPI_CHANNEL_MODE_LAST] = {
-               "normal", "swap",
-               "from_left", "from_right",
-               "to_left", "to_right"
+       static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = {
+               "invalid",
+               "Normal", "Swap",
+               "From Left", "From Right",
+               "To Left", "To Right"
        };
 
        u32 h_control = kcontrol->private_value;
        u16 mode;
        int i;
+       u16 mode_map[6];
+       int valid_modes = 0;
 
        /* HPI channel mode values can be from 1 to 6
        Some adapters only support a contiguous subset
        */
        for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
-               if (hpi_channel_mode_query_mode(
-                       ss,  h_control, i, &mode))
-                       break;
+               if (!hpi_channel_mode_query_mode(
+                       h_control, i, &mode)) {
+                       mode_map[valid_modes] = mode;
+                       valid_modes++;
+                       }
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
-       uinfo->value.enumerated.items = i;
+       uinfo->value.enumerated.items = valid_modes;
 
-       if (uinfo->value.enumerated.item >= i)
-               uinfo->value.enumerated.item = i - 1;
+       if (uinfo->value.enumerated.item >= valid_modes)
+               uinfo->value.enumerated.item = valid_modes - 1;
 
        strcpy(uinfo->value.enumerated.name,
-              mode_names[uinfo->value.enumerated.item]);
+              mode_names[mode_map[uinfo->value.enumerated.item]]);
 
        return 0;
 }
@@ -2291,7 +2290,7 @@ static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
        u32 h_control = kcontrol->private_value;
        u16 mode;
 
-       if (hpi_channel_mode_get(ss, h_control, &mode))
+       if (hpi_channel_mode_get(h_control, &mode))
                mode = 1;
 
        ucontrol->value.enumerated.item[0] = mode - 1;
@@ -2307,7 +2306,7 @@ static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol,
 
        change = 1;
 
-       hpi_handle_error(hpi_channel_mode_set(ss, h_control,
+       hpi_handle_error(hpi_channel_mode_set(h_control,
                           ucontrol->value.enumerated.item[0] + 1));
        return change;
 }
@@ -2319,7 +2318,7 @@ static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
        struct snd_card *card = asihpi->card;
        struct snd_kcontrol_new snd_control;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "channel mode");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Mode");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
        snd_control.info = snd_asihpi_cmode_info;
        snd_control.get = snd_asihpi_cmode_get;
@@ -2331,15 +2330,12 @@ static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
 /*------------------------------------------------------------
    Sampleclock source  controls
  ------------------------------------------------------------*/
-
-static char *sampleclock_sources[MAX_CLOCKSOURCES] =
-    { "N/A", "local PLL", "AES/EBU sync", "word external", "word header",
-         "SMPTE", "AES/EBU in1", "auto", "network", "invalid",
-         "prev module",
-         "AES/EBU in2", "AES/EBU in3", "AES/EBU in4", "AES/EBU in5",
-         "AES/EBU in6", "AES/EBU in7", "AES/EBU in8"};
-
-
+static char *sampleclock_sources[MAX_CLOCKSOURCES] = {
+       "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
+       "SMPTE", "Digital1", "Auto", "Network", "Invalid",
+       "Prev Module",
+       "Digital2", "Digital3", "Digital4", "Digital5",
+       "Digital6", "Digital7", "Digital8"};
 
 static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
@@ -2371,11 +2367,11 @@ static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
        int i;
 
        ucontrol->value.enumerated.item[0] = 0;
-       if (hpi_sample_clock_get_source(ss, h_control, &source))
+       if (hpi_sample_clock_get_source(h_control, &source))
                source = 0;
 
        if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
-               if (hpi_sample_clock_get_source_index(ss, h_control, &srcindex))
+               if (hpi_sample_clock_get_source_index(h_control, &srcindex))
                        srcindex = 0;
 
        for (i = 0; i < clkcache->count; i++)
@@ -2402,11 +2398,11 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
        if (item >= clkcache->count)
                item = clkcache->count-1;
 
-       hpi_handle_error(hpi_sample_clock_set_source(ss,
+       hpi_handle_error(hpi_sample_clock_set_source(
                                h_control, clkcache->s[item].source));
 
        if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
-               hpi_handle_error(hpi_sample_clock_set_source_index(ss,
+               hpi_handle_error(hpi_sample_clock_set_source_index(
                                h_control, clkcache->s[item].index));
        return change;
 }
@@ -2434,7 +2430,7 @@ static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol,
        u32 rate;
        u16 e;
 
-       e = hpi_sample_clock_get_local_rate(ss, h_control, &rate);
+       e = hpi_sample_clock_get_local_rate(h_control, &rate);
        if (!e)
                ucontrol->value.integer.value[0] = rate;
        else
@@ -2452,7 +2448,7 @@ static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol,
           asihpi->mixer_clkrate[addr][1] != right;
         */
        change = 1;
-       hpi_handle_error(hpi_sample_clock_set_local_rate(ss, h_control,
+       hpi_handle_error(hpi_sample_clock_set_local_rate(h_control,
                                      ucontrol->value.integer.value[0]));
        return change;
 }
@@ -2476,7 +2472,7 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
        u32 rate;
        u16 e;
 
-       e = hpi_sample_clock_get_sample_rate(ss, h_control, &rate);
+       e = hpi_sample_clock_get_sample_rate(h_control, &rate);
        if (!e)
                ucontrol->value.integer.value[0] = rate;
        else
@@ -2501,7 +2497,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
        clkcache->has_local = 0;
 
        for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) {
-               if  (hpi_sample_clock_query_source(ss, hSC,
+               if  (hpi_sample_clock_query_source(hSC,
                                i, &source))
                        break;
                clkcache->s[i].source = source;
@@ -2515,7 +2511,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
        if (has_aes_in)
                /* already will have picked up index 0 above */
                for (j = 1; j < 8; j++) {
-                       if (hpi_sample_clock_query_source_index(ss, hSC,
+                       if (hpi_sample_clock_query_source_index(hSC,
                                j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT,
                                &source))
                                break;
@@ -2528,7 +2524,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
                }
        clkcache->count = i;
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "source");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Source");
        snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
        snd_control.info = snd_asihpi_clksrc_info;
        snd_control.get = snd_asihpi_clksrc_get;
@@ -2538,7 +2534,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
 
 
        if (clkcache->has_local) {
-               asihpi_ctl_init(&snd_control, hpi_ctl, "local_rate");
+               asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate");
                snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
                snd_control.info = snd_asihpi_clklocal_info;
                snd_control.get = snd_asihpi_clklocal_get;
@@ -2549,7 +2545,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
                        return -EINVAL;
        }
 
-       asihpi_ctl_init(&snd_control, hpi_ctl, "rate");
+       asihpi_ctl_init(&snd_control, hpi_ctl, "Rate");
        snd_control.access =
            SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
        snd_control.info = snd_asihpi_clkrate_info;
@@ -2571,10 +2567,10 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
 
        if (snd_BUG_ON(!asihpi))
                return -EINVAL;
-       strcpy(card->mixername, "asihpi mixer");
+       strcpy(card->mixername, "Asihpi Mixer");
 
        err =
-           hpi_mixer_open(ss, asihpi->adapter_index,
+           hpi_mixer_open(asihpi->adapter_index,
                          &asihpi->h_mixer);
        hpi_handle_error(err);
        if (err)
@@ -2585,7 +2581,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
 
        for (idx = 0; idx < 2000; idx++) {
                err = hpi_mixer_get_control_by_index(
-                               ss, asihpi->h_mixer,
+                               asihpi->h_mixer,
                                idx,
                                &hpi_ctl.src_node_type,
                                &hpi_ctl.src_node_index,
@@ -2597,7 +2593,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
                        if (err == HPI_ERROR_CONTROL_DISABLED) {
                                if (mixer_dump)
                                        snd_printk(KERN_INFO
-                                                  "disabled HPI control(%d)\n",
+                                                  "Disabled HPI Control(%d)\n",
                                                   idx);
                                continue;
                        } else
@@ -2662,7 +2658,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
                default:
                        if (mixer_dump)
                                snd_printk(KERN_INFO
-                                       "untranslated HPI control"
+                                       "Untranslated HPI Control"
                                        "(%d) %d %d %d %d %d\n",
                                        idx,
                                        hpi_ctl.control_type,
@@ -2712,14 +2708,14 @@ snd_asihpi_proc_read(struct snd_info_entry *entry,
                version & 0x7,
                ((version >> 13) * 100) + ((version >> 7) & 0x3f));
 
-       err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+       err = hpi_mixer_get_control(asihpi->h_mixer,
                                  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
                                  HPI_CONTROL_SAMPLECLOCK, &h_control);
 
        if (!err) {
-               err = hpi_sample_clock_get_sample_rate(ss,
+               err = hpi_sample_clock_get_sample_rate(
                                        h_control, &rate);
-               err += hpi_sample_clock_get_source(ss, h_control, &source);
+               err += hpi_sample_clock_get_source(h_control, &source);
 
                if (!err)
                        snd_iprintf(buffer, "sample_clock=%d_hz, source %s\n",
@@ -2841,15 +2837,17 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
                if (err < 0)
                        return err;
                snd_printk(KERN_WARNING
-                       "**** WARNING **** adapter index %d->ALSA index %d\n",
+                       "**** WARNING **** Adapter index %d->ALSA index %d\n",
                        hpi_card->index, card->number);
        }
 
+       snd_card_set_dev(card, &pci_dev->dev);
+
        asihpi = (struct snd_card_asihpi *) card->private_data;
        asihpi->card = card;
-       asihpi->pci = hpi_card->pci;
+       asihpi->pci = pci_dev;
        asihpi->adapter_index = hpi_card->index;
-       hpi_handle_error(hpi_adapter_get_info(ss,
+       hpi_handle_error(hpi_adapter_get_info(
                                 asihpi->adapter_index,
                                 &asihpi->num_outstreams,
                                 &asihpi->num_instreams,
@@ -2859,7 +2857,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
        version = asihpi->version;
        snd_printk(KERN_INFO "adapter ID=%4X index=%d num_outstreams=%d "
                        "num_instreams=%d S/N=%d\n"
-                       "hw version %c%d DSP code version %03d\n",
+                       "Hw Version %c%d DSP code version %03d\n",
                        asihpi->type, asihpi->adapter_index,
                        asihpi->num_outstreams,
                        asihpi->num_instreams, asihpi->serial_number,
@@ -2871,33 +2869,33 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
        if (pcm_substreams < asihpi->num_instreams)
                pcm_substreams = asihpi->num_instreams;
 
-       err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+       err = hpi_adapter_get_property(asihpi->adapter_index,
                HPI_ADAPTER_PROPERTY_CAPS1,
                NULL, &asihpi->support_grouping);
        if (err)
                asihpi->support_grouping = 0;
 
-       err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+       err = hpi_adapter_get_property(asihpi->adapter_index,
                HPI_ADAPTER_PROPERTY_CAPS2,
                &asihpi->support_mrx, NULL);
        if (err)
                asihpi->support_mrx = 0;
 
-       err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+       err = hpi_adapter_get_property(asihpi->adapter_index,
                HPI_ADAPTER_PROPERTY_INTERVAL,
                NULL, &asihpi->update_interval_frames);
        if (err)
                asihpi->update_interval_frames = 512;
 
-       hpi_handle_error(hpi_instream_open(ss, asihpi->adapter_index,
+       hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
                             0, &h_stream));
 
-       err = hpi_instream_host_buffer_free(ss, h_stream);
+       err = hpi_instream_host_buffer_free(h_stream);
        asihpi->support_mmap = (!err);
 
-       hpi_handle_error(hpi_instream_close(ss, h_stream));
+       hpi_handle_error(hpi_instream_close(h_stream));
 
-       err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+       err = hpi_adapter_get_property(asihpi->adapter_index,
                HPI_ADAPTER_PROPERTY_CURCHANNELS,
                &asihpi->in_max_chans, &asihpi->out_max_chans);
        if (err) {
@@ -2923,13 +2921,13 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
                goto __nodev;
        }
 
-       err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+       err = hpi_mixer_get_control(asihpi->h_mixer,
                                  HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
                                  HPI_CONTROL_SAMPLECLOCK, &h_control);
 
        if (!err)
                err = hpi_sample_clock_set_local_rate(
-                       ss, h_control, adapter_fs);
+                       h_control, adapter_fs);
 
        snd_asihpi_proc_init(asihpi);
 
index 23399d02f666247601f25567f600f0666d4232c4..6fc025c448de838184e801e7a8becee4ae8698e9 100644 (file)
 
  The HPI is a low-level hardware abstraction layer to all
  AudioScience digital audio adapters
-*/
-/*
- You must define one operating system that the HPI is to be compiled under
- HPI_OS_WIN32_USER   32bit Windows
- HPI_OS_DSP_C6000    DSP TI C6000  (automatically set)
- HPI_OS_WDM          Windows WDM kernel driver
- HPI_OS_LINUX        Linux userspace
- HPI_OS_LINUX_KERNEL Linux kernel (automatically set)
 
 (C) Copyright AudioScience Inc. 1998-2010
-******************************************************************************/
+*/
+
 #ifndef _HPI_H_
 #define _HPI_H_
 /* HPI Version
@@ -50,20 +43,20 @@ i.e 3.05.02 is a development version
 #define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
 
 /* Use single digits for versions less that 10 to avoid octal. */
-#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 4, 1)
-#define HPI_VER_STRING "4.04.01"
+#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 6, 0)
+#define HPI_VER_STRING "4.06.00"
 
 /* Library version as documented in hpi-api-versions.txt */
 #define HPI_LIB_VER  HPI_VERSION_CONSTRUCTOR(9, 0, 0)
 
 #include <linux/types.h>
-#define HPI_EXCLUDE_DEPRECATED
+#define HPI_BUILD_EXCLUDE_DEPRECATED
+#define HPI_BUILD_KERNEL_MODE
 
-/******************************************************************************/
 /******************************************************************************/
 /********       HPI API DEFINITIONS                                       *****/
 /******************************************************************************/
-/******************************************************************************/
+
 /*******************************************/
 /**  Audio format types
 \ingroup stream
@@ -174,7 +167,6 @@ The range is +1.0 to -1.0, which corresponds to digital fullscale.
        HPI_FORMAT_UNDEFINED = 0xffff
 };
 
-/******************************************* in/out Stream states */
 /*******************************************/
 /** Stream States
 \ingroup stream
@@ -194,7 +186,7 @@ enum HPI_STREAM_STATES {
                cards to be ready. */
        HPI_STATE_WAIT = 6
 };
-/******************************************* mixer source node types */
+/*******************************************/
 /** Source node types
 \ingroup mixer
 */
@@ -224,7 +216,7 @@ enum HPI_SOURCENODES {
                /* AX6 max sourcenode types = 15 */
 };
 
-/******************************************* mixer dest node types */
+/*******************************************/
 /** Destination node types
 \ingroup mixer
 */
@@ -262,11 +254,11 @@ enum HPI_CONTROLS {
        HPI_CONTROL_MUTE = 4,   /*mute control - not used at present. */
        HPI_CONTROL_MULTIPLEXER = 5,    /**< multiplexer control. */
 
-       HPI_CONTROL_AESEBU_TRANSMITTER = 6,     /**< AES/EBU transmitter control. */
-       HPI_CONTROL_AESEBUTX = HPI_CONTROL_AESEBU_TRANSMITTER,
+       HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control */
+       HPI_CONTROL_AESEBUTX = 6,       /* HPI_CONTROL_AESEBU_TRANSMITTER */
 
        HPI_CONTROL_AESEBU_RECEIVER = 7, /**< AES/EBU receiver control. */
-       HPI_CONTROL_AESEBURX = HPI_CONTROL_AESEBU_RECEIVER,
+       HPI_CONTROL_AESEBURX = 7,       /* HPI_CONTROL_AESEBU_RECEIVER */
 
        HPI_CONTROL_LEVEL = 8, /**< level/trim control - works in d_bu. */
        HPI_CONTROL_TUNER = 9,  /**< tuner control. */
@@ -281,7 +273,7 @@ enum HPI_CONTROLS {
        HPI_CONTROL_SAMPLECLOCK = 17,   /**< sample clock control. */
        HPI_CONTROL_MICROPHONE = 18,    /**< microphone control. */
        HPI_CONTROL_PARAMETRIC_EQ = 19, /**< parametric EQ control. */
-       HPI_CONTROL_EQUALIZER = HPI_CONTROL_PARAMETRIC_EQ,
+       HPI_CONTROL_EQUALIZER = 19,     /*HPI_CONTROL_PARAMETRIC_EQ */
 
        HPI_CONTROL_COMPANDER = 20,     /**< compander control. */
        HPI_CONTROL_COBRANET = 21,      /**< cobranet control. */
@@ -296,10 +288,7 @@ enum HPI_CONTROLS {
 /* WARNING types 256 or greater impact bit packing in all AX6 DSP code */
 };
 
-/* Shorthand names that match attribute names */
-
-/******************************************* ADAPTER ATTRIBUTES ****/
-
+/*******************************************/
 /** Adapter properties
 These are used in HPI_AdapterSetProperty() and HPI_AdapterGetProperty()
 \ingroup adapter
@@ -330,12 +319,21 @@ by the driver and is not passed on to the DSP at all.
 Indicates the state of the adapter's SSX2 setting. This setting is stored in
 non-volatile memory on the adapter. A typical call sequence would be to use
 HPI_ADAPTER_PROPERTY_SSX2_SETTING to set SSX2 on the adapter and then to reload
-the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during startup
-and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2 to enable
-SSX2 stream mapping within the kernel level of the driver.
+the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during
+startup and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2
+to enable SSX2 stream mapping within the kernel level of the driver.
 */
        HPI_ADAPTER_PROPERTY_SSX2_SETTING = 4,
 
+/** Enables/disables PCI(e) IRQ.
+A setting of 0 indicates that no interrupts are being generated. A DSP boot
+this property is set to 0. Setting to a non-zero value specifies the number
+of frames of audio that should be processed between interrupts. This property
+should be set to multiple of the mixer interval as read back from the
+HPI_ADAPTER_PROPERTY_INTERVAL property.
+*/
+       HPI_ADAPTER_PROPERTY_IRQ_RATE = 5,
+
 /** Base number for readonly properties */
        HPI_ADAPTER_PROPERTY_READONLYBASE = 256,
 
@@ -440,21 +438,30 @@ return value is true (1) or false (0). If the current adapter
 mode is MONO SSX2 is disabled, even though this property will
 return true.
 */
-       HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271
+       HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271,
+/** Readonly supports PCI(e) IRQ.
+Indicates that the adapter in it's current mode supports interrupts
+across the host bus. Note, this does not imply that interrupts are
+enabled. Instead it indicates that they can be enabled.
+*/
+       HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ = 272
 };
 
 /** Adapter mode commands
 
-Used in wQueryOrSet field of HPI_AdapterSetModeEx().
+Used in wQueryOrSet parameter of HPI_AdapterSetModeEx().
 \ingroup adapter
 */
 enum HPI_ADAPTER_MODE_CMDS {
+       /** Set the mode to the given parameter */
        HPI_ADAPTER_MODE_SET = 0,
+       /** Return 0 or error depending whether mode is valid,
+       but don't set the mode */
        HPI_ADAPTER_MODE_QUERY = 1
 };
 
 /** Adapter Modes
      These are used by HPI_AdapterSetModeEx()
+ These are used by HPI_AdapterSetModeEx()
 
 \warning - more than 16 possible modes breaks
 a bitmask in the Windows WAVE DLL
@@ -629,10 +636,13 @@ enum HPI_MIXER_STORE_COMMAND {
        HPI_MIXER_STORE_SAVE_SINGLE = 6
 };
 
-/************************************* CONTROL ATTRIBUTE VALUES ****/
+/****************************/
+/* CONTROL ATTRIBUTE VALUES */
+/****************************/
+
 /** Used by mixer plugin enable functions
 
-E.g. HPI_ParametricEQ_SetState()
+E.g. HPI_ParametricEq_SetState()
 \ingroup mixer
 */
 enum HPI_SWITCH_STATES {
@@ -641,6 +651,7 @@ enum HPI_SWITCH_STATES {
 };
 
 /* Volume control special gain values */
+
 /** volumes units are 100ths of a dB
 \ingroup volume
 */
@@ -650,6 +661,11 @@ enum HPI_SWITCH_STATES {
 */
 #define HPI_GAIN_OFF                    (-100 * HPI_UNITS_PER_dB)
 
+/** channel mask specifying all channels
+\ingroup volume
+*/
+#define HPI_BITMASK_ALL_CHANNELS        (0xFFFFFFFF)
+
 /** value returned for no signal
 \ingroup meter
 */
@@ -667,7 +683,7 @@ enum HPI_VOLUME_AUTOFADES {
 
 /** The physical encoding format of the AESEBU I/O.
 
-Used in HPI_AESEBU_Transmitter_SetFormat(), HPI_AESEBU_Receiver_SetFormat()
+Used in HPI_Aesebu_Transmitter_SetFormat(), HPI_Aesebu_Receiver_SetFormat()
 along with related Get and Query functions
 \ingroup aestx
 */
@@ -680,7 +696,7 @@ enum HPI_AESEBU_FORMATS {
 
 /** AES/EBU error status bits
 
-Returned by HPI_AESEBU_Receiver_GetErrorStatus()
+Returned by HPI_Aesebu_Receiver_GetErrorStatus()
 \ingroup aesrx
 */
 enum HPI_AESEBU_ERRORS {
@@ -767,14 +783,6 @@ enum HPI_TUNER_MODE_VALUES {
        HPI_TUNER_MODE_RDS_RBDS = 2 /**<  RDS - RBDS mode */
 };
 
-/** Tuner Level settings
-\ingroup tuner
-*/
-enum HPI_TUNER_LEVEL {
-       HPI_TUNER_LEVEL_AVERAGE = 0,
-       HPI_TUNER_LEVEL_RAW = 1
-};
-
 /** Tuner Status Bits
 
 These bitfield values are returned by a call to HPI_Tuner_GetStatus().
@@ -783,13 +791,13 @@ Multiple fields are returned from a single call.
 */
 enum HPI_TUNER_STATUS_BITS {
        HPI_TUNER_VIDEO_COLOR_PRESENT = 0x0001, /**< video color is present. */
-       HPI_TUNER_VIDEO_IS_60HZ = 0x0020,       /**< 60 hz video detected. */
-       HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040,     /**< video HSYNC is missing. */
-       HPI_TUNER_VIDEO_STATUS_VALID = 0x0100,  /**< video status is valid. */
-       HPI_TUNER_PLL_LOCKED = 0x1000,          /**< the tuner's PLL is locked. */
-       HPI_TUNER_FM_STEREO = 0x2000,           /**< tuner reports back FM stereo. */
-       HPI_TUNER_DIGITAL = 0x0200,             /**< tuner reports digital programming. */
-       HPI_TUNER_MULTIPROGRAM = 0x0400         /**< tuner reports multiple programs. */
+       HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */
+       HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */
+       HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */
+       HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */
+       HPI_TUNER_MULTIPROGRAM = 0x0400, /**< tuner reports multiple programs. */
+       HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */
+       HPI_TUNER_FM_STEREO = 0x2000 /**< tuner reports back FM stereo. */
 };
 
 /** Channel Modes
@@ -839,7 +847,7 @@ enum HPI_SAMPLECLOCK_SOURCES {
        HPI_SAMPLECLOCK_SOURCE_LAST = 10
 };
 
-/** Equalizer filter types. Used by HPI_ParametricEQ_SetBand()
+/** Equalizer filter types. Used by HPI_ParametricEq_SetBand()
 \ingroup parmeq
 */
 enum HPI_FILTER_TYPE {
@@ -882,7 +890,7 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_INVALID_OBJ = 101,
        /** Function does not exist. */
        HPI_ERROR_INVALID_FUNC = 102,
-       /** The specified object (adapter/Stream) does not exist. */
+       /** The specified object does not exist. */
        HPI_ERROR_INVALID_OBJ_INDEX = 103,
        /** Trying to access an object that has not been opened yet. */
        HPI_ERROR_OBJ_NOT_OPEN = 104,
@@ -890,8 +898,7 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_OBJ_ALREADY_OPEN = 105,
        /** PCI, ISA resource not valid. */
        HPI_ERROR_INVALID_RESOURCE = 106,
-       /** GetInfo call from SubSysFindAdapters failed. */
-       HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO = 107,
+       /* HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO= 107 */
        /** Default response was never updated with actual error code. */
        HPI_ERROR_INVALID_RESPONSE = 108,
        /** wSize field of response was not updated,
@@ -899,38 +906,44 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_PROCESSING_MESSAGE = 109,
        /** The network did not respond in a timely manner. */
        HPI_ERROR_NETWORK_TIMEOUT = 110,
-       /** An HPI handle is invalid (uninitialised?). */
+       /* An HPI handle is invalid (uninitialised?). */
        HPI_ERROR_INVALID_HANDLE = 111,
        /** A function or attribute has not been implemented yet. */
        HPI_ERROR_UNIMPLEMENTED = 112,
-       /** There are too many clients attempting to access a network resource. */
+       /** There are too many clients attempting
+           to access a network resource. */
        HPI_ERROR_NETWORK_TOO_MANY_CLIENTS = 113,
-       /** Response buffer passed to HPI_Message was smaller than returned response */
+       /** Response buffer passed to HPI_Message
+           was smaller than returned response.
+           wSpecificError field of hpi response contains the required size.
+       */
        HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL = 114,
        /** The returned response did not match the sent message */
        HPI_ERROR_RESPONSE_MISMATCH = 115,
+       /** A control setting that should have been cached was not. */
+       HPI_ERROR_CONTROL_CACHING = 116,
+       /** A message buffer in the path to the adapter was smaller
+           than the message size.
+           wSpecificError field of hpi response contains the actual size.
+       */
+       HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL = 117,
 
-       /** Too many adapters.*/
-       HPI_ERROR_TOO_MANY_ADAPTERS = 200,
+       /* HPI_ERROR_TOO_MANY_ADAPTERS= 200 */
        /** Bad adpater. */
        HPI_ERROR_BAD_ADAPTER = 201,
        /** Adapter number out of range or not set properly. */
        HPI_ERROR_BAD_ADAPTER_NUMBER = 202,
        /** 2 adapters with the same adapter number. */
-       HPI_DUPLICATE_ADAPTER_NUMBER = 203,
-       /** DSP code failed to bootload. */
+       HPI_ERROR_DUPLICATE_ADAPTER_NUMBER = 203,
+       /** DSP code failed to bootload. (unused?) */
        HPI_ERROR_DSP_BOOTLOAD = 204,
-       /** Adapter failed DSP code self test. */
-       HPI_ERROR_DSP_SELFTEST = 205,
        /** Couldn't find or open the DSP code file. */
        HPI_ERROR_DSP_FILE_NOT_FOUND = 206,
        /** Internal DSP hardware error. */
        HPI_ERROR_DSP_HARDWARE = 207,
-       /** Could not allocate memory in DOS. */
-       HPI_ERROR_DOS_MEMORY_ALLOC = 208,
        /** Could not allocate memory */
        HPI_ERROR_MEMORY_ALLOC = 208,
-       /** Failed to correctly load/config PLD .*/
+       /** Failed to correctly load/config PLD. (unused) */
        HPI_ERROR_PLD_LOAD = 209,
        /** Unexpected end of file, block length too big etc. */
        HPI_ERROR_DSP_FILE_FORMAT = 210,
@@ -939,8 +952,7 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_DSP_FILE_ACCESS_DENIED = 211,
        /** First DSP code section header not found in DSP file. */
        HPI_ERROR_DSP_FILE_NO_HEADER = 212,
-       /** File read operation on DSP code file failed. */
-       HPI_ERROR_DSP_FILE_READ_ERROR = 213,
+       /* HPI_ERROR_DSP_FILE_READ_ERROR= 213, */
        /** DSP code for adapter family not found. */
        HPI_ERROR_DSP_SECTION_NOT_FOUND = 214,
        /** Other OS specific error opening DSP file. */
@@ -950,23 +962,21 @@ enum HPI_ERROR_CODES {
        /** DSP code section header had size == 0. */
        HPI_ERROR_DSP_FILE_NULL_HEADER = 217,
 
-       /** Base number for flash errors. */
-       HPI_ERROR_FLASH = 220,
+       /* HPI_ERROR_FLASH = 220, */
 
        /** Flash has bad checksum */
-       HPI_ERROR_BAD_CHECKSUM = (HPI_ERROR_FLASH + 1),
-       HPI_ERROR_BAD_SEQUENCE = (HPI_ERROR_FLASH + 2),
-       HPI_ERROR_FLASH_ERASE = (HPI_ERROR_FLASH + 3),
-       HPI_ERROR_FLASH_PROGRAM = (HPI_ERROR_FLASH + 4),
-       HPI_ERROR_FLASH_VERIFY = (HPI_ERROR_FLASH + 5),
-       HPI_ERROR_FLASH_TYPE = (HPI_ERROR_FLASH + 6),
-       HPI_ERROR_FLASH_START = (HPI_ERROR_FLASH + 7),
+       HPI_ERROR_BAD_CHECKSUM = 221,
+       HPI_ERROR_BAD_SEQUENCE = 222,
+       HPI_ERROR_FLASH_ERASE = 223,
+       HPI_ERROR_FLASH_PROGRAM = 224,
+       HPI_ERROR_FLASH_VERIFY = 225,
+       HPI_ERROR_FLASH_TYPE = 226,
+       HPI_ERROR_FLASH_START = 227,
 
        /** Reserved for OEMs. */
        HPI_ERROR_RESERVED_1 = 290,
 
-       /** Stream does not exist. */
-       HPI_ERROR_INVALID_STREAM = 300,
+       /* HPI_ERROR_INVALID_STREAM = 300 use HPI_ERROR_INVALID_OBJ_INDEX */
        /** Invalid compression format. */
        HPI_ERROR_INVALID_FORMAT = 301,
        /** Invalid format samplerate */
@@ -977,21 +987,19 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_INVALID_BITRATE = 304,
        /** Invalid datasize used for stream read/write. */
        HPI_ERROR_INVALID_DATASIZE = 305,
-       /** Stream buffer is full during stream write. */
-       HPI_ERROR_BUFFER_FULL = 306,
-       /** Stream buffer is empty during stream read. */
-       HPI_ERROR_BUFFER_EMPTY = 307,
-       /** Invalid datasize used for stream read/write. */
-       HPI_ERROR_INVALID_DATA_TRANSFER = 308,
+       /* HPI_ERROR_BUFFER_FULL = 306 use HPI_ERROR_INVALID_DATASIZE */
+       /* HPI_ERROR_BUFFER_EMPTY = 307 use HPI_ERROR_INVALID_DATASIZE */
+       /** Null data pointer used for stream read/write. */
+       HPI_ERROR_INVALID_DATA_POINTER = 308,
        /** Packet ordering error for stream read/write. */
        HPI_ERROR_INVALID_PACKET_ORDER = 309,
 
        /** Object can't do requested operation in its current
-       state, eg set format, change rec mux state while recording.*/
+           state, eg set format, change rec mux state while recording.*/
        HPI_ERROR_INVALID_OPERATION = 310,
 
-       /** Where an SRG is shared amongst streams, an incompatible samplerate is one
-       that is different to any currently playing or recording stream. */
+       /** Where a SRG is shared amongst streams, an incompatible samplerate
+           is one that is different to any currently active stream. */
        HPI_ERROR_INCOMPATIBLE_SAMPLERATE = 311,
        /** Adapter mode is illegal.*/
        HPI_ERROR_BAD_ADAPTER_MODE = 312,
@@ -1004,6 +1012,8 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_NO_INTERADAPTER_GROUPS = 314,
        /** Streams on different DSPs cannot be grouped. */
        HPI_ERROR_NO_INTERDSP_GROUPS = 315,
+       /** Stream wait cancelled before threshold reached. */
+       HPI_ERROR_WAIT_CANCELLED = 316,
 
        /** Invalid mixer node for this adapter. */
        HPI_ERROR_INVALID_NODE = 400,
@@ -1017,6 +1027,7 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_CONTROL_DISABLED = 404,
        /** I2C transaction failed due to a missing ACK. */
        HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405,
+       HPI_ERROR_I2C_MISSING_ACK = 405,
        /** Control is busy, or coming out of
        reset and cannot be accessed at this time. */
        HPI_ERROR_CONTROL_NOT_READY = 407,
@@ -1027,7 +1038,6 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_NVMEM_FAIL = 452,
 
        /** I2C */
-       HPI_ERROR_I2C_MISSING_ACK = HPI_ERROR_CONTROL_I2C_MISSING_ACK,
        HPI_ERROR_I2C_BAD_ADR = 460,
 
        /** Entity errors */
@@ -1035,6 +1045,7 @@ enum HPI_ERROR_CODES {
        HPI_ERROR_ENTITY_ITEM_COUNT = 471,
        HPI_ERROR_ENTITY_TYPE_INVALID = 472,
        HPI_ERROR_ENTITY_ROLE_INVALID = 473,
+       HPI_ERROR_ENTITY_SIZE_MISMATCH = 474,
 
        /* AES18 specific errors were 500..507 */
 
@@ -1044,11 +1055,18 @@ enum HPI_ERROR_CODES {
        /** hpioct32.c can't obtain mutex */
        HPI_ERROR_MUTEX_TIMEOUT = 700,
 
-       /** errors from HPI backends have values >= this */
+       /** Backend errors used to be greater than this.
+           \deprecated Now, all backends return only errors defined here in hpi.h
+       */
        HPI_ERROR_BACKEND_BASE = 900,
 
-       /** indicates a cached u16 value is invalid. */
-       HPI_ERROR_ILLEGAL_CACHE_VALUE = 0xffff
+       /** Communication with DSP failed */
+       HPI_ERROR_DSP_COMMUNICATION = 900
+               /* Note that the dsp communication error is set to this value so that
+                  it remains compatible with any software that expects such errors
+                  to be backend errors i.e. >= 900.
+                  Do not define any new error codes with values > 900.
+                */
 };
 
 /** \defgroup maximums HPI maximum values
@@ -1075,7 +1093,7 @@ enum HPI_ERROR_CODES {
 
 /**\}*/
 
-/* ////////////////////////////////////////////////////////////////////// */
+/**************/
 /* STRUCTURES */
 #ifndef DISABLE_PRAGMA_PACK1
 #pragma pack(push, 1)
@@ -1092,7 +1110,7 @@ struct hpi_format {
                                /**< Stereo/JointStereo/Mono */
        u16 mode_legacy;
                                /**< Legacy ancillary mode or idle bit  */
-       u16 unused;           /**< unused */
+       u16 unused;           /**< Unused */
        u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
        u16 format;   /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */
 };
@@ -1106,930 +1124,594 @@ struct hpi_anc_frame {
 */
 struct hpi_async_event {
        u16 event_type; /**< type of event. \sa async_event  */
-       u16 sequence;  /**< sequence number, allows lost event detection */
-       u32 state;    /**< new state */
-       u32 h_object;    /**< handle to the object returning the event. */
+       u16 sequence; /**< Sequence number, allows lost event detection */
+       u32 state; /**< New state */
+       u32 h_object; /**< handle to the object returning the event. */
        union {
                struct {
                        u16 index; /**< GPIO bit index. */
                } gpio;
                struct {
                        u16 node_index; /**< what node is the control on ? */
-                       u16 node_type;  /**< what type of node is the control on ? */
+                       u16 node_type; /**< what type of node is the control on ? */
                } control;
        } u;
 };
 
-/*/////////////////////////////////////////////////////////////////////////// */
-/* Public HPI Entity related definitions                                     */
-
-struct hpi_entity;
-
-enum e_entity_type {
-       entity_type_null,
-       entity_type_sequence,   /* sequence of potentially heterogeneous TLV entities */
-
-       entity_type_reference,  /* refers to a TLV entity or NULL */
-
-       entity_type_int,        /* 32 bit */
-       entity_type_float,      /* ieee754 binary 32 bit encoding */
-       entity_type_double,
-
-       entity_type_cstring,
-       entity_type_octet,
-       entity_type_ip4_address,
-       entity_type_ip6_address,
-       entity_type_mac_address,
-
-       LAST_ENTITY_TYPE
-};
-
-enum e_entity_role {
-       entity_role_null,
-       entity_role_value,
-       entity_role_classname,
-
-       entity_role_units,
-       entity_role_flags,
-       entity_role_range,
-
-       entity_role_mapping,
-       entity_role_enum,
-
-       entity_role_instance_of,
-       entity_role_depends_on,
-       entity_role_member_of_group,
-       entity_role_value_constraint,
-       entity_role_parameter_port,
-
-       entity_role_block,
-       entity_role_node_group,
-       entity_role_audio_port,
-       entity_role_clock_port,
-       LAST_ENTITY_ROLE
-};
-
 /* skip host side function declarations for
    DSP compile and documentation extraction */
 
-struct hpi_hsubsys {
-       int not_really_used;
-};
-
 #ifndef DISABLE_PRAGMA_PACK1
 #pragma pack(pop)
 #endif
 
-/*////////////////////////////////////////////////////////////////////////// */
+/*****************/
 /* HPI FUNCTIONS */
+/*****************/
 
-/*/////////////////////////// */
-/* DATA and FORMAT and STREAM */
-
+/* Stream */
 u16 hpi_stream_estimate_buffer_size(struct hpi_format *pF,
        u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size);
 
-/*/////////// */
-/* SUB SYSTEM */
-struct hpi_hsubsys *hpi_subsys_create(void
-       );
-
-void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys);
-
-u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys,
-       u32 *pversion);
-
-u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
-       u32 *pversion_ex);
-
-u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
-       u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
-
-u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
-       u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
-
-u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
-       int *pn_num_adapters);
-
-u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
-       u32 *padapter_index, u16 *pw_adapter_type);
-
-u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass);
+/*************/
+/* SubSystem */
+/*************/
 
-u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
-       const char *sz_interface);
+u16 hpi_subsys_get_version_ex(u32 *pversion_ex);
 
-/*///////// */
-/* ADAPTER */
+u16 hpi_subsys_get_num_adapters(int *pn_num_adapters);
 
-u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
+u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index,
+       u16 *pw_adapter_type);
 
-u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
+/***********/
+/* Adapter */
+/***********/
 
-u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
-       u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type);
+u16 hpi_adapter_open(u16 adapter_index);
 
-u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
-       u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
-       u16 *pw_module_type, u32 *ph_module);
+u16 hpi_adapter_close(u16 adapter_index);
 
-u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 adapter_mode);
+u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams,
+       u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number,
+       u16 *pw_adapter_type);
 
-u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 adapter_mode, u16 query_or_set);
-
-u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 *padapter_mode);
-
-u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 *assert_present, char *psz_assert,
-       u16 *pw_line_number);
-
-u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 *assert_present, char *psz_assert,
-       u32 *pline_number, u16 *pw_assert_on_dsp);
-
-u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 assert_id);
-
-u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 capability, u32 key);
-
-u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index);
-
-u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 dsp_address, char *p_bytes, int *count_bytes);
-
-u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 property, u16 paramter1, u16 paramter2);
-
-u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 property, u16 *pw_paramter1,
-       u16 *pw_paramter2);
-
-u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 index, u16 what_to_enumerate,
-       u16 property_index, u32 *psetting);
-
-/*////////////// */
-/* NonVol Memory */
-u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_nv_memory, u16 *pw_size_in_bytes);
-
-u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
-       u32 h_nv_memory, u16 index, u16 *pw_data);
-
-u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
-       u32 h_nv_memory, u16 index, u16 data);
-
-/*////////////// */
-/* Digital I/O */
-u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits);
-
-u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 bit_index, u16 *pw_bit_data);
-
-u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 aw_all_bit_data[4]
-       );
+u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index,
+       u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version,
+       u32 *pserial_number, u16 *pw_module_type, u32 *ph_module);
 
-u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 bit_index, u16 bit_data);
+u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode);
 
-u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 aw_all_bit_data[4]
-       );
+u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
+       u16 query_or_set);
 
-/**********************/
-/* Async Event Object */
-/**********************/
-u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 *ph_async);
+u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode);
 
-u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async);
+u16 hpi_adapter_get_assert2(u16 adapter_index, u16 *p_assert_count,
+       char *psz_assert, u32 *p_param1, u32 *p_param2,
+       u32 *p_dsp_string_addr, u16 *p_processor_id);
 
-u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
-       u16 maximum_events, struct hpi_async_event *p_events,
-       u16 *pw_number_returned);
+u16 hpi_adapter_test_assert(u16 adapter_index, u16 assert_id);
 
-u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
-       u32 h_async, u16 *pw_count);
+u16 hpi_adapter_enable_capability(u16 adapter_index, u16 capability, u32 key);
 
-u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
-       u16 maximum_events, struct hpi_async_event *p_events,
-       u16 *pw_number_returned);
+u16 hpi_adapter_self_test(u16 adapter_index);
 
-/*/////////// */
-/* WATCH-DOG  */
-u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_watchdog);
+u16 hpi_adapter_debug_read(u16 adapter_index, u32 dsp_address, char *p_bytes,
+       int *count_bytes);
 
-u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
-       u32 time_millisec);
+u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1,
+       u16 paramter2);
 
-u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog);
+u16 hpi_adapter_get_property(u16 adapter_index, u16 property,
+       u16 *pw_paramter1, u16 *pw_paramter2);
 
-/**************/
-/* OUT STREAM */
-/**************/
-u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u16 outstream_index, u32 *ph_outstream);
+u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index,
+       u16 what_to_enumerate, u16 property_index, u32 *psetting);
+/*************/
+/* OutStream */
+/*************/
+u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index,
+       u32 *ph_outstream);
 
-u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_close(u32 h_outstream);
 
-u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
-       u32 *psamples_played, u32 *pauxiliary_data_to_play);
+u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state,
+       u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played,
+       u32 *pauxiliary_data_to_play);
 
-u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, const u8 *pb_write_buf, u32 bytes_to_write,
-       const struct hpi_format *p_format);
+u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_write_buf,
+       u32 bytes_to_write, const struct hpi_format *p_format);
 
-u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_start(u32 h_outstream);
 
-u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream);
+u16 hpi_outstream_wait_start(u32 h_outstream);
 
-u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_stop(u32 h_outstream);
 
-u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream);
+u16 hpi_outstream_sinegen(u32 h_outstream);
 
-u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_reset(u32 h_outstream);
 
-u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, struct hpi_format *p_format);
+u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format);
 
-u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, struct hpi_format *p_format);
+u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format);
 
-u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample);
+u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample,
+       u32 punch_out_sample);
 
-u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, short velocity);
+u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity);
 
-u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u16 mode);
+u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode);
 
-u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 *pframes_available);
+u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available);
 
-u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_outstream_ancillary_read(u32 h_outstream,
+       struct hpi_anc_frame *p_anc_frame_buffer,
        u32 anc_frame_buffer_size_in_bytes,
        u32 number_of_ancillary_frames_to_read);
 
-u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 time_scaleX10000);
+u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scaleX10000);
 
-u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 size_in_bytes);
+u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes);
 
-u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream);
+u16 hpi_outstream_host_buffer_free(u32 h_outstream);
 
-u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 h_stream);
+u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream);
 
-u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map);
+u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map,
+       u32 *pinstream_map);
 
-u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream);
+u16 hpi_outstream_group_reset(u32 h_outstream);
 
-/*////////// */
-/* IN_STREAM */
-u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u16 instream_index, u32 *ph_instream);
+/************/
+/* InStream */
+/************/
+u16 hpi_instream_open(u16 adapter_index, u16 instream_index,
+       u32 *ph_instream);
 
-u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_close(u32 h_instream);
 
-u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, const struct hpi_format *p_format);
+u16 hpi_instream_query_format(u32 h_instream,
+       const struct hpi_format *p_format);
 
-u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, const struct hpi_format *p_format);
+u16 hpi_instream_set_format(u32 h_instream,
+       const struct hpi_format *p_format);
 
-u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
-       u8 *pb_read_buf, u32 bytes_to_read);
+u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_read_buf, u32 bytes_to_read);
 
-u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_start(u32 h_instream);
 
-u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream);
+u16 hpi_instream_wait_start(u32 h_instream);
 
-u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_stop(u32 h_instream);
 
-u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_reset(u32 h_instream);
 
-u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
-       u32 *psamples_recorded, u32 *pauxiliary_data_recorded);
+u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size,
+       u32 *pdata_recorded, u32 *psamples_recorded,
+       u32 *pauxiliary_data_recorded);
 
-u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
-       u16 idle_bit);
+u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame,
+       u16 mode, u16 alignment, u16 idle_bit);
 
-u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 *pframe_space);
+u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space);
 
-u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_instream_ancillary_write(u32 h_instream,
+       const struct hpi_anc_frame *p_anc_frame_buffer,
        u32 anc_frame_buffer_size_in_bytes,
        u32 number_of_ancillary_frames_to_write);
 
-u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 size_in_bytes);
+u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes);
 
-u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream);
+u16 hpi_instream_host_buffer_free(u32 h_instream);
 
-u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 h_stream);
+u16 hpi_instream_group_add(u32 h_instream, u32 h_stream);
 
-u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 *poutstream_map, u32 *pinstream_map);
+u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map,
+       u32 *pinstream_map);
 
-u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream);
+u16 hpi_instream_group_reset(u32 h_instream);
 
 /*********/
-/* MIXER */
+/* Mixer */
 /*********/
-u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_mixer);
-
-u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer);
-
-u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
-       u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
-       u16 dst_node_type_index, u16 control_type, u32 *ph_control);
-
-u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
-       u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
-       u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
-       u16 *pw_control_type, u32 *ph_control);
-
-u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
-       enum HPI_MIXER_STORE_COMMAND command, u16 index);
-/*************************/
-/* mixer CONTROLS                */
-/*************************/
-/*************************/
-/* volume control                */
-/*************************/
-u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer);
+
+u16 hpi_mixer_close(u32 h_mixer);
+
+u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type,
+       u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index,
+       u16 control_type, u32 *ph_control);
+
+u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index,
+       u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type,
+       u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control);
+
+u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command,
+       u16 index);
+/************/
+/* Controls */
+/************/
+/******************/
+/* Volume control */
+/******************/
+u16 hpi_volume_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
        );
 
-u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_get_gain(u32 h_control,
        short an_gain0_01dB_out[HPI_MAX_CHANNELS]
        );
 
+u16 hpi_volume_set_mute(u32 h_control, u32 mute);
+
+u16 hpi_volume_get_mute(u32 h_control, u32 *mute);
+
 #define hpi_volume_get_range hpi_volume_query_range
-u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
+u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB,
+       short *max_gain_01dB, short *step_gain_01dB);
 
-u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_volume, u32 *p_channels);
+u16 hpi_volume_query_channels(const u32 h_volume, u32 *p_channels);
 
-u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_auto_fade(u32 h_control,
        short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms);
 
-u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
-       u32 duration_ms, u16 profile);
+u16 hpi_volume_auto_fade_profile(u32 h_control,
+       short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms,
+       u16 profile);
 
-/*************************/
-/* level control         */
-/*************************/
-u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
+/*****************/
+/* Level control */
+/*****************/
+u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB,
+       short *max_gain_01dB, short *step_gain_01dB);
 
-u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
        );
 
-u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_level_get_gain(u32 h_control,
        short an_gain0_01dB_out[HPI_MAX_CHANNELS]
        );
 
-/*************************/
-/* meter control                 */
-/*************************/
-u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_meter, u32 *p_channels);
+/*****************/
+/* Meter control */
+/*****************/
+u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels);
 
-u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_meter_get_peak(u32 h_control,
        short an_peak0_01dB_out[HPI_MAX_CHANNELS]
        );
 
-u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_peak0_01dB_out[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_rms(u32 h_control, short an_peak0_01dB_out[HPI_MAX_CHANNELS]
        );
 
-u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 attack, u16 decay);
+u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay);
 
-u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 attack, u16 decay);
+u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay);
 
-u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *attack, u16 *decay);
+u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *attack, u16 *decay);
 
-u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *attack, u16 *decay);
+u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *attack, u16 *decay);
 
-/*************************/
-/* channel mode control  */
-/*************************/
-u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_mode, const u32 index, u16 *pw_mode);
+/************************/
+/* ChannelMode control */
+/************************/
+u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index,
+       u16 *pw_mode);
 
-u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 mode);
+u16 hpi_channel_mode_set(u32 h_control, u16 mode);
 
-u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 *mode);
+u16 hpi_channel_mode_get(u32 h_control, u16 *mode);
 
-/*************************/
-/* Tuner control                 */
-/*************************/
-u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, u16 *pw_band);
+/*****************/
+/* Tuner control */
+/*****************/
+u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band);
 
-u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 band);
+u16 hpi_tuner_set_band(u32 h_control, u16 band);
 
-u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 *pw_band);
+u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band);
 
-u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq);
+u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index,
+       const u16 band, u32 *pfreq);
 
-u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 freq_ink_hz);
+u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz);
 
-u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pw_freq_ink_hz);
+u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz);
 
-u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *pw_level);
+u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level);
 
-u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short *pw_level);
+u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level);
 
-u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, u16 *pw_gain);
+u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain);
 
-u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short gain);
+u16 hpi_tuner_set_gain(u32 h_control, short gain);
 
-u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *pn_gain);
+u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain);
 
-u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 *pw_status_mask, u16 *pw_status);
+u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status);
 
-u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 mode, u32 value);
+u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value);
 
-u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 mode, u32 *pn_value);
+u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value);
 
-u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *p_rds_data);
+u16 hpi_tuner_get_rds(u32 h_control, char *p_rds_data);
 
-u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis);
+u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index,
+       const u16 band, u32 *pdeemphasis);
 
-u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 deemphasis);
-u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pdeemphasis);
+u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis);
+u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis);
 
-u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, u32 *pbitmap_program);
+u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program);
 
-u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 program);
+u16 hpi_tuner_set_program(u32 h_control, u32 program);
 
-u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 *pprogram);
+u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram);
 
-u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, char *psz_dsp_version, const u32 string_size);
+u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version,
+       const u32 string_size);
 
-u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, char *psz_sdk_version, const u32 string_size);
+u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version,
+       const u32 string_size);
 
-u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pquality);
+u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality);
 
-u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pblend);
+u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend);
 
-u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, const u32 blend);
+u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend);
 
-/****************************/
-/* PADs control             */
-/****************************/
+/***************/
+/* PAD control */
+/***************/
 
-u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, char *psz_string, const u32 string_length);
+u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string,
+       const u32 string_length);
 
-u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *psz_string, const u32 string_length);
+u16 hpi_pad_get_artist(u32 h_control, char *psz_string,
+       const u32 string_length);
 
-u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *psz_string, const u32 string_length);
+u16 hpi_pad_get_title(u32 h_control, char *psz_string,
+       const u32 string_length);
 
-u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *psz_string, const u32 string_length);
+u16 hpi_pad_get_comment(u32 h_control, char *psz_string,
+       const u32 string_length);
 
-u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *ppTY);
+u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY);
 
-u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 *ppI);
+u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI);
 
-u16 HPI_PAD__get_program_type_string(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, const u32 data_type, const u32 pTY, char *psz_string,
-       const u32 string_length);
+u16 hpi_pad_get_program_type_string(u32 h_control, const u32 data_type,
+       const u32 pTY, char *psz_string, const u32 string_length);
 
 /****************************/
 /* AES/EBU Receiver control */
 /****************************/
-u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_aes_rx, const u32 index, u16 *pw_format);
+u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index,
+       u16 *pw_format);
 
-u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source);
+u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 source);
 
-u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_source);
+u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_source);
 
-u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *psample_rate);
+u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate);
 
-u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data);
 
-u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index,
+       u16 *pw_data);
 
-u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_error_data);
+u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data);
 
 /*******************************/
 /* AES/EBU Transmitter control */
 /*******************************/
-u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u32 sample_rate);
+u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate);
 
-u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 data);
+u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data);
 
-u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u16 index, u16 data);
+u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index,
+       u16 data);
 
-u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index,
+       u16 *pw_data);
 
-u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_aes_tx, const u32 index, u16 *pw_format);
+u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index,
+       u16 *pw_format);
 
-u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 output_format);
+u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format);
 
-u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_output_format);
+u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format);
 
 /***********************/
-/* multiplexer control */
+/* Multiplexer control */
 /***********************/
-u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source_node_type, u16 source_node_index);
-
-u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *source_node_type, u16 *source_node_index);
+u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type,
+       u16 source_node_index);
 
-u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 *source_node_type,
+u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type,
        u16 *source_node_index);
 
+u16 hpi_multiplexer_query_source(u32 h_control, u16 index,
+       u16 *source_node_type, u16 *source_node_index);
+
 /***************/
-/* VOX control */
+/* Vox control */
 /***************/
-u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_gain0_01dB);
+u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB);
 
-u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *an_gain0_01dB);
+u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB);
 
 /*********************/
 /* Bitstream control */
 /*********************/
-u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 edge_type);
+u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type);
 
-u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 polarity);
+u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity);
 
-u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity);
+u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity,
+       u16 *pw_data_activity);
 
 /***********************/
 /* SampleClock control */
 /***********************/
 
-u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_clock, const u32 index, u16 *pw_source);
+u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index,
+       u16 *pw_source);
 
-u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source);
+u16 hpi_sample_clock_set_source(u32 h_control, u16 source);
 
-u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_source);
+u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source);
 
-u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_clock, const u32 index, const u32 source,
-       u16 *pw_source_index);
+u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index,
+       const u32 source, u16 *pw_source_index);
 
-u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source_index);
+u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index);
 
-u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_source_index);
+u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index);
 
-u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *psample_rate);
+u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate);
 
-u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_clock, const u32 index, u32 *psource);
+u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
+       u32 *psource);
 
-u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 sample_rate);
+u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate);
 
-u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *psample_rate);
+u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate);
 
-u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 enable);
+u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable);
 
-u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *penable);
+u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable);
 
-u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 lock);
+u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock);
 
-u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *plock);
+u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock);
 
 /***********************/
 /* Microphone control */
 /***********************/
-u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 on_off);
+u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off);
 
-u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_on_off);
+u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off);
 
-/*******************************
-  Parametric Equalizer control
-*******************************/
-u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_number_of_bands, u16 *pw_enabled);
+/********************************/
+/* Parametric Equalizer control */
+/********************************/
+u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands,
+       u16 *pw_enabled);
 
-u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 on_off);
+u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off);
 
-u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
-       short gain0_01dB);
+u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type,
+       u32 frequency_hz, short q100, short gain0_01dB);
 
-u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
-       short *pnQ100, short *pn_gain0_01dB);
+u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type,
+       u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB);
 
-u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, short coeffs[5]
+u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5]
        );
 
-/*******************************
-  Compressor Expander control
-*******************************/
-
-u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 on);
-
-u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pon);
-
-u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short makeup_gain0_01dB);
-
-u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short *pn_makeup_gain0_01dB);
-
-u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u32 index, u32 attack);
-
-u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u32 index, u32 *pw_attack);
-
-u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 decay);
-
-u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 *pw_decay);
-
-u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, short threshold0_01dB);
-
-u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, short *pn_threshold0_01dB);
-
-u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 ratio100);
-
-u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 *pw_ratio100);
-
-/*******************************
-  Cobranet HMI control
-*******************************/
-u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 hmi_address, u32 byte_count, u8 *pb_data);
-
-u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data);
-
-u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pstatus, u32 *preadable_size,
-       u32 *pwriteable_size);
-
-/*Read the current IP address
-*/
-u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pi_paddress);
-
-/* Write the current IP address
-*/
-u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 i_paddress);
-
-/* Read the static IP address
-*/
-u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pi_paddress);
+/*******************************/
+/* Compressor Expander control */
+/*******************************/
 
-/* Write the static IP address
-*/
-u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 i_paddress);
+u16 hpi_compander_set_enable(u32 h_control, u32 on);
 
-/* Read the MAC address
-*/
-u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs);
+u16 hpi_compander_get_enable(u32 h_control, u32 *pon);
 
-/*******************************
-  Tone Detector control
-*******************************/
-u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       u32 *state);
+u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB);
 
-u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       u32 enable);
+u16 hpi_compander_get_makeup_gain(u32 h_control, short *pn_makeup_gain0_01dB);
 
-u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       u32 *enable);
+u16 hpi_compander_set_attack_time_constant(u32 h_control, u32 index,
+       u32 attack);
 
-u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 event_enable);
+u16 hpi_compander_get_attack_time_constant(u32 h_control, u32 index,
+       u32 *pw_attack);
 
-u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 *event_enable);
+u16 hpi_compander_set_decay_time_constant(u32 h_control, u32 index,
+       u32 decay);
 
-u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, int threshold);
+u16 hpi_compander_get_decay_time_constant(u32 h_control, u32 index,
+       u32 *pw_decay);
 
-u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, int *threshold);
+u16 hpi_compander_set_threshold(u32 h_control, u32 index,
+       short threshold0_01dB);
 
-u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 index, u32 *frequency);
+u16 hpi_compander_get_threshold(u32 h_control, u32 index,
+       short *pn_threshold0_01dB);
 
-/*******************************
-  Silence Detector control
-*******************************/
-u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 *state);
+u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100);
 
-u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 enable);
+u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *pw_ratio100);
 
-u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 *enable);
+/********************/
+/* Cobranet control */
+/********************/
+u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
+       u8 *pb_data);
 
-u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 event_enable);
+u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
+       u32 *pbyte_count, u8 *pb_data);
 
-u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 *event_enable);
+u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
+       u32 *preadable_size, u32 *pwriteable_size);
 
-u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 delay);
+u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address);
 
-u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, u32 *delay);
+u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address);
 
-u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, int threshold);
+u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address);
 
-u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 hC, int *threshold);
+u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address);
 
-/*******************************
-  Universal control
-*******************************/
-u16 hpi_entity_find_next(struct hpi_entity *container_entity,
-       enum e_entity_type type, enum e_entity_role role, int recursive_flag,
-       struct hpi_entity **current_match);
+u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs,
+       u32 *p_mac_lsbs);
 
-u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
-       enum e_entity_type type, size_t item_count, void *value_dst_p);
+/*************************/
+/* Tone Detector control */
+/*************************/
+u16 hpi_tone_detector_get_state(u32 hC, u32 *state);
 
-u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
-       size_t *items, enum e_entity_role *role, void **value);
+u16 hpi_tone_detector_set_enable(u32 hC, u32 enable);
 
-u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
-       const size_t item_count, const enum e_entity_role role, void *value,
-       struct hpi_entity **entity);
+u16 hpi_tone_detector_get_enable(u32 hC, u32 *enable);
 
-void hpi_entity_free(struct hpi_entity *entity);
+u16 hpi_tone_detector_set_event_enable(u32 hC, u32 event_enable);
 
-u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       struct hpi_entity **info);
+u16 hpi_tone_detector_get_event_enable(u32 hC, u32 *event_enable);
 
-u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       struct hpi_entity **value);
+u16 hpi_tone_detector_set_threshold(u32 hC, int threshold);
 
-u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       struct hpi_entity *value);
+u16 hpi_tone_detector_get_threshold(u32 hC, int *threshold);
 
-/*/////////// */
-/* DSP CLOCK  */
-/*/////////// */
-u16 hpi_clock_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_dsp_clock);
+u16 hpi_tone_detector_get_frequency(u32 hC, u32 index, u32 *frequency);
 
-u16 hpi_clock_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
-       u16 hour, u16 minute, u16 second, u16 milli_second);
+/****************************/
+/* Silence Detector control */
+/****************************/
+u16 hpi_silence_detector_get_state(u32 hC, u32 *state);
 
-u16 hpi_clock_get_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
-       u16 *pw_hour, u16 *pw_minute, u16 *pw_second, u16 *pw_milli_second);
+u16 hpi_silence_detector_set_enable(u32 hC, u32 enable);
 
-/*/////////// */
-/* PROFILE        */
-/*/////////// */
-u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 profile_index, u32 *ph_profile,
-       u16 *pw_max_profiles);
+u16 hpi_silence_detector_get_enable(u32 hC, u32 *enable);
 
-u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
-       u16 index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
-       u32 *pmax_micro_seconds, u32 *pmin_micro_seconds);
+u16 hpi_silence_detector_set_event_enable(u32 hC, u32 event_enable);
 
-u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
+u16 hpi_silence_detector_get_event_enable(u32 hC, u32 *event_enable);
 
-u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
+u16 hpi_silence_detector_set_delay(u32 hC, u32 delay);
 
-u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
-       u16 index, char *sz_profile_name, u16 profile_name_length);
+u16 hpi_silence_detector_get_delay(u32 hC, u32 *delay);
 
-u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
-       u32 h_profile, u32 *putilization);
+u16 hpi_silence_detector_set_threshold(u32 hC, int threshold);
 
-/*//////////////////// */
-/* UTILITY functions */
+u16 hpi_silence_detector_get_threshold(u32 hC, int *threshold);
+/*********************/
+/* Utility functions */
+/*********************/
 
 u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
        u32 sample_rate, u32 bit_rate, u32 attributes);
 
-/* Until it's verified, this function is for Windows OSs only */
-
-#endif  /*_H_HPI_ */
-/*
-///////////////////////////////////////////////////////////////////////////////
-// See CVS for history.  Last complete set in rev 1.146
-////////////////////////////////////////////////////////////////////////////////
-*/
+#endif  /*_HPI_H_ */
index 1b9bf9395cfe820f87b34fa14d57d5a667a67217..3e3c2ef6efd855f8efc132c3a407c5d46bfe2489 100644 (file)
 #define HPI_HIF_ERROR_MASK      0x4000
 
 /* HPI6000 specific error codes */
+#define HPI6000_ERROR_BASE 900 /* not actually used anywhere */
 
-#define HPI6000_ERROR_BASE                              900
+/* operational/messaging errors */
 #define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT             901
-#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK             902
+
 #define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK             903
 #define HPI6000_ERROR_MSG_GET_ADR                       904
 #define HPI6000_ERROR_RESP_GET_ADR                      905
 #define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32             906
 #define HPI6000_ERROR_MSG_RESP_BLOCKREAD32              907
-#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX             908
+
 #define HPI6000_ERROR_CONTROL_CACHE_PARAMS              909
 
 #define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT            911
@@ -62,7 +63,6 @@
 #define HPI6000_ERROR_SEND_DATA_CMD                     915
 #define HPI6000_ERROR_SEND_DATA_WRITE                   916
 #define HPI6000_ERROR_SEND_DATA_IDLECMD                 917
-#define HPI6000_ERROR_SEND_DATA_VERIFY                  918
 
 #define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT             921
 #define HPI6000_ERROR_GET_DATA_ACK                      922
@@ -76,9 +76,8 @@
 
 #define HPI6000_ERROR_MSG_RESP_GETRESPCMD               961
 #define HPI6000_ERROR_MSG_RESP_IDLECMD                  962
-#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32            963
 
-/* adapter init errors */
+/* Initialisation/bootload errors */
 #define HPI6000_ERROR_UNHANDLED_SUBSYS_ID               930
 
 /* can't access PCI2040 */
@@ -210,6 +209,8 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
 static short create_adapter_obj(struct hpi_adapter_obj *pao,
        u32 *pos_error_code);
 
+static void delete_adapter_obj(struct hpi_adapter_obj *pao);
+
 /* local globals */
 
 static u16 gw_pci_read_asserts;        /* used to count PCI2040 errors */
@@ -217,17 +218,7 @@ static u16 gw_pci_write_asserts;   /* used to count PCI2040 errors */
 
 static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
 {
-
        switch (phm->function) {
-       case HPI_SUBSYS_OPEN:
-       case HPI_SUBSYS_CLOSE:
-       case HPI_SUBSYS_GET_INFO:
-       case HPI_SUBSYS_DRIVER_UNLOAD:
-       case HPI_SUBSYS_DRIVER_LOAD:
-       case HPI_SUBSYS_FIND_ADAPTERS:
-               /* messages that should not get here */
-               phr->error = HPI_ERROR_UNIMPLEMENTED;
-               break;
        case HPI_SUBSYS_CREATE_ADAPTER:
                subsys_create_adapter(phm, phr);
                break;
@@ -243,7 +234,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
 static void control_message(struct hpi_adapter_obj *pao,
        struct hpi_message *phm, struct hpi_response *phr)
 {
-
        switch (phm->function) {
        case HPI_CONTROL_GET_STATE:
                if (pao->has_control_cache) {
@@ -251,7 +241,13 @@ static void control_message(struct hpi_adapter_obj *pao,
                        err = hpi6000_update_control_cache(pao, phm);
 
                        if (err) {
-                               phr->error = err;
+                               if (err >= HPI_ERROR_BACKEND_BASE) {
+                                       phr->error =
+                                               HPI_ERROR_CONTROL_CACHING;
+                                       phr->specific_error = err;
+                               } else {
+                                       phr->error = err;
+                               }
                                break;
                        }
 
@@ -262,16 +258,15 @@ static void control_message(struct hpi_adapter_obj *pao,
                }
                hw_message(pao, phm, phr);
                break;
-       case HPI_CONTROL_GET_INFO:
-               hw_message(pao, phm, phr);
-               break;
        case HPI_CONTROL_SET_STATE:
                hw_message(pao, phm, phr);
-               hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)->
-                       p_cache, phm, phr);
+               hpi_cmn_control_cache_sync_to_msg(((struct hpi_hw_obj *)pao->
+                               priv)->p_cache, phm, phr);
                break;
+
+       case HPI_CONTROL_GET_INFO:
        default:
-               phr->error = HPI_ERROR_INVALID_FUNC;
+               hw_message(pao, phm, phr);
                break;
        }
 }
@@ -280,26 +275,12 @@ static void adapter_message(struct hpi_adapter_obj *pao,
        struct hpi_message *phm, struct hpi_response *phr)
 {
        switch (phm->function) {
-       case HPI_ADAPTER_GET_INFO:
-               hw_message(pao, phm, phr);
-               break;
        case HPI_ADAPTER_GET_ASSERT:
                adapter_get_asserts(pao, phm, phr);
                break;
-       case HPI_ADAPTER_OPEN:
-       case HPI_ADAPTER_CLOSE:
-       case HPI_ADAPTER_TEST_ASSERT:
-       case HPI_ADAPTER_SELFTEST:
-       case HPI_ADAPTER_GET_MODE:
-       case HPI_ADAPTER_SET_MODE:
-       case HPI_ADAPTER_FIND_OBJECT:
-       case HPI_ADAPTER_GET_PROPERTY:
-       case HPI_ADAPTER_SET_PROPERTY:
-       case HPI_ADAPTER_ENUM_PROPERTY:
-               hw_message(pao, phm, phr);
-               break;
+
        default:
-               phr->error = HPI_ERROR_INVALID_FUNC;
+               hw_message(pao, phm, phr);
                break;
        }
 }
@@ -311,7 +292,7 @@ static void outstream_message(struct hpi_adapter_obj *pao,
        case HPI_OSTREAM_HOSTBUFFER_ALLOC:
        case HPI_OSTREAM_HOSTBUFFER_FREE:
                /* Don't let these messages go to the HW function because
-                * they're called without allocating the spinlock.
+                * they're called without locking the spinlock.
                 * For the HPI6000 adapters the HW would return
                 * HPI_ERROR_INVALID_FUNC anyway.
                 */
@@ -331,7 +312,7 @@ static void instream_message(struct hpi_adapter_obj *pao,
        case HPI_ISTREAM_HOSTBUFFER_ALLOC:
        case HPI_ISTREAM_HOSTBUFFER_FREE:
                /* Don't let these messages go to the HW function because
-                * they're called without allocating the spinlock.
+                * they're called without locking the spinlock.
                 * For the HPI6000 adapters the HW would return
                 * HPI_ERROR_INVALID_FUNC anyway.
                 */
@@ -355,7 +336,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
        /* 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->object, phm->function);
+       /*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) {
@@ -433,21 +414,13 @@ static void subsys_create_adapter(struct hpi_message *phm,
        struct hpi_adapter_obj ao;
        struct hpi_adapter_obj *pao;
        u32 os_error_code;
-       short error = 0;
+       u16 err = 0;
        u32 dsp_index = 0;
 
        HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n");
 
        memset(&ao, 0, sizeof(ao));
 
-       /* this HPI only creates adapters for TI/PCI2040 based devices */
-       if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
-               return;
-       if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
-               return;
-       if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040)
-               return;
-
        ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
        if (!ao.priv) {
                HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
@@ -456,16 +429,19 @@ static void subsys_create_adapter(struct hpi_message *phm,
        }
 
        /* create the adapter object based on the resource information */
-       /*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */
        ao.pci = *phm->u.s.resource.r.pci;
 
-       error = create_adapter_obj(&ao, &os_error_code);
-       if (!error)
-               error = hpi_add_adapter(&ao);
-       if (error) {
+       err = create_adapter_obj(&ao, &os_error_code);
+       if (err) {
+               delete_adapter_obj(&ao);
+               if (err >= HPI_ERROR_BACKEND_BASE) {
+                       phr->error = HPI_ERROR_DSP_BOOTLOAD;
+                       phr->specific_error = err;
+               } else {
+                       phr->error = err;
+               }
+
                phr->u.s.data = os_error_code;
-               kfree(ao.priv);
-               phr->error = error;
                return;
        }
        /* need to update paParentAdapter */
@@ -473,7 +449,7 @@ static void subsys_create_adapter(struct hpi_message *phm,
        if (!pao) {
                /* We just added this adapter, why can't we find it!? */
                HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n");
-               phr->error = 950;
+               phr->error = HPI_ERROR_BAD_ADAPTER;
                return;
        }
 
@@ -482,9 +458,8 @@ static void subsys_create_adapter(struct hpi_message *phm,
                phw->ado[dsp_index].pa_parent_adapter = pao;
        }
 
-       phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
+       phr->u.s.adapter_type = ao.adapter_type;
        phr->u.s.adapter_index = ao.index;
-       phr->u.s.num_adapters++;
        phr->error = 0;
 }
 
@@ -492,20 +467,13 @@ static void subsys_delete_adapter(struct hpi_message *phm,
        struct hpi_response *phr)
 {
        struct hpi_adapter_obj *pao = NULL;
-       struct hpi_hw_obj *phw;
 
-       pao = hpi_find_adapter(phm->adapter_index);
+       pao = hpi_find_adapter(phm->obj_index);
        if (!pao)
                return;
 
-       phw = (struct hpi_hw_obj *)pao->priv;
-
-       if (pao->has_control_cache)
-               hpi_free_control_cache(phw->p_cache);
-
+       delete_adapter_obj(pao);
        hpi_delete_adapter(pao);
-       kfree(phw);
-
        phr->error = 0;
 }
 
@@ -519,9 +487,6 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
        u32 control_cache_count = 0;
        struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
 
-       /* init error reporting */
-       pao->dsp_crashed = 0;
-
        /* The PCI2040 has the following address map */
        /* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */
        /* BAR1 - 32K = HPI registers on DSP */
@@ -575,36 +540,36 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
        /* get info about the adapter by asking the adapter */
        /* send a HPI_ADAPTER_GET_INFO message */
        {
-               struct hpi_message hM;
-               struct hpi_response hR0;        /* response from DSP 0 */
-               struct hpi_response hR1;        /* response from DSP 1 */
+               struct hpi_message hm;
+               struct hpi_response hr0;        /* response from DSP 0 */
+               struct hpi_response hr1;        /* response from DSP 1 */
                u16 error = 0;
 
                HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n");
-               memset(&hM, 0, sizeof(hM));
-               hM.type = HPI_TYPE_MESSAGE;
-               hM.size = sizeof(struct hpi_message);
-               hM.object = HPI_OBJ_ADAPTER;
-               hM.function = HPI_ADAPTER_GET_INFO;
-               hM.adapter_index = 0;
-               memset(&hR0, 0, sizeof(hR0));
-               memset(&hR1, 0, sizeof(hR1));
-               hR0.size = sizeof(hR0);
-               hR1.size = sizeof(hR1);
-
-               error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0);
-               if (hR0.error) {
-                       HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error);
-                       return hR0.error;
+               memset(&hm, 0, sizeof(hm));
+               hm.type = HPI_TYPE_MESSAGE;
+               hm.size = sizeof(struct hpi_message);
+               hm.object = HPI_OBJ_ADAPTER;
+               hm.function = HPI_ADAPTER_GET_INFO;
+               hm.adapter_index = 0;
+               memset(&hr0, 0, sizeof(hr0));
+               memset(&hr1, 0, sizeof(hr1));
+               hr0.size = sizeof(hr0);
+               hr1.size = sizeof(hr1);
+
+               error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0);
+               if (hr0.error) {
+                       HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error);
+                       return hr0.error;
                }
                if (phw->num_dsp == 2) {
-                       error = hpi6000_message_response_sequence(pao, 1, &hM,
-                               &hR1);
+                       error = hpi6000_message_response_sequence(pao, 1, &hm,
+                               &hr1);
                        if (error)
                                return error;
                }
-               pao->adapter_type = hR0.u.a.adapter_type;
-               pao->index = hR0.u.a.adapter_index;
+               pao->adapter_type = hr0.u.ax.info.adapter_type;
+               pao->index = hr0.u.ax.info.adapter_index;
        }
 
        memset(&phw->control_cache[0], 0,
@@ -618,22 +583,37 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
                control_cache_count =
                        hpi_read_word(&phw->ado[0],
                        HPI_HIF_ADDR(control_cache_count));
-               pao->has_control_cache = 1;
 
                phw->p_cache =
                        hpi_alloc_control_cache(control_cache_count,
-                       control_cache_size, (struct hpi_control_cache_info *)
+                       control_cache_size, (unsigned char *)
                        &phw->control_cache[0]
                        );
-               if (!phw->p_cache)
-                       pao->has_control_cache = 0;
-       } else
-               pao->has_control_cache = 0;
+               if (phw->p_cache)
+                       pao->has_control_cache = 1;
+       }
 
        HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n",
                pao->adapter_type, pao->index);
        pao->open = 0;  /* upon creation the adapter is closed */
-       return 0;
+
+       if (phw->p_cache)
+               phw->p_cache->adap_idx = pao->index;
+
+       return hpi_add_adapter(pao);
+}
+
+static void delete_adapter_obj(struct hpi_adapter_obj *pao)
+{
+       struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+
+       if (pao->has_control_cache)
+               hpi_free_control_cache(phw->p_cache);
+
+       /* reset DSPs on adapter */
+       iowrite32(0x0003000F, phw->dw2040_HPICSR + HPI_RESET);
+
+       kfree(phw);
 }
 
 /************************************************************************/
@@ -645,11 +625,13 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
 #ifndef HIDE_PCI_ASSERTS
        /* if we have PCI2040 asserts then collect them */
        if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) {
-               phr->u.a.serial_number =
+               phr->u.ax.assert.p1 =
                        gw_pci_read_asserts * 100 + gw_pci_write_asserts;
-               phr->u.a.adapter_index = 1;     /* assert count */
-               phr->u.a.adapter_type = -1;     /* "dsp index" */
-               strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error");
+               phr->u.ax.assert.p2 = 0;
+               phr->u.ax.assert.count = 1;     /* assert count */
+               phr->u.ax.assert.dsp_index = -1;        /* "dsp index" */
+               strcpy(phr->u.ax.assert.sz_message, "PCI2040 error");
+               phr->u.ax.assert.dsp_msg_addr = 0;
                gw_pci_read_asserts = 0;
                gw_pci_write_asserts = 0;
                phr->error = 0;
@@ -686,10 +668,10 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
 
        /* NOTE don't use wAdapterType in this routine. It is not setup yet */
 
-       switch (pao->pci.subsys_device_id) {
+       switch (pao->pci.pci_dev->subsystem_device) {
        case 0x5100:
        case 0x5110:    /* ASI5100 revB or higher with C6711D */
-       case 0x5200:    /* ASI5200 PC_ie version of ASI5100 */
+       case 0x5200:    /* ASI5200 PCIe version of ASI5100 */
        case 0x6100:
        case 0x6200:
                boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
@@ -709,8 +691,9 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
         * note that bits 4..15 are read-only and so should always return zero,
         * even though we wrote 1 to them
         */
-       for (i = 0; i < 1000; i++)
-               delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+       hpios_delay_micro_seconds(1000);
+       delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+
        if (delay != dw2040_reset) {
                HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset,
                        delay);
@@ -743,8 +726,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
        dw2040_reset = dw2040_reset & (~0x00000008);
        iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
        /*delay to allow DSP to get going */
-       for (i = 0; i < 100; i++)
-               delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+       hpios_delay_micro_seconds(100);
 
        /* loop through all DSPs, downloading DSP code */
        for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) {
@@ -783,27 +765,27 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                         */
                        /* bypass PLL */
                        hpi_write_word(pdo, 0x01B7C100, 0x0000);
-                       for (i = 0; i < 100; i++)
-                               delay = ioread32(phw->dw2040_HPICSR +
-                                       HPI_RESET);
+                       hpios_delay_micro_seconds(100);
 
                        /*  ** use default of PLL  x7 ** */
                        /* EMIF = 225/3=75MHz */
                        hpi_write_word(pdo, 0x01B7C120, 0x8002);
+                       hpios_delay_micro_seconds(100);
+
                        /* peri = 225/2 */
                        hpi_write_word(pdo, 0x01B7C11C, 0x8001);
+                       hpios_delay_micro_seconds(100);
+
                        /* cpu  = 225/1 */
                        hpi_write_word(pdo, 0x01B7C118, 0x8000);
-                       /* ~200us delay */
-                       for (i = 0; i < 2000; i++)
-                               delay = ioread32(phw->dw2040_HPICSR +
-                                       HPI_RESET);
+
+                       /* ~2ms delay */
+                       hpios_delay_micro_seconds(2000);
+
                        /* PLL not bypassed */
                        hpi_write_word(pdo, 0x01B7C100, 0x0001);
-                       /* ~200us delay */
-                       for (i = 0; i < 2000; i++)
-                               delay = ioread32(phw->dw2040_HPICSR +
-                                       HPI_RESET);
+                       /* ~2ms delay */
+                       hpios_delay_micro_seconds(2000);
                }
 
                /* test r/w to internal DSP memory
@@ -927,9 +909,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                }
 
                /* delay a little to allow SDRAM and DSP to "get going" */
-
-               for (i = 0; i < 1000; i++)
-                       delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+               hpios_delay_micro_seconds(1000);
 
                /* test access to SDRAM */
                {
@@ -976,7 +956,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
 
                /* write the DSP code down into the DSPs memory */
                /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */
-               dsp_code.ps_dev = pao->pci.p_os_data;
+               dsp_code.ps_dev = pao->pci.pci_dev;
 
                error = hpi_dsp_code_open(boot_load_family, &dsp_code,
                        pos_error_code);
@@ -1073,8 +1053,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
 
                /* step 3. Start code by sending interrupt */
                iowrite32(0x00030003, pdo->prHPI_control);
-               for (i = 0; i < 10000; i++)
-                       delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+               hpios_delay_micro_seconds(10000);
 
                /* wait for a non-zero value in hostcmd -
                 * indicating initialization is complete
@@ -1101,7 +1080,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                         * locks up with a bluescreen (NOT GPF or pagefault).
                         */
                        else
-                               hpios_delay_micro_seconds(1000);
+                               hpios_delay_micro_seconds(10000);
                }
                if (timeout == 0)
                        return HPI6000_ERROR_INIT_NOACK;
@@ -1132,14 +1111,14 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                                mask = 0xFFFFFF00L;
                                /* ASI5100 uses AX6 code, */
                                /* but has no PLD r/w register to test */
-                               if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
-                                               subsys_device_id) ==
+                               if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev->
+                                               subsystem_device) ==
                                        HPI_ADAPTER_FAMILY_ASI(0x5100))
                                        mask = 0x00000000L;
                                /* ASI5200 uses AX6 code, */
                                /* but has no PLD r/w register to test */
-                               if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
-                                               subsys_device_id) ==
+                               if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev->
+                                               subsystem_device) ==
                                        HPI_ADAPTER_FAMILY_ASI(0x5200))
                                        mask = 0x00000000L;
                                break;
@@ -1204,7 +1183,7 @@ static u32 hpi_read_word(struct dsp_obj *pdo, u32 address)
        u32 data = 0;
 
        if (hpi_set_address(pdo, address))
-               return 0;       /*? no way to return error */
+               return 0;       /*? No way to return error */
 
        /* take care of errata in revB DSP (2.0.1) */
        data = ioread32(pdo->prHPI_data);
@@ -1340,10 +1319,6 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
        u32 *p_data;
        u16 error = 0;
 
-       /* does the DSP we are referencing exist? */
-       if (dsp_index >= phw->num_dsp)
-               return HPI6000_ERROR_MSG_INVALID_DSP_INDEX;
-
        ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
        if (ack & HPI_HIF_ERROR_MASK) {
                pao->dsp_crashed++;
@@ -1351,9 +1326,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
        }
        pao->dsp_crashed = 0;
 
-       /* send the message */
-
-       /* get the address and size */
+       /* get the message address and size */
        if (phw->message_buffer_address_on_dsp == 0) {
                timeout = TIMEOUT;
                do {
@@ -1368,10 +1341,9 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
        } else
                address = phw->message_buffer_address_on_dsp;
 
-       /*        dwLength = sizeof(struct hpi_message); */
        length = phm->size;
 
-       /* send it */
+       /* send the message */
        p_data = (u32 *)phm;
        if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data,
                        (u16)length / 4))
@@ -1385,7 +1357,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
        if (ack & HPI_HIF_ERROR_MASK)
                return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK;
 
-       /* get the address and size */
+       /* get the response address */
        if (phw->response_buffer_address_on_dsp == 0) {
                timeout = TIMEOUT;
                do {
@@ -1409,7 +1381,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
        if (!timeout)
                length = sizeof(struct hpi_response);
 
-       /* get it */
+       /* get the response */
        p_data = (u32 *)phr;
        if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data,
                        (u16)length / 4))
@@ -1805,17 +1777,11 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
        hpios_dsplock_lock(pao);
        error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr);
 
-       /* maybe an error response */
-       if (error) {
-               /* something failed in the HPI/DSP interface */
-               phr->error = error;
-               /* just the header of the response is valid */
-               phr->size = sizeof(struct hpi_response_header);
+       if (error)      /* something failed in the HPI/DSP interface */
                goto err;
-       }
 
-       if (phr->error != 0)    /* something failed in the DSP */
-               goto err;
+       if (phr->error) /* something failed in the DSP */
+               goto out;
 
        switch (phm->function) {
        case HPI_OSTREAM_WRITE:
@@ -1827,21 +1793,30 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
                error = hpi6000_get_data(pao, dsp_index, phm, phr);
                break;
        case HPI_ADAPTER_GET_ASSERT:
-               phr->u.a.adapter_index = 0;     /* dsp 0 default */
+               phr->u.ax.assert.dsp_index = 0; /* dsp 0 default */
                if (num_dsp == 2) {
-                       if (!phr->u.a.adapter_type) {
+                       if (!phr->u.ax.assert.count) {
                                /* no assert from dsp 0, check dsp 1 */
                                error = hpi6000_message_response_sequence(pao,
                                        1, phm, phr);
-                               phr->u.a.adapter_index = 1;
+                               phr->u.ax.assert.dsp_index = 1;
                        }
                }
        }
 
-       if (error)
-               phr->error = error;
-
 err:
+       if (error) {
+               if (error >= HPI_ERROR_BACKEND_BASE) {
+                       phr->error = HPI_ERROR_DSP_COMMUNICATION;
+                       phr->specific_error = error;
+               } else {
+                       phr->error = error;
+               }
+
+               /* just the header of the response is valid */
+               phr->size = sizeof(struct hpi_response_header);
+       }
+out:
        hpios_dsplock_unlock(pao);
        return;
 }
index 2672f6591ceb7246b9a0a24ecbfa8f4cf2c46288..620525bdac599f62b6a50204eef8d3771fd01613 100644 (file)
 
 /*****************************************************************************/
 /* HPI6205 specific error codes */
-#define HPI6205_ERROR_BASE                      1000
-/*#define HPI6205_ERROR_MEM_ALLOC 1001 */
+#define HPI6205_ERROR_BASE 1000        /* not actually used anywhere */
+
+/* operational/messaging errors */
+#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT     1015
+#define HPI6205_ERROR_MSG_RESP_TIMEOUT          1016
+
+/* initialization/bootload errors */
 #define HPI6205_ERROR_6205_NO_IRQ               1002
 #define HPI6205_ERROR_6205_INIT_FAILED          1003
-/*#define HPI6205_ERROR_MISSING_DSPCODE 1004 */
-#define HPI6205_ERROR_UNKNOWN_PCI_DEVICE        1005
 #define HPI6205_ERROR_6205_REG                  1006
 #define HPI6205_ERROR_6205_DSPPAGE              1007
-#define HPI6205_ERROR_BAD_DSPINDEX              1008
 #define HPI6205_ERROR_C6713_HPIC                1009
 #define HPI6205_ERROR_C6713_HPIA                1010
 #define HPI6205_ERROR_C6713_PLL                 1011
 #define HPI6205_ERROR_DSP_INTMEM                1012
 #define HPI6205_ERROR_DSP_EXTMEM                1013
 #define HPI6205_ERROR_DSP_PLD                   1014
-#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT     1015
-#define HPI6205_ERROR_MSG_RESP_TIMEOUT          1016
 #define HPI6205_ERROR_6205_EEPROM               1017
 #define HPI6205_ERROR_DSP_EMIF                  1018
 
-#define hpi6205_error(dsp_index, err) (err)
 /*****************************************************************************/
 /* for C6205 PCI i/f */
 /* Host Status Register (HSR) bitfields */
@@ -128,9 +127,6 @@ struct hpi_hw_obj {
        u32 outstream_host_buffer_size[HPI_MAX_STREAMS];
 
        struct consistent_dma_area h_control_cache;
-       struct consistent_dma_area h_async_event_buffer;
-/*      struct hpi_control_cache_single *pControlCache; */
-       struct hpi_async_event *p_async_event_buffer;
        struct hpi_control_cache *p_cache;
 };
 
@@ -208,8 +204,8 @@ static void instream_start(struct hpi_adapter_obj *pao,
 static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
        u32 address);
 
-static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
-       u32 address, u32 data);
+static void boot_loader_write_mem32(struct hpi_adapter_obj *pao,
+       int dsp_index, u32 address, u32 data);
 
 static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao,
        int dsp_index);
@@ -229,17 +225,7 @@ 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)
 {
-
        switch (phm->function) {
-       case HPI_SUBSYS_OPEN:
-       case HPI_SUBSYS_CLOSE:
-       case HPI_SUBSYS_GET_INFO:
-       case HPI_SUBSYS_DRIVER_UNLOAD:
-       case HPI_SUBSYS_DRIVER_LOAD:
-       case HPI_SUBSYS_FIND_ADAPTERS:
-               /* messages that should not get here */
-               phr->error = HPI_ERROR_UNIMPLEMENTED;
-               break;
        case HPI_SUBSYS_CREATE_ADAPTER:
                subsys_create_adapter(phm, phr);
                break;
@@ -257,15 +243,22 @@ static void control_message(struct hpi_adapter_obj *pao,
 {
 
        struct hpi_hw_obj *phw = pao->priv;
+       u16 pending_cache_error = 0;
 
        switch (phm->function) {
        case HPI_CONTROL_GET_STATE:
                if (pao->has_control_cache) {
-                       rmb();  /* make sure we see updates DM_aed from DSP */
-                       if (hpi_check_control_cache(phw->p_cache, phm, phr))
+                       rmb();  /* make sure we see updates DMAed from DSP */
+                       if (hpi_check_control_cache(phw->p_cache, phm, phr)) {
                                break;
+                       } else if (phm->u.c.attribute == HPI_METER_PEAK) {
+                               pending_cache_error =
+                                       HPI_ERROR_CONTROL_CACHING;
+                       }
                }
                hw_message(pao, phm, phr);
+               if (pending_cache_error && !phr->error)
+                       phr->error = pending_cache_error;
                break;
        case HPI_CONTROL_GET_INFO:
                hw_message(pao, phm, phr);
@@ -273,7 +266,8 @@ static void control_message(struct hpi_adapter_obj *pao,
        case HPI_CONTROL_SET_STATE:
                hw_message(pao, phm, phr);
                if (pao->has_control_cache)
-                       hpi_sync_control_cache(phw->p_cache, phm, phr);
+                       hpi_cmn_control_cache_sync_to_msg(phw->p_cache, phm,
+                               phr);
                break;
        default:
                phr->error = HPI_ERROR_INVALID_FUNC;
@@ -296,9 +290,9 @@ static void outstream_message(struct hpi_adapter_obj *pao,
 {
 
        if (phm->obj_index >= HPI_MAX_STREAMS) {
-               phr->error = HPI_ERROR_INVALID_STREAM;
+               phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
                HPI_DEBUG_LOG(WARNING,
-                       "message referencing invalid stream %d "
+                       "Message referencing invalid stream %d "
                        "on adapter index %d\n", phm->obj_index,
                        phm->adapter_index);
                return;
@@ -340,9 +334,9 @@ static void instream_message(struct hpi_adapter_obj *pao,
 {
 
        if (phm->obj_index >= HPI_MAX_STREAMS) {
-               phr->error = HPI_ERROR_INVALID_STREAM;
+               phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
                HPI_DEBUG_LOG(WARNING,
-                       "message referencing invalid stream %d "
+                       "Message referencing invalid stream %d "
                        "on adapter index %d\n", phm->obj_index,
                        phm->adapter_index);
                return;
@@ -385,8 +379,8 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
         * 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->object,
-               phm->function);
+       /* 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) {
@@ -411,8 +405,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
 
        /* Init default response  */
        if (phm->function != HPI_SUBSYS_CREATE_ADAPTER)
-               hpi_init_response(phr, phm->object, phm->function,
-                       HPI_ERROR_PROCESSING_MESSAGE);
+               phr->error = HPI_ERROR_PROCESSING_MESSAGE;
 
        HPI_DEBUG_LOG(VERBOSE, "start of switch\n");
        switch (phm->type) {
@@ -423,9 +416,6 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
                        break;
 
                case HPI_OBJ_ADAPTER:
-                       phr->size =
-                               sizeof(struct hpi_response_header) +
-                               sizeof(struct hpi_adapter_res);
                        adapter_message(pao, phm, phr);
                        break;
 
@@ -474,14 +464,6 @@ static void subsys_create_adapter(struct hpi_message *phm,
 
        memset(&ao, 0, sizeof(ao));
 
-       /* this HPI only creates adapters for TI/PCI devices */
-       if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
-               return;
-       if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
-               return;
-       if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205)
-               return;
-
        ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
        if (!ao.priv) {
                HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
@@ -491,18 +473,20 @@ static void subsys_create_adapter(struct hpi_message *phm,
 
        ao.pci = *phm->u.s.resource.r.pci;
        err = create_adapter_obj(&ao, &os_error_code);
-       if (!err)
-               err = hpi_add_adapter(&ao);
        if (err) {
-               phr->u.s.data = os_error_code;
                delete_adapter_obj(&ao);
-               phr->error = err;
+               if (err >= HPI_ERROR_BACKEND_BASE) {
+                       phr->error = HPI_ERROR_DSP_BOOTLOAD;
+                       phr->specific_error = err;
+               } else {
+                       phr->error = err;
+               }
+               phr->u.s.data = os_error_code;
                return;
        }
 
-       phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
+       phr->u.s.adapter_type = ao.adapter_type;
        phr->u.s.adapter_index = ao.index;
-       phr->u.s.num_adapters++;
        phr->error = 0;
 }
 
@@ -513,7 +497,7 @@ static void subsys_delete_adapter(struct hpi_message *phm,
        struct hpi_adapter_obj *pao;
        struct hpi_hw_obj *phw;
 
-       pao = hpi_find_adapter(phm->adapter_index);
+       pao = hpi_find_adapter(phm->obj_index);
        if (!pao) {
                phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
                return;
@@ -526,6 +510,7 @@ static void subsys_delete_adapter(struct hpi_message *phm,
        iowrite32(C6205_HDCR_WARMRESET, phw->prHDCR);
 
        delete_adapter_obj(pao);
+       hpi_delete_adapter(pao);
        phr->error = 0;
 }
 
@@ -538,10 +523,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
        struct hpi_hw_obj *phw = pao->priv;
        struct bus_master_interface *interface;
        u32 phys_addr;
-#ifndef HPI6205_NO_HSR_POLL
-       u32 time_out = HPI6205_TIMEOUT;
-       u32 temp1;
-#endif
        int i;
        u16 err;
 
@@ -566,7 +547,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
 
        if (hpios_locked_mem_alloc(&phw->h_locked_mem,
                        sizeof(struct bus_master_interface),
-                       pao->pci.p_os_data))
+                       pao->pci.pci_dev))
                phw->p_interface_buffer = NULL;
        else if (hpios_locked_mem_get_virt_addr(&phw->h_locked_mem,
                        (void *)&phw->p_interface_buffer))
@@ -591,49 +572,29 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
 
        /* allow boot load even if mem alloc wont work */
        if (!phw->p_interface_buffer)
-               return hpi6205_error(0, HPI_ERROR_MEMORY_ALLOC);
+               return HPI_ERROR_MEMORY_ALLOC;
 
        interface = phw->p_interface_buffer;
 
-#ifndef HPI6205_NO_HSR_POLL
-       /* wait for first interrupt indicating the DSP init is done */
-       time_out = HPI6205_TIMEOUT * 10;
-       temp1 = 0;
-       while (((temp1 & C6205_HSR_INTSRC) == 0) && --time_out)
-               temp1 = ioread32(phw->prHSR);
-
-       if (temp1 & C6205_HSR_INTSRC)
-               HPI_DEBUG_LOG(INFO,
-                       "interrupt confirming DSP code running OK\n");
-       else {
-               HPI_DEBUG_LOG(ERROR,
-                       "timed out waiting for interrupt "
-                       "confirming DSP code running\n");
-               return hpi6205_error(0, HPI6205_ERROR_6205_NO_IRQ);
-       }
-
-       /* reset the interrupt */
-       iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
-
        /* make sure the DSP has started ok */
        if (!wait_dsp_ack(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) {
                HPI_DEBUG_LOG(ERROR, "timed out waiting reset state \n");
-               return hpi6205_error(0, HPI6205_ERROR_6205_INIT_FAILED);
+               return HPI6205_ERROR_6205_INIT_FAILED;
        }
        /* Note that *pao, *phw are zeroed after allocation,
         * so pointers and flags are NULL by default.
         * Allocate bus mastering control cache buffer and tell the DSP about it
         */
        if (interface->control_cache.number_of_controls) {
-               void *p_control_cache_virtual;
+               u8 *p_control_cache_virtual;
 
                err = hpios_locked_mem_alloc(&phw->h_control_cache,
                        interface->control_cache.size_in_bytes,
-                       pao->pci.p_os_data);
+                       pao->pci.pci_dev);
                if (!err)
                        err = hpios_locked_mem_get_virt_addr(&phw->
-                               h_control_cache, &p_control_cache_virtual);
+                               h_control_cache,
+                               (void *)&p_control_cache_virtual);
                if (!err) {
                        memset(p_control_cache_virtual, 0,
                                interface->control_cache.size_in_bytes);
@@ -642,7 +603,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
                                hpi_alloc_control_cache(interface->
                                control_cache.number_of_controls,
                                interface->control_cache.size_in_bytes,
-                               (struct hpi_control_cache_info *)
                                p_control_cache_virtual);
                        if (!phw->p_cache)
                                err = HPI_ERROR_MEMORY_ALLOC;
@@ -662,78 +622,56 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
                        pao->has_control_cache = 0;
                }
        }
-       /* allocate bus mastering async buffer and tell the DSP about it */
-       if (interface->async_buffer.b.size) {
-               err = hpios_locked_mem_alloc(&phw->h_async_event_buffer,
-                       interface->async_buffer.b.size *
-                       sizeof(struct hpi_async_event), pao->pci.p_os_data);
-               if (!err)
-                       err = hpios_locked_mem_get_virt_addr
-                               (&phw->h_async_event_buffer, (void *)
-                               &phw->p_async_event_buffer);
-               if (!err)
-                       memset((void *)phw->p_async_event_buffer, 0,
-                               interface->async_buffer.b.size *
-                               sizeof(struct hpi_async_event));
-               if (!err) {
-                       err = hpios_locked_mem_get_phys_addr
-                               (&phw->h_async_event_buffer, &phys_addr);
-                       interface->async_buffer.physical_address32 =
-                               phys_addr;
-               }
-               if (err) {
-                       if (hpios_locked_mem_valid(&phw->
-                                       h_async_event_buffer)) {
-                               hpios_locked_mem_free
-                                       (&phw->h_async_event_buffer);
-                               phw->p_async_event_buffer = NULL;
-                       }
-               }
-       }
        send_dsp_command(phw, H620_HIF_IDLE);
 
        {
-               struct hpi_message hM;
-               struct hpi_response hR;
+               struct hpi_message hm;
+               struct hpi_response hr;
                u32 max_streams;
 
                HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n");
-               memset(&hM, 0, sizeof(hM));
-               hM.type = HPI_TYPE_MESSAGE;
-               hM.size = sizeof(hM);
-               hM.object = HPI_OBJ_ADAPTER;
-               hM.function = HPI_ADAPTER_GET_INFO;
-               hM.adapter_index = 0;
-               memset(&hR, 0, sizeof(hR));
-               hR.size = sizeof(hR);
-
-               err = message_response_sequence(pao, &hM, &hR);
+               memset(&hm, 0, sizeof(hm));
+               hm.type = HPI_TYPE_MESSAGE;
+               hm.size = sizeof(hm);
+               hm.object = HPI_OBJ_ADAPTER;
+               hm.function = HPI_ADAPTER_GET_INFO;
+               hm.adapter_index = 0;
+               memset(&hr, 0, sizeof(hr));
+               hr.size = sizeof(hr);
+
+               err = message_response_sequence(pao, &hm, &hr);
                if (err) {
                        HPI_DEBUG_LOG(ERROR, "message transport error %d\n",
                                err);
                        return err;
                }
-               if (hR.error)
-                       return hR.error;
+               if (hr.error)
+                       return hr.error;
 
-               pao->adapter_type = hR.u.a.adapter_type;
-               pao->index = hR.u.a.adapter_index;
+               pao->adapter_type = hr.u.ax.info.adapter_type;
+               pao->index = hr.u.ax.info.adapter_index;
 
-               max_streams = hR.u.a.num_outstreams + hR.u.a.num_instreams;
+               max_streams =
+                       hr.u.ax.info.num_outstreams +
+                       hr.u.ax.info.num_instreams;
 
                hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams,
-                       65536, pao->pci.p_os_data);
+                       65536, pao->pci.pci_dev);
 
                HPI_DEBUG_LOG(VERBOSE,
                        "got adapter info type %x index %d serial %d\n",
-                       hR.u.a.adapter_type, hR.u.a.adapter_index,
-                       hR.u.a.serial_number);
+                       hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index,
+                       hr.u.ax.info.serial_number);
        }
 
        pao->open = 0;  /* upon creation the adapter is closed */
 
+       if (phw->p_cache)
+               phw->p_cache->adap_idx = pao->index;
+
        HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
-       return 0;
+
+       return hpi_add_adapter(pao);
 }
 
 /** Free memory areas allocated by adapter
@@ -747,11 +685,6 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
 
        phw = pao->priv;
 
-       if (hpios_locked_mem_valid(&phw->h_async_event_buffer)) {
-               hpios_locked_mem_free(&phw->h_async_event_buffer);
-               phw->p_async_event_buffer = NULL;
-       }
-
        if (hpios_locked_mem_valid(&phw->h_control_cache)) {
                hpios_locked_mem_free(&phw->h_control_cache);
                hpi_free_control_cache(phw->p_cache);
@@ -776,12 +709,14 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
                        phw->outstream_host_buffer_size[i] = 0;
                }
 
-       hpios_locked_mem_unprepare(pao->pci.p_os_data);
+       hpios_locked_mem_unprepare(pao->pci.pci_dev);
 
-       hpi_delete_adapter(pao);
        kfree(phw);
 }
 
+/*****************************************************************************/
+/* Adapter functions */
+
 /*****************************************************************************/
 /* OutStream Host buffer functions */
 
@@ -824,7 +759,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
 
                err = hpios_locked_mem_alloc(&phw->outstream_host_buffers
                        [phm->obj_index], phm->u.d.u.buffer.buffer_size,
-                       pao->pci.p_os_data);
+                       pao->pci.pci_dev);
 
                if (err) {
                        phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -861,7 +796,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
                if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
                                buffer_size - 1)) {
                        HPI_DEBUG_LOG(ERROR,
-                               "buffer size must be 2^N not %d\n",
+                               "Buffer size must be 2^N not %d\n",
                                phm->u.d.u.buffer.buffer_size);
                        phr->error = HPI_ERROR_INVALID_DATASIZE;
                        return;
@@ -875,6 +810,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
                status->dSP_index = 0;
                status->host_index = status->dSP_index;
                status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
+               status->auxiliary_data_available = 0;
 
                hw_message(pao, phm, phr);
 
@@ -966,51 +902,6 @@ static void outstream_write(struct hpi_adapter_obj *pao,
        hpi_init_response(phr, phm->object, phm->function, 0);
        status = &interface->outstream_host_buffer_status[phm->obj_index];
 
-       if (phw->flag_outstream_just_reset[phm->obj_index]) {
-               /* First OutStremWrite() call following reset will write data to the
-                  adapter's buffers, reducing delay before stream can start. The DSP
-                  takes care of setting the stream data format using format information
-                  embedded in phm.
-                */
-               int partial_write = 0;
-               unsigned int original_size = 0;
-
-               phw->flag_outstream_just_reset[phm->obj_index] = 0;
-
-               /* Send the first buffer to the DSP the old way. */
-               /* Limit size of first transfer - */
-               /* expect that this will not usually be triggered. */
-               if (phm->u.d.u.data.data_size > HPI6205_SIZEOF_DATA) {
-                       partial_write = 1;
-                       original_size = phm->u.d.u.data.data_size;
-                       phm->u.d.u.data.data_size = HPI6205_SIZEOF_DATA;
-               }
-               /* write it */
-               phm->function = HPI_OSTREAM_WRITE;
-               hw_message(pao, phm, phr);
-
-               if (phr->error)
-                       return;
-
-               /* update status information that the DSP would typically
-                * update (and will update next time the DSP
-                * buffer update task reads data from the host BBM buffer)
-                */
-               status->auxiliary_data_available = phm->u.d.u.data.data_size;
-               status->host_index += phm->u.d.u.data.data_size;
-               status->dSP_index += phm->u.d.u.data.data_size;
-
-               /* if we did a full write, we can return from here. */
-               if (!partial_write)
-                       return;
-
-               /* tweak buffer parameters and let the rest of the */
-               /* buffer land in internal BBM buffer */
-               phm->u.d.u.data.data_size =
-                       original_size - HPI6205_SIZEOF_DATA;
-               phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA;
-       }
-
        space_available = outstream_get_space_available(status);
        if (space_available < phm->u.d.u.data.data_size) {
                phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -1047,6 +938,24 @@ static void outstream_write(struct hpi_adapter_obj *pao,
                memcpy(p_bbm_data, p_app_data + l_first_write,
                        phm->u.d.u.data.data_size - l_first_write);
        }
+
+       /*
+        * This version relies on the DSP code triggering an OStream buffer
+        * update immediately following a SET_FORMAT call. The host has
+        * already written data into the BBM buffer, but the DSP won't know
+        * about it until dwHostIndex is adjusted.
+        */
+       if (phw->flag_outstream_just_reset[phm->obj_index]) {
+               /* Format can only change after reset. Must tell DSP. */
+               u16 function = phm->function;
+               phw->flag_outstream_just_reset[phm->obj_index] = 0;
+               phm->function = HPI_OSTREAM_SET_FORMAT;
+               hw_message(pao, phm, phr);      /* send the format to the DSP */
+               phm->function = function;
+               if (phr->error)
+                       return;
+       }
+
        status->host_index += phm->u.d.u.data.data_size;
 }
 
@@ -1132,7 +1041,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
 
                err = hpios_locked_mem_alloc(&phw->instream_host_buffers[phm->
                                obj_index], phm->u.d.u.buffer.buffer_size,
-                       pao->pci.p_os_data);
+                       pao->pci.pci_dev);
 
                if (err) {
                        phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -1163,7 +1072,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
                if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
                                buffer_size - 1)) {
                        HPI_DEBUG_LOG(ERROR,
-                               "buffer size must be 2^N not %d\n",
+                               "Buffer size must be 2^N not %d\n",
                                phm->u.d.u.buffer.buffer_size);
                        phr->error = HPI_ERROR_INVALID_DATASIZE;
                        return;
@@ -1178,8 +1087,10 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
                status->dSP_index = 0;
                status->host_index = status->dSP_index;
                status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
+               status->auxiliary_data_available = 0;
 
                hw_message(pao, phm, phr);
+
                if (phr->error
                        && hpios_locked_mem_valid(&phw->
                                instream_host_buffers[phm->obj_index])) {
@@ -1344,33 +1255,36 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
        struct hpi_hw_obj *phw = pao->priv;
        struct dsp_code dsp_code;
        u16 boot_code_id[HPI6205_MAX_FILES_TO_LOAD];
-       u16 firmware_id = pao->pci.subsys_device_id;
        u32 temp;
        int dsp = 0, i = 0;
        u16 err = 0;
 
        boot_code_id[0] = HPI_ADAPTER_ASI(0x6205);
 
-       /* special cases where firmware_id != subsys ID */
-       switch (firmware_id) {
+       boot_code_id[1] = pao->pci.pci_dev->subsystem_device;
+       boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(boot_code_id[1]);
+
+       /* fix up cases where bootcode id[1] != subsys id */
+       switch (boot_code_id[1]) {
        case HPI_ADAPTER_FAMILY_ASI(0x5000):
-               boot_code_id[0] = firmware_id;
-               firmware_id = 0;
+               boot_code_id[0] = boot_code_id[1];
+               boot_code_id[1] = 0;
                break;
        case HPI_ADAPTER_FAMILY_ASI(0x5300):
        case HPI_ADAPTER_FAMILY_ASI(0x5400):
        case HPI_ADAPTER_FAMILY_ASI(0x6300):
-               firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6400);
+               boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
                break;
        case HPI_ADAPTER_FAMILY_ASI(0x5600):
        case HPI_ADAPTER_FAMILY_ASI(0x6500):
-               firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600);
+               boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
                break;
        case HPI_ADAPTER_FAMILY_ASI(0x8800):
-               firmware_id = HPI_ADAPTER_FAMILY_ASI(0x8900);
+               boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x8900);
+               break;
+       default:
                break;
        }
-       boot_code_id[1] = firmware_id;
 
        /* reset DSP by writing a 1 to the WARMRESET bit */
        temp = C6205_HDCR_WARMRESET;
@@ -1381,7 +1295,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
        temp = ioread32(phw->prHSR);
        if ((temp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) !=
                C6205_HSR_EEREAD)
-               return hpi6205_error(0, HPI6205_ERROR_6205_EEPROM);
+               return HPI6205_ERROR_6205_EEPROM;
        temp |= 0x04;
        /* disable PINTA interrupt */
        iowrite32(temp, phw->prHSR);
@@ -1389,27 +1303,27 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
        /* check control register reports PCI boot mode */
        temp = ioread32(phw->prHDCR);
        if (!(temp & C6205_HDCR_PCIBOOT))
-               return hpi6205_error(0, HPI6205_ERROR_6205_REG);
+               return HPI6205_ERROR_6205_REG;
 
-       /* try writing a couple of numbers to the DSP page register */
+       /* try writing a few numbers to the DSP page register */
        /* and reading them back. */
-       temp = 1;
+       temp = 3;
        iowrite32(temp, phw->prDSPP);
        if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
-               return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+               return HPI6205_ERROR_6205_DSPPAGE;
        temp = 2;
        iowrite32(temp, phw->prDSPP);
        if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
-               return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
-       temp = 3;
+               return HPI6205_ERROR_6205_DSPPAGE;
+       temp = 1;
        iowrite32(temp, phw->prDSPP);
        if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
-               return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+               return HPI6205_ERROR_6205_DSPPAGE;
        /* reset DSP page to the correct number */
        temp = 0;
        iowrite32(temp, phw->prDSPP);
        if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
-               return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+               return HPI6205_ERROR_6205_DSPPAGE;
        phw->dsp_page = 0;
 
        /* release 6713 from reset before 6205 is bootloaded.
@@ -1455,7 +1369,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                        return err;
 
                /* write the DSP code down into the DSPs memory */
-               dsp_code.ps_dev = pao->pci.p_os_data;
+               dsp_code.ps_dev = pao->pci.pci_dev;
                err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code,
                        pos_error_code);
                if (err)
@@ -1484,10 +1398,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                        if (err)
                                break;
                        for (i = 0; i < (int)length; i++) {
-                               err = boot_loader_write_mem32(pao, dsp,
-                                       address, *pcode);
-                               if (err)
-                                       break;
+                               boot_loader_write_mem32(pao, dsp, address,
+                                       *pcode);
                                /* dummy read every 4 words */
                                /* for 6205 advisory 1.4.4 */
                                if (i % 4 == 0)
@@ -1561,7 +1473,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
                host_mailbox_address_on_dsp = 0x80000000;
                while ((physicalPC_iaddress != physicalPC_iaddress_verify)
                        && time_out--) {
-                       err = boot_loader_write_mem32(pao, 0,
+                       boot_loader_write_mem32(pao, 0,
                                host_mailbox_address_on_dsp,
                                physicalPC_iaddress);
                        physicalPC_iaddress_verify =
@@ -1631,11 +1543,10 @@ static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
        return data;
 }
 
-static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
-       u32 address, u32 data)
+static void boot_loader_write_mem32(struct hpi_adapter_obj *pao,
+       int dsp_index, u32 address, u32 data)
 {
        struct hpi_hw_obj *phw = pao->priv;
-       u16 err = 0;
        __iomem u32 *p_data;
        /*      u32 dwVerifyData=0; */
 
@@ -1675,15 +1586,11 @@ static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
 
                /* dummy read every 4 words for 6205 advisory 1.4.4 */
                boot_loader_read_mem32(pao, 0, 0);
-       } else
-               err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
-       return err;
+       }
 }
 
 static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
 {
-       u16 err = 0;
-
        if (dsp_index == 0) {
                u32 setting;
 
@@ -1711,8 +1618,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                boot_loader_write_mem32(pao, dsp_index, 0x01800008, setting);
                if (setting != boot_loader_read_mem32(pao, dsp_index,
                                0x01800008))
-                       return hpi6205_error(dsp_index,
-                               HPI6205_ERROR_DSP_EMIF);
+                       return HPI6205_ERROR_DSP_EMIF;
 
                /* EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI, */
                /* which occupies D15..0. 6713 starts at 27MHz, so need */
@@ -1725,8 +1631,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                boot_loader_write_mem32(pao, dsp_index, 0x01800004, setting);
                if (setting != boot_loader_read_mem32(pao, dsp_index,
                                0x01800004))
-                       return hpi6205_error(dsp_index,
-                               HPI6205_ERROR_DSP_EMIF);
+                       return HPI6205_ERROR_DSP_EMIF;
 
                /* EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI, */
                /* which occupies D15..0. 6713 starts at 27MHz, so need */
@@ -1738,8 +1643,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                boot_loader_write_mem32(pao, dsp_index, 0x01800010, setting);
                if (setting != boot_loader_read_mem32(pao, dsp_index,
                                0x01800010))
-                       return hpi6205_error(dsp_index,
-                               HPI6205_ERROR_DSP_EMIF);
+                       return HPI6205_ERROR_DSP_EMIF;
 
                /* EMIF CE3 setup - 32 bit async. */
                /* This is the PLD on the ASI5000 cards only */
@@ -1750,8 +1654,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                boot_loader_write_mem32(pao, dsp_index, 0x01800014, setting);
                if (setting != boot_loader_read_mem32(pao, dsp_index,
                                0x01800014))
-                       return hpi6205_error(dsp_index,
-                               HPI6205_ERROR_DSP_EMIF);
+                       return HPI6205_ERROR_DSP_EMIF;
 
                /* set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank) */
                /*  need to use this else DSP code crashes? */
@@ -1775,12 +1678,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                read_data =
                        0xFFF7 & boot_loader_read_mem32(pao, 0, HPICL_ADDR);
                if (write_data != read_data) {
-                       err = hpi6205_error(dsp_index,
-                               HPI6205_ERROR_C6713_HPIC);
                        HPI_DEBUG_LOG(ERROR, "HPICL %x %x\n", write_data,
                                read_data);
-
-                       return err;
+                       return HPI6205_ERROR_C6713_HPIC;
                }
                /* HPIA - walking ones test */
                write_data = 1;
@@ -1798,11 +1698,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                                                HPIAH_ADDR))
                                << 16);
                        if (read_data != write_data) {
-                               err = hpi6205_error(dsp_index,
-                                       HPI6205_ERROR_C6713_HPIA);
                                HPI_DEBUG_LOG(ERROR, "HPIA %x %x\n",
                                        write_data, read_data);
-                               return err;
+                               return HPI6205_ERROR_C6713_HPIA;
                        }
                        write_data = write_data << 1;
                }
@@ -1847,30 +1745,81 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
                /* PLL should not be bypassed! */
                if ((boot_loader_read_mem32(pao, dsp_index, 0x01B7C100) & 0xF)
                        != 0x0001) {
-                       err = hpi6205_error(dsp_index,
-                               HPI6205_ERROR_C6713_PLL);
-                       return err;
+                       return HPI6205_ERROR_C6713_PLL;
                }
                /* setup C67x EMIF  (note this is the only use of
                   BAR1 via BootLoader_WriteMem32) */
                boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_GCTL,
                        0x000034A8);
+
+               /* EMIF CE0 setup - 2Mx32 Sync DRAM
+                  31..28       Wr setup
+                  27..22       Wr strobe
+                  21..20       Wr hold
+                  19..16       Rd setup
+                  15..14       -
+                  13..8        Rd strobe
+                  7..4         MTYPE   0011            Sync DRAM 32bits
+                  3            Wr hold MSB
+                  2..0         Rd hold
+                */
                boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_CE0,
                        0x00000030);
+
+               /* EMIF SDRAM Extension
+                  0x00
+                  31-21        0000b 0000b 000b
+                  20           WR2RD = 2cycles-1  = 1b
+
+                  19-18        WR2DEAC = 3cycle-1 = 10b
+                  17           WR2WR = 2cycle-1   = 1b
+                  16-15        R2WDQM = 4cycle-1  = 11b
+                  14-12        RD2WR = 6cycles-1  = 101b
+
+                  11-10        RD2DEAC = 4cycle-1 = 11b
+                  9            RD2RD = 2cycle-1   = 1b
+                  8-7          THZP = 3cycle-1    = 10b
+                  6-5          TWR  = 2cycle-1    = 01b (tWR = 17ns)
+                  4            TRRD = 2cycle      = 0b  (tRRD = 14ns)
+                  3-1          TRAS = 5cycle-1    = 100b (Tras=42ns)
+                  1            CAS latency = 3cyc = 1b
+                  (for Micron 2M32-7 operating at 100MHz)
+                */
                boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMEXT,
                        0x001BDF29);
+
+               /* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank)
+                  31           -       0b       -
+                  30           SDBSZ   1b              4 bank
+                  29..28       SDRSZ   00b             11 row address pins
+
+                  27..26       SDCSZ   01b             8 column address pins
+                  25           RFEN    1b              refersh enabled
+                  24           INIT    1b              init SDRAM!
+
+                  23..20       TRCD    0001b                   (Trcd/Tcyc)-1 = (20/10)-1 = 1
+
+                  19..16       TRP     0001b                   (Trp/Tcyc)-1 = (20/10)-1 = 1
+
+                  15..12       TRC     0110b                   (Trc/Tcyc)-1 = (70/10)-1 = 6
+
+                  11..0        -       0000b 0000b 0000b
+                */
                boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMCTL,
-                       0x47117000);
+                       0x47116000);
+
+               /* SDRAM refresh timing
+                  Need 4,096 refresh cycles every 64ms = 15.625us = 1562cycles of 100MHz = 0x61A
+                */
                boot_loader_write_mem32(pao, dsp_index,
                        C6713_EMIF_SDRAMTIMING, 0x00000410);
 
                hpios_delay_micro_seconds(1000);
        } else if (dsp_index == 2) {
                /* DSP 2 is a C6713 */
+       }
 
-       } else
-               err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
-       return err;
+       return 0;
 }
 
 static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
@@ -1896,7 +1845,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
                                test_addr);
                        if (data != test_data) {
                                HPI_DEBUG_LOG(VERBOSE,
-                                       "memtest error details  "
+                                       "Memtest error details  "
                                        "%08x %08x %08x %i\n", test_addr,
                                        test_data, data, dsp_index);
                                return 1;       /* error */
@@ -1916,7 +1865,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
                data = boot_loader_read_mem32(pao, dsp_index, test_addr);
                if (data != test_data) {
                        HPI_DEBUG_LOG(VERBOSE,
-                               "memtest error details  "
+                               "Memtest error details  "
                                "%08x %08x %08x %i\n", test_addr, test_data,
                                data, dsp_index);
                        return 1;       /* error */
@@ -1946,8 +1895,8 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
                        /* 64K data mem */
                        err = boot_loader_test_memory(pao, dsp_index,
                                0x80000000, 0x10000);
-       } else if ((dsp_index == 1) || (dsp_index == 2)) {
-               /* DSP 1&2 are a C6713 */
+       } else if (dsp_index == 1) {
+               /* DSP 1 is a C6713 */
                /* 192K internal mem */
                err = boot_loader_test_memory(pao, dsp_index, 0x00000000,
                        0x30000);
@@ -1955,11 +1904,10 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
                        /* 64K internal mem / L2 cache */
                        err = boot_loader_test_memory(pao, dsp_index,
                                0x00030000, 0x10000);
-       } else
-               return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
+       }
 
        if (err)
-               return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_INTMEM);
+               return HPI6205_ERROR_DSP_INTMEM;
        else
                return 0;
 }
@@ -1972,24 +1920,23 @@ static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao,
 
        if (dsp_index == 0) {
                /* only test for SDRAM if an ASI5000 card */
-               if (pao->pci.subsys_device_id == 0x5000) {
+               if (pao->pci.pci_dev->subsystem_device == 0x5000) {
                        /* DSP 0 is always C6205 */
                        dRAM_start_address = 0x00400000;
                        dRAM_size = 0x200000;
                        /*dwDRAMinc=1024; */
                } else
                        return 0;
-       } else if ((dsp_index == 1) || (dsp_index == 2)) {
+       } else if (dsp_index == 1) {
                /* DSP 1 is a C6713 */
                dRAM_start_address = 0x80000000;
                dRAM_size = 0x200000;
                /*dwDRAMinc=1024; */
-       } else
-               return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
+       }
 
        if (boot_loader_test_memory(pao, dsp_index, dRAM_start_address,
                        dRAM_size))
-               return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_EXTMEM);
+               return HPI6205_ERROR_DSP_EXTMEM;
        return 0;
 }
 
@@ -1998,28 +1945,25 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index)
        u32 data = 0;
        if (dsp_index == 0) {
                /* only test for DSP0 PLD on ASI5000 card */
-               if (pao->pci.subsys_device_id == 0x5000) {
+               if (pao->pci.pci_dev->subsystem_device == 0x5000) {
                        /* PLD is located at CE3=0x03000000 */
                        data = boot_loader_read_mem32(pao, dsp_index,
                                0x03000008);
                        if ((data & 0xF) != 0x5)
-                               return hpi6205_error(dsp_index,
-                                       HPI6205_ERROR_DSP_PLD);
+                               return HPI6205_ERROR_DSP_PLD;
                        data = boot_loader_read_mem32(pao, dsp_index,
                                0x0300000C);
                        if ((data & 0xF) != 0xA)
-                               return hpi6205_error(dsp_index,
-                                       HPI6205_ERROR_DSP_PLD);
+                               return HPI6205_ERROR_DSP_PLD;
                }
        } else if (dsp_index == 1) {
                /* DSP 1 is a C6713 */
-               if (pao->pci.subsys_device_id == 0x8700) {
+               if (pao->pci.pci_dev->subsystem_device == 0x8700) {
                        /* PLD is located at CE1=0x90000000 */
                        data = boot_loader_read_mem32(pao, dsp_index,
                                0x90000010);
                        if ((data & 0xFF) != 0xAA)
-                               return hpi6205_error(dsp_index,
-                                       HPI6205_ERROR_DSP_PLD);
+                               return HPI6205_ERROR_DSP_PLD;
                        /* 8713 - LED on */
                        boot_loader_write_mem32(pao, dsp_index, 0x90000000,
                                0x02);
@@ -2037,14 +1981,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
        struct hpi_hw_obj *phw = pao->priv;
        u32 data_transferred = 0;
        u16 err = 0;
-#ifndef HPI6205_NO_HSR_POLL
-       u32 time_out;
-#endif
        u32 temp2;
        struct bus_master_interface *interface = phw->p_interface_buffer;
 
        if (!p_data)
-               return HPI_ERROR_INVALID_DATA_TRANSFER;
+               return HPI_ERROR_INVALID_DATA_POINTER;
 
        data_size &= ~3L;       /* round data_size down to nearest 4 bytes */
 
@@ -2064,14 +2005,10 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
 
                interface->transfer_size_in_bytes = this_copy;
 
-#ifdef HPI6205_NO_HSR_POLL
                /* DSP must change this back to nOperation */
                interface->dsp_ack = H620_HIF_IDLE;
-#endif
-
                send_dsp_command(phw, operation);
 
-#ifdef HPI6205_NO_HSR_POLL
                temp2 = wait_dsp_ack(phw, operation, HPI6205_TIMEOUT);
                HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
                        HPI6205_TIMEOUT - temp2, this_copy);
@@ -2079,45 +2016,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
                if (!temp2) {
                        /* timed out */
                        HPI_DEBUG_LOG(ERROR,
-                               "timed out waiting for " "state %d got %d\n",
+                               "Timed out waiting for " "state %d got %d\n",
                                operation, interface->dsp_ack);
 
                        break;
                }
-#else
-               /* spin waiting on the result */
-               time_out = HPI6205_TIMEOUT;
-               temp2 = 0;
-               while ((temp2 == 0) && time_out--) {
-                       /* give 16k bus mastering transfer time to happen */
-                       /*(16k / 132Mbytes/s = 122usec) */
-                       hpios_delay_micro_seconds(20);
-                       temp2 = ioread32(phw->prHSR);
-                       temp2 &= C6205_HSR_INTSRC;
-               }
-               HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
-                       HPI6205_TIMEOUT - time_out, this_copy);
-               if (temp2 == C6205_HSR_INTSRC) {
-                       HPI_DEBUG_LOG(VERBOSE,
-                               "interrupt from HIF <data> OK\n");
-                       /*
-                          if(interface->dwDspAck != nOperation) {
-                          HPI_DEBUG_LOG(DEBUG("interface->dwDspAck=%d,
-                          expected %d \n",
-                          interface->dwDspAck,nOperation);
-                          }
-                        */
-               }
-/* need to handle this differently... */
-               else {
-                       HPI_DEBUG_LOG(ERROR,
-                               "interrupt from HIF <data> BAD\n");
-                       err = HPI_ERROR_DSP_HARDWARE;
-               }
-
-               /* reset the interrupt from the DSP */
-               iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
                if (operation == H620_HIF_GET_DATA)
                        memcpy(&p_data[data_transferred],
                                (void *)&interface->u.b_data[0], this_copy);
@@ -2174,31 +2077,39 @@ static unsigned int message_count;
 static u16 message_response_sequence(struct hpi_adapter_obj *pao,
        struct hpi_message *phm, struct hpi_response *phr)
 {
-#ifndef HPI6205_NO_HSR_POLL
-       u32 temp2;
-#endif
        u32 time_out, time_out2;
        struct hpi_hw_obj *phw = pao->priv;
        struct bus_master_interface *interface = phw->p_interface_buffer;
        u16 err = 0;
 
        message_count++;
+       if (phm->size > sizeof(interface->u)) {
+               phr->error = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
+               phr->specific_error = sizeof(interface->u);
+               phr->size = sizeof(struct hpi_response_header);
+               HPI_DEBUG_LOG(ERROR,
+                       "message len %d too big for buffer %zd \n", phm->size,
+                       sizeof(interface->u));
+               return 0;
+       }
+
        /* Assume buffer of type struct bus_master_interface
           is allocated "noncacheable" */
 
        if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
                HPI_DEBUG_LOG(DEBUG, "timeout waiting for idle\n");
-               return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
+               return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT;
        }
-       interface->u.message_buffer = *phm;
+
+       memcpy(&interface->u.message_buffer, phm, phm->size);
        /* signal we want a response */
        send_dsp_command(phw, H620_HIF_GET_RESP);
 
        time_out2 = wait_dsp_ack(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT);
 
-       if (time_out2 == 0) {
+       if (!time_out2) {
                HPI_DEBUG_LOG(ERROR,
-                       "(%u) timed out waiting for " "GET_RESP state [%x]\n",
+                       "(%u) Timed out waiting for " "GET_RESP state [%x]\n",
                        message_count, interface->dsp_ack);
        } else {
                HPI_DEBUG_LOG(VERBOSE,
@@ -2208,58 +2119,38 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
        /* spin waiting on HIF interrupt flag (end of msg process) */
        time_out = HPI6205_TIMEOUT;
 
-#ifndef HPI6205_NO_HSR_POLL
-       temp2 = 0;
-       while ((temp2 == 0) && --time_out) {
-               temp2 = ioread32(phw->prHSR);
-               temp2 &= C6205_HSR_INTSRC;
-               hpios_delay_micro_seconds(1);
-       }
-       if (temp2 == C6205_HSR_INTSRC) {
-               rmb();  /* ensure we see latest value for dsp_ack */
-               if ((interface->dsp_ack != H620_HIF_GET_RESP)) {
-                       HPI_DEBUG_LOG(DEBUG,
-                               "(%u)interface->dsp_ack(0x%x) != "
-                               "H620_HIF_GET_RESP, t=%u\n", message_count,
-                               interface->dsp_ack,
-                               HPI6205_TIMEOUT - time_out);
-               } else {
-                       HPI_DEBUG_LOG(VERBOSE,
-                               "(%u)int with GET_RESP after %u\n",
-                               message_count, HPI6205_TIMEOUT - time_out);
+       /* read the result */
+       if (time_out) {
+               if (interface->u.response_buffer.size <= phr->size)
+                       memcpy(phr, &interface->u.response_buffer,
+                               interface->u.response_buffer.size);
+               else {
+                       HPI_DEBUG_LOG(ERROR,
+                               "response len %d too big for buffer %d\n",
+                               interface->u.response_buffer.size, phr->size);
+                       memcpy(phr, &interface->u.response_buffer,
+                               sizeof(struct hpi_response_header));
+                       phr->error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
+                       phr->specific_error =
+                               interface->u.response_buffer.size;
+                       phr->size = sizeof(struct hpi_response_header);
                }
-
-       } else {
-               /* can we do anything else in response to the error ? */
-               HPI_DEBUG_LOG(ERROR,
-                       "interrupt from HIF module BAD (function %x)\n",
-                       phm->function);
        }
-
-       /* reset the interrupt from the DSP */
-       iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
-
-       /* read the result */
-       if (time_out != 0)
-               *phr = interface->u.response_buffer;
-
        /* set interface back to idle */
        send_dsp_command(phw, H620_HIF_IDLE);
 
-       if ((time_out == 0) || (time_out2 == 0)) {
+       if (!time_out || !time_out2) {
                HPI_DEBUG_LOG(DEBUG, "something timed out!\n");
-               return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_TIMEOUT);
+               return HPI6205_ERROR_MSG_RESP_TIMEOUT;
        }
        /* special case for adapter close - */
        /* wait for the DSP to indicate it is idle */
        if (phm->function == HPI_ADAPTER_CLOSE) {
                if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
                        HPI_DEBUG_LOG(DEBUG,
-                               "timeout waiting for idle "
+                               "Timeout waiting for idle "
                                "(on adapter_close)\n");
-                       return hpi6205_error(0,
-                               HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
+                       return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT;
                }
        }
        err = hpi_validate_response(phm, phr);
@@ -2279,7 +2170,13 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
        /* maybe an error response */
        if (err) {
                /* something failed in the HPI/DSP interface */
-               phr->error = err;
+               if (err >= HPI_ERROR_BACKEND_BASE) {
+                       phr->error = HPI_ERROR_DSP_COMMUNICATION;
+                       phr->specific_error = err;
+               } else {
+                       phr->error = err;
+               }
+
                pao->dsp_crashed++;
 
                /* just the header of the response is valid */
index 1adae0857cda197e492d6ccbcad245c9abaee514..df2f02c0c7b430ccf8e67f5de969961755b87c2e 100644 (file)
@@ -25,9 +25,6 @@ Copyright AudioScience, Inc., 2003
 #ifndef _HPI6205_H_
 #define _HPI6205_H_
 
-/* transitional conditional compile shared between host and DSP */
-/* #define HPI6205_NO_HSR_POLL */
-
 #include "hpi_internal.h"
 
 /***********************************************************
@@ -78,8 +75,8 @@ struct bus_master_interface {
        u32 dsp_ack;
        u32 transfer_size_in_bytes;
        union {
-               struct hpi_message message_buffer;
-               struct hpi_response response_buffer;
+               struct hpi_message_header message_buffer;
+               struct hpi_response_header response_buffer;
                u8 b_data[HPI6205_SIZEOF_DATA];
        } u;
        struct controlcache_6205 control_cache;
index 16f502d459deda02fee5e2f34e2b8d016fe3debd..af678be0aa15bc1133ddb136c5e86650426665d1 100644 (file)
@@ -28,7 +28,7 @@ HPI internal definitions
 /** maximum number of memory regions mapped to an adapter */
 #define HPI_MAX_ADAPTER_MEM_SPACES (2)
 
-/* Each OS needs its own hpios.h, or specific define as above */
+/* Each OS needs its own hpios.h */
 #include "hpios.h"
 
 /* physical memory allocation */
@@ -49,7 +49,7 @@ HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle.
 */
 u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle,
                                                           /**< memory handle */
-       u32 size, /**< size in bytes to allocate */
+       u32 size, /**< Size in bytes to allocate */
        struct pci_dev *p_os_reference
        /**< OS specific data required for memory allocation */
        );
@@ -96,41 +96,6 @@ typedef void hpi_handler_func(struct hpi_message *, struct hpi_response *);
 #define compile_time_assert(cond, msg) \
     typedef char ASSERT_##msg[(cond) ? 1 : -1]
 
-/*/////////////////////////////////////////////////////////////////////////// */
-/* Private HPI Entity related definitions                                     */
-
-#define STR_SIZE_FIELD_MAX 65535U
-#define STR_TYPE_FIELD_MAX 255U
-#define STR_ROLE_FIELD_MAX 255U
-
-struct hpi_entity_str {
-       u16 size;
-       u8 type;
-       u8 role;
-};
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4200)
-#endif
-
-struct hpi_entity {
-       struct hpi_entity_str header;
-#if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008))
-       /* DSP C6000 compiler v6.0.8 and lower
-          do not support  flexible array member */
-       u8 value[];
-#else
-       /* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */
-#define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE
-       u8 value[1];
-#endif
-};
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
 /******************************************* bus types */
 enum HPI_BUSES {
        HPI_BUS_ISAPNP = 1,
@@ -139,206 +104,155 @@ enum HPI_BUSES {
        HPI_BUS_NET = 4
 };
 
+enum HPI_SUBSYS_OPTIONS {
+       /* 0, 256 are invalid, 1..255 reserved for global options */
+       HPI_SUBSYS_OPT_NET_ENABLE = 257,
+       HPI_SUBSYS_OPT_NET_BROADCAST = 258,
+       HPI_SUBSYS_OPT_NET_UNICAST = 259,
+       HPI_SUBSYS_OPT_NET_ADDR = 260,
+       HPI_SUBSYS_OPT_NET_MASK = 261,
+       HPI_SUBSYS_OPT_NET_ADAPTER_ADDRESS_ADD = 262
+};
+
+/** Volume flags
+*/
+enum HPI_VOLUME_FLAGS {
+       /** Set if the volume control is muted */
+       HPI_VOLUME_FLAG_MUTED = (1 << 0),
+       /** Set if the volume control has a mute function */
+       HPI_VOLUME_FLAG_HAS_MUTE = (1 << 1),
+       /** Set if volume control can do autofading */
+       HPI_VOLUME_FLAG_HAS_AUTOFADE = (1 << 2)
+               /* Note Flags >= (1<<8) are for DSP internal use only */
+};
+
 /******************************************* CONTROL ATTRIBUTES ****/
 /* (in order of control type ID */
 
 /* This allows for 255 control types, 256 unique attributes each */
-#define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai)
+#define HPI_CTL_ATTR(ctl, ai) ((HPI_CONTROL_##ctl << 8) + ai)
 
 /* Get the sub-index of the attribute for a control type */
-#define HPI_CTL_ATTR_INDEX(i) (i&0xff)
+#define HPI_CTL_ATTR_INDEX(i) (i & 0xff)
 
 /* Extract the control from the control attribute */
-#define HPI_CTL_ATTR_CONTROL(i) (i>>8)
-
-/* Generic control attributes.  */
-
-/** Enable a control.
-0=disable, 1=enable
-\note generic to all mixer plugins?
-*/
-#define HPI_GENERIC_ENABLE HPI_CTL_ATTR(GENERIC, 1)
+#define HPI_CTL_ATTR_CONTROL(i) (i >> 8)
 
 /** Enable event generation for a control.
 0=disable, 1=enable
 \note generic to all controls that can generate events
 */
-#define HPI_GENERIC_EVENT_ENABLE HPI_CTL_ATTR(GENERIC, 2)
-
-/* Volume Control attributes */
-#define HPI_VOLUME_GAIN                 HPI_CTL_ATTR(VOLUME, 1)
-#define HPI_VOLUME_AUTOFADE             HPI_CTL_ATTR(VOLUME, 2)
-
-/** For HPI_ControlQuery() to get the number of channels of a volume control*/
-#define HPI_VOLUME_NUM_CHANNELS         HPI_CTL_ATTR(VOLUME, 6)
-#define HPI_VOLUME_RANGE                HPI_CTL_ATTR(VOLUME, 10)
-
-/** Level Control attributes */
-#define HPI_LEVEL_GAIN                  HPI_CTL_ATTR(LEVEL, 1)
-#define HPI_LEVEL_RANGE                 HPI_CTL_ATTR(LEVEL, 10)
-
-/* Meter Control attributes */
-/** return RMS signal level */
-#define HPI_METER_RMS                   HPI_CTL_ATTR(METER, 1)
-/** return peak signal level */
-#define HPI_METER_PEAK                  HPI_CTL_ATTR(METER, 2)
-/** ballistics for ALL rms meters on adapter */
-#define HPI_METER_RMS_BALLISTICS        HPI_CTL_ATTR(METER, 3)
-/** ballistics for ALL peak meters on adapter */
-#define HPI_METER_PEAK_BALLISTICS       HPI_CTL_ATTR(METER, 4)
-
-/** For HPI_ControlQuery() to get the number of channels of a meter control*/
-#define HPI_METER_NUM_CHANNELS          HPI_CTL_ATTR(METER, 5)
-
-/* Multiplexer control attributes */
-#define HPI_MULTIPLEXER_SOURCE          HPI_CTL_ATTR(MULTIPLEXER, 1)
-#define HPI_MULTIPLEXER_QUERYSOURCE     HPI_CTL_ATTR(MULTIPLEXER, 2)
-
-/** AES/EBU transmitter control attributes */
-/** AESEBU or SPDIF */
-#define HPI_AESEBUTX_FORMAT             HPI_CTL_ATTR(AESEBUTX, 1)
-#define HPI_AESEBUTX_SAMPLERATE         HPI_CTL_ATTR(AESEBUTX, 3)
-#define HPI_AESEBUTX_CHANNELSTATUS      HPI_CTL_ATTR(AESEBUTX, 4)
-#define HPI_AESEBUTX_USERDATA           HPI_CTL_ATTR(AESEBUTX, 5)
-
-/** AES/EBU receiver control attributes */
-#define HPI_AESEBURX_FORMAT             HPI_CTL_ATTR(AESEBURX, 1)
-#define HPI_AESEBURX_ERRORSTATUS        HPI_CTL_ATTR(AESEBURX, 2)
-#define HPI_AESEBURX_SAMPLERATE         HPI_CTL_ATTR(AESEBURX, 3)
-#define HPI_AESEBURX_CHANNELSTATUS      HPI_CTL_ATTR(AESEBURX, 4)
-#define HPI_AESEBURX_USERDATA           HPI_CTL_ATTR(AESEBURX, 5)
-
-/** \defgroup tuner_defs Tuners
-\{
-*/
-/** \defgroup tuner_attrs Tuner control attributes
-\{
-*/
-#define HPI_TUNER_BAND                  HPI_CTL_ATTR(TUNER, 1)
-#define HPI_TUNER_FREQ                  HPI_CTL_ATTR(TUNER, 2)
-#define HPI_TUNER_LEVEL                 HPI_CTL_ATTR(TUNER, 3)
-#define HPI_TUNER_AUDIOMUTE             HPI_CTL_ATTR(TUNER, 4)
-/* use TUNER_STATUS instead */
-#define HPI_TUNER_VIDEO_STATUS          HPI_CTL_ATTR(TUNER, 5)
-#define HPI_TUNER_GAIN                  HPI_CTL_ATTR(TUNER, 6)
-#define HPI_TUNER_STATUS                HPI_CTL_ATTR(TUNER, 7)
-#define HPI_TUNER_MODE                  HPI_CTL_ATTR(TUNER, 8)
-/** RDS data. */
-#define HPI_TUNER_RDS                   HPI_CTL_ATTR(TUNER, 9)
-/** Audio pre-emphasis. */
-#define HPI_TUNER_DEEMPHASIS            HPI_CTL_ATTR(TUNER, 10)
-/** HD Radio tuner program control. */
-#define HPI_TUNER_PROGRAM               HPI_CTL_ATTR(TUNER, 11)
-/** HD Radio tuner digital signal quality. */
-#define HPI_TUNER_HDRADIO_SIGNAL_QUALITY        HPI_CTL_ATTR(TUNER, 12)
-/** HD Radio SDK firmware version. */
-#define HPI_TUNER_HDRADIO_SDK_VERSION   HPI_CTL_ATTR(TUNER, 13)
-/** HD Radio DSP firmware version. */
-#define HPI_TUNER_HDRADIO_DSP_VERSION   HPI_CTL_ATTR(TUNER, 14)
-/** HD Radio signal blend (force analog, or automatic). */
-#define HPI_TUNER_HDRADIO_BLEND         HPI_CTL_ATTR(TUNER, 15)
-
-/** \} */
-
-/** \defgroup pads_attrs Tuner PADs control attributes
-\{
-*/
-/** The text string containing the station/channel combination. */
-#define HPI_PAD_CHANNEL_NAME            HPI_CTL_ATTR(PAD, 1)
-/** The text string containing the artist. */
-#define HPI_PAD_ARTIST                  HPI_CTL_ATTR(PAD, 2)
-/** The text string containing the title. */
-#define HPI_PAD_TITLE                   HPI_CTL_ATTR(PAD, 3)
-/** The text string containing the comment. */
-#define HPI_PAD_COMMENT                 HPI_CTL_ATTR(PAD, 4)
-/** The integer containing the PTY code. */
-#define HPI_PAD_PROGRAM_TYPE            HPI_CTL_ATTR(PAD, 5)
-/** The integer containing the program identification. */
-#define HPI_PAD_PROGRAM_ID              HPI_CTL_ATTR(PAD, 6)
-/** The integer containing whether traffic information is supported.
-Contains either 1 or 0. */
-#define HPI_PAD_TA_SUPPORT              HPI_CTL_ATTR(PAD, 7)
-/** The integer containing whether traffic announcement is in progress.
-Contains either 1 or 0. */
-#define HPI_PAD_TA_ACTIVE               HPI_CTL_ATTR(PAD, 8)
-/** \} */
-/** \} */
-
-/* VOX control attributes */
-#define HPI_VOX_THRESHOLD               HPI_CTL_ATTR(VOX, 1)
-
-/*?? channel mode used hpi_multiplexer_source attribute == 1 */
-#define HPI_CHANNEL_MODE_MODE HPI_CTL_ATTR(CHANNEL_MODE, 1)
-
-/** \defgroup channel_modes Channel Modes
-Used for HPI_ChannelModeSet/Get()
-\{
+
+/** Unique identifiers for every control attribute
 */
-/** Left channel out = left channel in, Right channel out = right channel in. */
-#define HPI_CHANNEL_MODE_NORMAL                 1
-/** Left channel out = right channel in, Right channel out = left channel in. */
-#define HPI_CHANNEL_MODE_SWAP                   2
-/** Left channel out = left channel in, Right channel out = left channel in. */
-#define HPI_CHANNEL_MODE_LEFT_TO_STEREO         3
-/** Left channel out = right channel in, Right channel out = right channel in.*/
-#define HPI_CHANNEL_MODE_RIGHT_TO_STEREO        4
-/** Left channel out = (left channel in + right channel in)/2,
-    Right channel out = mute. */
-#define HPI_CHANNEL_MODE_STEREO_TO_LEFT         5
-/** Left channel out = mute,
-    Right channel out = (right channel in + left channel in)/2. */
-#define HPI_CHANNEL_MODE_STEREO_TO_RIGHT        6
-#define HPI_CHANNEL_MODE_LAST                   6
-/** \} */
-
-/* Bitstream control set attributes */
-#define HPI_BITSTREAM_DATA_POLARITY     HPI_CTL_ATTR(BITSTREAM, 1)
-#define HPI_BITSTREAM_CLOCK_EDGE        HPI_CTL_ATTR(BITSTREAM, 2)
-#define HPI_BITSTREAM_CLOCK_SOURCE      HPI_CTL_ATTR(BITSTREAM, 3)
+enum HPI_CONTROL_ATTRIBUTES {
+       HPI_GENERIC_ENABLE = HPI_CTL_ATTR(GENERIC, 1),
+       HPI_GENERIC_EVENT_ENABLE = HPI_CTL_ATTR(GENERIC, 2),
+
+       HPI_VOLUME_GAIN = HPI_CTL_ATTR(VOLUME, 1),
+       HPI_VOLUME_AUTOFADE = HPI_CTL_ATTR(VOLUME, 2),
+       HPI_VOLUME_MUTE = HPI_CTL_ATTR(VOLUME, 3),
+       HPI_VOLUME_GAIN_AND_FLAGS = HPI_CTL_ATTR(VOLUME, 4),
+       HPI_VOLUME_NUM_CHANNELS = HPI_CTL_ATTR(VOLUME, 6),
+       HPI_VOLUME_RANGE = HPI_CTL_ATTR(VOLUME, 10),
+
+       HPI_METER_RMS = HPI_CTL_ATTR(METER, 1),
+       HPI_METER_PEAK = HPI_CTL_ATTR(METER, 2),
+       HPI_METER_RMS_BALLISTICS = HPI_CTL_ATTR(METER, 3),
+       HPI_METER_PEAK_BALLISTICS = HPI_CTL_ATTR(METER, 4),
+       HPI_METER_NUM_CHANNELS = HPI_CTL_ATTR(METER, 5),
+
+       HPI_MULTIPLEXER_SOURCE = HPI_CTL_ATTR(MULTIPLEXER, 1),
+       HPI_MULTIPLEXER_QUERYSOURCE = HPI_CTL_ATTR(MULTIPLEXER, 2),
+
+       HPI_AESEBUTX_FORMAT = HPI_CTL_ATTR(AESEBUTX, 1),
+       HPI_AESEBUTX_SAMPLERATE = HPI_CTL_ATTR(AESEBUTX, 3),
+       HPI_AESEBUTX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBUTX, 4),
+       HPI_AESEBUTX_USERDATA = HPI_CTL_ATTR(AESEBUTX, 5),
+
+       HPI_AESEBURX_FORMAT = HPI_CTL_ATTR(AESEBURX, 1),
+       HPI_AESEBURX_ERRORSTATUS = HPI_CTL_ATTR(AESEBURX, 2),
+       HPI_AESEBURX_SAMPLERATE = HPI_CTL_ATTR(AESEBURX, 3),
+       HPI_AESEBURX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBURX, 4),
+       HPI_AESEBURX_USERDATA = HPI_CTL_ATTR(AESEBURX, 5),
+
+       HPI_LEVEL_GAIN = HPI_CTL_ATTR(LEVEL, 1),
+       HPI_LEVEL_RANGE = HPI_CTL_ATTR(LEVEL, 10),
+
+       HPI_TUNER_BAND = HPI_CTL_ATTR(TUNER, 1),
+       HPI_TUNER_FREQ = HPI_CTL_ATTR(TUNER, 2),
+       HPI_TUNER_LEVEL_AVG = HPI_CTL_ATTR(TUNER, 3),
+       HPI_TUNER_LEVEL_RAW = HPI_CTL_ATTR(TUNER, 4),
+       HPI_TUNER_SNR = HPI_CTL_ATTR(TUNER, 5),
+       HPI_TUNER_GAIN = HPI_CTL_ATTR(TUNER, 6),
+       HPI_TUNER_STATUS = HPI_CTL_ATTR(TUNER, 7),
+       HPI_TUNER_MODE = HPI_CTL_ATTR(TUNER, 8),
+       HPI_TUNER_RDS = HPI_CTL_ATTR(TUNER, 9),
+       HPI_TUNER_DEEMPHASIS = HPI_CTL_ATTR(TUNER, 10),
+       HPI_TUNER_PROGRAM = HPI_CTL_ATTR(TUNER, 11),
+       HPI_TUNER_HDRADIO_SIGNAL_QUALITY = HPI_CTL_ATTR(TUNER, 12),
+       HPI_TUNER_HDRADIO_SDK_VERSION = HPI_CTL_ATTR(TUNER, 13),
+       HPI_TUNER_HDRADIO_DSP_VERSION = HPI_CTL_ATTR(TUNER, 14),
+       HPI_TUNER_HDRADIO_BLEND = HPI_CTL_ATTR(TUNER, 15),
+
+       HPI_VOX_THRESHOLD = HPI_CTL_ATTR(VOX, 1),
+
+       HPI_CHANNEL_MODE_MODE = HPI_CTL_ATTR(CHANNEL_MODE, 1),
+
+       HPI_BITSTREAM_DATA_POLARITY = HPI_CTL_ATTR(BITSTREAM, 1),
+       HPI_BITSTREAM_CLOCK_EDGE = HPI_CTL_ATTR(BITSTREAM, 2),
+       HPI_BITSTREAM_CLOCK_SOURCE = HPI_CTL_ATTR(BITSTREAM, 3),
+       HPI_BITSTREAM_ACTIVITY = HPI_CTL_ATTR(BITSTREAM, 4),
+
+       HPI_SAMPLECLOCK_SOURCE = HPI_CTL_ATTR(SAMPLECLOCK, 1),
+       HPI_SAMPLECLOCK_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 2),
+       HPI_SAMPLECLOCK_SOURCE_INDEX = HPI_CTL_ATTR(SAMPLECLOCK, 3),
+       HPI_SAMPLECLOCK_LOCAL_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 4),
+       HPI_SAMPLECLOCK_AUTO = HPI_CTL_ATTR(SAMPLECLOCK, 5),
+       HPI_SAMPLECLOCK_LOCAL_LOCK = HPI_CTL_ATTR(SAMPLECLOCK, 6),
+
+       HPI_MICROPHONE_PHANTOM_POWER = HPI_CTL_ATTR(MICROPHONE, 1),
+
+       HPI_EQUALIZER_NUM_FILTERS = HPI_CTL_ATTR(EQUALIZER, 1),
+       HPI_EQUALIZER_FILTER = HPI_CTL_ATTR(EQUALIZER, 2),
+       HPI_EQUALIZER_COEFFICIENTS = HPI_CTL_ATTR(EQUALIZER, 3),
+
+       HPI_COMPANDER_PARAMS = HPI_CTL_ATTR(COMPANDER, 1),
+       HPI_COMPANDER_MAKEUPGAIN = HPI_CTL_ATTR(COMPANDER, 2),
+       HPI_COMPANDER_THRESHOLD = HPI_CTL_ATTR(COMPANDER, 3),
+       HPI_COMPANDER_RATIO = HPI_CTL_ATTR(COMPANDER, 4),
+       HPI_COMPANDER_ATTACK = HPI_CTL_ATTR(COMPANDER, 5),
+       HPI_COMPANDER_DECAY = HPI_CTL_ATTR(COMPANDER, 6),
+
+       HPI_COBRANET_SET = HPI_CTL_ATTR(COBRANET, 1),
+       HPI_COBRANET_GET = HPI_CTL_ATTR(COBRANET, 2),
+       HPI_COBRANET_SET_DATA = HPI_CTL_ATTR(COBRANET, 3),
+       HPI_COBRANET_GET_DATA = HPI_CTL_ATTR(COBRANET, 4),
+       HPI_COBRANET_GET_STATUS = HPI_CTL_ATTR(COBRANET, 5),
+       HPI_COBRANET_SEND_PACKET = HPI_CTL_ATTR(COBRANET, 6),
+       HPI_COBRANET_GET_PACKET = HPI_CTL_ATTR(COBRANET, 7),
+
+       HPI_TONEDETECTOR_THRESHOLD = HPI_CTL_ATTR(TONEDETECTOR, 1),
+       HPI_TONEDETECTOR_STATE = HPI_CTL_ATTR(TONEDETECTOR, 2),
+       HPI_TONEDETECTOR_FREQUENCY = HPI_CTL_ATTR(TONEDETECTOR, 3),
+
+       HPI_SILENCEDETECTOR_THRESHOLD = HPI_CTL_ATTR(SILENCEDETECTOR, 1),
+       HPI_SILENCEDETECTOR_STATE = HPI_CTL_ATTR(SILENCEDETECTOR, 2),
+       HPI_SILENCEDETECTOR_DELAY = HPI_CTL_ATTR(SILENCEDETECTOR, 3),
+
+       HPI_PAD_CHANNEL_NAME = HPI_CTL_ATTR(PAD, 1),
+       HPI_PAD_ARTIST = HPI_CTL_ATTR(PAD, 2),
+       HPI_PAD_TITLE = HPI_CTL_ATTR(PAD, 3),
+       HPI_PAD_COMMENT = HPI_CTL_ATTR(PAD, 4),
+       HPI_PAD_PROGRAM_TYPE = HPI_CTL_ATTR(PAD, 5),
+       HPI_PAD_PROGRAM_ID = HPI_CTL_ATTR(PAD, 6),
+       HPI_PAD_TA_SUPPORT = HPI_CTL_ATTR(PAD, 7),
+       HPI_PAD_TA_ACTIVE = HPI_CTL_ATTR(PAD, 8)
+};
 
 #define HPI_POLARITY_POSITIVE           0
 #define HPI_POLARITY_NEGATIVE           1
 
-/* Bitstream control get attributes */
-#define HPI_BITSTREAM_ACTIVITY          1
-
-/* SampleClock control attributes */
-#define HPI_SAMPLECLOCK_SOURCE                  HPI_CTL_ATTR(SAMPLECLOCK, 1)
-#define HPI_SAMPLECLOCK_SAMPLERATE              HPI_CTL_ATTR(SAMPLECLOCK, 2)
-#define HPI_SAMPLECLOCK_SOURCE_INDEX            HPI_CTL_ATTR(SAMPLECLOCK, 3)
-#define HPI_SAMPLECLOCK_LOCAL_SAMPLERATE\
-       HPI_CTL_ATTR(SAMPLECLOCK, 4)
-#define HPI_SAMPLECLOCK_AUTO                    HPI_CTL_ATTR(SAMPLECLOCK, 5)
-#define HPI_SAMPLECLOCK_LOCAL_LOCK                      HPI_CTL_ATTR(SAMPLECLOCK, 6)
-
-/* Microphone control attributes */
-#define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1)
-
-/** Equalizer control attributes */
-/** Used to get number of filters in an EQ. (Can't set) */
-#define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1)
-/** Set/get the filter by type, freq, Q, gain */
-#define HPI_EQUALIZER_FILTER HPI_CTL_ATTR(EQUALIZER, 2)
-/** Get the biquad coefficients */
-#define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3)
-
-/* Note compander also uses HPI_GENERIC_ENABLE */
-#define HPI_COMPANDER_PARAMS     HPI_CTL_ATTR(COMPANDER, 1)
-#define HPI_COMPANDER_MAKEUPGAIN HPI_CTL_ATTR(COMPANDER, 2)
-#define HPI_COMPANDER_THRESHOLD  HPI_CTL_ATTR(COMPANDER, 3)
-#define HPI_COMPANDER_RATIO      HPI_CTL_ATTR(COMPANDER, 4)
-#define HPI_COMPANDER_ATTACK     HPI_CTL_ATTR(COMPANDER, 5)
-#define HPI_COMPANDER_DECAY      HPI_CTL_ATTR(COMPANDER, 6)
-
-/* Cobranet control attributes. */
-#define HPI_COBRANET_SET         HPI_CTL_ATTR(COBRANET, 1)
-#define HPI_COBRANET_GET         HPI_CTL_ATTR(COBRANET, 2)
-#define HPI_COBRANET_SET_DATA    HPI_CTL_ATTR(COBRANET, 3)
-#define HPI_COBRANET_GET_DATA    HPI_CTL_ATTR(COBRANET, 4)
-#define HPI_COBRANET_GET_STATUS  HPI_CTL_ATTR(COBRANET, 5)
-#define HPI_COBRANET_SEND_PACKET HPI_CTL_ATTR(COBRANET, 6)
-#define HPI_COBRANET_GET_PACKET  HPI_CTL_ATTR(COBRANET, 7)
-
 /*------------------------------------------------------------
  Cobranet Chip Bridge - copied from HMI.H
 ------------------------------------------------------------*/
@@ -395,69 +309,22 @@ Used for HPI_ChannelModeSet/Get()
 
 #define HPI_ETHERNET_UDP_PORT (44600)  /*!< UDP messaging port */
 
-/** Base network time out is set to 100 milli-seconds. */
-#define HPI_ETHERNET_TIMEOUT_MS      (100)
-
-/** \defgroup tonedet_attr Tonedetector attributes
-\{
-Used by HPI_ToneDetector_Set() and HPI_ToneDetector_Get()
-*/
-
-/** Set the threshold level of a tonedetector,
-Threshold is a -ve number in units of dB/100,
-*/
-#define HPI_TONEDETECTOR_THRESHOLD HPI_CTL_ATTR(TONEDETECTOR, 1)
-
-/** Get the current state of tonedetection
-The result is a bitmap of detected tones.  pairs of bits represent the left
-and right channels, with left channel in LSB.
-The lowest frequency detector state is in the LSB
-*/
-#define HPI_TONEDETECTOR_STATE HPI_CTL_ATTR(TONEDETECTOR, 2)
-
-/** Get the frequency of a tonedetector band.
-*/
-#define HPI_TONEDETECTOR_FREQUENCY HPI_CTL_ATTR(TONEDETECTOR, 3)
-
-/**\}*/
+/** Default network timeout in milli-seconds. */
+#define HPI_ETHERNET_TIMEOUT_MS 500
 
-/** \defgroup silencedet_attr SilenceDetector attributes
-\{
-*/
-
-/** Get the current state of tonedetection
-The result is a bitmap with 1s for silent channels. Left channel is in LSB
-*/
-#define HPI_SILENCEDETECTOR_STATE \
-  HPI_CTL_ATTR(SILENCEDETECTOR, 2)
-
-/** Set the threshold level of a SilenceDetector,
-Threshold is a -ve number in units of dB/100,
-*/
-#define HPI_SILENCEDETECTOR_THRESHOLD \
-  HPI_CTL_ATTR(SILENCEDETECTOR, 1)
-
-/** get/set the silence time before the detector triggers
-*/
-#define HPI_SILENCEDETECTOR_DELAY \
-       HPI_CTL_ATTR(SILENCEDETECTOR, 3)
-
-/**\}*/
-
-/* Locked memory buffer alloc/free phases */
-/** use one message to allocate or free physical memory */
-#define HPI_BUFFER_CMD_EXTERNAL                 0
-/** alloc physical memory */
-#define HPI_BUFFER_CMD_INTERNAL_ALLOC           1
-/** send physical memory address to adapter */
-#define HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER    2
-/** notify adapter to stop using physical buffer */
-#define HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER   3
-/** free physical buffer */
-#define HPI_BUFFER_CMD_INTERNAL_FREE            4
-
-/******************************************* CONTROLX ATTRIBUTES ****/
-/* NOTE: All controlx attributes must be unique, unlike control attributes */
+/** Locked memory buffer alloc/free phases */
+enum HPI_BUFFER_CMDS {
+       /** use one message to allocate or free physical memory */
+       HPI_BUFFER_CMD_EXTERNAL = 0,
+       /** alloc physical memory */
+       HPI_BUFFER_CMD_INTERNAL_ALLOC = 1,
+       /** send physical memory address to adapter */
+       HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER = 2,
+       /** notify adapter to stop using physical buffer */
+       HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER = 3,
+       /** free physical buffer */
+       HPI_BUFFER_CMD_INTERNAL_FREE = 4
+};
 
 /*****************************************************************************/
 /*****************************************************************************/
@@ -482,6 +349,12 @@ Threshold is a -ve number in units of dB/100,
 #define HPI_USB_W2K_TAG                 0x57495341     /* "ASIW"       */
 #define HPI_USB_LINUX_TAG               0x4C495341     /* "ASIL"       */
 
+/** Invalid Adapter index
+Used in HPI messages that are not addressed to a specific adapter
+Used in DLL to indicate device not present
+*/
+#define HPI_ADAPTER_INDEX_INVALID 0xFFFF
+
 /** First 2 hex digits define the adapter family */
 #define HPI_ADAPTER_FAMILY_MASK         0xff00
 #define HPI_MODULE_FAMILY_MASK          0xfff0
@@ -490,178 +363,185 @@ Threshold is a -ve number in units of dB/100,
 #define HPI_MODULE_FAMILY_ASI(f)   (f & HPI_MODULE_FAMILY_MASK)
 #define HPI_ADAPTER_ASI(f)   (f)
 
-/******************************************* message types */
-#define HPI_TYPE_MESSAGE                        1
-#define HPI_TYPE_RESPONSE                       2
-#define HPI_TYPE_DATA                           3
-#define HPI_TYPE_SSX2BYPASS_MESSAGE             4
-
-/******************************************* object types */
-#define HPI_OBJ_SUBSYSTEM                       1
-#define HPI_OBJ_ADAPTER                         2
-#define HPI_OBJ_OSTREAM                         3
-#define HPI_OBJ_ISTREAM                         4
-#define HPI_OBJ_MIXER                           5
-#define HPI_OBJ_NODE                            6
-#define HPI_OBJ_CONTROL                         7
-#define HPI_OBJ_NVMEMORY                        8
-#define HPI_OBJ_GPIO                            9
-#define HPI_OBJ_WATCHDOG                        10
-#define HPI_OBJ_CLOCK                           11
-#define HPI_OBJ_PROFILE                         12
-#define HPI_OBJ_CONTROLEX                       13
-#define HPI_OBJ_ASYNCEVENT                      14
-
-#define HPI_OBJ_MAXINDEX                        14
-
-/******************************************* methods/functions */
-
-#define HPI_OBJ_FUNCTION_SPACING        0x100
-#define HPI_MAKE_INDEX(obj, index) (obj * HPI_OBJ_FUNCTION_SPACING + index)
+enum HPI_MESSAGE_TYPES {
+       HPI_TYPE_MESSAGE = 1,
+       HPI_TYPE_RESPONSE = 2,
+       HPI_TYPE_DATA = 3,
+       HPI_TYPE_SSX2BYPASS_MESSAGE = 4
+};
+
+enum HPI_OBJECT_TYPES {
+       HPI_OBJ_SUBSYSTEM = 1,
+       HPI_OBJ_ADAPTER = 2,
+       HPI_OBJ_OSTREAM = 3,
+       HPI_OBJ_ISTREAM = 4,
+       HPI_OBJ_MIXER = 5,
+       HPI_OBJ_NODE = 6,
+       HPI_OBJ_CONTROL = 7,
+       HPI_OBJ_NVMEMORY = 8,
+       HPI_OBJ_GPIO = 9,
+       HPI_OBJ_WATCHDOG = 10,
+       HPI_OBJ_CLOCK = 11,
+       HPI_OBJ_PROFILE = 12,
+       HPI_OBJ_CONTROLEX = 13,
+       HPI_OBJ_ASYNCEVENT = 14
+#define HPI_OBJ_MAXINDEX 14
+};
+
+#define HPI_OBJ_FUNCTION_SPACING 0x100
+#define HPI_FUNC_ID(obj, i) (HPI_OBJ_##obj * HPI_OBJ_FUNCTION_SPACING + i)
+
 #define HPI_EXTRACT_INDEX(fn) (fn & 0xff)
 
-/* SUB-SYSTEM */
-#define HPI_SUBSYS_OPEN                 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 1)
-#define HPI_SUBSYS_GET_VERSION          HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 2)
-#define HPI_SUBSYS_GET_INFO             HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 3)
-#define HPI_SUBSYS_FIND_ADAPTERS        HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 4)
-#define HPI_SUBSYS_CREATE_ADAPTER       HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 5)
-#define HPI_SUBSYS_CLOSE                HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 6)
-#define HPI_SUBSYS_DELETE_ADAPTER       HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 7)
-#define HPI_SUBSYS_DRIVER_LOAD          HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 8)
-#define HPI_SUBSYS_DRIVER_UNLOAD        HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 9)
-#define HPI_SUBSYS_READ_PORT_8          HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 10)
-#define HPI_SUBSYS_WRITE_PORT_8         HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 11)
-#define HPI_SUBSYS_GET_NUM_ADAPTERS     HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 12)
-#define HPI_SUBSYS_GET_ADAPTER          HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 13)
-#define HPI_SUBSYS_SET_NETWORK_INTERFACE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 14)
-#define HPI_SUBSYS_FUNCTION_COUNT 14
-/* ADAPTER */
-#define HPI_ADAPTER_OPEN                HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 1)
-#define HPI_ADAPTER_CLOSE               HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 2)
-#define HPI_ADAPTER_GET_INFO            HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 3)
-#define HPI_ADAPTER_GET_ASSERT          HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 4)
-#define HPI_ADAPTER_TEST_ASSERT         HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 5)
-#define HPI_ADAPTER_SET_MODE            HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 6)
-#define HPI_ADAPTER_GET_MODE            HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 7)
-#define HPI_ADAPTER_ENABLE_CAPABILITY   HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 8)
-#define HPI_ADAPTER_SELFTEST            HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 9)
-#define HPI_ADAPTER_FIND_OBJECT         HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 10)
-#define HPI_ADAPTER_QUERY_FLASH         HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 11)
-#define HPI_ADAPTER_START_FLASH         HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 12)
-#define HPI_ADAPTER_PROGRAM_FLASH       HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 13)
-#define HPI_ADAPTER_SET_PROPERTY        HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 14)
-#define HPI_ADAPTER_GET_PROPERTY        HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 15)
-#define HPI_ADAPTER_ENUM_PROPERTY       HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 16)
-#define HPI_ADAPTER_MODULE_INFO         HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 17)
-#define HPI_ADAPTER_DEBUG_READ          HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 18)
-#define HPI_ADAPTER_FUNCTION_COUNT 18
-/* OUTPUT STREAM */
-#define HPI_OSTREAM_OPEN                HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 1)
-#define HPI_OSTREAM_CLOSE               HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 2)
-#define HPI_OSTREAM_WRITE               HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 3)
-#define HPI_OSTREAM_START               HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 4)
-#define HPI_OSTREAM_STOP                HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 5)
-#define HPI_OSTREAM_RESET               HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 6)
-#define HPI_OSTREAM_GET_INFO            HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 7)
-#define HPI_OSTREAM_QUERY_FORMAT        HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 8)
-#define HPI_OSTREAM_DATA                HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 9)
-#define HPI_OSTREAM_SET_VELOCITY        HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 10)
-#define HPI_OSTREAM_SET_PUNCHINOUT      HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 11)
-#define HPI_OSTREAM_SINEGEN             HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 12)
-#define HPI_OSTREAM_ANC_RESET           HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 13)
-#define HPI_OSTREAM_ANC_GET_INFO        HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 14)
-#define HPI_OSTREAM_ANC_READ            HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 15)
-#define HPI_OSTREAM_SET_TIMESCALE       HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 16)
-#define HPI_OSTREAM_SET_FORMAT          HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 17)
-#define HPI_OSTREAM_HOSTBUFFER_ALLOC    HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 18)
-#define HPI_OSTREAM_HOSTBUFFER_FREE     HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 19)
-#define HPI_OSTREAM_GROUP_ADD           HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 20)
-#define HPI_OSTREAM_GROUP_GETMAP        HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 21)
-#define HPI_OSTREAM_GROUP_RESET         HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 22)
-#define HPI_OSTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 23)
-#define HPI_OSTREAM_WAIT_START          HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 24)
-#define HPI_OSTREAM_FUNCTION_COUNT              24
-/* INPUT STREAM */
-#define HPI_ISTREAM_OPEN                HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 1)
-#define HPI_ISTREAM_CLOSE               HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 2)
-#define HPI_ISTREAM_SET_FORMAT          HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 3)
-#define HPI_ISTREAM_READ                HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 4)
-#define HPI_ISTREAM_START               HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 5)
-#define HPI_ISTREAM_STOP                HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 6)
-#define HPI_ISTREAM_RESET               HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 7)
-#define HPI_ISTREAM_GET_INFO            HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 8)
-#define HPI_ISTREAM_QUERY_FORMAT        HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 9)
-#define HPI_ISTREAM_ANC_RESET           HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 10)
-#define HPI_ISTREAM_ANC_GET_INFO        HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 11)
-#define HPI_ISTREAM_ANC_WRITE           HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 12)
-#define HPI_ISTREAM_HOSTBUFFER_ALLOC    HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 13)
-#define HPI_ISTREAM_HOSTBUFFER_FREE     HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 14)
-#define HPI_ISTREAM_GROUP_ADD           HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 15)
-#define HPI_ISTREAM_GROUP_GETMAP        HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 16)
-#define HPI_ISTREAM_GROUP_RESET         HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 17)
-#define HPI_ISTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 18)
-#define HPI_ISTREAM_WAIT_START          HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 19)
-#define HPI_ISTREAM_FUNCTION_COUNT              19
-/* MIXER */
+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_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_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_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),
+       HPI_SUBSYS_OPTION_INFO = HPI_FUNC_ID(SUBSYSTEM, 15),
+       HPI_SUBSYS_OPTION_GET = HPI_FUNC_ID(SUBSYSTEM, 16),
+       HPI_SUBSYS_OPTION_SET = HPI_FUNC_ID(SUBSYSTEM, 17),
+#define HPI_SUBSYS_FUNCTION_COUNT 17
+
+       HPI_ADAPTER_OPEN = HPI_FUNC_ID(ADAPTER, 1),
+       HPI_ADAPTER_CLOSE = HPI_FUNC_ID(ADAPTER, 2),
+       HPI_ADAPTER_GET_INFO = HPI_FUNC_ID(ADAPTER, 3),
+       HPI_ADAPTER_GET_ASSERT = HPI_FUNC_ID(ADAPTER, 4),
+       HPI_ADAPTER_TEST_ASSERT = HPI_FUNC_ID(ADAPTER, 5),
+       HPI_ADAPTER_SET_MODE = HPI_FUNC_ID(ADAPTER, 6),
+       HPI_ADAPTER_GET_MODE = HPI_FUNC_ID(ADAPTER, 7),
+       HPI_ADAPTER_ENABLE_CAPABILITY = HPI_FUNC_ID(ADAPTER, 8),
+       HPI_ADAPTER_SELFTEST = HPI_FUNC_ID(ADAPTER, 9),
+       HPI_ADAPTER_FIND_OBJECT = HPI_FUNC_ID(ADAPTER, 10),
+       HPI_ADAPTER_QUERY_FLASH = HPI_FUNC_ID(ADAPTER, 11),
+       HPI_ADAPTER_START_FLASH = HPI_FUNC_ID(ADAPTER, 12),
+       HPI_ADAPTER_PROGRAM_FLASH = HPI_FUNC_ID(ADAPTER, 13),
+       HPI_ADAPTER_SET_PROPERTY = HPI_FUNC_ID(ADAPTER, 14),
+       HPI_ADAPTER_GET_PROPERTY = HPI_FUNC_ID(ADAPTER, 15),
+       HPI_ADAPTER_ENUM_PROPERTY = HPI_FUNC_ID(ADAPTER, 16),
+       HPI_ADAPTER_MODULE_INFO = HPI_FUNC_ID(ADAPTER, 17),
+       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_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
+       HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
+       HPI_OSTREAM_WRITE = HPI_FUNC_ID(OSTREAM, 3),
+       HPI_OSTREAM_START = HPI_FUNC_ID(OSTREAM, 4),
+       HPI_OSTREAM_STOP = HPI_FUNC_ID(OSTREAM, 5),
+       HPI_OSTREAM_RESET = HPI_FUNC_ID(OSTREAM, 6),
+       HPI_OSTREAM_GET_INFO = HPI_FUNC_ID(OSTREAM, 7),
+       HPI_OSTREAM_QUERY_FORMAT = HPI_FUNC_ID(OSTREAM, 8),
+       HPI_OSTREAM_DATA = HPI_FUNC_ID(OSTREAM, 9),
+       HPI_OSTREAM_SET_VELOCITY = HPI_FUNC_ID(OSTREAM, 10),
+       HPI_OSTREAM_SET_PUNCHINOUT = HPI_FUNC_ID(OSTREAM, 11),
+       HPI_OSTREAM_SINEGEN = HPI_FUNC_ID(OSTREAM, 12),
+       HPI_OSTREAM_ANC_RESET = HPI_FUNC_ID(OSTREAM, 13),
+       HPI_OSTREAM_ANC_GET_INFO = HPI_FUNC_ID(OSTREAM, 14),
+       HPI_OSTREAM_ANC_READ = HPI_FUNC_ID(OSTREAM, 15),
+       HPI_OSTREAM_SET_TIMESCALE = HPI_FUNC_ID(OSTREAM, 16),
+       HPI_OSTREAM_SET_FORMAT = HPI_FUNC_ID(OSTREAM, 17),
+       HPI_OSTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(OSTREAM, 18),
+       HPI_OSTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(OSTREAM, 19),
+       HPI_OSTREAM_GROUP_ADD = HPI_FUNC_ID(OSTREAM, 20),
+       HPI_OSTREAM_GROUP_GETMAP = HPI_FUNC_ID(OSTREAM, 21),
+       HPI_OSTREAM_GROUP_RESET = HPI_FUNC_ID(OSTREAM, 22),
+       HPI_OSTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(OSTREAM, 23),
+       HPI_OSTREAM_WAIT_START = HPI_FUNC_ID(OSTREAM, 24),
+       HPI_OSTREAM_WAIT = HPI_FUNC_ID(OSTREAM, 25),
+#define HPI_OSTREAM_FUNCTION_COUNT 25
+
+       HPI_ISTREAM_OPEN = HPI_FUNC_ID(ISTREAM, 1),
+       HPI_ISTREAM_CLOSE = HPI_FUNC_ID(ISTREAM, 2),
+       HPI_ISTREAM_SET_FORMAT = HPI_FUNC_ID(ISTREAM, 3),
+       HPI_ISTREAM_READ = HPI_FUNC_ID(ISTREAM, 4),
+       HPI_ISTREAM_START = HPI_FUNC_ID(ISTREAM, 5),
+       HPI_ISTREAM_STOP = HPI_FUNC_ID(ISTREAM, 6),
+       HPI_ISTREAM_RESET = HPI_FUNC_ID(ISTREAM, 7),
+       HPI_ISTREAM_GET_INFO = HPI_FUNC_ID(ISTREAM, 8),
+       HPI_ISTREAM_QUERY_FORMAT = HPI_FUNC_ID(ISTREAM, 9),
+       HPI_ISTREAM_ANC_RESET = HPI_FUNC_ID(ISTREAM, 10),
+       HPI_ISTREAM_ANC_GET_INFO = HPI_FUNC_ID(ISTREAM, 11),
+       HPI_ISTREAM_ANC_WRITE = HPI_FUNC_ID(ISTREAM, 12),
+       HPI_ISTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(ISTREAM, 13),
+       HPI_ISTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(ISTREAM, 14),
+       HPI_ISTREAM_GROUP_ADD = HPI_FUNC_ID(ISTREAM, 15),
+       HPI_ISTREAM_GROUP_GETMAP = HPI_FUNC_ID(ISTREAM, 16),
+       HPI_ISTREAM_GROUP_RESET = HPI_FUNC_ID(ISTREAM, 17),
+       HPI_ISTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(ISTREAM, 18),
+       HPI_ISTREAM_WAIT_START = HPI_FUNC_ID(ISTREAM, 19),
+       HPI_ISTREAM_WAIT = HPI_FUNC_ID(ISTREAM, 20),
+#define HPI_ISTREAM_FUNCTION_COUNT 20
+
 /* NOTE:
    GET_NODE_INFO, SET_CONNECTION, GET_CONNECTIONS are not currently used */
-#define HPI_MIXER_OPEN                  HPI_MAKE_INDEX(HPI_OBJ_MIXER, 1)
-#define HPI_MIXER_CLOSE                 HPI_MAKE_INDEX(HPI_OBJ_MIXER, 2)
-#define HPI_MIXER_GET_INFO              HPI_MAKE_INDEX(HPI_OBJ_MIXER, 3)
-#define HPI_MIXER_GET_NODE_INFO         HPI_MAKE_INDEX(HPI_OBJ_MIXER, 4)
-#define HPI_MIXER_GET_CONTROL           HPI_MAKE_INDEX(HPI_OBJ_MIXER, 5)
-#define HPI_MIXER_SET_CONNECTION        HPI_MAKE_INDEX(HPI_OBJ_MIXER, 6)
-#define HPI_MIXER_GET_CONNECTIONS       HPI_MAKE_INDEX(HPI_OBJ_MIXER, 7)
-#define HPI_MIXER_GET_CONTROL_BY_INDEX  HPI_MAKE_INDEX(HPI_OBJ_MIXER, 8)
-#define HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX  HPI_MAKE_INDEX(HPI_OBJ_MIXER, 9)
-#define HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES HPI_MAKE_INDEX(HPI_OBJ_MIXER, 10)
-#define HPI_MIXER_STORE                 HPI_MAKE_INDEX(HPI_OBJ_MIXER, 11)
-#define HPI_MIXER_FUNCTION_COUNT        11
-/* MIXER CONTROLS */
-#define HPI_CONTROL_GET_INFO            HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 1)
-#define HPI_CONTROL_GET_STATE           HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 2)
-#define HPI_CONTROL_SET_STATE           HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 3)
+       HPI_MIXER_OPEN = HPI_FUNC_ID(MIXER, 1),
+       HPI_MIXER_CLOSE = HPI_FUNC_ID(MIXER, 2),
+       HPI_MIXER_GET_INFO = HPI_FUNC_ID(MIXER, 3),
+       HPI_MIXER_GET_NODE_INFO = HPI_FUNC_ID(MIXER, 4),
+       HPI_MIXER_GET_CONTROL = HPI_FUNC_ID(MIXER, 5),
+       HPI_MIXER_SET_CONNECTION = HPI_FUNC_ID(MIXER, 6),
+       HPI_MIXER_GET_CONNECTIONS = HPI_FUNC_ID(MIXER, 7),
+       HPI_MIXER_GET_CONTROL_BY_INDEX = HPI_FUNC_ID(MIXER, 8),
+       HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX = HPI_FUNC_ID(MIXER, 9),
+       HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES = HPI_FUNC_ID(MIXER, 10),
+       HPI_MIXER_STORE = HPI_FUNC_ID(MIXER, 11),
+       HPI_MIXER_GET_CACHE_INFO = HPI_FUNC_ID(MIXER, 12),
+#define HPI_MIXER_FUNCTION_COUNT 12
+
+       HPI_CONTROL_GET_INFO = HPI_FUNC_ID(CONTROL, 1),
+       HPI_CONTROL_GET_STATE = HPI_FUNC_ID(CONTROL, 2),
+       HPI_CONTROL_SET_STATE = HPI_FUNC_ID(CONTROL, 3),
 #define HPI_CONTROL_FUNCTION_COUNT 3
-/* NONVOL MEMORY */
-#define HPI_NVMEMORY_OPEN               HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 1)
-#define HPI_NVMEMORY_READ_BYTE          HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 2)
-#define HPI_NVMEMORY_WRITE_BYTE         HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 3)
+
+       HPI_NVMEMORY_OPEN = HPI_FUNC_ID(NVMEMORY, 1),
+       HPI_NVMEMORY_READ_BYTE = HPI_FUNC_ID(NVMEMORY, 2),
+       HPI_NVMEMORY_WRITE_BYTE = HPI_FUNC_ID(NVMEMORY, 3),
 #define HPI_NVMEMORY_FUNCTION_COUNT 3
-/* GPIO */
-#define HPI_GPIO_OPEN                   HPI_MAKE_INDEX(HPI_OBJ_GPIO, 1)
-#define HPI_GPIO_READ_BIT               HPI_MAKE_INDEX(HPI_OBJ_GPIO, 2)
-#define HPI_GPIO_WRITE_BIT              HPI_MAKE_INDEX(HPI_OBJ_GPIO, 3)
-#define HPI_GPIO_READ_ALL               HPI_MAKE_INDEX(HPI_OBJ_GPIO, 4)
-#define HPI_GPIO_WRITE_STATUS           HPI_MAKE_INDEX(HPI_OBJ_GPIO, 5)
+
+       HPI_GPIO_OPEN = HPI_FUNC_ID(GPIO, 1),
+       HPI_GPIO_READ_BIT = HPI_FUNC_ID(GPIO, 2),
+       HPI_GPIO_WRITE_BIT = HPI_FUNC_ID(GPIO, 3),
+       HPI_GPIO_READ_ALL = HPI_FUNC_ID(GPIO, 4),
+       HPI_GPIO_WRITE_STATUS = HPI_FUNC_ID(GPIO, 5),
 #define HPI_GPIO_FUNCTION_COUNT 5
-/* ASYNC EVENT */
-#define HPI_ASYNCEVENT_OPEN             HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 1)
-#define HPI_ASYNCEVENT_CLOSE            HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 2)
-#define HPI_ASYNCEVENT_WAIT             HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 3)
-#define HPI_ASYNCEVENT_GETCOUNT         HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 4)
-#define HPI_ASYNCEVENT_GET              HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 5)
-#define HPI_ASYNCEVENT_SENDEVENTS       HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 6)
+
+       HPI_ASYNCEVENT_OPEN = HPI_FUNC_ID(ASYNCEVENT, 1),
+       HPI_ASYNCEVENT_CLOSE = HPI_FUNC_ID(ASYNCEVENT, 2),
+       HPI_ASYNCEVENT_WAIT = HPI_FUNC_ID(ASYNCEVENT, 3),
+       HPI_ASYNCEVENT_GETCOUNT = HPI_FUNC_ID(ASYNCEVENT, 4),
+       HPI_ASYNCEVENT_GET = HPI_FUNC_ID(ASYNCEVENT, 5),
+       HPI_ASYNCEVENT_SENDEVENTS = HPI_FUNC_ID(ASYNCEVENT, 6),
 #define HPI_ASYNCEVENT_FUNCTION_COUNT 6
-/* WATCH-DOG */
-#define HPI_WATCHDOG_OPEN               HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 1)
-#define HPI_WATCHDOG_SET_TIME           HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 2)
-#define HPI_WATCHDOG_PING               HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 3)
-/* CLOCK */
-#define HPI_CLOCK_OPEN                  HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 1)
-#define HPI_CLOCK_SET_TIME              HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 2)
-#define HPI_CLOCK_GET_TIME              HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 3)
-/* PROFILE */
-#define HPI_PROFILE_OPEN_ALL            HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 1)
-#define HPI_PROFILE_START_ALL           HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 2)
-#define HPI_PROFILE_STOP_ALL            HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 3)
-#define HPI_PROFILE_GET                 HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 4)
-#define HPI_PROFILE_GET_IDLECOUNT       HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 5)
-#define HPI_PROFILE_GET_NAME            HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 6)
-#define HPI_PROFILE_GET_UTILIZATION     HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 7)
+
+       HPI_WATCHDOG_OPEN = HPI_FUNC_ID(WATCHDOG, 1),
+       HPI_WATCHDOG_SET_TIME = HPI_FUNC_ID(WATCHDOG, 2),
+       HPI_WATCHDOG_PING = HPI_FUNC_ID(WATCHDOG, 3),
+
+       HPI_CLOCK_OPEN = HPI_FUNC_ID(CLOCK, 1),
+       HPI_CLOCK_SET_TIME = HPI_FUNC_ID(CLOCK, 2),
+       HPI_CLOCK_GET_TIME = HPI_FUNC_ID(CLOCK, 3),
+
+       HPI_PROFILE_OPEN_ALL = HPI_FUNC_ID(PROFILE, 1),
+       HPI_PROFILE_START_ALL = HPI_FUNC_ID(PROFILE, 2),
+       HPI_PROFILE_STOP_ALL = HPI_FUNC_ID(PROFILE, 3),
+       HPI_PROFILE_GET = HPI_FUNC_ID(PROFILE, 4),
+       HPI_PROFILE_GET_IDLECOUNT = HPI_FUNC_ID(PROFILE, 5),
+       HPI_PROFILE_GET_NAME = HPI_FUNC_ID(PROFILE, 6),
+       HPI_PROFILE_GET_UTILIZATION = HPI_FUNC_ID(PROFILE, 7)
 #define HPI_PROFILE_FUNCTION_COUNT 7
-/* ////////////////////////////////////////////////////////////////////// */
-/* PRIVATE ATTRIBUTES */
+};
 
 /* ////////////////////////////////////////////////////////////////////// */
 /* STRUCTURES */
@@ -672,18 +552,7 @@ Threshold is a -ve number in units of dB/100,
 /** PCI bus resource */
 struct hpi_pci {
        u32 __iomem *ap_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
-       struct pci_dev *p_os_data;
-
-#ifndef HPI64BIT               /* keep structure size constant */
-       u32 padding[HPI_MAX_ADAPTER_MEM_SPACES + 1];
-#endif
-       u16 vendor_id;
-       u16 device_id;
-       u16 subsys_vendor_id;
-       u16 subsys_device_id;
-       u16 bus_number;
-       u16 device_number;
-       u32 interrupt;
+       struct pci_dev *pci_dev;
 };
 
 struct hpi_resource {
@@ -702,12 +571,10 @@ struct hpi_resource {
 /** Format info used inside struct hpi_message
     Not the same as public API struct hpi_format */
 struct hpi_msg_format {
-       u32 sample_rate;
-                               /**< 11025, 32000, 44100 ... */
-       u32 bit_rate;         /**< for MPEG */
-       u32 attributes;
-                               /**< Stereo/JointStereo/Mono */
-       u16 channels;         /**< 1,2..., (or ancillary mode or idle bit */
+       u32 sample_rate; /**< 11025, 32000, 44100 etc. */
+       u32 bit_rate; /**< for MPEG */
+       u32 attributes; /**< stereo/joint_stereo/mono */
+       u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
        u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see \ref HPI_FORMATS. */
 };
 
@@ -742,7 +609,7 @@ struct hpi_data_compat32 {
 struct hpi_buffer {
   /** placehoder for backward compatability (see dwBufferSize) */
        struct hpi_msg_format reserved;
-       u32 command;    /**< HPI_BUFFER_CMD_xxx*/
+       u32 command; /**< HPI_BUFFER_CMD_xxx*/
        u32 pci_address; /**< PCI physical address of buffer for DSP DMA */
        u32 buffer_size; /**< must line up with data_size of HPI_DATA*/
 };
@@ -777,30 +644,25 @@ struct hpi_subsys_msg {
 
 struct hpi_subsys_res {
        u32 version;
-       u32 data;               /* used to return extended version */
-       u16 num_adapters;       /* number of adapters */
+       u32 data;               /* extended version */
+       u16 num_adapters;
        u16 adapter_index;
-       u16 aw_adapter_list[HPI_MAX_ADAPTERS];
-};
-
-struct hpi_adapter_msg {
-       u32 adapter_mode;       /* adapter mode */
-       u16 assert_id;          /* assert number for "test assert" call
-                                  object_index for find object call
-                                  query_or_set for hpi_adapter_set_mode_ex() */
-       u16 object_type;        /* for adapter find object call */
+       u16 adapter_type;
+       u16 pad16;
 };
 
 union hpi_adapterx_msg {
-       struct hpi_adapter_msg adapter;
        struct {
-               u32 offset;
-       } query_flash;
+               u32 dsp_address;
+               u32 count_bytes;
+       } debug_read;
        struct {
-               u32 offset;
-               u32 length;
-               u32 key;
-       } start_flash;
+               u32 adapter_mode;
+               u16 query_or_set;
+       } mode;
+       struct {
+               u16 index;
+       } module_info;
        struct {
                u32 checksum;
                u16 sequence;
@@ -808,29 +670,42 @@ union hpi_adapterx_msg {
                u16 offset; /**< offset from start of msg to data */
                u16 unused;
        } program_flash;
+       struct {
+               u16 index;
+               u16 what;
+               u16 property_index;
+       } property_enum;
        struct {
                u16 property;
                u16 parameter1;
                u16 parameter2;
        } property_set;
        struct {
-               u16 index;
-               u16 what;
-               u16 property_index;
-       } property_enum;
+               u32 offset;
+       } query_flash;
        struct {
-               u16 index;
-       } module_info;
+               u32 pad32;
+               u16 key1;
+               u16 key2;
+       } restart;
        struct {
-               u32 dsp_address;
-               u32 count_bytes;
-       } debug_read;
+               u32 offset;
+               u32 length;
+               u32 key;
+       } start_flash;
+       struct {
+               u32 pad32;
+               u16 value;
+       } test_assert;
+       struct {
+               u32 yes;
+       } irq_query;
 };
 
 struct hpi_adapter_res {
        u32 serial_number;
        u16 adapter_type;
-       u16 adapter_index;      /* is this needed? also used for dsp_index */
+       u16 adapter_index;
        u16 num_instreams;
        u16 num_outstreams;
        u16 num_mixers;
@@ -839,12 +714,18 @@ struct hpi_adapter_res {
 };
 
 union hpi_adapterx_res {
-       struct hpi_adapter_res adapter;
+       struct hpi_adapter_res info;
        struct {
-               u32 checksum;
-               u32 length;
-               u32 version;
-       } query_flash;
+               u32 p1;
+               u16 count;
+               u16 dsp_index;
+               u32 p2;
+               u32 dsp_msg_addr;
+               char sz_message[HPI_STRING_LEN];
+       } assert;
+       struct {
+               u32 adapter_mode;
+       } mode;
        struct {
                u16 sequence;
        } program_flash;
@@ -852,6 +733,14 @@ union hpi_adapterx_res {
                u16 parameter1;
                u16 parameter2;
        } property_get;
+       struct {
+               u32 checksum;
+               u32 length;
+               u32 version;
+       } query_flash;
+       struct {
+               u32 yes;
+       } irq_query;
 };
 
 struct hpi_stream_msg {
@@ -863,6 +752,7 @@ struct hpi_stream_msg {
                u32 time_scale;
                struct hpi_buffer buffer;
                struct hpi_streamid stream;
+               u32 threshold_bytes;
        } u;
 };
 
@@ -911,7 +801,7 @@ struct hpi_stream_res {
 struct hpi_mixer_msg {
        u16 control_index;
        u16 control_type;       /* = HPI_CONTROL_METER _VOLUME etc */
-       u16 padding1;           /* maintain alignment of subsequent fields */
+       u16 padding1;           /* Maintain alignment of subsequent fields */
        u16 node_type1;         /* = HPI_SOURCENODE_LINEIN etc */
        u16 node_index1;        /* = 0..N */
        u16 node_type2;
@@ -949,6 +839,11 @@ union hpi_mixerx_res {
                u32 p_data;     /* pointer to data array */
                u16 more_to_do; /* indicates if there is more to do */
        } gcabi;
+       struct {
+               u32 total_controls;     /* count of controls in the mixer */
+               u32 cache_controls;     /* count of controls in the cac */
+               u32 cache_bytes;        /* size of cache */
+       } cache_info;
 };
 
 struct hpi_control_msg {
@@ -1000,12 +895,16 @@ union hpi_control_union_res {
                u32 band;
                u32 frequency;
                u32 gain;
-               u32 level;
                u32 deemphasis;
                struct {
                        u32 data[2];
                        u32 bLER;
                } rds;
+               short s_level;
+               struct {
+                       u16 value;
+                       u16 mask;
+               } status;
        } tuner;
        struct {
                char sz_data[8];
@@ -1178,11 +1077,11 @@ struct hpi_profile_res_open {
 };
 
 struct hpi_profile_res_time {
-       u32 micro_seconds;
+       u32 total_tick_count;
        u32 call_count;
-       u32 max_micro_seconds;
-       u32 min_micro_seconds;
-       u16 seconds;
+       u32 max_tick_count;
+       u32 ticks_per_millisecond;
+       u16 profile_interval;
 };
 
 struct hpi_profile_res_name {
@@ -1218,7 +1117,6 @@ struct hpi_message {
        u16 obj_index;          /*  */
        union {
                struct hpi_subsys_msg s;
-               struct hpi_adapter_msg a;
                union hpi_adapterx_msg ax;
                struct hpi_stream_msg d;
                struct hpi_mixer_msg m;
@@ -1239,7 +1137,7 @@ struct hpi_message {
 };
 
 #define HPI_MESSAGE_SIZE_BY_OBJECT { \
-       sizeof(struct hpi_message_header) ,   /* default, no object type 0 */ \
+       sizeof(struct hpi_message_header) ,   /* Default, no object type 0 */ \
        sizeof(struct hpi_message_header) + sizeof(struct hpi_subsys_msg),\
        sizeof(struct hpi_message_header) + sizeof(union hpi_adapterx_msg),\
        sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\
@@ -1256,6 +1154,11 @@ struct hpi_message {
        sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \
 }
 
+/*
+Note that the wSpecificError error field should be inspected and potentially
+reported whenever HPI_ERROR_DSP_COMMUNICATION or HPI_ERROR_DSP_BOOTLOAD is
+returned in wError.
+*/
 struct hpi_response_header {
        u16 size;
        u8 type;                /* HPI_TYPE_RESPONSE  */
@@ -1277,7 +1180,6 @@ struct hpi_response {
        u16 specific_error;     /* adapter specific error */
        union {
                struct hpi_subsys_res s;
-               struct hpi_adapter_res a;
                union hpi_adapterx_res ax;
                struct hpi_stream_res d;
                struct hpi_mixer_res m;
@@ -1297,7 +1199,7 @@ struct hpi_response {
 };
 
 #define HPI_RESPONSE_SIZE_BY_OBJECT { \
-       sizeof(struct hpi_response_header) ,/* default, no object type 0 */ \
+       sizeof(struct hpi_response_header) ,/* Default, no object type 0 */ \
        sizeof(struct hpi_response_header) + sizeof(struct hpi_subsys_res),\
        sizeof(struct hpi_response_header) + sizeof(union  hpi_adapterx_res),\
        sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\
@@ -1314,7 +1216,7 @@ struct hpi_response {
        sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \
 }
 
-/*********************** version 1 message/response *****************************/
+/*********************** version 1 message/response **************************/
 #define HPINET_ETHERNET_DATA_SIZE (1500)
 #define HPINET_IP_HDR_SIZE (20)
 #define HPINET_IP_DATA_SIZE (HPINET_ETHERNET_DATA_SIZE - HPINET_IP_HDR_SIZE)
@@ -1394,6 +1296,17 @@ struct hpi_res_adapter_program_flash {
                sizeof(struct hpi_response_header) - sizeof(u16)];
 };
 
+struct hpi_msg_adapter_debug_read {
+       struct hpi_message_header h;
+       u32 dsp_address;
+       u32 count_bytes;
+};
+
+struct hpi_res_adapter_debug_read {
+       struct hpi_response_header h;
+       u8 bytes[256];
+};
+
 #if 1
 #define hpi_message_header_v1 hpi_message_header
 #define hpi_response_header_v1 hpi_response_header
@@ -1414,23 +1327,10 @@ struct hpi_response_header_v1 {
 };
 #endif
 
-/* STRV HPI Packet */
-struct hpi_msg_strv {
-       struct hpi_message_header h;
-       struct hpi_entity strv;
-};
-
-struct hpi_res_strv {
-       struct hpi_response_header h;
-       struct hpi_entity strv;
-};
-#define MIN_STRV_PACKET_SIZE sizeof(struct hpi_res_strv)
-
 struct hpi_msg_payload_v0 {
        struct hpi_message_header h;
        union {
                struct hpi_subsys_msg s;
-               struct hpi_adapter_msg a;
                union hpi_adapterx_msg ax;
                struct hpi_stream_msg d;
                struct hpi_mixer_msg m;
@@ -1451,7 +1351,6 @@ struct hpi_res_payload_v0 {
        struct hpi_response_header h;
        union {
                struct hpi_subsys_res s;
-               struct hpi_adapter_res a;
                union hpi_adapterx_res ax;
                struct hpi_stream_res d;
                struct hpi_mixer_res m;
@@ -1471,13 +1370,13 @@ struct hpi_res_payload_v0 {
 union hpi_message_buffer_v1 {
        struct hpi_message m0;  /* version 0 */
        struct hpi_message_header_v1 h;
-       unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
+       u8 buf[HPI_MAX_PAYLOAD_SIZE];
 };
 
 union hpi_response_buffer_v1 {
        struct hpi_response r0; /* version 0 */
        struct hpi_response_header_v1 h;
-       unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
+       u8 buf[HPI_MAX_PAYLOAD_SIZE];
 };
 
 compile_time_assert((sizeof(union hpi_message_buffer_v1) <=
@@ -1499,6 +1398,11 @@ struct hpi_control_defn {
 /*////////////////////////////////////////////////////////////////////////// */
 /* declarations for control caching (internal to HPI<->DSP interaction)      */
 
+/** indicates a cached u16 value is invalid. */
+#define HPI_CACHE_INVALID_UINT16 0xFFFF
+/** indicates a cached short value is invalid. */
+#define HPI_CACHE_INVALID_SHORT -32768
+
 /** A compact representation of (part of) a controls state.
 Used for efficient transfer of the control state
 between DSP and host or across a network
@@ -1512,58 +1416,104 @@ struct hpi_control_cache_info {
        u16 control_index;
 };
 
-struct hpi_control_cache_single {
+struct hpi_control_cache_vol {
+       struct hpi_control_cache_info i;
+       short an_log[2];
+       unsigned short flags;
+       char padding[2];
+};
+
+struct hpi_control_cache_meter {
+       struct hpi_control_cache_info i;
+       short an_log_peak[2];
+       short an_logRMS[2];
+};
+
+struct hpi_control_cache_channelmode {
+       struct hpi_control_cache_info i;
+       u16 mode;
+       char temp_padding[6];
+};
+
+struct hpi_control_cache_mux {
+       struct hpi_control_cache_info i;
+       u16 source_node_type;
+       u16 source_node_index;
+       char temp_padding[4];
+};
+
+struct hpi_control_cache_level {
        struct hpi_control_cache_info i;
+       short an_log[2];
+       char temp_padding[4];
+};
+
+struct hpi_control_cache_tuner {
+       struct hpi_control_cache_info i;
+       u32 freq_ink_hz;
+       u16 band;
+       short s_level_avg;
+};
+
+struct hpi_control_cache_aes3rx {
+       struct hpi_control_cache_info i;
+       u32 error_status;
+       u32 format;
+};
+
+struct hpi_control_cache_aes3tx {
+       struct hpi_control_cache_info i;
+       u32 format;
+       char temp_padding[4];
+};
+
+struct hpi_control_cache_tonedetector {
+       struct hpi_control_cache_info i;
+       u16 state;
+       char temp_padding[6];
+};
+
+struct hpi_control_cache_silencedetector {
+       struct hpi_control_cache_info i;
+       u32 state;
+       char temp_padding[4];
+};
+
+struct hpi_control_cache_sampleclock {
+       struct hpi_control_cache_info i;
+       u16 source;
+       u16 source_index;
+       u32 sample_rate;
+};
+
+struct hpi_control_cache_microphone {
+       struct hpi_control_cache_info i;
+       u16 phantom_state;
+       char temp_padding[6];
+};
+
+struct hpi_control_cache_generic {
+       struct hpi_control_cache_info i;
+       u32 dw1;
+       u32 dw2;
+};
+
+struct hpi_control_cache_single {
        union {
-               struct {        /* volume */
-                       short an_log[2];
-               } v;
-               struct {        /* peak meter */
-                       short an_log_peak[2];
-                       short an_logRMS[2];
-               } p;
-               struct {        /* channel mode */
-                       u16 mode;
-               } m;
-               struct {        /* multiplexer */
-                       u16 source_node_type;
-                       u16 source_node_index;
-               } x;
-               struct {        /* level/trim */
-                       short an_log[2];
-               } l;
-               struct {        /* tuner - partial caching.
-                                  some attributes go to the DSP. */
-                       u32 freq_ink_hz;
-                       u16 band;
-                       u16 level;
-               } t;
-               struct {        /* AESEBU rx status */
-                       u32 error_status;
-                       u32 source;
-               } aes3rx;
-               struct {        /* AESEBU tx */
-                       u32 format;
-               } aes3tx;
-               struct {        /* tone detector */
-                       u16 state;
-               } tone;
-               struct {        /* silence detector */
-                       u32 state;
-                       u32 count;
-               } silence;
-               struct {        /* sample clock */
-                       u16 source;
-                       u16 source_index;
-                       u32 sample_rate;
-               } clk;
-               struct {        /* microphone control */
-                       u16 state;
-               } phantom_power;
-               struct {        /* generic control */
-                       u32 dw1;
-                       u32 dw2;
-               } g;
+               struct hpi_control_cache_info i;
+               struct hpi_control_cache_vol vol;
+               struct hpi_control_cache_meter meter;
+               struct hpi_control_cache_channelmode mode;
+               struct hpi_control_cache_mux mux;
+               struct hpi_control_cache_level level;
+               struct hpi_control_cache_tuner tuner;
+               struct hpi_control_cache_aes3rx aes3rx;
+               struct hpi_control_cache_aes3tx aes3tx;
+               struct hpi_control_cache_tonedetector tone;
+               struct hpi_control_cache_silencedetector silence;
+               struct hpi_control_cache_sampleclock clk;
+               struct hpi_control_cache_microphone microphone;
+               struct hpi_control_cache_generic generic;
        } u;
 };
 
@@ -1580,8 +1530,7 @@ struct hpi_control_cache_pad {
        u32 traffic_anouncement;
 };
 
-/*/////////////////////////////////////////////////////////////////////////// */
-/* declarations for 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
+/* 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
 struct hpi_fifo_buffer {
        u32 size;
        u32 dSP_index;
@@ -1606,25 +1555,18 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
 /*////////////////////////////////////////////////////////////////////////// */
 
 /* main HPI entry point */
-hpi_handler_func hpi_send_recv;
-
-/* UDP message */
-void hpi_send_recvUDP(struct hpi_message *phm, struct hpi_response *phr,
-       const unsigned int timeout);
+void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
 
 /* used in PnP OS/driver */
-u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
-       const struct hpi_resource *p_resource, u16 *pw_adapter_index);
+u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
+       u16 *pw_adapter_index);
 
-u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index);
+u16 hpi_subsys_delete_adapter(u16 adapter_index);
 
-u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u8 **pp_buffer,
+u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
        struct hpi_hostbuffer_status **pp_status);
 
-u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u8 **pp_buffer,
+u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
        struct hpi_hostbuffer_status **pp_status);
 
 u16 hpi_adapter_restart(u16 adapter_index);
index d67f4d3db911dd9685d24e08f29d6b6bc23f16f9..3e9c5c289764d7bc1923169853950028f95e129d 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "hpi_internal.h"
 #include "hpidebug.h"
+#include "hpimsginit.h"
+
 #include "hpicmn.h"
 
 struct hpi_adapters_list {
@@ -43,14 +45,24 @@ static struct hpi_adapters_list adapters;
 **/
 u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
 {
-       u16 error = 0;
+       if (phr->type != HPI_TYPE_RESPONSE) {
+               HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type);
+               return HPI_ERROR_INVALID_RESPONSE;
+       }
 
-       if ((phr->type != HPI_TYPE_RESPONSE)
-               || (phr->object != phm->object)
-               || (phr->function != phm->function))
-               error = HPI_ERROR_INVALID_RESPONSE;
+       if (phr->object != phm->object) {
+               HPI_DEBUG_LOG(ERROR, "header object %d invalid\n",
+                       phr->object);
+               return HPI_ERROR_INVALID_RESPONSE;
+       }
+
+       if (phr->function != phm->function) {
+               HPI_DEBUG_LOG(ERROR, "header type %d invalid\n",
+                       phr->function);
+               return HPI_ERROR_INVALID_RESPONSE;
+       }
 
-       return error;
+       return 0;
 }
 
 u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
@@ -66,8 +78,18 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
        }
 
        if (adapters.adapter[pao->index].adapter_type) {
-               {
-                       retval = HPI_DUPLICATE_ADAPTER_NUMBER;
+               int a;
+               for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) {
+                       if (!adapters.adapter[a].adapter_type) {
+                               HPI_DEBUG_LOG(WARNING,
+                                       "ASI%X duplicate index %d moved to %d\n",
+                                       pao->adapter_type, pao->index, a);
+                               pao->index = a;
+                               break;
+                       }
+               }
+               if (a < 0) {
+                       retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;
                        goto unlock;
                }
        }
@@ -76,17 +98,22 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
        adapters.gw_num_adapters++;
 
 unlock:
-       hpios_alistlock_un_lock(&adapters);
+       hpios_alistlock_unlock(&adapters);
        return retval;
 }
 
 void hpi_delete_adapter(struct hpi_adapter_obj *pao)
 {
-       memset(pao, 0, sizeof(struct hpi_adapter_obj));
+       if (!pao->adapter_type) {
+               HPI_DEBUG_LOG(ERROR, "removing null adapter?\n");
+               return;
+       }
 
        hpios_alistlock_lock(&adapters);
-       adapters.gw_num_adapters--;     /* dec the number of adapters */
-       hpios_alistlock_un_lock(&adapters);
+       if (adapters.adapter[pao->index].adapter_type)
+               adapters.gw_num_adapters--;
+       memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0]));
+       hpios_alistlock_unlock(&adapters);
 }
 
 /**
@@ -99,7 +126,7 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
        struct hpi_adapter_obj *pao = NULL;
 
        if (adapter_index >= HPI_MAX_ADAPTERS) {
-               HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ",
+               HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d\n",
                        adapter_index);
                return NULL;
        }
@@ -125,51 +152,34 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
 * wipe an HPI_ADAPTERS_LIST structure.
 *
 **/
-static void wipe_adapter_list(void
-       )
+static void wipe_adapter_list(void)
 {
        memset(&adapters, 0, sizeof(adapters));
 }
 
-/**
-* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure
-* with all adapters in the given HPI_ADAPTERS_LIST.
-*
-*/
-static void subsys_get_adapters(struct hpi_response *phr)
+static void subsys_get_adapter(struct hpi_message *phm,
+       struct hpi_response *phr)
 {
-       /* fill in the response adapter array with the position */
-       /* identified by the adapter number/index of the adapters in */
-       /* this HPI */
-       /* i.e. if we have an A120 with it's jumper set to */
-       /* Adapter Number 2 then put an Adapter type A120 in the */
-       /* array in position 1 */
-       /* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */
-
-       /* input:  NONE */
-       /* output: wNumAdapters */
-       /*                 awAdapter[] */
-       /* */
-
-       short i;
-       struct hpi_adapter_obj *pao = NULL;
+       int count = phm->obj_index;
+       u16 index = 0;
 
-       HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n");
-
-       /* for each adapter, place it's type in the position of the array */
-       /* corresponding to it's adapter number */
-       for (i = 0; i < adapters.gw_num_adapters; i++) {
-               pao = &adapters.adapter[i];
-               if (phr->u.s.aw_adapter_list[pao->index] != 0) {
-                       phr->error = HPI_DUPLICATE_ADAPTER_NUMBER;
-                       phr->specific_error = pao->index;
-                       return;
+       /* find the nCount'th nonzero adapter in array */
+       for (index = 0; index < HPI_MAX_ADAPTERS; index++) {
+               if (adapters.adapter[index].adapter_type) {
+                       if (!count)
+                               break;
+                       count--;
                }
-               phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type;
        }
 
-       phr->u.s.num_adapters = adapters.gw_num_adapters;
-       phr->error = 0; /* the function completed OK; */
+       if (index < HPI_MAX_ADAPTERS) {
+               phr->u.s.adapter_index = adapters.adapter[index].index;
+               phr->u.s.adapter_type = adapters.adapter[index].adapter_type;
+       } else {
+               phr->u.s.adapter_index = 0;
+               phr->u.s.adapter_type = 0;
+               phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
+       }
 }
 
 static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
@@ -178,67 +188,98 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
        int cached = 0;
        if (!pC)
                return 0;
-       if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count)
-               && (pC->cache_size_in_bytes)
-               ) {
-               u32 *p_master_cache;
-               pC->init = 1;
-
-               p_master_cache = (u32 *)pC->p_cache;
-               HPI_DEBUG_LOG(VERBOSE, "check %d controls\n",
+
+       if (pC->init)
+               return pC->init;
+
+       if (!pC->p_cache)
+               return 0;
+
+       if (pC->control_count && pC->cache_size_in_bytes) {
+               char *p_master_cache;
+               unsigned int byte_count = 0;
+
+               p_master_cache = (char *)pC->p_cache;
+               HPI_DEBUG_LOG(DEBUG, "check %d controls\n",
                        pC->control_count);
                for (i = 0; i < pC->control_count; i++) {
                        struct hpi_control_cache_info *info =
                                (struct hpi_control_cache_info *)
-                               p_master_cache;
+                               &p_master_cache[byte_count];
+
+                       if (!info->size_in32bit_words) {
+                               if (!i) {
+                                       HPI_DEBUG_LOG(INFO,
+                                               "adap %d cache not ready?\n",
+                                               pC->adap_idx);
+                                       return 0;
+                               }
+                               /* The cache is invalid.
+                                * Minimum valid entry size is
+                                * sizeof(struct hpi_control_cache_info)
+                                */
+                               HPI_DEBUG_LOG(ERROR,
+                                       "adap %d zero size cache entry %d\n",
+                                       pC->adap_idx, i);
+                               break;
+                       }
 
                        if (info->control_type) {
-                               pC->p_info[i] = info;
+                               pC->p_info[info->control_index] = info;
                                cached++;
-                       } else
-                               pC->p_info[i] = NULL;
+                       } else  /* dummy cache entry */
+                               pC->p_info[info->control_index] = NULL;
 
-                       if (info->size_in32bit_words)
-                               p_master_cache += info->size_in32bit_words;
-                       else
-                               p_master_cache +=
-                                       sizeof(struct
-                                       hpi_control_cache_single) /
-                                       sizeof(u32);
+                       byte_count += info->size_in32bit_words * 4;
 
                        HPI_DEBUG_LOG(VERBOSE,
-                               "cached %d, pinfo %p index %d type %d\n",
-                               cached, pC->p_info[i], info->control_index,
-                               info->control_type);
+                               "cached %d, pinfo %p index %d type %d size %d\n",
+                               cached, pC->p_info[info->control_index],
+                               info->control_index, info->control_type,
+                               info->size_in32bit_words);
+
+                       /* quit loop early if whole cache has been scanned.
+                        * dwControlCount is the maximum possible entries
+                        * but some may be absent from the cache
+                        */
+                       if (byte_count >= pC->cache_size_in_bytes)
+                               break;
+                       /* have seen last control index */
+                       if (info->control_index == pC->control_count - 1)
+                               break;
                }
-               /*
-                  We didn't find anything to cache, so try again later !
-                */
-               if (!cached)
-                       pC->init = 0;
+
+               if (byte_count != pC->cache_size_in_bytes)
+                       HPI_DEBUG_LOG(WARNING,
+                               "adap %d bytecount %d != cache size %d\n",
+                               pC->adap_idx, byte_count,
+                               pC->cache_size_in_bytes);
+               else
+                       HPI_DEBUG_LOG(DEBUG,
+                               "adap %d cache good, bytecount == cache size = %d\n",
+                               pC->adap_idx, byte_count);
+
+               pC->init = (u16)cached;
        }
        return pC->init;
 }
 
 /** Find a control.
 */
-static short find_control(struct hpi_message *phm,
-       struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI,
-       u16 *pw_control_index)
+static short find_control(u16 control_index,
+       struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI)
 {
-       *pw_control_index = phm->obj_index;
-
        if (!control_cache_alloc_check(p_cache)) {
                HPI_DEBUG_LOG(VERBOSE,
-                       "control_cache_alloc_check() failed. adap%d ci%d\n",
-                       phm->adapter_index, *pw_control_index);
+                       "control_cache_alloc_check() failed %d\n",
+                       control_index);
                return 0;
        }
 
-       *pI = p_cache->p_info[*pw_control_index];
+       *pI = p_cache->p_info[control_index];
        if (!*pI) {
-               HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n",
-                       phm->adapter_index, *pw_control_index);
+               HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n",
+                       control_index);
                return 0;
        } else {
                HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
@@ -247,25 +288,6 @@ static short find_control(struct hpi_message *phm,
        return 1;
 }
 
-/** Used by the kernel driver to figure out if a buffer needs mapping.
- */
-short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
-       struct hpi_message *phm, void **p, unsigned int *pN)
-{
-       *pN = 0;
-       *p = NULL;
-       if ((phm->function == HPI_CONTROL_GET_STATE)
-               && (phm->object == HPI_OBJ_CONTROLEX)
-               ) {
-               u16 control_index;
-               struct hpi_control_cache_info *pI;
-
-               if (!find_control(phm, p_cache, &pI, &control_index))
-                       return 0;
-       }
-       return 0;
-}
-
 /* allow unified treatment of several string fields within struct */
 #define HPICMN_PAD_OFS_AND_SIZE(m)  {\
        offsetof(struct hpi_control_cache_pad, m), \
@@ -290,13 +312,16 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
        struct hpi_message *phm, struct hpi_response *phr)
 {
        short found = 1;
-       u16 control_index;
        struct hpi_control_cache_info *pI;
        struct hpi_control_cache_single *pC;
        struct hpi_control_cache_pad *p_pad;
 
-       if (!find_control(phm, p_cache, &pI, &control_index))
+       if (!find_control(phm->obj_index, p_cache, &pI)) {
+               HPI_DEBUG_LOG(VERBOSE,
+                       "HPICMN find_control() failed for adap %d\n",
+                       phm->adapter_index);
                return 0;
+       }
 
        phr->error = 0;
 
@@ -310,55 +335,79 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
 
        case HPI_CONTROL_METER:
                if (phm->u.c.attribute == HPI_METER_PEAK) {
-                       phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0];
-                       phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1];
+                       phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0];
+                       phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1];
                } else if (phm->u.c.attribute == HPI_METER_RMS) {
-                       phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0];
-                       phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1];
+                       if (pC->u.meter.an_logRMS[0] ==
+                               HPI_CACHE_INVALID_SHORT) {
+                               phr->error =
+                                       HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+                               phr->u.c.an_log_value[0] = HPI_METER_MINIMUM;
+                               phr->u.c.an_log_value[1] = HPI_METER_MINIMUM;
+                       } else {
+                               phr->u.c.an_log_value[0] =
+                                       pC->u.meter.an_logRMS[0];
+                               phr->u.c.an_log_value[1] =
+                                       pC->u.meter.an_logRMS[1];
+                       }
                } else
                        found = 0;
                break;
        case HPI_CONTROL_VOLUME:
                if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
-                       phr->u.c.an_log_value[0] = pC->u.v.an_log[0];
-                       phr->u.c.an_log_value[1] = pC->u.v.an_log[1];
-               } else
+                       phr->u.c.an_log_value[0] = pC->u.vol.an_log[0];
+                       phr->u.c.an_log_value[1] = pC->u.vol.an_log[1];
+               } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
+                       if (pC->u.vol.flags & HPI_VOLUME_FLAG_HAS_MUTE) {
+                               if (pC->u.vol.flags & HPI_VOLUME_FLAG_MUTED)
+                                       phr->u.c.param1 =
+                                               HPI_BITMASK_ALL_CHANNELS;
+                               else
+                                       phr->u.c.param1 = 0;
+                       } else {
+                               phr->error =
+                                       HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+                               phr->u.c.param1 = 0;
+                       }
+               } else {
                        found = 0;
+               }
                break;
        case HPI_CONTROL_MULTIPLEXER:
                if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
-                       phr->u.c.param1 = pC->u.x.source_node_type;
-                       phr->u.c.param2 = pC->u.x.source_node_index;
+                       phr->u.c.param1 = pC->u.mux.source_node_type;
+                       phr->u.c.param2 = pC->u.mux.source_node_index;
                } else {
                        found = 0;
                }
                break;
        case HPI_CONTROL_CHANNEL_MODE:
                if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
-                       phr->u.c.param1 = pC->u.m.mode;
+                       phr->u.c.param1 = pC->u.mode.mode;
                else
                        found = 0;
                break;
        case HPI_CONTROL_LEVEL:
                if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
-                       phr->u.c.an_log_value[0] = pC->u.l.an_log[0];
-                       phr->u.c.an_log_value[1] = pC->u.l.an_log[1];
+                       phr->u.c.an_log_value[0] = pC->u.level.an_log[0];
+                       phr->u.c.an_log_value[1] = pC->u.level.an_log[1];
                } else
                        found = 0;
                break;
        case HPI_CONTROL_TUNER:
                if (phm->u.c.attribute == HPI_TUNER_FREQ)
-                       phr->u.c.param1 = pC->u.t.freq_ink_hz;
+                       phr->u.c.param1 = pC->u.tuner.freq_ink_hz;
                else if (phm->u.c.attribute == HPI_TUNER_BAND)
-                       phr->u.c.param1 = pC->u.t.band;
-               else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
-                       && (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE))
-                       if (pC->u.t.level == HPI_ERROR_ILLEGAL_CACHE_VALUE) {
-                               phr->u.c.param1 = 0;
+                       phr->u.c.param1 = pC->u.tuner.band;
+               else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG)
+                       if (pC->u.tuner.s_level_avg ==
+                               HPI_CACHE_INVALID_SHORT) {
+                               phr->u.cu.tuner.s_level = 0;
                                phr->error =
                                        HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
                        } else
-                               phr->u.c.param1 = pC->u.t.level;
+                               phr->u.cu.tuner.s_level =
+                                       pC->u.tuner.s_level_avg;
                else
                        found = 0;
                break;
@@ -366,7 +415,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
                if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
                        phr->u.c.param1 = pC->u.aes3rx.error_status;
                else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
-                       phr->u.c.param1 = pC->u.aes3rx.source;
+                       phr->u.c.param1 = pC->u.aes3rx.format;
                else
                        found = 0;
                break;
@@ -385,13 +434,12 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
        case HPI_CONTROL_SILENCEDETECTOR:
                if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {
                        phr->u.c.param1 = pC->u.silence.state;
-                       phr->u.c.param2 = pC->u.silence.count;
                } else
                        found = 0;
                break;
        case HPI_CONTROL_MICROPHONE:
                if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
-                       phr->u.c.param1 = pC->u.phantom_power.state;
+                       phr->u.c.param1 = pC->u.microphone.phantom_state;
                else
                        found = 0;
                break;
@@ -400,7 +448,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
                        phr->u.c.param1 = pC->u.clk.source;
                else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
                        if (pC->u.clk.source_index ==
-                               HPI_ERROR_ILLEGAL_CACHE_VALUE) {
+                               HPI_CACHE_INVALID_UINT16) {
                                phr->u.c.param1 = 0;
                                phr->error =
                                        HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
@@ -411,60 +459,63 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
                else
                        found = 0;
                break;
-       case HPI_CONTROL_PAD:
+       case HPI_CONTROL_PAD:{
+                       struct hpi_control_cache_pad *p_pad;
+                       p_pad = (struct hpi_control_cache_pad *)pI;
 
-               if (!(p_pad->field_valid_flags & (1 <<
-                                       HPI_CTL_ATTR_INDEX(phm->u.c.
-                                               attribute)))) {
-                       phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
-                       break;
-               }
-
-               if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
-                       phr->u.c.param1 = p_pad->pI;
-               else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
-                       phr->u.c.param1 = p_pad->pTY;
-               else {
-                       unsigned int index =
-                               HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1;
-                       unsigned int offset = phm->u.c.param1;
-                       unsigned int pad_string_len, field_size;
-                       char *pad_string;
-                       unsigned int tocopy;
-
-                       HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n",
-                               phm->u.c.attribute);
-
-                       if (index > ARRAY_SIZE(pad_desc) - 1) {
+                       if (!(p_pad->field_valid_flags & (1 <<
+                                               HPI_CTL_ATTR_INDEX(phm->u.c.
+                                                       attribute)))) {
                                phr->error =
                                        HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
                                break;
                        }
 
-                       pad_string = ((char *)p_pad) + pad_desc[index].offset;
-                       field_size = pad_desc[index].field_size;
-                       /* Ensure null terminator */
-                       pad_string[field_size - 1] = 0;
-
-                       pad_string_len = strlen(pad_string) + 1;
-
-                       if (offset > pad_string_len) {
-                               phr->error = HPI_ERROR_INVALID_CONTROL_VALUE;
-                               break;
+                       if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
+                               phr->u.c.param1 = p_pad->pI;
+                       else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
+                               phr->u.c.param1 = p_pad->pTY;
+                       else {
+                               unsigned int index =
+                                       HPI_CTL_ATTR_INDEX(phm->u.c.
+                                       attribute) - 1;
+                               unsigned int offset = phm->u.c.param1;
+                               unsigned int pad_string_len, field_size;
+                               char *pad_string;
+                               unsigned int tocopy;
+
+                               if (index > ARRAY_SIZE(pad_desc) - 1) {
+                                       phr->error =
+                                               HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+                                       break;
+                               }
+
+                               pad_string =
+                                       ((char *)p_pad) +
+                                       pad_desc[index].offset;
+                               field_size = pad_desc[index].field_size;
+                               /* Ensure null terminator */
+                               pad_string[field_size - 1] = 0;
+
+                               pad_string_len = strlen(pad_string) + 1;
+
+                               if (offset > pad_string_len) {
+                                       phr->error =
+                                               HPI_ERROR_INVALID_CONTROL_VALUE;
+                                       break;
+                               }
+
+                               tocopy = pad_string_len - offset;
+                               if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
+                                       tocopy = sizeof(phr->u.cu.chars8.
+                                               sz_data);
+
+                               memcpy(phr->u.cu.chars8.sz_data,
+                                       &pad_string[offset], tocopy);
+
+                               phr->u.cu.chars8.remaining_chars =
+                                       pad_string_len - offset - tocopy;
                        }
-
-                       tocopy = pad_string_len - offset;
-                       if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
-                               tocopy = sizeof(phr->u.cu.chars8.sz_data);
-
-                       HPI_DEBUG_LOG(VERBOSE,
-                               "PADS memcpy(%d), offset %d \n", tocopy,
-                               offset);
-                       memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset],
-                               tocopy);
-
-                       phr->u.cu.chars8.remaining_chars =
-                               pad_string_len - offset - tocopy;
                }
                break;
        default:
@@ -472,16 +523,9 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
                break;
        }
 
-       if (found)
-               HPI_DEBUG_LOG(VERBOSE,
-                       "cached adap %d, ctl %d, type %d, attr %d\n",
-                       phm->adapter_index, pI->control_index,
-                       pI->control_type, phm->u.c.attribute);
-       else
-               HPI_DEBUG_LOG(VERBOSE,
-                       "uncached adap %d, ctl %d, ctl type %d\n",
-                       phm->adapter_index, pI->control_index,
-                       pI->control_type);
+       HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n",
+               found ? "Cached" : "Uncached", phm->adapter_index,
+               pI->control_index, pI->control_type, phm->u.c.attribute);
 
        if (found)
                phr->size =
@@ -497,18 +541,21 @@ Only update if no error.
 Volume and Level return the limited values in the response, so use these
 Multiplexer does so use sent values
 */
-void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
+void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
        struct hpi_message *phm, struct hpi_response *phr)
 {
-       u16 control_index;
        struct hpi_control_cache_single *pC;
        struct hpi_control_cache_info *pI;
 
        if (phr->error)
                return;
 
-       if (!find_control(phm, p_cache, &pI, &control_index))
+       if (!find_control(phm->obj_index, p_cache, &pI)) {
+               HPI_DEBUG_LOG(VERBOSE,
+                       "HPICMN find_control() failed for adap %d\n",
+                       phm->adapter_index);
                return;
+       }
 
        /* pC is the default cached control strucure.
           May be cast to something else in the following switch statement.
@@ -518,31 +565,36 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
        switch (pI->control_type) {
        case HPI_CONTROL_VOLUME:
                if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
-                       pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
-                       pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
+                       pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
+                       pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
+               } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
+                       if (phm->u.c.param1)
+                               pC->u.vol.flags |= HPI_VOLUME_FLAG_MUTED;
+                       else
+                               pC->u.vol.flags &= ~HPI_VOLUME_FLAG_MUTED;
                }
                break;
        case HPI_CONTROL_MULTIPLEXER:
                /* mux does not return its setting on Set command. */
                if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
-                       pC->u.x.source_node_type = (u16)phm->u.c.param1;
-                       pC->u.x.source_node_index = (u16)phm->u.c.param2;
+                       pC->u.mux.source_node_type = (u16)phm->u.c.param1;
+                       pC->u.mux.source_node_index = (u16)phm->u.c.param2;
                }
                break;
        case HPI_CONTROL_CHANNEL_MODE:
                /* mode does not return its setting on Set command. */
                if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
-                       pC->u.m.mode = (u16)phm->u.c.param1;
+                       pC->u.mode.mode = (u16)phm->u.c.param1;
                break;
        case HPI_CONTROL_LEVEL:
                if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
-                       pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
-                       pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
+                       pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
+                       pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
                }
                break;
        case HPI_CONTROL_MICROPHONE:
                if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
-                       pC->u.phantom_power.state = (u16)phm->u.c.param1;
+                       pC->u.microphone.phantom_state = (u16)phm->u.c.param1;
                break;
        case HPI_CONTROL_AESEBU_TRANSMITTER:
                if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
@@ -550,7 +602,7 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
                break;
        case HPI_CONTROL_AESEBU_RECEIVER:
                if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
-                       pC->u.aes3rx.source = phm->u.c.param1;
+                       pC->u.aes3rx.format = phm->u.c.param1;
                break;
        case HPI_CONTROL_SAMPLECLOCK:
                if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
@@ -565,59 +617,57 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
        }
 }
 
-struct hpi_control_cache *hpi_alloc_control_cache(const u32
-       number_of_controls, const u32 size_in_bytes,
-       struct hpi_control_cache_info *pDSP_control_buffer)
+struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
+       const u32 size_in_bytes, u8 *p_dsp_control_buffer)
 {
        struct hpi_control_cache *p_cache =
                kmalloc(sizeof(*p_cache), GFP_KERNEL);
        if (!p_cache)
                return NULL;
+
        p_cache->p_info =
-               kmalloc(sizeof(*p_cache->p_info) * number_of_controls,
-                       GFP_KERNEL);
+               kmalloc(sizeof(*p_cache->p_info) * control_count, GFP_KERNEL);
        if (!p_cache->p_info) {
                kfree(p_cache);
                return NULL;
        }
+       memset(p_cache->p_info, 0, sizeof(*p_cache->p_info) * control_count);
        p_cache->cache_size_in_bytes = size_in_bytes;
-       p_cache->control_count = number_of_controls;
-       p_cache->p_cache =
-               (struct hpi_control_cache_single *)pDSP_control_buffer;
+       p_cache->control_count = control_count;
+       p_cache->p_cache = p_dsp_control_buffer;
        p_cache->init = 0;
        return p_cache;
 }
 
 void hpi_free_control_cache(struct hpi_control_cache *p_cache)
 {
-       if (p_cache->init) {
+       if (p_cache) {
                kfree(p_cache->p_info);
-               p_cache->p_info = NULL;
-               p_cache->init = 0;
                kfree(p_cache);
        }
 }
 
 static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
 {
+       hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0);
 
        switch (phm->function) {
        case HPI_SUBSYS_OPEN:
        case HPI_SUBSYS_CLOSE:
        case HPI_SUBSYS_DRIVER_UNLOAD:
-               phr->error = 0;
                break;
        case HPI_SUBSYS_DRIVER_LOAD:
                wipe_adapter_list();
                hpios_alistlock_init(&adapters);
-               phr->error = 0;
                break;
-       case HPI_SUBSYS_GET_INFO:
-               subsys_get_adapters(phr);
+       case HPI_SUBSYS_GET_ADAPTER:
+               subsys_get_adapter(phm, phr);
+               break;
+       case HPI_SUBSYS_GET_NUM_ADAPTERS:
+               phr->u.s.num_adapters = adapters.gw_num_adapters;
                break;
        case HPI_SUBSYS_CREATE_ADAPTER:
        case HPI_SUBSYS_DELETE_ADAPTER:
-               phr->error = 0;
                break;
        default:
                phr->error = HPI_ERROR_INVALID_FUNC;
index 6229022f56cb0d910cef2b2a9c68cd06fc6c48fb..590f0b69e655ae57e0b63f442f6dbb1809af941c 100644 (file)
@@ -33,18 +33,19 @@ struct hpi_adapter_obj {
 };
 
 struct hpi_control_cache {
-       u32 init;            /**< indicates whether the
-                               structures are initialized */
+       /** indicates whether the structures are initialized */
+       u16 init;
+       u16 adap_idx;
        u32 control_count;
        u32 cache_size_in_bytes;
-       struct hpi_control_cache_info
-       **p_info;                /**< pointer to allocated memory of
-                               lookup pointers. */
-       struct hpi_control_cache_single
-       *p_cache;                /**< pointer to DSP's control cache. */
+       /** pointer to allocated memory of lookup pointers. */
+       struct hpi_control_cache_info **p_info;
+       /** pointer to DSP's control cache. */
+       u8 *p_cache;
 };
 
 struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
+
 u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
 
 void hpi_delete_adapter(struct hpi_adapter_obj *pao);
@@ -52,13 +53,10 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao);
 short hpi_check_control_cache(struct hpi_control_cache *pC,
        struct hpi_message *phm, struct hpi_response *phr);
 struct hpi_control_cache *hpi_alloc_control_cache(const u32
-       number_of_controls, const u32 size_in_bytes,
-       struct hpi_control_cache_info
-       *pDSP_control_buffer);
+       number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer);
 void hpi_free_control_cache(struct hpi_control_cache *p_cache);
 
-void hpi_sync_control_cache(struct hpi_control_cache *pC,
+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);
-short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
-       struct hpi_message *phm, void **p, unsigned int *pN);
index 949836ec913a12df3e753c68b526a3f2b53ea024..b52baf62791ed3bf96bab2515bfaaf62383e3597 100644 (file)
@@ -45,161 +45,14 @@ int hpi_debug_level_get(void)
        return hpi_debug_level;
 }
 
-#ifdef HPIOS_DEBUG_PRINT
-/* implies OS has no printf-like function */
-#include <stdarg.h>
-
-void hpi_debug_printf(char *fmt, ...)
-{
-       va_list arglist;
-       char buffer[128];
-
-       va_start(arglist, fmt);
-
-       if (buffer[0])
-               HPIOS_DEBUG_PRINT(buffer);
-       va_end(arglist);
-}
-#endif
-
-struct treenode {
-       void *array;
-       unsigned int num_elements;
-};
-
-#define make_treenode_from_array(nodename, array) \
-static void *tmp_strarray_##nodename[] = array; \
-static struct treenode nodename = { \
-       &tmp_strarray_##nodename, \
-       ARRAY_SIZE(tmp_strarray_##nodename) \
-};
-
-#define get_treenode_elem(node_ptr, idx, type)  \
-       (&(*((type *)(node_ptr)->array)[idx]))
-
-make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS)
-
-       make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS)
-       make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS)
-       make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS)
-       make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS)
-       make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS)
-       make_treenode_from_array(hpi_node_strings,
-       {
-       "NODE is invalid object"})
-
-       make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS)
-       make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS)
-       make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS)
-       make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS)
-       make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS)
-       make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS)
-       make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS)
-#define HPI_FUNCTION_STRINGS \
-{ \
-  &hpi_subsys_strings,\
-  &hpi_adapter_strings,\
-  &hpi_ostream_strings,\
-  &hpi_istream_strings,\
-  &hpi_mixer_strings,\
-  &hpi_node_strings,\
-  &hpi_control_strings,\
-  &hpi_nvmemory_strings,\
-  &hpi_digitalio_strings,\
-  &hpi_watchdog_strings,\
-  &hpi_clock_strings,\
-  &hpi_profile_strings,\
-  &hpi_control_strings, \
-  &hpi_asyncevent_strings \
-}
-       make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
-
-       compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
-
-static char *hpi_function_string(unsigned int function)
-{
-       unsigned int object;
-       struct treenode *tmp;
-
-       object = function / HPI_OBJ_FUNCTION_SPACING;
-       function = function - object * HPI_OBJ_FUNCTION_SPACING;
-
-       if (object == 0 || object == HPI_OBJ_NODE
-               || object > hpi_function_strings.num_elements)
-               return "invalid object";
-
-       tmp = get_treenode_elem(&hpi_function_strings, object - 1,
-               struct treenode *);
-
-       if (function == 0 || function > tmp->num_elements)
-               return "invalid function";
-
-       return get_treenode_elem(tmp, function - 1, char *);
-}
-
 void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
 {
        if (phm) {
-               if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) {
-                       u16 index = 0;
-                       u16 attrib = 0;
-                       int is_control = 0;
-
-                       index = phm->obj_index;
-                       switch (phm->object) {
-                       case HPI_OBJ_ADAPTER:
-                       case HPI_OBJ_PROFILE:
-                               break;
-                       case HPI_OBJ_MIXER:
-                               if (phm->function ==
-                                       HPI_MIXER_GET_CONTROL_BY_INDEX)
-                                       index = phm->u.m.control_index;
-                               break;
-                       case HPI_OBJ_OSTREAM:
-                       case HPI_OBJ_ISTREAM:
-                               break;
-
-                       case HPI_OBJ_CONTROLEX:
-                       case HPI_OBJ_CONTROL:
-                               if (phm->version == 1)
-                                       attrib = HPI_CTL_ATTR(UNIVERSAL, 1);
-                               else
-                                       attrib = phm->u.c.attribute;
-                               is_control = 1;
-                               break;
-                       default:
-                               break;
-                       }
-
-                       if (is_control && (attrib & 0xFF00)) {
-                               int control_type = (attrib & 0xFF00) >> 8;
-                               int attr_index = HPI_CTL_ATTR_INDEX(attrib);
-                               /* note the KERN facility level
-                                  is in szFileline already */
-                               printk("%s adapter %d %s "
-                                       "ctrl_index x%04x %s %d\n",
-                                       sz_fileline, phm->adapter_index,
-                                       hpi_function_string(phm->function),
-                                       index,
-                                       get_treenode_elem
-                                       (&hpi_control_type_strings,
-                                               control_type, char *),
-                                       attr_index);
-
-                       } else
-                               printk("%s adapter %d %s "
-                                       "idx x%04x attr x%04x \n",
-                                       sz_fileline, phm->adapter_index,
-                                       hpi_function_string(phm->function),
-                                       index, attrib);
-               } else {
-                       printk("adap=%d, invalid obj=%d, func=0x%x\n",
-                               phm->adapter_index, phm->object,
-                               phm->function);
-               }
-       } else
-               printk(KERN_ERR
-                       "NULL message pointer to hpi_debug_message!\n");
+               printk(KERN_DEBUG "HPI_MSG%d,%d,%d,%d,%d\n", phm->version,
+                       phm->adapter_index, phm->obj_index, phm->function,
+                       phm->u.c.attribute);
+       }
+
 }
 
 void hpi_debug_data(u16 *pdata, u32 len)
index a2f0952a99f00413e87518e0884633cbf2761b27..940f54c3c5383e377a77dbf3f794818f06147085 100644 (file)
@@ -37,7 +37,7 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0,     /* always log errors */
 #define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
 
 /* an OS can define an extra flag string that is appended to
-   the start of each message, eg see hpios_linux.h */
+   the start of each message, eg see linux kernel hpios.h */
 
 #ifdef SOURCEFILE_NAME
 #define FILE_LINE  SOURCEFILE_NAME ":" __stringify(__LINE__) " "
@@ -45,18 +45,11 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0,   /* always log errors */
 #define FILE_LINE  __FILE__ ":" __stringify(__LINE__) " "
 #endif
 
-#if defined(HPI_DEBUG) && defined(_WINDOWS)
-#define HPI_DEBUGBREAK() debug_break()
-#else
-#define HPI_DEBUGBREAK()
-#endif
-
 #define HPI_DEBUG_ASSERT(expression) \
        do { \
-               if (!(expression)) {\
-                       printk(KERN_ERR  FILE_LINE\
-                               "ASSERT " __stringify(expression));\
-                       HPI_DEBUGBREAK();\
+               if (!(expression)) { \
+                       printk(KERN_ERR  FILE_LINE \
+                               "ASSERT " __stringify(expression)); \
                } \
        } while (0)
 
@@ -78,28 +71,27 @@ void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
 
 void hpi_debug_data(u16 *pdata, u32 len);
 
-#define HPI_DEBUG_DATA(pdata, len)                                      \
-       do {                                                            \
+#define HPI_DEBUG_DATA(pdata, len) \
+       do { \
                if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
                        hpi_debug_data(pdata, len); \
        } while (0)
 
-#define HPI_DEBUG_MESSAGE(level, phm)                                   \
-       do {                                                            \
-               if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) {         \
-                       hpi_debug_message(phm,HPI_DEBUG_FLAG_##level    \
-                               FILE_LINE __stringify(level));\
-               }                                                       \
+#define HPI_DEBUG_MESSAGE(level, phm) \
+       do { \
+               if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
+                       hpi_debug_message(phm, HPI_DEBUG_FLAG_##level \
+                               FILE_LINE __stringify(level)); \
+               } \
        } while (0)
 
-#define HPI_DEBUG_RESPONSE(phr)                                         \
-       do {                                                            \
-               if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\
-                       HPI_DEBUG_LOG(ERROR, \
-                               "HPI response - error# %d\n", \
-                               phr->error); \
-               else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
-                       HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\
+#define HPI_DEBUG_RESPONSE(phr) \
+       do { \
+               if (((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && \
+                       (phr->error)) ||\
+               (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)) \
+                       printk(KERN_DEBUG "HPI_RES%d,%d,%d\n", \
+                               phr->version, phr->error, phr->specific_error); \
        } while (0)
 
 #ifndef compile_time_assert
@@ -107,279 +99,4 @@ void hpi_debug_data(u16 *pdata, u32 len);
     typedef char msg[(cond) ? 1 : -1]
 #endif
 
-         /* check that size is exactly some number */
-#define function_count_check(sym, size) \
-    compile_time_assert((sym##_FUNCTION_COUNT) == (size),\
-           strings_match_defs_##sym)
-
-/* These strings should be generated using a macro which defines
-   the corresponding symbol values.  */
-#define HPI_OBJ_STRINGS \
-{                               \
-  "HPI_OBJ_SUBSYSTEM",        \
-  "HPI_OBJ_ADAPTER",          \
-  "HPI_OBJ_OSTREAM",          \
-  "HPI_OBJ_ISTREAM",          \
-  "HPI_OBJ_MIXER",            \
-  "HPI_OBJ_NODE",             \
-  "HPI_OBJ_CONTROL",          \
-  "HPI_OBJ_NVMEMORY",         \
-  "HPI_OBJ_DIGITALIO",        \
-  "HPI_OBJ_WATCHDOG",         \
-  "HPI_OBJ_CLOCK",            \
-  "HPI_OBJ_PROFILE",          \
-  "HPI_OBJ_CONTROLEX"         \
-}
-
-#define HPI_SUBSYS_STRINGS      \
-{                               \
-  "HPI_SUBSYS_OPEN",          \
-  "HPI_SUBSYS_GET_VERSION",   \
-  "HPI_SUBSYS_GET_INFO",      \
-  "HPI_SUBSYS_FIND_ADAPTERS", \
-  "HPI_SUBSYS_CREATE_ADAPTER",\
-  "HPI_SUBSYS_CLOSE",         \
-  "HPI_SUBSYS_DELETE_ADAPTER", \
-  "HPI_SUBSYS_DRIVER_LOAD", \
-  "HPI_SUBSYS_DRIVER_UNLOAD", \
-  "HPI_SUBSYS_READ_PORT_8",   \
-  "HPI_SUBSYS_WRITE_PORT_8",  \
-  "HPI_SUBSYS_GET_NUM_ADAPTERS",\
-  "HPI_SUBSYS_GET_ADAPTER",   \
-  "HPI_SUBSYS_SET_NETWORK_INTERFACE"\
-}
-function_count_check(HPI_SUBSYS, 14);
-
-#define HPI_ADAPTER_STRINGS     \
-{                               \
-  "HPI_ADAPTER_OPEN",         \
-  "HPI_ADAPTER_CLOSE",        \
-  "HPI_ADAPTER_GET_INFO",     \
-  "HPI_ADAPTER_GET_ASSERT",   \
-  "HPI_ADAPTER_TEST_ASSERT",    \
-  "HPI_ADAPTER_SET_MODE",       \
-  "HPI_ADAPTER_GET_MODE",       \
-  "HPI_ADAPTER_ENABLE_CAPABILITY",\
-  "HPI_ADAPTER_SELFTEST",        \
-  "HPI_ADAPTER_FIND_OBJECT",     \
-  "HPI_ADAPTER_QUERY_FLASH",     \
-  "HPI_ADAPTER_START_FLASH",     \
-  "HPI_ADAPTER_PROGRAM_FLASH",   \
-  "HPI_ADAPTER_SET_PROPERTY",    \
-  "HPI_ADAPTER_GET_PROPERTY",    \
-  "HPI_ADAPTER_ENUM_PROPERTY",    \
-  "HPI_ADAPTER_MODULE_INFO",    \
-  "HPI_ADAPTER_DEBUG_READ"    \
-}
-
-function_count_check(HPI_ADAPTER, 18);
-
-#define HPI_OSTREAM_STRINGS     \
-{                               \
-  "HPI_OSTREAM_OPEN",         \
-  "HPI_OSTREAM_CLOSE",        \
-  "HPI_OSTREAM_WRITE",        \
-  "HPI_OSTREAM_START",        \
-  "HPI_OSTREAM_STOP",         \
-  "HPI_OSTREAM_RESET",                \
-  "HPI_OSTREAM_GET_INFO",     \
-  "HPI_OSTREAM_QUERY_FORMAT", \
-  "HPI_OSTREAM_DATA",         \
-  "HPI_OSTREAM_SET_VELOCITY", \
-  "HPI_OSTREAM_SET_PUNCHINOUT", \
-  "HPI_OSTREAM_SINEGEN",        \
-  "HPI_OSTREAM_ANC_RESET",      \
-  "HPI_OSTREAM_ANC_GET_INFO",   \
-  "HPI_OSTREAM_ANC_READ",       \
-  "HPI_OSTREAM_SET_TIMESCALE",\
-  "HPI_OSTREAM_SET_FORMAT", \
-  "HPI_OSTREAM_HOSTBUFFER_ALLOC", \
-  "HPI_OSTREAM_HOSTBUFFER_FREE", \
-  "HPI_OSTREAM_GROUP_ADD",\
-  "HPI_OSTREAM_GROUP_GETMAP", \
-  "HPI_OSTREAM_GROUP_RESET", \
-  "HPI_OSTREAM_HOSTBUFFER_GET_INFO", \
-  "HPI_OSTREAM_WAIT_START", \
-}
-function_count_check(HPI_OSTREAM, 24);
-
-#define HPI_ISTREAM_STRINGS     \
-{                               \
-  "HPI_ISTREAM_OPEN",         \
-  "HPI_ISTREAM_CLOSE",        \
-  "HPI_ISTREAM_SET_FORMAT",   \
-  "HPI_ISTREAM_READ",         \
-  "HPI_ISTREAM_START",        \
-  "HPI_ISTREAM_STOP",         \
-  "HPI_ISTREAM_RESET",        \
-  "HPI_ISTREAM_GET_INFO",     \
-  "HPI_ISTREAM_QUERY_FORMAT", \
-  "HPI_ISTREAM_ANC_RESET",      \
-  "HPI_ISTREAM_ANC_GET_INFO",   \
-  "HPI_ISTREAM_ANC_WRITE",   \
-  "HPI_ISTREAM_HOSTBUFFER_ALLOC",\
-  "HPI_ISTREAM_HOSTBUFFER_FREE", \
-  "HPI_ISTREAM_GROUP_ADD", \
-  "HPI_ISTREAM_GROUP_GETMAP", \
-  "HPI_ISTREAM_GROUP_RESET", \
-  "HPI_ISTREAM_HOSTBUFFER_GET_INFO", \
-  "HPI_ISTREAM_WAIT_START", \
-}
-function_count_check(HPI_ISTREAM, 19);
-
-#define HPI_MIXER_STRINGS       \
-{                               \
-  "HPI_MIXER_OPEN",           \
-  "HPI_MIXER_CLOSE",          \
-  "HPI_MIXER_GET_INFO",       \
-  "HPI_MIXER_GET_NODE_INFO",  \
-  "HPI_MIXER_GET_CONTROL",    \
-  "HPI_MIXER_SET_CONNECTION", \
-  "HPI_MIXER_GET_CONNECTIONS",        \
-  "HPI_MIXER_GET_CONTROL_BY_INDEX",   \
-  "HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX",     \
-  "HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES",    \
-  "HPI_MIXER_STORE",  \
-}
-function_count_check(HPI_MIXER, 11);
-
-#define HPI_CONTROL_STRINGS     \
-{                               \
-  "HPI_CONTROL_GET_INFO",     \
-  "HPI_CONTROL_GET_STATE",    \
-  "HPI_CONTROL_SET_STATE"     \
-}
-function_count_check(HPI_CONTROL, 3);
-
-#define HPI_NVMEMORY_STRINGS    \
-{                               \
-  "HPI_NVMEMORY_OPEN",        \
-  "HPI_NVMEMORY_READ_BYTE",   \
-  "HPI_NVMEMORY_WRITE_BYTE"   \
-}
-function_count_check(HPI_NVMEMORY, 3);
-
-#define HPI_DIGITALIO_STRINGS   \
-{                               \
-  "HPI_GPIO_OPEN",            \
-  "HPI_GPIO_READ_BIT",        \
-  "HPI_GPIO_WRITE_BIT",       \
-  "HPI_GPIO_READ_ALL",                \
-  "HPI_GPIO_WRITE_STATUS"\
-}
-function_count_check(HPI_GPIO, 5);
-
-#define HPI_WATCHDOG_STRINGS    \
-{                               \
-  "HPI_WATCHDOG_OPEN",        \
-  "HPI_WATCHDOG_SET_TIME",    \
-  "HPI_WATCHDOG_PING"         \
-}
-
-#define HPI_CLOCK_STRINGS       \
-{                               \
-  "HPI_CLOCK_OPEN",           \
-  "HPI_CLOCK_SET_TIME",       \
-  "HPI_CLOCK_GET_TIME"        \
-}
-
-#define HPI_PROFILE_STRINGS     \
-{                               \
-  "HPI_PROFILE_OPEN_ALL",     \
-  "HPI_PROFILE_START_ALL",    \
-  "HPI_PROFILE_STOP_ALL",     \
-  "HPI_PROFILE_GET",          \
-  "HPI_PROFILE_GET_IDLECOUNT",  \
-  "HPI_PROFILE_GET_NAME",       \
-  "HPI_PROFILE_GET_UTILIZATION" \
-}
-function_count_check(HPI_PROFILE, 7);
-
-#define HPI_ASYNCEVENT_STRINGS  \
-{                               \
-  "HPI_ASYNCEVENT_OPEN",\
-  "HPI_ASYNCEVENT_CLOSE  ",\
-  "HPI_ASYNCEVENT_WAIT",\
-  "HPI_ASYNCEVENT_GETCOUNT",\
-  "HPI_ASYNCEVENT_GET",\
-  "HPI_ASYNCEVENT_SENDEVENTS"\
-}
-function_count_check(HPI_ASYNCEVENT, 6);
-
-#define HPI_CONTROL_TYPE_STRINGS \
-{ \
-       "null control", \
-       "HPI_CONTROL_CONNECTION", \
-       "HPI_CONTROL_VOLUME", \
-       "HPI_CONTROL_METER", \
-       "HPI_CONTROL_MUTE", \
-       "HPI_CONTROL_MULTIPLEXER", \
-       "HPI_CONTROL_AESEBU_TRANSMITTER", \
-       "HPI_CONTROL_AESEBU_RECEIVER", \
-       "HPI_CONTROL_LEVEL", \
-       "HPI_CONTROL_TUNER", \
-       "HPI_CONTROL_ONOFFSWITCH", \
-       "HPI_CONTROL_VOX", \
-       "HPI_CONTROL_AES18_TRANSMITTER", \
-       "HPI_CONTROL_AES18_RECEIVER", \
-       "HPI_CONTROL_AES18_BLOCKGENERATOR", \
-       "HPI_CONTROL_CHANNEL_MODE", \
-       "HPI_CONTROL_BITSTREAM", \
-       "HPI_CONTROL_SAMPLECLOCK", \
-       "HPI_CONTROL_MICROPHONE", \
-       "HPI_CONTROL_PARAMETRIC_EQ", \
-       "HPI_CONTROL_COMPANDER", \
-       "HPI_CONTROL_COBRANET", \
-       "HPI_CONTROL_TONE_DETECT", \
-       "HPI_CONTROL_SILENCE_DETECT", \
-       "HPI_CONTROL_PAD", \
-       "HPI_CONTROL_SRC" ,\
-       "HPI_CONTROL_UNIVERSAL" \
-}
-
-compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27),
-       controltype_strings_match_defs);
-
-#define HPI_SOURCENODE_STRINGS \
-{ \
-       "no source", \
-       "HPI_SOURCENODE_OSTREAM", \
-       "HPI_SOURCENODE_LINEIN", \
-       "HPI_SOURCENODE_AESEBU_IN", \
-       "HPI_SOURCENODE_TUNER", \
-       "HPI_SOURCENODE_RF", \
-       "HPI_SOURCENODE_CLOCK_SOURCE", \
-       "HPI_SOURCENODE_RAW_BITSTREAM", \
-       "HPI_SOURCENODE_MICROPHONE", \
-       "HPI_SOURCENODE_COBRANET", \
-       "HPI_SOURCENODE_ANALOG", \
-       "HPI_SOURCENODE_ADAPTER" \
-}
-
-compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_NONE + 1) ==
-       (12), sourcenode_strings_match_defs);
-
-#define HPI_DESTNODE_STRINGS \
-{ \
-       "no destination", \
-       "HPI_DESTNODE_ISTREAM", \
-       "HPI_DESTNODE_LINEOUT", \
-       "HPI_DESTNODE_AESEBU_OUT", \
-       "HPI_DESTNODE_RF", \
-       "HPI_DESTNODE_SPEAKER", \
-       "HPI_DESTNODE_COBRANET", \
-       "HPI_DESTNODE_ANALOG" \
-}
-compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_NONE + 1) == (8),
-       destnode_strings_match_defs);
-
-#define HPI_CONTROL_CHANNEL_MODE_STRINGS \
-{ \
-       "XXX HPI_CHANNEL_MODE_ERROR XXX", \
-       "HPI_CHANNEL_MODE_NORMAL", \
-       "HPI_CHANNEL_MODE_SWAP", \
-       "HPI_CHANNEL_MODE_LEFT_ONLY", \
-       "HPI_CHANNEL_MODE_RIGHT_ONLY" \
-}
-
-#endif                         /* _HPIDEBUG_H  */
+#endif                         /* _HPIDEBUG_H_  */
index 9b10d9a5c255f5a9b7f435c5cbc14d3a20a8d287..fb311d8c05bff2365776260b089f878c7cb02ac6 100644 (file)
@@ -71,47 +71,50 @@ short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
        int err;
 
        sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
-       HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name);
 
        err = request_firmware(&ps_firmware, fw_name,
                &ps_dsp_code->ps_dev->dev);
+
        if (err != 0) {
-               HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for  %s\n",
-                       err, fw_name);
+               dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+                       "%d, request_firmware failed for  %s\n", err,
+                       fw_name);
                goto error1;
        }
        if (ps_firmware->size < sizeof(header)) {
-               HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name);
+               dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+                       "Header size too small %s\n", fw_name);
                goto error2;
        }
        memcpy(&header, ps_firmware->data, sizeof(header));
        if (header.adapter != adapter) {
-               HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n",
-                       header.adapter, adapter);
+               dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+                       "Adapter type incorrect %4x != %4x\n", header.adapter,
+                       adapter);
                goto error2;
        }
        if (header.size != ps_firmware->size) {
-               HPI_DEBUG_LOG(ERROR, "code size wrong  %d != %ld\n",
-                       header.size, (unsigned long)ps_firmware->size);
+               dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+                       "Code size wrong  %d != %ld\n", header.size,
+                       (unsigned long)ps_firmware->size);
                goto error2;
        }
 
-       if (header.version / 10000 != HPI_VER_DECIMAL / 10000) {
-               HPI_DEBUG_LOG(ERROR,
-                       "firmware major version mismatch "
-                       "DSP image %d != driver %d\n", header.version,
+       if (header.version / 100 != HPI_VER_DECIMAL / 100) {
+               dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev,
+                       "Incompatible firmware version "
+                       "DSP image %d != Driver %d\n", header.version,
                        HPI_VER_DECIMAL);
                goto error2;
        }
 
        if (header.version != HPI_VER_DECIMAL) {
-               HPI_DEBUG_LOG(WARNING,
-                       "version mismatch  DSP image %d != driver %d\n",
+               dev_printk(KERN_WARNING, &ps_dsp_code->ps_dev->dev,
+                       "Firmware: release version mismatch  DSP image %d != Driver %d\n",
                        header.version, HPI_VER_DECIMAL);
-               /* goto error2;  still allow driver to load */
        }
 
-       HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name);
+       HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
        ps_dsp_code->ps_firmware = ps_firmware;
        ps_dsp_code->block_length = header.size / sizeof(u32);
        ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
@@ -148,7 +151,7 @@ void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
 short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
 {
        if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
-               return (HPI_ERROR_DSP_FILE_FORMAT);
+               return HPI_ERROR_DSP_FILE_FORMAT;
 
        *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
                word_count];
index d7c240398225cdde0401a59706f0a6bffb29a37c..65f0ca732704645d4eadebba86faee736b1a18e5 100644 (file)
@@ -87,7 +87,7 @@ void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
 */
 short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
                                      /**< DSP code descriptor */
-       u32 *pword /**< where to store the read word */
+       u32 *pword /**< Where to store the read word */
        );
 
 /** Get a block of dsp code into an internal buffer, and provide a pointer to
index 1e92eb6dd5091c8e5411fbac7000f4cc127beb99..c38fc9487560cea2a235e0ba829c43b197e7f9e9 100644 (file)
@@ -30,16 +30,25 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
        return handle.w;
 }
 
-void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
-       u16 *pw_object_index)
+static u16 hpi_handle_indexes(const u32 h, u16 *p1, u16 *p2)
 {
        union handle_word uhandle;
-       uhandle.w = handle;
+       if (!h)
+               return HPI_ERROR_INVALID_HANDLE;
+
+       uhandle.w = h;
+
+       *p1 = (u16)uhandle.h.adapter_index;
+       if (p2)
+               *p2 = (u16)uhandle.h.obj_index;
+
+       return 0;
+}
 
-       if (pw_adapter_index)
-               *pw_adapter_index = (u16)uhandle.h.adapter_index;
-       if (pw_object_index)
-               *pw_object_index = (u16)uhandle.h.obj_index;
+void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
+       u16 *pw_object_index)
+{
+       hpi_handle_indexes(handle, pw_adapter_index, pw_object_index);
 }
 
 char hpi_handle_object(const u32 handle)
@@ -49,22 +58,6 @@ char hpi_handle_object(const u32 handle)
        return (char)uhandle.h.obj_type;
 }
 
-#define u32TOINDEX(h, i1) \
-do {\
-       if (h == 0) \
-               return HPI_ERROR_INVALID_OBJ; \
-       else \
-               hpi_handle_to_indexes(h, i1, NULL); \
-} while (0)
-
-#define u32TOINDEXES(h, i1, i2) \
-do {\
-       if (h == 0) \
-               return HPI_ERROR_INVALID_OBJ; \
-       else \
-               hpi_handle_to_indexes(h, i1, i2);\
-} while (0)
-
 void hpi_format_to_msg(struct hpi_msg_format *pMF,
        const struct hpi_format *pF)
 {
@@ -94,52 +87,13 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR)
        pSR->u.legacy_stream_info.state = pSR->u.stream_info.state;
 }
 
-static struct hpi_hsubsys gh_subsys;
-
-struct hpi_hsubsys *hpi_subsys_create(void)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-
-       memset(&gh_subsys, 0, sizeof(struct hpi_hsubsys));
-
-       {
-               hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
-                       HPI_SUBSYS_OPEN);
-               hpi_send_recv(&hm, &hr);
-
-               if (hr.error == 0)
-                       return &gh_subsys;
-
-       }
-       return NULL;
-}
-
-void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
-               HPI_SUBSYS_CLOSE);
-       hpi_send_recv(&hm, &hr);
-
-}
-
-u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, u32 *pversion)
+static inline void hpi_send_recvV1(struct hpi_message_header *m,
+       struct hpi_response_header *r)
 {
-       struct hpi_message hm;
-       struct hpi_response hr;
-
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
-               HPI_SUBSYS_GET_VERSION);
-       hpi_send_recv(&hm, &hr);
-       *pversion = hr.u.s.version;
-       return hr.error;
+       hpi_send_recv((struct hpi_message *)m, (struct hpi_response *)r);
 }
 
-u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
-       u32 *pversion_ex)
+u16 hpi_subsys_get_version_ex(u32 *pversion_ex)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -151,51 +105,8 @@ u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
-       u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
-               HPI_SUBSYS_GET_INFO);
-
-       hpi_send_recv(&hm, &hr);
-
-       *pversion = hr.u.s.version;
-       if (list_length > HPI_MAX_ADAPTERS)
-               memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
-                       HPI_MAX_ADAPTERS);
-       else
-               memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, list_length);
-       *pw_num_adapters = hr.u.s.num_adapters;
-       return hr.error;
-}
-
-u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
-       u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
-               HPI_SUBSYS_FIND_ADAPTERS);
-
-       hpi_send_recv(&hm, &hr);
-
-       if (list_length > HPI_MAX_ADAPTERS) {
-               memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
-                       HPI_MAX_ADAPTERS * sizeof(u16));
-               memset(&aw_adapter_list[HPI_MAX_ADAPTERS], 0,
-                       (list_length - HPI_MAX_ADAPTERS) * sizeof(u16));
-       } else
-               memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
-                       list_length * sizeof(u16));
-       *pw_num_adapters = hr.u.s.num_adapters;
-
-       return hr.error;
-}
-
-u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
-       const struct hpi_resource *p_resource, u16 *pw_adapter_index)
+u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
+       u16 *pw_adapter_index)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -210,20 +121,18 @@ u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index)
+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.adapter_index = adapter_index;
+       hm.obj_index = adapter_index;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
-       int *pn_num_adapters)
+u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -234,35 +143,22 @@ u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
-       u32 *padapter_index, u16 *pw_adapter_type)
+u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index,
+       u16 *pw_adapter_type)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
                HPI_SUBSYS_GET_ADAPTER);
-       hm.adapter_index = (u16)iterator;
+       hm.obj_index = (u16)iterator;
        hpi_send_recv(&hm, &hr);
        *padapter_index = (int)hr.u.s.adapter_index;
-       *pw_adapter_type = hr.u.s.aw_adapter_list[0];
-       return hr.error;
-}
+       *pw_adapter_type = hr.u.s.adapter_type;
 
-u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
-       const char *sz_interface)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
-               HPI_SUBSYS_SET_NETWORK_INTERFACE);
-       if (sz_interface == NULL)
-               return HPI_ERROR_INVALID_RESOURCE;
-       hm.u.s.resource.r.net_if = sz_interface;
-       hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
+u16 hpi_adapter_open(u16 adapter_index)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -276,7 +172,7 @@ u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
 
 }
 
-u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
+u16 hpi_adapter_close(u16 adapter_index)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -289,15 +185,14 @@ u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
        return hr.error;
 }
 
-u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 adapter_mode)
+u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode)
 {
-       return hpi_adapter_set_mode_ex(ph_subsys, adapter_index, adapter_mode,
+       return hpi_adapter_set_mode_ex(adapter_index, adapter_mode,
                HPI_ADAPTER_MODE_SET);
 }
 
-u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 adapter_mode, u16 query_or_set)
+u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
+       u16 query_or_set)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -305,14 +200,13 @@ u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
                HPI_ADAPTER_SET_MODE);
        hm.adapter_index = adapter_index;
-       hm.u.a.adapter_mode = adapter_mode;
-       hm.u.a.assert_id = query_or_set;
+       hm.u.ax.mode.adapter_mode = adapter_mode;
+       hm.u.ax.mode.query_or_set = query_or_set;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 *padapter_mode)
+u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -321,13 +215,13 @@ u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
        hm.adapter_index = adapter_index;
        hpi_send_recv(&hm, &hr);
        if (padapter_mode)
-               *padapter_mode = hr.u.a.serial_number;
+               *padapter_mode = hr.u.ax.mode.adapter_mode;
        return hr.error;
 }
 
-u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
-       u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type)
+u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams,
+       u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number,
+       u16 *pw_adapter_type)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -337,18 +231,17 @@ u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
 
        hpi_send_recv(&hm, &hr);
 
-       *pw_adapter_type = hr.u.a.adapter_type;
-       *pw_num_outstreams = hr.u.a.num_outstreams;
-       *pw_num_instreams = hr.u.a.num_instreams;
-       *pw_version = hr.u.a.version;
-       *pserial_number = hr.u.a.serial_number;
+       *pw_adapter_type = hr.u.ax.info.adapter_type;
+       *pw_num_outstreams = hr.u.ax.info.num_outstreams;
+       *pw_num_instreams = hr.u.ax.info.num_instreams;
+       *pw_version = hr.u.ax.info.version;
+       *pserial_number = hr.u.ax.info.serial_number;
        return hr.error;
 }
 
-u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
-       u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
-       u16 *pw_module_type, u32 *ph_module)
+u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index,
+       u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version,
+       u32 *pserial_number, u16 *pw_module_type, u32 *ph_module)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -360,173 +253,18 @@ u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
 
        hpi_send_recv(&hm, &hr);
 
-       *pw_module_type = hr.u.a.adapter_type;
-       *pw_num_outputs = hr.u.a.num_outstreams;
-       *pw_num_inputs = hr.u.a.num_instreams;
-       *pw_version = hr.u.a.version;
-       *pserial_number = hr.u.a.serial_number;
+       *pw_module_type = hr.u.ax.info.adapter_type;
+       *pw_num_outputs = hr.u.ax.info.num_outstreams;
+       *pw_num_inputs = hr.u.ax.info.num_instreams;
+       *pw_version = hr.u.ax.info.version;
+       *pserial_number = hr.u.ax.info.serial_number;
        *ph_module = 0;
 
        return hr.error;
 }
 
-u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 *assert_present, char *psz_assert,
-       u16 *pw_line_number)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
-               HPI_ADAPTER_GET_ASSERT);
-       hm.adapter_index = adapter_index;
-       hpi_send_recv(&hm, &hr);
-
-       *assert_present = 0;
-
-       if (!hr.error) {
-
-               *pw_line_number = (u16)hr.u.a.serial_number;
-               if (*pw_line_number) {
-
-                       int i;
-                       char *src = (char *)hr.u.a.sz_adapter_assert;
-                       char *dst = psz_assert;
-
-                       *assert_present = 1;
-
-                       for (i = 0; i < HPI_STRING_LEN; i++) {
-                               char c;
-                               c = *src++;
-                               *dst++ = c;
-                               if (c == 0)
-                                       break;
-                       }
-
-               }
-       }
-       return hr.error;
-}
-
-u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 *assert_present, char *psz_assert,
-       u32 *pline_number, u16 *pw_assert_on_dsp)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
-               HPI_ADAPTER_GET_ASSERT);
-       hm.adapter_index = adapter_index;
-
-       hpi_send_recv(&hm, &hr);
-
-       *assert_present = 0;
-
-       if (!hr.error) {
-
-               *pline_number = hr.u.a.serial_number;
-
-               *assert_present = hr.u.a.adapter_type;
-
-               *pw_assert_on_dsp = hr.u.a.adapter_index;
-
-               if (!*assert_present && *pline_number)
-
-                       *assert_present = 1;
-
-               if (*assert_present) {
-
-                       int i;
-                       char *src = (char *)hr.u.a.sz_adapter_assert;
-                       char *dst = psz_assert;
-
-                       for (i = 0; i < HPI_STRING_LEN; i++) {
-                               char c;
-                               c = *src++;
-                               *dst++ = c;
-                               if (c == 0)
-                                       break;
-                       }
-
-               } else {
-                       *psz_assert = 0;
-               }
-       }
-       return hr.error;
-}
-
-u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 assert_id)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
-               HPI_ADAPTER_TEST_ASSERT);
-       hm.adapter_index = adapter_index;
-       hm.u.a.assert_id = assert_id;
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 capability, u32 key)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
-               HPI_ADAPTER_ENABLE_CAPABILITY);
-       hm.adapter_index = adapter_index;
-       hm.u.a.assert_id = capability;
-       hm.u.a.adapter_mode = key;
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
-               HPI_ADAPTER_SELFTEST);
-       hm.adapter_index = adapter_index;
-       hpi_send_recv(&hm, &hr);
-       return hr.error;
-}
-
-u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 dsp_address, char *p_buffer, int *count_bytes)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
-               HPI_ADAPTER_DEBUG_READ);
-
-       hr.size = sizeof(hr);
-
-       hm.adapter_index = adapter_index;
-       hm.u.ax.debug_read.dsp_address = dsp_address;
-
-       if (*count_bytes > (int)sizeof(hr.u.bytes))
-               *count_bytes = sizeof(hr.u.bytes);
-
-       hm.u.ax.debug_read.count_bytes = *count_bytes;
-
-       hpi_send_recv(&hm, &hr);
-
-       if (!hr.error) {
-               *count_bytes = hr.size - 12;
-               memcpy(p_buffer, &hr.u.bytes, *count_bytes);
-       } else
-               *count_bytes = 0;
-       return hr.error;
-}
-
-u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 property, u16 parameter1, u16 parameter2)
+u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 parameter1,
+       u16 parameter2)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -542,9 +280,8 @@ u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 property, u16 *pw_parameter1,
-       u16 *pw_parameter2)
+u16 hpi_adapter_get_property(u16 adapter_index, u16 property,
+       u16 *pw_parameter1, u16 *pw_parameter2)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -564,9 +301,8 @@ u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 index, u16 what_to_enumerate,
-       u16 property_index, u32 *psetting)
+u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index,
+       u16 what_to_enumerate, u16 property_index, u32 *psetting)
 {
        return 0;
 }
@@ -574,7 +310,7 @@ u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
 u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
        u32 sample_rate, u32 bit_rate, u32 attributes)
 {
-       u16 error = 0;
+       u16 err = 0;
        struct hpi_msg_format fmt;
 
        switch (channels) {
@@ -586,8 +322,8 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
        case 16:
                break;
        default:
-               error = HPI_ERROR_INVALID_CHANNELS;
-               return error;
+               err = HPI_ERROR_INVALID_CHANNELS;
+               return err;
        }
        fmt.channels = channels;
 
@@ -610,17 +346,17 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
        case HPI_FORMAT_OEM2:
                break;
        default:
-               error = HPI_ERROR_INVALID_FORMAT;
-               return error;
+               err = HPI_ERROR_INVALID_FORMAT;
+               return err;
        }
        fmt.format = format;
 
        if (sample_rate < 8000L) {
-               error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
+               err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
                sample_rate = 8000L;
        }
        if (sample_rate > 200000L) {
-               error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
+               err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
                sample_rate = 200000L;
        }
        fmt.sample_rate = sample_rate;
@@ -651,10 +387,10 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
                if ((channels == 1)
                        && (attributes != HPI_MPEG_MODE_DEFAULT)) {
                        attributes = HPI_MPEG_MODE_DEFAULT;
-                       error = HPI_ERROR_INVALID_FORMAT;
+                       err = HPI_ERROR_INVALID_FORMAT;
                } else if (attributes > HPI_MPEG_MODE_DUALCHANNEL) {
                        attributes = HPI_MPEG_MODE_DEFAULT;
-                       error = HPI_ERROR_INVALID_FORMAT;
+                       err = HPI_ERROR_INVALID_FORMAT;
                }
                fmt.attributes = attributes;
                break;
@@ -663,7 +399,7 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
        }
 
        hpi_msg_to_format(p_format, &fmt);
-       return error;
+       return err;
 }
 
 u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format,
@@ -712,8 +448,8 @@ u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format,
        return 0;
 }
 
-u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u16 outstream_index, u32 *ph_outstream)
+u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index,
+       u32 *ph_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -733,38 +469,41 @@ u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
        return hr.error;
 }
 
-u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_close(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_HOSTBUFFER_FREE);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
+
        hpi_send_recv(&hm, &hr);
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_GROUP_RESET);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index);
        hpi_send_recv(&hm, &hr);
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_CLOSE);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index);
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
-       u32 *psamples_played, u32 *pauxiliary_data_to_play)
+u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state,
+       u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played,
+       u32 *pauxiliary_data_to_play)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_GET_INFO);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
@@ -782,15 +521,15 @@ u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, const u8 *pb_data, u32 bytes_to_write,
-       const struct hpi_format *p_format)
+u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_data,
+       u32 bytes_to_write, const struct hpi_format *p_format)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_WRITE);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.pb_data = (u8 *)pb_data;
        hm.u.d.u.data.data_size = bytes_to_write;
 
@@ -801,82 +540,85 @@ u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_start(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_START);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream)
+u16 hpi_outstream_wait_start(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_WAIT_START);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_stop(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_STOP);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream)
+u16 hpi_outstream_sinegen(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_SINEGEN);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_reset(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_RESET);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, struct hpi_format *p_format)
+u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_QUERY_FORMAT);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
 
@@ -885,15 +627,15 @@ u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, struct hpi_format *p_format)
+u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_SET_FORMAT);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
 
@@ -902,15 +644,15 @@ u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, short velocity)
+u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_SET_VELOCITY);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.velocity = velocity;
 
        hpi_send_recv(&hm, &hr);
@@ -918,15 +660,16 @@ u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample)
+u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample,
+       u32 punch_out_sample)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_SET_PUNCHINOUT);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.d.u.pio.punch_in_sample = punch_in_sample;
        hm.u.d.u.pio.punch_out_sample = punch_out_sample;
@@ -936,29 +679,29 @@ u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u16 mode)
+u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_ANC_RESET);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.format.channels = mode;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 *pframes_available)
+u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_ANC_GET_INFO);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
        if (hr.error == 0) {
                if (pframes_available)
@@ -969,8 +712,8 @@ u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_outstream_ancillary_read(u32 h_outstream,
+       struct hpi_anc_frame *p_anc_frame_buffer,
        u32 anc_frame_buffer_size_in_bytes,
        u32 number_of_ancillary_frames_to_read)
 {
@@ -979,7 +722,8 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_ANC_READ);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer;
        hm.u.d.u.data.data_size =
                number_of_ancillary_frames_to_read *
@@ -987,19 +731,19 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
        if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes)
                hpi_send_recv(&hm, &hr);
        else
-               hr.error = HPI_ERROR_INVALID_DATA_TRANSFER;
+               hr.error = HPI_ERROR_INVALID_DATASIZE;
        return hr.error;
 }
 
-u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 time_scale)
+u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scale)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_SET_TIMESCALE);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.d.u.time_scale = time_scale;
 
@@ -1008,22 +752,21 @@ u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 size_in_bytes)
+u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_HOSTBUFFER_ALLOC);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.data_size = size_in_bytes;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u8 **pp_buffer,
+u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
        struct hpi_hostbuffer_status **pp_status)
 {
        struct hpi_message hm;
@@ -1031,7 +774,8 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_HOSTBUFFER_GET_INFO);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
 
        if (hr.error == 0) {
@@ -1043,21 +787,20 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream)
+u16 hpi_outstream_host_buffer_free(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_HOSTBUFFER_FREE);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 h_stream)
+u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -1066,22 +809,22 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_GROUP_ADD);
-       hr.error = 0;
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
+
+       if (hpi_handle_indexes(h_stream, &adapter,
+                       &hm.u.d.u.stream.stream_index))
+               return HPI_ERROR_INVALID_HANDLE;
+
        c_obj_type = hpi_handle_object(h_stream);
        switch (c_obj_type) {
        case HPI_OBJ_OSTREAM:
-               hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM;
-               u32TOINDEXES(h_stream, &adapter,
-                       &hm.u.d.u.stream.stream_index);
-               break;
        case HPI_OBJ_ISTREAM:
-               hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM;
-               u32TOINDEXES(h_stream, &adapter,
-                       &hm.u.d.u.stream.stream_index);
+               hm.u.d.u.stream.object_type = c_obj_type;
                break;
        default:
-               return HPI_ERROR_INVALID_STREAM;
+               return HPI_ERROR_INVALID_OBJ;
        }
        if (adapter != hm.adapter_index)
                return HPI_ERROR_NO_INTERADAPTER_GROUPS;
@@ -1090,15 +833,16 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map)
+u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map,
+       u32 *pinstream_map)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_GROUP_GETMAP);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
 
        if (poutstream_map)
@@ -1109,21 +853,20 @@ u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_outstream)
+u16 hpi_outstream_group_reset(u32 h_outstream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                HPI_OSTREAM_GROUP_RESET);
-       u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u16 instream_index, u32 *ph_instream)
+u16 hpi_instream_open(u16 adapter_index, u16 instream_index, u32 *ph_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -1145,38 +888,40 @@ u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
        return hr.error;
 }
 
-u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_close(u32 h_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_HOSTBUFFER_FREE);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_GROUP_RESET);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index);
        hpi_send_recv(&hm, &hr);
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_CLOSE);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index);
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, const struct hpi_format *p_format)
+u16 hpi_instream_query_format(u32 h_instream,
+       const struct hpi_format *p_format)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_QUERY_FORMAT);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
 
        hpi_send_recv(&hm, &hr);
@@ -1184,15 +929,15 @@ u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, const struct hpi_format *p_format)
+u16 hpi_instream_set_format(u32 h_instream, const struct hpi_format *p_format)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_SET_FORMAT);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
 
        hpi_send_recv(&hm, &hr);
@@ -1200,15 +945,15 @@ u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
-       u8 *pb_data, u32 bytes_to_read)
+u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_data, u32 bytes_to_read)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_READ);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.data_size = bytes_to_read;
        hm.u.d.u.data.pb_data = pb_data;
 
@@ -1217,72 +962,76 @@ u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
        return hr.error;
 }
 
-u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_start(u32 h_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_START);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream)
+u16 hpi_instream_wait_start(u32 h_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_WAIT_START);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_stop(u32 h_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_STOP);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_reset(u32 h_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_RESET);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
        return hr.error;
 }
 
-u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
-       u32 *psamples_recorded, u32 *pauxiliary_data_recorded)
+u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size,
+       u32 *pdata_recorded, u32 *psamples_recorded,
+       u32 *pauxiliary_data_recorded)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_GET_INFO);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hpi_send_recv(&hm, &hr);
 
@@ -1300,15 +1049,15 @@ u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
-       u16 idle_bit)
+u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame,
+       u16 mode, u16 alignment, u16 idle_bit)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_ANC_RESET);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.format.attributes = bytes_per_frame;
        hm.u.d.u.data.format.format = (mode << 8) | (alignment & 0xff);
        hm.u.d.u.data.format.channels = idle_bit;
@@ -1316,14 +1065,14 @@ u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 *pframe_space)
+u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_ANC_GET_INFO);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
        if (pframe_space)
                *pframe_space =
@@ -1333,8 +1082,8 @@ u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_instream_ancillary_write(u32 h_instream,
+       const struct hpi_anc_frame *p_anc_frame_buffer,
        u32 anc_frame_buffer_size_in_bytes,
        u32 number_of_ancillary_frames_to_write)
 {
@@ -1343,7 +1092,8 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_ANC_WRITE);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer;
        hm.u.d.u.data.data_size =
                number_of_ancillary_frames_to_write *
@@ -1351,12 +1101,11 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
        if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes)
                hpi_send_recv(&hm, &hr);
        else
-               hr.error = HPI_ERROR_INVALID_DATA_TRANSFER;
+               hr.error = HPI_ERROR_INVALID_DATASIZE;
        return hr.error;
 }
 
-u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 size_in_bytes)
+u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes)
 {
 
        struct hpi_message hm;
@@ -1364,14 +1113,14 @@ u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_HOSTBUFFER_ALLOC);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.d.u.data.data_size = size_in_bytes;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u8 **pp_buffer,
+u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
        struct hpi_hostbuffer_status **pp_status)
 {
        struct hpi_message hm;
@@ -1379,7 +1128,8 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_HOSTBUFFER_GET_INFO);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
 
        if (hr.error == 0) {
@@ -1391,8 +1141,7 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream)
+u16 hpi_instream_host_buffer_free(u32 h_instream)
 {
 
        struct hpi_message hm;
@@ -1400,13 +1149,13 @@ u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_HOSTBUFFER_FREE);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 h_stream)
+u16 hpi_instream_group_add(u32 h_instream, u32 h_stream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -1416,22 +1165,23 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_GROUP_ADD);
        hr.error = 0;
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
+
+       if (hpi_handle_indexes(h_stream, &adapter,
+                       &hm.u.d.u.stream.stream_index))
+               return HPI_ERROR_INVALID_HANDLE;
+
        c_obj_type = hpi_handle_object(h_stream);
 
        switch (c_obj_type) {
        case HPI_OBJ_OSTREAM:
-               hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM;
-               u32TOINDEXES(h_stream, &adapter,
-                       &hm.u.d.u.stream.stream_index);
-               break;
        case HPI_OBJ_ISTREAM:
-               hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM;
-               u32TOINDEXES(h_stream, &adapter,
-                       &hm.u.d.u.stream.stream_index);
+               hm.u.d.u.stream.object_type = c_obj_type;
                break;
        default:
-               return HPI_ERROR_INVALID_STREAM;
+               return HPI_ERROR_INVALID_OBJ;
        }
 
        if (adapter != hm.adapter_index)
@@ -1441,15 +1191,16 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream, u32 *poutstream_map, u32 *pinstream_map)
+u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map,
+       u32 *pinstream_map)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_HOSTBUFFER_FREE);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
 
        if (poutstream_map)
@@ -1460,21 +1211,20 @@ u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
-       u32 h_instream)
+u16 hpi_instream_group_reset(u32 h_instream)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                HPI_ISTREAM_GROUP_RESET);
-       u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_mixer)
+u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer)
 {
        struct hpi_message hm;
        struct hpi_response hr;
@@ -1492,25 +1242,29 @@ u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
        return hr.error;
 }
 
-u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer)
+u16 hpi_mixer_close(u32 h_mixer)
 {
        struct hpi_message hm;
        struct hpi_response hr;
+
        hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE);
-       u32TOINDEX(h_mixer, &hm.adapter_index);
+       if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+               return HPI_ERROR_INVALID_HANDLE;
+
        hpi_send_recv(&hm, &hr);
        return hr.error;
 }
 
-u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
-       u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
-       u16 dst_node_type_index, u16 control_type, u32 *ph_control)
+u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type,
+       u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index,
+       u16 control_type, u32 *ph_control)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER,
                HPI_MIXER_GET_CONTROL);
-       u32TOINDEX(h_mixer, &hm.adapter_index);
+       if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.m.node_type1 = src_node_type;
        hm.u.m.node_index1 = src_node_type_index;
        hm.u.m.node_type2 = dst_node_type;
@@ -1528,16 +1282,16 @@ u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
        return hr.error;
 }
 
-u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
-       u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
-       u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
-       u16 *pw_control_type, u32 *ph_control)
+u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index,
+       u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type,
+       u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER,
                HPI_MIXER_GET_CONTROL_BY_INDEX);
-       u32TOINDEX(h_mixer, &hm.adapter_index);
+       if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.m.control_index = control_index;
        hpi_send_recv(&hm, &hr);
 
@@ -1562,13 +1316,14 @@ u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
-       enum HPI_MIXER_STORE_COMMAND command, u16 index)
+u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command,
+       u16 index)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE);
-       u32TOINDEX(h_mixer, &hm.adapter_index);
+       if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.mx.store.command = command;
        hm.u.mx.store.index = index;
        hpi_send_recv(&hm, &hr);
@@ -1576,16 +1331,16 @@ u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
 }
 
 static
-u16 hpi_control_param_set(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_control, const u16 attrib, const u32 param1,
-       const u32 param2)
+u16 hpi_control_param_set(const u32 h_control, const u16 attrib,
+       const u32 param1, const u32 param2)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = attrib;
        hm.u.c.param1 = param1;
        hm.u.c.param2 = param2;
@@ -1601,7 +1356,8 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = attrib;
        hm.u.c.an_log_value[0] = sv0;
        hm.u.c.an_log_value[1] = sv1;
@@ -1610,16 +1366,16 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
 }
 
 static
-u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_control, const u16 attrib, u32 param1, u32 param2,
-       u32 *pparam1, u32 *pparam2)
+u16 hpi_control_param_get(const u32 h_control, const u16 attrib, u32 param1,
+       u32 param2, u32 *pparam1, u32 *pparam2)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = attrib;
        hm.u.c.param1 = param1;
        hm.u.c.param2 = param2;
@@ -1632,19 +1388,20 @@ u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-#define hpi_control_param1_get(s, h, a, p1) \
-               hpi_control_param_get(s, h, a, 0, 0, p1, NULL)
-#define hpi_control_param2_get(s, h, a, p1, p2) \
-               hpi_control_param_get(s, h, a, 0, 0, p1, p2)
+#define hpi_control_param1_get(h, a, p1) \
+               hpi_control_param_get(h, a, 0, 0, p1, NULL)
+#define hpi_control_param2_get(h, a, p1, p2) \
+               hpi_control_param_get(h, a, 0, 0, p1, p2)
 
-static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 attrib, short *sv0, short *sv1)
+static u16 hpi_control_log_get2(u32 h_control, u16 attrib, short *sv0,
+       short *sv1)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = attrib;
 
        hpi_send_recv(&hm, &hr);
@@ -1655,8 +1412,7 @@ static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
 }
 
 static
-u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_control, const u16 attrib, const u32 index,
+u16 hpi_control_query(const u32 h_control, const u16 attrib, const u32 index,
        const u32 param, u32 *psetting)
 {
        struct hpi_message hm;
@@ -1664,7 +1420,8 @@ u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_INFO);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.c.attribute = attrib;
        hm.u.c.param1 = index;
@@ -1682,7 +1439,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
        unsigned int sub_string_index = 0, j = 0;
        char c = 0;
        unsigned int n = 0;
-       u16 hE = 0;
+       u16 err = 0;
 
        if ((string_length < 1) || (string_length > 256))
                return HPI_ERROR_INVALID_CONTROL_VALUE;
@@ -1693,7 +1450,9 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
 
                hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                        HPI_CONTROL_GET_STATE);
-               u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+               if (hpi_handle_indexes(h_control, &hm.adapter_index,
+                               &hm.obj_index))
+                       return HPI_ERROR_INVALID_HANDLE;
                hm.u.c.attribute = attribute;
                hm.u.c.param1 = sub_string_index;
                hm.u.c.param2 = 0;
@@ -1705,7 +1464,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
                        return HPI_ERROR_INVALID_CONTROL_VALUE;
 
                if (hr.error) {
-                       hE = hr.error;
+                       err = hr.error;
                        break;
                }
                for (j = 0; j < 8; j++) {
@@ -1714,7 +1473,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
                        n++;
                        if (n >= string_length) {
                                psz_string[string_length - 1] = 0;
-                               hE = HPI_ERROR_INVALID_CONTROL_VALUE;
+                               err = HPI_ERROR_INVALID_CONTROL_VALUE;
                                break;
                        }
                        if (c == 0)
@@ -1730,57 +1489,52 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
                if (c == 0)
                        break;
        }
-       return hE;
+       return err;
 }
 
-u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_aes_rx, const u32 index, u16 *pw_format)
+u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index,
+       u16 *pw_format)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_aes_rx, HPI_AESEBURX_FORMAT,
-               index, 0, &qr);
+       err = hpi_control_query(h_aes_rx, HPI_AESEBURX_FORMAT, index, 0, &qr);
        *pw_format = (u16)qr;
        return err;
 }
 
-u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 format)
+u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 format)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_AESEBURX_FORMAT, format, 0);
+       return hpi_control_param_set(h_control, HPI_AESEBURX_FORMAT, format,
+               0);
 }
 
-u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_format)
+u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_format)
 {
        u16 err;
        u32 param;
 
-       err = hpi_control_param1_get(ph_subsys, h_control,
-               HPI_AESEBURX_FORMAT, &param);
+       err = hpi_control_param1_get(h_control, HPI_AESEBURX_FORMAT, &param);
        if (!err && pw_format)
                *pw_format = (u16)param;
 
        return err;
 }
 
-u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *psample_rate)
+u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_AESEBURX_SAMPLERATE, psample_rate);
+       return hpi_control_param1_get(h_control, HPI_AESEBURX_SAMPLERATE,
+               psample_rate);
 }
 
-u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_AESEBURX_USERDATA;
        hm.u.c.param1 = index;
 
@@ -1791,14 +1545,15 @@ u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index,
+       u16 *pw_data)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_AESEBURX_CHANNELSTATUS;
        hm.u.c.param1 = index;
 
@@ -1809,101 +1564,93 @@ u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
        return hr.error;
 }
 
-u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_error_data)
+u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data)
 {
        u32 error_data = 0;
-       u16 error = 0;
+       u16 err = 0;
 
-       error = hpi_control_param1_get(ph_subsys, h_control,
-               HPI_AESEBURX_ERRORSTATUS, &error_data);
+       err = hpi_control_param1_get(h_control, HPI_AESEBURX_ERRORSTATUS,
+               &error_data);
        if (pw_error_data)
                *pw_error_data = (u16)error_data;
-       return error;
+       return err;
 }
 
-u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u32 sample_rate)
+u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_AESEBUTX_SAMPLERATE, sample_rate, 0);
+       return hpi_control_param_set(h_control, HPI_AESEBUTX_SAMPLERATE,
+               sample_rate, 0);
 }
 
-u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 data)
+u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_AESEBUTX_USERDATA, index, data);
+       return hpi_control_param_set(h_control, HPI_AESEBUTX_USERDATA, index,
+               data);
 }
 
-u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u16 index, u16 data)
+u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index,
+       u16 data)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_AESEBUTX_CHANNELSTATUS, index, data);
+       return hpi_control_param_set(h_control, HPI_AESEBUTX_CHANNELSTATUS,
+               index, data);
 }
 
-u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index,
+       u16 *pw_data)
 {
        return HPI_ERROR_INVALID_OPERATION;
 }
 
-u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_aes_tx, const u32 index, u16 *pw_format)
+u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index,
+       u16 *pw_format)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_aes_tx, HPI_AESEBUTX_FORMAT,
-               index, 0, &qr);
+       err = hpi_control_query(h_aes_tx, HPI_AESEBUTX_FORMAT, index, 0, &qr);
        *pw_format = (u16)qr;
        return err;
 }
 
-u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 output_format)
+u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_AESEBUTX_FORMAT, output_format, 0);
+       return hpi_control_param_set(h_control, HPI_AESEBUTX_FORMAT,
+               output_format, 0);
 }
 
-u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_output_format)
+u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format)
 {
        u16 err;
        u32 param;
 
-       err = hpi_control_param1_get(ph_subsys, h_control,
-               HPI_AESEBUTX_FORMAT, &param);
+       err = hpi_control_param1_get(h_control, HPI_AESEBUTX_FORMAT, &param);
        if (!err && pw_output_format)
                *pw_output_format = (u16)param;
 
        return err;
 }
 
-u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 edge_type)
+u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_BITSTREAM_CLOCK_EDGE, edge_type, 0);
+       return hpi_control_param_set(h_control, HPI_BITSTREAM_CLOCK_EDGE,
+               edge_type, 0);
 }
 
-u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 polarity)
+u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_BITSTREAM_DATA_POLARITY, polarity, 0);
+       return hpi_control_param_set(h_control, HPI_BITSTREAM_DATA_POLARITY,
+               polarity, 0);
 }
 
-u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity)
+u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity,
+       u16 *pw_data_activity)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_BITSTREAM_ACTIVITY;
        hpi_send_recv(&hm, &hr);
        if (pw_clk_activity)
@@ -1913,45 +1660,43 @@ u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_mode, const u32 index, u16 *pw_mode)
+u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index,
+       u16 *pw_mode)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_mode, HPI_CHANNEL_MODE_MODE,
-               index, 0, &qr);
+       err = hpi_control_query(h_mode, HPI_CHANNEL_MODE_MODE, index, 0, &qr);
        *pw_mode = (u16)qr;
        return err;
 }
 
-u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 mode)
+u16 hpi_channel_mode_set(u32 h_control, u16 mode)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_CHANNEL_MODE_MODE, mode, 0);
+       return hpi_control_param_set(h_control, HPI_CHANNEL_MODE_MODE, mode,
+               0);
 }
 
-u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 *mode)
+u16 hpi_channel_mode_get(u32 h_control, u16 *mode)
 {
        u32 mode32 = 0;
-       u16 error = hpi_control_param1_get(ph_subsys, h_control,
+       u16 err = hpi_control_param1_get(h_control,
                HPI_CHANNEL_MODE_MODE, &mode32);
        if (mode)
                *mode = (u16)mode32;
-       return error;
+       return err;
 }
 
-u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 hmi_address, u32 byte_count, u8 *pb_data)
+u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
+       u8 *pb_data)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.cx.u.cobranet_data.byte_count = byte_count;
        hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
@@ -1969,15 +1714,16 @@ u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data)
+u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
+       u32 *pbyte_count, u8 *pb_data)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.cx.u.cobranet_data.byte_count = max_byte_count;
        hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
@@ -2008,16 +1754,16 @@ u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pstatus, u32 *preadable_size,
-       u32 *pwriteable_size)
+u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
+       u32 *preadable_size, u32 *pwriteable_size)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.cx.attribute = HPI_COBRANET_GET_STATUS;
 
@@ -2035,177 +1781,176 @@ u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pi_paddress)
+u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address)
 {
        u32 byte_count;
        u32 iP;
-       u16 error;
+       u16 err;
 
-       error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+       err = hpi_cobranet_hmi_read(h_control,
                HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, &byte_count,
                (u8 *)&iP);
 
-       *pi_paddress =
+       *pdw_ip_address =
                ((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP &
                        0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8);
 
-       if (error)
-               *pi_paddress = 0;
+       if (err)
+               *pdw_ip_address = 0;
 
-       return error;
+       return err;
 
 }
 
-u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 i_paddress)
+u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address)
 {
        u32 iP;
-       u16 error;
+       u16 err;
 
-       iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) <<
-               8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress &
-                       0x000000ff) << 8);
+       iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address &
+                       0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >>
+               8) | ((dw_ip_address & 0x000000ff) << 8);
 
-       error = hpi_cobranet_hmi_write(ph_subsys, h_control,
+       err = hpi_cobranet_hmi_write(h_control,
                HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, (u8 *)&iP);
 
-       return error;
+       return err;
 
 }
 
-u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pi_paddress)
+u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address)
 {
        u32 byte_count;
        u32 iP;
-       u16 error;
-       error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+       u16 err;
+       err = hpi_cobranet_hmi_read(h_control,
                HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, &byte_count,
                (u8 *)&iP);
 
-       *pi_paddress =
+       *pdw_ip_address =
                ((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP &
                        0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8);
 
-       if (error)
-               *pi_paddress = 0;
+       if (err)
+               *pdw_ip_address = 0;
 
-       return error;
+       return err;
 
 }
 
-u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 i_paddress)
+u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address)
 {
        u32 iP;
-       u16 error;
+       u16 err;
 
-       iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) <<
-               8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress &
-                       0x000000ff) << 8);
+       iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address &
+                       0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >>
+               8) | ((dw_ip_address & 0x000000ff) << 8);
 
-       error = hpi_cobranet_hmi_write(ph_subsys, h_control,
+       err = hpi_cobranet_hmi_write(h_control,
                HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, (u8 *)&iP);
 
-       return error;
+       return err;
 
 }
 
-u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs)
+u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs,
+       u32 *p_mac_lsbs)
 {
        u32 byte_count;
-       u16 error;
-       u32 mAC;
+       u16 err;
+       u32 mac;
 
-       error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+       err = hpi_cobranet_hmi_read(h_control,
                HPI_COBRANET_HMI_cobra_if_phy_address, 4, &byte_count,
-               (u8 *)&mAC);
-       *pmAC_MS_bs =
-               ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC
-                       & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8);
-       error += hpi_cobranet_hmi_read(ph_subsys, h_control,
-               HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4, &byte_count,
-               (u8 *)&mAC);
-       *pmAC_LS_bs =
-               ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC
-                       & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8);
-
-       if (error) {
-               *pmAC_MS_bs = 0;
-               *pmAC_LS_bs = 0;
+               (u8 *)&mac);
+
+       if (!err) {
+               *p_mac_msbs =
+                       ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8)
+                       | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) <<
+                       8);
+
+               err = hpi_cobranet_hmi_read(h_control,
+                       HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4,
+                       &byte_count, (u8 *)&mac);
        }
 
-       return error;
+       if (!err) {
+               *p_mac_lsbs =
+                       ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8)
+                       | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) <<
+                       8);
+       } else {
+               *p_mac_msbs = 0;
+               *p_mac_lsbs = 0;
+       }
+
+       return err;
 }
 
-u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 enable)
+u16 hpi_compander_set_enable(u32 h_control, u32 enable)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
-               enable, 0);
+       return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+               0);
 }
 
-u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *enable)
+u16 hpi_compander_get_enable(u32 h_control, u32 *enable)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_GENERIC_ENABLE, enable);
+       return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
 }
 
-u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short makeup_gain0_01dB)
+u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB)
 {
        return hpi_control_log_set2(h_control, HPI_COMPANDER_MAKEUPGAIN,
                makeup_gain0_01dB, 0);
 }
 
-u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short *makeup_gain0_01dB)
+u16 hpi_compander_get_makeup_gain(u32 h_control, short *makeup_gain0_01dB)
 {
-       return hpi_control_log_get2(ph_subsys, h_control,
-               HPI_COMPANDER_MAKEUPGAIN, makeup_gain0_01dB, NULL);
+       return hpi_control_log_get2(h_control, HPI_COMPANDER_MAKEUPGAIN,
+               makeup_gain0_01dB, NULL);
 }
 
-u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, unsigned int index, u32 attack)
+u16 hpi_compander_set_attack_time_constant(u32 h_control, unsigned int index,
+       u32 attack)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_COMPANDER_ATTACK, attack, index);
+       return hpi_control_param_set(h_control, HPI_COMPANDER_ATTACK, attack,
+               index);
 }
 
-u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
-       *ph_subsys, u32 h_control, unsigned int index, u32 *attack)
+u16 hpi_compander_get_attack_time_constant(u32 h_control, unsigned int index,
+       u32 *attack)
 {
-       return hpi_control_param_get(ph_subsys, h_control,
-               HPI_COMPANDER_ATTACK, 0, index, attack, NULL);
+       return hpi_control_param_get(h_control, HPI_COMPANDER_ATTACK, 0,
+               index, attack, NULL);
 }
 
-u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, unsigned int index, u32 decay)
+u16 hpi_compander_set_decay_time_constant(u32 h_control, unsigned int index,
+       u32 decay)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_COMPANDER_DECAY, decay, index);
+       return hpi_control_param_set(h_control, HPI_COMPANDER_DECAY, decay,
+               index);
 }
 
-u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, unsigned int index, u32 *decay)
+u16 hpi_compander_get_decay_time_constant(u32 h_control, unsigned int index,
+       u32 *decay)
 {
-       return hpi_control_param_get(ph_subsys, h_control,
-               HPI_COMPANDER_DECAY, 0, index, decay, NULL);
+       return hpi_control_param_get(h_control, HPI_COMPANDER_DECAY, 0, index,
+               decay, NULL);
 
 }
 
-u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, unsigned int index, short threshold0_01dB)
+u16 hpi_compander_set_threshold(u32 h_control, unsigned int index,
+       short threshold0_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
        hm.u.c.param2 = index;
        hm.u.c.an_log_value[0] = threshold0_01dB;
@@ -2215,15 +1960,16 @@ u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, unsigned int index, short *threshold0_01dB)
+u16 hpi_compander_get_threshold(u32 h_control, unsigned int index,
+       short *threshold0_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
        hm.u.c.param2 = index;
 
@@ -2233,29 +1979,28 @@ u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 ratio100)
+u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_COMPANDER_RATIO, ratio100, index);
+       return hpi_control_param_set(h_control, HPI_COMPANDER_RATIO, ratio100,
+               index);
 }
 
-u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 *ratio100)
+u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *ratio100)
 {
-       return hpi_control_param_get(ph_subsys, h_control,
-               HPI_COMPANDER_RATIO, 0, index, ratio100, NULL);
+       return hpi_control_param_get(h_control, HPI_COMPANDER_RATIO, 0, index,
+               ratio100, NULL);
 }
 
-u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
+u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB,
+       short *max_gain_01dB, short *step_gain_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_LEVEL_RANGE;
 
        hpi_send_recv(&hm, &hr);
@@ -2273,31 +2018,27 @@ u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
        )
 {
        return hpi_control_log_set2(h_control, HPI_LEVEL_GAIN,
                an_gain0_01dB[0], an_gain0_01dB[1]);
 }
 
-u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_get_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
        )
 {
-       return hpi_control_log_get2(ph_subsys, h_control, HPI_LEVEL_GAIN,
+       return hpi_control_log_get2(h_control, HPI_LEVEL_GAIN,
                &an_gain0_01dB[0], &an_gain0_01dB[1]);
 }
 
-u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_meter, u32 *p_channels)
+u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels)
 {
-       return hpi_control_query(ph_subsys, h_meter, HPI_METER_NUM_CHANNELS,
-               0, 0, p_channels);
+       return hpi_control_query(h_meter, HPI_METER_NUM_CHANNELS, 0, 0,
+               p_channels);
 }
 
-u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_peakdB[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_peak(u32 h_control, short an_peakdB[HPI_MAX_CHANNELS]
        )
 {
        short i = 0;
@@ -2307,7 +2048,8 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.obj_index = hm.obj_index;
        hm.u.c.attribute = HPI_METER_PEAK;
 
@@ -2322,8 +2064,7 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_rmsdB[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_rms(u32 h_control, short an_rmsdB[HPI_MAX_CHANNELS]
        )
 {
        short i = 0;
@@ -2333,7 +2074,8 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_METER_RMS;
 
        hpi_send_recv(&hm, &hr);
@@ -2348,22 +2090,20 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 attack, u16 decay)
+u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_METER_RMS_BALLISTICS, attack, decay);
+       return hpi_control_param_set(h_control, HPI_METER_RMS_BALLISTICS,
+               attack, decay);
 }
 
-u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pn_attack, u16 *pn_decay)
+u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *pn_attack, u16 *pn_decay)
 {
        u32 attack;
        u32 decay;
        u16 error;
 
-       error = hpi_control_param2_get(ph_subsys, h_control,
-               HPI_METER_RMS_BALLISTICS, &attack, &decay);
+       error = hpi_control_param2_get(h_control, HPI_METER_RMS_BALLISTICS,
+               &attack, &decay);
 
        if (pn_attack)
                *pn_attack = (unsigned short)attack;
@@ -2373,22 +2113,21 @@ u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
        return error;
 }
 
-u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 attack, u16 decay)
+u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_METER_PEAK_BALLISTICS, attack, decay);
+       return hpi_control_param_set(h_control, HPI_METER_PEAK_BALLISTICS,
+               attack, decay);
 }
 
-u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pn_attack, u16 *pn_decay)
+u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *pn_attack,
+       u16 *pn_decay)
 {
        u32 attack;
        u32 decay;
        u16 error;
 
-       error = hpi_control_param2_get(ph_subsys, h_control,
-               HPI_METER_PEAK_BALLISTICS, &attack, &decay);
+       error = hpi_control_param2_get(h_control, HPI_METER_PEAK_BALLISTICS,
+               &attack, &decay);
 
        if (pn_attack)
                *pn_attack = (short)attack;
@@ -2398,55 +2137,53 @@ u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
        return error;
 }
 
-u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 on_off)
+u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_MICROPHONE_PHANTOM_POWER, (u32)on_off, 0);
+       return hpi_control_param_set(h_control, HPI_MICROPHONE_PHANTOM_POWER,
+               (u32)on_off, 0);
 }
 
-u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_on_off)
+u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off)
 {
        u16 error = 0;
        u32 on_off = 0;
-       error = hpi_control_param1_get(ph_subsys, h_control,
+       error = hpi_control_param1_get(h_control,
                HPI_MICROPHONE_PHANTOM_POWER, &on_off);
        if (pw_on_off)
                *pw_on_off = (u16)on_off;
        return error;
 }
 
-u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source_node_type, u16 source_node_index)
+u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type,
+       u16 source_node_index)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_MULTIPLEXER_SOURCE, source_node_type, source_node_index);
+       return hpi_control_param_set(h_control, HPI_MULTIPLEXER_SOURCE,
+               source_node_type, source_node_index);
 }
 
-u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *source_node_type, u16 *source_node_index)
+u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type,
+       u16 *source_node_index)
 {
        u32 node, index;
-       u16 error = hpi_control_param2_get(ph_subsys, h_control,
+       u16 err = hpi_control_param2_get(h_control,
                HPI_MULTIPLEXER_SOURCE, &node,
                &index);
        if (source_node_type)
                *source_node_type = (u16)node;
        if (source_node_index)
                *source_node_index = (u16)index;
-       return error;
+       return err;
 }
 
-u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 *source_node_type,
-       u16 *source_node_index)
+u16 hpi_multiplexer_query_source(u32 h_control, u16 index,
+       u16 *source_node_type, u16 *source_node_index)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_MULTIPLEXER_QUERYSOURCE;
        hm.u.c.param1 = index;
 
@@ -2459,15 +2196,15 @@ u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_number_of_bands, u16 *pw_on_off)
+u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands,
+       u16 *pw_on_off)
 {
        u32 oB = 0;
        u32 oO = 0;
        u16 error = 0;
 
-       error = hpi_control_param2_get(ph_subsys, h_control,
-               HPI_EQUALIZER_NUM_FILTERS, &oO, &oB);
+       error = hpi_control_param2_get(h_control, HPI_EQUALIZER_NUM_FILTERS,
+               &oO, &oB);
        if (pw_number_of_bands)
                *pw_number_of_bands = (u16)oB;
        if (pw_on_off)
@@ -2475,23 +2212,22 @@ u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
        return error;
 }
 
-u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 on_off)
+u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_EQUALIZER_NUM_FILTERS, on_off, 0);
+       return hpi_control_param_set(h_control, HPI_EQUALIZER_NUM_FILTERS,
+               on_off, 0);
 }
 
-u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
-       short *pnQ100, short *pn_gain0_01dB)
+u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type,
+       u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_EQUALIZER_FILTER;
        hm.u.c.param2 = index;
 
@@ -2509,16 +2245,16 @@ u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
-       short gain0_01dB)
+u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type,
+       u32 frequency_hz, short q100, short gain0_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        hm.u.c.param1 = frequency_hz;
        hm.u.c.param2 = (index & 0xFFFFL) + ((u32)type << 16);
@@ -2531,8 +2267,7 @@ u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 index, short coeffs[5]
+u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5]
        )
 {
        struct hpi_message hm;
@@ -2540,7 +2275,8 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_EQUALIZER_COEFFICIENTS;
        hm.u.c.param2 = index;
 
@@ -2555,444 +2291,388 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_clock, const u32 index, u16 *pw_source)
+u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index,
+       u16 *pw_source)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_clock, HPI_SAMPLECLOCK_SOURCE,
-               index, 0, &qr);
+       err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE, index, 0,
+               &qr);
        *pw_source = (u16)qr;
        return err;
 }
 
-u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source)
+u16 hpi_sample_clock_set_source(u32 h_control, u16 source)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_SOURCE, source, 0);
+       return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE,
+               source, 0);
 }
 
-u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_source)
+u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source)
 {
-       u16 error = 0;
+       u16 err = 0;
        u32 source = 0;
-       error = hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_SOURCE, &source);
-       if (!error)
+       err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE,
+               &source);
+       if (!err)
                if (pw_source)
                        *pw_source = (u16)source;
-       return error;
+       return err;
 }
 
-u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_clock, const u32 index, const u32 source,
-       u16 *pw_source_index)
+u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index,
+       const u32 source, u16 *pw_source_index)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_clock,
-               HPI_SAMPLECLOCK_SOURCE_INDEX, index, source, &qr);
+       err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE_INDEX, index,
+               source, &qr);
        *pw_source_index = (u16)qr;
        return err;
 }
 
-u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 source_index)
+u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_SOURCE_INDEX, source_index, 0);
+       return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX,
+               source_index, 0);
 }
 
-u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u16 *pw_source_index)
+u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index)
 {
-       u16 error = 0;
+       u16 err = 0;
        u32 source_index = 0;
-       error = hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_SOURCE_INDEX, &source_index);
-       if (!error)
+       err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX,
+               &source_index);
+       if (!err)
                if (pw_source_index)
                        *pw_source_index = (u16)source_index;
-       return error;
+       return err;
 }
 
-u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_clock, const u32 index, u32 *prate)
+u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
+       u32 *prate)
 {
        u16 err;
-       err = hpi_control_query(ph_subsys, h_clock,
-               HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, index, 0, prate);
+       err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE,
+               index, 0, prate);
 
        return err;
 }
 
-u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 sample_rate)
+u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
+       return hpi_control_param_set(h_control,
                HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, sample_rate, 0);
 }
 
-u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *psample_rate)
+u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate)
 {
-       u16 error = 0;
+       u16 err = 0;
        u32 sample_rate = 0;
-       error = hpi_control_param1_get(ph_subsys, h_control,
+       err = hpi_control_param1_get(h_control,
                HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, &sample_rate);
-       if (!error)
+       if (!err)
                if (psample_rate)
                        *psample_rate = sample_rate;
-       return error;
+       return err;
 }
 
-u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *psample_rate)
+u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate)
 {
-       u16 error = 0;
+       u16 err = 0;
        u32 sample_rate = 0;
-       error = hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_SAMPLERATE, &sample_rate);
-       if (!error)
+       err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SAMPLERATE,
+               &sample_rate);
+       if (!err)
                if (psample_rate)
                        *psample_rate = sample_rate;
-       return error;
+       return err;
 }
 
-u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 enable)
+u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_AUTO, enable, 0);
+       return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_AUTO, enable,
+               0);
 }
 
-u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *penable)
+u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_AUTO, penable);
+       return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_AUTO,
+               penable);
 }
 
-u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 lock)
+u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_LOCAL_LOCK, lock, 0);
+       return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK,
+               lock, 0);
 }
 
-u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *plock)
+u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SAMPLECLOCK_LOCAL_LOCK, plock);
+       return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK,
+               plock);
 }
 
-u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 index, u32 *frequency)
+u16 hpi_tone_detector_get_frequency(u32 h_control, u32 index, u32 *frequency)
 {
-       return hpi_control_param_get(ph_subsys, h_control,
-               HPI_TONEDETECTOR_FREQUENCY, index, 0, frequency, NULL);
+       return hpi_control_param_get(h_control, HPI_TONEDETECTOR_FREQUENCY,
+               index, 0, frequency, NULL);
 }
 
-u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *state)
+u16 hpi_tone_detector_get_state(u32 h_control, u32 *state)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_TONEDETECTOR_STATE, state);
+       return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_STATE,
+               state);
 }
 
-u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 enable)
+u16 hpi_tone_detector_set_enable(u32 h_control, u32 enable)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
-               (u32)enable, 0);
+       return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+               0);
 }
 
-u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *enable)
+u16 hpi_tone_detector_get_enable(u32 h_control, u32 *enable)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_GENERIC_ENABLE, enable);
+       return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
 }
 
-u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 event_enable)
+u16 hpi_tone_detector_set_event_enable(u32 h_control, u32 event_enable)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0);
+       return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE,
+               (u32)event_enable, 0);
 }
 
-u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *event_enable)
+u16 hpi_tone_detector_get_event_enable(u32 h_control, u32 *event_enable)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_GENERIC_EVENT_ENABLE, event_enable);
+       return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE,
+               event_enable);
 }
 
-u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, int threshold)
+u16 hpi_tone_detector_set_threshold(u32 h_control, int threshold)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_TONEDETECTOR_THRESHOLD, (u32)threshold, 0);
+       return hpi_control_param_set(h_control, HPI_TONEDETECTOR_THRESHOLD,
+               (u32)threshold, 0);
 }
 
-u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, int *threshold)
+u16 hpi_tone_detector_get_threshold(u32 h_control, int *threshold)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_TONEDETECTOR_THRESHOLD, (u32 *)threshold);
+       return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_THRESHOLD,
+               (u32 *)threshold);
 }
 
-u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *state)
+u16 hpi_silence_detector_get_state(u32 h_control, u32 *state)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SILENCEDETECTOR_STATE, state);
+       return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_STATE,
+               state);
 }
 
-u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 enable)
+u16 hpi_silence_detector_set_enable(u32 h_control, u32 enable)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
-               (u32)enable, 0);
+       return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+               0);
 }
 
-u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *enable)
+u16 hpi_silence_detector_get_enable(u32 h_control, u32 *enable)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_GENERIC_ENABLE, enable);
+       return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
 }
 
-u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 event_enable)
+u16 hpi_silence_detector_set_event_enable(u32 h_control, u32 event_enable)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_GENERIC_EVENT_ENABLE, event_enable, 0);
+       return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE,
+               event_enable, 0);
 }
 
-u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *event_enable)
+u16 hpi_silence_detector_get_event_enable(u32 h_control, u32 *event_enable)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_GENERIC_EVENT_ENABLE, event_enable);
+       return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE,
+               event_enable);
 }
 
-u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 delay)
+u16 hpi_silence_detector_set_delay(u32 h_control, u32 delay)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_SILENCEDETECTOR_DELAY, delay, 0);
+       return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_DELAY,
+               delay, 0);
 }
 
-u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *delay)
+u16 hpi_silence_detector_get_delay(u32 h_control, u32 *delay)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_SILENCEDETECTOR_DELAY, delay);
+       return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_DELAY,
+               delay);
 }
 
-u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, int threshold)
+u16 hpi_silence_detector_set_threshold(u32 h_control, int threshold)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_SILENCEDETECTOR_THRESHOLD, threshold, 0);
+       return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_THRESHOLD,
+               threshold, 0);
 }
 
-u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, int *threshold)
+u16 hpi_silence_detector_get_threshold(u32 h_control, int *threshold)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
+       return hpi_control_param1_get(h_control,
                HPI_SILENCEDETECTOR_THRESHOLD, (u32 *)threshold);
 }
 
-u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, u16 *pw_band)
+u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0,
-               &qr);
+       err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr);
        *pw_band = (u16)qr;
        return err;
 }
 
-u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 band)
+u16 hpi_tuner_set_band(u32 h_control, u16 band)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_BAND,
-               band, 0);
+       return hpi_control_param_set(h_control, HPI_TUNER_BAND, band, 0);
 }
 
-u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 *pw_band)
+u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band)
 {
        u32 band = 0;
        u16 error = 0;
 
-       error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_BAND,
-               &band);
+       error = hpi_control_param1_get(h_control, HPI_TUNER_BAND, &band);
        if (pw_band)
                *pw_band = (u16)band;
        return error;
 }
 
-u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq)
+u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index,
+       const u16 band, u32 *pfreq)
 {
-       return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_FREQ, index,
-               band, pfreq);
+       return hpi_control_query(h_tuner, HPI_TUNER_FREQ, index, band, pfreq);
 }
 
-u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 freq_ink_hz)
+u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_FREQ,
-               freq_ink_hz, 0);
+       return hpi_control_param_set(h_control, HPI_TUNER_FREQ, freq_ink_hz,
+               0);
 }
 
-u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pw_freq_ink_hz)
+u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz)
 {
-       return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_FREQ,
+       return hpi_control_param1_get(h_control, HPI_TUNER_FREQ,
                pw_freq_ink_hz);
 }
 
-u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, u16 *pw_gain)
+u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain)
 {
        u32 qr;
        u16 err;
 
-       err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0,
-               &qr);
+       err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr);
        *pw_gain = (u16)qr;
        return err;
 }
 
-u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short gain)
+u16 hpi_tuner_set_gain(u32 h_control, short gain)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_GAIN,
-               gain, 0);
+       return hpi_control_param_set(h_control, HPI_TUNER_GAIN, gain, 0);
 }
 
-u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *pn_gain)
+u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain)
 {
        u32 gain = 0;
        u16 error = 0;
 
-       error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_GAIN,
-               &gain);
+       error = hpi_control_param1_get(h_control, HPI_TUNER_GAIN, &gain);
        if (pn_gain)
                *pn_gain = (u16)gain;
        return error;
 }
 
-u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *pw_level)
+u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-       hm.u.c.attribute = HPI_TUNER_LEVEL;
-       hm.u.c.param1 = HPI_TUNER_LEVEL_AVERAGE;
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
+       hm.u.cu.attribute = HPI_TUNER_LEVEL_AVG;
        hpi_send_recv(&hm, &hr);
        if (pw_level)
-               *pw_level = (short)hr.u.c.param1;
+               *pw_level = hr.u.cu.tuner.s_level;
        return hr.error;
 }
 
-u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short *pw_level)
+u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-       hm.u.c.attribute = HPI_TUNER_LEVEL;
-       hm.u.c.param1 = HPI_TUNER_LEVEL_RAW;
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
+       hm.u.cu.attribute = HPI_TUNER_LEVEL_RAW;
        hpi_send_recv(&hm, &hr);
        if (pw_level)
-               *pw_level = (short)hr.u.c.param1;
+               *pw_level = hr.u.cu.tuner.s_level;
        return hr.error;
 }
 
-u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis)
+u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index,
+       const u16 band, u32 *pdeemphasis)
 {
-       return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_DEEMPHASIS,
-               index, band, pdeemphasis);
+       return hpi_control_query(h_tuner, HPI_TUNER_DEEMPHASIS, index, band,
+               pdeemphasis);
 }
 
-u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 deemphasis)
+u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_TUNER_DEEMPHASIS, deemphasis, 0);
+       return hpi_control_param_set(h_control, HPI_TUNER_DEEMPHASIS,
+               deemphasis, 0);
 }
 
-u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pdeemphasis)
+u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_TUNER_DEEMPHASIS, pdeemphasis);
+       return hpi_control_param1_get(h_control, HPI_TUNER_DEEMPHASIS,
+               pdeemphasis);
 }
 
-u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_tuner, u32 *pbitmap_program)
+u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program)
 {
-       return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_PROGRAM, 0, 0,
+       return hpi_control_query(h_tuner, HPI_TUNER_PROGRAM, 0, 0,
                pbitmap_program);
 }
 
-u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 program)
+u16 hpi_tuner_set_program(u32 h_control, u32 program)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_PROGRAM,
-               program, 0);
+       return hpi_control_param_set(h_control, HPI_TUNER_PROGRAM, program,
+               0);
 }
 
-u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 *pprogram)
+u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram)
 {
-       return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_PROGRAM,
-               pprogram);
+       return hpi_control_param1_get(h_control, HPI_TUNER_PROGRAM, pprogram);
 }
 
-u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, char *psz_dsp_version, const u32 string_size)
+u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version,
+       const u32 string_size)
 {
        return hpi_control_get_string(h_control,
                HPI_TUNER_HDRADIO_DSP_VERSION, psz_dsp_version, string_size);
 }
 
-u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, char *psz_sdk_version, const u32 string_size)
+u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version,
+       const u32 string_size)
 {
        return hpi_control_get_string(h_control,
                HPI_TUNER_HDRADIO_SDK_VERSION, psz_sdk_version, string_size);
 }
 
-u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u16 *pw_status_mask, u16 *pw_status)
+u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status)
 {
        u32 status = 0;
        u16 error = 0;
 
-       error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_STATUS,
-               &status);
+       error = hpi_control_param1_get(h_control, HPI_TUNER_STATUS, &status);
        if (pw_status) {
                if (!error) {
                        *pw_status_mask = (u16)(status >> 16);
@@ -3005,50 +2685,44 @@ u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return error;
 }
 
-u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 mode, u32 value)
+u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value)
 {
-       return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_MODE,
-               mode, value);
+       return hpi_control_param_set(h_control, HPI_TUNER_MODE, mode, value);
 }
 
-u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 mode, u32 *pn_value)
+u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value)
 {
-       return hpi_control_param_get(ph_subsys, h_control, HPI_TUNER_MODE,
-               mode, 0, pn_value, NULL);
+       return hpi_control_param_get(h_control, HPI_TUNER_MODE, mode, 0,
+               pn_value, NULL);
 }
 
-u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pquality)
+u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
+       return hpi_control_param1_get(h_control,
                HPI_TUNER_HDRADIO_SIGNAL_QUALITY, pquality);
 }
 
-u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *pblend)
+u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_TUNER_HDRADIO_BLEND, pblend);
+       return hpi_control_param1_get(h_control, HPI_TUNER_HDRADIO_BLEND,
+               pblend);
 }
 
-u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, const u32 blend)
+u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend)
 {
-       return hpi_control_param_set(ph_subsys, h_control,
-               HPI_TUNER_HDRADIO_BLEND, blend, 0);
+       return hpi_control_param_set(h_control, HPI_TUNER_HDRADIO_BLEND,
+               blend, 0);
 }
 
-u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *p_data)
+u16 hpi_tuner_get_rds(u32 h_control, char *p_data)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_TUNER_RDS;
        hpi_send_recv(&hm, &hr);
        if (p_data) {
@@ -3059,80 +2733,82 @@ u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, char *psz_string, const u32 data_length)
+u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string,
+       const u32 data_length)
 {
        return hpi_control_get_string(h_control, HPI_PAD_CHANNEL_NAME,
                psz_string, data_length);
 }
 
-u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *psz_string, const u32 data_length)
+u16 hpi_pad_get_artist(u32 h_control, char *psz_string, const u32 data_length)
 {
        return hpi_control_get_string(h_control, HPI_PAD_ARTIST, psz_string,
                data_length);
 }
 
-u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *psz_string, const u32 data_length)
+u16 hpi_pad_get_title(u32 h_control, char *psz_string, const u32 data_length)
 {
        return hpi_control_get_string(h_control, HPI_PAD_TITLE, psz_string,
                data_length);
 }
 
-u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       char *psz_string, const u32 data_length)
+u16 hpi_pad_get_comment(u32 h_control, char *psz_string,
+       const u32 data_length)
 {
        return hpi_control_get_string(h_control, HPI_PAD_COMMENT, psz_string,
                data_length);
 }
 
-u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, u32 *ppTY)
+u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_PAD_PROGRAM_TYPE, ppTY);
+       return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_TYPE, ppTY);
 }
 
-u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       u32 *ppI)
+u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI)
 {
-       return hpi_control_param1_get(ph_subsys, h_control,
-               HPI_PAD_PROGRAM_ID, ppI);
+       return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_ID, ppI);
 }
 
-u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
-       const u32 h_volume, u32 *p_channels)
+u16 hpi_volume_query_channels(const u32 h_volume, u32 *p_channels)
 {
-       return hpi_control_query(ph_subsys, h_volume, HPI_VOLUME_NUM_CHANNELS,
-               0, 0, p_channels);
+       return hpi_control_query(h_volume, HPI_VOLUME_NUM_CHANNELS, 0, 0,
+               p_channels);
 }
 
-u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_log_gain[HPI_MAX_CHANNELS]
+u16 hpi_volume_set_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS]
        )
 {
        return hpi_control_log_set2(h_control, HPI_VOLUME_GAIN,
                an_log_gain[0], an_log_gain[1]);
 }
 
-u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_log_gain[HPI_MAX_CHANNELS]
+u16 hpi_volume_get_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS]
        )
 {
-       return hpi_control_log_get2(ph_subsys, h_control, HPI_VOLUME_GAIN,
+       return hpi_control_log_get2(h_control, HPI_VOLUME_GAIN,
                &an_log_gain[0], &an_log_gain[1]);
 }
 
-u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
+u16 hpi_volume_set_mute(u32 h_control, u32 mute)
+{
+       return hpi_control_param_set(h_control, HPI_VOLUME_MUTE, mute, 0);
+}
+
+u16 hpi_volume_get_mute(u32 h_control, u32 *mute)
+{
+       return hpi_control_param1_get(h_control, HPI_VOLUME_MUTE, mute);
+}
+
+u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB,
+       short *max_gain_01dB, short *step_gain_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_VOLUME_RANGE;
 
        hpi_send_recv(&hm, &hr);
@@ -3150,16 +2826,17 @@ u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
-       u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
-       u32 duration_ms, u16 profile)
+u16 hpi_volume_auto_fade_profile(u32 h_control,
+       short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms,
+       u16 profile)
 {
        struct hpi_message hm;
        struct hpi_response hr;
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
 
        memcpy(hm.u.c.an_log_value, an_stop_gain0_01dB,
                sizeof(short) * HPI_MAX_CHANNELS);
@@ -3173,21 +2850,21 @@ u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
        return hr.error;
 }
 
-u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_auto_fade(u32 h_control,
        short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms)
 {
-       return hpi_volume_auto_fade_profile(ph_subsys, h_control,
-               an_stop_gain0_01dB, duration_ms, HPI_VOLUME_AUTOFADE_LOG);
+       return hpi_volume_auto_fade_profile(h_control, an_stop_gain0_01dB,
+               duration_ms, HPI_VOLUME_AUTOFADE_LOG);
 }
 
-u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short an_gain0_01dB)
+u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_VOX_THRESHOLD;
 
        hm.u.c.an_log_value[0] = an_gain0_01dB;
@@ -3197,14 +2874,14 @@ u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        return hr.error;
 }
 
-u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-       short *an_gain0_01dB)
+u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB)
 {
        struct hpi_message hm;
        struct hpi_response hr;
        hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
                HPI_CONTROL_GET_STATE);
-       u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+       if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+               return HPI_ERROR_INVALID_HANDLE;
        hm.u.c.attribute = HPI_VOX_THRESHOLD;
 
        hpi_send_recv(&hm, &hr);
@@ -3213,728 +2890,3 @@ u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 
        return hr.error;
 }
-
-static size_t strv_packet_size = MIN_STRV_PACKET_SIZE;
-
-static size_t entity_type_to_size[LAST_ENTITY_TYPE] = {
-       0,
-       sizeof(struct hpi_entity),
-       sizeof(void *),
-
-       sizeof(int),
-       sizeof(float),
-       sizeof(double),
-
-       sizeof(char),
-       sizeof(char),
-
-       4 * sizeof(char),
-       16 * sizeof(char),
-       6 * sizeof(char),
-};
-
-static inline size_t hpi_entity_size(struct hpi_entity *entity_ptr)
-{
-       return entity_ptr->header.size;
-}
-
-static inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr)
-{
-       return sizeof(entity_ptr->header);
-}
-
-static inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr)
-{
-       return hpi_entity_size(entity_ptr) -
-               hpi_entity_header_size(entity_ptr);
-}
-
-static inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr)
-{
-       return hpi_entity_value_size(entity_ptr) /
-               entity_type_to_size[entity_ptr->header.type];
-}
-
-static inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity
-       *entity_ptr)
-{
-       return (void *)(((u8 *)entity_ptr) + hpi_entity_size(entity_ptr));
-}
-
-static inline u16 hpi_entity_check_type(const enum e_entity_type t)
-{
-       if (t >= 0 && t < STR_TYPE_FIELD_MAX)
-               return 0;
-       return HPI_ERROR_ENTITY_TYPE_INVALID;
-}
-
-static inline u16 hpi_entity_check_role(const enum e_entity_role r)
-{
-       if (r >= 0 && r < STR_ROLE_FIELD_MAX)
-               return 0;
-       return HPI_ERROR_ENTITY_ROLE_INVALID;
-}
-
-static u16 hpi_entity_get_next(struct hpi_entity *entity, int recursive_flag,
-       void *guard_p, struct hpi_entity **next)
-{
-       HPI_DEBUG_ASSERT(entity != NULL);
-       HPI_DEBUG_ASSERT(next != NULL);
-       HPI_DEBUG_ASSERT(hpi_entity_size(entity) != 0);
-
-       if (guard_p <= (void *)entity) {
-               *next = NULL;
-               return 0;
-       }
-
-       if (recursive_flag && entity->header.type == entity_type_sequence)
-               *next = (struct hpi_entity *)entity->value;
-       else
-               *next = (struct hpi_entity *)hpi_entity_ptr_to_next(entity);
-
-       if (guard_p <= (void *)*next) {
-               *next = NULL;
-               return 0;
-       }
-
-       HPI_DEBUG_ASSERT(guard_p >= (void *)hpi_entity_ptr_to_next(*next));
-       return 0;
-}
-
-u16 hpi_entity_find_next(struct hpi_entity *container_entity,
-       enum e_entity_type type, enum e_entity_role role, int recursive_flag,
-       struct hpi_entity **current_match)
-{
-       struct hpi_entity *tmp = NULL;
-       void *guard_p = NULL;
-
-       HPI_DEBUG_ASSERT(container_entity != NULL);
-       guard_p = hpi_entity_ptr_to_next(container_entity);
-
-       if (*current_match != NULL)
-               hpi_entity_get_next(*current_match, recursive_flag, guard_p,
-                       &tmp);
-       else
-               hpi_entity_get_next(container_entity, 1, guard_p, &tmp);
-
-       while (tmp) {
-               u16 err;
-
-               HPI_DEBUG_ASSERT((void *)tmp >= (void *)container_entity);
-
-               if ((!type || tmp->header.type == type) && (!role
-                               || tmp->header.role == role)) {
-                       *current_match = tmp;
-                       return 0;
-               }
-
-               err = hpi_entity_get_next(tmp, recursive_flag, guard_p,
-                       current_match);
-               if (err)
-                       return err;
-
-               tmp = *current_match;
-       }
-
-       *current_match = NULL;
-       return 0;
-}
-
-void hpi_entity_free(struct hpi_entity *entity)
-{
-       kfree(entity);
-}
-
-static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src,
-       struct hpi_entity **dst)
-{
-       size_t buf_size;
-       HPI_DEBUG_ASSERT(dst != NULL);
-       HPI_DEBUG_ASSERT(src != NULL);
-
-       buf_size = hpi_entity_size(src);
-       *dst = kmalloc(buf_size, GFP_KERNEL);
-       if (*dst == NULL)
-               return HPI_ERROR_MEMORY_ALLOC;
-       memcpy(*dst, src, buf_size);
-       return 0;
-}
-
-u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       struct hpi_entity **info)
-{
-       struct hpi_msg_strv hm;
-       struct hpi_res_strv *phr;
-       u16 hpi_err;
-       int remaining_attempts = 2;
-       size_t resp_packet_size = 1024;
-
-       *info = NULL;
-
-       while (remaining_attempts--) {
-               phr = kmalloc(resp_packet_size, GFP_KERNEL);
-               HPI_DEBUG_ASSERT(phr != NULL);
-
-               hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h,
-                       (u16)resp_packet_size, HPI_OBJ_CONTROL,
-                       HPI_CONTROL_GET_INFO);
-               u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index);
-
-               hm.strv.header.size = sizeof(hm.strv);
-               phr->strv.header.size = resp_packet_size - sizeof(phr->h);
-
-               hpi_send_recv((struct hpi_message *)&hm.h,
-                       (struct hpi_response *)&phr->h);
-               if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
-
-                       HPI_DEBUG_ASSERT(phr->h.specific_error >
-                               MIN_STRV_PACKET_SIZE
-                               && phr->h.specific_error < 1500);
-                       resp_packet_size = phr->h.specific_error;
-               } else {
-                       remaining_attempts = 0;
-                       if (!phr->h.error)
-                               hpi_entity_alloc_and_copy(&phr->strv, info);
-               }
-
-               hpi_err = phr->h.error;
-               kfree(phr);
-       }
-
-       return hpi_err;
-}
-
-u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       struct hpi_entity **value)
-{
-       struct hpi_msg_strv hm;
-       struct hpi_res_strv *phr;
-       u16 hpi_err;
-       int remaining_attempts = 2;
-
-       *value = NULL;
-
-       while (remaining_attempts--) {
-               phr = kmalloc(strv_packet_size, GFP_KERNEL);
-               if (!phr)
-                       return HPI_ERROR_MEMORY_ALLOC;
-
-               hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h,
-                       (u16)strv_packet_size, HPI_OBJ_CONTROL,
-                       HPI_CONTROL_GET_STATE);
-               u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index);
-
-               hm.strv.header.size = sizeof(hm.strv);
-               phr->strv.header.size = strv_packet_size - sizeof(phr->h);
-
-               hpi_send_recv((struct hpi_message *)&hm.h,
-                       (struct hpi_response *)&phr->h);
-               if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
-
-                       HPI_DEBUG_ASSERT(phr->h.specific_error >
-                               MIN_STRV_PACKET_SIZE
-                               && phr->h.specific_error < 1000);
-                       strv_packet_size = phr->h.specific_error;
-               } else {
-                       remaining_attempts = 0;
-                       if (!phr->h.error)
-                               hpi_entity_alloc_and_copy(&phr->strv, value);
-               }
-
-               hpi_err = phr->h.error;
-               kfree(phr);
-       }
-
-       return hpi_err;
-}
-
-u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
-       struct hpi_entity *value)
-{
-       struct hpi_msg_strv *phm;
-       struct hpi_res_strv hr;
-
-       phm = kmalloc(sizeof(phm->h) + value->header.size, GFP_KERNEL);
-       HPI_DEBUG_ASSERT(phm != NULL);
-
-       hpi_init_message_responseV1(&phm->h,
-               sizeof(phm->h) + value->header.size, &hr.h, sizeof(hr),
-               HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
-       u32TOINDEXES(hC, &phm->h.adapter_index, &phm->h.obj_index);
-       hr.strv.header.size = sizeof(hr.strv);
-
-       memcpy(&phm->strv, value, value->header.size);
-       hpi_send_recv((struct hpi_message *)&phm->h,
-               (struct hpi_response *)&hr.h);
-
-       return hr.h.error;
-}
-
-u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
-       const size_t item_count, const enum e_entity_role role, void *value,
-       struct hpi_entity **entity)
-{
-       size_t bytes_to_copy, total_size;
-       u16 hE = 0;
-       *entity = NULL;
-
-       hE = hpi_entity_check_type(type);
-       if (hE)
-               return hE;
-
-       HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_TYPE);
-
-       bytes_to_copy = entity_type_to_size[type] * item_count;
-       total_size = hpi_entity_header_size(*entity) + bytes_to_copy;
-
-       HPI_DEBUG_ASSERT(total_size >= hpi_entity_header_size(*entity)
-               && total_size < STR_SIZE_FIELD_MAX);
-
-       *entity = kmalloc(total_size, GFP_KERNEL);
-       if (*entity == NULL)
-               return HPI_ERROR_MEMORY_ALLOC;
-       memcpy((*entity)->value, value, bytes_to_copy);
-       (*entity)->header.size =
-               hpi_entity_header_size(*entity) + bytes_to_copy;
-       (*entity)->header.type = type;
-       (*entity)->header.role = role;
-       return 0;
-}
-
-u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
-       enum e_entity_type type, size_t item_count, void *value_dst_p)
-{
-       size_t bytes_to_copy;
-
-       if (entity->header.type != type)
-               return HPI_ERROR_ENTITY_TYPE_MISMATCH;
-
-       if (hpi_entity_item_count(entity) != item_count)
-               return HPI_ERROR_ENTITY_ITEM_COUNT;
-
-       bytes_to_copy = entity_type_to_size[type] * item_count;
-       memcpy(value_dst_p, entity->value, bytes_to_copy);
-       return 0;
-}
-
-u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
-       size_t *item_count, enum e_entity_role *role, void **value)
-{
-       u16 err = 0;
-       HPI_DEBUG_ASSERT(entity != NULL);
-
-       if (type)
-               *type = entity->header.type;
-
-       if (role)
-               *role = entity->header.role;
-
-       if (value)
-               *value = entity->value;
-
-       if (item_count != NULL) {
-               if (entity->header.type == entity_type_sequence) {
-                       void *guard_p = hpi_entity_ptr_to_next(entity);
-                       struct hpi_entity *next = NULL;
-                       void *contents = entity->value;
-
-                       *item_count = 0;
-                       while (contents < guard_p) {
-                               (*item_count)++;
-                               err = hpi_entity_get_next(contents, 0,
-                                       guard_p, &next);
-                               if (next == NULL || err)
-                                       break;
-                               contents = next;
-                       }
-               } else {
-                       *item_count = hpi_entity_item_count(entity);
-               }
-       }
-       return err;
-}
-
-u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_OPEN);
-       hm.adapter_index = adapter_index;
-
-       hpi_send_recv(&hm, &hr);
-
-       if (hr.error == 0) {
-               *ph_gpio =
-                       hpi_indexes_to_handle(HPI_OBJ_GPIO, adapter_index, 0);
-               if (pw_number_input_bits)
-                       *pw_number_input_bits = hr.u.l.number_input_bits;
-               if (pw_number_output_bits)
-                       *pw_number_output_bits = hr.u.l.number_output_bits;
-       } else
-               *ph_gpio = 0;
-       return hr.error;
-}
-
-u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 bit_index, u16 *pw_bit_data)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_BIT);
-       u32TOINDEX(h_gpio, &hm.adapter_index);
-       hm.u.l.bit_index = bit_index;
-
-       hpi_send_recv(&hm, &hr);
-
-       *pw_bit_data = hr.u.l.bit_data[0];
-       return hr.error;
-}
-
-u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 aw_all_bit_data[4]
-       )
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_ALL);
-       u32TOINDEX(h_gpio, &hm.adapter_index);
-
-       hpi_send_recv(&hm, &hr);
-
-       if (aw_all_bit_data) {
-               aw_all_bit_data[0] = hr.u.l.bit_data[0];
-               aw_all_bit_data[1] = hr.u.l.bit_data[1];
-               aw_all_bit_data[2] = hr.u.l.bit_data[2];
-               aw_all_bit_data[3] = hr.u.l.bit_data[3];
-       }
-       return hr.error;
-}
-
-u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 bit_index, u16 bit_data)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_BIT);
-       u32TOINDEX(h_gpio, &hm.adapter_index);
-       hm.u.l.bit_index = bit_index;
-       hm.u.l.bit_data = bit_data;
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
-       u16 aw_all_bit_data[4]
-       )
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO,
-               HPI_GPIO_WRITE_STATUS);
-       u32TOINDEX(h_gpio, &hm.adapter_index);
-
-       hpi_send_recv(&hm, &hr);
-
-       if (aw_all_bit_data) {
-               aw_all_bit_data[0] = hr.u.l.bit_data[0];
-               aw_all_bit_data[1] = hr.u.l.bit_data[1];
-               aw_all_bit_data[2] = hr.u.l.bit_data[2];
-               aw_all_bit_data[3] = hr.u.l.bit_data[3];
-       }
-       return hr.error;
-}
-
-u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u32 *ph_async)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
-               HPI_ASYNCEVENT_OPEN);
-       hm.adapter_index = adapter_index;
-
-       hpi_send_recv(&hm, &hr);
-
-       if (hr.error == 0)
-
-               *ph_async =
-                       hpi_indexes_to_handle(HPI_OBJ_ASYNCEVENT,
-                       adapter_index, 0);
-       else
-               *ph_async = 0;
-       return hr.error;
-
-}
-
-u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
-               HPI_ASYNCEVENT_OPEN);
-       u32TOINDEX(h_async, &hm.adapter_index);
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
-       u16 maximum_events, struct hpi_async_event *p_events,
-       u16 *pw_number_returned)
-{
-
-       return 0;
-}
-
-u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
-       u32 h_async, u16 *pw_count)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
-               HPI_ASYNCEVENT_GETCOUNT);
-       u32TOINDEX(h_async, &hm.adapter_index);
-
-       hpi_send_recv(&hm, &hr);
-
-       if (hr.error == 0)
-               if (pw_count)
-                       *pw_count = hr.u.as.u.count.count;
-
-       return hr.error;
-}
-
-u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
-       u16 maximum_events, struct hpi_async_event *p_events,
-       u16 *pw_number_returned)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
-               HPI_ASYNCEVENT_GET);
-       u32TOINDEX(h_async, &hm.adapter_index);
-
-       hpi_send_recv(&hm, &hr);
-       if (!hr.error) {
-               memcpy(p_events, &hr.u.as.u.event,
-                       sizeof(struct hpi_async_event));
-               *pw_number_returned = 1;
-       }
-
-       return hr.error;
-}
-
-u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_nv_memory, u16 *pw_size_in_bytes)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
-               HPI_NVMEMORY_OPEN);
-       hm.adapter_index = adapter_index;
-
-       hpi_send_recv(&hm, &hr);
-
-       if (hr.error == 0) {
-               *ph_nv_memory =
-                       hpi_indexes_to_handle(HPI_OBJ_NVMEMORY, adapter_index,
-                       0);
-               if (pw_size_in_bytes)
-                       *pw_size_in_bytes = hr.u.n.size_in_bytes;
-       } else
-               *ph_nv_memory = 0;
-       return hr.error;
-}
-
-u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
-       u32 h_nv_memory, u16 index, u16 *pw_data)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
-               HPI_NVMEMORY_READ_BYTE);
-       u32TOINDEX(h_nv_memory, &hm.adapter_index);
-       hm.u.n.address = index;
-
-       hpi_send_recv(&hm, &hr);
-
-       *pw_data = hr.u.n.data;
-       return hr.error;
-}
-
-u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
-       u32 h_nv_memory, u16 index, u16 data)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
-               HPI_NVMEMORY_WRITE_BYTE);
-       u32TOINDEX(h_nv_memory, &hm.adapter_index);
-       hm.u.n.address = index;
-       hm.u.n.data = data;
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
-       u16 adapter_index, u16 profile_index, u32 *ph_profile,
-       u16 *pw_max_profiles)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
-               HPI_PROFILE_OPEN_ALL);
-       hm.adapter_index = adapter_index;
-       hm.obj_index = profile_index;
-       hpi_send_recv(&hm, &hr);
-
-       *pw_max_profiles = hr.u.p.u.o.max_profiles;
-       if (hr.error == 0)
-               *ph_profile =
-                       hpi_indexes_to_handle(HPI_OBJ_PROFILE, adapter_index,
-                       profile_index);
-       else
-               *ph_profile = 0;
-       return hr.error;
-}
-
-u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
-       u16 bin_index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
-       u32 *pmax_micro_seconds, u32 *pmin_micro_seconds)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET);
-       u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
-       hm.u.p.bin_index = bin_index;
-       hpi_send_recv(&hm, &hr);
-       if (pw_seconds)
-               *pw_seconds = hr.u.p.u.t.seconds;
-       if (pmicro_seconds)
-               *pmicro_seconds = hr.u.p.u.t.micro_seconds;
-       if (pcall_count)
-               *pcall_count = hr.u.p.u.t.call_count;
-       if (pmax_micro_seconds)
-               *pmax_micro_seconds = hr.u.p.u.t.max_micro_seconds;
-       if (pmin_micro_seconds)
-               *pmin_micro_seconds = hr.u.p.u.t.min_micro_seconds;
-       return hr.error;
-}
-
-u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
-       u32 h_profile, u32 *putilization)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
-               HPI_PROFILE_GET_UTILIZATION);
-       u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
-       hpi_send_recv(&hm, &hr);
-       if (hr.error) {
-               if (putilization)
-                       *putilization = 0;
-       } else {
-               if (putilization)
-                       *putilization = hr.u.p.u.t.call_count;
-       }
-       return hr.error;
-}
-
-u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
-       u16 bin_index, char *sz_name, u16 name_length)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
-               HPI_PROFILE_GET_NAME);
-       u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
-       hm.u.p.bin_index = bin_index;
-       hpi_send_recv(&hm, &hr);
-       if (hr.error) {
-               if (sz_name)
-                       strcpy(sz_name, "??");
-       } else {
-               if (sz_name)
-                       memcpy(sz_name, (char *)hr.u.p.u.n.sz_name,
-                               name_length);
-       }
-       return hr.error;
-}
-
-u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
-               HPI_PROFILE_START_ALL);
-       u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
-               HPI_PROFILE_STOP_ALL);
-       u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
-       u32 *ph_watchdog)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
-               HPI_WATCHDOG_OPEN);
-       hm.adapter_index = adapter_index;
-
-       hpi_send_recv(&hm, &hr);
-
-       if (hr.error == 0)
-               *ph_watchdog =
-                       hpi_indexes_to_handle(HPI_OBJ_WATCHDOG, adapter_index,
-                       0);
-       else
-               *ph_watchdog = 0;
-       return hr.error;
-}
-
-u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
-       u32 time_millisec)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
-               HPI_WATCHDOG_SET_TIME);
-       u32TOINDEX(h_watchdog, &hm.adapter_index);
-       hm.u.w.time_ms = time_millisec;
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
-
-u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog)
-{
-       struct hpi_message hm;
-       struct hpi_response hr;
-       hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
-               HPI_WATCHDOG_PING);
-       u32TOINDEX(h_watchdog, &hm.adapter_index);
-
-       hpi_send_recv(&hm, &hr);
-
-       return hr.error;
-}
index 8e1d099ed7e49d076c528a033084783565e39ae2..628376ce4a4989456ce9170fee2f63d41f1969a0 100644 (file)
@@ -31,21 +31,6 @@ static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
 /* Flag to enable alternate message type for SSX2 bypass. */
 static u16 gwSSX2_bypass;
 
-/** \internal
-  * Used by ASIO driver to disable SSX2 for a single process
-  * \param phSubSys Pointer to HPI subsystem handle.
-  * \param wBypass New bypass setting 0 = off, nonzero = on
-  * \return Previous bypass setting.
-  */
-u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass)
-{
-       u16 old_value = gwSSX2_bypass;
-
-       gwSSX2_bypass = bypass;
-
-       return old_value;
-}
-
 /** \internal
   * initialize the HPI message structure
   */
@@ -65,7 +50,8 @@ static void hpi_init_message(struct hpi_message *phm, u16 object,
        phm->object = object;
        phm->function = function;
        phm->version = 0;
-       /* Expect adapter index to be set by caller */
+       phm->adapter_index = HPI_ADAPTER_INDEX_INVALID;
+       /* Expect actual adapter index to be set by caller */
 }
 
 /** \internal
index 864ad020c9b383fada7eda4cf05a4529d325d69a..bfd330d78b580c2747368c990d895c898d64d3aa 100644 (file)
  (C) Copyright AudioScience Inc. 2007
 *******************************************************************************/
 /* Initialise response headers, or msg/response pairs.
-Note that it is valid to just init a response e.g. when a lower level is preparing
-a response to a message.
-However, when sending a message, a matching response buffer always must be prepared
+Note that it is valid to just init a response e.g. when a lower level is
+preparing a response to a message.
+However, when sending a message, a matching response buffer must always be
+prepared.
 */
 
+#ifndef _HPIMSGINIT_H_
+#define _HPIMSGINIT_H_
+
 void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
        u16 error);
 
@@ -38,3 +42,5 @@ void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
 void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
        struct hpi_response_header *phr, u16 res_size, u16 object,
        u16 function);
+
+#endif                         /* _HPIMSGINIT_H_ */
index f01ab964f6022ba847c04b9fe4d23426ae4d5b92..bcbdf30a6aa096071326b2e036aa48fde72f559b 100644 (file)
@@ -23,6 +23,7 @@ Extended Message Function With Response Cacheing
 #define SOURCEFILE_NAME "hpimsgx.c"
 #include "hpi_internal.h"
 #include "hpimsginit.h"
+#include "hpicmn.h"
 #include "hpimsgx.h"
 #include "hpidebug.h"
 
@@ -42,22 +43,24 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
 
        for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
                if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
-                       && asihpi_pci_tbl[i].vendor != pci_info->vendor_id)
+                       && asihpi_pci_tbl[i].vendor !=
+                       pci_info->pci_dev->vendor)
                        continue;
                if (asihpi_pci_tbl[i].device != PCI_ANY_ID
-                       && asihpi_pci_tbl[i].device != pci_info->device_id)
+                       && asihpi_pci_tbl[i].device !=
+                       pci_info->pci_dev->device)
                        continue;
                if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
                        && asihpi_pci_tbl[i].subvendor !=
-                       pci_info->subsys_vendor_id)
+                       pci_info->pci_dev->subsystem_vendor)
                        continue;
                if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
                        && asihpi_pci_tbl[i].subdevice !=
-                       pci_info->subsys_device_id)
+                       pci_info->pci_dev->subsystem_device)
                        continue;
 
-               HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i,
-                       asihpi_pci_tbl[i].driver_data);
+               /* HPI_DEBUG_LOG(DEBUG, " %x,%lx\n", i,
+                  asihpi_pci_tbl[i].driver_data); */
                return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
        }
 
@@ -67,21 +70,12 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
 static inline void hw_entry_point(struct hpi_message *phm,
        struct hpi_response *phr)
 {
-
-       hpi_handler_func *ep;
-
-       if (phm->adapter_index < HPI_MAX_ADAPTERS) {
-               ep = (hpi_handler_func *) hpi_entry_points[phm->
-                       adapter_index];
-               if (ep) {
-                       HPI_DEBUG_MESSAGE(DEBUG, phm);
-                       ep(phm, phr);
-                       HPI_DEBUG_RESPONSE(phr);
-                       return;
-               }
-       }
-       hpi_init_response(phr, phm->object, phm->function,
-               HPI_ERROR_PROCESSING_MESSAGE);
+       if ((phm->adapter_index < HPI_MAX_ADAPTERS)
+               && hpi_entry_points[phm->adapter_index])
+               hpi_entry_points[phm->adapter_index] (phm, phr);
+       else
+               hpi_init_response(phr, phm->object, phm->function,
+                       HPI_ERROR_PROCESSING_MESSAGE);
 }
 
 static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
@@ -100,6 +94,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
        void *h_owner);
 
 static void HPIMSGX__reset(u16 adapter_index);
+
 static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
 static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
 
@@ -153,8 +148,6 @@ static struct hpi_stream_response
 
 static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
 
-static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS;
-
 static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
 
 /* use these to keep track of opens from user mode apps/DLLs */
@@ -167,6 +160,11 @@ static struct asi_open_state
 static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
        void *h_owner)
 {
+       if (phm->adapter_index != HPI_ADAPTER_INDEX_INVALID)
+               HPI_DEBUG_LOG(WARNING,
+                       "suspicious adapter index %d in subsys message 0x%x.\n",
+                       phm->adapter_index, phm->function);
+
        switch (phm->function) {
        case HPI_SUBSYS_GET_VERSION:
                hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
@@ -204,85 +202,37 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
                        HPI_SUBSYS_DRIVER_UNLOAD, 0);
                return;
 
-       case HPI_SUBSYS_GET_INFO:
-               HPI_COMMON(phm, phr);
-               break;
-
-       case HPI_SUBSYS_FIND_ADAPTERS:
-               memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
-                       sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
-               break;
        case HPI_SUBSYS_GET_NUM_ADAPTERS:
-               memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
-                       sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
-               phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS;
-               break;
        case HPI_SUBSYS_GET_ADAPTER:
-               {
-                       int count = phm->adapter_index;
-                       int index = 0;
-                       hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
-                               HPI_SUBSYS_GET_ADAPTER, 0);
-
-                       /* This is complicated by the fact that we want to
-                        * "skip" 0's in the adapter list.
-                        * First, make sure we are pointing to a
-                        * non-zero adapter type.
-                        */
-                       while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
-                               s.aw_adapter_list[index] == 0) {
-                               index++;
-                               if (index >= HPI_MAX_ADAPTERS)
-                                       break;
-                       }
-                       while (count) {
-                               /* move on to the next adapter */
-                               index++;
-                               if (index >= HPI_MAX_ADAPTERS)
-                                       break;
-                               while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
-                                       s.aw_adapter_list[index] == 0) {
-                                       index++;
-                                       if (index >= HPI_MAX_ADAPTERS)
-                                               break;
-                               }
-                               count--;
-                       }
+               HPI_COMMON(phm, phr);
+               break;
 
-                       if (index < HPI_MAX_ADAPTERS) {
-                               phr->u.s.adapter_index = (u16)index;
-                               phr->u.s.aw_adapter_list[0] =
-                                       gRESP_HPI_SUBSYS_FIND_ADAPTERS.
-                                       s.aw_adapter_list[index];
-                       } else {
-                               phr->u.s.adapter_index = 0;
-                               phr->u.s.aw_adapter_list[0] = 0;
-                               phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
-                       }
-                       break;
-               }
        case HPI_SUBSYS_CREATE_ADAPTER:
                HPIMSGX__init(phm, phr);
                break;
+
        case HPI_SUBSYS_DELETE_ADAPTER:
-               HPIMSGX__cleanup(phm->adapter_index, h_owner);
+               HPIMSGX__cleanup(phm->obj_index, h_owner);
                {
                        struct hpi_message hm;
                        struct hpi_response hr;
-                       /* call to HPI_ADAPTER_CLOSE */
                        hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
                                HPI_ADAPTER_CLOSE);
-                       hm.adapter_index = phm->adapter_index;
+                       hm.adapter_index = phm->obj_index;
                        hw_entry_point(&hm, &hr);
                }
-               hw_entry_point(phm, phr);
-               gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.
-                       aw_adapter_list[phm->adapter_index]
-                       = 0;
-               hpi_entry_points[phm->adapter_index] = NULL;
+               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:
-               hw_entry_point(phm, phr);
+               /* Must explicitly handle every subsys message in this switch */
+               hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
+                       HPI_ERROR_INVALID_FUNC);
                break;
        }
 }
@@ -409,33 +359,7 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
                break;
        }
        HPI_DEBUG_RESPONSE(phr);
-#if 1
-       if (phr->error >= HPI_ERROR_BACKEND_BASE) {
-               void *ep = NULL;
-               char *ep_name;
-
-               HPI_DEBUG_MESSAGE(ERROR, phm);
-
-               if (phm->adapter_index < HPI_MAX_ADAPTERS)
-                       ep = hpi_entry_points[phm->adapter_index];
-
-               /* Don't need this? Have adapter index in debug info
-                  Know at driver load time index->backend mapping */
-               if (ep == HPI_6000)
-                       ep_name = "HPI_6000";
-               else if (ep == HPI_6205)
-                       ep_name = "HPI_6205";
-               else
-                       ep_name = "unknown";
-
-               HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name,
-                       phr->error);
-
-               if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)
-                       hpi_debug_data((u16 *)phm,
-                               sizeof(*phm) / sizeof(u16));
-       }
-#endif
+
 }
 
 static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
@@ -484,7 +408,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
        else {
                instream_user_open[phm->adapter_index][phm->
                        obj_index].open_flag = 1;
-               hpios_msgxlock_un_lock(&msgx_lock);
+               hpios_msgxlock_unlock(&msgx_lock);
 
                /* issue a reset */
                hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
@@ -509,7 +433,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
                                sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
                }
        }
-       hpios_msgxlock_un_lock(&msgx_lock);
+       hpios_msgxlock_unlock(&msgx_lock);
 }
 
 static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
@@ -530,7 +454,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
                   phm->wAdapterIndex, phm->wObjIndex, hOwner); */
                instream_user_open[phm->adapter_index][phm->
                        obj_index].h_owner = NULL;
-               hpios_msgxlock_un_lock(&msgx_lock);
+               hpios_msgxlock_unlock(&msgx_lock);
                /* issue a reset */
                hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
                        HPI_ISTREAM_RESET);
@@ -556,7 +480,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
                                obj_index].h_owner);
                phr->error = HPI_ERROR_OBJ_NOT_OPEN;
        }
-       hpios_msgxlock_un_lock(&msgx_lock);
+       hpios_msgxlock_unlock(&msgx_lock);
 }
 
 static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
@@ -581,7 +505,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
        else {
                outstream_user_open[phm->adapter_index][phm->
                        obj_index].open_flag = 1;
-               hpios_msgxlock_un_lock(&msgx_lock);
+               hpios_msgxlock_unlock(&msgx_lock);
 
                /* issue a reset */
                hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
@@ -606,7 +530,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
                                sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
                }
        }
-       hpios_msgxlock_un_lock(&msgx_lock);
+       hpios_msgxlock_unlock(&msgx_lock);
 }
 
 static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
@@ -628,7 +552,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
                   phm->wAdapterIndex, phm->wObjIndex, hOwner); */
                outstream_user_open[phm->adapter_index][phm->
                        obj_index].h_owner = NULL;
-               hpios_msgxlock_un_lock(&msgx_lock);
+               hpios_msgxlock_unlock(&msgx_lock);
                /* issue a reset */
                hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
                        HPI_OSTREAM_RESET);
@@ -654,7 +578,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
                                obj_index].h_owner);
                phr->error = HPI_ERROR_OBJ_NOT_OPEN;
        }
-       hpios_msgxlock_un_lock(&msgx_lock);
+       hpios_msgxlock_unlock(&msgx_lock);
 }
 
 static u16 adapter_prepare(u16 adapter)
@@ -683,16 +607,9 @@ static u16 adapter_prepare(u16 adapter)
        if (hr.error)
                return hr.error;
 
-       aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams;
-       aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams;
-       aDAPTER_INFO[adapter].type = hr.u.a.adapter_type;
-
-       gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] =
-               hr.u.a.adapter_type;
-       gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++;
-       if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS)
-               gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters =
-                       HPI_MAX_ADAPTERS;
+       aDAPTER_INFO[adapter].num_outstreams = hr.u.ax.info.num_outstreams;
+       aDAPTER_INFO[adapter].num_instreams = hr.u.ax.info.num_instreams;
+       aDAPTER_INFO[adapter].type = hr.u.ax.info.adapter_type;
 
        /* call to HPI_OSTREAM_OPEN */
        for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
@@ -727,7 +644,7 @@ static u16 adapter_prepare(u16 adapter)
        memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
                sizeof(rESP_HPI_MIXER_OPEN[0]));
 
-       return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error;
+       return 0;
 }
 
 static void HPIMSGX__reset(u16 adapter_index)
@@ -737,12 +654,6 @@ static void HPIMSGX__reset(u16 adapter_index)
        struct hpi_response hr;
 
        if (adapter_index == HPIMSGX_ALLADAPTERS) {
-               /* reset all responses to contain errors */
-               hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
-                       HPI_SUBSYS_FIND_ADAPTERS, 0);
-               memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
-                       sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
-
                for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
 
                        hpi_init_response(&hr, HPI_OBJ_ADAPTER,
@@ -783,12 +694,6 @@ static void HPIMSGX__reset(u16 adapter_index)
                        rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
                                HPI_ERROR_INVALID_OBJ;
                }
-               if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
-                       s.aw_adapter_list[adapter_index]) {
-                       gRESP_HPI_SUBSYS_FIND_ADAPTERS.
-                               s.aw_adapter_list[adapter_index] = 0;
-                       gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--;
-               }
        }
 }
 
@@ -802,15 +707,9 @@ static u16 HPIMSGX__init(struct hpi_message *phm,
        hpi_handler_func *entry_point_func;
        struct hpi_response hr;
 
-       if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS)
-               return HPI_ERROR_BAD_ADAPTER_NUMBER;
-
        /* Init response here so we can pass in previous adapter list */
        hpi_init_response(&hr, phm->object, phm->function,
                HPI_ERROR_INVALID_OBJ);
-       memcpy(hr.u.s.aw_adapter_list,
-               gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list,
-               sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list));
 
        entry_point_func =
                hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
@@ -860,7 +759,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
                                struct hpi_response hr;
 
                                HPI_DEBUG_LOG(DEBUG,
-                                       "close adapter %d ostream %d\n",
+                                       "Close adapter %d ostream %d\n",
                                        adapter, i);
 
                                hpi_init_message_response(&hm, &hr,
@@ -884,7 +783,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
                                struct hpi_response hr;
 
                                HPI_DEBUG_LOG(DEBUG,
-                                       "close adapter %d istream %d\n",
+                                       "Close adapter %d istream %d\n",
                                        adapter, i);
 
                                hpi_init_message_response(&hm, &hr,
index 22dbd91811a4e61398a9b9077c5cbe26c0139119..cd624f13ff8e4755ca7d094bf0b9c6e2d212cc27 100644 (file)
@@ -30,6 +30,7 @@ Common Linux HPI ioctl and module probe/remove functions
 #include <linux/slab.h>
 #include <linux/moduleparam.h>
 #include <asm/uaccess.h>
+#include <linux/pci.h>
 #include <linux/stringify.h>
 
 #ifdef MODULE_FIRMWARE
@@ -45,7 +46,7 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin");
 static int prealloc_stream_buf;
 module_param(prealloc_stream_buf, int, S_IRUGO);
 MODULE_PARM_DESC(prealloc_stream_buf,
-       "preallocate size for per-adapter stream buffer");
+       "Preallocate size for per-adapter stream buffer");
 
 /* Allow the debug level to be changed after module load.
  E.g.   echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
@@ -121,8 +122,8 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
 
        /* Read the message and response pointers from user space.  */
-       if (get_user(puhm, &phpi_ioctl_data->phm) ||
-           get_user(puhr, &phpi_ioctl_data->phr)) {
+       if (get_user(puhm, &phpi_ioctl_data->phm)
+               || get_user(puhr, &phpi_ioctl_data->phr)) {
                err = -EFAULT;
                goto out;
        }
@@ -135,7 +136,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        if (hm->h.size > sizeof(*hm))
                hm->h.size = sizeof(*hm);
 
-       /*printk(KERN_INFO "message size %d\n", hm->h.wSize); */
+       /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */
 
        uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
        if (uncopied_bytes) {
@@ -155,8 +156,13 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                goto out;
        }
 
+       if (hm->h.adapter_index >= HPI_MAX_ADAPTERS) {
+               err = -EINVAL;
+               goto out;
+       }
+
        pa = &adapters[hm->h.adapter_index];
-       hr->h.size = 0;
+       hr->h.size = res_max_size;
        if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
                switch (hm->h.function) {
                case HPI_SUBSYS_CREATE_ADAPTER:
@@ -216,7 +222,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                 */
                                if (pa->buffer_size < size) {
                                        HPI_DEBUG_LOG(DEBUG,
-                                               "realloc adapter %d stream "
+                                               "Realloc adapter %d stream "
                                                "buffer from %zd to %d\n",
                                                hm->h.adapter_index,
                                                pa->buffer_size, size);
@@ -259,7 +265,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                copy_from_user(pa->p_buffer, ptr, size);
                        if (uncopied_bytes)
                                HPI_DEBUG_LOG(WARNING,
-                                       "missed %d of %d "
+                                       "Missed %d of %d "
                                        "bytes from user\n", uncopied_bytes,
                                        size);
                }
@@ -271,7 +277,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                copy_to_user(ptr, pa->p_buffer, size);
                        if (uncopied_bytes)
                                HPI_DEBUG_LOG(WARNING,
-                                       "missed %d of %d " "bytes to user\n",
+                                       "Missed %d of %d " "bytes to user\n",
                                        uncopied_bytes, size);
                }
 
@@ -290,9 +296,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        if (hr->h.size > res_max_size) {
                HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
                        res_max_size);
-               /*HPI_DEBUG_MESSAGE(ERROR, hm); */
-               err = -EFAULT;
-               goto out;
+               hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
+               hr->h.specific_error = hr->h.size;
+               hr->h.size = sizeof(hr->h);
        }
 
        uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
@@ -320,18 +326,26 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
 
        memset(&adapter, 0, sizeof(adapter));
 
-       printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n",
-               pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor,
+       dev_printk(KERN_DEBUG, &pci_dev->dev,
+               "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor,
+               pci_dev->device, pci_dev->subsystem_vendor,
                pci_dev->subsystem_device, pci_dev->devfn);
 
+       if (pci_enable_device(pci_dev) < 0) {
+               dev_printk(KERN_ERR, &pci_dev->dev,
+                       "pci_enable_device failed, disabling device\n");
+               return -EIO;
+       }
+
+       pci_set_master(pci_dev);        /* also sets latency timer if < 16 */
+
        hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
                HPI_SUBSYS_CREATE_ADAPTER);
        hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
                HPI_ERROR_PROCESSING_MESSAGE);
 
-       hm.adapter_index = -1;  /* an invalid index */
+       hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
 
-       /* fill in HPI_PCI information from kernel provided information */
        adapter.pci = pci_dev;
 
        nm = HPI_MAX_ADAPTER_MEM_SPACES;
@@ -359,19 +373,7 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
                pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx];
        }
 
-       /* could replace Pci with direct pointer to pci_dev for linux
-          Instead wrap accessor functions for IDs etc.
-          Would it work for windows?
-        */
-       pci.bus_number = pci_dev->bus->number;
-       pci.vendor_id = (u16)pci_dev->vendor;
-       pci.device_id = (u16)pci_dev->device;
-       pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff);
-       pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff);
-       pci.device_number = pci_dev->devfn;
-       pci.interrupt = pci_dev->irq;
-       pci.p_os_data = pci_dev;
-
+       pci.pci_dev = pci_dev;
        hm.u.s.resource.bus_type = HPI_BUS_PCI;
        hm.u.s.resource.r.pci = &pci;
 
@@ -392,10 +394,10 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
        }
 
        adapter.index = hr.u.s.adapter_index;
-       adapter.type = hr.u.s.aw_adapter_list[adapter.index];
+       adapter.type = hr.u.s.adapter_type;
        hm.adapter_index = adapter.index;
 
-       err = hpi_adapter_open(NULL, adapter.index);
+       err = hpi_adapter_open(adapter.index);
        if (err)
                goto err;
 
@@ -407,8 +409,9 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
        mutex_init(&adapters[adapter.index].mutex);
        pci_set_drvdata(pci_dev, &adapters[adapter.index]);
 
-       printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n",
-               adapter.type, adapter.index);
+       dev_printk(KERN_INFO, &pci_dev->dev,
+               "probe succeeded for ASI%04X HPI index %d\n", adapter.type,
+               adapter.index);
 
        return 0;
 
@@ -439,7 +442,8 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
 
        hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
                HPI_SUBSYS_DELETE_ADAPTER);
-       hm.adapter_index = pa->index;
+       hm.obj_index = pa->index;
+       hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
        hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
 
        /* unmap PCI memory space, mapped during device init. */
@@ -450,20 +454,18 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
                }
        }
 
-       if (pa->p_buffer) {
-               pa->buffer_size = 0;
+       if (pa->p_buffer)
                vfree(pa->p_buffer);
-       }
 
        pci_set_drvdata(pci_dev, NULL);
-       /*
-          printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x),"
-          " HPI index # %d, removed.\n",
-          pci_dev->vendor, pci_dev->device,
-          pci_dev->subsystem_vendor,
-          pci_dev->subsystem_device, pci_dev->devfn,
-          pa->index);
-        */
+       if (1)
+               dev_printk(KERN_INFO, &pci_dev->dev,
+                       "remove %04x:%04x,%04x:%04x,%04x," " HPI index %d.\n",
+                       pci_dev->vendor, pci_dev->device,
+                       pci_dev->subsystem_vendor, pci_dev->subsystem_device,
+                       pci_dev->devfn, pa->index);
+
+       memset(pa, 0, sizeof(*pa));
 }
 
 void __init asihpi_init(void)
index 370f39b43f853fb1ad9a50c117e391965df8007f..03273e729f99c714fb9c78eb50e56b1097dd3774 100644 (file)
@@ -27,9 +27,7 @@ HPI Operating System Specific macros for Linux Kernel driver
 #define HPI_OS_LINUX_KERNEL
 
 #define HPI_OS_DEFINED
-#define HPI_KERNEL_MODE
-
-#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX
+#define HPI_BUILD_KERNEL_MODE
 
 #include <linux/io.h>
 #include <asm/system.h>
@@ -135,20 +133,20 @@ static inline void cond_unlock(struct hpios_spinlock *l)
 
 #define hpios_msgxlock_init(obj)      spin_lock_init(&(obj)->lock)
 #define hpios_msgxlock_lock(obj)   cond_lock(obj)
-#define hpios_msgxlock_un_lock(obj) cond_unlock(obj)
+#define hpios_msgxlock_unlock(obj) cond_unlock(obj)
 
 #define hpios_dsplock_init(obj)       spin_lock_init(&(obj)->dsp_lock.lock)
 #define hpios_dsplock_lock(obj)    cond_lock(&(obj)->dsp_lock)
 #define hpios_dsplock_unlock(obj)  cond_unlock(&(obj)->dsp_lock)
 
 #ifdef CONFIG_SND_DEBUG
-#define HPI_DEBUG
+#define HPI_BUILD_DEBUG
 #endif
 
 #define HPI_ALIST_LOCKING
 #define hpios_alistlock_init(obj)    spin_lock_init(&((obj)->list_lock.lock))
 #define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
-#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock))
+#define hpios_alistlock_unlock(obj) spin_unlock(&((obj)->list_lock.lock))
 
 struct hpi_adapter {
        /* mutex prevents contention for one card
index 49d572a7b235cd21dfc8a3f5d8069b728a9ec2dc..3119cd97a217e748a48cce8b211a964354de3fed 100644 (file)
@@ -522,7 +522,7 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
                atiixp_read(chip, CMD);
                mdelay(1);
                atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
-               if (--timeout) {
+               if (!--timeout) {
                        snd_printk(KERN_ERR "atiixp: codec reset timeout\n");
                        break;
                }
index 91d7036b64118572a7c4ffa3ed20724e1ede6c00..2f74c2fdf1ea573fd2a596ed7f1005980942c3cb 100644 (file)
@@ -498,7 +498,7 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
                atiixp_read(chip, CMD);
                msleep(1);
                atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
-               if (--timeout) {
+               if (!--timeout) {
                        snd_printk(KERN_ERR "atiixp-modem: codec reset timeout\n");
                        break;
                }
index 38602b85874d5716c4e4bd7693ee394b2fc48101..278ed8189fca9123d7360180fdb9e4b475f4b197 100644 (file)
@@ -896,7 +896,8 @@ static int __devinit vortex_eq_init(vortex_t * vortex)
                if ((kcontrol =
                     snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL)
                        return -ENOMEM;
-               strcpy(kcontrol->id.name, EqBandLabels[i]);
+               snprintf(kcontrol->id.name, sizeof(kcontrol->id.name),
+                       "%s Playback Volume", EqBandLabels[i]);
                kcontrol->private_value = i;
                if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
                        return err;
index 573594bf3225810366186751b70b5ccf9b962a02..5715c4d055735a4bed90f5a53cb75b582db30c95 100644 (file)
@@ -1,6 +1,5 @@
-/*
- *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- *  Copyright (C) 2002, 2005 - 2010 by Andreas Mohr <andi AT lisas.de>
+/*  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
+ *  Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de>
  *
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
  *    addresses illegally. So far unfortunately it looks like the very flexible
  *    ALSA AC97 support is still not enough to easily compensate for such a
  *    grave layout violation despite all tweaks and quirks mechanisms it offers.
+ *    Well, not quite: now ac97 layer is much improved (bus-specific ops!),
+ *    thus I was able to implement support - it's actually working quite well.
+ *    An interesting item might be Aztech AMR 2800-W, since it's an AC97
+ *    modem card which might reveal the Aztech-specific codec ID which
+ *    we might want to pretend, too. Dito PCI168's brother, PCI368,
+ *    where the advertising datasheet says it's AC97-based and has a
+ *    Digital Enhanced Game Port.
  *  - builtin genuine OPL3 - verified to work fine, 20080506
  *  - full duplex 16bit playback/record at independent sampling rate
  *  - MPU401 (+ legacy address support, claimed by one official spec sheet)
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
 #include <sound/initval.h>
+/*
+ * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
+ * If the AC97 compatibility parts we needed to implement locally turn out
+ * to work nicely, then remove the old implementation eventually.
+ */
+#define AZF_USE_AC97_LAYER 1
+
+#ifdef AZF_USE_AC97_LAYER
+#include <sound/ac97_codec.h>
+#endif
 #include "azt3328.h"
 
 MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
@@ -328,6 +344,10 @@ struct snd_azf3328 {
        /* playback, recording and I2S out codecs */
        struct snd_azf3328_codec_data codecs[3];
 
+#ifdef AZF_USE_AC97_LAYER
+       struct snd_ac97 *ac97;
+#endif
+
        struct snd_card *card;
        struct snd_rawmidi *rmidi;
 
@@ -506,7 +526,7 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
 #define AZF_MUTE_BIT 0x80
 
 static bool
-snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
+snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
                           unsigned reg, bool do_mute
 )
 {
@@ -521,6 +541,323 @@ snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
        return (do_mute) ? !updated : updated;
 }
 
+static inline bool
+snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
+                          bool do_mute
+)
+{
+       return snd_azf3328_mixer_mute_control(
+               chip,
+               IDX_MIXER_PLAY_MASTER,
+               do_mute
+       );
+}
+
+static inline bool
+snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
+                          bool do_mute
+)
+{
+       return snd_azf3328_mixer_mute_control(
+               chip,
+               IDX_MIXER_WAVEOUT,
+               do_mute
+       );
+}
+
+static inline void
+snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
+{
+       /* reset (close) mixer:
+        * first mute master volume, then reset
+        */
+       snd_azf3328_mixer_mute_control_master(chip, 1);
+       snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+}
+
+#ifdef AZF_USE_AC97_LAYER
+
+static inline void
+snd_azf3328_mixer_ac97_map_unsupported(unsigned short reg, const char *mode)
+{
+       /* need to add some more or less clever emulation? */
+       printk(KERN_WARNING
+               "azt3328: missing %s emulation for AC97 register 0x%02x!\n",
+               mode, reg);
+}
+
+/*
+ * Need to have _special_ AC97 mixer hardware register index mapper,
+ * to compensate for the issue of a rather AC97-incompatible hardware layout.
+ */
+#define AZF_REG_MASK 0x3f
+#define AZF_AC97_REG_UNSUPPORTED 0x8000
+#define AZF_AC97_REG_REAL_IO_READ 0x4000
+#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
+#define AZF_AC97_REG_REAL_IO_RW \
+       (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
+#define AZF_AC97_REG_EMU_IO_READ 0x0400
+#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
+#define AZF_AC97_REG_EMU_IO_RW \
+       (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
+static unsigned short
+snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
+{
+       static const struct {
+               unsigned short azf_reg;
+       } azf_reg_mapper[] = {
+               /* Especially when taking into consideration
+                * mono/stereo-based sequence of azf vs. AC97 control series,
+                * it's quite obvious that azf simply got rid
+                * of the AC97_HEADPHONE control at its intended offset,
+                * thus shifted _all_ controls by one,
+                * and _then_ simply added it as an FMSYNTH control at the end,
+                * to make up for the offset.
+                * This means we'll have to translate indices here as
+                * needed and then do some tiny AC97 patch action
+                * (snd_ac97_rename_vol_ctl() etc.) - that's it.
+                */
+               { /* AC97_RESET */ IDX_MIXER_RESET
+                       | AZF_AC97_REG_REAL_IO_WRITE
+                       | AZF_AC97_REG_EMU_IO_READ },
+               { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
+                /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
+               { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
+               { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
+               { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
+               { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
+               { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
+               { /* AC97_MIC */ IDX_MIXER_MIC },
+               { /* AC97_LINE */ IDX_MIXER_LINEIN },
+               { /* AC97_CD */ IDX_MIXER_CDAUDIO },
+               { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
+               { /* AC97_AUX */ IDX_MIXER_AUX },
+               { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
+               { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
+               { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
+               { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
+               { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
+               { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
+       };
+
+       unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
+
+       /* azf3328 supports the low-numbered and low-spec:ed range
+          of AC97 regs only */
+       if (reg <= AC97_3D_CONTROL) {
+               unsigned short reg_idx = reg / 2;
+               reg_azf = azf_reg_mapper[reg_idx].azf_reg;
+               /* a translation-only entry means it's real read/write: */
+               if (!(reg_azf & ~AZF_REG_MASK))
+                       reg_azf |= AZF_AC97_REG_REAL_IO_RW;
+       } else {
+               switch (reg) {
+               case AC97_POWERDOWN:
+                       reg_azf = AZF_AC97_REG_EMU_IO_RW;
+                       break;
+               case AC97_EXTENDED_ID:
+                       reg_azf = AZF_AC97_REG_EMU_IO_READ;
+                       break;
+               case AC97_EXTENDED_STATUS:
+                       /* I don't know what the h*ll AC97 layer
+                        * would consult this _extended_ register for
+                        * given a base-AC97-advertised card,
+                        * but let's just emulate it anyway :-P
+                        */
+                       reg_azf = AZF_AC97_REG_EMU_IO_RW;
+                       break;
+               case AC97_VENDOR_ID1:
+               case AC97_VENDOR_ID2:
+                       reg_azf = AZF_AC97_REG_EMU_IO_READ;
+                       break;
+               }
+       }
+       return reg_azf;
+}
+
+static const unsigned short
+azf_emulated_ac97_caps =
+       AC97_BC_DEDICATED_MIC |
+       AC97_BC_BASS_TREBLE |
+       /* Headphone is an FM Synth control here */
+       AC97_BC_HEADPHONE |
+       /* no AC97_BC_LOUDNESS! */
+       /* mask 0x7c00 is
+          vendor-specific 3D enhancement
+          vendor indicator.
+          Since there actually _is_ an
+          entry for Aztech Labs
+          (13), make damn sure
+          to indicate it. */
+       (13 << 10);
+
+static const unsigned short
+azf_emulated_ac97_powerdown =
+       /* pretend everything to be active */
+               AC97_PD_ADC_STATUS |
+               AC97_PD_DAC_STATUS |
+               AC97_PD_MIXER_STATUS |
+               AC97_PD_VREF_STATUS;
+
+/*
+ * Emulated, _inofficial_ vendor ID
+ * (there might be some devices such as the MR 2800-W
+ * which could reveal the real Aztech AC97 ID).
+ * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
+ * (better don't use 0x68 since there's a PCI368 as well).
+ */
+static const unsigned int
+azf_emulated_ac97_vendor_id = 0x415a5401;
+
+static unsigned short
+snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
+{
+       const struct snd_azf3328 *chip = ac97->private_data;
+       unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
+       unsigned short reg_val = 0;
+       bool unsupported = 0;
+
+       snd_azf3328_dbgmixer(
+               "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
+                       reg_ac97
+       );
+       if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
+               unsupported = 1;
+       else {
+               if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
+                       reg_val = snd_azf3328_mixer_inw(chip,
+                                               reg_azf & AZF_REG_MASK);
+               else {
+                       /*
+                        * Proceed with dummy I/O read,
+                        * to ensure compatible timing where this may matter.
+                        * (ALSA AC97 layer usually doesn't call I/O functions
+                        * due to intelligent I/O caching anyway)
+                        * Choose a mixer register that's thoroughly unrelated
+                        * to common audio (try to minimize distortion).
+                        */
+                       snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
+               }
+
+               if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
+                       switch (reg_ac97) {
+                       case AC97_RESET:
+                               reg_val |= azf_emulated_ac97_caps;
+                               break;
+                       case AC97_POWERDOWN:
+                               reg_val |= azf_emulated_ac97_powerdown;
+                               break;
+                       case AC97_EXTENDED_ID:
+                       case AC97_EXTENDED_STATUS:
+                               /* AFAICS we simply can't support anything: */
+                               reg_val |= 0;
+                               break;
+                       case AC97_VENDOR_ID1:
+                               reg_val = azf_emulated_ac97_vendor_id >> 16;
+                               break;
+                       case AC97_VENDOR_ID2:
+                               reg_val = azf_emulated_ac97_vendor_id & 0xffff;
+                               break;
+                       default:
+                               unsupported = 1;
+                               break;
+                       }
+               }
+       }
+       if (unsupported)
+               snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "read");
+
+       return reg_val;
+}
+
+static void
+snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
+                    unsigned short reg_ac97, unsigned short val)
+{
+       const struct snd_azf3328 *chip = ac97->private_data;
+       unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
+       bool unsupported = 0;
+
+       snd_azf3328_dbgmixer(
+               "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
+                       reg_ac97, val
+       );
+       if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
+               unsupported = 1;
+       else {
+               if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
+                       snd_azf3328_mixer_outw(
+                               chip,
+                               reg_azf & AZF_REG_MASK,
+                               val
+                       );
+               else
+               if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
+                       switch (reg_ac97) {
+                       case AC97_REC_GAIN_MIC:
+                       case AC97_POWERDOWN:
+                       case AC97_EXTENDED_STATUS:
+                               /*
+                                * Silently swallow these writes.
+                                * Since for most registers our card doesn't
+                                * actually support a comparable feature,
+                                * this is exactly what we should do here.
+                                * The AC97 layer's I/O caching probably
+                                * automatically takes care of all the rest...
+                                * (remembers written values etc.)
+                                */
+                               break;
+                       default:
+                               unsupported = 1;
+                               break;
+                       }
+               }
+       }
+       if (unsupported)
+               snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "write");
+}
+
+static int __devinit
+snd_azf3328_mixer_new(struct snd_azf3328 *chip)
+{
+       struct snd_ac97_bus *bus;
+       struct snd_ac97_template ac97;
+       static struct snd_ac97_bus_ops ops = {
+               .write = snd_azf3328_mixer_ac97_write,
+               .read = snd_azf3328_mixer_ac97_read,
+       };
+       int rc;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.scaps = AC97_SCAP_SKIP_MODEM
+                       | AC97_SCAP_AUDIO /* we support audio! */
+                       | AC97_SCAP_NO_SPDIF;
+       ac97.private_data = chip;
+       ac97.pci = chip->pci;
+
+       /*
+        * ALSA's AC97 layer has terrible init crackling issues,
+        * unfortunately, and since it makes use of AC97_RESET,
+        * there's no use trying to mute Master Playback proactively.
+        */
+
+       rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+       if (!rc)
+               rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
+               /*
+                * Make sure to complain loudly in case of AC97 init failure,
+                * since failure may happen quite often,
+                * due to this card being a very quirky AC97 "lookalike".
+                */
+       if (rc)
+               printk(KERN_ERR "azt3328: AC97 init failed, err %d!\n", rc);
+
+       /* If we return an error here, then snd_card_free() should
+        * free up any ac97 codecs that got created, as well as the bus.
+        */
+       return rc;
+}
+#else /* AZF_USE_AC97_LAYER */
 static void
 snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
                                         unsigned reg,
@@ -945,6 +1282,7 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
        snd_azf3328_dbgcallleave();
        return 0;
 }
+#endif /* AZF_USE_AC97_LAYER */
 
 static int
 snd_azf3328_hw_params(struct snd_pcm_substream *substream,
@@ -1233,8 +1571,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                if (is_main_mixer_playback_codec) {
                        /* mute WaveOut (avoid clicking during setup) */
                        previously_muted =
-                               snd_azf3328_mixer_set_mute(
-                                               chip, IDX_MIXER_WAVEOUT, 1
+                               snd_azf3328_mixer_mute_control_pcm(
+                                               chip, 1
                                );
                }
 
@@ -1290,8 +1628,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                if (is_main_mixer_playback_codec) {
                        /* now unmute WaveOut */
                        if (!previously_muted)
-                               snd_azf3328_mixer_set_mute(
-                                               chip, IDX_MIXER_WAVEOUT, 0
+                               snd_azf3328_mixer_mute_control_pcm(
+                                               chip, 0
                                );
                }
 
@@ -1315,8 +1653,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                if (is_main_mixer_playback_codec) {
                        /* mute WaveOut (avoid clicking during setup) */
                        previously_muted =
-                               snd_azf3328_mixer_set_mute(
-                                               chip, IDX_MIXER_WAVEOUT, 1
+                               snd_azf3328_mixer_mute_control_pcm(
+                                               chip, 1
                                );
                }
 
@@ -1341,8 +1679,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                if (is_main_mixer_playback_codec) {
                        /* now unmute WaveOut */
                        if (!previously_muted)
-                               snd_azf3328_mixer_set_mute(
-                                               chip, IDX_MIXER_WAVEOUT, 0
+                               snd_azf3328_mixer_mute_control_pcm(
+                                               chip, 0
                                );
                }
 
@@ -2050,11 +2388,7 @@ snd_azf3328_free(struct snd_azf3328 *chip)
        if (chip->irq < 0)
                goto __end_hw;
 
-       /* reset (close) mixer:
-        * first mute master volume, then reset
-        */
-       snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
-       snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+       snd_azf3328_mixer_reset(chip);
 
        snd_azf3328_timer_stop(chip->timer);
        snd_azf3328_gameport_free(chip);
@@ -2407,6 +2741,55 @@ snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
        }
 }
 
+static inline void
+snd_azf3328_resume_regs(const u32 *saved_regs,
+                       unsigned long io_addr,
+                       unsigned count
+)
+{
+       unsigned reg;
+
+       for (reg = 0; reg < count; ++reg) {
+               outl(*saved_regs, io_addr);
+               snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
+                       io_addr, *saved_regs, inl(io_addr));
+               ++saved_regs;
+               io_addr += sizeof(*saved_regs);
+       }
+}
+
+static inline void
+snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
+{
+#ifdef AZF_USE_AC97_LAYER
+       snd_ac97_suspend(chip->ac97);
+#else
+       snd_azf3328_suspend_regs(chip->mixer_io,
+               ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
+
+       /* make sure to disable master volume etc. to prevent looping sound */
+       snd_azf3328_mixer_mute_control_master(chip, 1);
+       snd_azf3328_mixer_mute_control_pcm(chip, 1);
+#endif /* AZF_USE_AC97_LAYER */
+}
+
+static inline void
+snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
+{
+#ifdef AZF_USE_AC97_LAYER
+       snd_ac97_resume(chip->ac97);
+#else
+       snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
+                                       ARRAY_SIZE(chip->saved_regs_mixer));
+
+       /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
+          and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
+          resulting in a mixer reset condition persisting until _after_
+          master vol was restored. Thus master vol needs an extra restore. */
+       outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+#endif /* AZF_USE_AC97_LAYER */
+}
+
 static int
 snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
 {
@@ -2420,12 +2803,7 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
        snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
        snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
 
-       snd_azf3328_suspend_regs(chip->mixer_io,
-               ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
-
-       /* make sure to disable master volume etc. to prevent looping sound */
-       snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
-       snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+       snd_azf3328_suspend_ac97(chip);
 
        snd_azf3328_suspend_regs(chip->ctrl_io,
                ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
@@ -2447,23 +2825,6 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
        return 0;
 }
 
-static inline void
-snd_azf3328_resume_regs(const u32 *saved_regs,
-                       unsigned long io_addr,
-                       unsigned count
-)
-{
-       unsigned reg;
-
-       for (reg = 0; reg < count; ++reg) {
-               outl(*saved_regs, io_addr);
-               snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
-                       io_addr, *saved_regs, inl(io_addr));
-               ++saved_regs;
-               io_addr += sizeof(*saved_regs);
-       }
-}
-
 static int
 snd_azf3328_resume(struct pci_dev *pci)
 {
@@ -2487,14 +2848,7 @@ snd_azf3328_resume(struct pci_dev *pci)
        snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
                                        ARRAY_SIZE(chip->saved_regs_opl3));
 
-       snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
-                                       ARRAY_SIZE(chip->saved_regs_mixer));
-
-       /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
-          and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
-          resulting in a mixer reset condition persisting until _after_
-          master vol was restored. Thus master vol needs an extra restore. */
-       outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+       snd_azf3328_resume_ac97(chip);
 
        snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
                                        ARRAY_SIZE(chip->saved_regs_ctrl));
index 1bff80cde0a2f88000eaa1711867b882adcae433..b9321544c31c4375fb33a17c5bf4b849217cbe61 100644 (file)
@@ -869,7 +869,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
        mutex_lock(&atc->atc_mutex);
        dao->ops->get_spos(dao, &status);
        if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) {
-               status &= ((~IEC958_AES3_CON_FS) << 24);
+               status &= ~(IEC958_AES3_CON_FS << 24);
                status |= (iec958_con_fs << 24);
                dao->ops->set_spos(dao, status);
                dao->ops->commit_write(dao);
index af56eb949bded282affb84a59691dc49f3d3054e..47d9ea97de02e28cc0d7f9064a39a8c9222e29d0 100644 (file)
@@ -176,6 +176,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
        if (!entry)
                return -ENOMEM;
 
+       dao->ops->clear_left_input(dao);
        /* Program master and conjugate resources */
        input->ops->master(input);
        daio->rscl.ops->master(&daio->rscl);
@@ -204,6 +205,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
        if (!entry)
                return -ENOMEM;
 
+       dao->ops->clear_right_input(dao);
        /* Program master and conjugate resources */
        input->ops->master(input);
        daio->rscr.ops->master(&daio->rscr);
index b6b11bfe7574e2445b5811d0b2c4495a67ad50d9..5364164674e4cce9e39f56542182e150c582659d 100644 (file)
@@ -1307,10 +1307,10 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
        set_field(&pllctl, PLLCTL_B, 0);
        if (48000 == rsr) {
                set_field(&pllctl, PLLCTL_FD, 16 - 2);
-               set_field(&pllctl, PLLCTL_RD, 1 - 1);
+               set_field(&pllctl, PLLCTL_RD, 1 - 1); /* 3000*16/1 = 48000 */
        } else { /* 44100 */
                set_field(&pllctl, PLLCTL_FD, 147 - 2);
-               set_field(&pllctl, PLLCTL_RD, 10 - 1);
+               set_field(&pllctl, PLLCTL_RD, 10 - 1); /* 3000*147/10 = 44100 */
        }
        hw_write_20kx(hw, PLL_CTL, pllctl);
        mdelay(40);
@@ -1740,6 +1740,10 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
        return data;
 }
 
+#define MIC_BOOST_0DB 0xCF
+#define MIC_BOOST_STEPS_PER_DB 2
+#define MIC_BOOST_20DB (MIC_BOOST_0DB + 20 * MIC_BOOST_STEPS_PER_DB)
+
 static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
 {
        u32 data;
@@ -1751,10 +1755,12 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
                hw_write_20kx(hw, GPIO_DATA, data);
                hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
                                MAKE_WM8775_DATA(0x101)); /* Mic-in */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
-                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
-                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               hw20k2_i2c_write(hw,
+                               MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB),
+                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
+               hw20k2_i2c_write(hw,
+                               MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB),
+                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
                break;
        case ADC_LINEIN:
                data &= ~(0x1 << 14);
@@ -1827,10 +1833,12 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
 
                hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
                                MAKE_WM8775_DATA(0x101)); /* Mic-in */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
-                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
-               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
-                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               hw20k2_i2c_write(hw,
+                               MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB),
+                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
+               hw20k2_i2c_write(hw,
+                               MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB),
+                               MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */
        } else if (mux == 2) {
                /* Configures GPIO data to select Line-in */
                data &= ~(0x1 << 14);
index 15c1e7271ea889bbcaec9d6e45df709f0ee58845..c3519ff42fbbfd0b4684722b1389a81aa25e91c1 100644 (file)
@@ -566,19 +566,6 @@ static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int ct_spdif_default_get(struct snd_kcontrol *kcontrol,
-                               struct snd_ctl_elem_value *ucontrol)
-{
-       unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF;
-
-       ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
-       ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
-       ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
-       ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
-
-       return 0;
-}
-
 static int ct_spdif_get(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
@@ -586,6 +573,10 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol,
        unsigned int status;
 
        atc->spdif_out_get_status(atc, &status);
+
+       if (status == 0)
+               status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
        ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
        ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
        ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
@@ -629,7 +620,7 @@ static struct snd_kcontrol_new iec958_default_ctl = {
        .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
        .count          = 1,
        .info           = ct_spdif_info,
-       .get            = ct_spdif_default_get,
+       .get            = ct_spdif_get,
        .put            = ct_spdif_put,
        .private_value  = MIXER_IEC958_DEFAULT
 };
index 65da6e466f80bb27af0d466239a473436e8f40ac..b78f3fc3c33caabd26d49a5b41ed4bfda861d39d 100644 (file)
@@ -52,8 +52,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
 
        if (entry->size == size) {
                /* Move the vm node from unused list to used list directly */
-               list_del(&entry->list);
-               list_add(&entry->list, &vm->used);
+               list_move(&entry->list, &vm->used);
                vm->size -= size;
                block = entry;
                goto out;
index 66c7fb3ced3ef0b14e689f64b762739f7f87cdd3..5e619a84da061295fa4758e313935e89aea8009d 100644 (file)
@@ -926,7 +926,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
        snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19);
        /* Unknown. */
        snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c);
-       /* IRQ Enable: Alll on */
+       /* IRQ Enable: All on */
        /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
        /* IRQ Enable: All off */
        snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
index ae5c5d5e4b7ce73aaef33052d2707e0847a6e15b..2c79e96d0324db2042fe973fab81471146cfab77 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/asoundef.h>
 #include <sound/tlv.h>
 #include <sound/initval.h>
+#include <sound/jack.h>
 #include "hda_local.h"
 #include "hda_beep.h"
 #include <sound/hda_hwdep.h>
@@ -4959,5 +4960,109 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 }
 EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
 
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+/*
+ * Input-jack notification support
+ */
+struct hda_jack_item {
+       hda_nid_t nid;
+       int type;
+       struct snd_jack *jack;
+};
+
+static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid,
+                                        int type)
+{
+       switch (type) {
+       case SND_JACK_HEADPHONE:
+               return "Headphone";
+       case SND_JACK_MICROPHONE:
+               return "Mic";
+       case SND_JACK_LINEOUT:
+               return "Line-out";
+       case SND_JACK_HEADSET:
+               return "Headset";
+       default:
+               return "Misc";
+       }
+}
+
+static void hda_free_jack_priv(struct snd_jack *jack)
+{
+       struct hda_jack_item *jacks = jack->private_data;
+       jacks->nid = 0;
+       jacks->jack = NULL;
+}
+
+int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type,
+                          const char *name)
+{
+       struct hda_jack_item *jack;
+       int err;
+
+       snd_array_init(&codec->jacks, sizeof(*jack), 32);
+       jack = snd_array_new(&codec->jacks);
+       if (!jack)
+               return -ENOMEM;
+
+       jack->nid = nid;
+       jack->type = type;
+       if (!name)
+               name = get_jack_default_name(codec, nid, type);
+       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+       if (err < 0) {
+               jack->nid = 0;
+               return err;
+       }
+       jack->jack->private_data = jack;
+       jack->jack->private_free = hda_free_jack_priv;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_input_jack_add);
+
+void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_jack_item *jacks = codec->jacks.list;
+       int i;
+
+       if (!jacks)
+               return;
+
+       for (i = 0; i < codec->jacks.used; i++, jacks++) {
+               unsigned int pin_ctl;
+               unsigned int present;
+               int type;
+
+               if (jacks->nid != nid)
+                       continue;
+               present = snd_hda_jack_detect(codec, nid);
+               type = jacks->type;
+               if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) {
+                       pin_ctl = snd_hda_codec_read(codec, nid, 0,
+                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       type = (pin_ctl & AC_PINCTL_HP_EN) ?
+                               SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+               }
+               snd_jack_report(jacks->jack, present ? type : 0);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_input_jack_report);
+
+/* free jack instances manually when clearing/reconfiguring */
+void snd_hda_input_jack_free(struct hda_codec *codec)
+{
+       if (!codec->bus->shutdown && codec->jacks.list) {
+               struct hda_jack_item *jacks = codec->jacks.list;
+               int i;
+               for (i = 0; i < codec->jacks.used; i++, jacks++) {
+                       if (jacks->jack)
+                               snd_device_free(codec->bus->card, jacks->jack);
+               }
+       }
+       snd_array_free(&codec->jacks);
+}
+EXPORT_SYMBOL_HDA(snd_hda_input_jack_free);
+#endif /* CONFIG_SND_HDA_INPUT_JACK */
+
 MODULE_DESCRIPTION("HDA codec core");
 MODULE_LICENSE("GPL");
index fdf8d44f8b6b041fc4a9b1e0e63a31315a9fbe3b..e46d5420a9f21150e6e5f24549015c506b919235 100644 (file)
@@ -866,6 +866,11 @@ struct hda_codec {
        /* codec-specific additional proc output */
        void (*proc_widget_hook)(struct snd_info_buffer *buffer,
                                 struct hda_codec *codec, hda_nid_t nid);
+
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+       /* jack detection */
+       struct snd_array jacks;
+#endif
 };
 
 /* direction */
index fcedad9a5feffab614d084f92a3f3588d89a3382..70a9d32f0e967561ee64c83d515c721c2903978c 100644 (file)
@@ -1052,9 +1052,12 @@ static void azx_init_pci(struct azx *chip)
        /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
         * TCSEL == Traffic Class Select Register, which sets PCI express QOS
         * Ensuring these bits are 0 clears playback static on some HD Audio
-        * codecs
+        * codecs.
+        * The PCI register TCSEL is defined in the Intel manuals.
         */
-       update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
+       if (chip->driver_type != AZX_DRIVER_ATI &&
+           chip->driver_type != AZX_DRIVER_ATIHDMI)
+               update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
 
        switch (chip->driver_type) {
        case AZX_DRIVER_ATI:
index 3ab5e7a303db23a4d7f0af841f7bece6bba880c9..ff5e2ac2239ae210cb673326f4f1db42dce93c11 100644 (file)
@@ -656,4 +656,28 @@ static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
 #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
 void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
 
+/*
+ * Input-jack notification support
+ */
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type,
+                          const char *name);
+void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid);
+void snd_hda_input_jack_free(struct hda_codec *codec);
+#else /* CONFIG_SND_HDA_INPUT_JACK */
+static inline int snd_hda_input_jack_add(struct hda_codec *codec,
+                                        hda_nid_t nid, int type,
+                                        const char *name)
+{
+       return 0;
+}
+static inline void snd_hda_input_jack_report(struct hda_codec *codec,
+                                            hda_nid_t nid)
+{
+}
+static inline void snd_hda_input_jack_free(struct hda_codec *codec)
+{
+}
+#endif /* CONFIG_SND_HDA_INPUT_JACK */
+
 #endif /* __SOUND_HDA_LOCAL_H */
index 8dabab798689bfc65456d7859bfa439558103c84..734c6ee55d8a7ecc20fe56e3dce6c6b3f75397f0 100644 (file)
 #include "hda_beep.h"
 
 struct ad198x_spec {
-       struct snd_kcontrol_new *mixers[5];
+       struct snd_kcontrol_new *mixers[6];
        int num_mixers;
        unsigned int beep_amp;  /* beep amp value, set via set_beep_amp() */
-       const struct hda_verb *init_verbs[5];   /* initialization verbs
+       const struct hda_verb *init_verbs[6];   /* initialization verbs
                                                 * don't forget NULL termination!
                                                 */
        unsigned int num_init_verbs;
@@ -331,36 +331,11 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
 }
 
-static int ad198x_alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                               struct hda_codec *codec,
-                               unsigned int stream_tag,
-                               unsigned int format,
-                               struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       snd_hda_codec_setup_stream(codec, spec->alt_dac_nid[0], stream_tag,
-                                       0, format);
-       return 0;
-}
-
-static int ad198x_alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                               struct hda_codec *codec,
-                               struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       snd_hda_codec_cleanup_stream(codec, spec->alt_dac_nid[0]);
-       return 0;
-}
-
 static struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
        /* NID is set in ad198x_build_pcms */
-       .ops = {
-               .prepare = ad198x_alt_playback_pcm_prepare,
-               .cleanup = ad198x_alt_playback_pcm_cleanup
-       },
 };
 
 /*
@@ -2239,29 +2214,6 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
 static struct snd_kcontrol_new ad1988_6stack_fp_mixers[] = {
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
 
-       HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
-
        { } /* end */
 };
 
@@ -2545,11 +2497,6 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
 };
 
 static struct hda_verb ad1988_6stack_fp_init_verbs[] = {
-       /* Front, Surround, CLFE, side DAC; unmute as default */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* Headphone; unmute as default */
        {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* Port-A front headphon path */
@@ -2558,50 +2505,6 @@ static struct hda_verb ad1988_6stack_fp_init_verbs[] = {
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       /* Port-D line-out path */
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-F surround path */
-       {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-G CLFE path */
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-H side path */
-       {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Mono out path */
-       {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
-       /* Port-B front mic-in path */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Port-C line-in path */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Port-E mic-in path */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Analog CD Input */
-       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 
        { }
 };
@@ -3316,20 +3219,20 @@ static int patch_ad1988(struct hda_codec *codec)
                        spec->mixers[0] = ad1988_6stack_mixers1_rev2;
                else
                        spec->mixers[0] = ad1988_6stack_mixers1;
+               spec->mixers[1] = ad1988_6stack_mixers2;
+               spec->num_init_verbs = 1;
+               spec->init_verbs[0] = ad1988_6stack_init_verbs;
                if (board_config == AD1988_6STACK_DIG_FP) {
-                       spec->mixers[1] = ad1988_6stack_fp_mixers;
+                       spec->num_mixers++;
+                       spec->mixers[2] = ad1988_6stack_fp_mixers;
+                       spec->num_init_verbs++;
+                       spec->init_verbs[1] = ad1988_6stack_fp_init_verbs;
                        spec->slave_vols = ad1988_6stack_fp_slave_vols;
                        spec->slave_sws = ad1988_6stack_fp_slave_sws;
                        spec->alt_dac_nid = ad1988_alt_dac_nid;
                        spec->stream_analog_alt_playback =
                                &ad198x_pcm_analog_alt_playback;
-               } else
-                       spec->mixers[1] = ad1988_6stack_mixers2;
-               spec->num_init_verbs = 1;
-               if (board_config == AD1988_6STACK_DIG_FP)
-                       spec->init_verbs[0] = ad1988_6stack_fp_init_verbs;
-               else
-                       spec->init_verbs[0] = ad1988_6stack_init_verbs;
+               }
                if ((board_config == AD1988_6STACK_DIG) ||
                        (board_config == AD1988_6STACK_DIG_FP)) {
                        spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
index 4d5004e693f03aa60ad00245a9abfc315f311033..d08cf31596f3aae13856cfcd547a4a66cb02abb7 100644 (file)
 #define AUTO_MIC_PORTB         (1 << 1)
 #define AUTO_MIC_PORTC         (1 << 2)
 
-struct conexant_jack {
-
-       hda_nid_t nid;
-       int type;
-       struct snd_jack *jack;
-
-};
-
 struct pin_dac_pair {
        hda_nid_t pin;
        hda_nid_t dac;
@@ -111,9 +103,6 @@ struct conexant_spec {
 
        unsigned int spdif_route;
 
-       /* jack detection */
-       struct snd_array jacks;
-
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
        struct hda_input_mux private_imux;
@@ -393,71 +382,9 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
                                     &spec->cur_mux[adc_idx]);
 }
 
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void conexant_free_jack_priv(struct snd_jack *jack)
-{
-       struct conexant_jack *jacks = jack->private_data;
-       jacks->nid = 0;
-       jacks->jack = NULL;
-}
-
-static int conexant_add_jack(struct hda_codec *codec,
-               hda_nid_t nid, int type)
-{
-       struct conexant_spec *spec;
-       struct conexant_jack *jack;
-       const char *name;
-       int i, err;
-
-       spec = codec->spec;
-       snd_array_init(&spec->jacks, sizeof(*jack), 32);
-
-       jack = spec->jacks.list;
-       for (i = 0; i < spec->jacks.used; i++, jack++)
-               if (jack->nid == nid)
-                       return 0 ; /* already present */
-
-       jack = snd_array_new(&spec->jacks);
-       name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
-
-       if (!jack)
-               return -ENOMEM;
-
-       jack->nid = nid;
-       jack->type = type;
-
-       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
-       if (err < 0)
-               return err;
-       jack->jack->private_data = jack;
-       jack->jack->private_free = conexant_free_jack_priv;
-       return 0;
-}
-
-static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct conexant_spec *spec = codec->spec;
-       struct conexant_jack *jacks = spec->jacks.list;
-
-       if (jacks) {
-               int i;
-               for (i = 0; i < spec->jacks.used; i++) {
-                       if (jacks->nid == nid) {
-                               unsigned int present;
-                               present = snd_hda_jack_detect(codec, nid);
-
-                               present = (present) ? jacks->type : 0 ;
-
-                               snd_jack_report(jacks->jack,
-                                               present);
-                       }
-                       jacks++;
-               }
-       }
-}
-
 static int conexant_init_jacks(struct hda_codec *codec)
 {
+#ifdef CONFIG_SND_HDA_INPUT_JACK
        struct conexant_spec *spec = codec->spec;
        int i;
 
@@ -469,15 +396,15 @@ static int conexant_init_jacks(struct hda_codec *codec)
                        int err = 0;
                        switch (hv->param ^ AC_USRSP_EN) {
                        case CONEXANT_HP_EVENT:
-                               err = conexant_add_jack(codec, hv->nid,
-                                               SND_JACK_HEADPHONE);
-                               conexant_report_jack(codec, hv->nid);
+                               err = snd_hda_input_jack_add(codec, hv->nid,
+                                               SND_JACK_HEADPHONE, NULL);
+                               snd_hda_input_jack_report(codec, hv->nid);
                                break;
                        case CXT5051_PORTC_EVENT:
                        case CONEXANT_MIC_EVENT:
-                               err = conexant_add_jack(codec, hv->nid,
-                                               SND_JACK_MICROPHONE);
-                               conexant_report_jack(codec, hv->nid);
+                               err = snd_hda_input_jack_add(codec, hv->nid,
+                                               SND_JACK_MICROPHONE, NULL);
+                               snd_hda_input_jack_report(codec, hv->nid);
                                break;
                        }
                        if (err < 0)
@@ -485,19 +412,9 @@ static int conexant_init_jacks(struct hda_codec *codec)
                        ++hv;
                }
        }
-       return 0;
-
-}
-#else
-static inline void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-}
-
-static inline int conexant_init_jacks(struct hda_codec *codec)
-{
+#endif /* CONFIG_SND_HDA_INPUT_JACK */
        return 0;
 }
-#endif
 
 static int conexant_init(struct hda_codec *codec)
 {
@@ -511,18 +428,7 @@ static int conexant_init(struct hda_codec *codec)
 
 static void conexant_free(struct hda_codec *codec)
 {
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-       struct conexant_spec *spec = codec->spec;
-       if (spec->jacks.list) {
-               struct conexant_jack *jacks = spec->jacks.list;
-               int i;
-               for (i = 0; i < spec->jacks.used; i++, jacks++) {
-                       if (jacks->jack)
-                               snd_device_free(codec->bus->card, jacks->jack);
-               }
-               snd_array_free(&spec->jacks);
-       }
-#endif
+       snd_hda_input_jack_free(codec);
        snd_hda_detach_beep_device(codec);
        kfree(codec->spec);
 }
@@ -1787,7 +1693,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec,
                cxt5051_portc_automic(codec);
                break;
        }
-       conexant_report_jack(codec, nid);
+       snd_hda_input_jack_report(codec, nid);
 }
 
 static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
@@ -1959,10 +1865,8 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_codec_write(codec, nid, 0,
                            AC_VERB_SET_UNSOLICITED_ENABLE,
                            AC_USRSP_EN | event);
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-       conexant_add_jack(codec, nid, SND_JACK_MICROPHONE);
-       conexant_report_jack(codec, nid);
-#endif
+       snd_hda_input_jack_add(codec, nid, SND_JACK_MICROPHONE, NULL);
+       snd_hda_input_jack_report(codec, nid);
 }
 
 static struct hda_verb cxt5051_ideapad_init_verbs[] = {
@@ -3477,11 +3381,11 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
        switch (res >> 26) {
        case CONEXANT_HP_EVENT:
                cx_auto_hp_automute(codec);
-               conexant_report_jack(codec, nid);
+               snd_hda_input_jack_report(codec, nid);
                break;
        case CONEXANT_MIC_EVENT:
                cx_auto_automic(codec);
-               conexant_report_jack(codec, nid);
+               snd_hda_input_jack_report(codec, nid);
                break;
        }
 }
index ec0fa2dd0a2792a3fb7e1a18334fd3eb28225b29..251773e45f61bf0a252ed5c1e888c9853e6b14b9 100644 (file)
@@ -110,6 +110,12 @@ struct dp_audio_infoframe {
        u8 LFEPBL01_LSV36_DM_INH7;
 };
 
+union audio_infoframe {
+       struct hdmi_audio_infoframe hdmi;
+       struct dp_audio_infoframe dp;
+       u8 bytes[0];
+};
+
 /*
  * CEA speaker placement:
  *
@@ -620,8 +626,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
        int channels = substream->runtime->channels;
        int ca;
        int i;
-       u8 ai[max(sizeof(struct hdmi_audio_infoframe),
-                 sizeof(struct dp_audio_infoframe))];
+       union audio_infoframe ai;
 
        ca = hdmi_channel_allocation(codec, nid, channels);
 
@@ -633,11 +638,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
 
                pin_nid = spec->pin[i];
 
-               memset(ai, 0, sizeof(ai));
+               memset(&ai, 0, sizeof(ai));
                if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
-                       struct hdmi_audio_infoframe *hdmi_ai;
+                       struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
 
-                       hdmi_ai = (struct hdmi_audio_infoframe *)ai;
                        hdmi_ai->type           = 0x84;
                        hdmi_ai->ver            = 0x01;
                        hdmi_ai->len            = 0x0a;
@@ -645,9 +649,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
                        hdmi_ai->CA             = ca;
                        hdmi_checksum_audio_infoframe(hdmi_ai);
                } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
-                       struct dp_audio_infoframe *dp_ai;
+                       struct dp_audio_infoframe *dp_ai = &ai.dp;
 
-                       dp_ai = (struct dp_audio_infoframe *)ai;
                        dp_ai->type             = 0x84;
                        dp_ai->len              = 0x1b;
                        dp_ai->ver              = 0x11 << 2;
@@ -664,7 +667,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
                 * sizeof(*dp_ai) to avoid partial match/update problems when
                 * the user switches between HDMI/DP monitors.
                 */
-               if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
+               if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+                                            sizeof(ai))) {
                        snd_printdd("hdmi_setup_audio_infoframe: "
                                    "cvt=%d pin=%d channels=%d\n",
                                    nid, pin_nid,
@@ -672,7 +676,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
                        hdmi_setup_channel_mapping(codec, pin_nid, ca);
                        hdmi_stop_infoframe_trans(codec, pin_nid);
                        hdmi_fill_audio_infoframe(codec, pin_nid,
-                                                 ai, sizeof(ai));
+                                                 ai.bytes, sizeof(ai));
                        hdmi_start_infoframe_trans(codec, pin_nid);
                }
        }
index 4261bb8eec1d524b283346ef6effa181134601e8..f1a03f223495f70043914326c4a563798f10ef34 100644 (file)
@@ -282,12 +282,6 @@ struct alc_mic_route {
        unsigned char amix_idx;
 };
 
-struct alc_jack {
-       hda_nid_t nid;
-       int type;
-       struct snd_jack *jack;
-};
-
 #define MUX_IDX_UNDEF  ((unsigned char)-1)
 
 struct alc_customize_define {
@@ -366,9 +360,6 @@ struct alc_spec {
        /* PCM information */
        struct hda_pcm pcm_rec[3];      /* used in alc_build_pcms() */
 
-       /* jack detection */
-       struct snd_array jacks;
-
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
        struct alc_customize_define cdefine;
@@ -394,6 +385,7 @@ struct alc_spec {
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
        unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
+       unsigned int single_input_src:1;
        int init_amp;
        int codec_variant;      /* flag for other variants */
 
@@ -1032,94 +1024,32 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
        alc_fix_pll(codec);
 }
 
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void alc_free_jack_priv(struct snd_jack *jack)
-{
-       struct alc_jack *jacks = jack->private_data;
-       jacks->nid = 0;
-       jacks->jack = NULL;
-}
-
-static int alc_add_jack(struct hda_codec *codec,
-               hda_nid_t nid, int type)
-{
-       struct alc_spec *spec;
-       struct alc_jack *jack;
-       const char *name;
-       int err;
-
-       spec = codec->spec;
-       snd_array_init(&spec->jacks, sizeof(*jack), 32);
-       jack = snd_array_new(&spec->jacks);
-       if (!jack)
-               return -ENOMEM;
-
-       jack->nid = nid;
-       jack->type = type;
-       name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
-
-       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
-       if (err < 0)
-               return err;
-       jack->jack->private_data = jack;
-       jack->jack->private_free = alc_free_jack_priv;
-       return 0;
-}
-
-static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct alc_spec *spec = codec->spec;
-       struct alc_jack *jacks = spec->jacks.list;
-
-       if (jacks) {
-               int i;
-               for (i = 0; i < spec->jacks.used; i++) {
-                       if (jacks->nid == nid) {
-                               unsigned int present;
-                               present = snd_hda_jack_detect(codec, nid);
-
-                               present = (present) ? jacks->type : 0;
-
-                               snd_jack_report(jacks->jack, present);
-                       }
-                       jacks++;
-               }
-       }
-}
-
 static int alc_init_jacks(struct hda_codec *codec)
 {
+#ifdef CONFIG_SND_HDA_INPUT_JACK
        struct alc_spec *spec = codec->spec;
        int err;
        unsigned int hp_nid = spec->autocfg.hp_pins[0];
        unsigned int mic_nid = spec->ext_mic.pin;
 
        if (hp_nid) {
-               err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
+               err = snd_hda_input_jack_add(codec, hp_nid,
+                                            SND_JACK_HEADPHONE, NULL);
                if (err < 0)
                        return err;
-               alc_report_jack(codec, hp_nid);
+               snd_hda_input_jack_report(codec, hp_nid);
        }
 
        if (mic_nid) {
-               err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
+               err = snd_hda_input_jack_add(codec, mic_nid,
+                                            SND_JACK_MICROPHONE, NULL);
                if (err < 0)
                        return err;
-               alc_report_jack(codec, mic_nid);
+               snd_hda_input_jack_report(codec, mic_nid);
        }
-
-       return 0;
-}
-#else
-static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-}
-
-static inline int alc_init_jacks(struct hda_codec *codec)
-{
+#endif /* CONFIG_SND_HDA_INPUT_JACK */
        return 0;
 }
-#endif
 
 static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
 {
@@ -1133,7 +1063,7 @@ static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
                nid = spec->autocfg.hp_pins[i];
                if (!nid)
                        break;
-               alc_report_jack(codec, nid);
+               snd_hda_input_jack_report(codec, nid);
                spec->jack_present |= snd_hda_jack_detect(codec, nid);
        }
 
@@ -1240,7 +1170,7 @@ static void alc_mic_automute(struct hda_codec *codec)
                                          AC_VERB_SET_CONNECT_SEL,
                                          alive->mux_idx);
        }
-       alc_report_jack(codec, spec->ext_mic.pin);
+       snd_hda_input_jack_report(codec, spec->ext_mic.pin);
 
        /* FIXME: analog mixer */
 }
@@ -2292,13 +2222,13 @@ static struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = {
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0f, 2, 0x0,
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
                HDA_OUTPUT),
-       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0f, 2, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0f, 1, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("Side Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("Side Playback Switch", 0x0e, 2, HDA_INPUT),
+       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
@@ -2309,7 +2239,6 @@ static struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = {
        { } /* end */
 };
 
-
 static struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -3919,6 +3848,8 @@ static struct hda_amp_list alc880_lg_loopbacks[] = {
  * Common callbacks
  */
 
+static void alc_init_special_input_src(struct hda_codec *codec);
+
 static int alc_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -3929,6 +3860,7 @@ static int alc_init(struct hda_codec *codec)
 
        for (i = 0; i < spec->num_init_verbs; i++)
                snd_hda_sequence_write(codec, spec->init_verbs[i]);
+       alc_init_special_input_src(codec);
 
        if (spec->init_hook)
                spec->init_hook(codec);
@@ -4284,6 +4216,7 @@ static void alc_free(struct hda_codec *codec)
                return;
 
        alc_shutup(codec);
+       snd_hda_input_jack_free(codec);
        alc_free_kctls(codec);
        kfree(spec);
        snd_hda_detach_beep_device(codec);
@@ -4702,7 +4635,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
        SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
        SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
-       SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
+       SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_F1734),
        SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
        SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
        SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
@@ -5151,7 +5084,9 @@ static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg,
 
        switch (cfg->line_out_type) {
        case AUTO_PIN_SPEAKER_OUT:
-               return "Speaker";
+               if (cfg->line_outs == 1)
+                       return "Speaker";
+               break;
        case AUTO_PIN_HP_OUT:
                return "Headphone";
        default:
@@ -5205,16 +5140,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                return err;
                } else {
                        const char *name = pfx;
-                       if (!name)
+                       int index = i;
+                       if (!name) {
                                name = chname[i];
+                               index = 0;
+                       }
                        err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
-                                               name, i,
+                                               name, index,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
                        err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
-                                              name, i,
+                                              name, index,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -5585,6 +5523,7 @@ static void fixup_single_adc(struct hda_codec *codec)
                        spec->capsrc_nids += i;
                spec->adc_nids += i;
                spec->num_adc_nids = 1;
+               spec->single_input_src = 1;
        }
 }
 
@@ -5596,6 +5535,16 @@ static void fixup_dual_adc_switch(struct hda_codec *codec)
        init_capsrc_for_pin(codec, spec->int_mic.pin);
 }
 
+/* initialize some special cases for input sources */
+static void alc_init_special_input_src(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       if (spec->dual_adc_switch)
+               fixup_dual_adc_switch(codec);
+       else if (spec->single_input_src)
+               init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin);
+}
+
 static void set_capture_mixer(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -5611,7 +5560,7 @@ static void set_capture_mixer(struct hda_codec *codec)
                int mux = 0;
                int num_adcs = spec->num_adc_nids;
                if (spec->dual_adc_switch)
-                       fixup_dual_adc_switch(codec);
+                       num_adcs = 1;
                else if (spec->auto_mic)
                        fixup_automic_adc(codec);
                else if (spec->input_mux) {
@@ -5620,8 +5569,6 @@ static void set_capture_mixer(struct hda_codec *codec)
                        else if (spec->input_mux->num_items == 1)
                                fixup_single_adc(codec);
                }
-               if (spec->dual_adc_switch)
-                       num_adcs = 1;
                spec->cap_mixer = caps[mux][num_adcs - 1];
        }
 }
@@ -10748,6 +10695,7 @@ static struct alc_config_preset alc882_presets[] = {
  */
 enum {
        PINFIX_ABIT_AW9D_MAX,
+       PINFIX_LENOVO_Y530,
        PINFIX_PB_M5210,
        PINFIX_ACER_ASPIRE_7736,
 };
@@ -10762,6 +10710,14 @@ static const struct alc_fixup alc882_fixups[] = {
                        { }
                }
        },
+       [PINFIX_LENOVO_Y530] = {
+               .type = ALC_FIXUP_PINS,
+               .v.pins = (const struct alc_pincfg[]) {
+                       { 0x15, 0x99130112 }, /* rear int speakers */
+                       { 0x16, 0x99130111 }, /* subwoofer */
+                       { }
+               }
+       },
        [PINFIX_PB_M5210] = {
                .type = ALC_FIXUP_VERBS,
                .v.verbs = (const struct hda_verb[]) {
@@ -10777,6 +10733,7 @@ static const struct alc_fixup alc882_fixups[] = {
 
 static struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
+       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),
        {}
@@ -10829,23 +10786,28 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
        hda_nid_t pin, dac;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
-               pin = spec->autocfg.hp_pins[i];
-               if (!pin)
-                       break;
-               dac = spec->multiout.hp_nid;
-               if (!dac)
-                       dac = spec->multiout.dac_nids[0]; /* to front */
-               alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
+       if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) {
+               for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+                       pin = spec->autocfg.hp_pins[i];
+                       if (!pin)
+                               break;
+                       dac = spec->multiout.hp_nid;
+                       if (!dac)
+                               dac = spec->multiout.dac_nids[0]; /* to front */
+                       alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
+               }
        }
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
-               pin = spec->autocfg.speaker_pins[i];
-               if (!pin)
-                       break;
-               dac = spec->multiout.extra_out_nid[0];
-               if (!dac)
-                       dac = spec->multiout.dac_nids[0]; /* to front */
-               alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
+
+       if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) {
+               for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+                       pin = spec->autocfg.speaker_pins[i];
+                       if (!pin)
+                               break;
+                       dac = spec->multiout.extra_out_nid[0];
+                       if (!dac)
+                               dac = spec->multiout.dac_nids[0]; /* to front */
+                       alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
+               }
        }
 }
 
@@ -13796,6 +13758,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 }
 
 #define alc268_auto_init_analog_input  alc882_auto_init_analog_input
+#define alc268_auto_init_input_src     alc882_auto_init_input_src
 
 /* init callback for auto-configuration model -- overriding the default init */
 static void alc268_auto_init(struct hda_codec *codec)
@@ -13805,6 +13768,7 @@ static void alc268_auto_init(struct hda_codec *codec)
        alc268_auto_init_hp_out(codec);
        alc268_auto_init_mono_speaker_out(codec);
        alc268_auto_init_analog_input(codec);
+       alc268_auto_init_input_src(codec);
        alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
@@ -14092,7 +14056,6 @@ static int patch_alc268(struct hda_codec *codec)
        if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
                /* check whether NID 0x07 is valid */
                unsigned int wcap = get_wcaps(codec, 0x07);
-               int i;
 
                spec->capsrc_nids = alc268_capsrc_nids;
                /* get type */
@@ -14112,13 +14075,6 @@ static int patch_alc268(struct hda_codec *codec)
                        spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
                        add_mixer(spec, alc268_capture_mixer);
                }
-               /* set default input source */
-               for (i = 0; i < spec->num_adc_nids; i++)
-                       snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
-                               0, AC_VERB_SET_CONNECT_SEL,
-                               i < spec->num_mux_defs ?
-                               spec->input_mux[i].items[0].index :
-                               spec->input_mux->items[0].index);
        }
 
        spec->vmaster_nid = 0x02;
@@ -14495,7 +14451,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, bits);
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
                                 HDA_AMP_MUTE, bits);
-       alc_report_jack(codec, nid);
+       snd_hda_input_jack_report(codec, nid);
 }
 
 /* unsolicited event for HP jack sensing */
@@ -14807,11 +14763,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
                fillup_priv_adc_nids(codec, alc269_adc_candidates,
                                     sizeof(alc269_adc_candidates));
 
-       /* set default input source */
-       if (!spec->dual_adc_switch)
-               select_or_unmute_capsrc(codec, spec->capsrc_nids[0],
-                                       spec->input_mux->items[0].index);
-
        err = alc_auto_add_mic_boost(codec);
        if (err < 0)
                return err;
@@ -14825,6 +14776,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 #define alc269_auto_init_multi_out     alc268_auto_init_multi_out
 #define alc269_auto_init_hp_out                alc268_auto_init_hp_out
 #define alc269_auto_init_analog_input  alc882_auto_init_analog_input
+#define alc269_auto_init_input_src     alc882_auto_init_input_src
 
 
 /* init callback for auto-configuration model -- overriding the default init */
@@ -14834,6 +14786,8 @@ static void alc269_auto_init(struct hda_codec *codec)
        alc269_auto_init_multi_out(codec);
        alc269_auto_init_hp_out(codec);
        alc269_auto_init_analog_input(codec);
+       if (!spec->dual_adc_switch)
+               alc269_auto_init_input_src(codec);
        alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
index bd7b123f64407e5398f0ddfe8a48ec10acfb1308..05fcd60cc46f79f0d5442f1297639fdf4d3ec79b 100644 (file)
@@ -180,18 +180,16 @@ struct sigmatel_event {
        int data;
 };
 
-struct sigmatel_jack {
-       hda_nid_t nid;
-       int type;
-       struct snd_jack *jack;
-};
-
 struct sigmatel_mic_route {
        hda_nid_t pin;
        signed char mux_idx;
        signed char dmux_idx;
 };
 
+#define MAX_PINS_NUM 16
+#define MAX_ADCS_NUM 4
+#define MAX_DMICS_NUM 4
+
 struct sigmatel_spec {
        struct snd_kcontrol_new *mixers[4];
        unsigned int num_mixers;
@@ -229,9 +227,6 @@ struct sigmatel_spec {
        hda_nid_t *pwr_nids;
        hda_nid_t *dac_list;
 
-       /* jack detection */
-       struct snd_array jacks;
-
        /* events */
        struct snd_array events;
 
@@ -309,6 +304,17 @@ struct sigmatel_spec {
        struct hda_input_mux private_imux;
        struct hda_input_mux private_smux;
        struct hda_input_mux private_mono_mux;
+
+       /* auto spec */
+       unsigned auto_pin_cnt;
+       hda_nid_t auto_pin_nids[MAX_PINS_NUM];
+       unsigned auto_adc_cnt;
+       hda_nid_t auto_adc_nids[MAX_ADCS_NUM];
+       hda_nid_t auto_mux_nids[MAX_ADCS_NUM];
+       hda_nid_t auto_dmux_nids[MAX_ADCS_NUM];
+       unsigned long auto_capvols[MAX_ADCS_NUM];
+       unsigned auto_dmic_cnt;
+       hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
 };
 
 static hda_nid_t stac9200_adc_nids[1] = {
@@ -364,14 +370,6 @@ static unsigned long stac92hd73xx_capvols[] = {
 
 #define STAC92HD83_DAC_COUNT 3
 
-static hda_nid_t stac92hd83xxx_mux_nids[2] = {
-       0x17, 0x18,
-};
-
-static hda_nid_t stac92hd83xxx_adc_nids[2] = {
-       0x15, 0x16,
-};
-
 static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
        0xa, 0xb, 0xd, 0xe,
 };
@@ -384,25 +382,9 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
        0x03, 0x0c, 0x20, 0x40,
 };
 
-#define STAC92HD83XXX_NUM_DMICS         2
-static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
-       0x11, 0x20, 0
-};
-
-#define STAC92HD88XXX_NUM_DMICS        STAC92HD83XXX_NUM_DMICS
-#define stac92hd88xxx_dmic_nids        stac92hd83xxx_dmic_nids
-
-#define STAC92HD87B_NUM_DMICS   1
-static hda_nid_t stac92hd87b_dmic_nids[STAC92HD87B_NUM_DMICS + 1] = {
-       0x11, 0
-};
-
-#define STAC92HD83XXX_NUM_CAPS 2
-static unsigned long stac92hd83xxx_capvols[] = {
-       HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
-       HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT),
+static hda_nid_t stac92hd83xxx_dmic_nids[] = {
+               0x11, 0x20,
 };
-#define stac92hd83xxx_capsws   stac92hd83xxx_capvols
 
 static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
        0x0a, 0x0d, 0x0f
@@ -581,21 +563,6 @@ static hda_nid_t stac92hd73xx_pin_nids[13] = {
        0x14, 0x22, 0x23
 };
 
-static hda_nid_t stac92hd83xxx_pin_nids[10] = {
-       0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
-       0x0f, 0x10, 0x11, 0x1f, 0x20,
-};
-
-static hda_nid_t stac92hd87xxx_pin_nids[6] = {
-       0x0a, 0x0b, 0x0c, 0x0d,
-       0x0f, 0x11,
-};
-
-static hda_nid_t stac92hd88xxx_pin_nids[8] = {
-       0x0a, 0x0b, 0x0c, 0x0d,
-       0x0f, 0x11, 0x1f, 0x20,
-};
-
 #define STAC92HD71BXX_NUM_PINS 13
 static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
        0x0a, 0x0b, 0x0c, 0x0d, 0x00,
@@ -757,7 +724,7 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        struct sigmatel_spec *spec = codec->spec;
        unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        const struct hda_input_mux *imux = spec->input_mux;
-       unsigned int idx, prev_idx;
+       unsigned int idx, prev_idx, didx;
 
        idx = ucontrol->value.enumerated.item[0];
        if (idx >= imux->num_items)
@@ -769,7 +736,8 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
                snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
                                          AC_VERB_SET_CONNECT_SEL,
                                          imux->items[idx].index);
-               if (prev_idx >= spec->num_analog_muxes) {
+               if (prev_idx >= spec->num_analog_muxes &&
+                   spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) {
                        imux = spec->dinput_mux;
                        /* 0 = analog */
                        snd_hda_codec_write_cache(codec,
@@ -779,9 +747,13 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
                }
        } else {
                imux = spec->dinput_mux;
+               /* first dimux item is hardcoded to select analog imux,
+                * so lets skip it
+                */
+               didx = idx - spec->num_analog_muxes + 1;
                snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
                                          AC_VERB_SET_CONNECT_SEL,
-                                         imux->items[idx - 1].index);
+                                         imux->items[didx].index);
        }
        spec->cur_mux[adc_idx] = idx;
        return 1;
@@ -3419,6 +3391,17 @@ static const char * const stac92xx_dmic_labels[5] = {
        "Digital Mic 3", "Digital Mic 4"
 };
 
+static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux,
+                                   int idx)
+{
+       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+       int nums;
+       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+       if (idx >= 0 && idx < nums)
+               return conn[idx];
+       return 0;
+}
+
 static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
                                hda_nid_t nid)
 {
@@ -3429,6 +3412,15 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
        for (i = 0; i < nums; i++)
                if (conn[i] == nid)
                        return i;
+
+       for (i = 0; i < nums; i++) {
+               unsigned int wid_caps = get_wcaps(codec, conn[i]);
+               unsigned int wid_type = get_wcaps_type(wid_caps);
+
+               if (wid_type != AC_WID_PIN && wid_type != AC_WID_AUD_MIX)
+                       if (get_connection_index(codec, conn[i], nid) >= 0)
+                               return i;
+       }
        return -1;
 }
 
@@ -3501,6 +3493,16 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
                                                      type_idx, HDA_OUTPUT);
                        if (err < 0)
                                return err;
+                       if (!err) {
+                               nid = get_connected_node(codec,
+                                               spec->dmux_nids[0], index);
+                               if (nid)
+                                       err = create_elem_capture_vol(codec,
+                                                       nid, label,
+                                                       type_idx, HDA_INPUT);
+                               if (err < 0)
+                                       return err;
+                       }
                }
        }
 
@@ -4054,21 +4056,10 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
                           AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void stac92xx_free_jack_priv(struct snd_jack *jack)
-{
-       struct sigmatel_jack *jacks = jack->private_data;
-       jacks->nid = 0;
-       jacks->jack = NULL;
-}
-#endif
-
 static int stac92xx_add_jack(struct hda_codec *codec,
                hda_nid_t nid, int type)
 {
 #ifdef CONFIG_SND_HDA_INPUT_JACK
-       struct sigmatel_spec *spec = codec->spec;
-       struct sigmatel_jack *jack;
        int def_conf = snd_hda_codec_get_pincfg(codec, nid);
        int connectivity = get_defcfg_connect(def_conf);
        char name[32];
@@ -4077,26 +4068,15 @@ static int stac92xx_add_jack(struct hda_codec *codec,
        if (connectivity && connectivity != AC_JACK_PORT_FIXED)
                return 0;
 
-       snd_array_init(&spec->jacks, sizeof(*jack), 32);
-       jack = snd_array_new(&spec->jacks);
-       if (!jack)
-               return -ENOMEM;
-       jack->nid = nid;
-       jack->type = type;
-
        snprintf(name, sizeof(name), "%s at %s %s Jack",
                snd_hda_get_jack_type(def_conf),
                snd_hda_get_jack_connectivity(def_conf),
                snd_hda_get_jack_location(def_conf));
 
-       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
-       if (err < 0) {
-               jack->nid = 0;
+       err = snd_hda_input_jack_add(codec, nid, type, name);
+       if (err < 0)
                return err;
-       }
-       jack->jack->private_data = jack;
-       jack->jack->private_free = stac92xx_free_jack_priv;
-#endif
+#endif /* CONFIG_SND_HDA_INPUT_JACK */
        return 0;
 }
 
@@ -4399,23 +4379,6 @@ static int stac92xx_init(struct hda_codec *codec)
        return 0;
 }
 
-static void stac92xx_free_jacks(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-       /* free jack instances manually when clearing/reconfiguring */
-       struct sigmatel_spec *spec = codec->spec;
-       if (!codec->bus->shutdown && spec->jacks.list) {
-               struct sigmatel_jack *jacks = spec->jacks.list;
-               int i;
-               for (i = 0; i < spec->jacks.used; i++, jacks++) {
-                       if (jacks->jack)
-                               snd_device_free(codec->bus->card, jacks->jack);
-               }
-       }
-       snd_array_free(&spec->jacks);
-#endif
-}
-
 static void stac92xx_free_kctls(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4449,7 +4412,7 @@ static void stac92xx_free(struct hda_codec *codec)
                return;
 
        stac92xx_shutup(codec);
-       stac92xx_free_jacks(codec);
+       snd_hda_input_jack_free(codec);
        snd_array_free(&spec->events);
 
        kfree(spec);
@@ -4667,33 +4630,6 @@ static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
        stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
 }
 
-static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct sigmatel_spec *spec = codec->spec;
-       struct sigmatel_jack *jacks = spec->jacks.list;
-
-       if (jacks) {
-               int i;
-               for (i = 0; i < spec->jacks.used; i++) {
-                       if (jacks->nid == nid) {
-                               unsigned int pin_ctl =
-                                       snd_hda_codec_read(codec, nid,
-                                       0, AC_VERB_GET_PIN_WIDGET_CONTROL,
-                                        0x00);
-                               int type = jacks->type;
-                               if (type == (SND_JACK_LINEOUT
-                                               | SND_JACK_HEADPHONE))
-                                       type = (pin_ctl & AC_PINCTL_HP_EN)
-                                       ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
-                               snd_jack_report(jacks->jack,
-                                       get_pin_presence(codec, nid)
-                                       ? type : 0);
-                       }
-                       jacks++;
-               }
-       }
-}
-
 /* get the pin connection (fixed, none, etc) */
 static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
 {
@@ -4782,7 +4718,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
        case STAC_PWR_EVENT:
                if (spec->num_pwrs > 0)
                        stac92xx_pin_sense(codec, event->nid);
-               stac92xx_report_jack(codec, event->nid);
+               snd_hda_input_jack_report(codec, event->nid);
 
                switch (codec->subsystem_id) {
                case 0x103c308f:
@@ -5378,6 +5314,105 @@ static int hp_bnb2011_with_dock(struct hda_codec *codec)
        return 0;
 }
 
+static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       int i;
+
+       spec->auto_pin_nids[spec->auto_pin_cnt] = nid;
+       spec->auto_pin_cnt++;
+
+       if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+           get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) {
+               for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) {
+                       if (nid == stac92hd83xxx_dmic_nids[i]) {
+                               spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid;
+                               spec->auto_dmic_cnt++;
+                       }
+               }
+       }
+}
+
+static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       spec->auto_adc_nids[spec->auto_adc_cnt] = nid;
+       spec->auto_adc_cnt++;
+}
+
+static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid)
+{
+       int i, j;
+       struct sigmatel_spec *spec = codec->spec;
+
+       for (i = 0; i < spec->auto_adc_cnt; i++) {
+               if (get_connection_index(codec,
+                               spec->auto_adc_nids[i], nid) >= 0) {
+                       /* mux and volume for adc_nids[i] */
+                       if (!spec->auto_mux_nids[i]) {
+                               spec->auto_mux_nids[i] = nid;
+                               /* 92hd codecs capture volume is in mux */
+                               spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid,
+                                                       3, 0, HDA_OUTPUT);
+                       }
+                       for (j = 0; j < spec->auto_dmic_cnt; j++) {
+                               if (get_connection_index(codec, nid,
+                                               spec->auto_dmic_nids[j]) >= 0) {
+                                       /* dmux for adc_nids[i] */
+                                       if (!spec->auto_dmux_nids[i])
+                                               spec->auto_dmux_nids[i] = nid;
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+}
+
+static void stac92hd8x_fill_auto_spec(struct hda_codec *codec)
+{
+       hda_nid_t nid, end_nid;
+       unsigned int wid_caps, wid_type;
+       struct sigmatel_spec *spec = codec->spec;
+
+       end_nid = codec->start_nid + codec->num_nodes;
+
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               wid_caps = get_wcaps(codec, nid);
+               wid_type = get_wcaps_type(wid_caps);
+
+               if (wid_type == AC_WID_PIN)
+                       stac92hd8x_add_pin(codec, nid);
+
+               if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL))
+                       stac92hd8x_add_adc(codec, nid);
+       }
+
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               wid_caps = get_wcaps(codec, nid);
+               wid_type = get_wcaps_type(wid_caps);
+
+               if (wid_type == AC_WID_AUD_SEL)
+                       stac92hd8x_add_mux(codec, nid);
+       }
+
+       spec->pin_nids = spec->auto_pin_nids;
+       spec->num_pins = spec->auto_pin_cnt;
+       spec->adc_nids = spec->auto_adc_nids;
+       spec->num_adcs = spec->auto_adc_cnt;
+       spec->capvols = spec->auto_capvols;
+       spec->capsws = spec->auto_capvols;
+       spec->num_caps = spec->auto_adc_cnt;
+       spec->mux_nids = spec->auto_mux_nids;
+       spec->num_muxes = spec->auto_adc_cnt;
+       spec->dmux_nids = spec->auto_dmux_nids;
+       spec->num_dmuxes = spec->auto_adc_cnt;
+       spec->dmic_nids = spec->auto_dmic_nids;
+       spec->num_dmics = spec->auto_dmic_cnt;
+}
+
 static int patch_stac92hd83xxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
@@ -5399,26 +5434,17 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
        snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
        codec->no_trigger_sense = 1;
        codec->spec = spec;
+
+       stac92hd8x_fill_auto_spec(codec);
+
        spec->linear_tone_beep = 0;
        codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
        spec->digbeep_nid = 0x21;
-       spec->dmic_nids = stac92hd83xxx_dmic_nids;
-       spec->dmux_nids = stac92hd83xxx_mux_nids;
-       spec->mux_nids = stac92hd83xxx_mux_nids;
-       spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
-       spec->adc_nids = stac92hd83xxx_adc_nids;
-       spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
        spec->pwr_nids = stac92hd83xxx_pwr_nids;
        spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
        spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
        spec->multiout.dac_nids = spec->dac_nids;
-
        spec->init = stac92hd83xxx_core_init;
-       spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
-       spec->pin_nids = stac92hd83xxx_pin_nids;
-       spec->num_caps = STAC92HD83XXX_NUM_CAPS;
-       spec->capvols = stac92hd83xxx_capvols;
-       spec->capsws = stac92hd83xxx_capsws;
 
        spec->board_config = snd_hda_check_board_config(codec,
                                                        STAC_92HD83XXX_MODELS,
@@ -5436,28 +5462,11 @@ again:
        case 0x111d76d1:
        case 0x111d76d9:
        case 0x111d76e5:
-               spec->dmic_nids = stac92hd87b_dmic_nids;
-               spec->num_dmics = stac92xx_connected_ports(codec,
-                               stac92hd87b_dmic_nids,
-                               STAC92HD87B_NUM_DMICS);
-               spec->num_pins = ARRAY_SIZE(stac92hd87xxx_pin_nids);
-               spec->pin_nids = stac92hd87xxx_pin_nids;
-               spec->mono_nid = 0;
-               spec->num_pwrs = 0;
-               break;
        case 0x111d7666:
        case 0x111d7667:
        case 0x111d7668:
        case 0x111d7669:
        case 0x111d76e3:
-               spec->num_dmics = stac92xx_connected_ports(codec,
-                               stac92hd88xxx_dmic_nids,
-                               STAC92HD88XXX_NUM_DMICS);
-               spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
-               spec->pin_nids = stac92hd88xxx_pin_nids;
-               spec->mono_nid = 0;
-               spec->num_pwrs = 0;
-               break;
        case 0x111d7604:
        case 0x111d76d4:
        case 0x111d7605:
@@ -5466,9 +5475,6 @@ again:
                if (spec->board_config == STAC_92HD83XXX_PWR_REF)
                        break;
                spec->num_pwrs = 0;
-               spec->num_dmics = stac92xx_connected_ports(codec,
-                               stac92hd83xxx_dmic_nids,
-                               STAC92HD83XXX_NUM_DMICS);
                break;
        }
 
index 13cec1e5ced9634300e8ca4a50678f4e86eb6674..2ae8d29500a87722a86c6a02c59b78ea90acd93e 100644 (file)
@@ -341,9 +341,9 @@ static int snd_intel8x0m_codec_semaphore(struct intel8x0m *chip, unsigned int co
        return -EBUSY;
 }
  
-static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
-                                    unsigned short reg,
-                                    unsigned short val)
+static void snd_intel8x0m_codec_write(struct snd_ac97 *ac97,
+                                     unsigned short reg,
+                                     unsigned short val)
 {
        struct intel8x0m *chip = ac97->private_data;
        
@@ -354,8 +354,8 @@ static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
        iaputword(chip, reg + ac97->num * 0x80, val);
 }
 
-static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
-                                             unsigned short reg)
+static unsigned short snd_intel8x0m_codec_read(struct snd_ac97 *ac97,
+                                              unsigned short reg)
 {
        struct intel8x0m *chip = ac97->private_data;
        unsigned short res;
@@ -385,7 +385,7 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
 /*
  * DMA I/O
  */
-static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev) 
+static void snd_intel8x0m_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev)
 {
        int idx;
        u32 *bdbar = ichdev->bdbar;
@@ -437,7 +437,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
  *  Interrupt handler
  */
 
-static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ichdev)
+static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *ichdev)
 {
        unsigned long port = ichdev->reg_offset;
        int civ, i, step;
@@ -489,7 +489,7 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
        iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }
 
-static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
+static irqreturn_t snd_intel8x0m_interrupt(int irq, void *dev_id)
 {
        struct intel8x0m *chip = dev_id;
        struct ichdev *ichdev;
@@ -512,7 +512,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
        for (i = 0; i < chip->bdbars_count; i++) {
                ichdev = &chip->ichd[i];
                if (status & ichdev->int_sta_mask)
-                       snd_intel8x0_update(chip, ichdev);
+                       snd_intel8x0m_update(chip, ichdev);
        }
 
        /* ack them */
@@ -526,7 +526,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
  *  PCM part
  */
 
-static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int snd_intel8x0m_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct intel8x0m *chip = snd_pcm_substream_chip(substream);
        struct ichdev *ichdev = get_ichdev(substream);
@@ -561,18 +561,18 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
        return 0;
 }
 
-static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
+static int snd_intel8x0m_hw_params(struct snd_pcm_substream *substream,
                                  struct snd_pcm_hw_params *hw_params)
 {
        return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
-static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
+static int snd_intel8x0m_hw_free(struct snd_pcm_substream *substream)
 {
        return snd_pcm_lib_free_pages(substream);
 }
 
-static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t snd_intel8x0m_pcm_pointer(struct snd_pcm_substream *substream)
 {
        struct intel8x0m *chip = snd_pcm_substream_chip(substream);
        struct ichdev *ichdev = get_ichdev(substream);
@@ -600,7 +600,7 @@ static int snd_intel8x0m_pcm_prepare(struct snd_pcm_substream *substream)
        ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
        snd_ac97_write(ichdev->ac97, AC97_LINE1_RATE, runtime->rate);
        snd_ac97_write(ichdev->ac97, AC97_LINE1_LEVEL, 0);
-       snd_intel8x0_setup_periods(chip, ichdev);
+       snd_intel8x0m_setup_periods(chip, ichdev);
        return 0;
 }
 
@@ -682,22 +682,22 @@ static struct snd_pcm_ops snd_intel8x0m_playback_ops = {
        .open =         snd_intel8x0m_playback_open,
        .close =        snd_intel8x0m_playback_close,
        .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_intel8x0_hw_params,
-       .hw_free =      snd_intel8x0_hw_free,
+       .hw_params =    snd_intel8x0m_hw_params,
+       .hw_free =      snd_intel8x0m_hw_free,
        .prepare =      snd_intel8x0m_pcm_prepare,
-       .trigger =      snd_intel8x0_pcm_trigger,
-       .pointer =      snd_intel8x0_pcm_pointer,
+       .trigger =      snd_intel8x0m_pcm_trigger,
+       .pointer =      snd_intel8x0m_pcm_pointer,
 };
 
 static struct snd_pcm_ops snd_intel8x0m_capture_ops = {
        .open =         snd_intel8x0m_capture_open,
        .close =        snd_intel8x0m_capture_close,
        .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_intel8x0_hw_params,
-       .hw_free =      snd_intel8x0_hw_free,
+       .hw_params =    snd_intel8x0m_hw_params,
+       .hw_free =      snd_intel8x0m_hw_free,
        .prepare =      snd_intel8x0m_pcm_prepare,
-       .trigger =      snd_intel8x0_pcm_trigger,
-       .pointer =      snd_intel8x0_pcm_pointer,
+       .trigger =      snd_intel8x0m_pcm_trigger,
+       .pointer =      snd_intel8x0m_pcm_pointer,
 };
 
 
@@ -710,7 +710,7 @@ struct ich_pcm_table {
        int ac97_idx;
 };
 
-static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device,
+static int __devinit snd_intel8x0m_pcm1(struct intel8x0m *chip, int device,
                                       struct ich_pcm_table *rec)
 {
        struct snd_pcm *pcm;
@@ -759,7 +759,7 @@ static struct ich_pcm_table intel_pcms[] __devinitdata = {
        },
 };
 
-static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
+static int __devinit snd_intel8x0m_pcm(struct intel8x0m *chip)
 {
        int i, tblsize, device, err;
        struct ich_pcm_table *tbl, *rec;
@@ -791,7 +791,7 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
                        if (! chip->ichd[rec->ac97_idx].ac97)
                                continue;
                }
-               err = snd_intel8x0_pcm1(chip, device, rec);
+               err = snd_intel8x0m_pcm1(chip, device, rec);
                if (err < 0)
                        return err;
                device++;
@@ -806,20 +806,20 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
  *  Mixer part
  */
 
-static void snd_intel8x0_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
+static void snd_intel8x0m_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
 {
        struct intel8x0m *chip = bus->private_data;
        chip->ac97_bus = NULL;
 }
 
-static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97)
+static void snd_intel8x0m_mixer_free_ac97(struct snd_ac97 *ac97)
 {
        struct intel8x0m *chip = ac97->private_data;
        chip->ac97 = NULL;
 }
 
 
-static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock)
+static int __devinit snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock)
 {
        struct snd_ac97_bus *pbus;
        struct snd_ac97_template ac97;
@@ -827,22 +827,22 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock)
        int err;
        unsigned int glob_sta = 0;
        static struct snd_ac97_bus_ops ops = {
-               .write = snd_intel8x0_codec_write,
-               .read = snd_intel8x0_codec_read,
+               .write = snd_intel8x0m_codec_write,
+               .read = snd_intel8x0m_codec_read,
        };
 
        chip->in_ac97_init = 1;
        
        memset(&ac97, 0, sizeof(ac97));
        ac97.private_data = chip;
-       ac97.private_free = snd_intel8x0_mixer_free_ac97;
+       ac97.private_free = snd_intel8x0m_mixer_free_ac97;
        ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
 
        glob_sta = igetdword(chip, ICHREG(GLOB_STA));
 
        if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
                goto __err;
-       pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
+       pbus->private_free = snd_intel8x0m_mixer_free_ac97_bus;
        if (ac97_clock >= 8000 && ac97_clock <= 48000)
                pbus->clock = ac97_clock;
        chip->ac97_bus = pbus;
@@ -894,7 +894,8 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
        /* finish cold or do warm reset */
        cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
        iputdword(chip, ICHREG(GLOB_CNT), cnt);
-       end_time = (jiffies + (HZ / 4)) + 1;
+       usleep_range(500, 1000); /* give warm reset some time */
+       end_time = jiffies + HZ / 4;
        do {
                if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
                        goto __ok;
@@ -959,7 +960,7 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
        return 0;
 }
 
-static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing)
+static int snd_intel8x0m_chip_init(struct intel8x0m *chip, int probing)
 {
        unsigned int i;
        int err;
@@ -980,7 +981,7 @@ static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing)
        return 0;
 }
 
-static int snd_intel8x0_free(struct intel8x0m *chip)
+static int snd_intel8x0m_free(struct intel8x0m *chip)
 {
        unsigned int i;
 
@@ -1045,7 +1046,7 @@ static int intel8x0m_resume(struct pci_dev *pci)
                return -EIO;
        }
        pci_set_master(pci);
-       if (request_irq(pci->irq, snd_intel8x0_interrupt,
+       if (request_irq(pci->irq, snd_intel8x0m_interrupt,
                        IRQF_SHARED, card->shortname, chip)) {
                printk(KERN_ERR "intel8x0m: unable to grab IRQ %d, "
                       "disabling device\n", pci->irq);
@@ -1053,7 +1054,7 @@ static int intel8x0m_resume(struct pci_dev *pci)
                return -EIO;
        }
        chip->irq = pci->irq;
-       snd_intel8x0_chip_init(chip, 0);
+       snd_intel8x0m_chip_init(chip, 0);
        snd_ac97_resume(chip->ac97);
 
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -1094,10 +1095,10 @@ static void __devinit snd_intel8x0m_proc_init(struct intel8x0m * chip)
 #endif /* CONFIG_PROC_FS */
 
 
-static int snd_intel8x0_dev_free(struct snd_device *device)
+static int snd_intel8x0m_dev_free(struct snd_device *device)
 {
        struct intel8x0m *chip = device->device_data;
-       return snd_intel8x0_free(chip);
+       return snd_intel8x0m_free(chip);
 }
 
 struct ich_reg_info {
@@ -1108,7 +1109,7 @@ struct ich_reg_info {
 static int __devinit snd_intel8x0m_create(struct snd_card *card,
                                         struct pci_dev *pci,
                                         unsigned long device_type,
-                                        struct intel8x0m ** r_intel8x0)
+                                        struct intel8x0m **r_intel8x0m)
 {
        struct intel8x0m *chip;
        int err;
@@ -1116,7 +1117,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
        unsigned int int_sta_masks;
        struct ichdev *ichdev;
        static struct snd_device_ops ops = {
-               .dev_free =     snd_intel8x0_dev_free,
+               .dev_free =     snd_intel8x0m_dev_free,
        };
        static struct ich_reg_info intel_regs[2] = {
                { ICH_MIINT, 0 },
@@ -1124,7 +1125,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
        };
        struct ich_reg_info *tbl;
 
-       *r_intel8x0 = NULL;
+       *r_intel8x0m = NULL;
 
        if ((err = pci_enable_device(pci)) < 0)
                return err;
@@ -1158,7 +1159,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
                chip->addr = pci_iomap(pci, 0, 0);
        if (!chip->addr) {
                snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
-               snd_intel8x0_free(chip);
+               snd_intel8x0m_free(chip);
                return -EIO;
        }
        if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
@@ -1167,15 +1168,15 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
                chip->bmaddr = pci_iomap(pci, 1, 0);
        if (!chip->bmaddr) {
                snd_printk(KERN_ERR "Controller space ioremap problem\n");
-               snd_intel8x0_free(chip);
+               snd_intel8x0m_free(chip);
                return -EIO;
        }
 
  port_inited:
-       if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED,
+       if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
                        card->shortname, chip)) {
                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
-               snd_intel8x0_free(chip);
+               snd_intel8x0m_free(chip);
                return -EBUSY;
        }
        chip->irq = pci->irq;
@@ -1210,7 +1211,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
        if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
                                chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
                                &chip->bdbars) < 0) {
-               snd_intel8x0_free(chip);
+               snd_intel8x0m_free(chip);
                return -ENOMEM;
        }
        /* tables must be aligned to 8 bytes here, but the kernel pages
@@ -1225,19 +1226,19 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
        chip->int_sta_reg = ICH_REG_GLOB_STA;
        chip->int_sta_mask = int_sta_masks;
 
-       if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
-               snd_intel8x0_free(chip);
+       if ((err = snd_intel8x0m_chip_init(chip, 1)) < 0) {
+               snd_intel8x0m_free(chip);
                return err;
        }
 
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-               snd_intel8x0_free(chip);
+               snd_intel8x0m_free(chip);
                return err;
        }
 
        snd_card_set_dev(card, &pci->dev);
 
-       *r_intel8x0 = chip;
+       *r_intel8x0m = chip;
        return 0;
 }
 
@@ -1295,11 +1296,11 @@ static int __devinit snd_intel8x0m_probe(struct pci_dev *pci,
        }
        card->private_data = chip;
 
-       if ((err = snd_intel8x0_mixer(chip, ac97_clock)) < 0) {
+       if ((err = snd_intel8x0m_mixer(chip, ac97_clock)) < 0) {
                snd_card_free(card);
                return err;
        }
-       if ((err = snd_intel8x0_pcm(chip)) < 0) {
+       if ((err = snd_intel8x0m_pcm(chip)) < 0) {
                snd_card_free(card);
                return err;
        }
index f5eadfc0672ab0073db93f8fe2b484069b42c814..a323eafb9e039b42a02b13139138b9101415a0d8 100644 (file)
@@ -8,6 +8,21 @@
  *      Modified 2006-06-01 for AES32 support by Remy Bruno
  *                                               <remy.bruno@trinnov.com>
  *
+ *      Modified 2009-04-13 for proper metering by Florian Faber
+ *                                               <faber@faberman.de>
+ *
+ *      Modified 2009-04-14 for native float support by Florian Faber
+ *                                               <faber@faberman.de>
+ *
+ *      Modified 2009-04-26 fixed bug in rms metering by Florian Faber
+ *                                               <faber@faberman.de>
+ *
+ *      Modified 2009-04-30 added hw serial number support by Florian Faber
+ *
+ *      Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
+ *
+ *     Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
+ *
  *   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
@@ -35,6 +50,7 @@
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <sound/info.h>
 #include <sound/asoundef.h>
 #include <sound/rawmidi.h>
@@ -47,15 +63,6 @@ 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;/* Enable this card */
 
-/* Disable precise pointer at start */
-static int precise_ptr[SNDRV_CARDS];
-
-/* Send all playback to line outs */
-static int line_outs_monitor[SNDRV_CARDS];
-
-/* Enable Analog Outs on Channel 63/64 by default */
-static int enable_monitor[SNDRV_CARDS];
-
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
 
@@ -65,42 +72,39 @@ MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
 
-module_param_array(precise_ptr, bool, NULL, 0444);
-MODULE_PARM_DESC(precise_ptr, "Enable or disable precise pointer.");
-
-module_param_array(line_outs_monitor, bool, NULL, 0444);
-MODULE_PARM_DESC(line_outs_monitor,
-                "Send playback streams to analog outs by default.");
-
-module_param_array(enable_monitor, bool, NULL, 0444);
-MODULE_PARM_DESC(enable_monitor,
-                "Enable Analog Out on Channel 63/64 by default.");
 
 MODULE_AUTHOR
-      ("Winfried Ritsch <ritsch_AT_iem.at>, "
-       "Paul Davis <paul@linuxaudiosystems.com>, "
-       "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
-       "Remy Bruno <remy.bruno@trinnov.com>");
+(
+       "Winfried Ritsch <ritsch_AT_iem.at>, "
+       "Paul Davis <paul@linuxaudiosystems.com>, "
+       "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, "
+       "Remy Bruno <remy.bruno@trinnov.com>, "
+       "Florian Faber <faberman@linuxproaudio.org>, "
+       "Adrian Knoth <adi@drcomp.erfurt.thur.de>"
+);
 MODULE_DESCRIPTION("RME HDSPM");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 
-/* --- Write registers. --- 
+/* --- Write registers. ---
   These are defined as byte-offsets from the iobase value.  */
 
+#define HDSPM_WR_SETTINGS             0
+#define HDSPM_outputBufferAddress    32
+#define HDSPM_inputBufferAddress     36
 #define HDSPM_controlRegister       64
 #define HDSPM_interruptConfirmation  96
 #define HDSPM_control2Reg           256  /* not in specs ???????? */
 #define HDSPM_freqReg                256  /* for AES32 */
-#define HDSPM_midiDataOut0          352  /* just believe in old code */
-#define HDSPM_midiDataOut1          356
+#define HDSPM_midiDataOut0          352  /* just believe in old code */
+#define HDSPM_midiDataOut1          356
 #define HDSPM_eeprom_wr                     384  /* for AES32 */
 
 /* DMA enable for 64 channels, only Bit 0 is relevant */
-#define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */ 
+#define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */
 #define HDSPM_inputEnableBase        768  /* 768-1023 output DMA */
 
-/* 16 page addresses for each of the 64 channels DMA buffer in and out 
+/* 16 page addresses for each of the 64 channels DMA buffer in and out
    (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
 #define HDSPM_pageAddressBufferOut       8192
 #define HDSPM_pageAddressBufferIn        (HDSPM_pageAddressBufferOut+64*16*4)
@@ -119,22 +123,84 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_statusRegister2  192
 #define HDSPM_timecodeRegister 128
 
+/* AIO, RayDAT */
+#define HDSPM_RD_STATUS_0 0
+#define HDSPM_RD_STATUS_1 64
+#define HDSPM_RD_STATUS_2 128
+#define HDSPM_RD_STATUS_3 192
+
+#define HDSPM_RD_TCO           256
+#define HDSPM_RD_PLL_FREQ      512
+#define HDSPM_WR_TCO           128
+
+#define HDSPM_TCO1_TCO_lock                    0x00000001
+#define HDSPM_TCO1_WCK_Input_Range_LSB         0x00000002
+#define HDSPM_TCO1_WCK_Input_Range_MSB         0x00000004
+#define HDSPM_TCO1_LTC_Input_valid             0x00000008
+#define HDSPM_TCO1_WCK_Input_valid             0x00000010
+#define HDSPM_TCO1_Video_Input_Format_NTSC     0x00000020
+#define HDSPM_TCO1_Video_Input_Format_PAL      0x00000040
+
+#define HDSPM_TCO1_set_TC                      0x00000100
+#define HDSPM_TCO1_set_drop_frame_flag         0x00000200
+#define HDSPM_TCO1_LTC_Format_LSB              0x00000400
+#define HDSPM_TCO1_LTC_Format_MSB              0x00000800
+
+#define HDSPM_TCO2_TC_run                      0x00010000
+#define HDSPM_TCO2_WCK_IO_ratio_LSB            0x00020000
+#define HDSPM_TCO2_WCK_IO_ratio_MSB            0x00040000
+#define HDSPM_TCO2_set_num_drop_frames_LSB     0x00080000
+#define HDSPM_TCO2_set_num_drop_frames_MSB     0x00100000
+#define HDSPM_TCO2_set_jam_sync                        0x00200000
+#define HDSPM_TCO2_set_flywheel                        0x00400000
+
+#define HDSPM_TCO2_set_01_4                    0x01000000
+#define HDSPM_TCO2_set_pull_down               0x02000000
+#define HDSPM_TCO2_set_pull_up                 0x04000000
+#define HDSPM_TCO2_set_freq                    0x08000000
+#define HDSPM_TCO2_set_term_75R                        0x10000000
+#define HDSPM_TCO2_set_input_LSB               0x20000000
+#define HDSPM_TCO2_set_input_MSB               0x40000000
+#define HDSPM_TCO2_set_freq_from_app           0x80000000
+
+
+#define HDSPM_midiDataOut0    352
+#define HDSPM_midiDataOut1    356
+#define HDSPM_midiDataOut2    368
+
 #define HDSPM_midiDataIn0     360
 #define HDSPM_midiDataIn1     364
+#define HDSPM_midiDataIn2     372
+#define HDSPM_midiDataIn3     376
 
 /* status is data bytes in MIDI-FIFO (0-128) */
-#define HDSPM_midiStatusOut0  384      
-#define HDSPM_midiStatusOut1  388      
-#define HDSPM_midiStatusIn0   392      
-#define HDSPM_midiStatusIn1   396      
+#define HDSPM_midiStatusOut0  384
+#define HDSPM_midiStatusOut1  388
+#define HDSPM_midiStatusOut2  400
+
+#define HDSPM_midiStatusIn0   392
+#define HDSPM_midiStatusIn1   396
+#define HDSPM_midiStatusIn2   404
+#define HDSPM_midiStatusIn3   408
 
 
 /* the meters are regular i/o-mapped registers, but offset
    considerably from the rest. the peak registers are reset
-   when read; the least-significant 4 bits are full-scale counters; 
+   when read; the least-significant 4 bits are full-scale counters;
    the actual peak value is in the most-significant 24 bits.
 */
-#define HDSPM_MADI_peakrmsbase         4096    /* 4096-8191 2x64x32Bit Meters */
+
+#define HDSPM_MADI_INPUT_PEAK          4096
+#define HDSPM_MADI_PLAYBACK_PEAK       4352
+#define HDSPM_MADI_OUTPUT_PEAK         4608
+
+#define HDSPM_MADI_INPUT_RMS_L         6144
+#define HDSPM_MADI_PLAYBACK_RMS_L      6400
+#define HDSPM_MADI_OUTPUT_RMS_L                6656
+
+#define HDSPM_MADI_INPUT_RMS_H         7168
+#define HDSPM_MADI_PLAYBACK_RMS_H      7424
+#define HDSPM_MADI_OUTPUT_RMS_H                7680
 
 /* --- Control Register bits --------- */
 #define HDSPM_Start                (1<<0) /* start engine */
@@ -143,7 +209,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_Latency1             (1<<2) /* where n is defined */
 #define HDSPM_Latency2             (1<<3) /* by Latency{2,1,0} */
 
-#define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Slave/Autosync */
+#define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Autosync */
+#define HDSPM_c0Master         0x1    /* Master clock bit in settings
+                                         register [RayDAT, AIO] */
 
 #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
 
@@ -157,7 +225,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
                                     56channelMODE=0 */ /* MADI ONLY*/
 #define HDSPM_Emphasis    (1<<10) /* Emphasis */ /* AES32 ONLY */
 
-#define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode, 
+#define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode,
                                      0=off, 1=on  */ /* MADI ONLY */
 #define HDSPM_Dolby       (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */
 
@@ -166,22 +234,23 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
                                    */
 #define HDSPM_InputSelect1 (1<<15) /* should be 0 */
 
-#define HDSPM_SyncRef0     (1<<16) /* 0=WOrd, 1=MADI */
-#define HDSPM_SyncRef1     (1<<17) /* for AES32: SyncRefN codes the AES # */
 #define HDSPM_SyncRef2     (1<<13)
 #define HDSPM_SyncRef3     (1<<25)
 
 #define HDSPM_SMUX         (1<<18) /* Frame ??? */ /* MADI ONY */
-#define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use 
+#define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use
                                       AES additional bits in
                                      lower 5 Audiodatabits ??? */
 #define HDSPM_taxi_reset   (1<<20) /* ??? */ /* MADI ONLY ? */
 #define HDSPM_WCK48        (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */
 
-#define HDSPM_Midi0InterruptEnable (1<<22)
-#define HDSPM_Midi1InterruptEnable (1<<23)
+#define HDSPM_Midi0InterruptEnable 0x0400000
+#define HDSPM_Midi1InterruptEnable 0x0800000
+#define HDSPM_Midi2InterruptEnable 0x0200000
+#define HDSPM_Midi3InterruptEnable 0x4000000
 
 #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
+#define HDSPe_FLOAT_FORMAT         0x2000000
 
 #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */
 #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */
@@ -198,11 +267,18 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_InputCoaxial   (HDSPM_InputSelect0)
 #define HDSPM_SyncRefMask    (HDSPM_SyncRef0|HDSPM_SyncRef1|\
                              HDSPM_SyncRef2|HDSPM_SyncRef3)
-#define HDSPM_SyncRef_Word   0
-#define HDSPM_SyncRef_MADI   (HDSPM_SyncRef0)
 
-#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */
-#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */
+#define HDSPM_c0_SyncRef0      0x2
+#define HDSPM_c0_SyncRef1      0x4
+#define HDSPM_c0_SyncRef2      0x8
+#define HDSPM_c0_SyncRef3      0x10
+#define HDSPM_c0_SyncRefMask   (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\
+                               HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3)
+
+#define HDSPM_SYNC_FROM_WORD    0      /* Preferred sync reference */
+#define HDSPM_SYNC_FROM_MADI    1      /* choices - used by "pref_sync_ref" */
+#define HDSPM_SYNC_FROM_TCO     2
+#define HDSPM_SYNC_FROM_SYNC_IN 3
 
 #define HDSPM_Frequency32KHz    HDSPM_Frequency0
 #define HDSPM_Frequency44_1KHz  HDSPM_Frequency1
@@ -216,17 +292,6 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_Frequency192KHz   (HDSPM_QuadSpeed|HDSPM_Frequency1|\
                                 HDSPM_Frequency0)
 
-/* --- for internal discrimination */
-#define HDSPM_CLOCK_SOURCE_AUTOSYNC          0 /* Sample Clock Sources */
-#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ    1
-#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ  2
-#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ    3
-#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ    4
-#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ  5
-#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ    6
-#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ   7
-#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8
-#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ   9
 
 /* Synccheck Status */
 #define HDSPM_SYNC_CHECK_NO_LOCK 0
@@ -236,14 +301,16 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 /* AutoSync References - used by "autosync_ref" control switch */
 #define HDSPM_AUTOSYNC_FROM_WORD      0
 #define HDSPM_AUTOSYNC_FROM_MADI      1
-#define HDSPM_AUTOSYNC_FROM_NONE      2
+#define HDSPM_AUTOSYNC_FROM_TCO       2
+#define HDSPM_AUTOSYNC_FROM_SYNC_IN   3
+#define HDSPM_AUTOSYNC_FROM_NONE      4
 
 /* Possible sources of MADI input */
 #define HDSPM_OPTICAL 0                /* optical   */
 #define HDSPM_COAXIAL 1                /* BNC */
 
 #define hdspm_encode_latency(x)       (((x)<<1) & HDSPM_LatencyMask)
-#define hdspm_decode_latency(x)       (((x) & HDSPM_LatencyMask)>>1)
+#define hdspm_decode_latency(x)       ((((x) & HDSPM_LatencyMask)>>1))
 
 #define hdspm_encode_in(x) (((x)&0x3)<<14)
 #define hdspm_decode_in(x) (((x)>>14)&0x3)
@@ -270,13 +337,21 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_AB_int             (1<<2)        /* InputChannel Opt=0, Coax=1
                                         * (like inp0)
                                         */
+
 #define HDSPM_madiLock           (1<<3)        /* MADI Locked =1, no=0 */
+#define HDSPM_madiSync          (1<<18) /* MADI is in sync */
+
+#define HDSPM_tcoLock    0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */
+#define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status */
+
+#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */
+#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */
 
 #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
-                                           /* since 64byte accurate last 6 bits 
-                                              are not used */
+                       /* since 64byte accurate, last 6 bits are not used */
+
+
 
-#define HDSPM_madiSync          (1<<18) /* MADI is in sync */
 #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
 
 #define HDSPM_madiFreq0         (1<<22)        /* system freq 0=error */
@@ -287,8 +362,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_BufferID          (1<<26)        /* (Double)Buffer ID toggles with
                                         * Interrupt
                                         */
-#define HDSPM_midi0IRQPending   (1<<30)        /* MIDI IRQ is pending  */
-#define HDSPM_midi1IRQPending   (1<<31)        /* and aktiv */
+#define HDSPM_tco_detect         0x08000000
+#define HDSPM_tco_lock          0x20000000
+
+#define HDSPM_s2_tco_detect      0x00000040
+#define HDSPM_s2_AEBO_D          0x00000080
+#define HDSPM_s2_AEBI_D          0x00000100
+
+
+#define HDSPM_midi0IRQPending    0x40000000
+#define HDSPM_midi1IRQPending    0x80000000
+#define HDSPM_midi2IRQPending    0x20000000
+#define HDSPM_midi2IRQPendingAES 0x00000020
+#define HDSPM_midi3IRQPending    0x00200000
 
 /* --- status bit helpers */
 #define HDSPM_madiFreqMask  (HDSPM_madiFreq0|HDSPM_madiFreq1|\
@@ -317,7 +403,10 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_wc_freq2 (1<<7)  /* 100=64, 101=88.2, 110=96, */
 /* missing Bit   for               111=128, 1000=176.4, 1001=192 */
 
-#define HDSPM_SelSyncRef0 (1<<8)       /* Sync Source in slave mode */
+#define HDSPM_SyncRef0 0x10000  /* Sync Reference */
+#define HDSPM_SyncRef1 0x20000
+
+#define HDSPM_SelSyncRef0 (1<<8)       /* AutoSync Source */
 #define HDSPM_SelSyncRef1 (1<<9)       /* 000=word, 001=MADI, */
 #define HDSPM_SelSyncRef2 (1<<10)      /* 111=no valid signal */
 
@@ -331,11 +420,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_wcFreq88_2  (HDSPM_wc_freq0|HDSPM_wc_freq2)
 #define HDSPM_wcFreq96    (HDSPM_wc_freq1|HDSPM_wc_freq2)
 
+#define HDSPM_status1_F_0 0x0400000
+#define HDSPM_status1_F_1 0x0800000
+#define HDSPM_status1_F_2 0x1000000
+#define HDSPM_status1_F_3 0x2000000
+#define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3)
+
 
 #define HDSPM_SelSyncRefMask       (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
                                    HDSPM_SelSyncRef2)
 #define HDSPM_SelSyncRef_WORD      0
 #define HDSPM_SelSyncRef_MADI      (HDSPM_SelSyncRef0)
+#define HDSPM_SelSyncRef_TCO       (HDSPM_SelSyncRef1)
+#define HDSPM_SelSyncRef_SyncIn    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1)
 #define HDSPM_SelSyncRef_NVALID    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\
                                    HDSPM_SelSyncRef2)
 
@@ -345,7 +442,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 /* status */
 #define HDSPM_AES32_wcLock     0x0200000
 #define HDSPM_AES32_wcFreq_bit  22
-/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function 
+/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function
   HDSPM_bit2freq */
 #define HDSPM_AES32_syncref_bit  16
 /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */
@@ -398,28 +495,348 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define MADI_DS_CHANNELS       32
 #define MADI_QS_CHANNELS       16
 
+#define RAYDAT_SS_CHANNELS     36
+#define RAYDAT_DS_CHANNELS     20
+#define RAYDAT_QS_CHANNELS     12
+
+#define AIO_IN_SS_CHANNELS        14
+#define AIO_IN_DS_CHANNELS        10
+#define AIO_IN_QS_CHANNELS        8
+#define AIO_OUT_SS_CHANNELS        16
+#define AIO_OUT_DS_CHANNELS        12
+#define AIO_OUT_QS_CHANNELS        10
+
+#define AES32_CHANNELS         16
+
 /* the size of a substream (1 mono data stream) */
 #define HDSPM_CHANNEL_BUFFER_SAMPLES  (16*1024)
 #define HDSPM_CHANNEL_BUFFER_BYTES    (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
 
 /* the size of the area we need to allocate for DMA transfers. the
    size is the same regardless of the number of channels, and
-   also the latency to use. 
+   also the latency to use.
    for one direction !!!
 */
 #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
 #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
 
 /* revisions >= 230 indicate AES32 card */
-#define HDSPM_AESREVISION 230
+#define HDSPM_MADI_REV         210
+#define HDSPM_RAYDAT_REV       211
+#define HDSPM_AIO_REV          212
+#define HDSPM_MADIFACE_REV     213
+#define HDSPM_AES_REV          240
+#define HDSPM_AES32_REV                234
+#define HDSPM_AES32_OLD_REV    233
 
 /* speed factor modes */
 #define HDSPM_SPEED_SINGLE 0
 #define HDSPM_SPEED_DOUBLE 1
 #define HDSPM_SPEED_QUAD   2
+
 /* names for speed modes */
 static char *hdspm_speed_names[] = { "single", "double", "quad" };
 
+static char *texts_autosync_aes_tco[] = { "Word Clock",
+                                         "AES1", "AES2", "AES3", "AES4",
+                                         "AES5", "AES6", "AES7", "AES8",
+                                         "TCO" };
+static char *texts_autosync_aes[] = { "Word Clock",
+                                     "AES1", "AES2", "AES3", "AES4",
+                                     "AES5", "AES6", "AES7", "AES8" };
+static char *texts_autosync_madi_tco[] = { "Word Clock",
+                                          "MADI", "TCO", "Sync In" };
+static char *texts_autosync_madi[] = { "Word Clock",
+                                      "MADI", "Sync In" };
+
+static char *texts_autosync_raydat_tco[] = {
+       "Word Clock",
+       "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
+       "AES", "SPDIF", "TCO", "Sync In"
+};
+static char *texts_autosync_raydat[] = {
+       "Word Clock",
+       "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
+       "AES", "SPDIF", "Sync In"
+};
+static char *texts_autosync_aio_tco[] = {
+       "Word Clock",
+       "ADAT", "AES", "SPDIF", "TCO", "Sync In"
+};
+static char *texts_autosync_aio[] = { "Word Clock",
+                                     "ADAT", "AES", "SPDIF", "Sync In" };
+
+static char *texts_freq[] = {
+       "No Lock",
+       "32 kHz",
+       "44.1 kHz",
+       "48 kHz",
+       "64 kHz",
+       "88.2 kHz",
+       "96 kHz",
+       "128 kHz",
+       "176.4 kHz",
+       "192 kHz"
+};
+
+static char *texts_ports_madi[] = {
+       "MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6",
+       "MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12",
+       "MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18",
+       "MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24",
+       "MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30",
+       "MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36",
+       "MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42",
+       "MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48",
+       "MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54",
+       "MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60",
+       "MADI.61", "MADI.62", "MADI.63", "MADI.64",
+};
+
+
+static char *texts_ports_raydat_ss[] = {
+       "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6",
+       "ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
+       "ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2",
+       "ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8",
+       "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6",
+       "ADAT4.7", "ADAT4.8",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R"
+};
+
+static char *texts_ports_raydat_ds[] = {
+       "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4",
+       "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4",
+       "ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4",
+       "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R"
+};
+
+static char *texts_ports_raydat_qs[] = {
+       "ADAT1.1", "ADAT1.2",
+       "ADAT2.1", "ADAT2.2",
+       "ADAT3.1", "ADAT3.2",
+       "ADAT4.1", "ADAT4.2",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R"
+};
+
+
+static char *texts_ports_aio_in_ss[] = {
+       "Analogue.L", "Analogue.R",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R",
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
+       "ADAT.7", "ADAT.8"
+};
+
+static char *texts_ports_aio_out_ss[] = {
+       "Analogue.L", "Analogue.R",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R",
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
+       "ADAT.7", "ADAT.8",
+       "Phone.L", "Phone.R"
+};
+
+static char *texts_ports_aio_in_ds[] = {
+       "Analogue.L", "Analogue.R",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R",
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
+};
+
+static char *texts_ports_aio_out_ds[] = {
+       "Analogue.L", "Analogue.R",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R",
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+       "Phone.L", "Phone.R"
+};
+
+static char *texts_ports_aio_in_qs[] = {
+       "Analogue.L", "Analogue.R",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R",
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
+};
+
+static char *texts_ports_aio_out_qs[] = {
+       "Analogue.L", "Analogue.R",
+       "AES.L", "AES.R",
+       "SPDIF.L", "SPDIF.R",
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+       "Phone.L", "Phone.R"
+};
+
+static char *texts_ports_aes32[] = {
+       "AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7",
+       "AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14",
+       "AES.15", "AES.16"
+};
+
+/* These tables map the ALSA channels 1..N to the channels that we
+   need to use in order to find the relevant channel buffer. RME
+   refers to this kind of mapping as between "the ADAT channel and
+   the DMA channel." We index it using the logical audio channel,
+   and the value is the DMA channel (i.e. channel buffer number)
+   where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = {
+       0, 1, 2, 3, 4, 5, 6, 7,
+       8, 9, 10, 11, 12, 13, 14, 15,
+       16, 17, 18, 19, 20, 21, 22, 23,
+       24, 25, 26, 27, 28, 29, 30, 31,
+       32, 33, 34, 35, 36, 37, 38, 39,
+       40, 41, 42, 43, 44, 45, 46, 47,
+       48, 49, 50, 51, 52, 53, 54, 55,
+       56, 57, 58, 59, 60, 61, 62, 63
+};
+
+static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = {
+       4, 5, 6, 7, 8, 9, 10, 11,       /* ADAT 1 */
+       12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */
+       20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */
+       28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */
+       0, 1,                   /* AES */
+       2, 3,                   /* SPDIF */
+       -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = {
+       4, 5, 6, 7,             /* ADAT 1 */
+       8, 9, 10, 11,           /* ADAT 2 */
+       12, 13, 14, 15,         /* ADAT 3 */
+       16, 17, 18, 19,         /* ADAT 4 */
+       0, 1,                   /* AES */
+       2, 3,                   /* SPDIF */
+       -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = {
+       4, 5,                   /* ADAT 1 */
+       6, 7,                   /* ADAT 2 */
+       8, 9,                   /* ADAT 3 */
+       10, 11,                 /* ADAT 4 */
+       0, 1,                   /* AES */
+       2, 3,                   /* SPDIF */
+       -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
+       0, 1,                   /* line in */
+       8, 9,                   /* aes in, */
+       10, 11,                 /* spdif in */
+       12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */
+       -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
+       0, 1,                   /* line out */
+       8, 9,                   /* aes out */
+       10, 11,                 /* spdif out */
+       12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */
+       6, 7,                   /* phone out */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
+       0, 1,                   /* line in */
+       8, 9,                   /* aes in */
+       10, 11,                 /* spdif in */
+       12, 14, 16, 18,         /* adat in */
+       -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
+       0, 1,                   /* line out */
+       8, 9,                   /* aes out */
+       10, 11,                 /* spdif out */
+       12, 14, 16, 18,         /* adat out */
+       6, 7,                   /* phone out */
+       -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
+       0, 1,                   /* line in */
+       8, 9,                   /* aes in */
+       10, 11,                 /* spdif in */
+       12, 16,                 /* adat in */
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
+       0, 1,                   /* line out */
+       8, 9,                   /* aes out */
+       10, 11,                 /* spdif out */
+       12, 16,                 /* adat out */
+       6, 7,                   /* phone out */
+       -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_aes32[HDSPM_MAX_CHANNELS] = {
+       0, 1, 2, 3, 4, 5, 6, 7,
+       8, 9, 10, 11, 12, 13, 14, 15,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1
+};
+
 struct hdspm_midi {
        struct hdspm *hdspm;
        int id;
@@ -430,6 +847,21 @@ struct hdspm_midi {
        struct timer_list timer;
        spinlock_t lock;
        int pending;
+       int dataIn;
+       int statusIn;
+       int dataOut;
+       int statusOut;
+       int ie;
+       int irq;
+};
+
+struct hdspm_tco {
+       int input;
+       int framerate;
+       int wordclock;
+       int samplerate;
+       int pull;
+       int term; /* 0 = off, 1 = on */
 };
 
 struct hdspm {
@@ -441,21 +873,39 @@ struct hdspm {
        char *card_name;             /* for procinfo */
        unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/
 
-       unsigned char is_aes32;    /* indicates if card is AES32 */
+       uint8_t io_type;
 
-       int precise_ptr;        /* use precise pointers, to be tested */
        int monitor_outs;       /* set up monitoring outs init flag */
 
        u32 control_register;   /* cached value */
        u32 control2_register;  /* cached value */
+       u32 settings_register;
 
-       struct hdspm_midi midi[2];
+       struct hdspm_midi midi[4];
        struct tasklet_struct midi_tasklet;
 
        size_t period_bytes;
-       unsigned char ss_channels;      /* channels of card in single speed */
-       unsigned char ds_channels;      /* Double Speed */
-       unsigned char qs_channels;      /* Quad Speed */
+       unsigned char ss_in_channels;
+       unsigned char ds_in_channels;
+       unsigned char qs_in_channels;
+       unsigned char ss_out_channels;
+       unsigned char ds_out_channels;
+       unsigned char qs_out_channels;
+
+       unsigned char max_channels_in;
+       unsigned char max_channels_out;
+
+       char *channel_map_in;
+       char *channel_map_out;
+
+       char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs;
+       char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs;
+
+       char **port_names_in;
+       char **port_names_out;
+
+       char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs;
+       char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs;
 
        unsigned char *playback_buffer; /* suitably aligned address */
        unsigned char *capture_buffer;  /* suitably aligned address */
@@ -468,14 +918,13 @@ struct hdspm {
        int last_internal_sample_rate;
        int system_sample_rate;
 
-       char *channel_map;      /* channel map for DS and Quadspeed */
-
        int dev;                /* Hardware vars... */
        int irq;
        unsigned long port;
        void __iomem *iobase;
 
        int irq_count;          /* for debug */
+       int midiPorts;
 
        struct snd_card *card;  /* one card */
        struct snd_pcm *pcm;            /* has one pcm */
@@ -487,28 +936,17 @@ struct hdspm {
        struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS];
        /* but input to much, so not used */
        struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS];
-       /* full mixer accessible over mixer ioctl or hwdep-device */
+       /* full mixer accessable over mixer ioctl or hwdep-device */
        struct hdspm_mixer *mixer;
 
-};
+       struct hdspm_tco *tco;  /* NULL if no TCO detected */
 
-/* These tables map the ALSA channels 1..N to the channels that we
-   need to use in order to find the relevant channel buffer. RME
-   refer to this kind of mapping as between "the ADAT channel and
-   the DMA channel." We index it using the logical audio channel,
-   and the value is the DMA channel (i.e. channel buffer number)
-   where the data for that channel can be read/written from/to.
-*/
+       char **texts_autosync;
+       int texts_autosync_items;
+
+       cycles_t last_interrupt;
 
-static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = {
-   0, 1, 2, 3, 4, 5, 6, 7,
-   8, 9, 10, 11, 12, 13, 14, 15,
-   16, 17, 18, 19, 20, 21, 22, 23,
-   24, 25, 26, 27, 28, 29, 30, 31,
-   32, 33, 34, 35, 36, 37, 38, 39,
-   40, 41, 42, 43, 44, 45, 46, 47,
-   48, 49, 50, 51, 52, 53, 54, 55,
-   56, 57, 58, 59, 60, 61, 62, 63
+       struct hdspm_peak_rms peak_rms;
 };
 
 
@@ -532,11 +970,11 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
 static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
                                          struct hdspm * hdspm);
 
-static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm);
-static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm);
-static int hdspm_autosync_ref(struct hdspm * hdspm);
-static int snd_hdspm_set_defaults(struct hdspm * hdspm);
-static void hdspm_set_sgbuf(struct hdspm * hdspm,
+static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
+static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
+static int hdspm_autosync_ref(struct hdspm *hdspm);
+static int snd_hdspm_set_defaults(struct hdspm *hdspm);
+static void hdspm_set_sgbuf(struct hdspm *hdspm,
                            struct snd_pcm_substream *substream,
                             unsigned int reg, int channels);
 
@@ -550,7 +988,7 @@ static inline int HDSPM_bit2freq(int n)
        return bit2freq_tab[n];
 }
 
-/* Write/read to/from HDSPM with Addresses in Bytes
+/* Write/read to/from HDSPM with Adresses in Bytes
    not words but only 32Bit writes are allowed */
 
 static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg,
@@ -564,8 +1002,8 @@ static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg)
        return readl(hdspm->iobase + reg);
 }
 
-/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader 
-   mixer is write only on hardware so we have to cache him for read 
+/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader
+   mixer is write only on hardware so we have to cache him for read
    each fader is a u32, but uses only the first 16 bit */
 
 static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan,
@@ -641,30 +1079,67 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
 /* check for external sample rate */
 static int hdspm_external_sample_rate(struct hdspm *hdspm)
 {
-       if (hdspm->is_aes32) {
-               unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
-               unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
-               unsigned int timecode =
-                       hdspm_read(hdspm, HDSPM_timecodeRegister);
+       unsigned int status, status2, timecode;
+       int syncref, rate = 0, rate_bits;
 
-               int syncref = hdspm_autosync_ref(hdspm);
+       switch (hdspm->io_type) {
+       case AES32:
+               status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+               status = hdspm_read(hdspm, HDSPM_statusRegister);
+               timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
+
+               syncref = hdspm_autosync_ref(hdspm);
 
                if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
                                status & HDSPM_AES32_wcLock)
-                       return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit)
-                                             & 0xF);
+                       return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF);
+
                if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
-                       syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
-                       status2 & (HDSPM_LockAES >>
-                                 (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
-                       return HDSPM_bit2freq((timecode >>
-                         (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
+                               syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
+                               status2 & (HDSPM_LockAES >>
+                               (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
+                       return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
                return 0;
-       } else {
-               unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
-               unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
-               unsigned int rate_bits;
-               int rate = 0;
+               break;
+
+       case MADIface:
+               status = hdspm_read(hdspm, HDSPM_statusRegister);
+
+               if (!(status & HDSPM_madiLock)) {
+                       rate = 0;  /* no lock */
+               } else {
+                       switch (status & (HDSPM_status1_freqMask)) {
+                       case HDSPM_status1_F_0*1:
+                               rate = 32000; break;
+                       case HDSPM_status1_F_0*2:
+                               rate = 44100; break;
+                       case HDSPM_status1_F_0*3:
+                               rate = 48000; break;
+                       case HDSPM_status1_F_0*4:
+                               rate = 64000; break;
+                       case HDSPM_status1_F_0*5:
+                               rate = 88200; break;
+                       case HDSPM_status1_F_0*6:
+                               rate = 96000; break;
+                       case HDSPM_status1_F_0*7:
+                               rate = 128000; break;
+                       case HDSPM_status1_F_0*8:
+                               rate = 176400; break;
+                       case HDSPM_status1_F_0*9:
+                               rate = 192000; break;
+                       default:
+                               rate = 0; break;
+                       }
+               }
+
+               break;
+
+       case MADI:
+       case AIO:
+       case RayDAT:
+               status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+               status = hdspm_read(hdspm, HDSPM_statusRegister);
+               rate = 0;
 
                /* if wordclock has synced freq and wordclock is valid */
                if ((status2 & HDSPM_wcLock) != 0 &&
@@ -672,6 +1147,7 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
 
                        rate_bits = status2 & HDSPM_wcFreqMask;
 
+
                        switch (rate_bits) {
                        case HDSPM_wcFreq32:
                                rate = 32000;
@@ -691,7 +1167,6 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
                        case HDSPM_wcFreq96:
                                rate = 96000;
                                break;
-                               /* Quadspeed Bit missing ???? */
                        default:
                                rate = 0;
                                break;
@@ -702,10 +1177,10 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
                 * word has priority to MADI
                 */
                if (rate != 0 &&
-                   (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
+               (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
                        return rate;
 
-               /* maby a madi input (which is taken if sel sync is madi) */
+               /* maybe a madi input (which is taken if sel sync is madi) */
                if (status & HDSPM_madiLock) {
                        rate_bits = status & HDSPM_madiFreqMask;
 
@@ -742,36 +1217,35 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
                                break;
                        }
                }
-               return rate;
+               break;
        }
+
+       return rate;
 }
 
 /* Latency function */
-static inline void hdspm_compute_period_size(struct hdspm * hdspm)
+static inline void hdspm_compute_period_size(struct hdspm *hdspm)
 {
-       hdspm->period_bytes =
-           1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
+       hdspm->period_bytes = 1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
 }
 
-static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm * hdspm)
+
+static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm)
 {
        int position;
 
        position = hdspm_read(hdspm, HDSPM_statusRegister);
 
-       if (!hdspm->precise_ptr)
-               return (position & HDSPM_BufferID) ?
+       switch (hdspm->io_type) {
+       case RayDAT:
+       case AIO:
+               position &= HDSPM_BufferPositionMask;
+               position /= 4; /* Bytes per sample */
+               break;
+       default:
+               position = (position & HDSPM_BufferID) ?
                        (hdspm->period_bytes / 4) : 0;
-
-       /* hwpointer comes in bytes and is 64Bytes accurate (by docu since
-          PCI Burst)
-          i have experimented that it is at most 64 Byte to much for playing 
-          so substraction of 64 byte should be ok for ALSA, but use it only
-          for application where you know what you do since if you come to
-          near with record pointer it can be a disaster */
-
-       position &= HDSPM_BufferPositionMask;
-       position = ((position - 64) % (2 * hdspm->period_bytes)) / 4;
+       }
 
        return position;
 }
@@ -805,7 +1279,7 @@ static void hdspm_silence_playback(struct hdspm *hdspm)
        }
 }
 
-static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames)
+static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
 {
        int n;
 
@@ -829,21 +1303,53 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames)
        return 0;
 }
 
+static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period)
+{
+       u64 freq_const;
+
+       if (period == 0)
+               return 0;
+
+       switch (hdspm->io_type) {
+       case MADI:
+       case AES32:
+               freq_const = 110069313433624ULL;
+               break;
+       case RayDAT:
+       case AIO:
+               freq_const = 104857600000000ULL;
+               break;
+       case MADIface:
+               freq_const = 131072000000000ULL;
+       }
+
+       return div_u64(freq_const, period);
+}
+
+
 static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
 {
        u64 n;
-       
+
        if (rate >= 112000)
                rate /= 4;
        else if (rate >= 56000)
                rate /= 2;
 
-       /* RME says n = 104857600000000, but in the windows MADI driver, I see:
-//     return 104857600000000 / rate; // 100 MHz
-       return 110100480000000 / rate; // 105 MHz
-        */        
-       /* n = 104857600000000ULL; */ /*  =  2^20 * 10^8 */
-       n = 110100480000000ULL;    /* Value checked for AES32 and MADI */
+       switch (hdspm->io_type) {
+       case MADIface:
+         n = 131072000000000ULL;  /* 125 MHz */
+         break;
+       case MADI:
+       case AES32:
+         n = 110069313433624ULL;  /* 105 MHz */
+         break;
+       case RayDAT:
+       case AIO:
+         n = 104857600000000ULL;  /* 100 MHz */
+         break;
+       }
+
        n = div_u64(n, rate);
        /* n should be less than 2^32 for being written to FREQ register */
        snd_BUG_ON(n >> 32);
@@ -864,13 +1370,13 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
 
        if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
 
-               /* SLAVE --- */ 
+               /* SLAVE --- */
                if (called_internally) {
 
-                 /* request from ctl or card initialization 
-                    just make a warning an remember setting 
-                    for future master mode switching */
-    
+                       /* request from ctl or card initialization
+                          just make a warning an remember setting
+                          for future master mode switching */
+
                        snd_printk(KERN_WARNING "HDSPM: "
                                   "Warning: device is not running "
                                   "as a clock master.\n");
@@ -907,7 +1413,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
 
           Note that a similar but essentially insoluble problem exists for
           externally-driven rate changes. All we can do is to flag rate
-          changes in the read/write routines.  
+          changes in the read/write routines.
         */
 
        if (current_rate <= 48000)
@@ -975,16 +1481,35 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
        /* For AES32, need to set DDS value in FREQ register
           For MADI, also apparently */
        hdspm_set_dds_value(hdspm, rate);
-       
-       if (hdspm->is_aes32 && rate != current_rate)
+
+       if (AES32 == hdspm->io_type && rate != current_rate)
                hdspm_write(hdspm, HDSPM_eeprom_wr, 0);
-       
-       /* For AES32 and for MADI (at least rev 204), channel_map needs to
-        * always be channel_map_madi_ss, whatever the sample rate */
-       hdspm->channel_map = channel_map_madi_ss;
 
        hdspm->system_sample_rate = rate;
 
+       if (rate <= 48000) {
+               hdspm->channel_map_in = hdspm->channel_map_in_ss;
+               hdspm->channel_map_out = hdspm->channel_map_out_ss;
+               hdspm->max_channels_in = hdspm->ss_in_channels;
+               hdspm->max_channels_out = hdspm->ss_out_channels;
+               hdspm->port_names_in = hdspm->port_names_in_ss;
+               hdspm->port_names_out = hdspm->port_names_out_ss;
+       } else if (rate <= 96000) {
+               hdspm->channel_map_in = hdspm->channel_map_in_ds;
+               hdspm->channel_map_out = hdspm->channel_map_out_ds;
+               hdspm->max_channels_in = hdspm->ds_in_channels;
+               hdspm->max_channels_out = hdspm->ds_out_channels;
+               hdspm->port_names_in = hdspm->port_names_in_ds;
+               hdspm->port_names_out = hdspm->port_names_out_ds;
+       } else {
+               hdspm->channel_map_in = hdspm->channel_map_in_qs;
+               hdspm->channel_map_out = hdspm->channel_map_out_qs;
+               hdspm->max_channels_in = hdspm->qs_in_channels;
+               hdspm->max_channels_out = hdspm->qs_out_channels;
+               hdspm->port_names_in = hdspm->port_names_in_qs;
+               hdspm->port_names_out = hdspm->port_names_out_qs;
+       }
+
        if (not_set != 0)
                return -1;
 
@@ -1019,39 +1544,26 @@ static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm,
                                                      int id)
 {
        /* the hardware already does the relevant bit-mask with 0xff */
-       if (id)
-               return hdspm_read(hdspm, HDSPM_midiDataIn1);
-       else
-               return hdspm_read(hdspm, HDSPM_midiDataIn0);
+       return hdspm_read(hdspm, hdspm->midi[id].dataIn);
 }
 
 static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id,
                                              int val)
 {
        /* the hardware already does the relevant bit-mask with 0xff */
-       if (id)
-               hdspm_write(hdspm, HDSPM_midiDataOut1, val);
-       else
-               hdspm_write(hdspm, HDSPM_midiDataOut0, val);
+       return hdspm_write(hdspm, hdspm->midi[id].dataOut, val);
 }
 
 static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id)
 {
-       if (id)
-               return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff);
-       else
-               return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff);
+       return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF;
 }
 
 static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id)
 {
        int fifo_bytes_used;
 
-       if (id)
-               fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1);
-       else
-               fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0);
-       fifo_bytes_used &= 0xff;
+       fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF;
 
        if (fifo_bytes_used < 128)
                return  128 - fifo_bytes_used;
@@ -1074,7 +1586,7 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
        unsigned char buf[128];
 
        /* Output is not interrupt driven */
-               
+
        spin_lock_irqsave (&hmidi->lock, flags);
        if (hmidi->output &&
            !snd_rawmidi_transmit_empty (hmidi->output)) {
@@ -1083,11 +1595,11 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi)
                if (n_pending > 0) {
                        if (n_pending > (int)sizeof (buf))
                                n_pending = sizeof (buf);
-               
+
                        to_write = snd_rawmidi_transmit (hmidi->output, buf,
                                                         n_pending);
                        if (to_write > 0) {
-                               for (i = 0; i < to_write; ++i) 
+                               for (i = 0; i < to_write; ++i)
                                        snd_hdspm_midi_write_byte (hmidi->hdspm,
                                                                   hmidi->id,
                                                                   buf[i]);
@@ -1127,12 +1639,11 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi)
                }
        }
        hmidi->pending = 0;
-       if (hmidi->id)
-               hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable;
-       else
-               hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable;
+
+       hmidi->hdspm->control_register |= hmidi->ie;
        hdspm_write(hmidi->hdspm, HDSPM_controlRegister,
                    hmidi->hdspm->control_register);
+
        spin_unlock_irqrestore (&hmidi->lock, flags);
        return snd_hdspm_midi_output_write (hmidi);
 }
@@ -1143,20 +1654,18 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
        struct hdspm *hdspm;
        struct hdspm_midi *hmidi;
        unsigned long flags;
-       u32 ie;
 
        hmidi = substream->rmidi->private_data;
        hdspm = hmidi->hdspm;
-       ie = hmidi->id ?
-               HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable;
+
        spin_lock_irqsave (&hdspm->lock, flags);
        if (up) {
-               if (!(hdspm->control_register & ie)) {
+               if (!(hdspm->control_register & hmidi->ie)) {
                        snd_hdspm_flush_midi_input (hdspm, hmidi->id);
-                       hdspm->control_register |= ie;
+                       hdspm->control_register |= hmidi->ie;
                }
        } else {
-               hdspm->control_register &= ~ie;
+               hdspm->control_register &= ~hmidi->ie;
        }
 
        hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
@@ -1167,14 +1676,14 @@ static void snd_hdspm_midi_output_timer(unsigned long data)
 {
        struct hdspm_midi *hmidi = (struct hdspm_midi *) data;
        unsigned long flags;
-       
+
        snd_hdspm_midi_output_write(hmidi);
        spin_lock_irqsave (&hmidi->lock, flags);
 
        /* this does not bump hmidi->istimer, because the
           kernel automatically removed the timer when it
           expired, and we are now adding it back, thus
-          leaving istimer wherever it was set before.  
+          leaving istimer wherever it was set before.
        */
 
        if (hmidi->istimer) {
@@ -1288,22 +1797,103 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card,
        hdspm->midi[id].hdspm = hdspm;
        spin_lock_init (&hdspm->midi[id].lock);
 
-       sprintf (buf, "%s MIDI %d", card->shortname, id+1);
-       err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi);
-       if (err < 0)
-               return err;
+       if (0 == id) {
+               if (MADIface == hdspm->io_type) {
+                       /* MIDI-over-MADI on HDSPe MADIface */
+                       hdspm->midi[0].dataIn = HDSPM_midiDataIn2;
+                       hdspm->midi[0].statusIn = HDSPM_midiStatusIn2;
+                       hdspm->midi[0].dataOut = HDSPM_midiDataOut2;
+                       hdspm->midi[0].statusOut = HDSPM_midiStatusOut2;
+                       hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable;
+                       hdspm->midi[0].irq = HDSPM_midi2IRQPending;
+               } else {
+                       hdspm->midi[0].dataIn = HDSPM_midiDataIn0;
+                       hdspm->midi[0].statusIn = HDSPM_midiStatusIn0;
+                       hdspm->midi[0].dataOut = HDSPM_midiDataOut0;
+                       hdspm->midi[0].statusOut = HDSPM_midiStatusOut0;
+                       hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable;
+                       hdspm->midi[0].irq = HDSPM_midi0IRQPending;
+               }
+       } else if (1 == id) {
+               hdspm->midi[1].dataIn = HDSPM_midiDataIn1;
+               hdspm->midi[1].statusIn = HDSPM_midiStatusIn1;
+               hdspm->midi[1].dataOut = HDSPM_midiDataOut1;
+               hdspm->midi[1].statusOut = HDSPM_midiStatusOut1;
+               hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable;
+               hdspm->midi[1].irq = HDSPM_midi1IRQPending;
+       } else if ((2 == id) && (MADI == hdspm->io_type)) {
+               /* MIDI-over-MADI on HDSPe MADI */
+               hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
+               hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
+               hdspm->midi[2].dataOut = HDSPM_midiDataOut2;
+               hdspm->midi[2].statusOut = HDSPM_midiStatusOut2;
+               hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
+               hdspm->midi[2].irq = HDSPM_midi2IRQPending;
+       } else if (2 == id) {
+               /* TCO MTC, read only */
+               hdspm->midi[2].dataIn = HDSPM_midiDataIn2;
+               hdspm->midi[2].statusIn = HDSPM_midiStatusIn2;
+               hdspm->midi[2].dataOut = -1;
+               hdspm->midi[2].statusOut = -1;
+               hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable;
+               hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES;
+       } else if (3 == id) {
+               /* TCO MTC on HDSPe MADI */
+               hdspm->midi[3].dataIn = HDSPM_midiDataIn3;
+               hdspm->midi[3].statusIn = HDSPM_midiStatusIn3;
+               hdspm->midi[3].dataOut = -1;
+               hdspm->midi[3].statusOut = -1;
+               hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable;
+               hdspm->midi[3].irq = HDSPM_midi3IRQPending;
+       }
+
+       if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) ||
+                                       (MADIface == hdspm->io_type)))) {
+               if ((id == 0) && (MADIface == hdspm->io_type)) {
+                       sprintf(buf, "%s MIDIoverMADI", card->shortname);
+               } else if ((id == 2) && (MADI == hdspm->io_type)) {
+                       sprintf(buf, "%s MIDIoverMADI", card->shortname);
+               } else {
+                       sprintf(buf, "%s MIDI %d", card->shortname, id+1);
+               }
+               err = snd_rawmidi_new(card, buf, id, 1, 1,
+                               &hdspm->midi[id].rmidi);
+               if (err < 0)
+                       return err;
+
+               sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d",
+                               card->id, id+1);
+               hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
+
+               snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+                               SNDRV_RAWMIDI_STREAM_OUTPUT,
+                               &snd_hdspm_midi_output);
+               snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+                               SNDRV_RAWMIDI_STREAM_INPUT,
+                               &snd_hdspm_midi_input);
+
+               hdspm->midi[id].rmidi->info_flags |=
+                       SNDRV_RAWMIDI_INFO_OUTPUT |
+                       SNDRV_RAWMIDI_INFO_INPUT |
+                       SNDRV_RAWMIDI_INFO_DUPLEX;
+       } else {
+               /* TCO MTC, read only */
+               sprintf(buf, "%s MTC %d", card->shortname, id+1);
+               err = snd_rawmidi_new(card, buf, id, 1, 1,
+                               &hdspm->midi[id].rmidi);
+               if (err < 0)
+                       return err;
 
-       sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1);
-       hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
+               sprintf(hdspm->midi[id].rmidi->name,
+                               "%s MTC %d", card->id, id+1);
+               hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
 
-       snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
-                           &snd_hdspm_midi_output);
-       snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
-                           &snd_hdspm_midi_input);
+               snd_rawmidi_set_ops(hdspm->midi[id].rmidi,
+                               SNDRV_RAWMIDI_STREAM_INPUT,
+                               &snd_hdspm_midi_input);
 
-       hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
-               SNDRV_RAWMIDI_INFO_INPUT |
-               SNDRV_RAWMIDI_INFO_DUPLEX;
+               hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+       }
 
        return 0;
 }
@@ -1312,12 +1902,15 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card,
 static void hdspm_midi_tasklet(unsigned long arg)
 {
        struct hdspm *hdspm = (struct hdspm *)arg;
-       
-       if (hdspm->midi[0].pending)
-               snd_hdspm_midi_input_read (&hdspm->midi[0]);
-       if (hdspm->midi[1].pending)
-               snd_hdspm_midi_input_read (&hdspm->midi[1]);
-} 
+       int i = 0;
+
+       while (i < hdspm->midiPorts) {
+               if (hdspm->midi[i].pending)
+                       snd_hdspm_midi_input_read(&hdspm->midi[i]);
+
+               i++;
+       }
+}
 
 
 /*-----------------------------------------------------------------------------
@@ -1326,6 +1919,22 @@ static void hdspm_midi_tasklet(unsigned long arg)
 
 /* get the system sample rate which is set */
 
+
+/**
+ * Calculate the real sample rate from the
+ * current DDS value.
+ **/
+static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
+{
+       unsigned int period, rate;
+
+       period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+       rate = hdspm_calc_dds_value(hdspm, period);
+
+       return rate;
+}
+
+
 #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -1340,112 +1949,274 @@ static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol,
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 1;
+       uinfo->value.integer.min = 27000;
+       uinfo->value.integer.max = 207000;
+       uinfo->value.integer.step = 1;
        return 0;
 }
 
+
 static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol,
                                            struct snd_ctl_elem_value *
                                            ucontrol)
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate;
+       ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm);
        return 0;
 }
 
+
+/**
+ * Returns the WordClock sample rate class for the given card.
+ **/
+static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
+{
+       int status;
+
+       switch (hdspm->io_type) {
+       case RayDAT:
+       case AIO:
+               status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+               return (status >> 16) & 0xF;
+               break;
+       default:
+               break;
+       }
+
+
+       return 0;
+}
+
+
+/**
+ * Returns the TCO sample rate class for the given card.
+ **/
+static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
+{
+       int status;
+
+       if (hdspm->tco) {
+               switch (hdspm->io_type) {
+               case RayDAT:
+               case AIO:
+                       status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+                       return (status >> 20) & 0xF;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * Returns the SYNC_IN sample rate class for the given card.
+ **/
+static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
+{
+       int status;
+
+       if (hdspm->tco) {
+               switch (hdspm->io_type) {
+               case RayDAT:
+               case AIO:
+                       status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
+                       return (status >> 12) & 0xF;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * Returns the sample rate class for input source <idx> for
+ * 'new style' cards like the AIO and RayDAT.
+ **/
+static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
+{
+       int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2);
+
+       return (status >> (idx*4)) & 0xF;
+}
+
+
+
 #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .access = SNDRV_CTL_ELEM_ACCESS_READ, \
-  .info = snd_hdspm_info_autosync_sample_rate, \
-  .get = snd_hdspm_get_autosync_sample_rate \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .private_value = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+       .info = snd_hdspm_info_autosync_sample_rate, \
+       .get = snd_hdspm_get_autosync_sample_rate \
 }
 
+
 static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
                                               struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "32000", "44100", "48000",
-               "64000", "88200", "96000",
-               "128000", "176400", "192000",
-               "None"
-       };
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
        uinfo->value.enumerated.items = 10;
+
        if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-               uinfo->value.enumerated.item =
-                   uinfo->value.enumerated.items - 1;
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
        strcpy(uinfo->value.enumerated.name,
-              texts[uinfo->value.enumerated.item]);
+                       texts_freq[uinfo->value.enumerated.item]);
        return 0;
 }
 
+
 static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
                                              struct snd_ctl_elem_value *
                                              ucontrol)
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       switch (hdspm_external_sample_rate(hdspm)) {
-       case 32000:
-               ucontrol->value.enumerated.item[0] = 0;
-               break;
-       case 44100:
-               ucontrol->value.enumerated.item[0] = 1;
-               break;
-       case 48000:
-               ucontrol->value.enumerated.item[0] = 2;
-               break;
-       case 64000:
-               ucontrol->value.enumerated.item[0] = 3;
-               break;
-       case 88200:
-               ucontrol->value.enumerated.item[0] = 4;
-               break;
-       case 96000:
-               ucontrol->value.enumerated.item[0] = 5;
-               break;
-       case 128000:
-               ucontrol->value.enumerated.item[0] = 6;
-               break;
-       case 176400:
-               ucontrol->value.enumerated.item[0] = 7;
-               break;
-       case 192000:
-               ucontrol->value.enumerated.item[0] = 8;
-               break;
+       switch (hdspm->io_type) {
+       case RayDAT:
+               switch (kcontrol->private_value) {
+               case 0:
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_wc_sample_rate(hdspm);
+                       break;
+               case 7:
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_tco_sample_rate(hdspm);
+                       break;
+               case 8:
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_sync_in_sample_rate(hdspm);
+                       break;
+               default:
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_s1_sample_rate(hdspm,
+                                               kcontrol->private_value-1);
+               }
+
+       case AIO:
+               switch (kcontrol->private_value) {
+               case 0: /* WC */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_wc_sample_rate(hdspm);
+                       break;
+               case 4: /* TCO */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_tco_sample_rate(hdspm);
+                       break;
+               case 5: /* SYNC_IN */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_sync_in_sample_rate(hdspm);
+                       break;
+               default:
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_s1_sample_rate(hdspm,
+                                               ucontrol->id.index-1);
+               }
+
+       case AES32:
+
+               switch (kcontrol->private_value) {
+               case 0: /* WC */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_wc_sample_rate(hdspm);
+                       break;
+               case 9: /* TCO */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_tco_sample_rate(hdspm);
+                       break;
+               case 10: /* SYNC_IN */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_sync_in_sample_rate(hdspm);
+                       break;
+               default: /* AES1 to AES8 */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_get_s1_sample_rate(hdspm,
+                                               kcontrol->private_value-1);
+                       break;
 
+               }
        default:
-               ucontrol->value.enumerated.item[0] = 9;
+               break;
        }
+
        return 0;
 }
 
+
 #define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .access = SNDRV_CTL_ELEM_ACCESS_READ, \
-  .info = snd_hdspm_info_system_clock_mode, \
-  .get = snd_hdspm_get_system_clock_mode, \
-}
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_system_clock_mode, \
+       .get = snd_hdspm_get_system_clock_mode, \
+       .put = snd_hdspm_put_system_clock_mode, \
+}
+
+
+/**
+ * Returns the system clock mode for the given card.
+ * @returns 0 - master, 1 - slave
+ **/
+static int hdspm_system_clock_mode(struct hdspm *hdspm)
+{
+       switch (hdspm->io_type) {
+       case AIO:
+       case RayDAT:
+               if (hdspm->settings_register & HDSPM_c0Master)
+                       return 0;
+               break;
+
+       default:
+               if (hdspm->control_register & HDSPM_ClockModeMaster)
+                       return 0;
+       }
 
+       return 1;
+}
 
 
-static int hdspm_system_clock_mode(struct hdspm * hdspm)
+/**
+ * Sets the system clock mode.
+ * @param mode 0 - master, 1 - slave
+ **/
+static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
 {
-        /* Always reflect the hardware info, rme is never wrong !!!! */
+       switch (hdspm->io_type) {
+       case AIO:
+       case RayDAT:
+               if (0 == mode)
+                       hdspm->settings_register |= HDSPM_c0Master;
+               else
+                       hdspm->settings_register &= ~HDSPM_c0Master;
 
-       if (hdspm->control_register & HDSPM_ClockModeMaster)
-               return 0;
-       return 1;
+               hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
+               break;
+
+       default:
+               if (0 == mode)
+                       hdspm->control_register |= HDSPM_ClockModeMaster;
+               else
+                       hdspm->control_register &= ~HDSPM_ClockModeMaster;
+
+               hdspm_write(hdspm, HDSPM_controlRegister,
+                               hdspm->control_register);
+       }
 }
 
+
 static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
                                            struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "Master", "Slave" };
+       static char *texts[] = { "Master", "AutoSync" };
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
@@ -1463,96 +2234,83 @@ static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol,
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       ucontrol->value.enumerated.item[0] =
-           hdspm_system_clock_mode(hdspm);
+       ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm);
        return 0;
 }
 
-#define HDSPM_CLOCK_SOURCE(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .info = snd_hdspm_info_clock_source, \
-  .get = snd_hdspm_get_clock_source, \
-  .put = snd_hdspm_put_clock_source \
+static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol,
+                                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+
+       val = ucontrol->value.enumerated.item[0];
+       if (val < 0)
+               val = 0;
+       else if (val > 1)
+               val = 1;
+
+       hdspm_set_system_clock_mode(hdspm, val);
+
+       return 0;
+}
+
+
+#define HDSPM_INTERNAL_CLOCK(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .info = snd_hdspm_info_clock_source, \
+       .get = snd_hdspm_get_clock_source, \
+       .put = snd_hdspm_put_clock_source \
 }
 
+
 static int hdspm_clock_source(struct hdspm * hdspm)
 {
-       if (hdspm->control_register & HDSPM_ClockModeMaster) {
-               switch (hdspm->system_sample_rate) {
-               case 32000:
-                       return 1;
-               case 44100:
-                       return 2;
-               case 48000:
-                       return 3;
-               case 64000:
-                       return 4;
-               case 88200:
-                       return 5;
-               case 96000:
-                       return 6;
-               case 128000:
-                       return 7;
-               case 176400:
-                       return 8;
-               case 192000:
-                       return 9;
-               default:
-                       return 3;
-               }
-       } else {
-               return 0;
+       switch (hdspm->system_sample_rate) {
+       case 32000: return 0;
+       case 44100: return 1;
+       case 48000: return 2;
+       case 64000: return 3;
+       case 88200: return 4;
+       case 96000: return 5;
+       case 128000: return 6;
+       case 176400: return 7;
+       case 192000: return 8;
        }
+
+       return -1;
 }
 
 static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
 {
        int rate;
        switch (mode) {
-
-       case HDSPM_CLOCK_SOURCE_AUTOSYNC:
-               if (hdspm_external_sample_rate(hdspm) != 0) {
-                       hdspm->control_register &= ~HDSPM_ClockModeMaster;
-                       hdspm_write(hdspm, HDSPM_controlRegister,
-                                   hdspm->control_register);
-                       return 0;
-               }
-               return -1;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
-               rate = 32000;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
-               rate = 44100;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
-               rate = 48000;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
-               rate = 64000;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
-               rate = 88200;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
-               rate = 96000;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
-               rate = 128000;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
-               rate = 176400;
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
-               rate = 192000;
-               break;
-
+       case 0:
+               rate = 32000; break;
+       case 1:
+               rate = 44100; break;
+       case 2:
+               rate = 48000; break;
+       case 3:
+               rate = 64000; break;
+       case 4:
+               rate = 88200; break;
+       case 5:
+               rate = 96000; break;
+       case 6:
+               rate = 128000; break;
+       case 7:
+               rate = 176400; break;
+       case 8:
+               rate = 192000; break;
        default:
-               rate = 44100;
+               rate = 48000;
        }
-       hdspm->control_register |= HDSPM_ClockModeMaster;
-       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
        hdspm_set_rate(hdspm, rate, 1);
        return 0;
 }
@@ -1560,25 +2318,16 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode)
 static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "AutoSync",
-               "Internal 32.0 kHz", "Internal 44.1 kHz",
-                   "Internal 48.0 kHz",
-               "Internal 64.0 kHz", "Internal 88.2 kHz",
-                   "Internal 96.0 kHz",
-               "Internal 128.0 kHz", "Internal 176.4 kHz",
-                   "Internal 192.0 kHz"
-       };
-
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
-       uinfo->value.enumerated.items = 10;
+       uinfo->value.enumerated.items = 9;
 
        if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
                uinfo->value.enumerated.item =
                    uinfo->value.enumerated.items - 1;
 
        strcpy(uinfo->value.enumerated.name,
-              texts[uinfo->value.enumerated.item]);
+              texts_freq[uinfo->value.enumerated.item+1]);
 
        return 0;
 }
@@ -1615,134 +2364,301 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol,
        return change;
 }
 
-#define HDSPM_PREF_SYNC_REF(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .info = snd_hdspm_info_pref_sync_ref, \
-  .get = snd_hdspm_get_pref_sync_ref, \
-  .put = snd_hdspm_put_pref_sync_ref \
-}
 
+#define HDSPM_PREF_SYNC_REF(xname, xindex) \
+{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_pref_sync_ref, \
+       .get = snd_hdspm_get_pref_sync_ref, \
+       .put = snd_hdspm_put_pref_sync_ref \
+}
+
+
+/**
+ * Returns the current preferred sync reference setting.
+ * The semantics of the return value are depending on the
+ * card, please see the comments for clarification.
+ **/
 static int hdspm_pref_sync_ref(struct hdspm * hdspm)
 {
-       /* Notice that this looks at the requested sync source,
-          not the one actually in use.
-        */
-       if (hdspm->is_aes32) {
+       switch (hdspm->io_type) {
+       case AES32:
                switch (hdspm->control_register & HDSPM_SyncRefMask) {
-               /* number gives AES index, except for 0 which
-                  corresponds to WordClock */
-               case 0: return 0;
-               case HDSPM_SyncRef0: return 1;
-               case HDSPM_SyncRef1: return 2;
-               case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3;
-               case HDSPM_SyncRef2: return 4;
-               case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5;
-               case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6;
-               case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7;
-               case HDSPM_SyncRef3: return 8;
+               case 0: return 0;  /* WC */
+               case HDSPM_SyncRef0: return 1; /* AES 1 */
+               case HDSPM_SyncRef1: return 2; /* AES 2 */
+               case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */
+               case HDSPM_SyncRef2: return 4; /* AES 4 */
+               case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */
+               case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */
+               case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0:
+                                                   return 7; /* AES 7 */
+               case HDSPM_SyncRef3: return 8; /* AES 8 */
+               case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */
                }
-       } else {
-               switch (hdspm->control_register & HDSPM_SyncRefMask) {
-               case HDSPM_SyncRef_Word:
-                       return HDSPM_SYNC_FROM_WORD;
-               case HDSPM_SyncRef_MADI:
-                       return HDSPM_SYNC_FROM_MADI;
+               break;
+
+       case MADI:
+       case MADIface:
+               if (hdspm->tco) {
+                       switch (hdspm->control_register & HDSPM_SyncRefMask) {
+                       case 0: return 0;  /* WC */
+                       case HDSPM_SyncRef0: return 1;  /* MADI */
+                       case HDSPM_SyncRef1: return 2;  /* TCO */
+                       case HDSPM_SyncRef1+HDSPM_SyncRef0:
+                                            return 3;  /* SYNC_IN */
+                       }
+               } else {
+                       switch (hdspm->control_register & HDSPM_SyncRefMask) {
+                       case 0: return 0;  /* WC */
+                       case HDSPM_SyncRef0: return 1;  /* MADI */
+                       case HDSPM_SyncRef1+HDSPM_SyncRef0:
+                                            return 2;  /* SYNC_IN */
+                       }
+               }
+               break;
+
+       case RayDAT:
+               if (hdspm->tco) {
+                       switch ((hdspm->settings_register &
+                               HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+                       case 0: return 0;  /* WC */
+                       case 3: return 1;  /* ADAT 1 */
+                       case 4: return 2;  /* ADAT 2 */
+                       case 5: return 3;  /* ADAT 3 */
+                       case 6: return 4;  /* ADAT 4 */
+                       case 1: return 5;  /* AES */
+                       case 2: return 6;  /* SPDIF */
+                       case 9: return 7;  /* TCO */
+                       case 10: return 8; /* SYNC_IN */
+                       }
+               } else {
+                       switch ((hdspm->settings_register &
+                               HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+                       case 0: return 0;  /* WC */
+                       case 3: return 1;  /* ADAT 1 */
+                       case 4: return 2;  /* ADAT 2 */
+                       case 5: return 3;  /* ADAT 3 */
+                       case 6: return 4;  /* ADAT 4 */
+                       case 1: return 5;  /* AES */
+                       case 2: return 6;  /* SPDIF */
+                       case 10: return 7; /* SYNC_IN */
+                       }
                }
+
+               break;
+
+       case AIO:
+               if (hdspm->tco) {
+                       switch ((hdspm->settings_register &
+                               HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+                       case 0: return 0;  /* WC */
+                       case 3: return 1;  /* ADAT */
+                       case 1: return 2;  /* AES */
+                       case 2: return 3;  /* SPDIF */
+                       case 9: return 4;  /* TCO */
+                       case 10: return 5; /* SYNC_IN */
+                       }
+               } else {
+                       switch ((hdspm->settings_register &
+                               HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) {
+                       case 0: return 0;  /* WC */
+                       case 3: return 1;  /* ADAT */
+                       case 1: return 2;  /* AES */
+                       case 2: return 3;  /* SPDIF */
+                       case 10: return 4; /* SYNC_IN */
+                       }
+               }
+
+               break;
        }
 
-       return HDSPM_SYNC_FROM_WORD;
+       return -1;
 }
 
+
+/**
+ * Set the preferred sync reference to <pref>. The semantics
+ * of <pref> are depending on the card type, see the comments
+ * for clarification.
+ **/
 static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref)
 {
-       hdspm->control_register &= ~HDSPM_SyncRefMask;
+       int p = 0;
 
-       if (hdspm->is_aes32) {
-               switch (pref) {
-               case 0:
-                      hdspm->control_register |= 0;
-                      break;
-               case 1:
-                      hdspm->control_register |= HDSPM_SyncRef0;
-                      break;
-               case 2:
-                      hdspm->control_register |= HDSPM_SyncRef1;
-                      break;
-               case 3:
-                      hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0;
-                      break;
-               case 4:
-                      hdspm->control_register |= HDSPM_SyncRef2;
-                      break;
-               case 5:
-                      hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0;
-                      break;
-               case 6:
-                      hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1;
-                      break;
-               case 7:
-                      hdspm->control_register |=
-                              HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
-                      break;
-               case 8:
-                      hdspm->control_register |= HDSPM_SyncRef3;
-                      break;
-               default:
-                      return -1;
-               }
-       } else {
+       switch (hdspm->io_type) {
+       case AES32:
+               hdspm->control_register &= ~HDSPM_SyncRefMask;
                switch (pref) {
-               case HDSPM_SYNC_FROM_MADI:
-                       hdspm->control_register |= HDSPM_SyncRef_MADI;
+               case 0: /* WC  */
+                       break;
+               case 1: /* AES 1 */
+                       hdspm->control_register |= HDSPM_SyncRef0;
+                       break;
+               case 2: /* AES 2 */
+                       hdspm->control_register |= HDSPM_SyncRef1;
+                       break;
+               case 3: /* AES 3 */
+                       hdspm->control_register |=
+                               HDSPM_SyncRef1+HDSPM_SyncRef0;
+                       break;
+               case 4: /* AES 4 */
+                       hdspm->control_register |= HDSPM_SyncRef2;
+                       break;
+               case 5: /* AES 5 */
+                       hdspm->control_register |=
+                               HDSPM_SyncRef2+HDSPM_SyncRef0;
+                       break;
+               case 6: /* AES 6 */
+                       hdspm->control_register |=
+                               HDSPM_SyncRef2+HDSPM_SyncRef1;
+                       break;
+               case 7: /* AES 7 */
+                       hdspm->control_register |=
+                               HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0;
                        break;
-               case HDSPM_SYNC_FROM_WORD:
-                       hdspm->control_register |= HDSPM_SyncRef_Word;
+               case 8: /* AES 8 */
+                       hdspm->control_register |= HDSPM_SyncRef3;
+                       break;
+               case 9: /* TCO */
+                       hdspm->control_register |=
+                               HDSPM_SyncRef3+HDSPM_SyncRef0;
                        break;
                default:
                        return -1;
                }
+
+               break;
+
+       case MADI:
+       case MADIface:
+               hdspm->control_register &= ~HDSPM_SyncRefMask;
+               if (hdspm->tco) {
+                       switch (pref) {
+                       case 0: /* WC */
+                               break;
+                       case 1: /* MADI */
+                               hdspm->control_register |= HDSPM_SyncRef0;
+                               break;
+                       case 2: /* TCO */
+                               hdspm->control_register |= HDSPM_SyncRef1;
+                               break;
+                       case 3: /* SYNC_IN */
+                               hdspm->control_register |=
+                                       HDSPM_SyncRef0+HDSPM_SyncRef1;
+                               break;
+                       default:
+                               return -1;
+                       }
+               } else {
+                       switch (pref) {
+                       case 0: /* WC */
+                               break;
+                       case 1: /* MADI */
+                               hdspm->control_register |= HDSPM_SyncRef0;
+                               break;
+                       case 2: /* SYNC_IN */
+                               hdspm->control_register |=
+                                       HDSPM_SyncRef0+HDSPM_SyncRef1;
+                               break;
+                       default:
+                               return -1;
+                       }
+               }
+
+               break;
+
+       case RayDAT:
+               if (hdspm->tco) {
+                       switch (pref) {
+                       case 0: p = 0; break;  /* WC */
+                       case 1: p = 3; break;  /* ADAT 1 */
+                       case 2: p = 4; break;  /* ADAT 2 */
+                       case 3: p = 5; break;  /* ADAT 3 */
+                       case 4: p = 6; break;  /* ADAT 4 */
+                       case 5: p = 1; break;  /* AES */
+                       case 6: p = 2; break;  /* SPDIF */
+                       case 7: p = 9; break;  /* TCO */
+                       case 8: p = 10; break; /* SYNC_IN */
+                       default: return -1;
+                       }
+               } else {
+                       switch (pref) {
+                       case 0: p = 0; break;  /* WC */
+                       case 1: p = 3; break;  /* ADAT 1 */
+                       case 2: p = 4; break;  /* ADAT 2 */
+                       case 3: p = 5; break;  /* ADAT 3 */
+                       case 4: p = 6; break;  /* ADAT 4 */
+                       case 5: p = 1; break;  /* AES */
+                       case 6: p = 2; break;  /* SPDIF */
+                       case 7: p = 10; break; /* SYNC_IN */
+                       default: return -1;
+                       }
+               }
+               break;
+
+       case AIO:
+               if (hdspm->tco) {
+                       switch (pref) {
+                       case 0: p = 0; break;  /* WC */
+                       case 1: p = 3; break;  /* ADAT */
+                       case 2: p = 1; break;  /* AES */
+                       case 3: p = 2; break;  /* SPDIF */
+                       case 4: p = 9; break;  /* TCO */
+                       case 5: p = 10; break; /* SYNC_IN */
+                       default: return -1;
+                       }
+               } else {
+                       switch (pref) {
+                       case 0: p = 0; break;  /* WC */
+                       case 1: p = 3; break;  /* ADAT */
+                       case 2: p = 1; break;  /* AES */
+                       case 3: p = 2; break;  /* SPDIF */
+                       case 4: p = 10; break; /* SYNC_IN */
+                       default: return -1;
+                       }
+               }
+               break;
        }
-       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       switch (hdspm->io_type) {
+       case RayDAT:
+       case AIO:
+               hdspm->settings_register &= ~HDSPM_c0_SyncRefMask;
+               hdspm->settings_register |= HDSPM_c0_SyncRef0 * p;
+               hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
+               break;
+
+       case MADI:
+       case MADIface:
+       case AES32:
+               hdspm_write(hdspm, HDSPM_controlRegister,
+                               hdspm->control_register);
+       }
+
        return 0;
 }
 
+
 static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_info *uinfo)
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       if (hdspm->is_aes32) {
-               static char *texts[] = { "Word", "AES1", "AES2", "AES3",
-                       "AES4", "AES5", "AES6", "AES7", "AES8" };
-
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-               uinfo->count = 1;
-
-               uinfo->value.enumerated.items = 9;
-
-               if (uinfo->value.enumerated.item >=
-                   uinfo->value.enumerated.items)
-                       uinfo->value.enumerated.item =
-                               uinfo->value.enumerated.items - 1;
-               strcpy(uinfo->value.enumerated.name,
-                               texts[uinfo->value.enumerated.item]);
-       } else {
-               static char *texts[] = { "Word", "MADI" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = hdspm->texts_autosync_items;
 
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-               uinfo->count = 1;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
 
-               uinfo->value.enumerated.items = 2;
+       strcpy(uinfo->value.enumerated.name,
+                       hdspm->texts_autosync[uinfo->value.enumerated.item]);
 
-               if (uinfo->value.enumerated.item >=
-                   uinfo->value.enumerated.items)
-                       uinfo->value.enumerated.item =
-                               uinfo->value.enumerated.items - 1;
-               strcpy(uinfo->value.enumerated.name,
-                               texts[uinfo->value.enumerated.item]);
-       }
        return 0;
 }
 
@@ -1750,32 +2666,41 @@ static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_value *ucontrol)
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       int psf = hdspm_pref_sync_ref(hdspm);
 
-       ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm);
-       return 0;
+       if (psf >= 0) {
+               ucontrol->value.enumerated.item[0] = psf;
+               return 0;
+       }
+
+       return -1;
 }
 
 static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_value *ucontrol)
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
-       int change, max;
-       unsigned int val;
-
-       max = hdspm->is_aes32 ? 9 : 2;
+       int val, change = 0;
 
        if (!snd_hdspm_use_is_exclusive(hdspm))
                return -EBUSY;
 
-       val = ucontrol->value.enumerated.item[0] % max;
+       val = ucontrol->value.enumerated.item[0];
+
+       if (val < 0)
+               val = 0;
+       else if (val >= hdspm->texts_autosync_items)
+               val = hdspm->texts_autosync_items-1;
 
        spin_lock_irq(&hdspm->lock);
-       change = (int) val != hdspm_pref_sync_ref(hdspm);
-       hdspm_set_pref_sync_ref(hdspm, val);
+       if (val != hdspm_pref_sync_ref(hdspm))
+               change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0;
+
        spin_unlock_irq(&hdspm->lock);
        return change;
 }
 
+
 #define HDSPM_AUTOSYNC_REF(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -1785,18 +2710,18 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
   .get = snd_hdspm_get_autosync_ref, \
 }
 
-static int hdspm_autosync_ref(struct hdspm * hdspm)
+static int hdspm_autosync_ref(struct hdspm *hdspm)
 {
-       if (hdspm->is_aes32) {
+       if (AES32 == hdspm->io_type) {
                unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
-               unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) &
-                       0xF;
+               unsigned int syncref =
+                       (status >> HDSPM_AES32_syncref_bit) & 0xF;
                if (syncref == 0)
                        return HDSPM_AES32_AUTOSYNC_FROM_WORD;
                if (syncref <= 8)
                        return syncref;
                return HDSPM_AES32_AUTOSYNC_FROM_NONE;
-       } else {
+       } else if (MADI == hdspm->io_type) {
                /* This looks at the autosync selected sync reference */
                unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
 
@@ -1805,22 +2730,27 @@ static int hdspm_autosync_ref(struct hdspm * hdspm)
                        return HDSPM_AUTOSYNC_FROM_WORD;
                case HDSPM_SelSyncRef_MADI:
                        return HDSPM_AUTOSYNC_FROM_MADI;
+               case HDSPM_SelSyncRef_TCO:
+                       return HDSPM_AUTOSYNC_FROM_TCO;
+               case HDSPM_SelSyncRef_SyncIn:
+                       return HDSPM_AUTOSYNC_FROM_SYNC_IN;
                case HDSPM_SelSyncRef_NVALID:
                        return HDSPM_AUTOSYNC_FROM_NONE;
                default:
                        return 0;
                }
 
-               return 0;
        }
+       return 0;
 }
 
+
 static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       if (hdspm->is_aes32) {
+       if (AES32 == hdspm->io_type) {
                static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
                        "AES4", "AES5", "AES6", "AES7", "AES8", "None"};
 
@@ -1833,14 +2763,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
                                uinfo->value.enumerated.items - 1;
                strcpy(uinfo->value.enumerated.name,
                                texts[uinfo->value.enumerated.item]);
-       } else {
-               static char *texts[] = { "WordClock", "MADI", "None" };
+       } else if (MADI == hdspm->io_type) {
+               static char *texts[] = {"Word Clock", "MADI", "TCO",
+                       "Sync In", "None" };
 
                uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
                uinfo->count = 1;
-               uinfo->value.enumerated.items = 3;
+               uinfo->value.enumerated.items = 5;
                if (uinfo->value.enumerated.item >=
-                   uinfo->value.enumerated.items)
+                               uinfo->value.enumerated.items)
                        uinfo->value.enumerated.item =
                                uinfo->value.enumerated.items - 1;
                strcpy(uinfo->value.enumerated.name,
@@ -1858,6 +2789,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+
 #define HDSPM_LINE_OUT(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -1914,6 +2846,7 @@ static int snd_hdspm_put_line_out(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_TX_64(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -1969,6 +2902,7 @@ static int snd_hdspm_put_tx_64(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_C_TMS(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2024,6 +2958,7 @@ static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_SAFE_MODE(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2079,6 +3014,7 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_EMPHASIS(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2134,6 +3070,7 @@ static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_DOLBY(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2189,6 +3126,7 @@ static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_PROFESSIONAL(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2315,6 +3253,7 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_DS_WIRE(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2386,6 +3325,7 @@ static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+
 #define HDSPM_QS_WIRE(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .name = xname, \
@@ -2472,15 +3412,6 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
        return change;
 }
 
-/*           Simple Mixer
-  deprecated since to much faders ???
-  MIXER interface says output (source, destination, value)
-   where source > MAX_channels are playback channels 
-   on MADICARD 
-  - playback mixer matrix: [channelout+64] [output] [value]
-  - input(thru) mixer matrix: [channelin] [output] [value]
-  (better do 2 kontrols for separation ?)
-*/
 
 #define HDSPM_MIXER(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
@@ -2586,7 +3517,7 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol,
 
 /* The simple mixer control(s) provide gain control for the
    basic 1:1 mappings of playback streams to output
-   streams. 
+   streams.
 */
 
 #define HDSPM_PLAYBACK_MIXER \
@@ -2604,7 +3535,7 @@ static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol,
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 1;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 65536;
+       uinfo->value.integer.max = 64;
        uinfo->value.integer.step = 1;
        return 0;
 }
@@ -2614,28 +3545,17 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
        int channel;
-       int mapped_channel;
 
        channel = ucontrol->id.index - 1;
 
        if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
                return -EINVAL;
 
-       mapped_channel = hdspm->channel_map[channel];
-       if (mapped_channel < 0)
-               return -EINVAL;
-
        spin_lock_irq(&hdspm->lock);
        ucontrol->value.integer.value[0] =
-           hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel);
+         (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN;
        spin_unlock_irq(&hdspm->lock);
 
-       /*
-       snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, "
-                   "value %d\n",
-                   ucontrol->id.index, channel, mapped_channel,
-                   ucontrol->value.integer.value[0]); 
-       */
        return 0;
 }
 
@@ -2645,7 +3565,6 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
        int change;
        int channel;
-       int mapped_channel;
        int gain;
 
        if (!snd_hdspm_use_is_exclusive(hdspm))
@@ -2656,59 +3575,60 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
        if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
                return -EINVAL;
 
-       mapped_channel = hdspm->channel_map[channel];
-       if (mapped_channel < 0)
-               return -EINVAL;
-
-       gain = ucontrol->value.integer.value[0];
+       gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64;
 
        spin_lock_irq(&hdspm->lock);
        change =
-           gain != hdspm_read_pb_gain(hdspm, mapped_channel,
-                                      mapped_channel);
+           gain != hdspm_read_pb_gain(hdspm, channel,
+                                      channel);
        if (change)
-               hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel,
+               hdspm_write_pb_gain(hdspm, channel, channel,
                                    gain);
        spin_unlock_irq(&hdspm->lock);
        return change;
 }
 
-#define HDSPM_WC_SYNC_CHECK(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  .info = snd_hdspm_info_sync_check, \
-  .get = snd_hdspm_get_wc_sync_check \
+#define HDSPM_SYNC_CHECK(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .private_value = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_sync_check, \
+       .get = snd_hdspm_get_sync_check \
 }
 
+
 static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "No Lock", "Lock", "Sync" };
+       static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
-       uinfo->value.enumerated.items = 3;
+       uinfo->value.enumerated.items = 4;
        if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
                uinfo->value.enumerated.item =
-                   uinfo->value.enumerated.items - 1;
+                       uinfo->value.enumerated.items - 1;
        strcpy(uinfo->value.enumerated.name,
-              texts[uinfo->value.enumerated.item]);
+                       texts[uinfo->value.enumerated.item]);
        return 0;
 }
 
-static int hdspm_wc_sync_check(struct hdspm * hdspm)
+static int hdspm_wc_sync_check(struct hdspm *hdspm)
 {
-       if (hdspm->is_aes32) {
-               int status = hdspm_read(hdspm, HDSPM_statusRegister);
-               if (status & HDSPM_AES32_wcLock) {
-                       /* I don't know how to differenciate sync from lock.
-                          Doing as if sync for now */
+       int status, status2;
+
+       switch (hdspm->io_type) {
+       case AES32:
+               status = hdspm_read(hdspm, HDSPM_statusRegister);
+               if (status & HDSPM_wcSync)
                        return 2;
-               }
+               else if (status & HDSPM_wcLock)
+                       return 1;
                return 0;
-       } else {
-               int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+               break;
+
+       case MADI:
+               status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
                if (status2 & HDSPM_wcLock) {
                        if (status2 & HDSPM_wcSync)
                                return 2;
@@ -2716,29 +3636,30 @@ static int hdspm_wc_sync_check(struct hdspm * hdspm)
                                return 1;
                }
                return 0;
-       }
-}
+               break;
 
-static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol,
-                                      struct snd_ctl_elem_value *ucontrol)
-{
-       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       case RayDAT:
+       case AIO:
+               status = hdspm_read(hdspm, HDSPM_statusRegister);
 
-       ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm);
-       return 0;
-}
+               if (status & 0x2000000)
+                       return 2;
+               else if (status & 0x1000000)
+                       return 1;
+               return 0;
 
+               break;
 
-#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  .info = snd_hdspm_info_sync_check, \
-  .get = snd_hdspm_get_madisync_sync_check \
+       case MADIface:
+               break;
+       }
+
+
+       return 3;
 }
 
-static int hdspm_madisync_sync_check(struct hdspm * hdspm)
+
+static int hdspm_madi_sync_check(struct hdspm *hdspm)
 {
        int status = hdspm_read(hdspm, HDSPM_statusRegister);
        if (status & HDSPM_madiLock) {
@@ -2750,89 +3671,726 @@ static int hdspm_madisync_sync_check(struct hdspm * hdspm)
        return 0;
 }
 
-static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol,
-                                            struct snd_ctl_elem_value *
-                                            ucontrol)
+
+static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx)
 {
-       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       int status, lock, sync;
 
-       ucontrol->value.enumerated.item[0] =
-           hdspm_madisync_sync_check(hdspm);
+       status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+
+       lock = (status & (0x1<<idx)) ? 1 : 0;
+       sync = (status & (0x100<<idx)) ? 1 : 0;
+
+       if (lock && sync)
+               return 2;
+       else if (lock)
+               return 1;
        return 0;
 }
 
 
-#define HDSPM_AES_SYNC_CHECK(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-  .name = xname, \
-  .index = xindex, \
-  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  .info = snd_hdspm_info_sync_check, \
-  .get = snd_hdspm_get_aes_sync_check \
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm)
+{
+       int status, lock = 0, sync = 0;
+
+       switch (hdspm->io_type) {
+       case RayDAT:
+       case AIO:
+               status = hdspm_read(hdspm, HDSPM_RD_STATUS_3);
+               lock = (status & 0x400) ? 1 : 0;
+               sync = (status & 0x800) ? 1 : 0;
+               break;
+
+       case MADI:
+       case AES32:
+               status = hdspm_read(hdspm, HDSPM_statusRegister2);
+               lock = (status & HDSPM_syncInLock) ? 1 : 0;
+               sync = (status & HDSPM_syncInSync) ? 1 : 0;
+               break;
+
+       case MADIface:
+               break;
+       }
+
+       if (lock && sync)
+               return 2;
+       else if (lock)
+               return 1;
+
+       return 0;
 }
 
-static int hdspm_aes_sync_check(struct hdspm * hdspm, int idx)
+static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
 {
-       int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
-       if (status2 & (HDSPM_LockAES >> idx)) {
-               /* I don't know how to differenciate sync from lock.
-                  Doing as if sync for now */
+       int status2, lock, sync;
+       status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+       lock = (status2 & (0x0080 >> idx)) ? 1 : 0;
+       sync = (status2 & (0x8000 >> idx)) ? 1 : 0;
+
+       if (sync)
                return 2;
+       else if (lock)
+               return 1;
+       return 0;
+}
+
+
+static int hdspm_tco_sync_check(struct hdspm *hdspm)
+{
+       int status;
+
+       if (hdspm->tco) {
+               switch (hdspm->io_type) {
+               case MADI:
+               case AES32:
+                       status = hdspm_read(hdspm, HDSPM_statusRegister);
+                       if (status & HDSPM_tcoLock) {
+                               if (status & HDSPM_tcoSync)
+                                       return 2;
+                               else
+                                       return 1;
+                       }
+                       return 0;
+
+                       break;
+
+               case RayDAT:
+               case AIO:
+                       status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
+
+                       if (status & 0x8000000)
+                               return 2; /* Sync */
+                       if (status & 0x4000000)
+                               return 1; /* Lock */
+                       return 0; /* No signal */
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       return 3; /* N/A */
+}
+
+
+static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       int val = -1;
+
+       switch (hdspm->io_type) {
+       case RayDAT:
+               switch (kcontrol->private_value) {
+               case 0: /* WC */
+                       val = hdspm_wc_sync_check(hdspm); break;
+               case 7: /* TCO */
+                       val = hdspm_tco_sync_check(hdspm); break;
+               case 8: /* SYNC IN */
+                       val = hdspm_sync_in_sync_check(hdspm); break;
+               default:
+                       val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1);
+               }
+
+       case AIO:
+               switch (kcontrol->private_value) {
+               case 0: /* WC */
+                       val = hdspm_wc_sync_check(hdspm); break;
+               case 4: /* TCO */
+                       val = hdspm_tco_sync_check(hdspm); break;
+               case 5: /* SYNC IN */
+                       val = hdspm_sync_in_sync_check(hdspm); break;
+               default:
+                       val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1);
+               }
+
+       case MADI:
+               switch (kcontrol->private_value) {
+               case 0: /* WC */
+                       val = hdspm_wc_sync_check(hdspm); break;
+               case 1: /* MADI */
+                       val = hdspm_madi_sync_check(hdspm); break;
+               case 2: /* TCO */
+                       val = hdspm_tco_sync_check(hdspm); break;
+               case 3: /* SYNC_IN */
+                       val = hdspm_sync_in_sync_check(hdspm); break;
+               }
+
+       case MADIface:
+               val = hdspm_madi_sync_check(hdspm); /* MADI */
+               break;
+
+       case AES32:
+               switch (kcontrol->private_value) {
+               case 0: /* WC */
+                       val = hdspm_wc_sync_check(hdspm); break;
+               case 9: /* TCO */
+                       val = hdspm_tco_sync_check(hdspm); break;
+               case 10 /* SYNC IN */:
+                       val = hdspm_sync_in_sync_check(hdspm); break;
+               default: /* AES1 to AES8 */
+                        val = hdspm_aes_sync_check(hdspm,
+                                        kcontrol->private_value-1);
+               }
+
+       }
+
+       if (-1 == val)
+               val = 3;
+
+       ucontrol->value.enumerated.item[0] = val;
+       return 0;
+}
+
+
+
+/**
+ * TCO controls
+ **/
+static void hdspm_tco_write(struct hdspm *hdspm)
+{
+       unsigned int tc[4] = { 0, 0, 0, 0};
+
+       switch (hdspm->tco->input) {
+       case 0:
+               tc[2] |= HDSPM_TCO2_set_input_MSB;
+               break;
+       case 1:
+               tc[2] |= HDSPM_TCO2_set_input_LSB;
+               break;
+       default:
+               break;
+       }
+
+       switch (hdspm->tco->framerate) {
+       case 1:
+               tc[1] |= HDSPM_TCO1_LTC_Format_LSB;
+               break;
+       case 2:
+               tc[1] |= HDSPM_TCO1_LTC_Format_MSB;
+               break;
+       case 3:
+               tc[1] |= HDSPM_TCO1_LTC_Format_MSB +
+                       HDSPM_TCO1_set_drop_frame_flag;
+               break;
+       case 4:
+               tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
+                       HDSPM_TCO1_LTC_Format_MSB;
+               break;
+       case 5:
+               tc[1] |= HDSPM_TCO1_LTC_Format_LSB +
+                       HDSPM_TCO1_LTC_Format_MSB +
+                       HDSPM_TCO1_set_drop_frame_flag;
+               break;
+       default:
+               break;
+       }
+
+       switch (hdspm->tco->wordclock) {
+       case 1:
+               tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB;
+               break;
+       case 2:
+               tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB;
+               break;
+       default:
+               break;
+       }
+
+       switch (hdspm->tco->samplerate) {
+       case 1:
+               tc[2] |= HDSPM_TCO2_set_freq;
+               break;
+       case 2:
+               tc[2] |= HDSPM_TCO2_set_freq_from_app;
+               break;
+       default:
+               break;
        }
+
+       switch (hdspm->tco->pull) {
+       case 1:
+               tc[2] |= HDSPM_TCO2_set_pull_up;
+               break;
+       case 2:
+               tc[2] |= HDSPM_TCO2_set_pull_down;
+               break;
+       case 3:
+               tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4;
+               break;
+       case 4:
+               tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4;
+               break;
+       default:
+               break;
+       }
+
+       if (1 == hdspm->tco->term) {
+               tc[2] |= HDSPM_TCO2_set_term_75R;
+       }
+
+       hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]);
+       hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]);
+       hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]);
+       hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]);
+}
+
+
+#define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_tco_sample_rate, \
+       .get = snd_hdspm_get_tco_sample_rate, \
+       .put = snd_hdspm_put_tco_sample_rate \
+}
+
+static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "44.1 kHz", "48 kHz" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+                       texts[uinfo->value.enumerated.item]);
+
        return 0;
 }
 
-static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol,
+static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate;
+
+       return 0;
+}
+
+static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) {
+               hdspm->tco->samplerate = ucontrol->value.enumerated.item[0];
+
+               hdspm_tco_write(hdspm);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+
+#define HDSPM_TCO_PULL(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_tco_pull, \
+       .get = snd_hdspm_get_tco_pull, \
+       .put = snd_hdspm_put_tco_pull \
+}
+
+static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 5;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+                       texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm->tco->pull;
+
+       return 0;
+}
+
+static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) {
+               hdspm->tco->pull = ucontrol->value.enumerated.item[0];
+
+               hdspm_tco_write(hdspm);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+#define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_tco_wck_conversion, \
+       .get = snd_hdspm_get_tco_wck_conversion, \
+       .put = snd_hdspm_put_tco_wck_conversion \
+}
+
+static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+                       texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock;
+
+       return 0;
+}
+
+static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) {
+               hdspm->tco->wordclock = ucontrol->value.enumerated.item[0];
+
+               hdspm_tco_write(hdspm);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+
+#define HDSPM_TCO_FRAME_RATE(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_tco_frame_rate, \
+       .get = snd_hdspm_get_tco_frame_rate, \
+       .put = snd_hdspm_put_tco_frame_rate \
+}
+
+static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "24 fps", "25 fps", "29.97fps",
+               "29.97 dfps", "30 fps", "30 dfps" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 6;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+                       texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_value *ucontrol)
 {
-       int offset;
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       offset = ucontrol->id.index - 1;
-       if (offset < 0 || offset >= 8)
-               return -EINVAL;
+       ucontrol->value.enumerated.item[0] = hdspm->tco->framerate;
 
-       ucontrol->value.enumerated.item[0] =
-               hdspm_aes_sync_check(hdspm, offset);
        return 0;
 }
 
+static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) {
+               hdspm->tco->framerate = ucontrol->value.enumerated.item[0];
 
-static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
+               hdspm_tco_write(hdspm);
 
-       HDSPM_MIXER("Mixer", 0),
-/* 'Sample Clock Source' complies with the alsa control naming scheme */
-       HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
+               return 1;
+       }
+
+       return 0;
+}
+
+
+#define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_tco_sync_source, \
+       .get = snd_hdspm_get_tco_sync_source, \
+       .put = snd_hdspm_put_tco_sync_source \
+}
+
+static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "LTC", "Video", "WCK" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
 
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+                       texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm->tco->input;
+
+       return 0;
+}
+
+static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) {
+               hdspm->tco->input = ucontrol->value.enumerated.item[0];
+
+               hdspm_tco_write(hdspm);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+
+#define HDSPM_TCO_WORD_TERM(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+       .info = snd_hdspm_info_tco_word_term, \
+       .get = snd_hdspm_get_tco_word_term, \
+       .put = snd_hdspm_put_tco_word_term \
+}
+
+static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+
+static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm->tco->term;
+
+       return 0;
+}
+
+
+static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) {
+               hdspm->tco->term = ucontrol->value.enumerated.item[0];
+
+               hdspm_tco_write(hdspm);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+
+
+
+static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
+       HDSPM_MIXER("Mixer", 0),
+       HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
        HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
        HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
        HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
        HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
-/* 'External Rate' complies with the alsa control naming scheme */
-       HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
-       HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
-       HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0),
+       HDSPM_SYNC_CHECK("WC SyncCheck", 0),
+       HDSPM_SYNC_CHECK("MADI SyncCheck", 1),
+       HDSPM_SYNC_CHECK("TCO SyncCHeck", 2),
+       HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
        HDSPM_LINE_OUT("Line Out", 0),
        HDSPM_TX_64("TX 64 channels mode", 0),
        HDSPM_C_TMS("Clear Track Marker", 0),
        HDSPM_SAFE_MODE("Safe Mode", 0),
-       HDSPM_INPUT_SELECT("Input Select", 0),
+       HDSPM_INPUT_SELECT("Input Select", 0)
 };
 
-static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
 
+static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
        HDSPM_MIXER("Mixer", 0),
-/* 'Sample Clock Source' complies with the alsa control naming scheme */
-       HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
+       HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+       HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+       HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+       HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
+       HDSPM_TX_64("TX 64 channels mode", 0),
+       HDSPM_C_TMS("Clear Track Marker", 0),
+       HDSPM_SAFE_MODE("Safe Mode", 0)
+};
 
+static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
+       HDSPM_MIXER("Mixer", 0),
+       HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
        HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
        HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
        HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
        HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
-/* 'External Rate' complies with the alsa control naming scheme */
        HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
-       HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
-/*     HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */
+       HDSPM_SYNC_CHECK("WC SyncCheck", 0),
+       HDSPM_SYNC_CHECK("AES SyncCheck", 1),
+       HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
+       HDSPM_SYNC_CHECK("ADAT SyncCheck", 3),
+       HDSPM_SYNC_CHECK("TCO SyncCheck", 4),
+       HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5)
+
+               /*
+                  HDSPM_INPUT_SELECT("Input Select", 0),
+                  HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0),
+                  HDSPM_PROFESSIONAL("SPDIF Out Professional", 0);
+                  HDSPM_SPDIF_IN("SPDIF In", 0);
+                  HDSPM_BREAKOUT_CABLE("Breakout Cable", 0);
+                  HDSPM_INPUT_LEVEL("Input Level", 0);
+                  HDSPM_OUTPUT_LEVEL("Output Level", 0);
+                  HDSPM_PHONES("Phones", 0);
+                  */
+};
+
+static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
+       HDSPM_MIXER("Mixer", 0),
+       HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+       HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0),
+       HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0),
+       HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+       HDSPM_SYNC_CHECK("WC SyncCheck", 0),
+       HDSPM_SYNC_CHECK("AES SyncCheck", 1),
+       HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2),
+       HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3),
+       HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4),
+       HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5),
+       HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6),
+       HDSPM_SYNC_CHECK("TCO SyncCheck", 7),
+       HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8)
+};
+
+static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
+       HDSPM_MIXER("Mixer", 0),
+       HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
+       HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+       HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
+       HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
+       HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+       HDSPM_SYNC_CHECK("WC Sync Check", 0),
+       HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
+       HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
+       HDSPM_SYNC_CHECK("AES3 Sync Check", 3),
+       HDSPM_SYNC_CHECK("AES4 Sync Check", 4),
+       HDSPM_SYNC_CHECK("AES5 Sync Check", 5),
+       HDSPM_SYNC_CHECK("AES6 Sync Check", 6),
+       HDSPM_SYNC_CHECK("AES7 Sync Check", 7),
+       HDSPM_SYNC_CHECK("AES8 Sync Check", 8),
+       HDSPM_SYNC_CHECK("TCO Sync Check", 9),
+       HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10),
        HDSPM_LINE_OUT("Line Out", 0),
        HDSPM_EMPHASIS("Emphasis", 0),
        HDSPM_DOLBY("Non Audio", 0),
@@ -2842,6 +4400,19 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
        HDSPM_QS_WIRE("Quad Speed Wire Mode", 0),
 };
 
+
+
+/* Control elements for the optional TCO module */
+static struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
+       HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0),
+       HDSPM_TCO_PULL("TCO Pull", 0),
+       HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
+       HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
+       HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
+       HDSPM_TCO_WORD_TERM("TCO Word Term", 0)
+};
+
+
 static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
 
 
@@ -2849,78 +4420,76 @@ static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm)
 {
        int i;
 
-       for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) {
+       for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) {
                if (hdspm->system_sample_rate > 48000) {
                        hdspm->playback_mixer_ctls[i]->vd[0].access =
-                           SNDRV_CTL_ELEM_ACCESS_INACTIVE |
-                           SNDRV_CTL_ELEM_ACCESS_READ |
-                           SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+                               SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+                               SNDRV_CTL_ELEM_ACCESS_READ |
+                               SNDRV_CTL_ELEM_ACCESS_VOLATILE;
                } else {
                        hdspm->playback_mixer_ctls[i]->vd[0].access =
-                           SNDRV_CTL_ELEM_ACCESS_READWRITE |
-                           SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+                               SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                               SNDRV_CTL_ELEM_ACCESS_VOLATILE;
                }
                snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
-                              SNDRV_CTL_EVENT_MASK_INFO,
-                              &hdspm->playback_mixer_ctls[i]->id);
+                               SNDRV_CTL_EVENT_MASK_INFO,
+                               &hdspm->playback_mixer_ctls[i]->id);
        }
 
        return 0;
 }
 
 
-static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm)
+static int snd_hdspm_create_controls(struct snd_card *card,
+                                       struct hdspm *hdspm)
 {
        unsigned int idx, limit;
        int err;
        struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new *list = NULL;
 
-       /* add control list first */
-       if (hdspm->is_aes32) {
-               struct snd_kcontrol_new aes_sync_ctl =
-                       HDSPM_AES_SYNC_CHECK("AES Lock Status", 0);
+       switch (hdspm->io_type) {
+       case MADI:
+               list = snd_hdspm_controls_madi;
+               limit = ARRAY_SIZE(snd_hdspm_controls_madi);
+               break;
+       case MADIface:
+               list = snd_hdspm_controls_madiface;
+               limit = ARRAY_SIZE(snd_hdspm_controls_madiface);
+               break;
+       case AIO:
+               list = snd_hdspm_controls_aio;
+               limit = ARRAY_SIZE(snd_hdspm_controls_aio);
+               break;
+       case RayDAT:
+               list = snd_hdspm_controls_raydat;
+               limit = ARRAY_SIZE(snd_hdspm_controls_raydat);
+               break;
+       case AES32:
+               list = snd_hdspm_controls_aes32;
+               limit = ARRAY_SIZE(snd_hdspm_controls_aes32);
+               break;
+       }
 
-               for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32);
-                    idx++) {
-                       err = snd_ctl_add(card,
-                                         snd_ctl_new1(&snd_hdspm_controls_aes32[idx],
-                                                      hdspm));
-                       if (err < 0)
-                               return err;
-               }
-               for (idx = 1; idx <= 8; idx++) {
-                       aes_sync_ctl.index = idx;
-                       err = snd_ctl_add(card,
-                                         snd_ctl_new1(&aes_sync_ctl, hdspm));
-                       if (err < 0)
-                               return err;
-               }
-       } else {
-               for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi);
-                    idx++) {
+       if (NULL != list) {
+               for (idx = 0; idx < limit; idx++) {
                        err = snd_ctl_add(card,
-                                         snd_ctl_new1(&snd_hdspm_controls_madi[idx],
-                                                      hdspm));
+                                       snd_ctl_new1(&list[idx], hdspm));
                        if (err < 0)
                                return err;
                }
        }
 
-       /* Channel playback mixer as default control 
-          Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders,
-          thats too * big for any alsamixer they are accessible via special
-          IOCTL on hwdep and the mixer 2dimensional mixer control
-       */
 
+       /* create simple 1:1 playback mixer controls */
        snd_hdspm_playback_mixer.name = "Chn";
-       limit = HDSPM_MAX_CHANNELS;
-
-       /* The index values are one greater than the channel ID so that
-        * alsamixer will display them correctly. We want to use the index
-        * for fast lookup of the relevant channel, but if we use it at all,
-        * most ALSA software does the wrong thing with it ...
-        */
-
+       if (hdspm->system_sample_rate >= 128000) {
+               limit = hdspm->qs_out_channels;
+       } else if (hdspm->system_sample_rate >= 64000) {
+               limit = hdspm->ds_out_channels;
+       } else {
+               limit = hdspm->ss_out_channels;
+       }
        for (idx = 0; idx < limit; ++idx) {
                snd_hdspm_playback_mixer.index = idx + 1;
                kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm);
@@ -2930,11 +4499,24 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm
                hdspm->playback_mixer_ctls[idx] = kctl;
        }
 
+
+       if (hdspm->tco) {
+               /* add tco control elements */
+               list = snd_hdspm_controls_tco;
+               limit = ARRAY_SIZE(snd_hdspm_controls_tco);
+               for (idx = 0; idx < limit; idx++) {
+                       err = snd_ctl_add(card,
+                                       snd_ctl_new1(&list[idx], hdspm));
+                       if (err < 0)
+                               return err;
+               }
+       }
+
        return 0;
 }
 
 /*------------------------------------------------------------
-   /proc interface 
+   /proc interface
  ------------------------------------------------------------*/
 
 static void
@@ -2942,72 +4524,178 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
                         struct snd_info_buffer *buffer)
 {
        struct hdspm *hdspm = entry->private_data;
-       unsigned int status;
-       unsigned int status2;
+       unsigned int status, status2, control, freq;
+
        char *pref_sync_ref;
        char *autosync_ref;
        char *system_clock_mode;
-       char *clock_source;
        char *insel;
-       char *syncref;
        int x, x2;
 
+       /* TCO stuff */
+       int a, ltc, frames, seconds, minutes, hours;
+       unsigned int period;
+       u64 freq_const = 0;
+       u32 rate;
+
        status = hdspm_read(hdspm, HDSPM_statusRegister);
        status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+       control = hdspm->control_register;
+       freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
 
        snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
-                   hdspm->card_name, hdspm->card->number + 1,
-                   hdspm->firmware_rev,
-                   (status2 & HDSPM_version0) |
-                   (status2 & HDSPM_version1) | (status2 &
-                                                 HDSPM_version2));
+                       hdspm->card_name, hdspm->card->number + 1,
+                       hdspm->firmware_rev,
+                       (status2 & HDSPM_version0) |
+                       (status2 & HDSPM_version1) | (status2 &
+                               HDSPM_version2));
+
+       snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
+                       (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
+                       (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF);
 
        snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
-                   hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
+                       hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
 
        snd_iprintf(buffer, "--- System ---\n");
 
        snd_iprintf(buffer,
-                   "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
-                   status & HDSPM_audioIRQPending,
-                   (status & HDSPM_midi0IRQPending) ? 1 : 0,
-                   (status & HDSPM_midi1IRQPending) ? 1 : 0,
-                   hdspm->irq_count);
+               "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
+               status & HDSPM_audioIRQPending,
+               (status & HDSPM_midi0IRQPending) ? 1 : 0,
+               (status & HDSPM_midi1IRQPending) ? 1 : 0,
+               hdspm->irq_count);
        snd_iprintf(buffer,
-                   "HW pointer: id = %d, rawptr = %d (%d->%d) "
-                   "estimated= %ld (bytes)\n",
-                   ((status & HDSPM_BufferID) ? 1 : 0),
-                   (status & HDSPM_BufferPositionMask),
-                   (status & HDSPM_BufferPositionMask) %
-                   (2 * (int)hdspm->period_bytes),
-                   ((status & HDSPM_BufferPositionMask) - 64) %
-                   (2 * (int)hdspm->period_bytes),
-                   (long) hdspm_hw_pointer(hdspm) * 4);
+               "HW pointer: id = %d, rawptr = %d (%d->%d) "
+               "estimated= %ld (bytes)\n",
+               ((status & HDSPM_BufferID) ? 1 : 0),
+               (status & HDSPM_BufferPositionMask),
+               (status & HDSPM_BufferPositionMask) %
+               (2 * (int)hdspm->period_bytes),
+               ((status & HDSPM_BufferPositionMask) - 64) %
+               (2 * (int)hdspm->period_bytes),
+               (long) hdspm_hw_pointer(hdspm) * 4);
 
        snd_iprintf(buffer,
-                   "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
-                   hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
-                   hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
-                   hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
-                   hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
+               "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
+               hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
        snd_iprintf(buffer,
-                   "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
-                   "status2=0x%x\n",
-                   hdspm->control_register, hdspm->control2_register,
-                   status, status2);
+               "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
+               hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
+       snd_iprintf(buffer,
+               "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
+               "status2=0x%x\n",
+               hdspm->control_register, hdspm->control2_register,
+               status, status2);
+       if (status & HDSPM_tco_detect) {
+               snd_iprintf(buffer, "TCO module detected.\n");
+               a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
+               if (a & HDSPM_TCO1_LTC_Input_valid) {
+                       snd_iprintf(buffer, "  LTC valid, ");
+                       switch (a & (HDSPM_TCO1_LTC_Format_LSB |
+                                               HDSPM_TCO1_LTC_Format_MSB)) {
+                       case 0:
+                               snd_iprintf(buffer, "24 fps, ");
+                               break;
+                       case HDSPM_TCO1_LTC_Format_LSB:
+                               snd_iprintf(buffer, "25 fps, ");
+                               break;
+                       case HDSPM_TCO1_LTC_Format_MSB:
+                               snd_iprintf(buffer, "29.97 fps, ");
+                               break;
+                       default:
+                               snd_iprintf(buffer, "30 fps, ");
+                               break;
+                       }
+                       if (a & HDSPM_TCO1_set_drop_frame_flag) {
+                               snd_iprintf(buffer, "drop frame\n");
+                       } else {
+                               snd_iprintf(buffer, "full frame\n");
+                       }
+               } else {
+                       snd_iprintf(buffer, "  no LTC\n");
+               }
+               if (a & HDSPM_TCO1_Video_Input_Format_NTSC) {
+                       snd_iprintf(buffer, "  Video: NTSC\n");
+               } else if (a & HDSPM_TCO1_Video_Input_Format_PAL) {
+                       snd_iprintf(buffer, "  Video: PAL\n");
+               } else {
+                       snd_iprintf(buffer, "  No video\n");
+               }
+               if (a & HDSPM_TCO1_TCO_lock) {
+                       snd_iprintf(buffer, "  Sync: lock\n");
+               } else {
+                       snd_iprintf(buffer, "  Sync: no lock\n");
+               }
+
+               switch (hdspm->io_type) {
+               case MADI:
+               case AES32:
+                       freq_const = 110069313433624ULL;
+                       break;
+               case RayDAT:
+               case AIO:
+                       freq_const = 104857600000000ULL;
+                       break;
+               case MADIface:
+                       break; /* no TCO possible */
+               }
+
+               period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+               snd_iprintf(buffer, "    period: %u\n", period);
+
+
+               /* rate = freq_const/period; */
+               rate = div_u64(freq_const, period);
+
+               if (control & HDSPM_QuadSpeed) {
+                       rate *= 4;
+               } else if (control & HDSPM_DoubleSpeed) {
+                       rate *= 2;
+               }
+
+               snd_iprintf(buffer, "  Frequency: %u Hz\n",
+                               (unsigned int) rate);
+
+               ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
+               frames = ltc & 0xF;
+               ltc >>= 4;
+               frames += (ltc & 0x3) * 10;
+               ltc >>= 4;
+               seconds = ltc & 0xF;
+               ltc >>= 4;
+               seconds += (ltc & 0x7) * 10;
+               ltc >>= 4;
+               minutes = ltc & 0xF;
+               ltc >>= 4;
+               minutes += (ltc & 0x7) * 10;
+               ltc >>= 4;
+               hours = ltc & 0xF;
+               ltc >>= 4;
+               hours += (ltc & 0x3) * 10;
+               snd_iprintf(buffer,
+                       "  LTC In: %02d:%02d:%02d:%02d\n",
+                       hours, minutes, seconds, frames);
+
+       } else {
+               snd_iprintf(buffer, "No TCO module detected.\n");
+       }
 
        snd_iprintf(buffer, "--- Settings ---\n");
 
        x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
-                                          HDSPM_LatencyMask));
+                                                       HDSPM_LatencyMask));
 
        snd_iprintf(buffer,
-                   "Size (Latency): %d samples (2 periods of %lu bytes)\n",
-                   x, (unsigned long) hdspm->period_bytes);
+               "Size (Latency): %d samples (2 periods of %lu bytes)\n",
+               x, (unsigned long) hdspm->period_bytes);
 
-       snd_iprintf(buffer, "Line out: %s,   Precise Pointer: %s\n",
-                   (hdspm->control_register & HDSPM_LineOut) ? "on " : "off",
-                   (hdspm->precise_ptr) ? "on" : "off");
+       snd_iprintf(buffer, "Line out: %s\n",
+               (hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
 
        switch (hdspm->control_register & HDSPM_InputMask) {
        case HDSPM_InputOptical:
@@ -3017,63 +4705,22 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
                insel = "Coaxial";
                break;
        default:
-               insel = "Unknown";
-       }
-
-       switch (hdspm->control_register & HDSPM_SyncRefMask) {
-       case HDSPM_SyncRef_Word:
-               syncref = "WordClock";
-               break;
-       case HDSPM_SyncRef_MADI:
-               syncref = "MADI";
-               break;
-       default:
-               syncref = "Unknown";
+               insel = "Unkown";
        }
-       snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel,
-                   syncref);
 
        snd_iprintf(buffer,
-                   "ClearTrackMarker = %s, Transmit in %s Channel Mode, "
-                   "Auto Input %s\n",
-                   (hdspm->
-                    control_register & HDSPM_clr_tms) ? "on" : "off",
-                   (hdspm->
-                    control_register & HDSPM_TX_64ch) ? "64" : "56",
-                   (hdspm->
-                    control_register & HDSPM_AutoInp) ? "on" : "off");
+               "ClearTrackMarker = %s, Transmit in %s Channel Mode, "
+               "Auto Input %s\n",
+               (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
+               (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
+               (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
+
 
-       switch (hdspm_clock_source(hdspm)) {
-       case HDSPM_CLOCK_SOURCE_AUTOSYNC:
-               clock_source = "AutoSync";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
-               clock_source = "Internal 32 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
-               clock_source = "Internal 44.1 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
-               clock_source = "Internal 48 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
-               clock_source = "Internal 64 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
-               clock_source = "Internal 88.2 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
-               clock_source = "Internal 96 kHz";
-               break;
-       default:
-               clock_source = "Error";
-       }
-       snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
        if (!(hdspm->control_register & HDSPM_ClockModeMaster))
-               system_clock_mode = "Slave";
+               system_clock_mode = "AutoSync";
        else
                system_clock_mode = "Master";
-       snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
+       snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode);
 
        switch (hdspm_pref_sync_ref(hdspm)) {
        case HDSPM_SYNC_FROM_WORD:
@@ -3082,15 +4729,21 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
        case HDSPM_SYNC_FROM_MADI:
                pref_sync_ref = "MADI Sync";
                break;
+       case HDSPM_SYNC_FROM_TCO:
+               pref_sync_ref = "TCO";
+               break;
+       case HDSPM_SYNC_FROM_SYNC_IN:
+               pref_sync_ref = "Sync In";
+               break;
        default:
                pref_sync_ref = "XXXX Clock";
                break;
        }
        snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
-                   pref_sync_ref);
+                       pref_sync_ref);
 
        snd_iprintf(buffer, "System Clock Frequency: %d\n",
-                   hdspm->system_sample_rate);
+                       hdspm->system_sample_rate);
 
 
        snd_iprintf(buffer, "--- Status:\n");
@@ -3099,12 +4752,18 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
        x2 = status2 & HDSPM_wcSync;
 
        snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
-                   (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
-                   "NoLock",
-                   (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
-                   "NoLock");
+                       (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
+                       "NoLock",
+                       (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
+                       "NoLock");
 
        switch (hdspm_autosync_ref(hdspm)) {
+       case HDSPM_AUTOSYNC_FROM_SYNC_IN:
+               autosync_ref = "Sync In";
+               break;
+       case HDSPM_AUTOSYNC_FROM_TCO:
+               autosync_ref = "TCO";
+               break;
        case HDSPM_AUTOSYNC_FROM_WORD:
                autosync_ref = "Word Clock";
                break;
@@ -3119,15 +4778,15 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
                break;
        }
        snd_iprintf(buffer,
-                   "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
-                   autosync_ref, hdspm_external_sample_rate(hdspm),
-                   (status & HDSPM_madiFreqMask) >> 22,
-                   (status2 & HDSPM_wcFreqMask) >> 5);
+               "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
+               autosync_ref, hdspm_external_sample_rate(hdspm),
+               (status & HDSPM_madiFreqMask) >> 22,
+               (status2 & HDSPM_wcFreqMask) >> 5);
 
        snd_iprintf(buffer, "Input: %s, Mode=%s\n",
-                   (status & HDSPM_AB_int) ? "Coax" : "Optical",
-                   (status & HDSPM_RX_64ch) ? "64 channels" :
-                   "56 channels");
+               (status & HDSPM_AB_int) ? "Coax" : "Optical",
+               (status & HDSPM_RX_64ch) ? "64 channels" :
+               "56 channels");
 
        snd_iprintf(buffer, "\n");
 }
@@ -3142,8 +4801,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
        unsigned int timecode;
        int pref_syncref;
        char *autosync_ref;
-       char *system_clock_mode;
-       char *clock_source;
        int x;
 
        status = hdspm_read(hdspm, HDSPM_statusRegister);
@@ -3183,24 +4840,27 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
                    hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
                    hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
        snd_iprintf(buffer,
-                   "Register: ctrl1=0x%x, status1=0x%x, status2=0x%x, "
-                   "timecode=0x%x\n",
-                   hdspm->control_register,
-                   status, status2, timecode);
+                   "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
+                   hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
+                   hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
+       snd_iprintf(buffer,
+                   "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
+                   "status2=0x%x\n",
+                   hdspm->control_register, hdspm->control2_register,
+                   status, status2);
 
        snd_iprintf(buffer, "--- Settings ---\n");
 
        x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
-                                          HDSPM_LatencyMask));
+                               HDSPM_LatencyMask));
 
        snd_iprintf(buffer,
                    "Size (Latency): %d samples (2 periods of %lu bytes)\n",
                    x, (unsigned long) hdspm->period_bytes);
 
-       snd_iprintf(buffer, "Line out: %s,   Precise Pointer: %s\n",
+       snd_iprintf(buffer, "Line out: %s\n",
                    (hdspm->
-                    control_register & HDSPM_LineOut) ? "on " : "off",
-                   (hdspm->precise_ptr) ? "on" : "off");
+                    control_register & HDSPM_LineOut) ? "on " : "off");
 
        snd_iprintf(buffer,
                    "ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
@@ -3211,46 +4871,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
                    (hdspm->
                     control_register & HDSPM_Dolby) ? "on" : "off");
 
-       switch (hdspm_clock_source(hdspm)) {
-       case HDSPM_CLOCK_SOURCE_AUTOSYNC:
-               clock_source = "AutoSync";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
-               clock_source = "Internal 32 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
-               clock_source = "Internal 44.1 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
-               clock_source = "Internal 48 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
-               clock_source = "Internal 64 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
-               clock_source = "Internal 88.2 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
-               clock_source = "Internal 96 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
-               clock_source = "Internal 128 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
-               clock_source = "Internal 176.4 kHz";
-               break;
-       case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
-               clock_source = "Internal 192 kHz";
-               break;
-       default:
-               clock_source = "Error";
-       }
-       snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
-       if (!(hdspm->control_register & HDSPM_ClockModeMaster))
-               system_clock_mode = "Slave";
-       else
-               system_clock_mode = "Master";
-       snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
 
        pref_syncref = hdspm_pref_sync_ref(hdspm);
        if (pref_syncref == 0)
@@ -3274,38 +4894,108 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
        snd_iprintf(buffer, "--- Status:\n");
 
        snd_iprintf(buffer, "Word: %s  Frequency: %d\n",
-                   (status & HDSPM_AES32_wcLock)? "Sync   " : "No Lock",
+                   (status & HDSPM_AES32_wcLock) ? "Sync   " : "No Lock",
                    HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF));
 
        for (x = 0; x < 8; x++) {
                snd_iprintf(buffer, "AES%d: %s  Frequency: %d\n",
                            x+1,
                            (status2 & (HDSPM_LockAES >> x)) ?
-                           "Sync   ": "No Lock",
+                           "Sync   " : "No Lock",
                            HDSPM_bit2freq((timecode >> (4*x)) & 0xF));
        }
 
        switch (hdspm_autosync_ref(hdspm)) {
-       case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break;
-       case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break;
-       default: autosync_ref = "---"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_NONE:
+               autosync_ref = "None"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_WORD:
+               autosync_ref = "Word Clock"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES1:
+               autosync_ref = "AES1"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES2:
+               autosync_ref = "AES2"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES3:
+               autosync_ref = "AES3"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES4:
+               autosync_ref = "AES4"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES5:
+               autosync_ref = "AES5"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES6:
+               autosync_ref = "AES6"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES7:
+               autosync_ref = "AES7"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_AES8:
+               autosync_ref = "AES8"; break;
+       default:
+               autosync_ref = "---"; break;
        }
        snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
 
        snd_iprintf(buffer, "\n");
 }
 
+static void
+snd_hdspm_proc_read_raydat(struct snd_info_entry *entry,
+                        struct snd_info_buffer *buffer)
+{
+       struct hdspm *hdspm = entry->private_data;
+       unsigned int status1, status2, status3, control, i;
+       unsigned int lock, sync;
+
+       status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */
+       status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */
+       status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */
+
+       control = hdspm->control_register;
+
+       snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1);
+       snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2);
+       snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3);
+
+
+       snd_iprintf(buffer, "\n*** CLOCK MODE\n\n");
+
+       snd_iprintf(buffer, "Clock mode      : %s\n",
+               (hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave");
+       snd_iprintf(buffer, "System frequency: %d Hz\n",
+               hdspm_get_system_sample_rate(hdspm));
+
+       snd_iprintf(buffer, "\n*** INPUT STATUS\n\n");
+
+       lock = 0x1;
+       sync = 0x100;
+
+       for (i = 0; i < 8; i++) {
+               snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n",
+                               i,
+                               (status1 & lock) ? 1 : 0,
+                               (status1 & sync) ? 1 : 0,
+                               texts_freq[(status2 >> (i * 4)) & 0xF]);
+
+               lock = lock<<1;
+               sync = sync<<1;
+       }
+
+       snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n",
+                       (status1 & 0x1000000) ? 1 : 0,
+                       (status1 & 0x2000000) ? 1 : 0,
+                       texts_freq[(status1 >> 16) & 0xF]);
+
+       snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n",
+                       (status1 & 0x4000000) ? 1 : 0,
+                       (status1 & 0x8000000) ? 1 : 0,
+                       texts_freq[(status1 >> 20) & 0xF]);
+
+       snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n",
+                       (status3 & 0x400) ? 1 : 0,
+                       (status3 & 0x800) ? 1 : 0,
+                       texts_freq[(status2 >> 12) & 0xF]);
+
+}
+
 #ifdef CONFIG_SND_DEBUG
 static void
-snd_hdspm_proc_read_debug(struct snd_info_entry * entry,
+snd_hdspm_proc_read_debug(struct snd_info_entry *entry,
                          struct snd_info_buffer *buffer)
 {
        struct hdspm *hdspm = entry->private_data;
@@ -3322,16 +5012,68 @@ snd_hdspm_proc_read_debug(struct snd_info_entry * entry,
 #endif
 
 
+static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry,
+                         struct snd_info_buffer *buffer)
+{
+       struct hdspm *hdspm = entry->private_data;
+       int i;
+
+       snd_iprintf(buffer, "# generated by hdspm\n");
+
+       for (i = 0; i < hdspm->max_channels_in; i++) {
+               snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]);
+       }
+}
+
+static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry,
+                         struct snd_info_buffer *buffer)
+{
+       struct hdspm *hdspm = entry->private_data;
+       int i;
+
+       snd_iprintf(buffer, "# generated by hdspm\n");
+
+       for (i = 0; i < hdspm->max_channels_out; i++) {
+               snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]);
+       }
+}
+
 
-static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm)
+static void __devinit snd_hdspm_proc_init(struct hdspm *hdspm)
 {
        struct snd_info_entry *entry;
 
-       if (!snd_card_proc_new(hdspm->card, "hdspm", &entry))
-               snd_info_set_text_ops(entry, hdspm,
-                                     hdspm->is_aes32 ?
-                                     snd_hdspm_proc_read_aes32 :
-                                     snd_hdspm_proc_read_madi);
+       if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) {
+               switch (hdspm->io_type) {
+               case AES32:
+                       snd_info_set_text_ops(entry, hdspm,
+                                       snd_hdspm_proc_read_aes32);
+                       break;
+               case MADI:
+                       snd_info_set_text_ops(entry, hdspm,
+                                       snd_hdspm_proc_read_madi);
+                       break;
+               case MADIface:
+                       /* snd_info_set_text_ops(entry, hdspm,
+                        snd_hdspm_proc_read_madiface); */
+                       break;
+               case RayDAT:
+                       snd_info_set_text_ops(entry, hdspm,
+                                       snd_hdspm_proc_read_raydat);
+                       break;
+               case AIO:
+                       break;
+               }
+       }
+
+       if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) {
+               snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in);
+       }
+
+       if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) {
+               snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out);
+       }
+
 #ifdef CONFIG_SND_DEBUG
        /* debug file to read all hdspm registers */
        if (!snd_card_proc_new(hdspm->card, "debug", &entry))
@@ -3341,47 +5083,48 @@ static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm)
 }
 
 /*------------------------------------------------------------
-   hdspm intitialize 
+   hdspm intitialize
  ------------------------------------------------------------*/
 
 static int snd_hdspm_set_defaults(struct hdspm * hdspm)
 {
-       unsigned int i;
-
        /* ASSUMPTION: hdspm->lock is either held, or there is no need to
           hold it (e.g. during module initialization).
-        */
+          */
 
        /* set defaults:       */
 
-       if (hdspm->is_aes32)
+       hdspm->settings_register = 0;
+
+       switch (hdspm->io_type) {
+       case MADI:
+       case MADIface:
+               hdspm->control_register =
+                       0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
+               break;
+
+       case RayDAT:
+       case AIO:
+               hdspm->settings_register = 0x1 + 0x1000;
+               /* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0,
+                * line_out */
+               hdspm->control_register =
+                       0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000;
+               break;
+
+       case AES32:
                hdspm->control_register =
                        HDSPM_ClockModeMaster | /* Master Cloack Mode on */
-                       hdspm_encode_latency(7) | /* latency maximum =
-                                                  * 8192 samples
-                                                  */
+                       hdspm_encode_latency(7) | /* latency max=8192samples */
                        HDSPM_SyncRef0 |        /* AES1 is syncclock */
                        HDSPM_LineOut | /* Analog output in */
                        HDSPM_Professional;  /* Professional mode */
-       else
-               hdspm->control_register =
-                       HDSPM_ClockModeMaster | /* Master Cloack Mode on */
-                       hdspm_encode_latency(7) | /* latency maximum =
-                                                  * 8192 samples
-                                                  */
-                       HDSPM_InputCoaxial |    /* Input Coax not Optical */
-                       HDSPM_SyncRef_MADI |    /* Madi is syncclock */
-                       HDSPM_LineOut | /* Analog output in */
-                       HDSPM_TX_64ch | /* transmit in 64ch mode */
-                       HDSPM_AutoInp;  /* AutoInput chossing (takeover) */
-
-       /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */
-       /* !  HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */
-       /* ! HDSPM_clr_tms = do not clear bits in track marks */
+               break;
+       }
 
        hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
 
-        if (!hdspm->is_aes32) {
+       if (AES32 == hdspm->io_type) {
                /* No control2 register for AES32 */
 #ifdef SNDRV_BIG_ENDIAN
                hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
@@ -3397,57 +5140,59 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
 
        all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
 
-       if (line_outs_monitor[hdspm->dev]) {
-
-               snd_printk(KERN_INFO "HDSPM: "
-                          "sending all playback streams to line outs.\n");
-
-               for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) {
-                       if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN))
-                               return -EIO;
-               }
+       if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) {
+               hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
        }
 
        /* set a default rate so that the channel map is set up. */
-       hdspm->channel_map = channel_map_madi_ss;
-       hdspm_set_rate(hdspm, 44100, 1);
+       hdspm_set_rate(hdspm, 48000, 1);
 
        return 0;
 }
 
 
 /*------------------------------------------------------------
-   interrupt 
+   interrupt
  ------------------------------------------------------------*/
 
 static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
 {
        struct hdspm *hdspm = (struct hdspm *) dev_id;
        unsigned int status;
-       int audio;
-       int midi0;
-       int midi1;
-       unsigned int midi0status;
-       unsigned int midi1status;
-       int schedule = 0;
+       int i, audio, midi, schedule = 0;
+       /* cycles_t now; */
 
        status = hdspm_read(hdspm, HDSPM_statusRegister);
 
        audio = status & HDSPM_audioIRQPending;
-       midi0 = status & HDSPM_midi0IRQPending;
-       midi1 = status & HDSPM_midi1IRQPending;
+       midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending |
+                       HDSPM_midi2IRQPending | HDSPM_midi3IRQPending);
+
+       /* now = get_cycles(); */
+       /**
+        *   LAT_2..LAT_0 period  counter (win)  counter (mac)
+        *          6       4096   ~256053425     ~514672358
+        *          5       2048   ~128024983     ~257373821
+        *          4       1024    ~64023706     ~128718089
+        *          3        512    ~32005945      ~64385999
+        *          2        256    ~16003039      ~32260176
+        *          1        128     ~7998738      ~16194507
+        *          0         64     ~3998231       ~8191558
+        **/
+       /*
+          snd_printk(KERN_INFO "snd_hdspm_interrupt %llu @ %llx\n",
+          now-hdspm->last_interrupt, status & 0xFFC0);
+          hdspm->last_interrupt = now;
+       */
 
-       if (!audio && !midi0 && !midi1)
+       if (!audio && !midi)
                return IRQ_NONE;
 
        hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
        hdspm->irq_count++;
 
-       midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff;
-       midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff;
 
        if (audio) {
-
                if (hdspm->capture_substream)
                        snd_pcm_period_elapsed(hdspm->capture_substream);
 
@@ -3455,118 +5200,44 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
                        snd_pcm_period_elapsed(hdspm->playback_substream);
        }
 
-       if (midi0 && midi0status) {
-               /* we disable interrupts for this input until processing
-                * is done
-                */
-               hdspm->control_register &= ~HDSPM_Midi0InterruptEnable;
-               hdspm_write(hdspm, HDSPM_controlRegister,
-                           hdspm->control_register);
-               hdspm->midi[0].pending = 1;
-               schedule = 1;
-       }
-       if (midi1 && midi1status) {
-               /* we disable interrupts for this input until processing
-                * is done
-                */
-               hdspm->control_register &= ~HDSPM_Midi1InterruptEnable;
-               hdspm_write(hdspm, HDSPM_controlRegister,
-                           hdspm->control_register);
-               hdspm->midi[1].pending = 1;
-               schedule = 1;
+       if (midi) {
+               i = 0;
+               while (i < hdspm->midiPorts) {
+                       if ((hdspm_read(hdspm,
+                               hdspm->midi[i].statusIn) & 0xff) &&
+                                       (status & hdspm->midi[i].irq)) {
+                               /* we disable interrupts for this input until
+                                * processing is done
+                                */
+                               hdspm->control_register &= ~hdspm->midi[i].ie;
+                               hdspm_write(hdspm, HDSPM_controlRegister,
+                                               hdspm->control_register);
+                               hdspm->midi[i].pending = 1;
+                               schedule = 1;
+                       }
+
+                       i++;
+               }
+
+               if (schedule)
+                       tasklet_hi_schedule(&hdspm->midi_tasklet);
        }
-       if (schedule)
-               tasklet_schedule(&hdspm->midi_tasklet);
+
        return IRQ_HANDLED;
 }
 
 /*------------------------------------------------------------
-   pcm interface 
+   pcm interface
   ------------------------------------------------------------*/
 
 
-static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream *
-                                             substream)
+static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream
+                                             *substream)
 {
        struct hdspm *hdspm = snd_pcm_substream_chip(substream);
        return hdspm_hw_pointer(hdspm);
 }
 
-static char *hdspm_channel_buffer_location(struct hdspm * hdspm,
-                                          int stream, int channel)
-{
-       int mapped_channel;
-
-       if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
-               return NULL;
-
-       mapped_channel = hdspm->channel_map[channel];
-       if (mapped_channel < 0)
-               return NULL;
-
-       if (stream == SNDRV_PCM_STREAM_CAPTURE)
-               return hdspm->capture_buffer +
-                   mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
-       else
-               return hdspm->playback_buffer +
-                   mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
-}
-
-
-/* dont know why need it ??? */
-static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream,
-                                  int channel, snd_pcm_uframes_t pos,
-                                  void __user *src, snd_pcm_uframes_t count)
-{
-       struct hdspm *hdspm = snd_pcm_substream_chip(substream);
-       char *channel_buf;
-
-       if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
-               return -EINVAL;
-
-       channel_buf =
-               hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
-                                             channel);
-
-       if (snd_BUG_ON(!channel_buf))
-               return -EIO;
-
-       return copy_from_user(channel_buf + pos * 4, src, count * 4);
-}
-
-static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream,
-                                 int channel, snd_pcm_uframes_t pos,
-                                 void __user *dst, snd_pcm_uframes_t count)
-{
-       struct hdspm *hdspm = snd_pcm_substream_chip(substream);
-       char *channel_buf;
-
-       if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
-               return -EINVAL;
-
-       channel_buf =
-               hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
-                                             channel);
-       if (snd_BUG_ON(!channel_buf))
-               return -EIO;
-       return copy_to_user(dst, channel_buf + pos * 4, count * 4);
-}
-
-static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream,
-                               int channel, snd_pcm_uframes_t pos,
-                               snd_pcm_uframes_t count)
-{
-       struct hdspm *hdspm = snd_pcm_substream_chip(substream);
-       char *channel_buf;
-
-       channel_buf =
-               hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
-                                             channel);
-       if (snd_BUG_ON(!channel_buf))
-               return -EIO;
-       memset(channel_buf + pos * 4, 0, count * 4);
-       return 0;
-}
 
 static int snd_hdspm_reset(struct snd_pcm_substream *substream)
 {
@@ -3589,7 +5260,7 @@ static int snd_hdspm_reset(struct snd_pcm_substream *substream)
                snd_pcm_group_for_each_entry(s, substream) {
                        if (s == other) {
                                oruntime->status->hw_ptr =
-                                   runtime->status->hw_ptr;
+                                       runtime->status->hw_ptr;
                                break;
                        }
                }
@@ -3621,19 +5292,19 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
                /* The other stream is open, and not by the same
                   task as this one. Make sure that the parameters
                   that matter are the same.
-                */
+                  */
 
                if (params_rate(params) != hdspm->system_sample_rate) {
                        spin_unlock_irq(&hdspm->lock);
                        _snd_pcm_hw_param_setempty(params,
-                                                  SNDRV_PCM_HW_PARAM_RATE);
+                                       SNDRV_PCM_HW_PARAM_RATE);
                        return -EBUSY;
                }
 
                if (params_period_size(params) != hdspm->period_bytes / 4) {
                        spin_unlock_irq(&hdspm->lock);
                        _snd_pcm_hw_param_setempty(params,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+                                       SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
                        return -EBUSY;
                }
 
@@ -3646,18 +5317,20 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
        spin_lock_irq(&hdspm->lock);
        err = hdspm_set_rate(hdspm, params_rate(params), 0);
        if (err < 0) {
+               snd_printk(KERN_INFO "err on hdspm_set_rate: %d\n", err);
                spin_unlock_irq(&hdspm->lock);
                _snd_pcm_hw_param_setempty(params,
-                                          SNDRV_PCM_HW_PARAM_RATE);
+                               SNDRV_PCM_HW_PARAM_RATE);
                return err;
        }
        spin_unlock_irq(&hdspm->lock);
 
        err = hdspm_set_interrupt_interval(hdspm,
-                                          params_period_size(params));
+                       params_period_size(params));
        if (err < 0) {
+               snd_printk(KERN_INFO "err on hdspm_set_interrupt_interval: %d\n", err);
                _snd_pcm_hw_param_setempty(params,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+                               SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
                return err;
        }
 
@@ -3667,10 +5340,13 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
        /* malloc all buffer even if not enabled to get sure */
        /* Update for MADI rev 204: we need to allocate for all channels,
         * otherwise it doesn't work at 96kHz */
+
        err =
-           snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
-       if (err < 0)
+               snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES);
+       if (err < 0) {
+               snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err);
                return err;
+       }
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
@@ -3681,7 +5357,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
                        snd_hdspm_enable_out(hdspm, i, 1);
 
                hdspm->playback_buffer =
-                   (unsigned char *) substream->runtime->dma_area;
+                       (unsigned char *) substream->runtime->dma_area;
                snd_printdd("Allocated sample buffer for playback at %p\n",
                                hdspm->playback_buffer);
        } else {
@@ -3692,23 +5368,40 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
                        snd_hdspm_enable_in(hdspm, i, 1);
 
                hdspm->capture_buffer =
-                   (unsigned char *) substream->runtime->dma_area;
+                       (unsigned char *) substream->runtime->dma_area;
                snd_printdd("Allocated sample buffer for capture at %p\n",
                                hdspm->capture_buffer);
        }
+
        /*
           snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
           substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
           "playback" : "capture",
           snd_pcm_sgbuf_get_addr(substream, 0));
-        */
+          */
        /*
-       snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
-                       substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
-                         "playback" : "capture",
-                       params_rate(params), params_channels(params),
-                       params_buffer_size(params));
-       */
+          snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
+          substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+          "playback" : "capture",
+          params_rate(params), params_channels(params),
+          params_buffer_size(params));
+          */
+
+
+       /* Switch to native float format if requested */
+       if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
+               if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
+                       snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float format.\n");
+
+               hdspm->control_register |= HDSPe_FLOAT_FORMAT;
+       } else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) {
+               if (hdspm->control_register & HDSPe_FLOAT_FORMAT)
+                       snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer format.\n");
+
+               hdspm->control_register &= ~HDSPe_FLOAT_FORMAT;
+       }
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
        return 0;
 }
 
@@ -3719,14 +5412,14 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
-               /* params_channels(params) should be enough, 
+               /* params_channels(params) should be enough,
                   but to get sure in case of error */
-               for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
+               for (i = 0; i < hdspm->max_channels_out; ++i)
                        snd_hdspm_enable_out(hdspm, i, 0);
 
                hdspm->playback_buffer = NULL;
        } else {
-               for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
+               for (i = 0; i < hdspm->max_channels_in; ++i)
                        snd_hdspm_enable_in(hdspm, i, 0);
 
                hdspm->capture_buffer = NULL;
@@ -3738,37 +5431,58 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+
 static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_channel_info * info)
+               struct snd_pcm_channel_info *info)
 {
        struct hdspm *hdspm = snd_pcm_substream_chip(substream);
-       int mapped_channel;
 
-       if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS))
-               return -EINVAL;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) {
+                       snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel out of range (%d)\n", info->channel);
+                       return -EINVAL;
+               }
 
-       mapped_channel = hdspm->channel_map[info->channel];
-       if (mapped_channel < 0)
-               return -EINVAL;
+               if (hdspm->channel_map_out[info->channel] < 0) {
+                       snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel %d mapped out\n", info->channel);
+                       return -EINVAL;
+               }
+
+               info->offset = hdspm->channel_map_out[info->channel] *
+                       HDSPM_CHANNEL_BUFFER_BYTES;
+       } else {
+               if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) {
+                       snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel out of range (%d)\n", info->channel);
+                       return -EINVAL;
+               }
+
+               if (hdspm->channel_map_in[info->channel] < 0) {
+                       snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel %d mapped out\n", info->channel);
+                       return -EINVAL;
+               }
+
+               info->offset = hdspm->channel_map_in[info->channel] *
+                       HDSPM_CHANNEL_BUFFER_BYTES;
+       }
 
-       info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
        info->first = 0;
        info->step = 32;
        return 0;
 }
 
+
 static int snd_hdspm_ioctl(struct snd_pcm_substream *substream,
-                          unsigned int cmd, void *arg)
+               unsigned int cmd, void *arg)
 {
        switch (cmd) {
        case SNDRV_PCM_IOCTL1_RESET:
                return snd_hdspm_reset(substream);
 
        case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
-       {
-               struct snd_pcm_channel_info *info = arg;
-               return snd_hdspm_channel_info(substream, info);
-       }
+               {
+                       struct snd_pcm_channel_info *info = arg;
+                       return snd_hdspm_channel_info(substream, info);
+               }
        default:
                break;
        }
@@ -3815,19 +5529,19 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd)
                }
                if (cmd == SNDRV_PCM_TRIGGER_START) {
                        if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
-                           && substream->stream ==
-                           SNDRV_PCM_STREAM_CAPTURE)
+                                       && substream->stream ==
+                                       SNDRV_PCM_STREAM_CAPTURE)
                                hdspm_silence_playback(hdspm);
                } else {
                        if (running &&
-                           substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                                hdspm_silence_playback(hdspm);
                }
        } else {
                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                        hdspm_silence_playback(hdspm);
        }
-      _ok:
+_ok:
        snd_pcm_trigger_done(substream, substream);
        if (!hdspm->running && running)
                hdspm_start_audio(hdspm);
@@ -3844,8 +5558,18 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static unsigned int period_sizes[] =
-    { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+static unsigned int period_sizes_old[] = {
+       64, 128, 256, 512, 1024, 2048, 4096
+};
+
+static unsigned int period_sizes_new[] = {
+       32, 64, 128, 256, 512, 1024, 2048, 4096
+};
+
+/* RayDAT and AIO always have a buffer of 16384 samples per channel */
+static unsigned int raydat_aio_buffer_sizes[] = {
+       16384
+};
 
 static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
        .info = (SNDRV_PCM_INFO_MMAP |
@@ -3866,9 +5590,9 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
        .buffer_bytes_max =
            HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
        .period_bytes_min = (64 * 4),
-       .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
+       .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS,
        .periods_min = 2,
-       .periods_max = 2,
+       .periods_max = 512,
        .fifo_size = 0
 };
 
@@ -3891,20 +5615,66 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
        .buffer_bytes_max =
            HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
        .period_bytes_min = (64 * 4),
-       .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
+       .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS,
        .periods_min = 2,
-       .periods_max = 2,
+       .periods_max = 512,
        .fifo_size = 0
 };
 
-static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
-       .count = ARRAY_SIZE(period_sizes),
-       .list = period_sizes,
+static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_old = {
+       .count = ARRAY_SIZE(period_sizes_old),
+       .list = period_sizes_old,
+       .mask = 0
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_new = {
+       .count = ARRAY_SIZE(period_sizes_new),
+       .list = period_sizes_new,
        .mask = 0
 };
 
+static struct snd_pcm_hw_constraint_list hw_constraints_raydat_io_buffer = {
+       .count = ARRAY_SIZE(raydat_aio_buffer_sizes),
+       .list = raydat_aio_buffer_sizes,
+       .mask = 0
+};
+
+static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
+                                          struct snd_pcm_hw_rule *rule)
+{
+       struct hdspm *hdspm = rule->private;
+       struct snd_interval *c =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *r =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+       if (r->min > 96000 && r->max <= 192000) {
+               struct snd_interval t = {
+                       .min = hdspm->qs_in_channels,
+                       .max = hdspm->qs_in_channels,
+                       .integer = 1,
+               };
+               return snd_interval_refine(c, &t);
+       } else if (r->min > 48000 && r->max <= 96000) {
+               struct snd_interval t = {
+                       .min = hdspm->ds_in_channels,
+                       .max = hdspm->ds_in_channels,
+                       .integer = 1,
+               };
+               return snd_interval_refine(c, &t);
+       } else if (r->max < 64000) {
+               struct snd_interval t = {
+                       .min = hdspm->ss_in_channels,
+                       .max = hdspm->ss_in_channels,
+                       .integer = 1,
+               };
+               return snd_interval_refine(c, &t);
+       }
 
-static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params,
+       return 0;
+}
+
+static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params,
                                           struct snd_pcm_hw_rule * rule)
 {
        struct hdspm *hdspm = rule->private;
@@ -3913,25 +5683,33 @@ static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params,
        struct snd_interval *r =
            hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 
-       if (r->min > 48000 && r->max <= 96000) {
+       if (r->min > 96000 && r->max <= 192000) {
+               struct snd_interval t = {
+                       .min = hdspm->qs_out_channels,
+                       .max = hdspm->qs_out_channels,
+                       .integer = 1,
+               };
+               return snd_interval_refine(c, &t);
+       } else if (r->min > 48000 && r->max <= 96000) {
                struct snd_interval t = {
-                       .min = hdspm->ds_channels,
-                       .max = hdspm->ds_channels,
+                       .min = hdspm->ds_out_channels,
+                       .max = hdspm->ds_out_channels,
                        .integer = 1,
                };
                return snd_interval_refine(c, &t);
        } else if (r->max < 64000) {
                struct snd_interval t = {
-                       .min = hdspm->ss_channels,
-                       .max = hdspm->ss_channels,
+                       .min = hdspm->ss_out_channels,
+                       .max = hdspm->ss_out_channels,
                        .integer = 1,
                };
                return snd_interval_refine(c, &t);
+       } else {
        }
        return 0;
 }
 
-static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params,
+static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params,
                                           struct snd_pcm_hw_rule * rule)
 {
        struct hdspm *hdspm = rule->private;
@@ -3940,42 +5718,92 @@ static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params,
        struct snd_interval *r =
            hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 
-       if (c->min >= hdspm->ss_channels) {
+       if (c->min >= hdspm->ss_in_channels) {
                struct snd_interval t = {
                        .min = 32000,
                        .max = 48000,
                        .integer = 1,
                };
                return snd_interval_refine(r, &t);
-       } else if (c->max <= hdspm->ds_channels) {
+       } else if (c->max <= hdspm->qs_in_channels) {
+               struct snd_interval t = {
+                       .min = 128000,
+                       .max = 192000,
+                       .integer = 1,
+               };
+               return snd_interval_refine(r, &t);
+       } else if (c->max <= hdspm->ds_in_channels) {
                struct snd_interval t = {
                        .min = 64000,
                        .max = 96000,
                        .integer = 1,
                };
+               return snd_interval_refine(r, &t);
+       }
+
+       return 0;
+}
+static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params,
+                                          struct snd_pcm_hw_rule *rule)
+{
+       struct hdspm *hdspm = rule->private;
+       struct snd_interval *c =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *r =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 
+       if (c->min >= hdspm->ss_out_channels) {
+               struct snd_interval t = {
+                       .min = 32000,
+                       .max = 48000,
+                       .integer = 1,
+               };
+               return snd_interval_refine(r, &t);
+       } else if (c->max <= hdspm->qs_out_channels) {
+               struct snd_interval t = {
+                       .min = 128000,
+                       .max = 192000,
+                       .integer = 1,
+               };
+               return snd_interval_refine(r, &t);
+       } else if (c->max <= hdspm->ds_out_channels) {
+               struct snd_interval t = {
+                       .min = 64000,
+                       .max = 96000,
+                       .integer = 1,
+               };
                return snd_interval_refine(r, &t);
        }
+
        return 0;
 }
 
-static int snd_hdspm_hw_rule_channels(struct snd_pcm_hw_params *params,
+static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params,
                                      struct snd_pcm_hw_rule *rule)
 {
        unsigned int list[3];
        struct hdspm *hdspm = rule->private;
        struct snd_interval *c = hw_param_interval(params,
                        SNDRV_PCM_HW_PARAM_CHANNELS);
-       if (hdspm->is_aes32) {
-               list[0] = hdspm->qs_channels;
-               list[1] = hdspm->ds_channels;
-               list[2] = hdspm->ss_channels;
-               return snd_interval_list(c, 3, list, 0);
-       } else {
-               list[0] = hdspm->ds_channels;
-               list[1] = hdspm->ss_channels;
-               return snd_interval_list(c, 2, list, 0);
-       }
+
+       list[0] = hdspm->qs_in_channels;
+       list[1] = hdspm->ds_in_channels;
+       list[2] = hdspm->ss_in_channels;
+       return snd_interval_list(c, 3, list, 0);
+}
+
+static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params,
+                                     struct snd_pcm_hw_rule *rule)
+{
+       unsigned int list[3];
+       struct hdspm *hdspm = rule->private;
+       struct snd_interval *c = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       list[0] = hdspm->qs_out_channels;
+       list[1] = hdspm->ds_out_channels;
+       list[2] = hdspm->ss_out_channels;
+       return snd_interval_list(c, 3, list, 0);
 }
 
 
@@ -3999,6 +5827,7 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
 
        snd_pcm_set_sync(substream);
 
+
        runtime->hw = snd_hdspm_playback_subinfo;
 
        if (hdspm->capture_substream == NULL)
@@ -4011,25 +5840,41 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
 
        snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 
-       snd_pcm_hw_constraint_list(runtime, 0,
-                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                                  &hw_constraints_period_sizes);
+       switch (hdspm->io_type) {
+       case AIO:
+       case RayDAT:
+               snd_pcm_hw_constraint_list(runtime, 0,
+                               SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                               &hw_constraints_period_sizes_new);
+               snd_pcm_hw_constraint_list(runtime, 0,
+                               SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                               &hw_constraints_raydat_io_buffer);
+
+               break;
+
+       default:
+               snd_pcm_hw_constraint_list(runtime, 0,
+                               SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                               &hw_constraints_period_sizes_old);
+       }
 
-       if (hdspm->is_aes32) {
+       if (AES32 == hdspm->io_type) {
                snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                &hdspm_hw_constraints_aes32_sample_rates);
        } else {
-               snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                    snd_hdspm_hw_rule_channels, hdspm,
-                                    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-               snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                   snd_hdspm_hw_rule_channels_rate, hdspm,
-                                   SNDRV_PCM_HW_PARAM_RATE, -1);
-
                snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                   snd_hdspm_hw_rule_rate_channels, hdspm,
-                                   SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+                               snd_hdspm_hw_rule_rate_out_channels, hdspm,
+                               SNDRV_PCM_HW_PARAM_CHANNELS, -1);
        }
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       snd_hdspm_hw_rule_out_channels, hdspm,
+                       SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       snd_hdspm_hw_rule_out_channels_rate, hdspm,
+                       SNDRV_PCM_HW_PARAM_RATE, -1);
+
        return 0;
 }
 
@@ -4066,24 +5911,40 @@ static int snd_hdspm_capture_open(struct snd_pcm_substream *substream)
        spin_unlock_irq(&hdspm->lock);
 
        snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
-       snd_pcm_hw_constraint_list(runtime, 0,
-                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                                  &hw_constraints_period_sizes);
-       if (hdspm->is_aes32) {
+       switch (hdspm->io_type) {
+       case AIO:
+       case RayDAT:
+         snd_pcm_hw_constraint_list(runtime, 0,
+                                    SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                    &hw_constraints_period_sizes_new);
+         snd_pcm_hw_constraint_list(runtime, 0,
+                                    SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                    &hw_constraints_raydat_io_buffer);
+         break;
+
+       default:
+         snd_pcm_hw_constraint_list(runtime, 0,
+                                    SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                    &hw_constraints_period_sizes_old);
+       }
+
+       if (AES32 == hdspm->io_type) {
                snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                &hdspm_hw_constraints_aes32_sample_rates);
        } else {
-               snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                    snd_hdspm_hw_rule_channels, hdspm,
-                                    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-               snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                   snd_hdspm_hw_rule_channels_rate, hdspm,
-                                   SNDRV_PCM_HW_PARAM_RATE, -1);
-
                snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                   snd_hdspm_hw_rule_rate_channels, hdspm,
-                                   SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+                               snd_hdspm_hw_rule_rate_in_channels, hdspm,
+                               SNDRV_PCM_HW_PARAM_CHANNELS, -1);
        }
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       snd_hdspm_hw_rule_in_channels, hdspm,
+                       SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       snd_hdspm_hw_rule_in_channels_rate, hdspm,
+                       SNDRV_PCM_HW_PARAM_RATE, -1);
+
        return 0;
 }
 
@@ -4100,32 +5961,129 @@ static int snd_hdspm_capture_release(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
-                                unsigned int cmd, unsigned long arg)
+static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
+{
+       /* we have nothing to initialize but the call is required */
+       return 0;
+}
+
+static inline int copy_u32_le(void __user *dest, void __iomem *src)
+{
+       u32 val = readl(src);
+       return copy_to_user(dest, &val, 4);
+}
+
+static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+               unsigned int cmd, unsigned long __user arg)
 {
+       void __user *argp = (void __user *)arg;
        struct hdspm *hdspm = hw->private_data;
        struct hdspm_mixer_ioctl mixer;
-       struct hdspm_config_info info;
+       struct hdspm_config info;
+       struct hdspm_status status;
        struct hdspm_version hdspm_version;
-       struct hdspm_peak_rms_ioctl rms;
+       struct hdspm_peak_rms *levels;
+       struct hdspm_ltc ltc;
+       unsigned int statusregister;
+       long unsigned int s;
+       int i = 0;
 
        switch (cmd) {
 
        case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
-               if (copy_from_user(&rms, (void __user *)arg, sizeof(rms)))
+               levels = &hdspm->peak_rms;
+               for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
+                       levels->input_peaks[i] =
+                               readl(hdspm->iobase +
+                                               HDSPM_MADI_INPUT_PEAK + i*4);
+                       levels->playback_peaks[i] =
+                               readl(hdspm->iobase +
+                                               HDSPM_MADI_PLAYBACK_PEAK + i*4);
+                       levels->output_peaks[i] =
+                               readl(hdspm->iobase +
+                                               HDSPM_MADI_OUTPUT_PEAK + i*4);
+
+                       levels->input_rms[i] =
+                               ((uint64_t) readl(hdspm->iobase +
+                                       HDSPM_MADI_INPUT_RMS_H + i*4) << 32) |
+                               (uint64_t) readl(hdspm->iobase +
+                                               HDSPM_MADI_INPUT_RMS_L + i*4);
+                       levels->playback_rms[i] =
+                               ((uint64_t)readl(hdspm->iobase +
+                                       HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) |
+                               (uint64_t)readl(hdspm->iobase +
+                                       HDSPM_MADI_PLAYBACK_RMS_L + i*4);
+                       levels->output_rms[i] =
+                               ((uint64_t)readl(hdspm->iobase +
+                                       HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) |
+                               (uint64_t)readl(hdspm->iobase +
+                                               HDSPM_MADI_OUTPUT_RMS_L + i*4);
+               }
+
+               if (hdspm->system_sample_rate > 96000) {
+                       levels->speed = qs;
+               } else if (hdspm->system_sample_rate > 48000) {
+                       levels->speed = ds;
+               } else {
+                       levels->speed = ss;
+               }
+               levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+               s = copy_to_user(argp, levels, sizeof(struct hdspm_peak_rms));
+               if (0 != s) {
+                       /* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu
+                        [Levels]\n", sizeof(struct hdspm_peak_rms), s);
+                        */
                        return -EFAULT;
-               /* maybe there is a chance to memorymap in future
-                * so dont touch just copy
-                */
-               if(copy_to_user_fromio((void __user *)rms.peak,
-                                      hdspm->iobase+HDSPM_MADI_peakrmsbase,
-                                      sizeof(struct hdspm_peak_rms)) != 0 )
+               }
+               break;
+
+       case SNDRV_HDSPM_IOCTL_GET_LTC:
+               ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO);
+               i = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+               if (i & HDSPM_TCO1_LTC_Input_valid) {
+                       switch (i & (HDSPM_TCO1_LTC_Format_LSB |
+                               HDSPM_TCO1_LTC_Format_MSB)) {
+                       case 0:
+                               ltc.format = fps_24;
+                               break;
+                       case HDSPM_TCO1_LTC_Format_LSB:
+                               ltc.format = fps_25;
+                               break;
+                       case HDSPM_TCO1_LTC_Format_MSB:
+                               ltc.format = fps_2997;
+                               break;
+                       default:
+                               ltc.format = 30;
+                               break;
+                       }
+                       if (i & HDSPM_TCO1_set_drop_frame_flag) {
+                               ltc.frame = drop_frame;
+                       } else {
+                               ltc.frame = full_frame;
+                       }
+               } else {
+                       ltc.format = format_invalid;
+                       ltc.frame = frame_invalid;
+               }
+               if (i & HDSPM_TCO1_Video_Input_Format_NTSC) {
+                       ltc.input_format = ntsc;
+               } else if (i & HDSPM_TCO1_Video_Input_Format_PAL) {
+                       ltc.input_format = pal;
+               } else {
+                       ltc.input_format = no_video;
+               }
+
+               s = copy_to_user(argp, &ltc, sizeof(struct hdspm_ltc));
+               if (0 != s) {
+                       /*
+                        snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */
                        return -EFAULT;
+               }
 
                break;
-               
 
-       case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO:
+       case SNDRV_HDSPM_IOCTL_GET_CONFIG:
 
                memset(&info, 0, sizeof(info));
                spin_lock_irq(&hdspm->lock);
@@ -4134,7 +6092,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
 
                info.system_sample_rate = hdspm->system_sample_rate;
                info.autosync_sample_rate =
-                   hdspm_external_sample_rate(hdspm);
+                       hdspm_external_sample_rate(hdspm);
                info.system_clock_mode = hdspm_system_clock_mode(hdspm);
                info.clock_source = hdspm_clock_source(hdspm);
                info.autosync_ref = hdspm_autosync_ref(hdspm);
@@ -4145,10 +6103,58 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
                        return -EFAULT;
                break;
 
+       case SNDRV_HDSPM_IOCTL_GET_STATUS:
+               status.card_type = hdspm->io_type;
+
+               status.autosync_source = hdspm_autosync_ref(hdspm);
+
+               status.card_clock = 110069313433624ULL;
+               status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+
+               switch (hdspm->io_type) {
+               case MADI:
+               case MADIface:
+                       status.card_specific.madi.sync_wc =
+                               hdspm_wc_sync_check(hdspm);
+                       status.card_specific.madi.sync_madi =
+                               hdspm_madi_sync_check(hdspm);
+                       status.card_specific.madi.sync_tco =
+                               hdspm_tco_sync_check(hdspm);
+                       status.card_specific.madi.sync_in =
+                               hdspm_sync_in_sync_check(hdspm);
+
+                       statusregister =
+                               hdspm_read(hdspm, HDSPM_statusRegister);
+                       status.card_specific.madi.madi_input =
+                               (statusregister & HDSPM_AB_int) ? 1 : 0;
+                       status.card_specific.madi.channel_format =
+                               (statusregister & HDSPM_TX_64ch) ? 1 : 0;
+                       /* TODO: Mac driver sets it when f_s>48kHz */
+                       status.card_specific.madi.frame_format = 0;
+
+               default:
+                       break;
+               }
+
+               if (copy_to_user((void __user *) arg, &status, sizeof(status)))
+                       return -EFAULT;
+
+
+               break;
+
        case SNDRV_HDSPM_IOCTL_GET_VERSION:
+               hdspm_version.card_type = hdspm->io_type;
+               strncpy(hdspm_version.cardname, hdspm->card_name,
+                               sizeof(hdspm_version.cardname));
+               hdspm_version.serial = (hdspm_read(hdspm,
+                                       HDSPM_midiStatusIn0)>>8) & 0xFFFFFF;
                hdspm_version.firmware_rev = hdspm->firmware_rev;
+               hdspm_version.addons = 0;
+               if (hdspm->tco)
+                       hdspm_version.addons |= HDSPM_ADDON_TCO;
+
                if (copy_to_user((void __user *) arg, &hdspm_version,
-                                sizeof(hdspm_version)))
+                                       sizeof(hdspm_version)))
                        return -EFAULT;
                break;
 
@@ -4156,7 +6162,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
                if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer)))
                        return -EFAULT;
                if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
-                                sizeof(struct hdspm_mixer)))
+                                       sizeof(struct hdspm_mixer)))
                        return -EFAULT;
                break;
 
@@ -4175,8 +6181,6 @@ static struct snd_pcm_ops snd_hdspm_playback_ops = {
        .prepare = snd_hdspm_prepare,
        .trigger = snd_hdspm_trigger,
        .pointer = snd_hdspm_hw_pointer,
-       .copy = snd_hdspm_playback_copy,
-       .silence = snd_hdspm_hw_silence,
        .page = snd_pcm_sgbuf_ops_page,
 };
 
@@ -4189,7 +6193,6 @@ static struct snd_pcm_ops snd_hdspm_capture_ops = {
        .prepare = snd_hdspm_prepare,
        .trigger = snd_hdspm_trigger,
        .pointer = snd_hdspm_hw_pointer,
-       .copy = snd_hdspm_capture_copy,
        .page = snd_pcm_sgbuf_ops_page,
 };
 
@@ -4207,16 +6210,18 @@ static int __devinit snd_hdspm_create_hwdep(struct snd_card *card,
        hw->private_data = hdspm;
        strcpy(hw->name, "HDSPM hwdep interface");
 
+       hw->ops.open = snd_hdspm_hwdep_dummy_op;
        hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
+       hw->ops.release = snd_hdspm_hwdep_dummy_op;
 
        return 0;
 }
 
 
 /*------------------------------------------------------------
-   memory interface 
+   memory interface
  ------------------------------------------------------------*/
-static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
+static int __devinit snd_hdspm_preallocate_memory(struct hdspm *hdspm)
 {
        int err;
        struct snd_pcm *pcm;
@@ -4228,7 +6233,7 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
 
        err =
             snd_pcm_lib_preallocate_pages_for_all(pcm,
-                                                  SNDRV_DMA_TYPE_DEV_SG,
+                                                  SNDRV_DMA_TYPE_DEV_SG,
                                                   snd_dma_pci_data(hdspm->pci),
                                                   wanted,
                                                   wanted);
@@ -4242,19 +6247,23 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)
        return 0;
 }
 
-static void hdspm_set_sgbuf(struct hdspm * hdspm,
+
+static void hdspm_set_sgbuf(struct hdspm *hdspm,
                            struct snd_pcm_substream *substream,
                             unsigned int reg, int channels)
 {
        int i;
+
+       /* continuous memory segment */
        for (i = 0; i < (channels * 16); i++)
                hdspm_write(hdspm, reg + 4 * i,
-                           snd_pcm_sgbuf_get_addr(substream, 4096 * i));
+                               snd_pcm_sgbuf_get_addr(substream, 4096 * i));
 }
 
+
 /* ------------- ALSA Devices ---------------------------- */
 static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
-                                         struct hdspm * hdspm)
+                                         struct hdspm *hdspm)
 {
        struct snd_pcm *pcm;
        int err;
@@ -4283,27 +6292,30 @@ static int __devinit snd_hdspm_create_pcm(struct snd_card *card,
 
 static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm)
 {
-       snd_hdspm_flush_midi_input(hdspm, 0);
-       snd_hdspm_flush_midi_input(hdspm, 1);
+       int i;
+
+       for (i = 0; i < hdspm->midiPorts; i++)
+               snd_hdspm_flush_midi_input(hdspm, i);
 }
 
 static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
                                                   struct hdspm * hdspm)
 {
-       int err;
+       int err, i;
 
        snd_printdd("Create card...\n");
        err = snd_hdspm_create_pcm(card, hdspm);
        if (err < 0)
                return err;
 
-       err = snd_hdspm_create_midi(card, hdspm, 0);
-       if (err < 0)
-               return err;
-
-       err = snd_hdspm_create_midi(card, hdspm, 1);
-       if (err < 0)
-               return err;
+       i = 0;
+       while (i < hdspm->midiPorts) {
+               err = snd_hdspm_create_midi(card, hdspm, i);
+               if (err < 0) {
+                       return err;
+               }
+               i++;
+       }
 
        err = snd_hdspm_create_controls(card, hdspm);
        if (err < 0)
@@ -4346,37 +6358,55 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card,
 }
 
 static int __devinit snd_hdspm_create(struct snd_card *card,
-                                     struct hdspm *hdspm,
-                                     int precise_ptr, int enable_monitor)
-{
+               struct hdspm *hdspm) {
+
        struct pci_dev *pci = hdspm->pci;
        int err;
        unsigned long io_extent;
 
        hdspm->irq = -1;
-
-       spin_lock_init(&hdspm->midi[0].lock);
-       spin_lock_init(&hdspm->midi[1].lock);
-
        hdspm->card = card;
 
        spin_lock_init(&hdspm->lock);
 
-       tasklet_init(&hdspm->midi_tasklet,
-                    hdspm_midi_tasklet, (unsigned long) hdspm);
-
        pci_read_config_word(hdspm->pci,
-                            PCI_CLASS_REVISION, &hdspm->firmware_rev);
-
-       hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION);
+                       PCI_CLASS_REVISION, &hdspm->firmware_rev);
 
        strcpy(card->mixername, "Xilinx FPGA");
-       if (hdspm->is_aes32) {
-               strcpy(card->driver, "HDSPAES32");
-               hdspm->card_name = "RME HDSPM AES32";
-       } else {
-               strcpy(card->driver, "HDSPM");
-               hdspm->card_name = "RME HDSPM MADI";
+       strcpy(card->driver, "HDSPM");
+
+       switch (hdspm->firmware_rev) {
+       case HDSPM_MADI_REV:
+               hdspm->io_type = MADI;
+               hdspm->card_name = "RME MADI";
+               hdspm->midiPorts = 3;
+               break;
+       case HDSPM_RAYDAT_REV:
+               hdspm->io_type = RayDAT;
+               hdspm->card_name = "RME RayDAT";
+               hdspm->midiPorts = 2;
+               break;
+       case HDSPM_AIO_REV:
+               hdspm->io_type = AIO;
+               hdspm->card_name = "RME AIO";
+               hdspm->midiPorts = 1;
+               break;
+       case HDSPM_MADIFACE_REV:
+               hdspm->io_type = MADIface;
+               hdspm->card_name = "RME MADIface";
+               hdspm->midiPorts = 1;
+               break;
+       case HDSPM_AES_REV:
+       case HDSPM_AES32_REV:
+       case HDSPM_AES32_OLD_REV:
+               hdspm->io_type = AES32;
+               hdspm->card_name = "RME AES32";
+               hdspm->midiPorts = 2;
+               break;
+       default:
+               snd_printk(KERN_ERR "HDSPM: unknown firmware revision %x\n",
+                               hdspm->firmware_rev);
+               return -ENODEV;
        }
 
        err = pci_enable_device(pci);
@@ -4393,22 +6423,21 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
        io_extent = pci_resource_len(pci, 0);
 
        snd_printdd("grabbed memory region 0x%lx-0x%lx\n",
-                  hdspm->port, hdspm->port + io_extent - 1);
-
+                       hdspm->port, hdspm->port + io_extent - 1);
 
        hdspm->iobase = ioremap_nocache(hdspm->port, io_extent);
        if (!hdspm->iobase) {
                snd_printk(KERN_ERR "HDSPM: "
-                          "unable to remap region 0x%lx-0x%lx\n",
-                          hdspm->port, hdspm->port + io_extent - 1);
+                               "unable to remap region 0x%lx-0x%lx\n",
+                               hdspm->port, hdspm->port + io_extent - 1);
                return -EBUSY;
        }
        snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n",
-                  (unsigned long)hdspm->iobase, hdspm->port,
-                  hdspm->port + io_extent - 1);
+                       (unsigned long)hdspm->iobase, hdspm->port,
+                       hdspm->port + io_extent - 1);
 
        if (request_irq(pci->irq, snd_hdspm_interrupt,
-                       IRQF_SHARED, "hdspm", hdspm)) {
+                               IRQF_SHARED, "hdspm", hdspm)) {
                snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
                return -EBUSY;
        }
@@ -4416,23 +6445,219 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
        snd_printdd("use IRQ %d\n", pci->irq);
 
        hdspm->irq = pci->irq;
-       hdspm->precise_ptr = precise_ptr;
-
-       hdspm->monitor_outs = enable_monitor;
 
        snd_printdd("kmalloc Mixer memory of %zd Bytes\n",
-                  sizeof(struct hdspm_mixer));
+                       sizeof(struct hdspm_mixer));
        hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL);
        if (!hdspm->mixer) {
                snd_printk(KERN_ERR "HDSPM: "
-                          "unable to kmalloc Mixer memory of %d Bytes\n",
-                          (int)sizeof(struct hdspm_mixer));
+                               "unable to kmalloc Mixer memory of %d Bytes\n",
+                               (int)sizeof(struct hdspm_mixer));
                return err;
        }
 
-       hdspm->ss_channels = MADI_SS_CHANNELS;
-       hdspm->ds_channels = MADI_DS_CHANNELS;
-       hdspm->qs_channels = MADI_QS_CHANNELS;
+       hdspm->port_names_in = NULL;
+       hdspm->port_names_out = NULL;
+
+       switch (hdspm->io_type) {
+       case AES32:
+               hdspm->ss_in_channels = hdspm->ss_out_channels = AES32_CHANNELS;
+               hdspm->ds_in_channels = hdspm->ds_out_channels = AES32_CHANNELS;
+               hdspm->qs_in_channels = hdspm->qs_out_channels = AES32_CHANNELS;
+
+               hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
+                       channel_map_aes32;
+               hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
+                       channel_map_aes32;
+               hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
+                       channel_map_aes32;
+               hdspm->port_names_in_ss = hdspm->port_names_out_ss =
+                       texts_ports_aes32;
+               hdspm->port_names_in_ds = hdspm->port_names_out_ds =
+                       texts_ports_aes32;
+               hdspm->port_names_in_qs = hdspm->port_names_out_qs =
+                       texts_ports_aes32;
+
+               hdspm->max_channels_out = hdspm->max_channels_in =
+                       AES32_CHANNELS;
+               hdspm->port_names_in = hdspm->port_names_out =
+                       texts_ports_aes32;
+               hdspm->channel_map_in = hdspm->channel_map_out =
+                       channel_map_aes32;
+
+               break;
+
+       case MADI:
+       case MADIface:
+               hdspm->ss_in_channels = hdspm->ss_out_channels =
+                       MADI_SS_CHANNELS;
+               hdspm->ds_in_channels = hdspm->ds_out_channels =
+                       MADI_DS_CHANNELS;
+               hdspm->qs_in_channels = hdspm->qs_out_channels =
+                       MADI_QS_CHANNELS;
+
+               hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
+                       channel_map_unity_ss;
+               hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
+                       channel_map_unity_ss;
+               hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
+                       channel_map_unity_ss;
+
+               hdspm->port_names_in_ss = hdspm->port_names_out_ss =
+                       texts_ports_madi;
+               hdspm->port_names_in_ds = hdspm->port_names_out_ds =
+                       texts_ports_madi;
+               hdspm->port_names_in_qs = hdspm->port_names_out_qs =
+                       texts_ports_madi;
+               break;
+
+       case AIO:
+               if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
+                       snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n");
+               }
+
+               hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
+               hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
+               hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
+               hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS;
+               hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
+               hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
+
+               hdspm->channel_map_out_ss = channel_map_aio_out_ss;
+               hdspm->channel_map_out_ds = channel_map_aio_out_ds;
+               hdspm->channel_map_out_qs = channel_map_aio_out_qs;
+
+               hdspm->channel_map_in_ss = channel_map_aio_in_ss;
+               hdspm->channel_map_in_ds = channel_map_aio_in_ds;
+               hdspm->channel_map_in_qs = channel_map_aio_in_qs;
+
+               hdspm->port_names_in_ss = texts_ports_aio_in_ss;
+               hdspm->port_names_out_ss = texts_ports_aio_out_ss;
+               hdspm->port_names_in_ds = texts_ports_aio_in_ds;
+               hdspm->port_names_out_ds = texts_ports_aio_out_ds;
+               hdspm->port_names_in_qs = texts_ports_aio_in_qs;
+               hdspm->port_names_out_qs = texts_ports_aio_out_qs;
+
+               break;
+
+       case RayDAT:
+               hdspm->ss_in_channels = hdspm->ss_out_channels =
+                       RAYDAT_SS_CHANNELS;
+               hdspm->ds_in_channels = hdspm->ds_out_channels =
+                       RAYDAT_DS_CHANNELS;
+               hdspm->qs_in_channels = hdspm->qs_out_channels =
+                       RAYDAT_QS_CHANNELS;
+
+               hdspm->max_channels_in = RAYDAT_SS_CHANNELS;
+               hdspm->max_channels_out = RAYDAT_SS_CHANNELS;
+
+               hdspm->channel_map_in_ss = hdspm->channel_map_out_ss =
+                       channel_map_raydat_ss;
+               hdspm->channel_map_in_ds = hdspm->channel_map_out_ds =
+                       channel_map_raydat_ds;
+               hdspm->channel_map_in_qs = hdspm->channel_map_out_qs =
+                       channel_map_raydat_qs;
+               hdspm->channel_map_in = hdspm->channel_map_out =
+                       channel_map_raydat_ss;
+
+               hdspm->port_names_in_ss = hdspm->port_names_out_ss =
+                       texts_ports_raydat_ss;
+               hdspm->port_names_in_ds = hdspm->port_names_out_ds =
+                       texts_ports_raydat_ds;
+               hdspm->port_names_in_qs = hdspm->port_names_out_qs =
+                       texts_ports_raydat_qs;
+
+
+               break;
+
+       }
+
+       /* TCO detection */
+       switch (hdspm->io_type) {
+       case AIO:
+       case RayDAT:
+               if (hdspm_read(hdspm, HDSPM_statusRegister2) &
+                               HDSPM_s2_tco_detect) {
+                       hdspm->midiPorts++;
+                       hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
+                                       GFP_KERNEL);
+                       if (NULL != hdspm->tco) {
+                               hdspm_tco_write(hdspm);
+                       }
+                       snd_printk(KERN_INFO "HDSPM: AIO/RayDAT TCO module found\n");
+               } else {
+                       hdspm->tco = NULL;
+               }
+               break;
+
+       case MADI:
+               if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
+                       hdspm->midiPorts++;
+                       hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
+                                       GFP_KERNEL);
+                       if (NULL != hdspm->tco) {
+                               hdspm_tco_write(hdspm);
+                       }
+                       snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n");
+               } else {
+                       hdspm->tco = NULL;
+               }
+               break;
+
+       default:
+               hdspm->tco = NULL;
+       }
+
+       /* texts */
+       switch (hdspm->io_type) {
+       case AES32:
+               if (hdspm->tco) {
+                       hdspm->texts_autosync = texts_autosync_aes_tco;
+                       hdspm->texts_autosync_items = 10;
+               } else {
+                       hdspm->texts_autosync = texts_autosync_aes;
+                       hdspm->texts_autosync_items = 9;
+               }
+               break;
+
+       case MADI:
+               if (hdspm->tco) {
+                       hdspm->texts_autosync = texts_autosync_madi_tco;
+                       hdspm->texts_autosync_items = 4;
+               } else {
+                       hdspm->texts_autosync = texts_autosync_madi;
+                       hdspm->texts_autosync_items = 3;
+               }
+               break;
+
+       case MADIface:
+
+               break;
+
+       case RayDAT:
+               if (hdspm->tco) {
+                       hdspm->texts_autosync = texts_autosync_raydat_tco;
+                       hdspm->texts_autosync_items = 9;
+               } else {
+                       hdspm->texts_autosync = texts_autosync_raydat;
+                       hdspm->texts_autosync_items = 8;
+               }
+               break;
+
+       case AIO:
+               if (hdspm->tco) {
+                       hdspm->texts_autosync = texts_autosync_aio_tco;
+                       hdspm->texts_autosync_items = 6;
+               } else {
+                       hdspm->texts_autosync = texts_autosync_aio;
+                       hdspm->texts_autosync_items = 5;
+               }
+               break;
+
+       }
+
+       tasklet_init(&hdspm->midi_tasklet,
+                       hdspm_midi_tasklet, (unsigned long) hdspm);
 
        snd_printdd("create alsa devices.\n");
        err = snd_hdspm_create_alsa_devices(card, hdspm);
@@ -4444,6 +6669,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
        return 0;
 }
 
+
 static int snd_hdspm_free(struct hdspm * hdspm)
 {
 
@@ -4452,7 +6678,8 @@ static int snd_hdspm_free(struct hdspm * hdspm)
                /* stop th audio, and cancel all interrupts */
                hdspm->control_register &=
                    ~(HDSPM_Start | HDSPM_AudioInterruptEnable |
-                     HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable);
+                     HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable |
+                     HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable);
                hdspm_write(hdspm, HDSPM_controlRegister,
                            hdspm->control_register);
        }
@@ -4472,6 +6699,7 @@ static int snd_hdspm_free(struct hdspm * hdspm)
        return 0;
 }
 
+
 static void snd_hdspm_card_free(struct snd_card *card)
 {
        struct hdspm *hdspm = card->private_data;
@@ -4480,6 +6708,7 @@ static void snd_hdspm_card_free(struct snd_card *card)
                snd_hdspm_free(hdspm);
 }
 
+
 static int __devinit snd_hdspm_probe(struct pci_dev *pci,
                                     const struct pci_device_id *pci_id)
 {
@@ -4496,7 +6725,7 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci,
        }
 
        err = snd_card_create(index[dev], id[dev],
-                             THIS_MODULE, sizeof(struct hdspm), &card);
+                       THIS_MODULE, sizeof(struct hdspm), &card);
        if (err < 0)
                return err;
 
@@ -4507,16 +6736,25 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci,
 
        snd_card_set_dev(card, &pci->dev);
 
-       err = snd_hdspm_create(card, hdspm, precise_ptr[dev],
-                              enable_monitor[dev]);
+       err = snd_hdspm_create(card, hdspm);
        if (err < 0) {
                snd_card_free(card);
                return err;
        }
 
-       strcpy(card->shortname, "HDSPM MADI");
-       sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name,
-               hdspm->port, hdspm->irq);
+       if (hdspm->io_type != MADIface) {
+               sprintf(card->shortname, "%s_%x",
+                       hdspm->card_name,
+                       (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF);
+               sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d",
+                       hdspm->card_name,
+                       (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF,
+                       hdspm->port, hdspm->irq);
+       } else {
+               sprintf(card->shortname, "%s", hdspm->card_name);
+               sprintf(card->longname, "%s at 0x%lx, irq %d",
+                               hdspm->card_name, hdspm->port, hdspm->irq);
+       }
 
        err = snd_card_register(card);
        if (err < 0) {
index b47cfd45b3b98f04b2c6d89135de310fb3b264ef..3ecbd67f88c9143e31de7d4fcb38ca092d9ea758 100644 (file)
@@ -1034,7 +1034,11 @@ static int __devinit snd_pmac_detect(struct snd_pmac *chip)
        if (of_device_is_compatible(sound, "tumbler")) {
                chip->model = PMAC_TUMBLER;
                chip->can_capture = of_machine_is_compatible("PowerMac4,2")
-                               || of_machine_is_compatible("PowerBook4,1");
+                               || of_machine_is_compatible("PowerBook3,2")
+                               || of_machine_is_compatible("PowerBook3,3")
+                               || of_machine_is_compatible("PowerBook4,1")
+                               || of_machine_is_compatible("PowerBook4,2")
+                               || of_machine_is_compatible("PowerBook4,3");
                chip->can_duplex = 0;
                // chip->can_byte_swap = 0; /* FIXME: check this */
                chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
index a3efc52a34da28c6742fe688dd413151ff931b3d..8224db5f0434ffa7c16b93899f06401ae7c9ed3b 100644 (file)
@@ -50,10 +50,12 @@ source "sound/soc/jz4740/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
+source "sound/soc/mid-x86/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
 
 # Supported codecs
index ce913bf5213c0d58a14085ace1a1c315fcbc92a1..1ed61c5df2c583f655d5dedb1c4041f840c8cae1 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_SND_SOC) += ep93xx/
 obj-$(CONFIG_SND_SOC)  += fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)  += jz4740/
+obj-$(CONFIG_SND_SOC)  += mid-x86/
 obj-$(CONFIG_SND_SOC)  += nuc900/
 obj-$(CONFIG_SND_SOC)  += omap/
 obj-$(CONFIG_SND_SOC)  += kirkwood/
@@ -17,4 +18,5 @@ obj-$(CONFIG_SND_SOC) += pxa/
 obj-$(CONFIG_SND_SOC)  += samsung/
 obj-$(CONFIG_SND_SOC)  += s6000/
 obj-$(CONFIG_SND_SOC)  += sh/
+obj-$(CONFIG_SND_SOC)  += tegra/
 obj-$(CONFIG_SND_SOC)  += txx9/
index c48b23c1d4fc02244f3248a65da5b68f257ac9e2..d63c1754e05f82d75e5f2a8763bf4cf1540c763a 100644 (file)
@@ -26,17 +26,24 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
        select SND_SOC_CS42L51 if I2C
        select SND_SOC_CS4270 if I2C
+       select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
        select SND_SOC_CX20442
        select SND_SOC_DA7210 if I2C
+       select SND_SOC_DFBMCS320
        select SND_SOC_JZ4740_CODEC if SOC_JZ4740
+       select SND_SOC_LM4857 if I2C
        select SND_SOC_MAX98088 if I2C
+       select SND_SOC_MAX9850 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
+       select SND_SOC_SGTL5000 if I2C
+       select SND_SOC_SN95031 if INTEL_SCU_IPC
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
        select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
        select SND_SOC_TLV320AIC23 if I2C
        select SND_SOC_TLV320AIC26 if SPI_MASTER
+       select SND_SOC_TVL320AIC32X4 if I2C
        select SND_SOC_TLV320AIC3X if I2C
        select SND_SOC_TPA6130A2 if I2C
        select SND_SOC_TLV320DAC33 if I2C
@@ -76,6 +83,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8990 if I2C
+       select SND_SOC_WM8991 if I2C
        select SND_SOC_WM8993 if I2C
        select SND_SOC_WM8994 if MFD_WM8994
        select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
@@ -155,6 +163,9 @@ config SND_SOC_CS4270_VD33_ERRATA
        bool
        depends on SND_SOC_CS4270
 
+config SND_SOC_CS4271
+       tristate
+
 config SND_SOC_CX20442
        tristate
 
@@ -167,15 +178,28 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate
 
+config SND_SOC_DFBMCS320
+       tristate
+
 config SND_SOC_DMIC
        tristate
 
 config SND_SOC_MAX98088
        tristate
 
+config SND_SOC_MAX9850
+       tristate
+
 config SND_SOC_PCM3008
        tristate
 
+#Freescale sgtl5000 codec
+config SND_SOC_SGTL5000
+       tristate
+
+config SND_SOC_SN95031
+       tristate
+
 config SND_SOC_SPDIF
        tristate
 
@@ -192,6 +216,9 @@ config SND_SOC_TLV320AIC26
        tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
        depends on SPI
 
+config SND_SOC_TVL320AIC32X4
+       tristate
+
 config SND_SOC_TLV320AIC3X
        tristate
 
@@ -304,6 +331,9 @@ config SND_SOC_WM8988
 config SND_SOC_WM8990
        tristate
 
+config SND_SOC_WM8991
+       tristate
+
 config SND_SOC_WM8993
        tristate
 
@@ -326,6 +356,9 @@ config SND_SOC_WM9713
        tristate
 
 # Amp
+config SND_SOC_LM4857
+       tristate
+
 config SND_SOC_MAX9877
        tristate
 
@@ -337,4 +370,3 @@ config SND_SOC_WM2000
 
 config SND_SOC_WM9090
        tristate
-
index 579af9c4f1280f82890e55115436b5549cecb298..379bc55f072385150fb7abb5808920f47842de2f 100644 (file)
@@ -12,19 +12,25 @@ snd-soc-ak4671-objs := ak4671.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-cs4271-objs := cs4271.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
+snd-soc-dfbmcs320-objs := dfbmcs320.o
 snd-soc-dmic-objs := dmic.o
 snd-soc-l3-objs := l3.o
 snd-soc-max98088-objs := max98088.o
+snd-soc-max9850-objs := max9850.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
+snd-soc-sn95031-objs := sn95031.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
 snd-soc-tlv320dac33-objs := tlv320dac33.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-twl6040-objs := twl6040.o
@@ -61,6 +67,7 @@ snd-soc-wm8978-objs := wm8978.o
 snd-soc-wm8985-objs := wm8985.o
 snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8991-objs := wm8991.o
 snd-soc-wm8993-objs := wm8993.o
 snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
 snd-soc-wm8995-objs := wm8995.o
@@ -72,6 +79,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
 snd-soc-jz4740-codec-objs := jz4740.o
 
 # Amp
+snd-soc-lm4857-objs := lm4857.o
 snd-soc-max9877-objs := max9877.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-wm2000-objs := wm2000.o
@@ -88,23 +96,29 @@ obj-$(CONFIG_SND_SOC_AK4104)        += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CS4271)   += snd-soc-cs4271.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
+obj-$(CONFIG_SND_SOC_DFBMCS320)        += snd-soc-dfbmcs320.o
 obj-$(CONFIG_SND_SOC_DMIC)     += snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
-obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
+obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
+obj-$(CONFIG_SND_SOC_SN95031)  +=snd-soc-sn95031.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TVL320AIC32X4)     += snd-soc-tlv320aic32x4.o
 obj-$(CONFIG_SND_SOC_TLV320DAC33)      += snd-soc-tlv320dac33.o
 obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)  += snd-soc-twl6040.o
@@ -141,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8978)        += snd-soc-wm8978.o
 obj-$(CONFIG_SND_SOC_WM8985)   += snd-soc-wm8985.o
 obj-$(CONFIG_SND_SOC_WM8988)   += snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)   += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8991)   += snd-soc-wm8991.o
 obj-$(CONFIG_SND_SOC_WM8993)   += snd-soc-wm8993.o
 obj-$(CONFIG_SND_SOC_WM8994)   += snd-soc-wm8994.o
 obj-$(CONFIG_SND_SOC_WM8995)   += snd-soc-wm8995.o
@@ -151,6 +166,7 @@ obj-$(CONFIG_SND_SOC_WM9713)        += snd-soc-wm9713.o
 obj-$(CONFIG_SND_SOC_WM_HUBS)  += snd-soc-wm-hubs.o
 
 # Amp
+obj-$(CONFIG_SND_SOC_LM4857)   += snd-soc-lm4857.o
 obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)        += snd-soc-tpa6130a2.o
 obj-$(CONFIG_SND_SOC_WM2000)   += snd-soc-wm2000.o
index c27f8f59dc66d38cc4fb98fe7a428168333ac3db..cbf0b6d400b8d22d899854e6cd1d38b3d9fe2636 100644 (file)
@@ -294,7 +294,6 @@ static struct spi_driver ak4104_spi_driver = {
 
 static int __init ak4104_init(void)
 {
-       pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
        return spi_register_driver(&ak4104_spi_driver);
 }
 module_init(ak4104_init);
index f00eba313dfd6ffdf502452b8491e8e0963534a3..4be0570e3f1fc167fad8682e27f33dd7c810dd30 100644 (file)
 #define BCKO_MASK      (1 << 3)
 #define BCKO_64                BCKO_MASK
 
+#define DIF_MASK       (3 << 0)
+#define DSP            (0 << 0)
+#define RIGHT_J                (1 << 0)
+#define LEFT_J         (2 << 0)
+#define I2S            (3 << 0)
+
 /* MD_CTL2 */
 #define FS0            (1 << 0)
 #define FS1            (1 << 1)
@@ -354,6 +360,24 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        snd_soc_update_bits(codec, PW_MGMT2, MS, data);
        snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
 
+       /* format type */
+       data = 0;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_LEFT_J:
+               data = LEFT_J;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               data = I2S;
+               break;
+       /* FIXME
+        * Please add RIGHT_J / DSP support here
+        */
+       default:
+               return -EINVAL;
+               break;
+       }
+       snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+
        return 0;
 }
 
index 8b51245f2318853eb9a2da4215dae2c95c75d931..0206a17d72834aca9b91b5f45107f4ebcffcf21c 100644 (file)
@@ -193,12 +193,12 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
 /* The number of MCLK/LRCK ratios supported by the CS4270 */
 #define NUM_MCLK_RATIOS                ARRAY_SIZE(cs4270_mode_ratios)
 
-static int cs4270_reg_is_readable(unsigned int reg)
+static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
        return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
 }
 
-static int cs4270_reg_is_volatile(unsigned int reg)
+static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        /* Unreadable registers are considered volatile */
        if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
@@ -719,7 +719,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client)
 /*
  * cs4270_id - I2C device IDs supported by this driver
  */
-static struct i2c_device_id cs4270_id[] = {
+static const struct i2c_device_id cs4270_id[] = {
        {"cs4270", 0},
        {}
 };
@@ -743,8 +743,6 @@ static struct i2c_driver cs4270_i2c_driver = {
 
 static int __init cs4270_init(void)
 {
-       pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
-
        return i2c_add_driver(&cs4270_i2c_driver);
 }
 module_init(cs4270_init);
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
new file mode 100644 (file)
index 0000000..083aab9
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/cs4271.h>
+
+#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                           SNDRV_PCM_FMTBIT_S24_LE | \
+                           SNDRV_PCM_FMTBIT_S32_LE)
+#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
+
+/*
+ * CS4271 registers
+ * High byte represents SPI chip address (0x10) + write command (0)
+ * Low byte - codec register address
+ */
+#define CS4271_MODE1   0x2001  /* Mode Control 1 */
+#define CS4271_DACCTL  0x2002  /* DAC Control */
+#define CS4271_DACVOL  0x2003  /* DAC Volume & Mixing Control */
+#define CS4271_VOLA    0x2004  /* DAC Channel A Volume Control */
+#define CS4271_VOLB    0x2005  /* DAC Channel B Volume Control */
+#define CS4271_ADCCTL  0x2006  /* ADC Control */
+#define CS4271_MODE2   0x2007  /* Mode Control 2 */
+#define CS4271_CHIPID  0x2008  /* Chip ID */
+
+#define CS4271_FIRSTREG        CS4271_MODE1
+#define CS4271_LASTREG CS4271_MODE2
+#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
+
+/* Bit masks for the CS4271 registers */
+#define CS4271_MODE1_MODE_MASK 0xC0
+#define CS4271_MODE1_MODE_1X   0x00
+#define CS4271_MODE1_MODE_2X   0x80
+#define CS4271_MODE1_MODE_4X   0xC0
+
+#define CS4271_MODE1_DIV_MASK  0x30
+#define CS4271_MODE1_DIV_1     0x00
+#define CS4271_MODE1_DIV_15    0x10
+#define CS4271_MODE1_DIV_2     0x20
+#define CS4271_MODE1_DIV_3     0x30
+
+#define CS4271_MODE1_MASTER    0x08
+
+#define CS4271_MODE1_DAC_DIF_MASK      0x07
+#define CS4271_MODE1_DAC_DIF_LJ                0x00
+#define CS4271_MODE1_DAC_DIF_I2S       0x01
+#define CS4271_MODE1_DAC_DIF_RJ16      0x02
+#define CS4271_MODE1_DAC_DIF_RJ24      0x03
+#define CS4271_MODE1_DAC_DIF_RJ20      0x04
+#define CS4271_MODE1_DAC_DIF_RJ18      0x05
+
+#define CS4271_DACCTL_AMUTE    0x80
+#define CS4271_DACCTL_IF_SLOW  0x40
+
+#define CS4271_DACCTL_DEM_MASK 0x30
+#define CS4271_DACCTL_DEM_DIS  0x00
+#define CS4271_DACCTL_DEM_441  0x10
+#define CS4271_DACCTL_DEM_48   0x20
+#define CS4271_DACCTL_DEM_32   0x30
+
+#define CS4271_DACCTL_SVRU     0x08
+#define CS4271_DACCTL_SRD      0x04
+#define CS4271_DACCTL_INVA     0x02
+#define CS4271_DACCTL_INVB     0x01
+
+#define CS4271_DACVOL_BEQUA    0x40
+#define CS4271_DACVOL_SOFT     0x20
+#define CS4271_DACVOL_ZEROC    0x10
+
+#define CS4271_DACVOL_ATAPI_MASK       0x0F
+#define CS4271_DACVOL_ATAPI_M_M                0x00
+#define CS4271_DACVOL_ATAPI_M_BR       0x01
+#define CS4271_DACVOL_ATAPI_M_BL       0x02
+#define CS4271_DACVOL_ATAPI_M_BLR2     0x03
+#define CS4271_DACVOL_ATAPI_AR_M       0x04
+#define CS4271_DACVOL_ATAPI_AR_BR      0x05
+#define CS4271_DACVOL_ATAPI_AR_BL      0x06
+#define CS4271_DACVOL_ATAPI_AR_BLR2    0x07
+#define CS4271_DACVOL_ATAPI_AL_M       0x08
+#define CS4271_DACVOL_ATAPI_AL_BR      0x09
+#define CS4271_DACVOL_ATAPI_AL_BL      0x0A
+#define CS4271_DACVOL_ATAPI_AL_BLR2    0x0B
+#define CS4271_DACVOL_ATAPI_ALR2_M     0x0C
+#define CS4271_DACVOL_ATAPI_ALR2_BR    0x0D
+#define CS4271_DACVOL_ATAPI_ALR2_BL    0x0E
+#define CS4271_DACVOL_ATAPI_ALR2_BLR2  0x0F
+
+#define CS4271_VOLA_MUTE       0x80
+#define CS4271_VOLA_VOL_MASK   0x7F
+#define CS4271_VOLB_MUTE       0x80
+#define CS4271_VOLB_VOL_MASK   0x7F
+
+#define CS4271_ADCCTL_DITHER16 0x20
+
+#define CS4271_ADCCTL_ADC_DIF_MASK     0x10
+#define CS4271_ADCCTL_ADC_DIF_LJ       0x00
+#define CS4271_ADCCTL_ADC_DIF_I2S      0x10
+
+#define CS4271_ADCCTL_MUTEA    0x08
+#define CS4271_ADCCTL_MUTEB    0x04
+#define CS4271_ADCCTL_HPFDA    0x02
+#define CS4271_ADCCTL_HPFDB    0x01
+
+#define CS4271_MODE2_LOOP      0x10
+#define CS4271_MODE2_MUTECAEQUB        0x08
+#define CS4271_MODE2_FREEZE    0x04
+#define CS4271_MODE2_CPEN      0x02
+#define CS4271_MODE2_PDN       0x01
+
+#define CS4271_CHIPID_PART_MASK        0xF0
+#define CS4271_CHIPID_REV_MASK 0x0F
+
+/*
+ * Default CS4271 power-up configuration
+ * Array contains non-existing in hw register at address 0
+ * Array do not include Chip ID, as codec driver does not use
+ * registers read operations at all
+ */
+static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = {
+       0,
+       0,
+       CS4271_DACCTL_AMUTE,
+       CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR,
+       0,
+       0,
+       0,
+       0,
+};
+
+struct cs4271_private {
+       /* SND_SOC_I2C or SND_SOC_SPI */
+       enum snd_soc_control_type       bus_type;
+       void                            *control_data;
+       unsigned int                    mclk;
+       bool                            master;
+       bool                            deemph;
+       /* Current sample rate for de-emphasis control */
+       int                             rate;
+       /* GPIO driving Reset pin, if any */
+       int                             gpio_nreset;
+       /* GPIO that disable serial bus, if any */
+       int                             gpio_disable;
+};
+
+/*
+ * @freq is the desired MCLK rate
+ * MCLK rate should (c) be the sample rate, multiplied by one of the
+ * ratios listed in cs4271_mclk_fs_ratios table
+ */
+static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+       cs4271->mclk = freq;
+       return 0;
+}
+
+static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int format)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+       int ret;
+
+       switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               cs4271->master = 0;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               cs4271->master = 1;
+               val |= CS4271_MODE1_MASTER;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_LEFT_J:
+               val |= CS4271_MODE1_DAC_DIF_LJ;
+               ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
+                       CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
+               if (ret < 0)
+                       return ret;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val |= CS4271_MODE1_DAC_DIF_I2S;
+               ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
+                       CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
+               if (ret < 0)
+                       return ret;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_MODE1,
+               CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int cs4271_deemph[] = {0, 44100, 48000, 32000};
+
+static int cs4271_set_deemph(struct snd_soc_codec *codec)
+{
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       int i, ret;
+       int val = CS4271_DACCTL_DEM_DIS;
+
+       if (cs4271->deemph) {
+               /* Find closest de-emphasis freq */
+               val = 1;
+               for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
+                       if (abs(cs4271_deemph[i] - cs4271->rate) <
+                           abs(cs4271_deemph[val] - cs4271->rate))
+                               val = i;
+               val <<= 4;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_DACCTL,
+               CS4271_DACCTL_DEM_MASK, val);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = cs4271->deemph;
+       return 0;
+}
+
+static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+       cs4271->deemph = ucontrol->value.enumerated.item[0];
+       return cs4271_set_deemph(codec);
+}
+
+struct cs4271_clk_cfg {
+       bool            master;         /* codec mode */
+       u8              speed_mode;     /* codec speed mode: 1x, 2x, 4x */
+       unsigned short  ratio;          /* MCLK / sample rate */
+       u8              ratio_mask;     /* ratio bit mask for Master mode */
+};
+
+static struct cs4271_clk_cfg cs4271_clk_tab[] = {
+       {1, CS4271_MODE1_MODE_1X, 256,  CS4271_MODE1_DIV_1},
+       {1, CS4271_MODE1_MODE_1X, 384,  CS4271_MODE1_DIV_15},
+       {1, CS4271_MODE1_MODE_1X, 512,  CS4271_MODE1_DIV_2},
+       {1, CS4271_MODE1_MODE_1X, 768,  CS4271_MODE1_DIV_3},
+       {1, CS4271_MODE1_MODE_2X, 128,  CS4271_MODE1_DIV_1},
+       {1, CS4271_MODE1_MODE_2X, 192,  CS4271_MODE1_DIV_15},
+       {1, CS4271_MODE1_MODE_2X, 256,  CS4271_MODE1_DIV_2},
+       {1, CS4271_MODE1_MODE_2X, 384,  CS4271_MODE1_DIV_3},
+       {1, CS4271_MODE1_MODE_4X, 64,   CS4271_MODE1_DIV_1},
+       {1, CS4271_MODE1_MODE_4X, 96,   CS4271_MODE1_DIV_15},
+       {1, CS4271_MODE1_MODE_4X, 128,  CS4271_MODE1_DIV_2},
+       {1, CS4271_MODE1_MODE_4X, 192,  CS4271_MODE1_DIV_3},
+       {0, CS4271_MODE1_MODE_1X, 256,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_1X, 384,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_1X, 512,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_1X, 768,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_2X, 128,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_2X, 192,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_2X, 256,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_2X, 384,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_2X, 512,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_4X, 64,   CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_4X, 96,   CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_4X, 128,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_4X, 192,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_4X, 256,  CS4271_MODE1_DIV_2},
+};
+
+#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+
+static int cs4271_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       int i, ret;
+       unsigned int ratio, val;
+
+       cs4271->rate = params_rate(params);
+
+       /* Configure DAC */
+       if (cs4271->rate < 50000)
+               val = CS4271_MODE1_MODE_1X;
+       else if (cs4271->rate < 100000)
+               val = CS4271_MODE1_MODE_2X;
+       else
+               val = CS4271_MODE1_MODE_4X;
+
+       ratio = cs4271->mclk / cs4271->rate;
+       for (i = 0; i < CS4171_NR_RATIOS; i++)
+               if ((cs4271_clk_tab[i].master == cs4271->master) &&
+                   (cs4271_clk_tab[i].speed_mode == val) &&
+                   (cs4271_clk_tab[i].ratio == ratio))
+                       break;
+
+       if (i == CS4171_NR_RATIOS) {
+               dev_err(codec->dev, "Invalid sample rate\n");
+               return -EINVAL;
+       }
+
+       val |= cs4271_clk_tab[i].ratio_mask;
+
+       ret = snd_soc_update_bits(codec, CS4271_MODE1,
+               CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
+       if (ret < 0)
+               return ret;
+
+       return cs4271_set_deemph(codec);
+}
+
+static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       int ret;
+       int val_a = 0;
+       int val_b = 0;
+
+       if (mute) {
+               val_a = CS4271_VOLA_MUTE;
+               val_b = CS4271_VOLB_MUTE;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a);
+       if (ret < 0)
+               return ret;
+       ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* CS4271 controls */
+static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
+
+static const struct snd_kcontrol_new cs4271_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
+               0, 0x7F, 1, cs4271_dac_tlv),
+       SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
+       SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
+       SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
+       SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+               cs4271_get_deemph, cs4271_put_deemph),
+       SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
+       SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
+       SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
+       SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
+       SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
+       SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
+       SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
+       SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
+       SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
+       SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
+               7, 1, 1),
+};
+
+static struct snd_soc_dai_ops cs4271_dai_ops = {
+       .hw_params      = cs4271_hw_params,
+       .set_sysclk     = cs4271_set_dai_sysclk,
+       .set_fmt        = cs4271_set_dai_fmt,
+       .digital_mute   = cs4271_digital_mute,
+};
+
+static struct snd_soc_dai_driver cs4271_dai = {
+       .name = "cs4271-hifi",
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = CS4271_PCM_RATES,
+               .formats        = CS4271_PCM_FORMATS,
+       },
+       .capture = {
+               .stream_name    = "Capture",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = CS4271_PCM_RATES,
+               .formats        = CS4271_PCM_FORMATS,
+       },
+       .ops = &cs4271_dai_ops,
+       .symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
+{
+       int ret;
+       /* Set power-down bit */
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int cs4271_soc_resume(struct snd_soc_codec *codec)
+{
+       int ret;
+       /* Restore codec state */
+       ret = snd_soc_cache_sync(codec);
+       if (ret < 0)
+               return ret;
+       /* then disable the power-down bit */
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+#else
+#define cs4271_soc_suspend     NULL
+#define cs4271_soc_resume      NULL
+#endif /* CONFIG_PM */
+
+static int cs4271_probe(struct snd_soc_codec *codec)
+{
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+       int ret;
+       int gpio_nreset = -EINVAL;
+
+       codec->control_data = cs4271->control_data;
+
+       if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
+               gpio_nreset = cs4271plat->gpio_nreset;
+
+       if (gpio_nreset >= 0)
+               if (gpio_request(gpio_nreset, "CS4271 Reset"))
+                       gpio_nreset = -EINVAL;
+       if (gpio_nreset >= 0) {
+               /* Reset codec */
+               gpio_direction_output(gpio_nreset, 0);
+               udelay(1);
+               gpio_set_value(gpio_nreset, 1);
+               /* Give the codec time to wake up */
+               udelay(1);
+       }
+
+       cs4271->gpio_nreset = gpio_nreset;
+
+       /*
+        * In case of I2C, chip address specified in board data.
+        * So cache IO operations use 8 bit codec register address.
+        * In case of SPI, chip address and register address
+        * passed together as 16 bit value.
+        * Anyway, register address is masked with 0xFF inside
+        * soc-cache code.
+        */
+       if (cs4271->bus_type == SND_SOC_SPI)
+               ret = snd_soc_codec_set_cache_io(codec, 16, 8,
+                       cs4271->bus_type);
+       else
+               ret = snd_soc_codec_set_cache_io(codec, 8, 8,
+                       cs4271->bus_type);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, 0,
+               CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
+       if (ret < 0)
+               return ret;
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
+       if (ret < 0)
+               return ret;
+       /* Power-up sequence requires 85 uS */
+       udelay(85);
+
+       return snd_soc_add_controls(codec, cs4271_snd_controls,
+               ARRAY_SIZE(cs4271_snd_controls));
+}
+
+static int cs4271_remove(struct snd_soc_codec *codec)
+{
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       int gpio_nreset;
+
+       gpio_nreset = cs4271->gpio_nreset;
+
+       if (gpio_is_valid(gpio_nreset)) {
+               /* Set codec to the reset state */
+               gpio_set_value(gpio_nreset, 0);
+               gpio_free(gpio_nreset);
+       }
+
+       return 0;
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
+       .probe                  = cs4271_probe,
+       .remove                 = cs4271_remove,
+       .suspend                = cs4271_soc_suspend,
+       .resume                 = cs4271_soc_resume,
+       .reg_cache_default      = cs4271_dflt_reg,
+       .reg_cache_size         = ARRAY_SIZE(cs4271_dflt_reg),
+       .reg_word_size          = sizeof(cs4271_dflt_reg[0]),
+       .compress_type          = SND_SOC_FLAT_COMPRESSION,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit cs4271_spi_probe(struct spi_device *spi)
+{
+       struct cs4271_private *cs4271;
+
+       cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL);
+       if (!cs4271)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, cs4271);
+       cs4271->control_data = spi;
+       cs4271->bus_type = SND_SOC_SPI;
+
+       return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
+               &cs4271_dai, 1);
+}
+
+static int __devexit cs4271_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+       .driver = {
+               .name   = "cs4271",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = cs4271_spi_probe,
+       .remove         = __devexit_p(cs4271_spi_remove),
+};
+#endif /* defined(CONFIG_SPI_MASTER) */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static const struct i2c_device_id cs4271_i2c_id[] = {
+       {"cs4271", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static int __devinit cs4271_i2c_probe(struct i2c_client *client,
+                                     const struct i2c_device_id *id)
+{
+       struct cs4271_private *cs4271;
+
+       cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL);
+       if (!cs4271)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, cs4271);
+       cs4271->control_data = client;
+       cs4271->bus_type = SND_SOC_I2C;
+
+       return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
+               &cs4271_dai, 1);
+}
+
+static int __devexit cs4271_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static struct i2c_driver cs4271_i2c_driver = {
+       .driver = {
+               .name   = "cs4271",
+               .owner  = THIS_MODULE,
+       },
+       .id_table       = cs4271_i2c_id,
+       .probe          = cs4271_i2c_probe,
+       .remove         = __devexit_p(cs4271_i2c_remove),
+};
+#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */
+
+/*
+ * We only register our serial bus driver here without
+ * assignment to particular chip. So if any of the below
+ * fails, there is some problem with I2C or SPI subsystem.
+ * In most cases this module will be compiled with support
+ * of only one serial bus.
+ */
+static int __init cs4271_modinit(void)
+{
+       int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&cs4271_i2c_driver);
+       if (ret) {
+               pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
+               return ret;
+       }
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&cs4271_spi_driver);
+       if (ret) {
+               pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+module_init(cs4271_modinit);
+
+static void __exit cs4271_modexit(void)
+{
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&cs4271_spi_driver);
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&cs4271_i2c_driver);
+#endif
+}
+module_exit(cs4271_modexit);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c
new file mode 100644 (file)
index 0000000..704bbde
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Driver for the DFBM-CS320 bluetooth module
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver dfbmcs320_dai = {
+       .name = "dfbmcs320-pcm",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_8000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_8000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320;
+
+static int __devinit dfbmcs320_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320,
+                       &dfbmcs320_dai, 1);
+}
+
+static int __devexit dfbmcs320_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver dfmcs320_driver = {
+       .driver = {
+               .name = "dfbmcs320",
+               .owner = THIS_MODULE,
+       },
+       .probe = dfbmcs320_probe,
+       .remove = __devexit_p(dfbmcs320_remove),
+};
+
+static int __init dfbmcs320_init(void)
+{
+       return platform_driver_register(&dfmcs320_driver);
+}
+module_init(dfbmcs320_init);
+
+static void __exit dfbmcs320_exit(void)
+{
+       platform_driver_unregister(&dfmcs320_driver);
+}
+module_exit(dfbmcs320_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
new file mode 100644 (file)
index 0000000..72de47e
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * LM4857 AMP driver
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct lm4857 {
+       struct i2c_client *i2c;
+       uint8_t mode;
+};
+
+static const uint8_t lm4857_default_regs[] = {
+       0x00, 0x00, 0x00, 0x00,
+};
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg,
+               unsigned int value)
+{
+       uint8_t data;
+       int ret;
+
+       ret = snd_soc_cache_write(codec, reg, value);
+       if (ret < 0)
+               return ret;
+
+       data = (reg << 6) | value;
+       ret = i2c_master_send(codec->control_data, &data, 1);
+       if (ret != 1) {
+               dev_err(codec->dev, "Failed to write register: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static unsigned int lm4857_read(struct snd_soc_codec *codec,
+               unsigned int reg)
+{
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_cache_read(codec, reg, &val);
+       if (ret)
+               return -1;
+
+       return val;
+}
+
+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = lm4857->mode;
+
+       return 0;
+}
+
+static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+       uint8_t value = ucontrol->value.integer.value[0];
+
+       lm4857->mode = value;
+
+       if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6);
+
+       return 1;
+}
+
+static int lm4857_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0);
+               break;
+       default:
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static const char *lm4857_mode[] = {
+       "Earpiece",
+       "Loudspeaker",
+       "Loudspeaker + Headphone",
+       "Headphone",
+};
+
+static const struct soc_enum lm4857_mode_enum =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode);
+
+static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("IN"),
+
+       SND_SOC_DAPM_OUTPUT("LS"),
+       SND_SOC_DAPM_OUTPUT("HP"),
+       SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
+static const struct snd_kcontrol_new lm4857_controls[] = {
+       SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+               stereo_tlv),
+       SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+               stereo_tlv),
+       SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+               mono_tlv),
+       SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0),
+       SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0),
+       SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL,
+               LM4857_WAKEUP, 1, 0),
+       SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
+               LM4857_EPGAIN, 1, 0),
+
+       SOC_ENUM_EXT("Mode", lm4857_mode_enum,
+               lm4857_get_mode, lm4857_set_mode),
+};
+
+/* There is a demux inbetween the the input signal and the output signals.
+ * Currently there is no easy way to model it in ASoC and since it does not make
+ * much of a difference in practice simply connect the input direclty to the
+ * outputs. */
+static const struct snd_soc_dapm_route lm4857_routes[] = {
+       {"LS", NULL, "IN"},
+       {"HP", NULL, "IN"},
+       {"EP", NULL, "IN"},
+};
+
+static int lm4857_probe(struct snd_soc_codec *codec)
+{
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int ret;
+
+       codec->control_data = lm4857->i2c;
+
+       ret = snd_soc_add_controls(codec, lm4857_controls,
+                       ARRAY_SIZE(lm4857_controls));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets,
+                       ARRAY_SIZE(lm4857_dapm_widgets));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, lm4857_routes,
+                       ARRAY_SIZE(lm4857_routes));
+       if (ret)
+               return ret;
+
+       snd_soc_dapm_new_widgets(dapm);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
+       .write = lm4857_write,
+       .read = lm4857_read,
+       .probe = lm4857_probe,
+       .reg_cache_size = ARRAY_SIZE(lm4857_default_regs),
+       .reg_word_size = sizeof(uint8_t),
+       .reg_cache_default = lm4857_default_regs,
+       .set_bias_level = lm4857_set_bias_level,
+};
+
+static int __devinit lm4857_i2c_probe(struct i2c_client *i2c,
+       const struct i2c_device_id *id)
+{
+       struct lm4857 *lm4857;
+       int ret;
+
+       lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL);
+       if (!lm4857)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, lm4857);
+
+       lm4857->i2c = i2c;
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
+
+       if (ret) {
+               kfree(lm4857);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit lm4857_i2c_remove(struct i2c_client *i2c)
+{
+       struct lm4857 *lm4857 = i2c_get_clientdata(i2c);
+
+       snd_soc_unregister_codec(&i2c->dev);
+       kfree(lm4857);
+
+       return 0;
+}
+
+static const struct i2c_device_id lm4857_i2c_id[] = {
+       { "lm4857", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
+
+static struct i2c_driver lm4857_i2c_driver = {
+       .driver = {
+               .name = "lm4857",
+               .owner = THIS_MODULE,
+       },
+       .probe = lm4857_i2c_probe,
+       .remove = __devexit_p(lm4857_i2c_remove),
+       .id_table = lm4857_i2c_id,
+};
+
+static int __init lm4857_init(void)
+{
+       return i2c_add_driver(&lm4857_i2c_driver);
+}
+module_init(lm4857_init);
+
+static void __exit lm4857_exit(void)
+{
+       i2c_del_driver(&lm4857_i2c_driver);
+}
+module_exit(lm4857_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("LM4857 amplifier driver");
+MODULE_LICENSE("GPL");
index 89498f9ad2e5e5d9a2ecd6d3e0cfe1bde239497f..bd0517cb7980b3a309cd7e8e10a7e2e3ba432bfb 100644 (file)
@@ -608,7 +608,7 @@ static struct {
        { 0xFF, 0x00, 1 }, /* FF */
 };
 
-static int max98088_volatile_register(unsigned int reg)
+static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        return max98088_access[reg].vol;
 }
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
new file mode 100644 (file)
index 0000000..208d2ee
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * max9850.c  --  codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ *
+ * Author: Christian Glindkamp <christian.glindkamp@taskit.de>
+ *
+ * Initial development of this code was funded by
+ * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9850.h"
+
+struct max9850_priv {
+       unsigned int sysclk;
+};
+
+/* max9850 register cache */
+static const u8 max9850_reg[MAX9850_CACHEREGNUM] = {
+       0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* these registers are not used at the moment but provided for the sake of
+ * completeness */
+static int max9850_volatile_register(struct snd_soc_codec *codec,
+               unsigned int reg)
+{
+       switch (reg) {
+       case MAX9850_STATUSA:
+       case MAX9850_STATUSB:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static const unsigned int max9850_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
+       0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
+       0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
+       0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0),
+};
+
+static const struct snd_kcontrol_new max9850_controls[] = {
+SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
+SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1),
+SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new max9850_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0),
+SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0,
+               &max9850_mixer_controls[0],
+               ARRAY_SIZE(max9850_mixer_controls)),
+SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0),
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_INPUT("INL"),
+SND_SOC_DAPM_INPUT("INR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       /* output mixer */
+       {"Output Mixer", NULL, "DAC"},
+       {"Output Mixer", "Line In Switch", "Line Input"},
+
+       /* outputs */
+       {"Headphone Output", NULL, "Output Mixer"},
+       {"HPL", NULL, "Headphone Output"},
+       {"HPR", NULL, "Headphone Output"},
+       {"OUTL", NULL, "Output Mixer"},
+       {"OUTR", NULL, "Output Mixer"},
+
+       /* inputs */
+       {"Line Input", NULL, "INL"},
+       {"Line Input", NULL, "INR"},
+
+       /* supplies */
+       {"Output Mixer", NULL, "Charge Pump 1"},
+       {"Output Mixer", NULL, "Charge Pump 2"},
+       {"Output Mixer", NULL, "SHDN"},
+       {"DAC", NULL, "MCLK"},
+};
+
+static int max9850_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+       u64 lrclk_div;
+       u8 sf, da;
+
+       if (!max9850->sysclk)
+               return -EINVAL;
+
+       /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
+       sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1;
+       lrclk_div = (1 << 22);
+       lrclk_div *= params_rate(params);
+       lrclk_div *= sf;
+       do_div(lrclk_div, max9850->sysclk);
+
+       snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
+       snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               da = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               da = 0x2;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               da = 0x3;
+               break;
+       default:
+               return -EINVAL;
+       }
+       snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
+
+       return 0;
+}
+
+static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+
+       /* calculate mclk -> iclk divider */
+       if (freq <= 13000000)
+               snd_soc_write(codec, MAX9850_CLOCK, 0x0);
+       else if (freq <= 26000000)
+               snd_soc_write(codec, MAX9850_CLOCK, 0x4);
+       else if (freq <= 40000000)
+               snd_soc_write(codec, MAX9850_CLOCK, 0x8);
+       else
+               return -EINVAL;
+
+       max9850->sysclk = freq;
+       return 0;
+}
+
+static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 da = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               da |= MAX9850_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               da |= MAX9850_DLY;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               da |= MAX9850_RTJ;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               da |= MAX9850_BCINV | MAX9850_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               da |= MAX9850_BCINV;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               da |= MAX9850_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set da */
+       snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
+
+       return 0;
+}
+
+static int max9850_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = snd_soc_cache_sync(codec);
+                       if (ret) {
+                               dev_err(codec->dev,
+                                       "Failed to sync cache: %d\n", ret);
+                               return ret;
+                       }
+               }
+               break;
+       case SND_SOC_BIAS_OFF:
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max9850_dai_ops = {
+       .hw_params      = max9850_hw_params,
+       .set_sysclk     = max9850_set_dai_sysclk,
+       .set_fmt        = max9850_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver max9850_dai = {
+       .name = "max9850-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX9850_RATES,
+               .formats = MAX9850_FORMATS
+       },
+       .ops = &max9850_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int max9850_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max9850_resume(struct snd_soc_codec *codec)
+{
+       max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define max9850_suspend NULL
+#define max9850_resume NULL
+#endif
+
+static int max9850_probe(struct snd_soc_codec *codec)
+{
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int ret;
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* enable zero-detect */
+       snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
+       /* enable slew-rate control */
+       snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
+       /* set slew-rate 125ms */
+       snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
+
+       snd_soc_dapm_new_controls(dapm, max9850_dapm_widgets,
+                                 ARRAY_SIZE(max9850_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_add_controls(codec, max9850_controls,
+                       ARRAY_SIZE(max9850_controls));
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
+       .probe =        max9850_probe,
+       .suspend =      max9850_suspend,
+       .resume =       max9850_resume,
+       .set_bias_level = max9850_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(max9850_reg),
+       .reg_word_size = sizeof(u8),
+       .reg_cache_default = max9850_reg,
+       .volatile_register = max9850_volatile_register,
+};
+
+static int __devinit max9850_i2c_probe(struct i2c_client *i2c,
+               const struct i2c_device_id *id)
+{
+       struct max9850_priv *max9850;
+       int ret;
+
+       max9850 = kzalloc(sizeof(struct max9850_priv), GFP_KERNEL);
+       if (max9850 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max9850);
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max9850, &max9850_dai, 1);
+       if (ret < 0)
+               kfree(max9850);
+       return ret;
+}
+
+static __devexit int max9850_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id max9850_i2c_id[] = {
+       { "max9850", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
+
+static struct i2c_driver max9850_i2c_driver = {
+       .driver = {
+               .name = "max9850",
+               .owner = THIS_MODULE,
+       },
+       .probe = max9850_i2c_probe,
+       .remove = __devexit_p(max9850_i2c_remove),
+       .id_table = max9850_i2c_id,
+};
+
+static int __init max9850_init(void)
+{
+       return i2c_add_driver(&max9850_i2c_driver);
+}
+module_init(max9850_init);
+
+static void __exit max9850_exit(void)
+{
+       i2c_del_driver(&max9850_i2c_driver);
+}
+module_exit(max9850_exit);
+
+MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
+MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h
new file mode 100644 (file)
index 0000000..72b1ddb
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * max9850.h  --  codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ * Author: Christian Glindkamp <christian.glindkamp@taskit.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.
+ *
+ */
+
+#ifndef _MAX9850_H
+#define _MAX9850_H
+
+#define MAX9850_STATUSA                        0x00
+#define MAX9850_STATUSB                        0x01
+#define MAX9850_VOLUME                 0x02
+#define MAX9850_GENERAL_PURPOSE                0x03
+#define MAX9850_INTERRUPT              0x04
+#define MAX9850_ENABLE                 0x05
+#define MAX9850_CLOCK                  0x06
+#define MAX9850_CHARGE_PUMP            0x07
+#define MAX9850_LRCLK_MSB              0x08
+#define MAX9850_LRCLK_LSB              0x09
+#define MAX9850_DIGITAL_AUDIO          0x0a
+
+#define MAX9850_CACHEREGNUM 11
+
+/* MAX9850_DIGITAL_AUDIO */
+#define MAX9850_MASTER                 (1<<7)
+#define MAX9850_INV                    (1<<6)
+#define MAX9850_BCINV                  (1<<5)
+#define MAX9850_DLY                    (1<<3)
+#define MAX9850_RTJ                    (1<<2)
+
+#endif
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
new file mode 100644 (file)
index 0000000..1f7217f
--- /dev/null
@@ -0,0 +1,1513 @@
+/*
+ * sgtl5000.c  --  SGTL5000 ALSA SoC Audio driver
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "sgtl5000.h"
+
+#define SGTL5000_DAP_REG_OFFSET        0x0100
+#define SGTL5000_MAX_REG_OFFSET        0x013A
+
+/* default value of sgtl5000 registers except DAP */
+static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] =  {
+       0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */
+       0x0000, /* 0x0002, CHIP_DIG_POWER. */
+       0x0008, /* 0x0004, CHIP_CKL_CTRL */
+       0x0010, /* 0x0006, CHIP_I2S_CTRL */
+       0x0000, /* 0x0008, reserved */
+       0x0008, /* 0x000A, CHIP_SSS_CTRL */
+       0x0000, /* 0x000C, reserved */
+       0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */
+       0x3c3c, /* 0x0010, CHIP_DAC_VOL */
+       0x0000, /* 0x0012, reserved */
+       0x015f, /* 0x0014, CHIP_PAD_STRENGTH */
+       0x0000, /* 0x0016, reserved */
+       0x0000, /* 0x0018, reserved */
+       0x0000, /* 0x001A, reserved */
+       0x0000, /* 0x001E, reserved */
+       0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */
+       0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */
+       0x0111, /* 0x0024, CHIP_ANN_CTRL */
+       0x0000, /* 0x0026, CHIP_LINREG_CTRL */
+       0x0000, /* 0x0028, CHIP_REF_CTRL */
+       0x0000, /* 0x002A, CHIP_MIC_CTRL */
+       0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */
+       0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */
+       0x7060, /* 0x0030, CHIP_ANA_POWER */
+       0x5000, /* 0x0032, CHIP_PLL_CTRL */
+       0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */
+       0x0000, /* 0x0036, CHIP_ANA_STATUS */
+       0x0000, /* 0x0038, reserved */
+       0x0000, /* 0x003A, CHIP_ANA_TEST2 */
+       0x0000, /* 0x003C, CHIP_SHORT_CTRL */
+       0x0000, /* reserved */
+};
+
+/* default value of dap registers */
+static const u16 sgtl5000_dap_regs[] = {
+       0x0000, /* 0x0100, DAP_CONTROL */
+       0x0000, /* 0x0102, DAP_PEQ */
+       0x0040, /* 0x0104, DAP_BASS_ENHANCE */
+       0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */
+       0x0000, /* 0x0108, DAP_AUDIO_EQ */
+       0x0040, /* 0x010A, DAP_SGTL_SURROUND */
+       0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */
+       0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */
+       0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */
+       0x0000, /* 0x0112, reserved */
+       0x0000, /* 0x0114, reserved */
+       0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */
+       0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */
+       0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */
+       0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */
+       0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */
+       0x8000, /* 0x0120, DAP_MAIN_CHAN */
+       0x0000, /* 0x0122, DAP_MIX_CHAN */
+       0x0510, /* 0x0124, DAP_AVC_CTRL */
+       0x1473, /* 0x0126, DAP_AVC_THRESHOLD */
+       0x0028, /* 0x0128, DAP_AVC_ATTACK */
+       0x0050, /* 0x012A, DAP_AVC_DECAY */
+       0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */
+       0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */
+       0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */
+       0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */
+       0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */
+       0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */
+       0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */
+       0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+       VDDA,
+       VDDIO,
+       VDDD,
+       SGTL5000_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
+       "VDDA",
+       "VDDIO",
+       "VDDD"
+};
+
+#define LDO_CONSUMER_NAME      "VDDD_LDO"
+#define LDO_VOLTAGE            1200000
+
+static struct regulator_consumer_supply ldo_consumer[] = {
+       REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
+};
+
+static struct regulator_init_data ldo_init_data = {
+       .constraints = {
+               .min_uV                 = 850000,
+               .max_uV                 = 1600000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL,
+               .valid_ops_mask         = REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies = 1,
+       .consumer_supplies = &ldo_consumer[0],
+};
+
+/*
+ * sgtl5000 internal ldo regulator,
+ * enabled when VDDD not provided
+ */
+struct ldo_regulator {
+       struct regulator_desc desc;
+       struct regulator_dev *dev;
+       int voltage;
+       void *codec_data;
+       bool enabled;
+};
+
+/* sgtl5000 private structure in codec */
+struct sgtl5000_priv {
+       int sysclk;     /* sysclk rate */
+       int master;     /* i2s master or not */
+       int fmt;        /* i2s data format */
+       struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
+       struct ldo_regulator *ldo;
+};
+
+/*
+ * mic_bias power on/off share the same register bits with
+ * output impedance of mic bias, when power on mic bias, we
+ * need reclaim it to impedance value.
+ * 0x0 = Powered off
+ * 0x1 = 2Kohm
+ * 0x2 = 4Kohm
+ * 0x3 = 8Kohm
+ */
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* change mic bias resistor to 4Kohm */
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
+                               SGTL5000_BIAS_R_4k, SGTL5000_BIAS_R_4k);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /*
+                * SGTL5000_BIAS_R_8k as mask to clean the two bits
+                * of mic bias and output impedance
+                */
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
+                               SGTL5000_BIAS_R_8k, 0);
+               break;
+       }
+       return 0;
+}
+
+/*
+ * using codec assist to small pop, hp_powerup or lineout_powerup
+ * should stay setting until vag_powerup is fully ramped down,
+ * vag fully ramped down require 400ms.
+ */
+static int small_pop_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_VAG_POWERUP, 0);
+               msleep(400);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+/* input sources for ADC */
+static const char *adc_mux_text[] = {
+       "MIC_IN", "LINE_IN"
+};
+
+static const struct soc_enum adc_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text);
+
+static const struct snd_kcontrol_new adc_mux =
+SOC_DAPM_ENUM("Capture Mux", adc_enum);
+
+/* input sources for DAC */
+static const char *dac_mux_text[] = {
+       "DAC", "LINE_IN"
+};
+
+static const struct soc_enum dac_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("LINE_IN"),
+       SND_SOC_DAPM_INPUT("MIC_IN"),
+
+       SND_SOC_DAPM_OUTPUT("HP_OUT"),
+       SND_SOC_DAPM_OUTPUT("LINE_OUT"),
+
+       SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
+                               mic_bias_event,
+                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0,
+                       small_pop_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0,
+                       small_pop_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
+       SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+
+       /* aif for i2s input */
+       SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
+                               0, SGTL5000_CHIP_DIG_POWER,
+                               0, 0),
+
+       /* aif for i2s output */
+       SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
+                               0, SGTL5000_CHIP_DIG_POWER,
+                               1, 0),
+
+       SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
+
+       SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
+};
+
+/* routes for sgtl5000 */
+static const struct snd_soc_dapm_route audio_map[] = {
+       {"Capture Mux", "LINE_IN", "LINE_IN"},  /* line_in --> adc_mux */
+       {"Capture Mux", "MIC_IN", "MIC_IN"},    /* mic_in --> adc_mux */
+
+       {"ADC", NULL, "Capture Mux"},           /* adc_mux --> adc */
+       {"AIFOUT", NULL, "ADC"},                /* adc --> i2s_out */
+
+       {"DAC", NULL, "AIFIN"},                 /* i2s-->dac,skip audio mux */
+       {"Headphone Mux", "DAC", "DAC"},        /* dac --> hp_mux */
+       {"LO", NULL, "DAC"},                    /* dac --> line_out */
+
+       {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
+       {"HP", NULL, "Headphone Mux"},          /* hp_mux --> hp */
+
+       {"LINE_OUT", NULL, "LO"},
+       {"HP_OUT", NULL, "HP"},
+};
+
+/* custom function to fetch info of PCM playback volume */
+static int dac_info_volsw(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xfc - 0x3c;
+       return 0;
+}
+
+/*
+ * custom function to get of PCM playback volume
+ *
+ * dac volume register
+ * 15-------------8-7--------------0
+ * | R channel vol | L channel vol |
+ *  -------------------------------
+ *
+ * PCM volume with 0.5017 dB steps from 0 to -90 dB
+ *
+ * register values map to dB
+ * 0x3B and less = Reserved
+ * 0x3C = 0 dB
+ * 0x3D = -0.5 dB
+ * 0xF0 = -90 dB
+ * 0xFC and greater = Muted
+ *
+ * register value map to userspace value
+ *
+ * register value      0x3c(0dB)         0xf0(-90dB)0xfc
+ *                     ------------------------------
+ * userspace value     0xc0                         0
+ */
+static int dac_get_volsw(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int reg;
+       int l;
+       int r;
+
+       reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+
+       /* get left channel volume */
+       l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
+
+       /* get right channel volume */
+       r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+       /* make sure value fall in (0x3c,0xfc) */
+       l = clamp(l, 0x3c, 0xfc);
+       r = clamp(r, 0x3c, 0xfc);
+
+       /* invert it and map to userspace value */
+       l = 0xfc - l;
+       r = 0xfc - r;
+
+       ucontrol->value.integer.value[0] = l;
+       ucontrol->value.integer.value[1] = r;
+
+       return 0;
+}
+
+/*
+ * custom function to put of PCM playback volume
+ *
+ * dac volume register
+ * 15-------------8-7--------------0
+ * | R channel vol | L channel vol |
+ *  -------------------------------
+ *
+ * PCM volume with 0.5017 dB steps from 0 to -90 dB
+ *
+ * register values map to dB
+ * 0x3B and less = Reserved
+ * 0x3C = 0 dB
+ * 0x3D = -0.5 dB
+ * 0xF0 = -90 dB
+ * 0xFC and greater = Muted
+ *
+ * userspace value map to register value
+ *
+ * userspace value     0xc0                         0
+ *                     ------------------------------
+ * register value      0x3c(0dB)       0xf0(-90dB)0xfc
+ */
+static int dac_put_volsw(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int reg;
+       int l;
+       int r;
+
+       l = ucontrol->value.integer.value[0];
+       r = ucontrol->value.integer.value[1];
+
+       /* make sure userspace volume fall in (0, 0xfc-0x3c) */
+       l = clamp(l, 0, 0xfc - 0x3c);
+       r = clamp(r, 0, 0xfc - 0x3c);
+
+       /* invert it, get the value can be set to register */
+       l = 0xfc - l;
+       r = 0xfc - r;
+
+       /* shift to get the register value */
+       reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
+               r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+       snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
+
+/* tlv for mic gain, 0db 20db 30db 40db */
+static const unsigned int mic_gain_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+       1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
+};
+
+/* tlv for hp volume, -51.5db to 12.0db, step .5db */
+static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+
+static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
+       /* SOC_DOUBLE_S8_TLV with invert */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "PCM Playback Volume",
+               .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                       SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = dac_info_volsw,
+               .get = dac_get_volsw,
+               .put = dac_put_volsw,
+       },
+
+       SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
+       SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)",
+                       SGTL5000_CHIP_ANA_ADC_CTRL,
+                       8, 2, 0, capture_6db_attenuate),
+       SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
+
+       SOC_DOUBLE_TLV("Headphone Playback Volume",
+                       SGTL5000_CHIP_ANA_HP_CTRL,
+                       0, 8,
+                       0x7f, 1,
+                       headphone_volume),
+       SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL,
+                       5, 1, 0),
+
+       SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
+                       0, 4, 0, mic_gain_tlv),
+};
+
+/* mute the codec used by alsa core */
+static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+                       adcdac_ctrl, mute ? adcdac_ctrl : 0);
+
+       return 0;
+}
+
+/* set codec format */
+static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       u16 i2sctl = 0;
+
+       sgtl5000->master = 0;
+       /*
+        * i2s clock and frame master setting.
+        * ONLY support:
+        *  - clock and frame slave,
+        *  - clock and frame master
+        */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               i2sctl |= SGTL5000_I2S_MASTER;
+               sgtl5000->master = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* setting i2s data format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               i2sctl |= SGTL5000_I2S_MODE_PCM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               i2sctl |= SGTL5000_I2S_MODE_PCM;
+               i2sctl |= SGTL5000_I2S_LRALIGN;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               i2sctl |= SGTL5000_I2S_MODE_RJ;
+               i2sctl |= SGTL5000_I2S_LRPOL;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+               i2sctl |= SGTL5000_I2S_LRALIGN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+       /* Clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               i2sctl |= SGTL5000_I2S_SCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+
+       return 0;
+}
+
+/* set codec sysclk */
+static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       switch (clk_id) {
+       case SGTL5000_SYSCLK:
+               sgtl5000->sysclk = freq;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * set clock according to i2s frame clock,
+ * sgtl5000 provide 2 clock sources.
+ * 1. sys_mclk. sample freq can only configure to
+ *     1/256, 1/384, 1/512 of sys_mclk.
+ * 2. pll. can derive any audio clocks.
+ *
+ * clock setting rules:
+ * 1. in slave mode, only sys_mclk can use.
+ * 2. as constraint by sys_mclk, sample freq should
+ *     set to 32k, 44.1k and above.
+ * 3. using sys_mclk prefer to pll to save power.
+ */
+static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       int clk_ctl = 0;
+       int sys_fs;     /* sample freq */
+
+       /*
+        * sample freq should be divided by frame clock,
+        * if frame clock lower than 44.1khz, sample feq should set to
+        * 32khz or 44.1khz.
+        */
+       switch (frame_rate) {
+       case 8000:
+       case 16000:
+               sys_fs = 32000;
+               break;
+       case 11025:
+       case 22050:
+               sys_fs = 44100;
+               break;
+       default:
+               sys_fs = frame_rate;
+               break;
+       }
+
+       /* set divided factor of frame clock */
+       switch (sys_fs / frame_rate) {
+       case 4:
+               clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT;
+               break;
+       case 2:
+               clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT;
+               break;
+       case 1:
+               clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set the sys_fs according to frame rate */
+       switch (sys_fs) {
+       case 32000:
+               clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       case 44100:
+               clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       case 48000:
+               clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       case 96000:
+               clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       default:
+               dev_err(codec->dev, "frame rate %d not supported\n",
+                       frame_rate);
+               return -EINVAL;
+       }
+
+       /*
+        * calculate the divider of mclk/sample_freq,
+        * factor of freq =96k can only be 256, since mclk in range (12m,27m)
+        */
+       switch (sgtl5000->sysclk / sys_fs) {
+       case 256:
+               clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
+                       SGTL5000_MCLK_FREQ_SHIFT;
+               break;
+       case 384:
+               clk_ctl |= SGTL5000_MCLK_FREQ_384FS <<
+                       SGTL5000_MCLK_FREQ_SHIFT;
+               break;
+       case 512:
+               clk_ctl |= SGTL5000_MCLK_FREQ_512FS <<
+                       SGTL5000_MCLK_FREQ_SHIFT;
+               break;
+       default:
+               /* if mclk not satisify the divider, use pll */
+               if (sgtl5000->master) {
+                       clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
+                               SGTL5000_MCLK_FREQ_SHIFT;
+               } else {
+                       dev_err(codec->dev,
+                               "PLL not supported in slave mode\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* if using pll, please check manual 6.4.2 for detail */
+       if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+               u64 out, t;
+               int div2;
+               int pll_ctl;
+               unsigned int in, int_div, frac_div;
+
+               if (sgtl5000->sysclk > 17000000) {
+                       div2 = 1;
+                       in = sgtl5000->sysclk / 2;
+               } else {
+                       div2 = 0;
+                       in = sgtl5000->sysclk;
+               }
+               if (sys_fs == 44100)
+                       out = 180633600;
+               else
+                       out = 196608000;
+               t = do_div(out, in);
+               int_div = out;
+               t *= 2048;
+               do_div(t, in);
+               frac_div = t;
+               pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
+                   frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
+
+               snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+               if (div2)
+                       snd_soc_update_bits(codec,
+                               SGTL5000_CHIP_CLK_TOP_CTRL,
+                               SGTL5000_INPUT_FREQ_DIV2,
+                               SGTL5000_INPUT_FREQ_DIV2);
+               else
+                       snd_soc_update_bits(codec,
+                               SGTL5000_CHIP_CLK_TOP_CTRL,
+                               SGTL5000_INPUT_FREQ_DIV2,
+                               0);
+
+               /* power up pll */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
+                       SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+       } else {
+               /* power down pll */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
+                       0);
+       }
+
+       /* if using pll, clk_ctrl must be set after pll power up */
+       snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+
+       return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ * input: params_rate, params_fmt
+ */
+static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       int channels = params_channels(params);
+       int i2s_ctl = 0;
+       int stereo;
+       int ret;
+
+       /* sysclk should already set */
+       if (!sgtl5000->sysclk) {
+               dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+               return -EFAULT;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               stereo = SGTL5000_DAC_STEREO;
+       else
+               stereo = SGTL5000_ADC_STEREO;
+
+       /* set mono to save power */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo,
+                       channels == 1 ? 0 : stereo);
+
+       /* set codec clock base on lrclk */
+       ret = sgtl5000_set_clock(codec, params_rate(params));
+       if (ret)
+               return ret;
+
+       /* set i2s data format */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+                       return -EINVAL;
+               i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+                       return -EINVAL;
+               i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl, i2s_ctl);
+
+       return 0;
+}
+
+static int ldo_regulator_is_enabled(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+
+       return ldo->enabled;
+}
+
+static int ldo_regulator_enable(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+       struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
+       int reg;
+
+       if (ldo_regulator_is_enabled(dev))
+               return 0;
+
+       /* set regulator value firstly */
+       reg = (1600 - ldo->voltage / 1000) / 50;
+       reg = clamp(reg, 0x0, 0xf);
+
+       /* amend the voltage value, unit: uV */
+       ldo->voltage = (1600 - reg * 50) * 1000;
+
+       /* set voltage to register */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
+                               (0x1 << 4) - 1, reg);
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINEREG_D_POWERUP,
+                               SGTL5000_LINEREG_D_POWERUP);
+
+       /* when internal ldo enabled, simple digital power can be disabled */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP,
+                               0);
+
+       ldo->enabled = 1;
+       return 0;
+}
+
+static int ldo_regulator_disable(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+       struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINEREG_D_POWERUP,
+                               0);
+
+       /* clear voltage info */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
+                               (0x1 << 4) - 1, 0);
+
+       ldo->enabled = 0;
+
+       return 0;
+}
+
+static int ldo_regulator_get_voltage(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+
+       return ldo->voltage;
+}
+
+static struct regulator_ops ldo_regulator_ops = {
+       .is_enabled = ldo_regulator_is_enabled,
+       .enable = ldo_regulator_enable,
+       .disable = ldo_regulator_disable,
+       .get_voltage = ldo_regulator_get_voltage,
+};
+
+static int ldo_regulator_register(struct snd_soc_codec *codec,
+                               struct regulator_init_data *init_data,
+                               int voltage)
+{
+       struct ldo_regulator *ldo;
+
+       ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL);
+
+       if (!ldo) {
+               dev_err(codec->dev, "failed to allocate ldo_regulator\n");
+               return -ENOMEM;
+       }
+
+       ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL);
+       if (!ldo->desc.name) {
+               kfree(ldo);
+               dev_err(codec->dev, "failed to allocate decs name memory\n");
+               return -ENOMEM;
+       }
+
+       ldo->desc.type  = REGULATOR_VOLTAGE;
+       ldo->desc.owner = THIS_MODULE;
+       ldo->desc.ops   = &ldo_regulator_ops;
+       ldo->desc.n_voltages = 1;
+
+       ldo->codec_data = codec;
+       ldo->voltage = voltage;
+
+       ldo->dev = regulator_register(&ldo->desc, codec->dev,
+                                         init_data, ldo);
+       if (IS_ERR(ldo->dev)) {
+               int ret = PTR_ERR(ldo->dev);
+
+               dev_err(codec->dev, "failed to register regulator\n");
+               kfree(ldo->desc.name);
+               kfree(ldo);
+
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ldo_regulator_remove(struct snd_soc_codec *codec)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       struct ldo_regulator *ldo = sgtl5000->ldo;
+
+       if (!ldo)
+               return 0;
+
+       regulator_unregister(ldo->dev);
+       kfree(ldo->desc.name);
+       kfree(ldo);
+
+       return 0;
+}
+
+/*
+ * set dac bias
+ * common state changes:
+ * startup:
+ * off --> standby --> prepare --> on
+ * standby --> prepare --> on
+ *
+ * stop:
+ * on --> prepare --> standby
+ */
+static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       int ret;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(
+                                               ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+                       if (ret)
+                               return ret;
+                       udelay(10);
+               }
+
+               break;
+       case SND_SOC_BIAS_OFF:
+               regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                       SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops sgtl5000_ops = {
+       .hw_params = sgtl5000_pcm_hw_params,
+       .digital_mute = sgtl5000_digital_mute,
+       .set_fmt = sgtl5000_set_dai_fmt,
+       .set_sysclk = sgtl5000_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver sgtl5000_dai = {
+       .name = "sgtl5000",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               /*
+                * only support 8~48K + 96K,
+                * TODO modify hw_param to support more
+                */
+               .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+               .formats = SGTL5000_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+               .formats = SGTL5000_FORMATS,
+       },
+       .ops = &sgtl5000_ops,
+       .symmetric_rates = 1,
+};
+
+static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
+                                       unsigned int reg)
+{
+       switch (reg) {
+       case SGTL5000_CHIP_ID:
+       case SGTL5000_CHIP_ADCDAC_CTRL:
+       case SGTL5000_CHIP_ANA_STATUS:
+               return 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+/*
+ * restore all sgtl5000 registers,
+ * since a big hole between dap and regular registers,
+ * we will restore them respectively.
+ */
+static int sgtl5000_restore_regs(struct snd_soc_codec *codec)
+{
+       u16 *cache = codec->reg_cache;
+       int i;
+       int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1;
+
+       /* restore regular registers */
+       for (i = 0; i < regular_regs; i++) {
+               int reg = i << 1;
+
+               /* this regs depends on the others */
+               if (reg == SGTL5000_CHIP_ANA_POWER ||
+                       reg == SGTL5000_CHIP_CLK_CTRL ||
+                       reg == SGTL5000_CHIP_LINREG_CTRL ||
+                       reg == SGTL5000_CHIP_LINE_OUT_CTRL ||
+                       reg == SGTL5000_CHIP_CLK_CTRL)
+                       continue;
+
+               snd_soc_write(codec, reg, cache[i]);
+       }
+
+       /* restore dap registers */
+       for (i = SGTL5000_DAP_REG_OFFSET >> 1;
+                       i < SGTL5000_MAX_REG_OFFSET >> 1; i++) {
+               int reg = i << 1;
+
+               snd_soc_write(codec, reg, cache[i]);
+       }
+
+       /*
+        * restore power and other regs according
+        * to set_power() and set_clock()
+        */
+       snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL,
+                       cache[SGTL5000_CHIP_LINREG_CTRL >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER,
+                       cache[SGTL5000_CHIP_ANA_POWER >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL,
+                       cache[SGTL5000_CHIP_CLK_CTRL >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL,
+                       cache[SGTL5000_CHIP_REF_CTRL >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+                       cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]);
+       return 0;
+}
+
+static int sgtl5000_resume(struct snd_soc_codec *codec)
+{
+       /* Bring the codec back up to standby to enable regulators */
+       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Restore registers by cached in memory */
+       sgtl5000_restore_regs(codec);
+       return 0;
+}
+#else
+#define sgtl5000_suspend NULL
+#define sgtl5000_resume  NULL
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * sgtl5000 has 3 internal power supplies:
+ * 1. VAG, normally set to vdda/2
+ * 2. chargepump, set to different value
+ *     according to voltage of vdda and vddio
+ * 3. line out VAG, normally set to vddio/2
+ *
+ * and should be set according to:
+ * 1. vddd provided by external or not
+ * 2. vdda and vddio voltage value. > 3.1v or not
+ * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
+ */
+static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
+{
+       int vddd;
+       int vdda;
+       int vddio;
+       u16 ana_pwr;
+       u16 lreg_ctrl;
+       int vag;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       vdda  = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
+       vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
+       vddd  = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+
+       vdda  = vdda / 1000;
+       vddio = vddio / 1000;
+       vddd  = vddd / 1000;
+
+       if (vdda <= 0 || vddio <= 0 || vddd < 0) {
+               dev_err(codec->dev, "regulator voltage not set correctly\n");
+
+               return -EINVAL;
+       }
+
+       /* according to datasheet, maximum voltage of supplies */
+       if (vdda > 3600 || vddio > 3600 || vddd > 1980) {
+               dev_err(codec->dev,
+                       "exceed max voltage vdda %dmv vddio %dma vddd %dma\n",
+                       vdda, vddio, vddd);
+
+               return -EINVAL;
+       }
+
+       /* reset value */
+       ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+       ana_pwr |= SGTL5000_DAC_STEREO |
+                       SGTL5000_ADC_STEREO |
+                       SGTL5000_REFTOP_POWERUP;
+       lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL);
+
+       if (vddio < 3100 && vdda < 3100) {
+               /* enable internal oscillator used for charge pump */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL,
+                                       SGTL5000_INT_OSC_EN,
+                                       SGTL5000_INT_OSC_EN);
+               /* Enable VDDC charge pump */
+               ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
+       } else if (vddio >= 3100 && vdda >= 3100) {
+               /*
+                * if vddio and vddd > 3.1v,
+                * charge pump should be clean before set ana_pwr
+                */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_VDDC_CHRGPMP_POWERUP, 0);
+
+               /* VDDC use VDDIO rail */
+               lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
+               lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
+                           SGTL5000_VDDC_MAN_ASSN_SHIFT;
+       }
+
+       snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
+
+       snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+
+       /* set voltage to register */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
+                               (0x1 << 4) - 1, 0x8);
+
+       /*
+        * if vddd linear reg has been enabled,
+        * simple digital supply should be clear to get
+        * proper VDDD voltage.
+        */
+       if (ana_pwr & SGTL5000_LINEREG_D_POWERUP)
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP,
+                               0);
+       else
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP |
+                               SGTL5000_STARTUP_POWERUP,
+                               0);
+
+       /*
+        * set ADC/DAC VAG to vdda / 2,
+        * should stay in range (0.8v, 1.575v)
+        */
+       vag = vdda / 2;
+       if (vag <= SGTL5000_ANA_GND_BASE)
+               vag = 0;
+       else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP *
+                (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT))
+               vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT;
+       else
+               vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+                       vag << SGTL5000_ANA_GND_SHIFT,
+                       vag << SGTL5000_ANA_GND_SHIFT);
+
+       /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
+       vag = vddio / 2;
+       if (vag <= SGTL5000_LINE_OUT_GND_BASE)
+               vag = 0;
+       else if (vag >= SGTL5000_LINE_OUT_GND_BASE +
+               SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX)
+               vag = SGTL5000_LINE_OUT_GND_MAX;
+       else
+               vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+                   SGTL5000_LINE_OUT_GND_STP;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+                       vag << SGTL5000_LINE_OUT_GND_SHIFT |
+                       SGTL5000_LINE_OUT_CURRENT_360u <<
+                               SGTL5000_LINE_OUT_CURRENT_SHIFT,
+                       vag << SGTL5000_LINE_OUT_GND_SHIFT |
+                       SGTL5000_LINE_OUT_CURRENT_360u <<
+                               SGTL5000_LINE_OUT_CURRENT_SHIFT);
+
+       return 0;
+}
+
+static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
+{
+       u16 reg;
+       int ret;
+       int rev;
+       int i;
+       int external_vddd = 0;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++)
+               sgtl5000->supplies[i].supply = supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       if (!ret)
+               external_vddd = 1;
+       else {
+               /* set internal ldo to 1.2v */
+               int voltage = LDO_VOLTAGE;
+
+               ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
+               if (ret) {
+                       dev_err(codec->dev,
+                       "Failed to register vddd internal supplies: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+               ret = regulator_bulk_get(codec->dev,
+                               ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+
+               if (ret) {
+                       ldo_regulator_remove(codec);
+                       dev_err(codec->dev,
+                               "Failed to request supplies: %d\n", ret);
+
+                       return ret;
+               }
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+       if (ret)
+               goto err_regulator_free;
+
+       /* wait for all power rails bring up */
+       udelay(10);
+
+       /* read chip information */
+       reg = snd_soc_read(codec, SGTL5000_CHIP_ID);
+       if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+           SGTL5000_PARTID_PART_ID) {
+               dev_err(codec->dev,
+                       "Device with ID register %x is not a sgtl5000\n", reg);
+               ret = -ENODEV;
+               goto err_regulator_disable;
+       }
+
+       rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+       dev_info(codec->dev, "sgtl5000 revision %d\n", rev);
+
+       /*
+        * workaround for revision 0x11 and later,
+        * roll back to use internal LDO
+        */
+       if (external_vddd && rev >= 0x11) {
+               int voltage = LDO_VOLTAGE;
+               /* disable all regulator first */
+               regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+               /* free VDDD regulator */
+               regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+
+               ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
+               if (ret)
+                       return ret;
+
+               sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+               ret = regulator_bulk_get(codec->dev,
+                               ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+               if (ret) {
+                       ldo_regulator_remove(codec);
+                       dev_err(codec->dev,
+                               "Failed to request supplies: %d\n", ret);
+
+                       return ret;
+               }
+
+               ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+               if (ret)
+                       goto err_regulator_free;
+
+               /* wait for all power rails bring up */
+               udelay(10);
+       }
+
+       return 0;
+
+err_regulator_disable:
+       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+err_regulator_free:
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       if (external_vddd)
+               ldo_regulator_remove(codec);
+       return ret;
+
+}
+
+static int sgtl5000_probe(struct snd_soc_codec *codec)
+{
+       int ret;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       /* setup i2c data ops */
+       ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       ret = sgtl5000_enable_regulators(codec);
+       if (ret)
+               return ret;
+
+       /* power up sgtl5000 */
+       ret = sgtl5000_set_power_regs(codec);
+       if (ret)
+               goto err;
+
+       /* enable small pop, introduce 400ms delay in turning off */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+                               SGTL5000_SMALL_POP,
+                               SGTL5000_SMALL_POP);
+
+       /* disable short cut detector */
+       snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
+
+       /*
+        * set i2s as default input of sound switch
+        * TODO: add sound switch to control and dapm widge.
+        */
+       snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL,
+                       SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT);
+       snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER,
+                       SGTL5000_ADC_EN | SGTL5000_DAC_EN);
+
+       /* enable dac volume ramp by default */
+       snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+                       SGTL5000_DAC_VOL_RAMP_EN |
+                       SGTL5000_DAC_MUTE_RIGHT |
+                       SGTL5000_DAC_MUTE_LEFT);
+
+       snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+
+       snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
+                       SGTL5000_HP_ZCD_EN |
+                       SGTL5000_ADC_ZCD_EN);
+
+       snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0);
+
+       /*
+        * disable DAP
+        * TODO:
+        * Enable DAP in kcontrol and dapm.
+        */
+       snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+
+       /* leading to standby state */
+       ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       if (ret)
+               goto err;
+
+       snd_soc_add_controls(codec, sgtl5000_snd_controls,
+                            ARRAY_SIZE(sgtl5000_snd_controls));
+
+       snd_soc_dapm_new_controls(&codec->dapm, sgtl5000_dapm_widgets,
+                                 ARRAY_SIZE(sgtl5000_dapm_widgets));
+
+       snd_soc_dapm_add_routes(&codec->dapm, audio_map,
+                               ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_new_widgets(&codec->dapm);
+
+       return 0;
+
+err:
+       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       ldo_regulator_remove(codec);
+
+       return ret;
+}
+
+static int sgtl5000_remove(struct snd_soc_codec *codec)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       ldo_regulator_remove(codec);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver sgtl5000_driver = {
+       .probe = sgtl5000_probe,
+       .remove = sgtl5000_remove,
+       .suspend = sgtl5000_suspend,
+       .resume = sgtl5000_resume,
+       .set_bias_level = sgtl5000_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(sgtl5000_regs),
+       .reg_word_size = sizeof(u16),
+       .reg_cache_step = 2,
+       .reg_cache_default = sgtl5000_regs,
+       .volatile_register = sgtl5000_volatile_register,
+};
+
+static __devinit int sgtl5000_i2c_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct sgtl5000_priv *sgtl5000;
+       int ret;
+
+       sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL);
+       if (!sgtl5000)
+               return -ENOMEM;
+
+       /*
+        * copy DAP default values to default value array.
+        * sgtl5000 register space has a big hole, merge it
+        * at init phase makes life easy.
+        * FIXME: should we drop 'const' of sgtl5000_regs?
+        */
+       memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)),
+                       sgtl5000_dap_regs,
+                       SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET);
+
+       i2c_set_clientdata(client, sgtl5000);
+
+       ret = snd_soc_register_codec(&client->dev,
+                       &sgtl5000_driver, &sgtl5000_dai, 1);
+       if (ret) {
+               dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+               kfree(sgtl5000);
+               return ret;
+       }
+
+       return 0;
+}
+
+static __devexit int sgtl5000_i2c_remove(struct i2c_client *client)
+{
+       struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_codec(&client->dev);
+
+       kfree(sgtl5000);
+       return 0;
+}
+
+static const struct i2c_device_id sgtl5000_id[] = {
+       {"sgtl5000", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
+
+static struct i2c_driver sgtl5000_i2c_driver = {
+       .driver = {
+                  .name = "sgtl5000",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = sgtl5000_i2c_probe,
+       .remove = __devexit_p(sgtl5000_i2c_remove),
+       .id_table = sgtl5000_id,
+};
+
+static int __init sgtl5000_modinit(void)
+{
+       return i2c_add_driver(&sgtl5000_i2c_driver);
+}
+module_init(sgtl5000_modinit);
+
+static void __exit sgtl5000_exit(void)
+{
+       i2c_del_driver(&sgtl5000_i2c_driver);
+}
+module_exit(sgtl5000_exit);
+
+MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Zeng Zhaoming <zhaoming.zeng@freescale.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
new file mode 100644 (file)
index 0000000..eec3ab3
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * sgtl5000.h - SGTL5000 audio codec interface
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SGTL5000_H
+#define _SGTL5000_H
+
+/*
+ * Register values.
+ */
+#define SGTL5000_CHIP_ID                       0x0000
+#define SGTL5000_CHIP_DIG_POWER                        0x0002
+#define SGTL5000_CHIP_CLK_CTRL                 0x0004
+#define SGTL5000_CHIP_I2S_CTRL                 0x0006
+#define SGTL5000_CHIP_SSS_CTRL                 0x000a
+#define SGTL5000_CHIP_ADCDAC_CTRL              0x000e
+#define SGTL5000_CHIP_DAC_VOL                  0x0010
+#define SGTL5000_CHIP_PAD_STRENGTH             0x0014
+#define SGTL5000_CHIP_ANA_ADC_CTRL             0x0020
+#define SGTL5000_CHIP_ANA_HP_CTRL              0x0022
+#define SGTL5000_CHIP_ANA_CTRL                 0x0024
+#define SGTL5000_CHIP_LINREG_CTRL              0x0026
+#define SGTL5000_CHIP_REF_CTRL                 0x0028
+#define SGTL5000_CHIP_MIC_CTRL                 0x002a
+#define SGTL5000_CHIP_LINE_OUT_CTRL            0x002c
+#define SGTL5000_CHIP_LINE_OUT_VOL             0x002e
+#define SGTL5000_CHIP_ANA_POWER                        0x0030
+#define SGTL5000_CHIP_PLL_CTRL                 0x0032
+#define SGTL5000_CHIP_CLK_TOP_CTRL             0x0034
+#define SGTL5000_CHIP_ANA_STATUS               0x0036
+#define SGTL5000_CHIP_SHORT_CTRL               0x003c
+#define SGTL5000_CHIP_ANA_TEST2                        0x003a
+#define SGTL5000_DAP_CTRL                      0x0100
+#define SGTL5000_DAP_PEQ                       0x0102
+#define SGTL5000_DAP_BASS_ENHANCE              0x0104
+#define SGTL5000_DAP_BASS_ENHANCE_CTRL         0x0106
+#define SGTL5000_DAP_AUDIO_EQ                  0x0108
+#define SGTL5000_DAP_SURROUND                  0x010a
+#define SGTL5000_DAP_FLT_COEF_ACCESS           0x010c
+#define SGTL5000_DAP_COEF_WR_B0_MSB            0x010e
+#define SGTL5000_DAP_COEF_WR_B0_LSB            0x0110
+#define SGTL5000_DAP_EQ_BASS_BAND0             0x0116
+#define SGTL5000_DAP_EQ_BASS_BAND1             0x0118
+#define SGTL5000_DAP_EQ_BASS_BAND2             0x011a
+#define SGTL5000_DAP_EQ_BASS_BAND3             0x011c
+#define SGTL5000_DAP_EQ_BASS_BAND4             0x011e
+#define SGTL5000_DAP_MAIN_CHAN                 0x0120
+#define SGTL5000_DAP_MIX_CHAN                  0x0122
+#define SGTL5000_DAP_AVC_CTRL                  0x0124
+#define SGTL5000_DAP_AVC_THRESHOLD             0x0126
+#define SGTL5000_DAP_AVC_ATTACK                        0x0128
+#define SGTL5000_DAP_AVC_DECAY                 0x012a
+#define SGTL5000_DAP_COEF_WR_B1_MSB            0x012c
+#define SGTL5000_DAP_COEF_WR_B1_LSB            0x012e
+#define SGTL5000_DAP_COEF_WR_B2_MSB            0x0130
+#define SGTL5000_DAP_COEF_WR_B2_LSB            0x0132
+#define SGTL5000_DAP_COEF_WR_A1_MSB            0x0134
+#define SGTL5000_DAP_COEF_WR_A1_LSB            0x0136
+#define SGTL5000_DAP_COEF_WR_A2_MSB            0x0138
+#define SGTL5000_DAP_COEF_WR_A2_LSB            0x013a
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * SGTL5000_CHIP_ID
+ */
+#define SGTL5000_PARTID_MASK                   0xff00
+#define SGTL5000_PARTID_SHIFT                  8
+#define SGTL5000_PARTID_WIDTH                  8
+#define SGTL5000_PARTID_PART_ID                        0xa0
+#define SGTL5000_REVID_MASK                    0x00ff
+#define SGTL5000_REVID_SHIFT                   0
+#define SGTL5000_REVID_WIDTH                   8
+
+/*
+ * SGTL5000_CHIP_DIG_POWER
+ */
+#define SGTL5000_ADC_EN                                0x0040
+#define SGTL5000_DAC_EN                                0x0020
+#define SGTL5000_DAP_POWERUP                   0x0010
+#define SGTL5000_I2S_OUT_POWERUP               0x0002
+#define SGTL5000_I2S_IN_POWERUP                        0x0001
+
+/*
+ * SGTL5000_CHIP_CLK_CTRL
+ */
+#define SGTL5000_RATE_MODE_MASK                        0x0030
+#define SGTL5000_RATE_MODE_SHIFT               4
+#define SGTL5000_RATE_MODE_WIDTH               2
+#define SGTL5000_RATE_MODE_DIV_1               0
+#define SGTL5000_RATE_MODE_DIV_2               1
+#define SGTL5000_RATE_MODE_DIV_4               2
+#define SGTL5000_RATE_MODE_DIV_6               3
+#define SGTL5000_SYS_FS_MASK                   0x000c
+#define SGTL5000_SYS_FS_SHIFT                  2
+#define SGTL5000_SYS_FS_WIDTH                  2
+#define SGTL5000_SYS_FS_32k                    0x0
+#define SGTL5000_SYS_FS_44_1k                  0x1
+#define SGTL5000_SYS_FS_48k                    0x2
+#define SGTL5000_SYS_FS_96k                    0x3
+#define SGTL5000_MCLK_FREQ_MASK                        0x0003
+#define SGTL5000_MCLK_FREQ_SHIFT               0
+#define SGTL5000_MCLK_FREQ_WIDTH               2
+#define SGTL5000_MCLK_FREQ_256FS               0x0
+#define SGTL5000_MCLK_FREQ_384FS               0x1
+#define SGTL5000_MCLK_FREQ_512FS               0x2
+#define SGTL5000_MCLK_FREQ_PLL                 0x3
+
+/*
+ * SGTL5000_CHIP_I2S_CTRL
+ */
+#define SGTL5000_I2S_SCLKFREQ_MASK             0x0100
+#define SGTL5000_I2S_SCLKFREQ_SHIFT            8
+#define SGTL5000_I2S_SCLKFREQ_WIDTH            1
+#define SGTL5000_I2S_SCLKFREQ_64FS             0x0
+#define SGTL5000_I2S_SCLKFREQ_32FS             0x1     /* Not for RJ mode */
+#define SGTL5000_I2S_MASTER                    0x0080
+#define SGTL5000_I2S_SCLK_INV                  0x0040
+#define SGTL5000_I2S_DLEN_MASK                 0x0030
+#define SGTL5000_I2S_DLEN_SHIFT                        4
+#define SGTL5000_I2S_DLEN_WIDTH                        2
+#define SGTL5000_I2S_DLEN_32                   0x0
+#define SGTL5000_I2S_DLEN_24                   0x1
+#define SGTL5000_I2S_DLEN_20                   0x2
+#define SGTL5000_I2S_DLEN_16                   0x3
+#define SGTL5000_I2S_MODE_MASK                 0x000c
+#define SGTL5000_I2S_MODE_SHIFT                        2
+#define SGTL5000_I2S_MODE_WIDTH                        2
+#define SGTL5000_I2S_MODE_I2S_LJ               0x0
+#define SGTL5000_I2S_MODE_RJ                   0x1
+#define SGTL5000_I2S_MODE_PCM                  0x2
+#define SGTL5000_I2S_LRALIGN                   0x0002
+#define SGTL5000_I2S_LRPOL                     0x0001  /* set for which mode */
+
+/*
+ * SGTL5000_CHIP_SSS_CTRL
+ */
+#define SGTL5000_DAP_MIX_LRSWAP                        0x4000
+#define SGTL5000_DAP_LRSWAP                    0x2000
+#define SGTL5000_DAC_LRSWAP                    0x1000
+#define SGTL5000_I2S_OUT_LRSWAP                        0x0400
+#define SGTL5000_DAP_MIX_SEL_MASK              0x0300
+#define SGTL5000_DAP_MIX_SEL_SHIFT             8
+#define SGTL5000_DAP_MIX_SEL_WIDTH             2
+#define SGTL5000_DAP_MIX_SEL_ADC               0x0
+#define SGTL5000_DAP_MIX_SEL_I2S_IN            0x1
+#define SGTL5000_DAP_SEL_MASK                  0x00c0
+#define SGTL5000_DAP_SEL_SHIFT                 6
+#define SGTL5000_DAP_SEL_WIDTH                 2
+#define SGTL5000_DAP_SEL_ADC                   0x0
+#define SGTL5000_DAP_SEL_I2S_IN                        0x1
+#define SGTL5000_DAC_SEL_MASK                  0x0030
+#define SGTL5000_DAC_SEL_SHIFT                 4
+#define SGTL5000_DAC_SEL_WIDTH                 2
+#define SGTL5000_DAC_SEL_ADC                   0x0
+#define SGTL5000_DAC_SEL_I2S_IN                        0x1
+#define SGTL5000_DAC_SEL_DAP                   0x3
+#define SGTL5000_I2S_OUT_SEL_MASK              0x0003
+#define SGTL5000_I2S_OUT_SEL_SHIFT             0
+#define SGTL5000_I2S_OUT_SEL_WIDTH             2
+#define SGTL5000_I2S_OUT_SEL_ADC               0x0
+#define SGTL5000_I2S_OUT_SEL_I2S_IN            0x1
+#define SGTL5000_I2S_OUT_SEL_DAP               0x3
+
+/*
+ * SGTL5000_CHIP_ADCDAC_CTRL
+ */
+#define SGTL5000_VOL_BUSY_DAC_RIGHT            0x2000
+#define SGTL5000_VOL_BUSY_DAC_LEFT             0x1000
+#define SGTL5000_DAC_VOL_RAMP_EN               0x0200
+#define SGTL5000_DAC_VOL_RAMP_EXPO             0x0100
+#define SGTL5000_DAC_MUTE_RIGHT                        0x0008
+#define SGTL5000_DAC_MUTE_LEFT                 0x0004
+#define SGTL5000_ADC_HPF_FREEZE                        0x0002
+#define SGTL5000_ADC_HPF_BYPASS                        0x0001
+
+/*
+ * SGTL5000_CHIP_DAC_VOL
+ */
+#define SGTL5000_DAC_VOL_RIGHT_MASK            0xff00
+#define SGTL5000_DAC_VOL_RIGHT_SHIFT           8
+#define SGTL5000_DAC_VOL_RIGHT_WIDTH           8
+#define SGTL5000_DAC_VOL_LEFT_MASK             0x00ff
+#define SGTL5000_DAC_VOL_LEFT_SHIFT            0
+#define SGTL5000_DAC_VOL_LEFT_WIDTH            8
+
+/*
+ * SGTL5000_CHIP_PAD_STRENGTH
+ */
+#define SGTL5000_PAD_I2S_LRCLK_MASK            0x0300
+#define SGTL5000_PAD_I2S_LRCLK_SHIFT           8
+#define SGTL5000_PAD_I2S_LRCLK_WIDTH           2
+#define SGTL5000_PAD_I2S_SCLK_MASK             0x00c0
+#define SGTL5000_PAD_I2S_SCLK_SHIFT            6
+#define SGTL5000_PAD_I2S_SCLK_WIDTH            2
+#define SGTL5000_PAD_I2S_DOUT_MASK             0x0030
+#define SGTL5000_PAD_I2S_DOUT_SHIFT            4
+#define SGTL5000_PAD_I2S_DOUT_WIDTH            2
+#define SGTL5000_PAD_I2C_SDA_MASK              0x000c
+#define SGTL5000_PAD_I2C_SDA_SHIFT             2
+#define SGTL5000_PAD_I2C_SDA_WIDTH             2
+#define SGTL5000_PAD_I2C_SCL_MASK              0x0003
+#define SGTL5000_PAD_I2C_SCL_SHIFT             0
+#define SGTL5000_PAD_I2C_SCL_WIDTH             2
+
+/*
+ * SGTL5000_CHIP_ANA_ADC_CTRL
+ */
+#define SGTL5000_ADC_VOL_M6DB                  0x0100
+#define SGTL5000_ADC_VOL_RIGHT_MASK            0x00f0
+#define SGTL5000_ADC_VOL_RIGHT_SHIFT           4
+#define SGTL5000_ADC_VOL_RIGHT_WIDTH           4
+#define SGTL5000_ADC_VOL_LEFT_MASK             0x000f
+#define SGTL5000_ADC_VOL_LEFT_SHIFT            0
+#define SGTL5000_ADC_VOL_LEFT_WIDTH            4
+
+/*
+ * SGTL5000_CHIP_ANA_HP_CTRL
+ */
+#define SGTL5000_HP_VOL_RIGHT_MASK             0x7f00
+#define SGTL5000_HP_VOL_RIGHT_SHIFT            8
+#define SGTL5000_HP_VOL_RIGHT_WIDTH            7
+#define SGTL5000_HP_VOL_LEFT_MASK              0x007f
+#define SGTL5000_HP_VOL_LEFT_SHIFT             0
+#define SGTL5000_HP_VOL_LEFT_WIDTH             7
+
+/*
+ * SGTL5000_CHIP_ANA_CTRL
+ */
+#define SGTL5000_LINE_OUT_MUTE                 0x0100
+#define SGTL5000_HP_SEL_MASK                   0x0040
+#define SGTL5000_HP_SEL_SHIFT                  6
+#define SGTL5000_HP_SEL_WIDTH                  1
+#define SGTL5000_HP_SEL_DAC                    0x0
+#define SGTL5000_HP_SEL_LINE_IN                        0x1
+#define SGTL5000_HP_ZCD_EN                     0x0020
+#define SGTL5000_HP_MUTE                       0x0010
+#define SGTL5000_ADC_SEL_MASK                  0x0004
+#define SGTL5000_ADC_SEL_SHIFT                 2
+#define SGTL5000_ADC_SEL_WIDTH                 1
+#define SGTL5000_ADC_SEL_MIC                   0x0
+#define SGTL5000_ADC_SEL_LINE_IN               0x1
+#define SGTL5000_ADC_ZCD_EN                    0x0002
+#define SGTL5000_ADC_MUTE                      0x0001
+
+/*
+ * SGTL5000_CHIP_LINREG_CTRL
+ */
+#define SGTL5000_VDDC_MAN_ASSN_MASK            0x0040
+#define SGTL5000_VDDC_MAN_ASSN_SHIFT           6
+#define SGTL5000_VDDC_MAN_ASSN_WIDTH           1
+#define SGTL5000_VDDC_MAN_ASSN_VDDA            0x0
+#define SGTL5000_VDDC_MAN_ASSN_VDDIO           0x1
+#define SGTL5000_VDDC_ASSN_OVRD                        0x0020
+#define SGTL5000_LINREG_VDDD_MASK              0x000f
+#define SGTL5000_LINREG_VDDD_SHIFT             0
+#define SGTL5000_LINREG_VDDD_WIDTH             4
+
+/*
+ * SGTL5000_CHIP_REF_CTRL
+ */
+#define SGTL5000_ANA_GND_MASK                  0x01f0
+#define SGTL5000_ANA_GND_SHIFT                 4
+#define SGTL5000_ANA_GND_WIDTH                 5
+#define SGTL5000_ANA_GND_BASE                  800     /* mv */
+#define SGTL5000_ANA_GND_STP                   25      /*mv */
+#define SGTL5000_BIAS_CTRL_MASK                        0x000e
+#define SGTL5000_BIAS_CTRL_SHIFT               1
+#define SGTL5000_BIAS_CTRL_WIDTH               3
+#define SGTL5000_SMALL_POP                     0x0001
+
+/*
+ * SGTL5000_CHIP_MIC_CTRL
+ */
+#define SGTL5000_BIAS_R_MASK                   0x0200
+#define SGTL5000_BIAS_R_SHIFT                  8
+#define SGTL5000_BIAS_R_WIDTH                  2
+#define SGTL5000_BIAS_R_off                    0x0
+#define SGTL5000_BIAS_R_2K                     0x1
+#define SGTL5000_BIAS_R_4k                     0x2
+#define SGTL5000_BIAS_R_8k                     0x3
+#define SGTL5000_BIAS_VOLT_MASK                        0x0070
+#define SGTL5000_BIAS_VOLT_SHIFT               4
+#define SGTL5000_BIAS_VOLT_WIDTH               3
+#define SGTL5000_MIC_GAIN_MASK                 0x0003
+#define SGTL5000_MIC_GAIN_SHIFT                        0
+#define SGTL5000_MIC_GAIN_WIDTH                        2
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_CTRL
+ */
+#define SGTL5000_LINE_OUT_CURRENT_MASK         0x0f00
+#define SGTL5000_LINE_OUT_CURRENT_SHIFT                8
+#define SGTL5000_LINE_OUT_CURRENT_WIDTH                4
+#define SGTL5000_LINE_OUT_CURRENT_180u         0x0
+#define SGTL5000_LINE_OUT_CURRENT_270u         0x1
+#define SGTL5000_LINE_OUT_CURRENT_360u         0x3
+#define SGTL5000_LINE_OUT_CURRENT_450u         0x7
+#define SGTL5000_LINE_OUT_CURRENT_540u         0xf
+#define SGTL5000_LINE_OUT_GND_MASK             0x003f
+#define SGTL5000_LINE_OUT_GND_SHIFT            0
+#define SGTL5000_LINE_OUT_GND_WIDTH            6
+#define SGTL5000_LINE_OUT_GND_BASE             800     /* mv */
+#define SGTL5000_LINE_OUT_GND_STP              25
+#define SGTL5000_LINE_OUT_GND_MAX              0x23
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_VOL
+ */
+#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK       0x1f00
+#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT      8
+#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH      5
+#define SGTL5000_LINE_OUT_VOL_LEFT_MASK                0x001f
+#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT       0
+#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH       5
+
+/*
+ * SGTL5000_CHIP_ANA_POWER
+ */
+#define SGTL5000_DAC_STEREO                    0x4000
+#define SGTL5000_LINREG_SIMPLE_POWERUP         0x2000
+#define SGTL5000_STARTUP_POWERUP               0x1000
+#define SGTL5000_VDDC_CHRGPMP_POWERUP          0x0800
+#define SGTL5000_PLL_POWERUP                   0x0400
+#define SGTL5000_LINEREG_D_POWERUP             0x0200
+#define SGTL5000_VCOAMP_POWERUP                        0x0100
+#define SGTL5000_VAG_POWERUP                   0x0080
+#define SGTL5000_ADC_STEREO                    0x0040
+#define SGTL5000_REFTOP_POWERUP                        0x0020
+#define SGTL5000_HP_POWERUP                    0x0010
+#define SGTL5000_DAC_POWERUP                   0x0008
+#define SGTL5000_CAPLESS_HP_POWERUP            0x0004
+#define SGTL5000_ADC_POWERUP                   0x0002
+#define SGTL5000_LINE_OUT_POWERUP              0x0001
+
+/*
+ * SGTL5000_CHIP_PLL_CTRL
+ */
+#define SGTL5000_PLL_INT_DIV_MASK              0xf800
+#define SGTL5000_PLL_INT_DIV_SHIFT             11
+#define SGTL5000_PLL_INT_DIV_WIDTH             5
+#define SGTL5000_PLL_FRAC_DIV_MASK             0x0700
+#define SGTL5000_PLL_FRAC_DIV_SHIFT            0
+#define SGTL5000_PLL_FRAC_DIV_WIDTH            11
+
+/*
+ * SGTL5000_CHIP_CLK_TOP_CTRL
+ */
+#define SGTL5000_INT_OSC_EN                    0x0800
+#define SGTL5000_INPUT_FREQ_DIV2               0x0008
+
+/*
+ * SGTL5000_CHIP_ANA_STATUS
+ */
+#define SGTL5000_HP_LRSHORT                    0x0200
+#define SGTL5000_CAPLESS_SHORT                 0x0100
+#define SGTL5000_PLL_LOCKED                    0x0010
+
+/*
+ * SGTL5000_CHIP_SHORT_CTRL
+ */
+#define SGTL5000_LVLADJR_MASK                  0x7000
+#define SGTL5000_LVLADJR_SHIFT                 12
+#define SGTL5000_LVLADJR_WIDTH                 3
+#define SGTL5000_LVLADJL_MASK                  0x0700
+#define SGTL5000_LVLADJL_SHIFT                 8
+#define SGTL5000_LVLADJL_WIDTH                 3
+#define SGTL5000_LVLADJC_MASK                  0x0070
+#define SGTL5000_LVLADJC_SHIFT                 4
+#define SGTL5000_LVLADJC_WIDTH                 3
+#define SGTL5000_LR_SHORT_MOD_MASK             0x000c
+#define SGTL5000_LR_SHORT_MOD_SHIFT            2
+#define SGTL5000_LR_SHORT_MOD_WIDTH            2
+#define SGTL5000_CM_SHORT_MOD_MASK             0x0003
+#define SGTL5000_CM_SHORT_MOD_SHIFT            0
+#define SGTL5000_CM_SHORT_MOD_WIDTH            2
+
+/*
+ *SGTL5000_CHIP_ANA_TEST2
+ */
+#define SGTL5000_MONO_DAC                      0x1000
+
+/*
+ * SGTL5000_DAP_CTRL
+ */
+#define SGTL5000_DAP_MIX_EN                    0x0010
+#define SGTL5000_DAP_EN                                0x0001
+
+#define SGTL5000_SYSCLK                                0x00
+#define SGTL5000_LRCLK                         0x01
+
+#endif
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
new file mode 100644 (file)
index 0000000..2a30eae
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ *  sn95031.c -  TI sn95031 Codec driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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; version 2 of the License.
+ *
+ *  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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include "sn95031.h"
+
+#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
+#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/* adc helper functions */
+
+/* enables mic bias voltage */
+static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
+       snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
+}
+
+/* Enable/Disable the ADC depending on the argument */
+static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
+{
+       int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
+
+       if (val) {
+               /* Enable and start the ADC */
+               value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
+               value &= (~SN95031_ADC_NO_LOOP);
+       } else {
+               /* Just stop the ADC */
+               value &= (~SN95031_ADC_START);
+       }
+       snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
+}
+
+/*
+ * finds an empty channel for conversion
+ * If the ADC is not enabled then start using 0th channel
+ * itself. Otherwise find an empty channel by looking for a
+ * channel in which the stopbit is set to 1. returns the index
+ * of the first free channel if succeeds or an error code.
+ *
+ * Context: can sleep
+ *
+ */
+static int find_free_channel(struct snd_soc_codec *sn95031_codec)
+{
+       int ret = 0, i, value;
+
+       /* check whether ADC is enabled */
+       value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
+
+       if ((value & SN95031_ADC_ENBL) == 0)
+               return 0;
+
+       /* ADC is already enabled; Looking for an empty channel */
+       for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
+               value = snd_soc_read(sn95031_codec,
+                               SN95031_ADC_CHNL_START_ADDR + i);
+               if (value & SN95031_STOPBIT_MASK) {
+                       ret = i;
+                       break;
+               }
+       }
+       return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
+}
+
+/* Initialize the ADC for reading micbias values. Can sleep. */
+static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
+{
+       int base_addr, chnl_addr;
+       int value;
+       static int channel_index;
+
+       /* Index of the first channel in which the stop bit is set */
+       channel_index = find_free_channel(sn95031_codec);
+       if (channel_index < 0) {
+               pr_err("No free ADC channels");
+               return channel_index;
+       }
+
+       base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;
+
+       if (!(channel_index == 0 || channel_index ==  SN95031_ADC_LOOP_MAX)) {
+               /* Reset stop bit for channels other than 0 and 12 */
+               value = snd_soc_read(sn95031_codec, base_addr);
+               /* Set the stop bit to zero */
+               snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
+               /* Index of the first free channel */
+               base_addr++;
+               channel_index++;
+       }
+
+       /* Since this is the last channel, set the stop bit
+          to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
+       snd_soc_write(sn95031_codec, base_addr,
+                               SN95031_AUDIO_DETECT_CODE | 0x10);
+
+       chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
+       pr_debug("mid_initialize : %x", chnl_addr);
+       configure_adc(sn95031_codec, 1);
+       return chnl_addr;
+}
+
+
+/* reads the ADC registers and gets the mic bias value in mV. */
+static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
+{
+       u16 adc_adr = sn95031_initialize_adc(codec);
+       u16 adc_val1, adc_val2;
+       unsigned int mic_bias;
+
+       sn95031_enable_mic_bias(codec);
+
+       /* Enable the sound card for conversion before reading */
+       snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
+       /* Re-toggle the RRDATARD bit */
+       snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);
+
+       /* Read the higher bits of data */
+       msleep(1000);
+       adc_val1 = snd_soc_read(codec, adc_adr);
+       adc_adr++;
+       adc_val2 = snd_soc_read(codec, adc_adr);
+
+       /* Adding lower two bits to the higher bits */
+       mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
+       mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
+       pr_debug("mic bias = %dmV\n", mic_bias);
+       return mic_bias;
+}
+EXPORT_SYMBOL_GPL(sn95031_get_mic_bias);
+/*end - adc helper functions */
+
+static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
+                       unsigned int reg)
+{
+       u8 value = 0;
+       int ret;
+
+       ret = intel_scu_ipc_ioread8(reg, &value);
+       if (ret)
+               pr_err("read of %x failed, err %d\n", reg, ret);
+       return value;
+
+}
+
+static inline int sn95031_write(struct snd_soc_codec *codec,
+                       unsigned int reg, unsigned int value)
+{
+       int ret;
+
+       ret = intel_scu_ipc_iowrite8(reg, value);
+       if (ret)
+               pr_err("write of %x failed, err %d\n", reg, ret);
+       return ret;
+}
+
+static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
+               enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+                       pr_debug("vaud_bias powering up pll\n");
+                       /* power up the pll */
+                       snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
+                       /* enable pcm 2 */
+                       snd_soc_update_bits(codec, SN95031_PCM2C2,
+                                       BIT(0), BIT(0));
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       pr_debug("vaud_bias power up rail\n");
+                       /* power up the rail */
+                       snd_soc_write(codec, SN95031_VAUD,
+                                       BIT(2)|BIT(1)|BIT(0));
+                       msleep(1);
+               } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+                       /* turn off pcm */
+                       pr_debug("vaud_bias power dn pcm\n");
+                       snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
+                       snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+               }
+               break;
+
+
+       case SND_SOC_BIAS_OFF:
+               pr_debug("vaud_bias _OFF doing rail shutdown\n");
+               snd_soc_write(codec, SN95031_VAUD, BIT(3));
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+               /* power up the rail */
+               snd_soc_write(w->codec, SN95031_VHSP, 0x3D);
+               snd_soc_write(w->codec, SN95031_VHSN, 0x3F);
+               msleep(1);
+       } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+               pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+               snd_soc_write(w->codec, SN95031_VHSP, 0xC4);
+               snd_soc_write(w->codec, SN95031_VHSN, 0x04);
+       }
+       return 0;
+}
+
+static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+               /* power up the rail */
+               snd_soc_write(w->codec, SN95031_VIHF, 0x27);
+               msleep(1);
+       } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+               pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+               snd_soc_write(w->codec, SN95031_VIHF, 0x24);
+       }
+       return 0;
+}
+
+static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ldo = BIT(5)|BIT(4);
+               clk_dir = BIT(0);
+               data_dir = BIT(7);
+       }
+       /* program DMIC LDO, clock and set clock */
+       snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(0), clk_dir);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(7), data_dir);
+       return 0;
+}
+
+static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ldo = BIT(5)|BIT(4);
+               clk_dir = BIT(2);
+               data_dir = BIT(1);
+       }
+       /* program DMIC LDO, clock and set clock */
+       snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(2), clk_dir);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF45, BIT(1), data_dir);
+       return 0;
+}
+
+static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       unsigned int ldo = 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ldo = BIT(7)|BIT(6);
+
+       /* program DMIC LDO */
+       snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo);
+       return 0;
+}
+
+/* mux controls */
+static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" };
+
+static const struct soc_enum sn95031_micl_enum =
+       SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts);
+
+static const struct snd_kcontrol_new sn95031_micl_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_micl_enum);
+
+static const struct soc_enum sn95031_micr_enum =
+       SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts);
+
+static const struct snd_kcontrol_new sn95031_micr_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_micr_enum);
+
+static const char *sn95031_input_texts[] = {   "DMIC1", "DMIC2", "DMIC3",
+                                               "DMIC4", "DMIC5", "DMIC6",
+                                               "ADC Left", "ADC Right" };
+
+static const struct soc_enum sn95031_input1_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input1_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input1_enum);
+
+static const struct soc_enum sn95031_input2_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input2_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input2_enum);
+
+static const struct soc_enum sn95031_input3_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input3_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input3_enum);
+
+static const struct soc_enum sn95031_input4_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input4_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input4_enum);
+
+/* capture path controls */
+
+static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"};
+
+/* 0dB to 30dB in 10dB steps */
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0);
+
+static const struct soc_enum sn95031_micmode1_enum =
+       SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text);
+static const struct soc_enum sn95031_micmode2_enum =
+       SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text);
+
+static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"};
+
+static const struct soc_enum sn95031_dmic12_cfg_enum =
+       SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text);
+static const struct soc_enum sn95031_dmic34_cfg_enum =
+       SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text);
+static const struct soc_enum sn95031_dmic56_cfg_enum =
+       SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text);
+
+static const struct snd_kcontrol_new sn95031_snd_controls[] = {
+       SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum),
+       SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum),
+       SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum),
+       SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum),
+       SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum),
+       SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1,
+                       2, 4, 0, mic_tlv),
+       SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2,
+                       2, 4, 0, mic_tlv),
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = {
+
+       /* all end points mic, hs etc */
+       SND_SOC_DAPM_OUTPUT("HPOUTL"),
+       SND_SOC_DAPM_OUTPUT("HPOUTR"),
+       SND_SOC_DAPM_OUTPUT("EPOUT"),
+       SND_SOC_DAPM_OUTPUT("IHFOUTL"),
+       SND_SOC_DAPM_OUTPUT("IHFOUTR"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+       SND_SOC_DAPM_OUTPUT("VIB1OUT"),
+       SND_SOC_DAPM_OUTPUT("VIB2OUT"),
+
+       SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */
+       SND_SOC_DAPM_INPUT("AMIC2"),
+       SND_SOC_DAPM_INPUT("DMIC1"),
+       SND_SOC_DAPM_INPUT("DMIC2"),
+       SND_SOC_DAPM_INPUT("DMIC3"),
+       SND_SOC_DAPM_INPUT("DMIC4"),
+       SND_SOC_DAPM_INPUT("DMIC5"),
+       SND_SOC_DAPM_INPUT("DMIC6"),
+       SND_SOC_DAPM_INPUT("LINEINL"),
+       SND_SOC_DAPM_INPUT("LINEINR"),
+
+       SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0),
+       SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0),
+
+       SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0,
+                               sn95031_dmic12_event,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0,
+                               sn95031_dmic34_event,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0,
+                               sn95031_dmic56_event,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0,
+                       SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
+                       sn95031_vhs_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
+                       sn95031_vihf_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* playback path driver enables */
+       SND_SOC_DAPM_PGA("Headset Left Playback",
+                       SN95031_DRIVEREN, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Headset Right Playback",
+                       SN95031_DRIVEREN, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Left Playback",
+                       SN95031_DRIVEREN, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Right Playback",
+                       SN95031_DRIVEREN, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Vibra1 Playback",
+                       SN95031_DRIVEREN, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Vibra2 Playback",
+                       SN95031_DRIVEREN, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Earpiece Playback",
+                       SN95031_DRIVEREN, 6, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Lineout Left Playback",
+                       SN95031_LOCTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Lineout Right Playback",
+                       SN95031_LOCTL, 4, 0, NULL, 0),
+
+       /* playback path filter enable */
+       SND_SOC_DAPM_PGA("Headset Left Filter",
+                       SN95031_HSEPRXCTRL, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Headset Right Filter",
+                       SN95031_HSEPRXCTRL, 5, 0,  NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Left Filter",
+                       SN95031_IHFRXCTRL, 0, 0,  NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Right Filter",
+                       SN95031_IHFRXCTRL, 1, 0,  NULL, 0),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("HSDAC Left", "Headset",
+                       SN95031_DACCONFIG, 0, 0),
+       SND_SOC_DAPM_DAC("HSDAC Right", "Headset",
+                       SN95031_DACCONFIG, 1, 0),
+       SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker",
+                       SN95031_DACCONFIG, 2, 0),
+       SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker",
+                       SN95031_DACCONFIG, 3, 0),
+       SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1",
+                       SN95031_VIB1C5, 1, 0),
+       SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2",
+                       SN95031_VIB2C5, 1, 0),
+
+       /* capture widgets */
+       SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1,
+                               7, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2,
+                               7, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0),
+
+       /* ADC have null stream as they will be turned ON by TX path */
+       SND_SOC_DAPM_ADC("ADC Left", NULL,
+                       SN95031_ADCCONFIG, 0, 0),
+       SND_SOC_DAPM_ADC("ADC Right", NULL,
+                       SN95031_ADCCONFIG, 2, 0),
+
+       SND_SOC_DAPM_MUX("Mic_InputL Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control),
+       SND_SOC_DAPM_MUX("Mic_InputR Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control),
+
+       SND_SOC_DAPM_MUX("Txpath1 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control),
+       SND_SOC_DAPM_MUX("Txpath2 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control),
+       SND_SOC_DAPM_MUX("Txpath3 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control),
+       SND_SOC_DAPM_MUX("Txpath4 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control),
+
+};
+
+static const struct snd_soc_dapm_route sn95031_audio_map[] = {
+       /* headset and earpiece map */
+       { "HPOUTL", NULL, "Headset Rail"},
+       { "HPOUTR", NULL, "Headset Rail"},
+       { "HPOUTL", NULL, "Headset Left Playback" },
+       { "HPOUTR", NULL, "Headset Right Playback" },
+       { "EPOUT", NULL, "Earpiece Playback" },
+       { "Headset Left Playback", NULL, "Headset Left Filter"},
+       { "Headset Right Playback", NULL, "Headset Right Filter"},
+       { "Earpiece Playback", NULL, "Headset Left Filter"},
+       { "Headset Left Filter", NULL, "HSDAC Left"},
+       { "Headset Right Filter", NULL, "HSDAC Right"},
+
+       /* speaker map */
+       { "IHFOUTL", NULL, "Speaker Rail"},
+       { "IHFOUTR", NULL, "Speaker Rail"},
+       { "IHFOUTL", "NULL", "Speaker Left Playback"},
+       { "IHFOUTR", "NULL", "Speaker Right Playback"},
+       { "Speaker Left Playback", NULL, "Speaker Left Filter"},
+       { "Speaker Right Playback", NULL, "Speaker Right Filter"},
+       { "Speaker Left Filter", NULL, "IHFDAC Left"},
+       { "Speaker Right Filter", NULL, "IHFDAC Right"},
+
+       /* vibra map */
+       { "VIB1OUT", NULL, "Vibra1 Playback"},
+       { "Vibra1 Playback", NULL, "Vibra1 DAC"},
+
+       { "VIB2OUT", NULL, "Vibra2 Playback"},
+       { "Vibra2 Playback", NULL, "Vibra2 DAC"},
+
+       /* lineout */
+       { "LINEOUTL", NULL, "Lineout Left Playback"},
+       { "LINEOUTR", NULL, "Lineout Right Playback"},
+       { "Lineout Left Playback", NULL, "Headset Left Filter"},
+       { "Lineout Left Playback", NULL, "Speaker Left Filter"},
+       { "Lineout Left Playback", NULL, "Vibra1 DAC"},
+       { "Lineout Right Playback", NULL, "Headset Right Filter"},
+       { "Lineout Right Playback", NULL, "Speaker Right Filter"},
+       { "Lineout Right Playback", NULL, "Vibra2 DAC"},
+
+       /* Headset (AMIC1) mic */
+       { "AMIC1Bias", NULL, "AMIC1"},
+       { "MIC1 Enable", NULL, "AMIC1Bias"},
+       { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"},
+
+       /* AMIC2 */
+       { "AMIC2Bias", NULL, "AMIC2"},
+       { "MIC2 Enable", NULL, "AMIC2Bias"},
+       { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"},
+
+
+       /* Linein */
+       { "LineIn Enable Left", NULL, "LINEINL"},
+       { "LineIn Enable Right", NULL, "LINEINR"},
+       { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"},
+       { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"},
+
+       /* ADC connection */
+       { "ADC Left", NULL, "Mic_InputL Capture Route"},
+       { "ADC Right", NULL, "Mic_InputR Capture Route"},
+
+       /*DMIC connections */
+       { "DMIC1", NULL, "DMIC12supply"},
+       { "DMIC2", NULL, "DMIC12supply"},
+       { "DMIC3", NULL, "DMIC34supply"},
+       { "DMIC4", NULL, "DMIC34supply"},
+       { "DMIC5", NULL, "DMIC56supply"},
+       { "DMIC6", NULL, "DMIC56supply"},
+
+       { "DMIC12Bias", NULL, "DMIC1"},
+       { "DMIC12Bias", NULL, "DMIC2"},
+       { "DMIC34Bias", NULL, "DMIC3"},
+       { "DMIC34Bias", NULL, "DMIC4"},
+       { "DMIC56Bias", NULL, "DMIC5"},
+       { "DMIC56Bias", NULL, "DMIC6"},
+
+       /*TX path inputs*/
+       { "Txpath1 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath2 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath3 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath4 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath1 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath2 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath3 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath4 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath1 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath2 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath3 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath4 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath1 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath2 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath3 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath4 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath1 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath2 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath3 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath4 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath1 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath2 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath3 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath4 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath1 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath2 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath3 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath4 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath1 Capture Route", "DMIC6", "DMIC6"},
+       { "Txpath2 Capture Route", "DMIC6", "DMIC6"},
+       { "Txpath3 Capture Route", "DMIC6", "DMIC6"},
+       { "Txpath4 Capture Route", "DMIC6", "DMIC6"},
+
+       /* tx path */
+       { "TX1 Enable", NULL, "Txpath1 Capture Route"},
+       { "TX2 Enable", NULL, "Txpath2 Capture Route"},
+       { "TX3 Enable", NULL, "Txpath3 Capture Route"},
+       { "TX4 Enable", NULL, "Txpath4 Capture Route"},
+       { "PCM_Out", NULL, "TX1 Enable"},
+       { "PCM_Out", NULL, "TX2 Enable"},
+       { "PCM_Out", NULL, "TX3 Enable"},
+       { "PCM_Out", NULL, "TX4 Enable"},
+
+};
+
+/* speaker and headset mutes, for audio pops and clicks */
+static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute)
+{
+       snd_soc_update_bits(dai->codec,
+                       SN95031_HSLVOLCTRL, BIT(7), (!mute << 7));
+       snd_soc_update_bits(dai->codec,
+                       SN95031_HSRVOLCTRL, BIT(7), (!mute << 7));
+       return 0;
+}
+
+static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
+{
+       snd_soc_update_bits(dai->codec,
+                       SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7));
+       snd_soc_update_bits(dai->codec,
+                       SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7));
+       return 0;
+}
+
+int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       unsigned int format, rate;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               format = BIT(4)|BIT(5);
+               break;
+
+       case SNDRV_PCM_FORMAT_S24_LE:
+               format = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       snd_soc_update_bits(dai->codec, SN95031_PCM2C2,
+                       BIT(4)|BIT(5), format);
+
+       switch (params_rate(params)) {
+       case 48000:
+               pr_debug("RATE_48000\n");
+               rate = 0;
+               break;
+
+       case 44100:
+               pr_debug("RATE_44100\n");
+               rate = BIT(7);
+               break;
+
+       default:
+               pr_err("ERR rate %d\n", params_rate(params));
+               return -EINVAL;
+       }
+       snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
+
+       return 0;
+}
+
+/* Codec DAI section */
+static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
+       .digital_mute   = sn95031_pcm_hs_mute,
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
+       .digital_mute   = sn95031_pcm_spkr_mute,
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+struct snd_soc_dai_driver sn95031_dais[] = {
+{
+       .name = "SN95031 Headset",
+       .playback = {
+               .stream_name = "Headset",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 5,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_headset_dai_ops,
+},
+{      .name = "SN95031 Speaker",
+       .playback = {
+               .stream_name = "Speaker",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_speaker_dai_ops,
+},
+{      .name = "SN95031 Vibra1",
+       .playback = {
+               .stream_name = "Vibra1",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_vib1_dai_ops,
+},
+{      .name = "SN95031 Vibra2",
+       .playback = {
+               .stream_name = "Vibra2",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_vib2_dai_ops,
+},
+};
+
+static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, SN95031_BTNCTRL2, 0x00);
+}
+
+static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, SN95031_BTNCTRL1, 0x77);
+       snd_soc_write(codec, SN95031_BTNCTRL2, 0x01);
+}
+
+static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
+{
+       int micbias = sn95031_get_mic_bias(mfld_jack->codec);
+
+       int jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
+
+       pr_debug("jack type detected = %d\n", jack_type);
+       if (jack_type == SND_JACK_HEADSET)
+               sn95031_enable_jack_btn(mfld_jack->codec);
+       return jack_type;
+}
+
+void sn95031_jack_detection(struct mfld_jack_data *jack_data)
+{
+       unsigned int status;
+       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
+
+       pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
+       if (jack_data->intr_id & 0x1) {
+               pr_debug("short_push detected\n");
+               status = SND_JACK_HEADSET | SND_JACK_BTN_0;
+       } else if (jack_data->intr_id & 0x2) {
+               pr_debug("long_push detected\n");
+               status = SND_JACK_HEADSET | SND_JACK_BTN_1;
+       } else if (jack_data->intr_id & 0x4) {
+               pr_debug("headset or headphones inserted\n");
+               status = sn95031_get_headset_state(jack_data->mfld_jack);
+       } else if (jack_data->intr_id & 0x8) {
+               pr_debug("headset or headphones removed\n");
+               status = 0;
+               sn95031_disable_jack_btn(jack_data->mfld_jack->codec);
+       } else {
+               pr_err("unidentified interrupt\n");
+               return;
+       }
+
+       snd_soc_jack_report(jack_data->mfld_jack, status, mask);
+       /*button pressed and released so we send explicit button release */
+       if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1))
+               snd_soc_jack_report(jack_data->mfld_jack,
+                               SND_JACK_HEADSET, mask);
+}
+EXPORT_SYMBOL_GPL(sn95031_jack_detection);
+
+/* codec registration */
+static int sn95031_codec_probe(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       pr_debug("codec_probe called\n");
+
+       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+       codec->dapm.idle_bias_off = 1;
+
+       /* PCM interface config
+        * This sets the pcm rx slot conguration to max 6 slots
+        * for max 4 dais (2 stereo and 2 mono)
+        */
+       snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10);
+       snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32);
+       snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54);
+       snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10);
+       snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32);
+       /* pcm port setting
+        * This sets the pcm port to slave and clock at 19.2Mhz which
+        * can support 6slots, sampling rate set per stream in hw-params
+        */
+       snd_soc_write(codec, SN95031_PCM1C1, 0x00);
+       snd_soc_write(codec, SN95031_PCM2C1, 0x01);
+       snd_soc_write(codec, SN95031_PCM2C2, 0x0A);
+       snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4));
+       /* vendor vibra workround, the vibras are muted by
+        * custom register so unmute them
+        */
+       snd_soc_write(codec, SN95031_SSR5, 0x80);
+       snd_soc_write(codec, SN95031_SSR6, 0x80);
+       snd_soc_write(codec, SN95031_VIB1C5, 0x00);
+       snd_soc_write(codec, SN95031_VIB2C5, 0x00);
+       /* configure vibras for pcm port */
+       snd_soc_write(codec, SN95031_VIB1C3, 0x00);
+       snd_soc_write(codec, SN95031_VIB2C3, 0x00);
+
+       /* soft mute ramp time */
+       snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
+       /* fix the initial volume at 1dB,
+        * default in +9dB,
+        * 1dB give optimal swing on DAC, amps
+        */
+       snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08);
+       snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08);
+       snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08);
+       snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08);
+       /* dac mode and lineout workaround */
+       snd_soc_write(codec, SN95031_SSR2, 0x10);
+       snd_soc_write(codec, SN95031_SSR3, 0x40);
+
+       snd_soc_add_controls(codec, sn95031_snd_controls,
+                            ARRAY_SIZE(sn95031_snd_controls));
+
+       ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
+                               ARRAY_SIZE(sn95031_dapm_widgets));
+       if (ret)
+               pr_err("soc_dapm_new_control failed %d", ret);
+       ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
+                               ARRAY_SIZE(sn95031_audio_map));
+       if (ret)
+               pr_err("soc_dapm_add_routes failed %d", ret);
+
+       return ret;
+}
+
+static int sn95031_codec_remove(struct snd_soc_codec *codec)
+{
+       pr_debug("codec_remove called\n");
+       sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+struct snd_soc_codec_driver sn95031_codec = {
+       .probe          = sn95031_codec_probe,
+       .remove         = sn95031_codec_remove,
+       .read           = sn95031_read,
+       .write          = sn95031_write,
+       .set_bias_level = sn95031_set_vaud_bias,
+};
+
+static int __devinit sn95031_device_probe(struct platform_device *pdev)
+{
+       pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev));
+       return snd_soc_register_codec(&pdev->dev, &sn95031_codec,
+                       sn95031_dais, ARRAY_SIZE(sn95031_dais));
+}
+
+static int __devexit sn95031_device_remove(struct platform_device *pdev)
+{
+       pr_debug("codec device remove called\n");
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static struct platform_driver sn95031_codec_driver = {
+       .driver         = {
+               .name           = "sn95031",
+               .owner          = THIS_MODULE,
+       },
+       .probe          = sn95031_device_probe,
+       .remove         = sn95031_device_remove,
+};
+
+static int __init sn95031_init(void)
+{
+       pr_debug("driver init called\n");
+       return platform_driver_register(&sn95031_codec_driver);
+}
+module_init(sn95031_init);
+
+static void __exit sn95031_exit(void)
+{
+       pr_debug("driver exit called\n");
+       platform_driver_unregister(&sn95031_codec_driver);
+}
+module_exit(sn95031_exit);
+
+MODULE_DESCRIPTION("ASoC TI SN95031 codec driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sn95031");
diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h
new file mode 100644 (file)
index 0000000..20376d2
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *  sn95031.h - TI sn95031 Codec driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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; version 2 of the License.
+ *
+ *  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 _SN95031_H
+#define _SN95031_H
+
+/*register map*/
+#define SN95031_VAUD                   0xDB
+#define SN95031_VHSP                   0xDC
+#define SN95031_VHSN                   0xDD
+#define SN95031_VIHF                   0xC9
+
+#define SN95031_AUDPLLCTRL             0x240
+#define SN95031_DMICBUF0123            0x241
+#define SN95031_DMICBUF45              0x242
+#define SN95031_DMICGPO                        0x244
+#define SN95031_DMICMUX                        0x245
+#define SN95031_DMICLK                 0x246
+#define SN95031_MICBIAS                        0x247
+#define SN95031_ADCCONFIG              0x248
+#define SN95031_MICAMP1                        0x249
+#define SN95031_MICAMP2                        0x24A
+#define SN95031_NOISEMUX               0x24B
+#define SN95031_AUDIOMUX12             0x24C
+#define SN95031_AUDIOMUX34             0x24D
+#define SN95031_AUDIOSINC              0x24E
+#define SN95031_AUDIOTXEN              0x24F
+#define SN95031_HSEPRXCTRL             0x250
+#define SN95031_IHFRXCTRL              0x251
+#define SN95031_HSMIXER                        0x256
+#define SN95031_DACCONFIG              0x257
+#define SN95031_SOFTMUTE               0x258
+#define SN95031_HSLVOLCTRL             0x259
+#define SN95031_HSRVOLCTRL             0x25A
+#define SN95031_IHFLVOLCTRL            0x25B
+#define SN95031_IHFRVOLCTRL            0x25C
+#define SN95031_DRIVEREN               0x25D
+#define SN95031_LOCTL                  0x25E
+#define SN95031_VIB1C1                 0x25F
+#define SN95031_VIB1C2                 0x260
+#define SN95031_VIB1C3                 0x261
+#define SN95031_VIB1SPIPCM1            0x262
+#define SN95031_VIB1SPIPCM2            0x263
+#define SN95031_VIB1C5                 0x264
+#define SN95031_VIB2C1                 0x265
+#define SN95031_VIB2C2                 0x266
+#define SN95031_VIB2C3                 0x267
+#define SN95031_VIB2SPIPCM1            0x268
+#define SN95031_VIB2SPIPCM2            0x269
+#define SN95031_VIB2C5                 0x26A
+#define SN95031_BTNCTRL1               0x26B
+#define SN95031_BTNCTRL2               0x26C
+#define SN95031_PCM1TXSLOT01           0x26D
+#define SN95031_PCM1TXSLOT23           0x26E
+#define SN95031_PCM1TXSLOT45           0x26F
+#define SN95031_PCM1RXSLOT0_3          0x270
+#define SN95031_PCM1RXSLOT45           0x271
+#define SN95031_PCM2TXSLOT01           0x272
+#define SN95031_PCM2TXSLOT23           0x273
+#define SN95031_PCM2TXSLOT45           0x274
+#define SN95031_PCM2RXSLOT01           0x275
+#define SN95031_PCM2RXSLOT23           0x276
+#define SN95031_PCM2RXSLOT45           0x277
+#define SN95031_PCM1C1                 0x278
+#define SN95031_PCM1C2                 0x279
+#define SN95031_PCM1C3                 0x27A
+#define SN95031_PCM2C1                 0x27B
+#define SN95031_PCM2C2                 0x27C
+/*end codec register defn*/
+
+/*vendor defn these are not part of avp*/
+#define SN95031_SSR2                   0x381
+#define SN95031_SSR3                   0x382
+#define SN95031_SSR5                   0x384
+#define SN95031_SSR6                   0x385
+
+/* ADC registers */
+
+#define SN95031_ADC1CNTL1 0x1C0
+#define SN95031_ADC_ENBL 0x10
+#define SN95031_ADC_START 0x08
+#define SN95031_ADC1CNTL3 0x1C2
+#define SN95031_ADCTHERM_ENBL 0x04
+#define SN95031_ADCRRDATA_ENBL 0x05
+#define SN95031_STOPBIT_MASK 16
+#define SN95031_ADCTHERM_MASK 4
+#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */
+#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
+#define SN95031_ADC_NO_LOOP 0x07
+#define SN95031_AUDIO_GPIO_CTRL 0x070
+
+/* ADC channel code values */
+#define SN95031_AUDIO_DETECT_CODE 0x06
+
+/* ADC base addresses */
+#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
+#define SN95031_ADC_DATA_START_ADDR 0x1D4  /* increments by 2 */
+/* multipier to convert to mV */
+#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
+
+
+struct mfld_jack_data {
+       int intr_id;
+       int micbias_vol;
+       struct snd_soc_jack *mfld_jack;
+};
+
+extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
+
+#endif
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
new file mode 100644 (file)
index 0000000..e93b9d1
--- /dev/null
@@ -0,0 +1,794 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic32x4.c
+ *
+ * Copyright 2011 Vista Silicon S.L.
+ *
+ * Author: Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+
+#include <sound/tlv320aic32x4.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tlv320aic32x4.h"
+
+struct aic32x4_rate_divs {
+       u32 mclk;
+       u32 rate;
+       u8 p_val;
+       u8 pll_j;
+       u16 pll_d;
+       u16 dosr;
+       u8 ndac;
+       u8 mdac;
+       u8 aosr;
+       u8 nadc;
+       u8 madc;
+       u8 blck_N;
+};
+
+struct aic32x4_priv {
+       u32 sysclk;
+       s32 master;
+       u8 page_no;
+       void *control_data;
+       u32 power_cfg;
+       u32 micpga_routing;
+       bool swapdacs;
+};
+
+/* 0dB min, 1dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0);
+/* 0dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
+
+static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
+                       AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
+       SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
+                       AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1),
+       SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
+                       AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1),
+       SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
+                       AIC32X4_HPRGAIN, 6, 0x01, 1),
+       SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
+                       AIC32X4_LORGAIN, 6, 0x01, 1),
+       SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
+                       AIC32X4_RMICPGAVOL, 7, 0x01, 1),
+
+       SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0),
+       SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0),
+
+       SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL,
+                       AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5),
+       SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL,
+                       AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5),
+
+       SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0),
+
+       SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0),
+       SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0),
+       SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1,
+                       4, 0x07, 0),
+       SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1,
+                       0, 0x03, 0),
+       SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2,
+                       6, 0x03, 0),
+       SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2,
+                       1, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3,
+                       0, 0x7F, 0),
+       SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4,
+                       3, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5,
+                       3, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6,
+                       0, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7,
+                       0, 0x0F, 0),
+};
+
+static const struct aic32x4_rate_divs aic32x4_divs[] = {
+       /* 8k rate */
+       {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
+       {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
+       {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
+       /* 11.025k rate */
+       {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
+       {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
+       /* 16k rate */
+       {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
+       {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
+       {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
+       /* 22.05k rate */
+       {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
+       {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
+       {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
+       /* 32k rate */
+       {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
+       {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
+       /* 44.1k rate */
+       {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
+       {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
+       {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
+       /* 48k rate */
+       {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
+       {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
+       {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}
+};
+
+static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
+       SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0),
+       SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new lol_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_input_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0),
+       SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0),
+       SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_input_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0),
+       SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0),
+       SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0),
+       SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &hpl_output_mixer_controls[0],
+                          ARRAY_SIZE(hpl_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &lol_output_mixer_controls[0],
+                          ARRAY_SIZE(lol_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0),
+
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0),
+       SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &hpr_output_mixer_controls[0],
+                          ARRAY_SIZE(hpr_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &lor_output_mixer_controls[0],
+                          ARRAY_SIZE(lor_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
+                          &left_input_mixer_controls[0],
+                          ARRAY_SIZE(left_input_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
+                          &right_input_mixer_controls[0],
+                          ARRAY_SIZE(right_input_mixer_controls)),
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0),
+       SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("LOL"),
+       SND_SOC_DAPM_OUTPUT("LOR"),
+       SND_SOC_DAPM_INPUT("IN1_L"),
+       SND_SOC_DAPM_INPUT("IN1_R"),
+       SND_SOC_DAPM_INPUT("IN2_L"),
+       SND_SOC_DAPM_INPUT("IN2_R"),
+       SND_SOC_DAPM_INPUT("IN3_L"),
+       SND_SOC_DAPM_INPUT("IN3_R"),
+};
+
+static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
+       /* Left Output */
+       {"HPL Output Mixer", "L_DAC Switch", "Left DAC"},
+       {"HPL Output Mixer", "IN1_L Switch", "IN1_L"},
+
+       {"HPL Power", NULL, "HPL Output Mixer"},
+       {"HPL", NULL, "HPL Power"},
+
+       {"LOL Output Mixer", "L_DAC Switch", "Left DAC"},
+
+       {"LOL Power", NULL, "LOL Output Mixer"},
+       {"LOL", NULL, "LOL Power"},
+
+       /* Right Output */
+       {"HPR Output Mixer", "R_DAC Switch", "Right DAC"},
+       {"HPR Output Mixer", "IN1_R Switch", "IN1_R"},
+
+       {"HPR Power", NULL, "HPR Output Mixer"},
+       {"HPR", NULL, "HPR Power"},
+
+       {"LOR Output Mixer", "R_DAC Switch", "Right DAC"},
+
+       {"LOR Power", NULL, "LOR Output Mixer"},
+       {"LOR", NULL, "LOR Power"},
+
+       /* Left input */
+       {"Left Input Mixer", "IN1_L P Switch", "IN1_L"},
+       {"Left Input Mixer", "IN2_L P Switch", "IN2_L"},
+       {"Left Input Mixer", "IN3_L P Switch", "IN3_L"},
+
+       {"Left ADC", NULL, "Left Input Mixer"},
+
+       /* Right Input */
+       {"Right Input Mixer", "IN1_R P Switch", "IN1_R"},
+       {"Right Input Mixer", "IN2_R P Switch", "IN2_R"},
+       {"Right Input Mixer", "IN3_R P Switch", "IN3_R"},
+
+       {"Right ADC", NULL, "Right Input Mixer"},
+};
+
+static inline int aic32x4_change_page(struct snd_soc_codec *codec,
+                                       unsigned int new_page)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 data[2];
+       int ret;
+
+       data[0] = 0x00;
+       data[1] = new_page & 0xff;
+
+       ret = codec->hw_write(codec->control_data, data, 2);
+       if (ret == 2) {
+               aic32x4->page_no = new_page;
+               return 0;
+       } else {
+               return ret;
+       }
+}
+
+static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg,
+                               unsigned int val)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       unsigned int page = reg / 128;
+       unsigned int fixed_reg = reg % 128;
+       u8 data[2];
+       int ret;
+
+       /* A write to AIC32X4_PSEL is really a non-explicit page change */
+       if (reg == AIC32X4_PSEL)
+               return aic32x4_change_page(codec, val);
+
+       if (aic32x4->page_no != page) {
+               ret = aic32x4_change_page(codec, page);
+               if (ret != 0)
+                       return ret;
+       }
+
+       data[0] = fixed_reg & 0xff;
+       data[1] = val & 0xff;
+
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       unsigned int page = reg / 128;
+       unsigned int fixed_reg = reg % 128;
+       int ret;
+
+       if (aic32x4->page_no != page) {
+               ret = aic32x4_change_page(codec, page);
+               if (ret != 0)
+                       return ret;
+       }
+       return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff);
+}
+
+static inline int aic32x4_get_divs(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
+               if ((aic32x4_divs[i].rate == rate)
+                   && (aic32x4_divs[i].mclk == mclk)) {
+                       return i;
+               }
+       }
+       printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
+       return -EINVAL;
+}
+
+static int aic32x4_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
+                                 ARRAY_SIZE(aic32x4_dapm_widgets));
+
+       snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
+                               ARRAY_SIZE(aic32x4_dapm_routes));
+
+       snd_soc_dapm_new_widgets(&codec->dapm);
+       return 0;
+}
+
+static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+
+       switch (freq) {
+       case AIC32X4_FREQ_12000000:
+       case AIC32X4_FREQ_24000000:
+       case AIC32X4_FREQ_25000000:
+               aic32x4->sysclk = freq;
+               return 0;
+       }
+       printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
+       return -EINVAL;
+}
+
+static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 iface_reg_1;
+       u8 iface_reg_2;
+       u8 iface_reg_3;
+
+       iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1);
+       iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2);
+       iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2);
+       iface_reg_2 = 0;
+       iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3);
+       iface_reg_3 = iface_reg_3 & ~(1 << 3);
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aic32x4->master = 1;
+               iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               aic32x4->master = 0;
+               break;
+       default:
+               printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
+               iface_reg_3 |= (1 << 3); /* invert bit clock */
+               iface_reg_2 = 0x01; /* add offset 1 */
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
+               iface_reg_3 |= (1 << 3); /* invert bit clock */
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               iface_reg_1 |=
+                       (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface_reg_1 |=
+                       (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+               break;
+       default:
+               printk(KERN_ERR "aic32x4: invalid DAI interface format\n");
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1);
+       snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2);
+       snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3);
+       return 0;
+}
+
+static int aic32x4_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 data;
+       int i;
+
+       i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
+       if (i < 0) {
+               printk(KERN_ERR "aic32x4: sampling rate not supported\n");
+               return i;
+       }
+
+       /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */
+       snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN);
+       snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK);
+
+       /* We will fix R value to 1 and will make P & J=K.D as varialble */
+       data = snd_soc_read(codec, AIC32X4_PLLPR);
+       data &= ~(7 << 4);
+       snd_soc_write(codec, AIC32X4_PLLPR,
+                     (data | (aic32x4_divs[i].p_val << 4) | 0x01));
+
+       snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
+
+       snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
+       snd_soc_write(codec, AIC32X4_PLLDLSB,
+                     (aic32x4_divs[i].pll_d & 0xff));
+
+       /* NDAC divider value */
+       data = snd_soc_read(codec, AIC32X4_NDAC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac);
+
+       /* MDAC divider value */
+       data = snd_soc_read(codec, AIC32X4_MDAC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac);
+
+       /* DOSR MSB & LSB values */
+       snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
+       snd_soc_write(codec, AIC32X4_DOSRLSB,
+                     (aic32x4_divs[i].dosr & 0xff));
+
+       /* NADC divider value */
+       data = snd_soc_read(codec, AIC32X4_NADC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc);
+
+       /* MADC divider value */
+       data = snd_soc_read(codec, AIC32X4_MADC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc);
+
+       /* AOSR value */
+       snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr);
+
+       /* BCLK N divider */
+       data = snd_soc_read(codec, AIC32X4_BCLKN);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N);
+
+       data = snd_soc_read(codec, AIC32X4_IFACE1);
+       data = data & ~(3 << 4);
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT);
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT);
+               break;
+       }
+       snd_soc_write(codec, AIC32X4_IFACE1, data);
+
+       return 0;
+}
+
+static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 dac_reg;
+
+       dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON;
+       if (mute)
+               snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON);
+       else
+               snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg);
+       return 0;
+}
+
+static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 value;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               if (aic32x4->master) {
+                       /* Switch on PLL */
+                       value = snd_soc_read(codec, AIC32X4_PLLPR);
+                       snd_soc_write(codec, AIC32X4_PLLPR,
+                                     (value | AIC32X4_PLLEN));
+
+                       /* Switch on NDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NDAC);
+                       snd_soc_write(codec, AIC32X4_NDAC,
+                                     value | AIC32X4_NDACEN);
+
+                       /* Switch on MDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MDAC);
+                       snd_soc_write(codec, AIC32X4_MDAC,
+                                     value | AIC32X4_MDACEN);
+
+                       /* Switch on NADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NADC);
+                       snd_soc_write(codec, AIC32X4_NADC,
+                                     value | AIC32X4_MDACEN);
+
+                       /* Switch on MADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MADC);
+                       snd_soc_write(codec, AIC32X4_MADC,
+                                     value | AIC32X4_MDACEN);
+
+                       /* Switch on BCLK_N Divider */
+                       value = snd_soc_read(codec, AIC32X4_BCLKN);
+                       snd_soc_write(codec, AIC32X4_BCLKN,
+                                     value | AIC32X4_BCLKEN);
+               }
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (aic32x4->master) {
+                       /* Switch off PLL */
+                       value = snd_soc_read(codec, AIC32X4_PLLPR);
+                       snd_soc_write(codec, AIC32X4_PLLPR,
+                                     (value & ~AIC32X4_PLLEN));
+
+                       /* Switch off NDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NDAC);
+                       snd_soc_write(codec, AIC32X4_NDAC,
+                                     value & ~AIC32X4_NDACEN);
+
+                       /* Switch off MDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MDAC);
+                       snd_soc_write(codec, AIC32X4_MDAC,
+                                     value & ~AIC32X4_MDACEN);
+
+                       /* Switch off NADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NADC);
+                       snd_soc_write(codec, AIC32X4_NADC,
+                                     value & ~AIC32X4_NDACEN);
+
+                       /* Switch off MADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MADC);
+                       snd_soc_write(codec, AIC32X4_MADC,
+                                     value & ~AIC32X4_MDACEN);
+                       value = snd_soc_read(codec, AIC32X4_BCLKN);
+
+                       /* Switch off BCLK_N Divider */
+                       snd_soc_write(codec, AIC32X4_BCLKN,
+                                     value & ~AIC32X4_BCLKEN);
+               }
+               break;
+       case SND_SOC_BIAS_OFF:
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+#define AIC32X4_RATES  SNDRV_PCM_RATE_8000_48000
+#define AIC32X4_FORMATS        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+                        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops aic32x4_ops = {
+       .hw_params = aic32x4_hw_params,
+       .digital_mute = aic32x4_mute,
+       .set_fmt = aic32x4_set_dai_fmt,
+       .set_sysclk = aic32x4_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver aic32x4_dai = {
+       .name = "tlv320aic32x4-hifi",
+       .playback = {
+                    .stream_name = "Playback",
+                    .channels_min = 1,
+                    .channels_max = 2,
+                    .rates = AIC32X4_RATES,
+                    .formats = AIC32X4_FORMATS,},
+       .capture = {
+                   .stream_name = "Capture",
+                   .channels_min = 1,
+                   .channels_max = 2,
+                   .rates = AIC32X4_RATES,
+                   .formats = AIC32X4_FORMATS,},
+       .ops = &aic32x4_ops,
+       .symmetric_rates = 1,
+};
+
+static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int aic32x4_resume(struct snd_soc_codec *codec)
+{
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+static int aic32x4_probe(struct snd_soc_codec *codec)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u32 tmp_reg;
+
+       codec->hw_write = (hw_write_t) i2c_master_send;
+       codec->control_data = aic32x4->control_data;
+
+       snd_soc_write(codec, AIC32X4_RESET, 0x01);
+
+       /* Power platform configuration */
+       if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
+               snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
+                                                     AIC32X4_MICBIAS_2075V);
+       }
+       if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) {
+               snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
+       }
+       if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) {
+               snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN);
+       }
+       tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE);
+       if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) {
+               tmp_reg |= AIC32X4_LDOIN_18_36;
+       }
+       if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) {
+               tmp_reg |= AIC32X4_LDOIN2HP;
+       }
+       snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg);
+
+       /* Do DACs need to be swapped? */
+       if (aic32x4->swapdacs) {
+               snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN);
+       } else {
+               snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN);
+       }
+
+       /* Mic PGA routing */
+       if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) {
+               snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K);
+       }
+       if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) {
+               snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K);
+       }
+
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       snd_soc_add_controls(codec, aic32x4_snd_controls,
+                            ARRAY_SIZE(aic32x4_snd_controls));
+       aic32x4_add_widgets(codec);
+
+       return 0;
+}
+
+static int aic32x4_remove(struct snd_soc_codec *codec)
+{
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
+       .read = aic32x4_read,
+       .write = aic32x4_write,
+       .probe = aic32x4_probe,
+       .remove = aic32x4_remove,
+       .suspend = aic32x4_suspend,
+       .resume = aic32x4_resume,
+       .set_bias_level = aic32x4_set_bias_level,
+};
+
+static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct aic32x4_pdata *pdata = i2c->dev.platform_data;
+       struct aic32x4_priv *aic32x4;
+       int ret;
+
+       aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL);
+       if (aic32x4 == NULL)
+               return -ENOMEM;
+
+       aic32x4->control_data = i2c;
+       i2c_set_clientdata(i2c, aic32x4);
+
+       if (pdata) {
+               aic32x4->power_cfg = pdata->power_cfg;
+               aic32x4->swapdacs = pdata->swapdacs;
+               aic32x4->micpga_routing = pdata->micpga_routing;
+       } else {
+               aic32x4->power_cfg = 0;
+               aic32x4->swapdacs = false;
+               aic32x4->micpga_routing = 0;
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_aic32x4, &aic32x4_dai, 1);
+       if (ret < 0)
+               kfree(aic32x4);
+       return ret;
+}
+
+static __devexit int aic32x4_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id aic32x4_i2c_id[] = {
+       { "tlv320aic32x4", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
+
+static struct i2c_driver aic32x4_i2c_driver = {
+       .driver = {
+               .name = "tlv320aic32x4",
+               .owner = THIS_MODULE,
+       },
+       .probe =    aic32x4_i2c_probe,
+       .remove =   __devexit_p(aic32x4_i2c_remove),
+       .id_table = aic32x4_i2c_id,
+};
+
+static int __init aic32x4_modinit(void)
+{
+       int ret = 0;
+
+       ret = i2c_add_driver(&aic32x4_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n",
+                      ret);
+       }
+       return ret;
+}
+module_init(aic32x4_modinit);
+
+static void __exit aic32x4_exit(void)
+{
+       i2c_del_driver(&aic32x4_i2c_driver);
+}
+module_exit(aic32x4_exit);
+
+MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
new file mode 100644 (file)
index 0000000..aae2b24
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * tlv320aic32x4.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#ifndef _TLV320AIC32X4_H
+#define _TLV320AIC32X4_H
+
+/* tlv320aic32x4 register space (in decimal to match datasheet) */
+
+#define AIC32X4_PAGE1          128
+
+#define        AIC32X4_PSEL            0
+#define        AIC32X4_RESET           1
+#define        AIC32X4_CLKMUX          4
+#define        AIC32X4_PLLPR           5
+#define        AIC32X4_PLLJ            6
+#define        AIC32X4_PLLDMSB         7
+#define        AIC32X4_PLLDLSB         8
+#define        AIC32X4_NDAC            11
+#define        AIC32X4_MDAC            12
+#define AIC32X4_DOSRMSB                13
+#define AIC32X4_DOSRLSB                14
+#define        AIC32X4_NADC            18
+#define        AIC32X4_MADC            19
+#define AIC32X4_AOSR           20
+#define AIC32X4_CLKMUX2                25
+#define AIC32X4_CLKOUTM                26
+#define AIC32X4_IFACE1         27
+#define AIC32X4_IFACE2         28
+#define AIC32X4_IFACE3         29
+#define AIC32X4_BCLKN          30
+#define AIC32X4_IFACE4         31
+#define AIC32X4_IFACE5         32
+#define AIC32X4_IFACE6         33
+#define AIC32X4_DOUTCTL                53
+#define AIC32X4_DINCTL         54
+#define AIC32X4_DACSPB         60
+#define AIC32X4_ADCSPB         61
+#define AIC32X4_DACSETUP       63
+#define AIC32X4_DACMUTE                64
+#define AIC32X4_LDACVOL                65
+#define AIC32X4_RDACVOL                66
+#define AIC32X4_ADCSETUP       81
+#define        AIC32X4_ADCFGA          82
+#define AIC32X4_LADCVOL                83
+#define AIC32X4_RADCVOL                84
+#define AIC32X4_LAGC1          86
+#define AIC32X4_LAGC2          87
+#define AIC32X4_LAGC3          88
+#define AIC32X4_LAGC4          89
+#define AIC32X4_LAGC5          90
+#define AIC32X4_LAGC6          91
+#define AIC32X4_LAGC7          92
+#define AIC32X4_RAGC1          94
+#define AIC32X4_RAGC2          95
+#define AIC32X4_RAGC3          96
+#define AIC32X4_RAGC4          97
+#define AIC32X4_RAGC5          98
+#define AIC32X4_RAGC6          99
+#define AIC32X4_RAGC7          100
+#define AIC32X4_PWRCFG         (AIC32X4_PAGE1 + 1)
+#define AIC32X4_LDOCTL         (AIC32X4_PAGE1 + 2)
+#define AIC32X4_OUTPWRCTL      (AIC32X4_PAGE1 + 9)
+#define AIC32X4_CMMODE         (AIC32X4_PAGE1 + 10)
+#define AIC32X4_HPLROUTE       (AIC32X4_PAGE1 + 12)
+#define AIC32X4_HPRROUTE       (AIC32X4_PAGE1 + 13)
+#define AIC32X4_LOLROUTE       (AIC32X4_PAGE1 + 14)
+#define AIC32X4_LORROUTE       (AIC32X4_PAGE1 + 15)
+#define        AIC32X4_HPLGAIN         (AIC32X4_PAGE1 + 16)
+#define        AIC32X4_HPRGAIN         (AIC32X4_PAGE1 + 17)
+#define        AIC32X4_LOLGAIN         (AIC32X4_PAGE1 + 18)
+#define        AIC32X4_LORGAIN         (AIC32X4_PAGE1 + 19)
+#define AIC32X4_HEADSTART      (AIC32X4_PAGE1 + 20)
+#define AIC32X4_MICBIAS                (AIC32X4_PAGE1 + 51)
+#define AIC32X4_LMICPGAPIN     (AIC32X4_PAGE1 + 52)
+#define AIC32X4_LMICPGANIN     (AIC32X4_PAGE1 + 54)
+#define AIC32X4_RMICPGAPIN     (AIC32X4_PAGE1 + 55)
+#define AIC32X4_RMICPGANIN     (AIC32X4_PAGE1 + 57)
+#define AIC32X4_FLOATINGINPUT  (AIC32X4_PAGE1 + 58)
+#define AIC32X4_LMICPGAVOL     (AIC32X4_PAGE1 + 59)
+#define AIC32X4_RMICPGAVOL     (AIC32X4_PAGE1 + 60)
+
+#define AIC32X4_FREQ_12000000 12000000
+#define AIC32X4_FREQ_24000000 24000000
+#define AIC32X4_FREQ_25000000 25000000
+
+#define AIC32X4_WORD_LEN_16BITS                0x00
+#define AIC32X4_WORD_LEN_20BITS                0x01
+#define AIC32X4_WORD_LEN_24BITS                0x02
+#define AIC32X4_WORD_LEN_32BITS                0x03
+
+#define AIC32X4_I2S_MODE               0x00
+#define AIC32X4_DSP_MODE               0x01
+#define AIC32X4_RIGHT_JUSTIFIED_MODE   0x02
+#define AIC32X4_LEFT_JUSTIFIED_MODE    0x03
+
+#define AIC32X4_AVDDWEAKDISABLE                0x08
+#define AIC32X4_LDOCTLEN               0x01
+
+#define AIC32X4_LDOIN_18_36            0x01
+#define AIC32X4_LDOIN2HP               0x02
+
+#define AIC32X4_DACSPBLOCK_MASK                0x1f
+#define AIC32X4_ADCSPBLOCK_MASK                0x1f
+
+#define AIC32X4_PLLJ_SHIFT             6
+#define AIC32X4_DOSRMSB_SHIFT          4
+
+#define AIC32X4_PLLCLKIN               0x03
+
+#define AIC32X4_MICBIAS_LDOIN          0x08
+#define AIC32X4_MICBIAS_2075V          0x60
+
+#define AIC32X4_LMICPGANIN_IN2R_10K    0x10
+#define AIC32X4_RMICPGANIN_IN1L_10K    0x10
+
+#define AIC32X4_LMICPGAVOL_NOGAIN      0x80
+#define AIC32X4_RMICPGAVOL_NOGAIN      0x80
+
+#define AIC32X4_BCLKMASTER             0x08
+#define AIC32X4_WCLKMASTER             0x04
+#define AIC32X4_PLLEN                  (0x01 << 7)
+#define AIC32X4_NDACEN                 (0x01 << 7)
+#define AIC32X4_MDACEN                 (0x01 << 7)
+#define AIC32X4_NADCEN                 (0x01 << 7)
+#define AIC32X4_MADCEN                 (0x01 << 7)
+#define AIC32X4_BCLKEN                 (0x01 << 7)
+#define AIC32X4_DACEN                  (0x03 << 6)
+#define AIC32X4_RDAC2LCHN              (0x02 << 2)
+#define AIC32X4_LDAC2RCHN              (0x02 << 4)
+#define AIC32X4_LDAC2LCHN              (0x01 << 4)
+#define AIC32X4_RDAC2RCHN              (0x01 << 2)
+
+#define AIC32X4_SSTEP2WCLK             0x01
+#define AIC32X4_MUTEON                 0x0C
+#define        AIC32X4_DACMOD2BCLK             0x01
+
+#endif                         /* _TLV320AIC32X4_H */
index 71d7be8ac488f652f2907df9b966bd4b7f232c8c..00b6d87e7bdb9d6e9eb2fe6a0a9299e3c6d6999d 100644 (file)
@@ -1615,6 +1615,7 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = {
        },
        { },
 };
+MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
 
 static struct i2c_driver tlv320dac33_i2c_driver = {
        .driver = {
index 4bbf1b15a493ab3d39268d719d6780acfba407a0..482fcdb59bfa57c3d82eaa002b3e23d8b5bf760d 100644 (file)
@@ -724,8 +724,8 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-void twl6040_hs_jack_report(struct snd_soc_codec *codec,
-                               struct snd_soc_jack *jack, int report)
+static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
+                                  struct snd_soc_jack *jack, int report)
 {
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
        int status;
index 80ddf4fd23db0c82bf63363a8d67ffa99a9d19f7..a3b9cbb20ee9bc07ca849fbe4f7af593048da9c8 100644 (file)
@@ -836,24 +836,25 @@ static void wm2000_i2c_shutdown(struct i2c_client *i2c)
 }
 
 #ifdef CONFIG_PM
-static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+static int wm2000_i2c_suspend(struct device *dev)
 {
+       struct i2c_client *i2c = to_i2c_client(dev);
        struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
 
        return wm2000_anc_transition(wm2000, ANC_OFF);
 }
 
-static int wm2000_i2c_resume(struct i2c_client *i2c)
+static int wm2000_i2c_resume(struct device *dev)
 {
+       struct i2c_client *i2c = to_i2c_client(dev);
        struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
 
        return wm2000_anc_set_mode(wm2000);
 }
-#else
-#define wm2000_i2c_suspend NULL
-#define wm2000_i2c_resume NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(wm2000_pm, wm2000_i2c_suspend, wm2000_i2c_resume);
+
 static const struct i2c_device_id wm2000_i2c_id[] = {
        { "wm2000", 0 },
        { }
@@ -864,11 +865,10 @@ static struct i2c_driver wm2000_i2c_driver = {
        .driver = {
                .name = "wm2000",
                .owner = THIS_MODULE,
+               .pm = &wm2000_pm,
        },
        .probe = wm2000_i2c_probe,
        .remove = __devexit_p(wm2000_i2c_remove),
-       .suspend = wm2000_i2c_suspend,
-       .resume = wm2000_i2c_resume,
        .shutdown = wm2000_i2c_shutdown,
        .id_table = wm2000_i2c_id,
 };
index 5eb2f501ce32d2758386a1d4f7048a2a1861765f..4fd4d8dca0fc0af31439371fbab9040794162095 100644 (file)
@@ -58,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
        0x0000,     /* R8 - ZERO_DETECT */
 };
 
-static int wm8523_volatile_register(unsigned int reg)
+static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8523_DEVICE_ID:
@@ -414,7 +414,6 @@ static int wm8523_resume(struct snd_soc_codec *codec)
 static int wm8523_probe(struct snd_soc_codec *codec)
 {
        struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
-       u16 *reg_cache = codec->reg_cache;
        int ret, i;
 
        codec->hw_write = (hw_write_t)i2c_master_send;
@@ -471,8 +470,9 @@ static int wm8523_probe(struct snd_soc_codec *codec)
        }
 
        /* Change some default settings - latch VU and enable ZC */
-       reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
-       reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+       snd_soc_update_bits(codec, WM8523_DAC_GAINR,
+                           WM8523_DACR_VU, WM8523_DACR_VU);
+       snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
 
        wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
index 494f2d31d75bc7836a550c517580cc2de69ec56d..25af901fe8133029e7efe35914c07e02105e4085 100644 (file)
@@ -421,7 +421,6 @@ static int wm8741_resume(struct snd_soc_codec *codec)
 static int wm8741_probe(struct snd_soc_codec *codec)
 {
        struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
-       u16 *reg_cache = codec->reg_cache;
        int ret = 0;
 
        ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
@@ -437,10 +436,14 @@ static int wm8741_probe(struct snd_soc_codec *codec)
        }
 
        /* Change some default settings - latch VU */
-       reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
-       reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
-       reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
-       reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
+       snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+                           WM8741_UPDATELL, WM8741_UPDATELL);
+       snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+                           WM8741_UPDATELM, WM8741_UPDATELM);
+       snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+                           WM8741_UPDATERL, WM8741_UPDATERL);
+       snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+                           WM8741_UPDATERM, WM8741_UPDATERM);
 
        snd_soc_add_controls(codec, wm8741_snd_controls,
                             ARRAY_SIZE(wm8741_snd_controls));
index 79b02ae125c57984718fdff92cec6c94bf695fb9..3f09deea8d9d393b4f546c3f36e5134325225392 100644 (file)
@@ -55,8 +55,10 @@ static int caps_charge = 2000;
 module_param(caps_charge, int, 0);
 MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
 
-static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
-               struct snd_soc_dai *dai, unsigned int hifi);
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt);
+static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt);
 
 /*
  * wm8753 register cache
@@ -87,6 +89,10 @@ struct wm8753_priv {
        enum snd_soc_control_type control_type;
        unsigned int sysclk;
        unsigned int pcmclk;
+
+       unsigned int voice_fmt;
+       unsigned int hifi_fmt;
+
        int dai_func;
 };
 
@@ -170,9 +176,9 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       int mode = snd_soc_read(codec, WM8753_IOCTL);
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
 
-       ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;
+       ucontrol->value.integer.value[0] = wm8753->dai_func;
        return 0;
 }
 
@@ -180,16 +186,26 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       int mode = snd_soc_read(codec, WM8753_IOCTL);
        struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+       u16 ioctl;
+
+       if (codec->active)
+               return -EBUSY;
+
+       ioctl = snd_soc_read(codec, WM8753_IOCTL);
+
+       wm8753->dai_func = ucontrol->value.integer.value[0];
+
+       if (((ioctl >> 2) & 0x3) == wm8753->dai_func)
+               return 1;
+
+       ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2);
+       snd_soc_write(codec, WM8753_IOCTL, ioctl);
 
-       if (((mode & 0xc) >> 2) == ucontrol->value.integer.value[0])
-               return 0;
 
-       mode &= 0xfff3;
-       mode |= (ucontrol->value.integer.value[0] << 2);
+       wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt);
+       wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt);
 
-       wm8753->dai_func =  ucontrol->value.integer.value[0];
        return 1;
 }
 
@@ -828,10 +844,9 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 /*
  * Set's ADC and Voice DAC format.
  */
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec;
 
        /* interface format */
@@ -858,13 +873,6 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
-static int wm8753_pcm_startup(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       wm8753_set_dai_mode(dai->codec, dai, 0);
-       return 0;
-}
-
 /*
  * Set PCM DAI bit size and sample rate.
  */
@@ -905,10 +913,9 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
 /*
  * Set's PCM dai fmt and BCLK.
  */
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 voice, ioctl;
 
        voice = snd_soc_read(codec, WM8753_PCM) & 0x011f;
@@ -999,10 +1006,9 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 /*
  * Set's HiFi DAC format.
  */
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0;
 
        /* interface format */
@@ -1032,10 +1038,9 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
 /*
  * Set's I2S DAI format.
  */
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 ioctl, hifi;
 
        hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f;
@@ -1098,13 +1103,6 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
-static int wm8753_i2s_startup(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       wm8753_set_dai_mode(dai->codec, dai, 1);
-       return 0;
-}
-
 /*
  * Set PCM DAI bit size and sample rate.
  */
@@ -1147,61 +1145,117 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 clock;
 
        /* set clk source as pcmclk */
        clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
        snd_soc_write(codec, WM8753_CLOCK, clock);
 
-       if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
+       return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
 }
 
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
+       return wm8753_hdac_set_dai_fmt(codec, fmt);
 }
 
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 clock;
 
        /* set clk source as pcmclk */
        clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
        snd_soc_write(codec, WM8753_CLOCK, clock);
 
-       if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
+       return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
 }
 
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 clock;
 
        /* set clk source as mclk */
        clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
        snd_soc_write(codec, WM8753_CLOCK, clock | 0x4);
 
-       if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
+       if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0)
                return -EINVAL;
-       if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
+       return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
 }
 
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt)
+{
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       switch (wm8753->dai_func) {
+       case 0:
+               ret = wm8753_mode1h_set_dai_fmt(codec, fmt);
+               break;
+       case 1:
+               ret = wm8753_mode2_set_dai_fmt(codec, fmt);
+               break;
+       case 2:
+       case 3:
+               ret = wm8753_mode3_4_set_dai_fmt(codec, fmt);
+               break;
+       default:
+                break;
+       }
+       if (ret)
+               return ret;
+
+       return wm8753_i2s_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+       wm8753->hifi_fmt = fmt;
+
+       return wm8753_hifi_write_dai_fmt(codec, fmt);
+};
+
+static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt)
+{
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       if (wm8753->dai_func != 0)
+               return 0;
+
+       ret = wm8753_mode1v_set_dai_fmt(codec, fmt);
+       if (ret)
+               return ret;
+       ret = wm8753_pcm_set_dai_fmt(codec, fmt);
+       if (ret)
+               return ret;
+
+       return 0;
+};
+
+static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+       wm8753->voice_fmt = fmt;
+
+       return wm8753_voice_write_dai_fmt(codec, fmt);
+};
+
 static int wm8753_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -1268,57 +1322,25 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
  * 3. Voice disabled - HIFI over HIFI
  * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
  */
-static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = {
-       .startup = wm8753_i2s_startup,
+static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
        .hw_params      = wm8753_i2s_hw_params,
        .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode1h_set_dai_fmt,
-       .set_clkdiv     = wm8753_set_dai_clkdiv,
-       .set_pll        = wm8753_set_dai_pll,
-       .set_sysclk     = wm8753_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = {
-       .startup = wm8753_pcm_startup,
-       .hw_params      = wm8753_pcm_hw_params,
-       .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode1v_set_dai_fmt,
+       .set_fmt        = wm8753_hifi_set_dai_fmt,
        .set_clkdiv     = wm8753_set_dai_clkdiv,
        .set_pll        = wm8753_set_dai_pll,
        .set_sysclk     = wm8753_set_dai_sysclk,
 };
 
-static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = {
-       .startup = wm8753_pcm_startup,
+static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
        .hw_params      = wm8753_pcm_hw_params,
        .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode2_set_dai_fmt,
-       .set_clkdiv     = wm8753_set_dai_clkdiv,
-       .set_pll        = wm8753_set_dai_pll,
-       .set_sysclk     = wm8753_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3        = {
-       .startup = wm8753_i2s_startup,
-       .hw_params      = wm8753_i2s_hw_params,
-       .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode3_4_set_dai_fmt,
-       .set_clkdiv     = wm8753_set_dai_clkdiv,
-       .set_pll        = wm8753_set_dai_pll,
-       .set_sysclk     = wm8753_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4        = {
-       .startup = wm8753_i2s_startup,
-       .hw_params      = wm8753_i2s_hw_params,
-       .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode3_4_set_dai_fmt,
+       .set_fmt        = wm8753_voice_set_dai_fmt,
        .set_clkdiv     = wm8753_set_dai_clkdiv,
        .set_pll        = wm8753_set_dai_pll,
        .set_sysclk     = wm8753_set_dai_sysclk,
 };
 
-static struct snd_soc_dai_driver wm8753_all_dai[] = {
+static struct snd_soc_dai_driver wm8753_dai[] = {
 /* DAI HiFi mode 1 */
 {      .name = "wm8753-hifi",
        .playback = {
@@ -1326,14 +1348,16 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS},
+               .formats = WM8753_FORMATS
+       },
        .capture = { /* dummy for fast DAI switching */
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS},
-       .ops = &wm8753_dai_ops_hifi_mode1,
+               .formats = WM8753_FORMATS
+       },
+       .ops = &wm8753_dai_ops_hifi_mode,
 },
 /* DAI Voice mode 1 */
 {      .name = "wm8753-voice",
@@ -1342,97 +1366,19 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = {
                .channels_min = 1,
                .channels_max = 1,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_voice_mode1,
-},
-/* DAI HiFi mode 2 - dummy */
-{      .name = "wm8753-hifi",
-},
-/* DAI Voice mode 2 */
-{      .name = "wm8753-voice",
-       .playback = {
-               .stream_name = "Voice Playback",
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_voice_mode2,
-},
-/* DAI HiFi mode 3 */
-{      .name = "wm8753-hifi",
-       .playback = {
-               .stream_name = "HiFi Playback",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_hifi_mode3,
-},
-/* DAI Voice mode 3 - dummy */
-{      .name = "wm8753-voice",
-},
-/* DAI HiFi mode 4 */
-{      .name = "wm8753-hifi",
-       .playback = {
-               .stream_name = "HiFi Playback",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
+               .formats = WM8753_FORMATS,
+       },
        .capture = {
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_hifi_mode4,
-},
-/* DAI Voice mode 4 - dummy */
-{      .name = "wm8753-voice",
-},
-};
-
-static struct snd_soc_dai_driver wm8753_dai[] = {
-       {
-               .name = "wm8753-aif0",
-       },
-       {
-               .name = "wm8753-aif1",
+               .formats = WM8753_FORMATS,
        },
+       .ops = &wm8753_dai_ops_voice_mode,
+},
 };
 
-static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
-               struct snd_soc_dai *dai, unsigned int hifi)
-{
-       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
-
-       if (wm8753->dai_func < 4) {
-               if (hifi)
-                       dai->driver = &wm8753_all_dai[wm8753->dai_func << 1];
-               else
-                       dai->driver = &wm8753_all_dai[(wm8753->dai_func << 1) + 1];
-       }
-       snd_soc_write(codec, WM8753_IOCTL, wm8753->dai_func);
-}
-
 static void wm8753_work(struct work_struct *work)
 {
        struct snd_soc_dapm_context *dapm =
index 6dae1b40c9f744c5a96b1f7ce45c15b1c022a5c3..6785688f88069ad9b56fed4a28933d34c8751ebc 100644 (file)
@@ -175,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int wm8804_volatile(unsigned int reg)
+static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8804_RST_DEVID1:
index cd0959926d12a03c520812d0ac34d0807ce6f909..449ea09a193d6800596c481eb564bee1e35d2997 100644 (file)
@@ -180,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
        /* Remaining registers all zero */
 };
 
-static int wm8900_volatile_register(unsigned int reg)
+static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8900_REG_ID:
index 017d99ceb42eb28b84844a85ee19b0a840fd824a..ae1cadfae84c1fe173c6ef09939c7b2a6cd70f7f 100644 (file)
@@ -2,6 +2,7 @@
  * wm8903.c  --  WM8903 ALSA SoC Audio driver
  *
  * Copyright 2008 Wolfson Microelectronics
+ * Copyright 2011 NVIDIA, Inc.
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
@@ -19,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
@@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = {
 };
 
 struct wm8903_priv {
+       struct snd_soc_codec *codec;
 
        int sysclk;
        int irq;
@@ -220,25 +223,36 @@ struct wm8903_priv {
        int fs;
        int deemph;
 
+       int dcs_pending;
+       int dcs_cache[4];
+
        /* Reference count */
        int class_w_users;
 
-       struct completion wseq;
-
        struct snd_soc_jack *mic_jack;
        int mic_det;
        int mic_short;
        int mic_last_report;
        int mic_delay;
+
+#ifdef CONFIG_GPIOLIB
+       struct gpio_chip gpio_chip;
+#endif
 };
 
-static int wm8903_volatile_register(unsigned int reg)
+static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8903_SW_RESET_AND_ID:
        case WM8903_REVISION_NUMBER:
        case WM8903_INTERRUPT_STATUS_1:
        case WM8903_WRITE_SEQUENCER_4:
+       case WM8903_POWER_MANAGEMENT_3:
+       case WM8903_POWER_MANAGEMENT_2:
+       case WM8903_DC_SERVO_READBACK_1:
+       case WM8903_DC_SERVO_READBACK_2:
+       case WM8903_DC_SERVO_READBACK_3:
+       case WM8903_DC_SERVO_READBACK_4:
                return 1;
 
        default:
@@ -246,50 +260,6 @@ static int wm8903_volatile_register(unsigned int reg)
        }
 }
 
-static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
-{
-       u16 reg[5];
-       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-
-       BUG_ON(start > 48);
-
-       /* Enable the sequencer if it's not already on */
-       reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0);
-       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
-                     reg[0] | WM8903_WSEQ_ENA);
-
-       dev_dbg(codec->dev, "Starting sequence at %d\n", start);
-
-       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3,
-                    start | WM8903_WSEQ_START);
-
-       /* Wait for it to complete.  If we have the interrupt wired up then
-        * that will break us out of the poll early.
-        */
-       do {
-               wait_for_completion_timeout(&wm8903->wseq,
-                                           msecs_to_jiffies(10));
-
-               reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4);
-       } while (reg[4] & WM8903_WSEQ_BUSY);
-
-       dev_dbg(codec->dev, "Sequence complete\n");
-
-       /* Disable the sequencer again if we enabled it */
-       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
-
-       return 0;
-}
-
-static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
-{
-       int i;
-
-       /* There really ought to be something better we can do here :/ */
-       for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
-               cache[i] = codec->hw_read(codec, i);
-}
-
 static void wm8903_reset(struct snd_soc_codec *codec)
 {
        snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0);
@@ -297,11 +267,6 @@ static void wm8903_reset(struct snd_soc_codec *codec)
               sizeof(wm8903_reg_defaults));
 }
 
-#define WM8903_OUTPUT_SHORT 0x8
-#define WM8903_OUTPUT_OUT   0x4
-#define WM8903_OUTPUT_INT   0x2
-#define WM8903_OUTPUT_IN    0x1
-
 static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
                           struct snd_kcontrol *kcontrol, int event)
 {
@@ -311,97 +276,101 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-/*
- * Event for headphone and line out amplifier power changes.  Special
- * power up/down sequences are required in order to maximise pop/click
- * performance.
- */
-static int wm8903_output_event(struct snd_soc_dapm_widget *w,
-                              struct snd_kcontrol *kcontrol, int event)
+static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = w->codec;
-       u16 val;
-       u16 reg;
-       u16 dcs_reg;
-       u16 dcs_bit;
-       int shift;
+       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 
-       switch (w->reg) {
-       case WM8903_POWER_MANAGEMENT_2:
-               reg = WM8903_ANALOGUE_HP_0;
-               dcs_bit = 0 + w->shift;
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               wm8903->dcs_pending |= 1 << w->shift;
                break;
-       case WM8903_POWER_MANAGEMENT_3:
-               reg = WM8903_ANALOGUE_LINEOUT_0;
-               dcs_bit = 2 + w->shift;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+                                   1 << w->shift, 0);
                break;
-       default:
-               BUG();
-               return -EINVAL;  /* Spurious warning from some compilers */
        }
 
-       switch (w->shift) {
-       case 0:
-               shift = 0;
-               break;
-       case 1:
-               shift = 4;
-               break;
-       default:
-               BUG();
-               return -EINVAL;  /* Spurious warning from some compilers */
-       }
+       return 0;
+}
 
-       if (event & SND_SOC_DAPM_PRE_PMU) {
-               val = snd_soc_read(codec, reg);
+#define WM8903_DCS_MODE_WRITE_STOP 0
+#define WM8903_DCS_MODE_START_STOP 2
 
-               /* Short the output */
-               val &= ~(WM8903_OUTPUT_SHORT << shift);
-               snd_soc_write(codec, reg, val);
-       }
+static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
+                               enum snd_soc_dapm_type event, int subseq)
+{
+       struct snd_soc_codec *codec = container_of(dapm,
+                                                  struct snd_soc_codec, dapm);
+       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+       int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
+       int i, val;
 
-       if (event & SND_SOC_DAPM_POST_PMU) {
-               val = snd_soc_read(codec, reg);
+       /* Complete any pending DC servo starts */
+       if (wm8903->dcs_pending) {
+               dev_dbg(codec->dev, "Starting DC servo for %x\n",
+                       wm8903->dcs_pending);
 
-               val |= (WM8903_OUTPUT_IN << shift);
-               snd_soc_write(codec, reg, val);
+               /* If we've no cached values then we need to do startup */
+               for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
+                       if (!(wm8903->dcs_pending & (1 << i)))
+                               continue;
 
-               val |= (WM8903_OUTPUT_INT << shift);
-               snd_soc_write(codec, reg, val);
+                       if (wm8903->dcs_cache[i]) {
+                               dev_dbg(codec->dev,
+                                       "Restore DC servo %d value %x\n",
+                                       3 - i, wm8903->dcs_cache[i]);
+
+                               snd_soc_write(codec, WM8903_DC_SERVO_4 + i,
+                                             wm8903->dcs_cache[i] & 0xff);
+                       } else {
+                               dev_dbg(codec->dev,
+                                       "Calibrate DC servo %d\n", 3 - i);
+                               dcs_mode = WM8903_DCS_MODE_START_STOP;
+                       }
+               }
 
-               /* Turn on the output ENA_OUTP */
-               val |= (WM8903_OUTPUT_OUT << shift);
-               snd_soc_write(codec, reg, val);
+               /* Don't trust the cache for analogue */
+               if (wm8903->class_w_users)
+                       dcs_mode = WM8903_DCS_MODE_START_STOP;
 
-               /* Enable the DC servo */
-               dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
-               dcs_reg |= dcs_bit;
-               snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+               snd_soc_update_bits(codec, WM8903_DC_SERVO_2,
+                                   WM8903_DCS_MODE_MASK, dcs_mode);
 
-               /* Remove the short */
-               val |= (WM8903_OUTPUT_SHORT << shift);
-               snd_soc_write(codec, reg, val);
-       }
+               snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+                                   WM8903_DCS_ENA_MASK, wm8903->dcs_pending);
 
-       if (event & SND_SOC_DAPM_PRE_PMD) {
-               val = snd_soc_read(codec, reg);
+               switch (dcs_mode) {
+               case WM8903_DCS_MODE_WRITE_STOP:
+                       break;
 
-               /* Short the output */
-               val &= ~(WM8903_OUTPUT_SHORT << shift);
-               snd_soc_write(codec, reg, val);
+               case WM8903_DCS_MODE_START_STOP:
+                       msleep(270);
 
-               /* Disable the DC servo */
-               dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
-               dcs_reg &= ~dcs_bit;
-               snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+                       /* Cache the measured offsets for digital */
+                       if (wm8903->class_w_users)
+                               break;
 
-               /* Then disable the intermediate and output stages */
-               val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
-                         WM8903_OUTPUT_IN) << shift);
-               snd_soc_write(codec, reg, val);
-       }
+                       for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
+                               if (!(wm8903->dcs_pending & (1 << i)))
+                                       continue;
 
-       return 0;
+                               val = snd_soc_read(codec,
+                                                  WM8903_DC_SERVO_READBACK_1 + i);
+                               dev_dbg(codec->dev, "DC servo %d: %x\n",
+                                       3 - i, val);
+                               wm8903->dcs_cache[i] = val;
+                       }
+                       break;
+
+               default:
+                       pr_warn("DCS mode %d delay not set\n", dcs_mode);
+                       break;
+               }
+
+               wm8903->dcs_pending = 0;
+       }
 }
 
 /*
@@ -667,6 +636,22 @@ static const struct soc_enum lsidetone_enum =
 static const struct soc_enum rsidetone_enum =
        SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
 
+static const char *aif_text[] = {
+       "Left", "Right"
+};
+
+static const struct soc_enum lcapture_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text);
+
+static const struct soc_enum rcapture_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text);
+
+static const struct soc_enum lplay_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text);
+
+static const struct soc_enum rplay_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text);
+
 static const struct snd_kcontrol_new wm8903_snd_controls[] = {
 
 /* Input PGAs - No TLV since the scale depends on PGA mode */
@@ -784,6 +769,18 @@ static const struct snd_kcontrol_new lsidetone_mux =
 static const struct snd_kcontrol_new rsidetone_mux =
        SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
 
+static const struct snd_kcontrol_new lcapture_mux =
+       SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
+
+static const struct snd_kcontrol_new rcapture_mux =
+       SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum);
+
+static const struct snd_kcontrol_new lplay_mux =
+       SOC_DAPM_ENUM("Left Playback Mux", lplay_enum);
+
+static const struct snd_kcontrol_new rplay_mux =
+       SOC_DAPM_ENUM("Right Playback Mux", rplay_enum);
+
 static const struct snd_kcontrol_new left_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
@@ -847,14 +844,26 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
 SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
 SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
 
-SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
-SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux),
+SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
 
 SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux),
 SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux),
 
-SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
-SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux),
+SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0),
 
 SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0,
                   left_output_mixer, ARRAY_SIZE(left_output_mixer)),
@@ -866,23 +875,45 @@ SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0,
 SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
                   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
 
-SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
-                  1, 0, NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
-SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
-                  0, 0, NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
-
-SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
-                  NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
-SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
-                  NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0,
+                  4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0,
+                  0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 4, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 0, 0,
+                  NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 5, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0,
+                  NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
                 NULL, 0),
@@ -892,10 +923,18 @@ SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
 SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0,
                    wm8903_cp_event, SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
 
+       { "CLK_DSP", NULL, "CLK_SYS" },
+       { "Mic Bias", NULL, "CLK_SYS" },
+       { "HPL_DCS", NULL, "CLK_SYS" },
+       { "HPR_DCS", NULL, "CLK_SYS" },
+       { "LINEOUTL_DCS", NULL, "CLK_SYS" },
+       { "LINEOUTR_DCS", NULL, "CLK_SYS" },
+
        { "Left Input Mux", "IN1L", "IN1L" },
        { "Left Input Mux", "IN2L", "IN2L" },
        { "Left Input Mux", "IN3L", "IN3L" },
@@ -936,18 +975,36 @@ static const struct snd_soc_dapm_route intercon[] = {
        { "Left Input PGA", NULL, "Left Input Mode Mux" },
        { "Right Input PGA", NULL, "Right Input Mode Mux" },
 
+       { "Left Capture Mux", "Left", "ADCL" },
+       { "Left Capture Mux", "Right", "ADCR" },
+
+       { "Right Capture Mux", "Left", "ADCL" },
+       { "Right Capture Mux", "Right", "ADCR" },
+
+       { "AIFTXL", NULL, "Left Capture Mux" },
+       { "AIFTXR", NULL, "Right Capture Mux" },
+
        { "ADCL", NULL, "Left Input PGA" },
        { "ADCL", NULL, "CLK_DSP" },
        { "ADCR", NULL, "Right Input PGA" },
        { "ADCR", NULL, "CLK_DSP" },
 
+       { "Left Playback Mux", "Left", "AIFRXL" },
+       { "Left Playback Mux", "Right", "AIFRXR" },
+
+       { "Right Playback Mux", "Left", "AIFRXL" },
+       { "Right Playback Mux", "Right", "AIFRXR" },
+
        { "DACL Sidetone", "Left", "ADCL" },
        { "DACL Sidetone", "Right", "ADCR" },
        { "DACR Sidetone", "Left", "ADCL" },
        { "DACR Sidetone", "Right", "ADCR" },
 
+       { "DACL", NULL, "Left Playback Mux" },
        { "DACL", NULL, "DACL Sidetone" },
        { "DACL", NULL, "CLK_DSP" },
+
+       { "DACR", NULL, "Right Playback Mux" },
        { "DACR", NULL, "DACR Sidetone" },
        { "DACR", NULL, "CLK_DSP" },
 
@@ -980,11 +1037,35 @@ static const struct snd_soc_dapm_route intercon[] = {
        { "Left Speaker PGA", NULL, "Left Speaker Mixer" },
        { "Right Speaker PGA", NULL, "Right Speaker Mixer" },
 
-       { "HPOUTL", NULL, "Left Headphone Output PGA" },
-       { "HPOUTR", NULL, "Right Headphone Output PGA" },
+       { "HPL_ENA_DLY", NULL, "Left Headphone Output PGA" },
+       { "HPR_ENA_DLY", NULL, "Right Headphone Output PGA" },
+       { "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" },
+       { "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" },
+
+       { "HPL_DCS", NULL, "DCS Master" },
+       { "HPR_DCS", NULL, "DCS Master" },
+       { "LINEOUTL_DCS", NULL, "DCS Master" },
+       { "LINEOUTR_DCS", NULL, "DCS Master" },
+
+       { "HPL_DCS", NULL, "HPL_ENA_DLY" },
+       { "HPR_DCS", NULL, "HPR_ENA_DLY" },
+       { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" },
+       { "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" },
 
-       { "LINEOUTL", NULL, "Left Line Output PGA" },
-       { "LINEOUTR", NULL, "Right Line Output PGA" },
+       { "HPL_ENA_OUTP", NULL, "HPL_DCS" },
+       { "HPR_ENA_OUTP", NULL, "HPR_DCS" },
+       { "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" },
+       { "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" },
+
+       { "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" },
+       { "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" },
+       { "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" },
+       { "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" },
+
+       { "HPOUTL", NULL, "HPL_RMV_SHORT" },
+       { "HPOUTR", NULL, "HPR_RMV_SHORT" },
+       { "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" },
+       { "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" },
 
        { "LOP", NULL, "Left Speaker PGA" },
        { "LON", NULL, "Left Speaker PGA" },
@@ -1012,29 +1093,71 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec)
 static int wm8903_set_bias_level(struct snd_soc_codec *codec,
                                 enum snd_soc_bias_level level)
 {
-       u16 reg;
-
        switch (level) {
        case SND_SOC_BIAS_ON:
+               break;
+
        case SND_SOC_BIAS_PREPARE:
-               reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
-               reg &= ~(WM8903_VMID_RES_MASK);
-               reg |= WM8903_VMID_RES_50K;
-               snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
+               snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                   WM8903_VMID_RES_MASK,
+                                   WM8903_VMID_RES_50K);
                break;
 
        case SND_SOC_BIAS_STANDBY:
                if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
-                       snd_soc_write(codec, WM8903_CLOCK_RATES_2,
-                                    WM8903_CLK_SYS_ENA);
-
-                       /* Change DC servo dither level in startup sequence */
-                       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
-                       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
-                       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
-
-                       wm8903_run_sequence(codec, 0);
-                       wm8903_sync_reg_cache(codec, codec->reg_cache);
+                       snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+                                           WM8903_POBCTRL | WM8903_ISEL_MASK |
+                                           WM8903_STARTUP_BIAS_ENA |
+                                           WM8903_BIAS_ENA,
+                                           WM8903_POBCTRL |
+                                           (2 << WM8903_ISEL_SHIFT) |
+                                           WM8903_STARTUP_BIAS_ENA);
+
+                       snd_soc_update_bits(codec,
+                                           WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
+                                           WM8903_SPK_DISCHARGE,
+                                           WM8903_SPK_DISCHARGE);
+
+                       msleep(33);
+
+                       snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+                                           WM8903_SPKL_ENA | WM8903_SPKR_ENA,
+                                           WM8903_SPKL_ENA | WM8903_SPKR_ENA);
+
+                       snd_soc_update_bits(codec,
+                                           WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
+                                           WM8903_SPK_DISCHARGE, 0);
+
+                       snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                           WM8903_VMID_TIE_ENA |
+                                           WM8903_BUFIO_ENA |
+                                           WM8903_VMID_IO_ENA |
+                                           WM8903_VMID_SOFT_MASK |
+                                           WM8903_VMID_RES_MASK |
+                                           WM8903_VMID_BUF_ENA,
+                                           WM8903_VMID_TIE_ENA |
+                                           WM8903_BUFIO_ENA |
+                                           WM8903_VMID_IO_ENA |
+                                           (2 << WM8903_VMID_SOFT_SHIFT) |
+                                           WM8903_VMID_RES_250K |
+                                           WM8903_VMID_BUF_ENA);
+
+                       msleep(129);
+
+                       snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+                                           WM8903_SPKL_ENA | WM8903_SPKR_ENA,
+                                           0);
+
+                       snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                           WM8903_VMID_SOFT_MASK, 0);
+
+                       snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                           WM8903_VMID_RES_MASK,
+                                           WM8903_VMID_RES_50K);
+
+                       snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+                                           WM8903_BIAS_ENA | WM8903_POBCTRL,
+                                           WM8903_BIAS_ENA);
 
                        /* By default no bypass paths are enabled so
                         * enable Class W support.
@@ -1047,17 +1170,32 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
                                            WM8903_CP_DYN_V);
                }
 
-               reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
-               reg &= ~(WM8903_VMID_RES_MASK);
-               reg |= WM8903_VMID_RES_250K;
-               snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
+               snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                   WM8903_VMID_RES_MASK,
+                                   WM8903_VMID_RES_250K);
                break;
 
        case SND_SOC_BIAS_OFF:
-               wm8903_run_sequence(codec, 32);
-               reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2);
-               reg &= ~WM8903_CLK_SYS_ENA;
-               snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg);
+               snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+                                   WM8903_BIAS_ENA, 0);
+
+               snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                   WM8903_VMID_SOFT_MASK,
+                                   2 << WM8903_VMID_SOFT_SHIFT);
+
+               snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                   WM8903_VMID_BUF_ENA, 0);
+
+               msleep(290);
+
+               snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+                                   WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA |
+                                   WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK |
+                                   WM8903_VMID_SOFT_MASK |
+                                   WM8903_VMID_BUF_ENA, 0);
+
+               snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+                                   WM8903_STARTUP_BIAS_ENA, 0);
                break;
        }
 
@@ -1510,8 +1648,7 @@ static irqreturn_t wm8903_irq(int irq, void *data)
        int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask;
 
        if (int_val & WM8903_WSEQ_BUSY_EINT) {
-               dev_dbg(codec->dev, "Write sequencer done\n");
-               complete(&wm8903->wseq);
+               dev_warn(codec->dev, "Write sequencer done\n");
        }
 
        /*
@@ -1635,6 +1772,120 @@ static int wm8903_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip)
+{
+       return container_of(chip, struct wm8903_priv, gpio_chip);
+}
+
+static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+       if (offset >= WM8903_NUM_GPIO)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+       struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+       struct snd_soc_codec *codec = wm8903->codec;
+       unsigned int mask, val;
+
+       mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK;
+       val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
+               WM8903_GP1_DIR;
+
+       return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+                                  mask, val);
+}
+
+static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+       struct snd_soc_codec *codec = wm8903->codec;
+       int reg;
+
+       reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset);
+
+       return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
+}
+
+static int wm8903_gpio_direction_out(struct gpio_chip *chip,
+                                    unsigned offset, int value)
+{
+       struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+       struct snd_soc_codec *codec = wm8903->codec;
+       unsigned int mask, val;
+
+       mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK;
+       val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
+               (value << WM8903_GP2_LVL_SHIFT);
+
+       return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+                                  mask, val);
+}
+
+static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+       struct snd_soc_codec *codec = wm8903->codec;
+
+       snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+                           WM8903_GP1_LVL_MASK,
+                           !!value << WM8903_GP1_LVL_SHIFT);
+}
+
+static struct gpio_chip wm8903_template_chip = {
+       .label                  = "wm8903",
+       .owner                  = THIS_MODULE,
+       .request                = wm8903_gpio_request,
+       .direction_input        = wm8903_gpio_direction_in,
+       .get                    = wm8903_gpio_get,
+       .direction_output       = wm8903_gpio_direction_out,
+       .set                    = wm8903_gpio_set,
+       .can_sleep              = 1,
+};
+
+static void wm8903_init_gpio(struct snd_soc_codec *codec)
+{
+       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+       struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
+       int ret;
+
+       wm8903->gpio_chip = wm8903_template_chip;
+       wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
+       wm8903->gpio_chip.dev = codec->dev;
+
+       if (pdata && pdata->gpio_base)
+               wm8903->gpio_chip.base = pdata->gpio_base;
+       else
+               wm8903->gpio_chip.base = -1;
+
+       ret = gpiochip_add(&wm8903->gpio_chip);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8903_free_gpio(struct snd_soc_codec *codec)
+{
+       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = gpiochip_remove(&wm8903->gpio_chip);
+       if (ret != 0)
+               dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8903_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8903_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 static int wm8903_probe(struct snd_soc_codec *codec)
 {
        struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
@@ -1643,7 +1894,7 @@ static int wm8903_probe(struct snd_soc_codec *codec)
        int trigger, irq_pol;
        u16 val;
 
-       init_completion(&wm8903->wseq);
+       wm8903->codec = codec;
 
        ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
        if (ret != 0) {
@@ -1659,19 +1910,33 @@ static int wm8903_probe(struct snd_soc_codec *codec)
        }
 
        val = snd_soc_read(codec, WM8903_REVISION_NUMBER);
-       dev_info(codec->dev, "WM8903 revision %d\n",
-                val & WM8903_CHIP_REV_MASK);
+       dev_info(codec->dev, "WM8903 revision %c\n",
+                (val & WM8903_CHIP_REV_MASK) + 'A');
 
        wm8903_reset(codec);
 
        /* Set up GPIOs and microphone detection */
        if (pdata) {
+               bool mic_gpio = false;
+
                for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
-                       if (!pdata->gpio_cfg[i])
+                       if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG)
                                continue;
 
                        snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i,
                                      pdata->gpio_cfg[i] & 0xffff);
+
+                       val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK)
+                               >> WM8903_GP1_FN_SHIFT;
+
+                       switch (val) {
+                       case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT:
+                       case WM8903_GPn_FN_MICBIAS_SHORT_DETECT:
+                               mic_gpio = true;
+                               break;
+                       default:
+                               break;
+                       }
                }
 
                snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0,
@@ -1682,6 +1947,14 @@ static int wm8903_probe(struct snd_soc_codec *codec)
                        snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
                                            WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
 
+               /* If microphone detection is enabled by pdata but
+                * detected via IRQ then interrupts can be lost before
+                * the machine driver has set up microphone detection
+                * IRQs as the IRQs are clear on read.  The detection
+                * will be enabled when the machine driver configures.
+                */
+               WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA));
+
                wm8903->mic_delay = pdata->micdet_delay;
        }
        
@@ -1741,20 +2014,23 @@ static int wm8903_probe(struct snd_soc_codec *codec)
        snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
 
        /* Enable DAC soft mute by default */
-       val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
-       val |= WM8903_DAC_MUTEMODE;
-       snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);
+       snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1,
+                           WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
+                           WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
 
        snd_soc_add_controls(codec, wm8903_snd_controls,
                                ARRAY_SIZE(wm8903_snd_controls));
        wm8903_add_widgets(codec);
 
+       wm8903_init_gpio(codec);
+
        return ret;
 }
 
 /* power down chip */
 static int wm8903_remove(struct snd_soc_codec *codec)
 {
+       wm8903_free_gpio(codec);
        wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
@@ -1769,6 +2045,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_default = wm8903_reg_defaults,
        .volatile_register = wm8903_volatile_register,
+       .seq_notifier = wm8903_seq_notifier,
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -1807,7 +2084,7 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
 
 static struct i2c_driver wm8903_i2c_driver = {
        .driver = {
-               .name = "wm8903-codec",
+               .name = "wm8903",
                .owner = THIS_MODULE,
        },
        .probe =    wm8903_i2c_probe,
index e3ec2433b2159eb327b4668c3a0c026d8c717e13..db949311c0f206c401f70839a4f318f3fc8557e4 100644 (file)
@@ -75,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec,
 #define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0    0x41
 #define WM8903_DC_SERVO_0                       0x43
 #define WM8903_DC_SERVO_2                       0x45
+#define WM8903_DC_SERVO_4                      0x47
+#define WM8903_DC_SERVO_5                      0x48
+#define WM8903_DC_SERVO_6                      0x49
+#define WM8903_DC_SERVO_7                      0x4A
+#define WM8903_DC_SERVO_READBACK_1             0x51
+#define WM8903_DC_SERVO_READBACK_2             0x52
+#define WM8903_DC_SERVO_READBACK_3             0x53
+#define WM8903_DC_SERVO_READBACK_4             0x54
 #define WM8903_ANALOGUE_HP_0                    0x5A
 #define WM8903_ANALOGUE_LINEOUT_0               0x5E
 #define WM8903_CHARGE_PUMP_0                    0x62
index 9de44a4c05c044742cb02b8d53f9471cffd41646..443ae580445cb7f207029a5711eba52a5e49f157 100644 (file)
@@ -596,7 +596,7 @@ static struct {
        { 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */
 };
 
-static int wm8904_volatile_register(unsigned int reg)
+static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        return wm8904_access[reg].vol;
 }
@@ -2436,19 +2436,28 @@ static int wm8904_probe(struct snd_soc_codec *codec)
        }
 
        /* Change some default settings - latch VU and enable ZC */
-       reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
-       reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
-       reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
-       reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
-       reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
-               WM8904_HPOUTLZC;
-       reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
-               WM8904_HPOUTRZC;
-       reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
-               WM8904_LINEOUTLZC;
-       reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
-               WM8904_LINEOUTRZC;
-       reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
+       snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT,
+                           WM8904_ADC_VU, WM8904_ADC_VU);
+       snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
+                           WM8904_ADC_VU, WM8904_ADC_VU);
+       snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT,
+                           WM8904_DAC_VU, WM8904_DAC_VU);
+       snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
+                           WM8904_DAC_VU, WM8904_DAC_VU);
+       snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT,
+                           WM8904_HPOUT_VU | WM8904_HPOUTLZC,
+                           WM8904_HPOUT_VU | WM8904_HPOUTLZC);
+       snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT,
+                           WM8904_HPOUT_VU | WM8904_HPOUTRZC,
+                           WM8904_HPOUT_VU | WM8904_HPOUTRZC);
+       snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT,
+                           WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
+                           WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
+       snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT,
+                           WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
+                           WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
+       snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0,
+                           WM8904_SR_MODE, 0);
 
        /* Apply configuration from the platform data. */
        if (wm8904->pdata) {
@@ -2469,10 +2478,12 @@ static int wm8904_probe(struct snd_soc_codec *codec)
        /* Set Class W by default - this will be managed by the Class
         * G widget at runtime where bypass paths are available.
         */
-       reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
+       snd_soc_update_bits(codec, WM8904_CLASS_W_0,
+                           WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
 
        /* Use normal bias source */
-       reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
+       snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+                           WM8904_POBCTRL, 0);
 
        wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
index 7167dfc96aa7558c7c36d8a8983eb9c9ee5df846..5e0214d6293ea57672ebc54c427470e50768e4ca 100644 (file)
@@ -934,16 +934,27 @@ static int wm8955_probe(struct snd_soc_codec *codec)
        }
 
        /* Change some default settings - latch VU and enable ZC */
-       reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
-       reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
-       reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
-       reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
-       reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
-       reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
-       reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
+       snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
+                           WM8955_LDVU, WM8955_LDVU);
+       snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
+                           WM8955_RDVU, WM8955_RDVU);
+       snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
+                           WM8955_LO1VU | WM8955_LO1ZC,
+                           WM8955_LO1VU | WM8955_LO1ZC);
+       snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
+                           WM8955_RO1VU | WM8955_RO1ZC,
+                           WM8955_RO1VU | WM8955_RO1ZC);
+       snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
+                           WM8955_LO2VU | WM8955_LO2ZC,
+                           WM8955_LO2VU | WM8955_LO2ZC);
+       snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
+                           WM8955_RO2VU | WM8955_RO2ZC,
+                           WM8955_RO2VU | WM8955_RO2ZC);
+       snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
+                           WM8955_MOZC, WM8955_MOZC);
 
        /* Also enable adaptive bass boost by default */
-       reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
+       snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
 
        /* Set platform data values */
        if (pdata) {
index 55252e7d02c99a1e3d81c8f27096406fa74f62b9..cdee8103d09b323efebfee47497d87dbe525f8ac 100644 (file)
@@ -291,7 +291,7 @@ struct wm8961_priv {
        int sysclk;
 };
 
-static int wm8961_volatile_register(unsigned int reg)
+static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8961_SOFTWARE_RESET:
index b9cb1fcf8c92a650f74d74ead1fca07e34848074..3b71dd65c966c34e0643ae5d9732769b110f86a6 100644 (file)
@@ -1938,7 +1938,7 @@ static const struct wm8962_reg_access {
        [21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */
 };
 
-static int wm8962_volatile_register(unsigned int reg)
+static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        if (wm8962_reg_access[reg].vol)
                return 1;
@@ -1946,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg)
                return 0;
 }
 
-static int wm8962_readable_register(unsigned int reg)
+static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        if (wm8962_reg_access[reg].read)
                return 1;
@@ -3635,7 +3635,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
        struct snd_soc_codec *codec = wm8962->codec;
 
        snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
-                           WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT);
+                           WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT);
 }
 
 static int wm8962_gpio_direction_out(struct gpio_chip *chip,
@@ -3822,16 +3822,26 @@ static int wm8962_probe(struct snd_soc_codec *codec)
        }
 
        /* Latch volume update bits */
-       reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU;
-       reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU;
-       reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU;
-       reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU;
-       reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU;
-       reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU;
-       reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU;
-       reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU;
-       reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU;
-       reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU;
+       snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
+                           WM8962_IN_VU, WM8962_IN_VU);
+       snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME,
+                           WM8962_IN_VU, WM8962_IN_VU);
+       snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME,
+                           WM8962_ADC_VU, WM8962_ADC_VU);
+       snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME,
+                           WM8962_ADC_VU, WM8962_ADC_VU);
+       snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME,
+                           WM8962_DAC_VU, WM8962_DAC_VU);
+       snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME,
+                           WM8962_DAC_VU, WM8962_DAC_VU);
+       snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME,
+                           WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+       snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME,
+                           WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+       snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME,
+                           WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+       snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
+                           WM8962_HPOUT_VU, WM8962_HPOUT_VU);
 
        wm8962_add_widgets(codec);
 
index 8dfb0a0da67393a9909897375c20dc735ac727a3..85e3e630e763ca841ecd51e40cd66ab7733bcf8d 100644 (file)
@@ -93,6 +93,7 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
 static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
 static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
 
 static const struct snd_kcontrol_new wm8978_snd_controls[] = {
 
@@ -144,8 +145,8 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
 
        SOC_SINGLE("DAC Playback Limiter Threshold",
                WM8978_DAC_LIMITER_2, 4, 7, 0),
-       SOC_SINGLE("DAC Playback Limiter Boost",
-               WM8978_DAC_LIMITER_2, 0, 12, 0),
+       SOC_SINGLE_TLV("DAC Playback Limiter Volume",
+               WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
 
        SOC_ENUM("ALC Enable Switch", alc1),
        SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0),
@@ -967,7 +968,7 @@ static int wm8978_probe(struct snd_soc_codec *codec)
         * written.
         */
        for (i = 0; i < ARRAY_SIZE(update_reg); i++)
-               ((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
+               snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
 
        /* Reset the codec */
        ret = snd_soc_write(codec, WM8978_RESET, 0);
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
new file mode 100644 (file)
index 0000000..28fdfd6
--- /dev/null
@@ -0,0 +1,1427 @@
+/*
+ * wm8991.c  --  WM8991 ALSA Soc Audio driver
+ *
+ * Copyright 2007-2010 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         linux@wolfsonmicro.com
+ *
+ *  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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8991.h"
+
+struct wm8991_priv {
+       enum snd_soc_control_type control_type;
+       unsigned int pcmclk;
+};
+
+static const u16 wm8991_reg_defs[] = {
+       0x8991,     /* R0  - Reset */
+       0x0000,     /* R1  - Power Management (1) */
+       0x6000,     /* R2  - Power Management (2) */
+       0x0000,     /* R3  - Power Management (3) */
+       0x4050,     /* R4  - Audio Interface (1) */
+       0x4000,     /* R5  - Audio Interface (2) */
+       0x01C8,     /* R6  - Clocking (1) */
+       0x0000,     /* R7  - Clocking (2) */
+       0x0040,     /* R8  - Audio Interface (3) */
+       0x0040,     /* R9  - Audio Interface (4) */
+       0x0004,     /* R10 - DAC CTRL */
+       0x00C0,     /* R11 - Left DAC Digital Volume */
+       0x00C0,     /* R12 - Right DAC Digital Volume */
+       0x0000,     /* R13 - Digital Side Tone */
+       0x0100,     /* R14 - ADC CTRL */
+       0x00C0,     /* R15 - Left ADC Digital Volume */
+       0x00C0,     /* R16 - Right ADC Digital Volume */
+       0x0000,     /* R17 */
+       0x0000,     /* R18 - GPIO CTRL 1 */
+       0x1000,     /* R19 - GPIO1 & GPIO2 */
+       0x1010,     /* R20 - GPIO3 & GPIO4 */
+       0x1010,     /* R21 - GPIO5 & GPIO6 */
+       0x8000,     /* R22 - GPIOCTRL 2 */
+       0x0800,     /* R23 - GPIO_POL */
+       0x008B,     /* R24 - Left Line Input 1&2 Volume */
+       0x008B,     /* R25 - Left Line Input 3&4 Volume */
+       0x008B,     /* R26 - Right Line Input 1&2 Volume */
+       0x008B,     /* R27 - Right Line Input 3&4 Volume */
+       0x0000,     /* R28 - Left Output Volume */
+       0x0000,     /* R29 - Right Output Volume */
+       0x0066,     /* R30 - Line Outputs Volume */
+       0x0022,     /* R31 - Out3/4 Volume */
+       0x0079,     /* R32 - Left OPGA Volume */
+       0x0079,     /* R33 - Right OPGA Volume */
+       0x0003,     /* R34 - Speaker Volume */
+       0x0003,     /* R35 - ClassD1 */
+       0x0000,     /* R36 */
+       0x0100,     /* R37 - ClassD3 */
+       0x0000,     /* R38 */
+       0x0000,     /* R39 - Input Mixer1 */
+       0x0000,     /* R40 - Input Mixer2 */
+       0x0000,     /* R41 - Input Mixer3 */
+       0x0000,     /* R42 - Input Mixer4 */
+       0x0000,     /* R43 - Input Mixer5 */
+       0x0000,     /* R44 - Input Mixer6 */
+       0x0000,     /* R45 - Output Mixer1 */
+       0x0000,     /* R46 - Output Mixer2 */
+       0x0000,     /* R47 - Output Mixer3 */
+       0x0000,     /* R48 - Output Mixer4 */
+       0x0000,     /* R49 - Output Mixer5 */
+       0x0000,     /* R50 - Output Mixer6 */
+       0x0180,     /* R51 - Out3/4 Mixer */
+       0x0000,     /* R52 - Line Mixer1 */
+       0x0000,     /* R53 - Line Mixer2 */
+       0x0000,     /* R54 - Speaker Mixer */
+       0x0000,     /* R55 - Additional Control */
+       0x0000,     /* R56 - AntiPOP1 */
+       0x0000,     /* R57 - AntiPOP2 */
+       0x0000,     /* R58 - MICBIAS */
+       0x0000,     /* R59 */
+       0x0008,     /* R60 - PLL1 */
+       0x0031,     /* R61 - PLL2 */
+       0x0026,     /* R62 - PLL3 */
+};
+
+#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0)
+
+static const unsigned int rec_mix_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 7, TLV_DB_LINEAR_ITEM(-1500, 600),
+};
+
+static const unsigned int in_pga_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000),
+};
+
+static const unsigned int out_mix_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 7, TLV_DB_LINEAR_ITEM(0, -2100),
+};
+
+static const unsigned int out_pga_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 127, TLV_DB_LINEAR_ITEM(-7300, 600),
+};
+
+static const unsigned int out_omix_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 7, TLV_DB_LINEAR_ITEM(-600, 0),
+};
+
+static const unsigned int out_dac_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 255, TLV_DB_LINEAR_ITEM(-7163, 0),
+};
+
+static const unsigned int in_adc_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763),
+};
+
+static const unsigned int out_sidetone_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 31, TLV_DB_LINEAR_ITEM(-3600, 0),
+};
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int reg = kcontrol->private_value & 0xff;
+       int ret;
+       u16 val;
+
+       ret = snd_soc_put_volsw(kcontrol, ucontrol);
+       if (ret < 0)
+               return ret;
+
+       /* now hit the volume update bits (always bit 8) */
+       val = snd_soc_read(codec, reg);
+       return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+static const char *wm8991_digital_sidetone[] =
+{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8991_left_digital_sidetone_enum =
+       SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+                       WM8991_ADC_TO_DACL_SHIFT,
+                       WM8991_ADC_TO_DACL_MASK,
+                       wm8991_digital_sidetone);
+
+static const struct soc_enum wm8991_right_digital_sidetone_enum =
+       SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+                       WM8991_ADC_TO_DACR_SHIFT,
+                       WM8991_ADC_TO_DACR_MASK,
+                       wm8991_digital_sidetone);
+
+static const char *wm8991_adcmode[] =
+{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8991_right_adcmode_enum =
+       SOC_ENUM_SINGLE(WM8991_ADC_CTRL,
+                       WM8991_ADC_HPF_CUT_SHIFT,
+                       WM8991_ADC_HPF_CUT_MASK,
+                       wm8991_adcmode);
+
+static const struct snd_kcontrol_new wm8991_snd_controls[] = {
+       /* INMIXL */
+       SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0),
+       SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0),
+       /* INMIXR */
+       SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0),
+       SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0),
+
+       /* LOMIX */
+       SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3,
+               WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+               WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+               WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5,
+               WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+               WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+               WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+       /* ROMIX */
+       SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4,
+               WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+               WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+               WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6,
+               WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+               WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv),
+       SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+               WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv),
+
+       /* LOUT */
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME,
+               WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv),
+       SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0),
+
+       /* ROUT */
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME,
+               WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv),
+       SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0),
+
+       /* LOPGA */
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME,
+               WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv),
+       SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME,
+               WM8991_LOPGAZC_BIT, 1, 0),
+
+       /* ROPGA */
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME,
+               WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv),
+       SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME,
+               WM8991_ROPGAZC_BIT, 1, 0),
+
+       SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+               WM8991_LONMUTE_BIT, 1, 0),
+       SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+               WM8991_LOPMUTE_BIT, 1, 0),
+       SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+               WM8991_LOATTN_BIT, 1, 0),
+       SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+               WM8991_RONMUTE_BIT, 1, 0),
+       SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+               WM8991_ROPMUTE_BIT, 1, 0),
+       SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+               WM8991_ROATTN_BIT, 1, 0),
+
+       SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME,
+               WM8991_OUT3MUTE_BIT, 1, 0),
+       SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+               WM8991_OUT3ATTN_BIT, 1, 0),
+
+       SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME,
+               WM8991_OUT4MUTE_BIT, 1, 0),
+       SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+               WM8991_OUT4ATTN_BIT, 1, 0),
+
+       SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1,
+               WM8991_CDMODE_BIT, 1, 0),
+
+       SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME,
+               WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0),
+       SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3,
+               WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0),
+       SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3,
+               WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+               WM8991_LEFT_DAC_DIGITAL_VOLUME,
+               WM8991_DACL_VOL_SHIFT,
+               WM8991_DACL_VOL_MASK,
+               0,
+               out_dac_tlv),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+               WM8991_RIGHT_DAC_DIGITAL_VOLUME,
+               WM8991_DACR_VOL_SHIFT,
+               WM8991_DACR_VOL_MASK,
+               0,
+               out_dac_tlv),
+
+       SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum),
+       SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum),
+
+       SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+               WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0,
+               out_sidetone_tlv),
+       SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+               WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0,
+               out_sidetone_tlv),
+
+       SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL,
+               WM8991_ADC_HPF_ENA_BIT, 1, 0),
+
+       SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+               WM8991_LEFT_ADC_DIGITAL_VOLUME,
+               WM8991_ADCL_VOL_SHIFT,
+               WM8991_ADCL_VOL_MASK,
+               0,
+               in_adc_tlv),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+               WM8991_RIGHT_ADC_DIGITAL_VOLUME,
+               WM8991_ADCR_VOL_SHIFT,
+               WM8991_ADCR_VOL_MASK,
+               0,
+               in_adc_tlv),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+               WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+               WM8991_LIN12VOL_SHIFT,
+               WM8991_LIN12VOL_MASK,
+               0,
+               in_pga_tlv),
+
+       SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+               WM8991_LI12ZC_BIT, 1, 0),
+
+       SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+               WM8991_LI12MUTE_BIT, 1, 0),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+               WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+               WM8991_LIN34VOL_SHIFT,
+               WM8991_LIN34VOL_MASK,
+               0,
+               in_pga_tlv),
+
+       SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+               WM8991_LI34ZC_BIT, 1, 0),
+
+       SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+               WM8991_LI34MUTE_BIT, 1, 0),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+               WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+               WM8991_RIN12VOL_SHIFT,
+               WM8991_RIN12VOL_MASK,
+               0,
+               in_pga_tlv),
+
+       SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+               WM8991_RI12ZC_BIT, 1, 0),
+
+       SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+               WM8991_RI12MUTE_BIT, 1, 0),
+
+       SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+               WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+               WM8991_RIN34VOL_SHIFT,
+               WM8991_RIN34VOL_MASK,
+               0,
+               in_pga_tlv),
+
+       SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+               WM8991_RI34ZC_BIT, 1, 0),
+
+       SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+               WM8991_RI34MUTE_BIT, 1, 0),
+};
+
+/*
+ * _DAPM_ Controls
+ */
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       u16 reg, fakepower;
+
+       reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2);
+       fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS);
+
+       if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) |
+                        (1 << WM8991_AINLMUX_PWR_BIT)))
+               reg |= WM8991_AINL_ENA;
+       else
+               reg &= ~WM8991_AINL_ENA;
+
+       if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) |
+                        (1 << WM8991_AINRMUX_PWR_BIT)))
+               reg |= WM8991_AINR_ENA;
+       else
+               reg &= ~WM8991_AINL_ENA;
+
+       snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg);
+       return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+                         struct snd_kcontrol *kcontrol, int event)
+{
+       u32 reg_shift = kcontrol->private_value & 0xfff;
+       int ret = 0;
+       u16 reg;
+
+       switch (reg_shift) {
+       case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
+               reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER1);
+               if (reg & WM8991_LDLO) {
+                       printk(KERN_WARNING
+                              "Cannot set as Output Mixer 1 LDLO Set\n");
+                       ret = -1;
+               }
+               break;
+
+       case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
+               reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER2);
+               if (reg & WM8991_RDRO) {
+                       printk(KERN_WARNING
+                              "Cannot set as Output Mixer 2 RDRO Set\n");
+                       ret = -1;
+               }
+               break;
+
+       case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
+               reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+               if (reg & WM8991_LDSPK) {
+                       printk(KERN_WARNING
+                              "Cannot set as Speaker Mixer LDSPK Set\n");
+                       ret = -1;
+               }
+               break;
+
+       case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
+               reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+               if (reg & WM8991_RDSPK) {
+                       printk(KERN_WARNING
+                              "Cannot set as Speaker Mixer RDSPK Set\n");
+                       ret = -1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+       TLV_DB_RANGE_HEAD(1),
+       0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
+       SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = {
+       SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = {
+       SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0),
+       SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = {
+       SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0),
+       SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = {
+       SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3,
+               WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv),
+       SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT,
+               7, 0, in_mix_tlv),
+       SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+               1, 0),
+       SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+               1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
+       SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4,
+               WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv),
+       SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT,
+               7, 0, in_mix_tlv),
+       SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+               1, 0),
+       SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+               1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8991_ainlmux[] =
+{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8991_ainlmux_enum =
+       SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
+                       ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
+       SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8991_ainrmux[] =
+{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8991_ainrmux_enum =
+       SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
+                       ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
+       SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = {
+       SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT,
+               WM8991_LR4BVOL_MASK, 0, in_mix_tlv),
+       SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT,
+               WM8991_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = {
+       SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LRBLO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LLBLO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LRI3LO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LLI3LO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LR12LO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LL12LO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1,
+               WM8991_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = {
+       SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RLBRO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RRBRO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RLI3RO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RRI3RO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RL12RO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RR12RO_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2,
+               WM8991_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = {
+       SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+               WM8991_LLOPGALON_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1,
+               WM8991_LROPGALON_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1,
+               WM8991_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = {
+       SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1,
+               WM8991_LR12LOP_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1,
+               WM8991_LL12LOP_BIT, 1, 0),
+       SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+               WM8991_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = {
+       SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+               WM8991_RROPGARON_BIT, 1, 0),
+       SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2,
+               WM8991_RLOPGARON_BIT, 1, 0),
+       SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2,
+               WM8991_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = {
+       SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2,
+               WM8991_RL12ROP_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2,
+               WM8991_RR12ROP_BIT, 1, 0),
+       SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+               WM8991_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = {
+       SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER,
+               WM8991_LI4O3_BIT, 1, 0),
+       SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER,
+               WM8991_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = {
+       SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER,
+               WM8991_RPGAO4_BIT, 1, 0),
+       SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER,
+               WM8991_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = {
+       SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+               WM8991_LI2SPK_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER,
+               WM8991_LB2SPK_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+               WM8991_LOPGASPK_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER,
+               WM8991_LDSPK_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER,
+               WM8991_RDSPK_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+               WM8991_ROPGASPK_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER,
+               WM8991_RL12ROP_BIT, 1, 0),
+       SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+               WM8991_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
+       /* Input Side */
+       /* Input Lines */
+       SND_SOC_DAPM_INPUT("LIN1"),
+       SND_SOC_DAPM_INPUT("LIN2"),
+       SND_SOC_DAPM_INPUT("LIN3"),
+       SND_SOC_DAPM_INPUT("LIN4RXN"),
+       SND_SOC_DAPM_INPUT("RIN3"),
+       SND_SOC_DAPM_INPUT("RIN4RXP"),
+       SND_SOC_DAPM_INPUT("RIN1"),
+       SND_SOC_DAPM_INPUT("RIN2"),
+       SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+       /* DACs */
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2,
+               WM8991_ADCL_ENA_BIT, 0),
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2,
+               WM8991_ADCR_ENA_BIT, 0),
+
+       /* Input PGAs */
+       SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT,
+               0, &wm8991_dapm_lin12_pga_controls[0],
+               ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)),
+       SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT,
+               0, &wm8991_dapm_lin34_pga_controls[0],
+               ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)),
+       SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT,
+               0, &wm8991_dapm_rin12_pga_controls[0],
+               ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)),
+       SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT,
+               0, &wm8991_dapm_rin34_pga_controls[0],
+               ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),
+
+       /* INMIXL */
+       SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0,
+               &wm8991_dapm_inmixl_controls[0],
+               ARRAY_SIZE(wm8991_dapm_inmixl_controls),
+               inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* AINLMUX */
+       SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0,
+               &wm8991_dapm_ainlmux_controls, inmixer_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* INMIXR */
+       SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0,
+               &wm8991_dapm_inmixr_controls[0],
+               ARRAY_SIZE(wm8991_dapm_inmixr_controls),
+               inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* AINRMUX */
+       SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0,
+               &wm8991_dapm_ainrmux_controls, inmixer_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* Output Side */
+       /* DACs */
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3,
+               WM8991_DACL_ENA_BIT, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3,
+               WM8991_DACR_ENA_BIT, 0),
+
+       /* LOMIX */
+       SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT,
+               0, &wm8991_dapm_lomix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_lomix_controls),
+               outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+       /* LONMIX */
+       SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0,
+               &wm8991_dapm_lonmix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_lonmix_controls)),
+
+       /* LOPMIX */
+       SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0,
+               &wm8991_dapm_lopmix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_lopmix_controls)),
+
+       /* OUT3MIX */
+       SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0,
+               &wm8991_dapm_out3mix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_out3mix_controls)),
+
+       /* SPKMIX */
+       SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0,
+               &wm8991_dapm_spkmix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event,
+               SND_SOC_DAPM_PRE_REG),
+
+       /* OUT4MIX */
+       SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0,
+               &wm8991_dapm_out4mix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_out4mix_controls)),
+
+       /* ROPMIX */
+       SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0,
+               &wm8991_dapm_ropmix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_ropmix_controls)),
+
+       /* RONMIX */
+       SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0,
+               &wm8991_dapm_ronmix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_ronmix_controls)),
+
+       /* ROMIX */
+       SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT,
+               0, &wm8991_dapm_romix_controls[0],
+               ARRAY_SIZE(wm8991_dapm_romix_controls),
+               outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+       /* LOUT PGA */
+       SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0,
+               NULL, 0),
+
+       /* ROUT PGA */
+       SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0,
+               NULL, 0),
+
+       /* LOPGA */
+       SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0,
+               NULL, 0),
+
+       /* ROPGA */
+       SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0,
+               NULL, 0),
+
+       /* MICBIAS */
+       SND_SOC_DAPM_MICBIAS("MICBIAS", WM8991_POWER_MANAGEMENT_1,
+               WM8991_MICBIAS_ENA_BIT, 0),
+
+       SND_SOC_DAPM_OUTPUT("LON"),
+       SND_SOC_DAPM_OUTPUT("LOP"),
+       SND_SOC_DAPM_OUTPUT("OUT3"),
+       SND_SOC_DAPM_OUTPUT("LOUT"),
+       SND_SOC_DAPM_OUTPUT("SPKN"),
+       SND_SOC_DAPM_OUTPUT("SPKP"),
+       SND_SOC_DAPM_OUTPUT("ROUT"),
+       SND_SOC_DAPM_OUTPUT("OUT4"),
+       SND_SOC_DAPM_OUTPUT("ROP"),
+       SND_SOC_DAPM_OUTPUT("RON"),
+       SND_SOC_DAPM_OUTPUT("OUT"),
+
+       SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Make DACs turn on when playing even if not mixed into any outputs */
+       {"Internal DAC Sink", NULL, "Left DAC"},
+       {"Internal DAC Sink", NULL, "Right DAC"},
+
+       /* Make ADCs turn on when recording even if not mixed from any inputs */
+       {"Left ADC", NULL, "Internal ADC Source"},
+       {"Right ADC", NULL, "Internal ADC Source"},
+
+       /* Input Side */
+       /* LIN12 PGA */
+       {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+       {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+       /* LIN34 PGA */
+       {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+       {"LIN34 PGA", "LIN4 Switch", "LIN4RXN"},
+       /* INMIXL */
+       {"INMIXL", "Record Left Volume", "LOMIX"},
+       {"INMIXL", "LIN2 Volume", "LIN2"},
+       {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+       {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+       /* AINLMUX */
+       {"AINLMUX", "INMIXL Mix", "INMIXL"},
+       {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"},
+       {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"},
+       {"AINLMUX", "RXVOICE Mix", "LIN4RXN"},
+       {"AINLMUX", "RXVOICE Mix", "RIN4RXP"},
+       /* ADC */
+       {"Left ADC", NULL, "AINLMUX"},
+
+       /* RIN12 PGA */
+       {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+       {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+       /* RIN34 PGA */
+       {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+       {"RIN34 PGA", "RIN4 Switch", "RIN4RXP"},
+       /* INMIXL */
+       {"INMIXR", "Record Right Volume", "ROMIX"},
+       {"INMIXR", "RIN2 Volume", "RIN2"},
+       {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+       {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+       /* AINRMUX */
+       {"AINRMUX", "INMIXR Mix", "INMIXR"},
+       {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"},
+       {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"},
+       {"AINRMUX", "RXVOICE Mix", "LIN4RXN"},
+       {"AINRMUX", "RXVOICE Mix", "RIN4RXP"},
+       /* ADC */
+       {"Right ADC", NULL, "AINRMUX"},
+
+       /* LOMIX */
+       {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+       {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+       {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+       {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+       {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+       {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+       {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+       /* ROMIX */
+       {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+       {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+       {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+       {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+       {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+       {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+       {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+       /* SPKMIX */
+       {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+       {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+       {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+       {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+       {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+       {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+       {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+       {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+       /* LONMIX */
+       {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+       {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+       {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+       /* LOPMIX */
+       {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+       {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+       {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+       /* OUT3MIX */
+       {"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"},
+       {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+       /* OUT4MIX */
+       {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+       {"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"},
+
+       /* RONMIX */
+       {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+       {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+       {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+       /* ROPMIX */
+       {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+       {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+       {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+       /* Out Mixer PGAs */
+       {"LOPGA", NULL, "LOMIX"},
+       {"ROPGA", NULL, "ROMIX"},
+
+       {"LOUT PGA", NULL, "LOMIX"},
+       {"ROUT PGA", NULL, "ROMIX"},
+
+       /* Output Pins */
+       {"LON", NULL, "LONMIX"},
+       {"LOP", NULL, "LOPMIX"},
+       {"OUT", NULL, "OUT3MIX"},
+       {"LOUT", NULL, "LOUT PGA"},
+       {"SPKN", NULL, "SPKMIX"},
+       {"ROUT", NULL, "ROUT PGA"},
+       {"OUT4", NULL, "OUT4MIX"},
+       {"ROP", NULL, "ROPMIX"},
+       {"RON", NULL, "RONMIX"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+       u32 div2;
+       u32 n;
+       u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+                       unsigned int source)
+{
+       u64 Kpart;
+       unsigned int K, Ndiv, Nmod;
+
+
+       Ndiv = target / source;
+       if (Ndiv < 6) {
+               source >>= 1;
+               pll_div->div2 = 1;
+               Ndiv = target / source;
+       } else
+               pll_div->div2 = 0;
+
+       if ((Ndiv < 6) || (Ndiv > 12))
+               printk(KERN_WARNING
+                      "WM8991 N value outwith recommended range! N = %d\n", Ndiv);
+
+       pll_div->n = Ndiv;
+       Nmod = target % source;
+       Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+       do_div(Kpart, source);
+
+       K = Kpart & 0xFFFFFFFF;
+
+       /* Check if we need to round */
+       if ((K % 10) >= 5)
+               K += 5;
+
+       /* Move down to proper range now rounding is done */
+       K /= 10;
+
+       pll_div->k = K;
+}
+
+static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
+                             int pll_id, int src, unsigned int freq_in, unsigned int freq_out)
+{
+       u16 reg;
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct _pll_div pll_div;
+
+       if (freq_in && freq_out) {
+               pll_factors(&pll_div, freq_out * 4, freq_in);
+
+               /* Turn on PLL */
+               reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+               reg |= WM8991_PLL_ENA;
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+
+               /* sysclk comes from PLL */
+               reg = snd_soc_read(codec, WM8991_CLOCKING_2);
+               snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);
+
+               /* set up N , fractional mode and pre-divisor if neccessary */
+               snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM |
+                             (pll_div.div2 ? WM8991_PRESCALE : 0));
+               snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8));
+               snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
+       } else {
+               /* Turn on PLL */
+               reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+               reg &= ~WM8991_PLL_ENA;
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+       }
+       return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 audio1, audio3;
+
+       audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+       audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3);
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               audio3 &= ~WM8991_AIF_MSTR1;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               audio3 |= WM8991_AIF_MSTR1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       audio1 &= ~WM8991_AIF_FMT_MASK;
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               audio1 |= WM8991_AIF_TMF_I2S;
+               audio1 &= ~WM8991_AIF_LRCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               audio1 |= WM8991_AIF_TMF_RIGHTJ;
+               audio1 &= ~WM8991_AIF_LRCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               audio1 |= WM8991_AIF_TMF_LEFTJ;
+               audio1 &= ~WM8991_AIF_LRCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               audio1 |= WM8991_AIF_TMF_DSP;
+               audio1 &= ~WM8991_AIF_LRCLK_INV;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+       snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3);
+       return 0;
+}
+
+static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+                                int div_id, int div)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 reg;
+
+       switch (div_id) {
+       case WM8991_MCLK_DIV:
+               reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+                     ~WM8991_MCLK_DIV_MASK;
+               snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+               break;
+       case WM8991_DACCLK_DIV:
+               reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+                     ~WM8991_DAC_CLKDIV_MASK;
+               snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+               break;
+       case WM8991_ADCCLK_DIV:
+               reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+                     ~WM8991_ADC_CLKDIV_MASK;
+               snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+               break;
+       case WM8991_BCLK_DIV:
+               reg = snd_soc_read(codec, WM8991_CLOCKING_1) &
+                     ~WM8991_BCLK_DIV_MASK;
+               snd_soc_write(codec, WM8991_CLOCKING_1, reg | div);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8991_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+
+       audio1 &= ~WM8991_AIF_WL_MASK;
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               audio1 |= WM8991_AIF_WL_20BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               audio1 |= WM8991_AIF_WL_24BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               audio1 |= WM8991_AIF_WL_32BITS;
+               break;
+       }
+
+       snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+       return 0;
+}
+
+static int wm8991_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 val;
+
+       val  = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
+       if (mute)
+               snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+       else
+               snd_soc_write(codec, WM8991_DAC_CTRL, val);
+       return 0;
+}
+
+static int wm8991_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u16 val;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* VMID=2*50k */
+               val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+                     ~WM8991_VMID_MODE_MASK;
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       snd_soc_cache_sync(codec);
+                       /* Enable all output discharge bits */
+                       snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+                                     WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+                                     WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+                                     WM8991_DIS_ROUT);
+
+                       /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+                       snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+                                     WM8991_BUFDCOPEN | WM8991_POBCTRL |
+                                     WM8991_VMIDTOG);
+
+                       /* Delay to allow output caps to discharge */
+                       msleep(300);
+
+                       /* Disable VMIDTOG */
+                       snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+                                     WM8991_BUFDCOPEN | WM8991_POBCTRL);
+
+                       /* disable all output discharge bits */
+                       snd_soc_write(codec, WM8991_ANTIPOP1, 0);
+
+                       /* Enable outputs */
+                       snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00);
+
+                       msleep(50);
+
+                       /* Enable VMID at 2x50k */
+                       snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02);
+
+                       msleep(100);
+
+                       /* Enable VREF */
+                       snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+                       msleep(600);
+
+                       /* Enable BUFIOEN */
+                       snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+                                     WM8991_BUFDCOPEN | WM8991_POBCTRL |
+                                     WM8991_BUFIOEN);
+
+                       /* Disable outputs */
+                       snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3);
+
+                       /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+                       snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN);
+               }
+
+               /* VMID=2*250k */
+               val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+                     ~WM8991_VMID_MODE_MASK;
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* Enable POBCTRL and SOFT_ST */
+               snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+                             WM8991_POBCTRL | WM8991_BUFIOEN);
+
+               /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+               snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+                             WM8991_BUFDCOPEN | WM8991_POBCTRL |
+                             WM8991_BUFIOEN);
+
+               /* mute DAC */
+               val = snd_soc_read(codec, WM8991_DAC_CTRL);
+               snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+
+               /* Enable any disabled outputs */
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+               /* Disable VMID */
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01);
+
+               msleep(300);
+
+               /* Enable all output discharge bits */
+               snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+                             WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+                             WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+                             WM8991_DIS_ROUT);
+
+               /* Disable VREF */
+               snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0);
+
+               /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+               snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
+               codec->cache_sync = 1;
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static int wm8991_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8991_resume(struct snd_soc_codec *codec)
+{
+       wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+/* power down chip */
+static int wm8991_remove(struct snd_soc_codec *codec)
+{
+       wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8991_probe(struct snd_soc_codec *codec)
+{
+       struct wm8991_priv *wm8991;
+       int ret;
+       unsigned int reg;
+
+       wm8991 = snd_soc_codec_get_drvdata(codec);
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
+               return ret;
+       }
+
+       ret = wm8991_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       reg = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_4);
+       snd_soc_write(codec, WM8991_AUDIO_INTERFACE_4, reg | WM8991_ALRCGPIO1);
+
+       reg = snd_soc_read(codec, WM8991_GPIO1_GPIO2) &
+             ~WM8991_GPIO1_SEL_MASK;
+       snd_soc_write(codec, WM8991_GPIO1_GPIO2, reg | 1);
+
+       reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1);
+       snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, reg | WM8991_VREF_ENA|
+                     WM8991_VMID_MODE_MASK);
+
+       reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+       snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg | WM8991_OPCLK_ENA);
+
+       snd_soc_write(codec, WM8991_DAC_CTRL, 0);
+       snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+       snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+       snd_soc_add_controls(codec, wm8991_snd_controls,
+                            ARRAY_SIZE(wm8991_snd_controls));
+
+       snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets,
+                                 ARRAY_SIZE(wm8991_dapm_widgets));
+       snd_soc_dapm_add_routes(&codec->dapm, audio_map,
+                               ARRAY_SIZE(audio_map));
+       return 0;
+}
+
+#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8991_ops = {
+       .hw_params = wm8991_hw_params,
+       .digital_mute = wm8991_mute,
+       .set_fmt = wm8991_set_dai_fmt,
+       .set_clkdiv = wm8991_set_dai_clkdiv,
+       .set_pll = wm8991_set_dai_pll
+};
+
+/*
+ * The WM8991 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static struct snd_soc_dai_driver wm8991_dai = {
+       /* ADC/DAC on primary */
+       .name = "wm8991",
+       .id = 1,
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = WM8991_FORMATS
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = WM8991_FORMATS
+       },
+       .ops = &wm8991_ops
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
+       .probe = wm8991_probe,
+       .remove = wm8991_remove,
+       .suspend = wm8991_suspend,
+       .resume = wm8991_resume,
+       .set_bias_level = wm8991_set_bias_level,
+       .reg_cache_size = WM8991_MAX_REGISTER + 1,
+       .reg_word_size = sizeof(u16),
+       .reg_cache_default = wm8991_reg_defs
+};
+
+static __devinit int wm8991_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8991_priv *wm8991;
+       int ret;
+
+       wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL);
+       if (!wm8991)
+               return -ENOMEM;
+
+       wm8991->control_type = SND_SOC_I2C;
+       i2c_set_clientdata(i2c, wm8991);
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                                    &soc_codec_dev_wm8991, &wm8991_dai, 1);
+       if (ret < 0)
+               kfree(wm8991);
+       return ret;
+}
+
+static __devexit int wm8991_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id wm8991_i2c_id[] = {
+       { "wm8991", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
+
+static struct i2c_driver wm8991_i2c_driver = {
+       .driver = {
+               .name = "wm8991",
+               .owner = THIS_MODULE,
+       },
+       .probe = wm8991_i2c_probe,
+       .remove = __devexit_p(wm8991_i2c_remove),
+       .id_table = wm8991_i2c_id,
+};
+
+static int __init wm8991_modinit(void)
+{
+       int ret;
+       ret = i2c_add_driver(&wm8991_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n",
+                      ret);
+       }
+       return 0;
+}
+module_init(wm8991_modinit);
+
+static void __exit wm8991_exit(void)
+{
+       i2c_del_driver(&wm8991_i2c_driver);
+}
+module_exit(wm8991_exit);
+
+MODULE_DESCRIPTION("ASoC WM8991 driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
new file mode 100644 (file)
index 0000000..8a942ef
--- /dev/null
@@ -0,0 +1,833 @@
+/*
+ * wm8991.h  --  audio driver for WM8991
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  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.
+ */
+
+#ifndef _WM8991_H
+#define _WM8991_H
+
+/*
+ * Register values.
+ */
+#define WM8991_RESET                            0x00
+#define WM8991_POWER_MANAGEMENT_1               0x01
+#define WM8991_POWER_MANAGEMENT_2               0x02
+#define WM8991_POWER_MANAGEMENT_3               0x03
+#define WM8991_AUDIO_INTERFACE_1                0x04
+#define WM8991_AUDIO_INTERFACE_2                0x05
+#define WM8991_CLOCKING_1                       0x06
+#define WM8991_CLOCKING_2                       0x07
+#define WM8991_AUDIO_INTERFACE_3                0x08
+#define WM8991_AUDIO_INTERFACE_4                0x09
+#define WM8991_DAC_CTRL                         0x0A
+#define WM8991_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8991_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8991_DIGITAL_SIDE_TONE                0x0D
+#define WM8991_ADC_CTRL                         0x0E
+#define WM8991_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8991_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8991_GPIO_CTRL_1                      0x12
+#define WM8991_GPIO1_GPIO2                      0x13
+#define WM8991_GPIO3_GPIO4                      0x14
+#define WM8991_GPIO5_GPIO6                      0x15
+#define WM8991_GPIOCTRL_2                       0x16
+#define WM8991_GPIO_POL                         0x17
+#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8991_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8991_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8991_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8991_OUT3_4_VOLUME                    0x1F
+#define WM8991_LEFT_OPGA_VOLUME                 0x20
+#define WM8991_RIGHT_OPGA_VOLUME                0x21
+#define WM8991_SPEAKER_VOLUME                   0x22
+#define WM8991_CLASSD1                          0x23
+#define WM8991_CLASSD3                          0x25
+#define WM8991_INPUT_MIXER1                     0x27
+#define WM8991_INPUT_MIXER2                     0x28
+#define WM8991_INPUT_MIXER3                     0x29
+#define WM8991_INPUT_MIXER4                     0x2A
+#define WM8991_INPUT_MIXER5                     0x2B
+#define WM8991_INPUT_MIXER6                     0x2C
+#define WM8991_OUTPUT_MIXER1                    0x2D
+#define WM8991_OUTPUT_MIXER2                    0x2E
+#define WM8991_OUTPUT_MIXER3                    0x2F
+#define WM8991_OUTPUT_MIXER4                    0x30
+#define WM8991_OUTPUT_MIXER5                    0x31
+#define WM8991_OUTPUT_MIXER6                    0x32
+#define WM8991_OUT3_4_MIXER                     0x33
+#define WM8991_LINE_MIXER1                      0x34
+#define WM8991_LINE_MIXER2                      0x35
+#define WM8991_SPEAKER_MIXER                    0x36
+#define WM8991_ADDITIONAL_CONTROL               0x37
+#define WM8991_ANTIPOP1                         0x38
+#define WM8991_ANTIPOP2                         0x39
+#define WM8991_MICBIAS                          0x3A
+#define WM8991_PLL1                             0x3C
+#define WM8991_PLL2                             0x3D
+#define WM8991_PLL3                             0x3E
+#define WM8991_INTDRIVBITS                     0x3F
+
+#define WM8991_REGISTER_COUNT                   60
+#define WM8991_MAX_REGISTER                     0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8991_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET_CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8991_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8991_SPK_ENA_BIT                     12
+#define WM8991_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8991_OUT3_ENA_BIT                    11
+#define WM8991_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8991_OUT4_ENA_BIT                    10
+#define WM8991_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8991_LOUT_ENA_BIT                    9
+#define WM8991_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8991_ROUT_ENA_BIT                    8
+#define WM8991_MICBIAS_ENA                      0x0010  /* MICBIAS_ENA */
+#define WM8991_MICBIAS_ENA_BIT                 4
+#define WM8991_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8991_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8991_VREF_ENA_BIT                    0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8991_PLL_ENA                          0x8000  /* PLL_ENA */
+#define WM8991_PLL_ENA_BIT                     15
+#define WM8991_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8991_TSHUT_ENA_BIT                   14
+#define WM8991_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8991_TSHUT_OPDIS_BIT                 13
+#define WM8991_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8991_OPCLK_ENA_BIT                   11
+#define WM8991_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8991_AINL_ENA_BIT                    9
+#define WM8991_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8991_AINR_ENA_BIT                    8
+#define WM8991_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8991_LIN34_ENA_BIT                   7
+#define WM8991_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8991_LIN12_ENA_BIT                   6
+#define WM8991_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8991_RIN34_ENA_BIT                   5
+#define WM8991_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8991_RIN12_ENA_BIT                   4
+#define WM8991_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8991_ADCL_ENA_BIT                    1
+#define WM8991_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8991_ADCR_ENA_BIT                    0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8991_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8991_LON_ENA_BIT                     13
+#define WM8991_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8991_LOP_ENA_BIT                     12
+#define WM8991_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8991_RON_ENA_BIT                     11
+#define WM8991_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8991_ROP_ENA_BIT                     10
+#define WM8991_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8991_LOPGA_ENA_BIT                   7
+#define WM8991_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8991_ROPGA_ENA_BIT                   6
+#define WM8991_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8991_LOMIX_ENA_BIT                   5
+#define WM8991_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8991_ROMIX_ENA_BIT                   4
+#define WM8991_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8991_DACL_ENA_BIT                    1
+#define WM8991_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8991_DACR_ENA_BIT                    0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8991_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8991_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8991_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8991_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8991_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8991_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8991_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8991_AIF_WL_16BITS                   (0 << 5)
+#define WM8991_AIF_WL_20BITS                   (1 << 5)
+#define WM8991_AIF_WL_24BITS                   (2 << 5)
+#define WM8991_AIF_WL_32BITS                   (3 << 5)
+#define WM8991_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8991_AIF_TMF_RIGHTJ                  (0 << 3)
+#define WM8991_AIF_TMF_LEFTJ                   (1 << 3)
+#define WM8991_AIF_TMF_I2S                     (2 << 3)
+#define WM8991_AIF_TMF_DSP                     (3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8991_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8991_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8991_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8991_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8991_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8991_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8991_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8991_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8991_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8991_LOOPBACK                         0x0001  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8991_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8991_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8991_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8991_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8991_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8991_BCLK_DIV_1                      (0x0 << 1)
+#define WM8991_BCLK_DIV_1_5                    (0x1 << 1)
+#define WM8991_BCLK_DIV_2                      (0x2 << 1)
+#define WM8991_BCLK_DIV_3                      (0x3 << 1)
+#define WM8991_BCLK_DIV_4                      (0x4 << 1)
+#define WM8991_BCLK_DIV_5_5                    (0x5 << 1)
+#define WM8991_BCLK_DIV_6                      (0x6 << 1)
+#define WM8991_BCLK_DIV_8                      (0x7 << 1)
+#define WM8991_BCLK_DIV_11                     (0x8 << 1)
+#define WM8991_BCLK_DIV_12                     (0x9 << 1)
+#define WM8991_BCLK_DIV_16                     (0xA << 1)
+#define WM8991_BCLK_DIV_22                     (0xB << 1)
+#define WM8991_BCLK_DIV_24                     (0xC << 1)
+#define WM8991_BCLK_DIV_32                     (0xD << 1)
+#define WM8991_BCLK_DIV_44                     (0xE << 1)
+#define WM8991_BCLK_DIV_48                     (0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8991_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8991_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8991_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8991_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8991_MCLK_DIV_1                      (0 << 11)
+#define WM8991_MCLK_DIV_2                      ( 2 << 11)
+#define WM8991_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8991_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV - [7:5] */
+#define WM8991_ADC_CLKDIV_1                    (0 << 5)
+#define WM8991_ADC_CLKDIV_1_5                  (1 << 5)
+#define WM8991_ADC_CLKDIV_2                    (2 << 5)
+#define WM8991_ADC_CLKDIV_3                    (3 << 5)
+#define WM8991_ADC_CLKDIV_4                    (4 << 5)
+#define WM8991_ADC_CLKDIV_5_5                  (5 << 5)
+#define WM8991_ADC_CLKDIV_6                    (6 << 5)
+#define WM8991_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8991_DAC_CLKDIV_1                    (0 << 2)
+#define WM8991_DAC_CLKDIV_1_5                  (1 << 2)
+#define WM8991_DAC_CLKDIV_2                    (2 << 2)
+#define WM8991_DAC_CLKDIV_3                    (3 << 2)
+#define WM8991_DAC_CLKDIV_4                    (4 << 2)
+#define WM8991_DAC_CLKDIV_5_5                  (5 << 2)
+#define WM8991_DAC_CLKDIV_6                    (6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8991_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8991_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8991_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8991_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8991_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8991_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8991_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8991_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8991_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8991_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8991_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8991_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8991_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8991_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8991_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8991_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8991_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8991_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8991_DACR_DATINV                      0x0001  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8991_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8991_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8991_DACL_VOL_SHIFT                  0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8991_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8991_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8991_DACR_VOL_SHIFT                  0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8991_ADCL_DAC_SVOL_MASK               0x0F  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8991_ADCL_DAC_SVOL_SHIFT             9
+#define WM8991_ADCR_DAC_SVOL_MASK               0x0F  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8991_ADCR_DAC_SVOL_SHIFT             5
+#define WM8991_ADC_TO_DACL_MASK                 0x03  /* ADC_TO_DACL - [3:2] */
+#define WM8991_ADC_TO_DACL_SHIFT               2
+#define WM8991_ADC_TO_DACR_MASK                 0x03  /* ADC_TO_DACR - [1:0] */
+#define WM8991_ADC_TO_DACR_SHIFT               0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8991_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8991_ADC_HPF_ENA_BIT                 8
+#define WM8991_ADC_HPF_CUT_MASK                 0x03  /* ADC_HPF_CUT - [6:5] */
+#define WM8991_ADC_HPF_CUT_SHIFT               5
+#define WM8991_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8991_ADCL_DATINV_BIT                 1
+#define WM8991_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8991_ADCR_DATINV_BIT                 0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8991_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8991_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8991_ADCL_VOL_SHIFT                  0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8991_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8991_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8991_ADCR_VOL_SHIFT                  0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8991_IRQ                              0x1000  /* IRQ */
+#define WM8991_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8991_MICSHRT                          0x0400  /* MICSHRT */
+#define WM8991_MICDET                           0x0200  /* MICDET */
+#define WM8991_PLL_LCK                          0x0100  /* PLL_LCK */
+#define WM8991_GPI8_STATUS                      0x0080  /* GPI8_STATUS */
+#define WM8991_GPI7_STATUS                      0x0040  /* GPI7_STATUS */
+#define WM8991_GPIO6_STATUS                     0x0020  /* GPIO6_STATUS */
+#define WM8991_GPIO5_STATUS                     0x0010  /* GPIO5_STATUS */
+#define WM8991_GPIO4_STATUS                     0x0008  /* GPIO4_STATUS */
+#define WM8991_GPIO3_STATUS                     0x0004  /* GPIO3_STATUS */
+#define WM8991_GPIO2_STATUS                     0x0002  /* GPIO2_STATUS */
+#define WM8991_GPIO1_STATUS                     0x0001  /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8991_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8991_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8991_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8991_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8991_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8991_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8991_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8991_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8991_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8991_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8991_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8991_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8991_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8991_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8991_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8991_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8991_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8991_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8991_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8991_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8991_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8991_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8991_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8991_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8991_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8991_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8991_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8991_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8991_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8991_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8991_RD_3W_ENA                        0x8000  /* RD_3W_ENA */
+#define WM8991_MODE_3W4W                        0x4000  /* MODE_3W4W */
+#define WM8991_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8991_MICSHRT_IRQ_ENA                  0x0400  /* MICSHRT_IRQ_ENA */
+#define WM8991_MICDET_IRQ_ENA                   0x0200  /* MICDET_IRQ_ENA */
+#define WM8991_PLL_LCK_IRQ_ENA                  0x0100  /* PLL_LCK_IRQ_ENA */
+#define WM8991_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8991_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8991_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8991_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8991_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8991_GPI7_ENA                         0x0001  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8991_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8991_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8991_MICSHRT_POL                      0x0400  /* MICSHRT_POL */
+#define WM8991_MICDET_POL                       0x0200  /* MICDET_POL */
+#define WM8991_PLL_LCK_POL                      0x0100  /* PLL_LCK_POL */
+#define WM8991_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8991_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8991_GPIO6_POL                        0x0020  /* GPIO6_POL */
+#define WM8991_GPIO5_POL                        0x0010  /* GPIO5_POL */
+#define WM8991_GPIO4_POL                        0x0008  /* GPIO4_POL */
+#define WM8991_GPIO3_POL                        0x0004  /* GPIO3_POL */
+#define WM8991_GPIO2_POL                        0x0002  /* GPIO2_POL */
+#define WM8991_GPIO1_POL                        0x0001  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8991_LI12MUTE_BIT                    7
+#define WM8991_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8991_LI12ZC_BIT                      6
+#define WM8991_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8991_LIN12VOL_SHIFT                  0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8991_LI34MUTE_BIT                    7
+#define WM8991_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8991_LI34ZC_BIT                      6
+#define WM8991_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8991_LIN34VOL_SHIFT                  0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8991_RI12MUTE_BIT                    7
+#define WM8991_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8991_RI12ZC_BIT                      6
+#define WM8991_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8991_RIN12VOL_SHIFT                  0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8991_RI34MUTE_BIT                    7
+#define WM8991_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8991_RI34ZC_BIT                      6
+#define WM8991_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8991_RIN34VOL_SHIFT                  0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_LOZC                             0x0080  /* LOZC */
+#define WM8991_LOZC_BIT                                7
+#define WM8991_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8991_LOUTVOL_SHIFT                   0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_ROZC                             0x0080  /* ROZC */
+#define WM8991_ROZC_BIT                                7
+#define WM8991_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8991_ROUTVOL_SHIFT                   0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8991_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8991_LONMUTE_BIT                     6
+#define WM8991_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8991_LOPMUTE_BIT                     5
+#define WM8991_LOATTN                           0x0010  /* LOATTN */
+#define WM8991_LOATTN_BIT                      4
+#define WM8991_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8991_RONMUTE_BIT                     2
+#define WM8991_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8991_ROPMUTE_BIT                     1
+#define WM8991_ROATTN                           0x0001  /* ROATTN */
+#define WM8991_ROATTN_BIT                      0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8991_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8991_OUT3MUTE_BIT                    5
+#define WM8991_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8991_OUT3ATTN_BIT                    4
+#define WM8991_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8991_OUT4MUTE_BIT                    1
+#define WM8991_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8991_OUT4ATTN_BIT                    0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8991_LOPGAZC_BIT                     7
+#define WM8991_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8991_LOPGAVOL_SHIFT                  0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8991_ROPGAZC_BIT                     7
+#define WM8991_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8991_ROPGAVOL_SHIFT                  0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8991_SPKVOL_MASK                      0x0003  /* SPKVOL - [1:0] */
+#define WM8991_SPKVOL_SHIFT                    0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8991_CDMODE                           0x0100  /* CDMODE */
+#define WM8991_CDMODE_BIT                      8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8991_DCGAIN_MASK                      0x0007  /* DCGAIN - [5:3] */
+#define WM8991_DCGAIN_SHIFT                    3
+#define WM8991_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8991_ACGAIN_SHIFT                    0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8991_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8991_AINLMODE_SHIFT                  2
+#define WM8991_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8991_AINRMODE_SHIFT                  0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8991_LMP4                                                            0x0080  /* LMP4 */
+#define WM8991_LMP4_BIT                         7              /* LMP4 */
+#define WM8991_LMN3                             0x0040  /* LMN3 */
+#define WM8991_LMN3_BIT                         6       /* LMN3 */
+#define WM8991_LMP2                             0x0020  /* LMP2 */
+#define WM8991_LMP2_BIT                         5       /* LMP2 */
+#define WM8991_LMN1                             0x0010  /* LMN1 */
+#define WM8991_LMN1_BIT                         4       /* LMN1 */
+#define WM8991_RMP4                             0x0008  /* RMP4 */
+#define WM8991_RMP4_BIT                         3       /* RMP4 */
+#define WM8991_RMN3                             0x0004  /* RMN3 */
+#define WM8991_RMN3_BIT                         2       /* RMN3 */
+#define WM8991_RMP2                             0x0002  /* RMP2 */
+#define WM8991_RMP2_BIT                         1       /* RMP2 */
+#define WM8991_RMN1                             0x0001  /* RMN1 */
+#define WM8991_RMN1_BIT                         0       /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8991_L34MNB                           0x0100  /* L34MNB */
+#define WM8991_L34MNB_BIT                      8
+#define WM8991_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8991_L34MNBST_BIT                    7
+#define WM8991_L12MNB                           0x0020  /* L12MNB */
+#define WM8991_L12MNB_BIT                      5
+#define WM8991_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8991_L12MNBST_BIT                    4
+#define WM8991_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8991_LDBVOL_SHIFT                    0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8991_R34MNB                           0x0100  /* R34MNB */
+#define WM8991_R34MNB_BIT                      8
+#define WM8991_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8991_R34MNBST_BIT                    7
+#define WM8991_R12MNB                           0x0020  /* R12MNB */
+#define WM8991_R12MNB_BIT                      5
+#define WM8991_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8991_R12MNBST_BIT                    4
+#define WM8991_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8991_RDBVOL_SHIFT                    0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8991_LI2BVOL_MASK                     0x07  /* LI2BVOL - [8:6] */
+#define WM8991_LI2BVOL_SHIFT                   6
+#define WM8991_LR4BVOL_MASK                     0x07  /* LR4BVOL - [5:3] */
+#define WM8991_LR4BVOL_SHIFT                   3
+#define WM8991_LL4BVOL_MASK                     0x07  /* LL4BVOL - [2:0] */
+#define WM8991_LL4BVOL_SHIFT                   0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8991_RI2BVOL_MASK                     0x07  /* RI2BVOL - [8:6] */
+#define WM8991_RI2BVOL_SHIFT                   6
+#define WM8991_RL4BVOL_MASK                     0x07  /* RL4BVOL - [5:3] */
+#define WM8991_RL4BVOL_SHIFT                   3
+#define WM8991_RR4BVOL_MASK                     0x07  /* RR4BVOL - [2:0] */
+#define WM8991_RR4BVOL_SHIFT                   0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8991_LRBLO                            0x0080  /* LRBLO */
+#define WM8991_LRBLO_BIT                       7
+#define WM8991_LLBLO                            0x0040  /* LLBLO */
+#define WM8991_LLBLO_BIT                       6
+#define WM8991_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8991_LRI3LO_BIT                      5
+#define WM8991_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8991_LLI3LO_BIT                      4
+#define WM8991_LR12LO                           0x0008  /* LR12LO */
+#define WM8991_LR12LO_BIT                      3
+#define WM8991_LL12LO                           0x0004  /* LL12LO */
+#define WM8991_LL12LO_BIT                      2
+#define WM8991_LDLO                             0x0001  /* LDLO */
+#define WM8991_LDLO_BIT                                0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8991_RLBRO                            0x0080  /* RLBRO */
+#define WM8991_RLBRO_BIT                       7
+#define WM8991_RRBRO                            0x0040  /* RRBRO */
+#define WM8991_RRBRO_BIT                       6
+#define WM8991_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8991_RLI3RO_BIT                      5
+#define WM8991_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8991_RRI3RO_BIT                      4
+#define WM8991_RL12RO                           0x0008  /* RL12RO */
+#define WM8991_RL12RO_BIT                      3
+#define WM8991_RR12RO                           0x0004  /* RR12RO */
+#define WM8991_RR12RO_BIT                      2
+#define WM8991_RDRO                             0x0001  /* RDRO */
+#define WM8991_RDRO_BIT                                0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8991_LLI3LOVOL_MASK                   0x07  /* LLI3LOVOL - [8:6] */
+#define WM8991_LLI3LOVOL_SHIFT                 6
+#define WM8991_LR12LOVOL_MASK                   0x07  /* LR12LOVOL - [5:3] */
+#define WM8991_LR12LOVOL_SHIFT                 3
+#define WM8991_LL12LOVOL_MASK                   0x07  /* LL12LOVOL - [2:0] */
+#define WM8991_LL12LOVOL_SHIFT                 0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8991_RRI3ROVOL_MASK                   0x07  /* RRI3ROVOL - [8:6] */
+#define WM8991_RRI3ROVOL_SHIFT                 6
+#define WM8991_RL12ROVOL_MASK                   0x07  /* RL12ROVOL - [5:3] */
+#define WM8991_RL12ROVOL_SHIFT                 3
+#define WM8991_RR12ROVOL_MASK                   0x07  /* RR12ROVOL - [2:0] */
+#define WM8991_RR12ROVOL_SHIFT                 0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8991_LRI3LOVOL_MASK                   0x07  /* LRI3LOVOL - [8:6] */
+#define WM8991_LRI3LOVOL_SHIFT                 6
+#define WM8991_LRBLOVOL_MASK                    0x07  /* LRBLOVOL - [5:3] */
+#define WM8991_LRBLOVOL_SHIFT                  3
+#define WM8991_LLBLOVOL_MASK                    0x07  /* LLBLOVOL - [2:0] */
+#define WM8991_LLBLOVOL_SHIFT                  0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8991_RLI3ROVOL_MASK                   0x07  /* RLI3ROVOL - [8:6] */
+#define WM8991_RLI3ROVOL_SHIFT                 6
+#define WM8991_RLBROVOL_MASK                    0x07  /* RLBROVOL - [5:3] */
+#define WM8991_RLBROVOL_SHIFT                  3
+#define WM8991_RRBROVOL_MASK                    0x07  /* RRBROVOL - [2:0] */
+#define WM8991_RRBROVOL_SHIFT                  0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8991_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8991_LI4O3                            0x0020  /* LI4O3 */
+#define WM8991_LI4O3_BIT                       5
+#define WM8991_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8991_LPGAO3_BIT                      4
+#define WM8991_RI4O4                            0x0002  /* RI4O4 */
+#define WM8991_RI4O4_BIT                       1
+#define WM8991_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8991_RPGAO4_BIT                      0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8991_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8991_LLOPGALON_BIT                   6
+#define WM8991_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8991_LROPGALON_BIT                   5
+#define WM8991_LOPLON                           0x0010  /* LOPLON */
+#define WM8991_LOPLON_BIT                      4
+#define WM8991_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8991_LR12LOP_BIT                     2
+#define WM8991_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8991_LL12LOP_BIT                     1
+#define WM8991_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8991_LLOPGALOP_BIT                   0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8991_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8991_RROPGARON_BIT                   6
+#define WM8991_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8991_RLOPGARON_BIT                   5
+#define WM8991_ROPRON                           0x0010  /* ROPRON */
+#define WM8991_ROPRON_BIT                      4
+#define WM8991_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8991_RL12ROP_BIT                     2
+#define WM8991_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8991_RR12ROP_BIT                     1
+#define WM8991_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8991_RROPGAROP_BIT                   0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8991_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8991_LB2SPK_BIT                      7
+#define WM8991_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8991_RB2SPK_BIT                      6
+#define WM8991_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8991_LI2SPK_BIT                      5
+#define WM8991_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8991_RI2SPK_BIT                      4
+#define WM8991_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8991_LOPGASPK_BIT                    3
+#define WM8991_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8991_ROPGASPK_BIT                    2
+#define WM8991_LDSPK                            0x0002  /* LDSPK */
+#define WM8991_LDSPK_BIT                       1
+#define WM8991_RDSPK                            0x0001  /* RDSPK */
+#define WM8991_RDSPK_BIT                       0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8991_VROI                             0x0001  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8991_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8991_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8991_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8991_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8991_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8991_DIS_ROUT                         0x0001  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8991_SOFTST                           0x0040  /* SOFTST */
+#define WM8991_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8991_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8991_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8991_VMIDTOG                          0x0001  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8991_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8991_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8991_MCD                              0x0004  /* MCD */
+#define WM8991_MBSEL                            0x0001  /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8991_SDM                              0x0080  /* SDM */
+#define WM8991_PRESCALE                         0x0040  /* PRESCALE */
+#define WM8991_PLLN_MASK                        0x000F  /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8991_PLLK1_MASK                       0x00FF  /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8991_PLLK2_MASK                       0x00FF  /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8991_INMIXL_PWR_BIT                  0
+#define WM8991_AINLMUX_PWR_BIT                 1
+#define WM8991_INMIXR_PWR_BIT                  2
+#define WM8991_AINRMUX_PWR_BIT                 3
+
+#define WM8991_MCLK_DIV 0
+#define WM8991_DACCLK_DIV 1
+#define WM8991_ADCCLK_DIV 2
+#define WM8991_BCLK_DIV 3
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+                                        tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+#endif /* _WM8991_H */
index 18c0d9ce7c32ea31871745c75a8604019f12897f..379fa22c5b6c9748cfca06a3bf1cca37c0d20c92 100644 (file)
@@ -242,7 +242,7 @@ struct wm8993_priv {
        int fll_src;
 };
 
-static int wm8993_volatile(unsigned int reg)
+static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8993_SOFTWARE_RESET:
index 68e9b024dd48b98cbf8dc8300c8d84d5713fce1a..a87adbd05ee11151535bb3231122655c0c226525 100644 (file)
@@ -62,8 +62,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
        { 0x00FF, 0x00FF }, /* R58    - MICBIAS */
        { 0x000F, 0x000F }, /* R59    - LDO 1 */
        { 0x0007, 0x0007 }, /* R60    - LDO 2 */
-       { 0x0000, 0x0000 }, /* R61 */
-       { 0x0000, 0x0000 }, /* R62 */
+       { 0xFFFF, 0xFFFF }, /* R61 */
+       { 0xFFFF, 0xFFFF }, /* R62 */
        { 0x0000, 0x0000 }, /* R63 */
        { 0x0000, 0x0000 }, /* R64 */
        { 0x0000, 0x0000 }, /* R65 */
@@ -209,9 +209,9 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
        { 0x0000, 0x0000 }, /* R205 */
        { 0x0000, 0x0000 }, /* R206 */
        { 0x0000, 0x0000 }, /* R207 */
-       { 0x0000, 0x0000 }, /* R208 */
-       { 0x0000, 0x0000 }, /* R209 */
-       { 0x0000, 0x0000 }, /* R210 */
+       { 0xFFFF, 0xFFFF }, /* R208 */
+       { 0xFFFF, 0xFFFF }, /* R209 */
+       { 0xFFFF, 0xFFFF }, /* R210 */
        { 0x0000, 0x0000 }, /* R211 */
        { 0x0000, 0x0000 }, /* R212 */
        { 0x0000, 0x0000 }, /* R213 */
@@ -1573,7 +1573,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
        { 0x03C3, 0x03C3 }, /* R1569  - Sidetone */
 };
 
-const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
+const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
        0x8994,     /* R0     - Software Reset */
        0x0000,     /* R1     - Power Management (1) */
        0x6000,     /* R2     - Power Management (2) */
index c6c958ee5d5920f8f7755a2498c8af73585b52de..3dc64c8b6a5c534c089f6d7f05becad32bc3bb4c 100644 (file)
@@ -102,8 +102,7 @@ struct wm8994_priv {
 
        wm8958_micdet_cb jack_cb;
        void *jack_cb_data;
-       bool jack_is_mic;
-       bool jack_is_video;
+       int micdet_irq;
 
        int revision;
        struct wm8994_pdata *pdata;
@@ -115,7 +114,7 @@ struct wm8994_priv {
        unsigned int aif2clk_disable:1;
 };
 
-static int wm8994_readable(unsigned int reg)
+static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8994_GPIO_1:
@@ -142,7 +141,7 @@ static int wm8994_readable(unsigned int reg)
        return wm8994_access_masks[reg].readable != 0;
 }
 
-static int wm8994_volatile(unsigned int reg)
+static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        if (reg >= WM8994_CACHE_SIZE)
                return 1;
@@ -170,7 +169,7 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg,
 
        BUG_ON(reg > WM8994_MAX_REGISTER);
 
-       if (!wm8994_volatile(reg)) {
+       if (!wm8994_volatile(codec, reg)) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret != 0)
                        dev_err(codec->dev, "Cache write to %x failed: %d\n",
@@ -188,7 +187,7 @@ static unsigned int wm8994_read(struct snd_soc_codec *codec,
 
        BUG_ON(reg > WM8994_MAX_REGISTER);
 
-       if (!wm8994_volatile(reg) && wm8994_readable(reg) &&
+       if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) &&
            reg < codec->driver->reg_cache_size) {
                ret = snd_soc_cache_read(codec, reg, &val);
                if (ret >= 0)
@@ -529,7 +528,7 @@ static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct wm8994_priv *wm8994 =snd_soc_codec_get_drvdata(codec);
+       struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
 
        ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block];
@@ -1103,6 +1102,13 @@ static int adc_mux_ev(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int micbias_ev(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol, int event)
+{
+       late_enable_ev(w, kcontrol, event);
+       return 0;
+}
+
 static int dac_ev(struct snd_soc_dapm_widget *w,
                  struct snd_kcontrol *kcontrol, int event)
 {
@@ -1440,6 +1446,10 @@ SND_SOC_DAPM_INPUT("DMIC1DAT"),
 SND_SOC_DAPM_INPUT("DMIC2DAT"),
 SND_SOC_DAPM_INPUT("Clock"),
 
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0),
+SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
+                     SND_SOC_DAPM_PRE_PMU),
+
 SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
                    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
@@ -1755,6 +1765,8 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
        { "AIF2DACDAT", NULL, "AIF1DACDAT" },
        { "AIF1ADCDAT", NULL, "AIF2ADCDAT" },
        { "AIF2ADCDAT", NULL, "AIF1ADCDAT" },
+       { "MICBIAS", NULL, "CLK_SYS" },
+       { "MICBIAS", NULL, "MICBIAS Supply" },
 };
 
 static const struct snd_soc_dapm_route wm8994_intercon[] = {
@@ -2883,6 +2895,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
        else
                snd_soc_add_controls(wm8994->codec, wm8994_eq_controls,
                                     ARRAY_SIZE(wm8994_eq_controls));
+
+       for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
+               if (pdata->micbias[i]) {
+                       snd_soc_write(codec, WM8958_MICBIAS1 + i,
+                               pdata->micbias[i] & 0xffff);
+               }
+       }
 }
 
 /**
@@ -2993,46 +3012,18 @@ static void wm8958_default_micdet(u16 status, void *data)
        int report = 0;
 
        /* If nothing present then clear our statuses */
-       if (!(status & WM8958_MICD_STS)) {
-               wm8994->jack_is_video = false;
-               wm8994->jack_is_mic = false;
+       if (!(status & WM8958_MICD_STS))
                goto done;
-       }
 
-       /* Assume anything over 475 ohms is a microphone and remember
-        * that we've seen one (since buttons override it) */
-       if (status & 0x600)
-               wm8994->jack_is_mic = true;
-       if (wm8994->jack_is_mic)
-               report |= SND_JACK_MICROPHONE;
-
-       /* Video has an impedence of approximately 75 ohms; assume
-        * this isn't used as a button and remember it since buttons
-        * override it. */
-       if (status & 0x40)
-               wm8994->jack_is_video = true;
-       if (wm8994->jack_is_video)
-               report |= SND_JACK_VIDEOOUT;
+       report = SND_JACK_MICROPHONE;
 
        /* Everything else is buttons; just assign slots */
-       if (status & 0x4)
+       if (status & 0x1c0)
                report |= SND_JACK_BTN_0;
-       if (status & 0x8)
-               report |= SND_JACK_BTN_1;
-       if (status & 0x10)
-               report |= SND_JACK_BTN_2;
-       if (status & 0x20)
-               report |= SND_JACK_BTN_3;
-       if (status & 0x80)
-               report |= SND_JACK_BTN_4;
-       if (status & 0x100)
-               report |= SND_JACK_BTN_5;
 
 done:
        snd_soc_jack_report(wm8994->micdet[0].jack, report,
-                           SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
-                           SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 |
-                           SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT);
+                           SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
 }
 
 /**
@@ -3131,13 +3122,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
        wm8994->pdata = dev_get_platdata(codec->dev->parent);
        wm8994->codec = codec;
 
+       if (wm8994->pdata && wm8994->pdata->micdet_irq)
+               wm8994->micdet_irq = wm8994->pdata->micdet_irq;
+       else if (wm8994->pdata && wm8994->pdata->irq_base)
+               wm8994->micdet_irq = wm8994->pdata->irq_base +
+                                    WM8994_IRQ_MIC1_DET;
+
        pm_runtime_enable(codec->dev);
        pm_runtime_resume(codec->dev);
 
        /* Read our current status back from the chip - we don't want to
         * reset as this may interfere with the GPIO or LDO operation. */
        for (i = 0; i < WM8994_CACHE_SIZE; i++) {
-               if (!wm8994_readable(i) || wm8994_volatile(i))
+               if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i))
                        continue;
 
                ret = wm8994_reg_read(codec->control_data, i);
@@ -3179,14 +3176,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
        switch (control->type) {
        case WM8994:
-               ret = wm8994_request_irq(codec->control_data,
-                                        WM8994_IRQ_MIC1_DET,
-                                        wm8994_mic_irq, "Mic 1 detect",
-                                        wm8994);
-               if (ret != 0)
-                       dev_warn(codec->dev,
-                                "Failed to request Mic1 detect IRQ: %d\n",
-                                ret);
+               if (wm8994->micdet_irq) {
+                       ret = request_threaded_irq(wm8994->micdet_irq, NULL,
+                                                  wm8994_mic_irq,
+                                                  IRQF_TRIGGER_RISING,
+                                                  "Mic1 detect",
+                                                  wm8994);
+                       if (ret != 0)
+                               dev_warn(codec->dev,
+                                        "Failed to request Mic1 detect IRQ: %d\n",
+                                        ret);
+               }
 
                ret = wm8994_request_irq(codec->control_data,
                                         WM8994_IRQ_MIC1_SHRT,
@@ -3217,15 +3217,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                break;
 
        case WM8958:
-               ret = wm8994_request_irq(codec->control_data,
-                                        WM8994_IRQ_MIC1_DET,
-                                        wm8958_mic_irq, "Mic detect",
-                                        wm8994);
-               if (ret != 0)
-                       dev_warn(codec->dev,
-                                "Failed to request Mic detect IRQ: %d\n",
-                                ret);
-               break;
+               if (wm8994->micdet_irq) {
+                       ret = request_threaded_irq(wm8994->micdet_irq, NULL,
+                                                  wm8958_mic_irq,
+                                                  IRQF_TRIGGER_RISING,
+                                                  "Mic detect",
+                                                  wm8994);
+                       if (ret != 0)
+                               dev_warn(codec->dev,
+                                        "Failed to request Mic detect IRQ: %d\n",
+                                        ret);
+               }
        }
 
        /* Remember if AIFnLRCLK is configured as a GPIO.  This should be
@@ -3369,7 +3371,8 @@ err_irq:
        wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994);
        wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994);
        wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
-       wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994);
+       if (wm8994->micdet_irq)
+               free_irq(wm8994->micdet_irq, wm8994);
 err:
        kfree(wm8994);
        return ret;
@@ -3386,8 +3389,8 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
 
        switch (control->type) {
        case WM8994:
-               wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT,
-                               wm8994);
+               if (wm8994->micdet_irq)
+                       free_irq(wm8994->micdet_irq, wm8994);
                wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET,
                                wm8994);
                wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT,
@@ -3397,8 +3400,8 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
                break;
 
        case WM8958:
-               wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET,
-                               wm8994);
+               if (wm8994->micdet_irq)
+                       free_irq(wm8994->micdet_irq, wm8994);
                break;
        }
        kfree(wm8994->retune_mobile_texts);
index 0c355bfc88f120d1740d8431fa445ee2f940a320..999b8851226b6840371ec041493355ffd6f3d905 100644 (file)
@@ -43,6 +43,6 @@ struct wm8994_access_mask {
 };
 
 extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
-extern const __devinitdata  u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
+extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
 
 #endif
index 608c84c5aa8e6472e3bb43b20b279753f0a4a1c1..67eaaecbb42ebdf1dfeec71e8b51e90105877ee7 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
 #include "wm8995.h"
 
+#define WM8995_NUM_SUPPLIES 8
+static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
+       "DCVDD",
+       "DBVDD1",
+       "DBVDD2",
+       "DBVDD3",
+       "AVDD1",
+       "AVDD2",
+       "CPVDD",
+       "MICVDD"
+};
+
 static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
        [0]     = 0x8995, [5]     = 0x0100, [16]    = 0x000b, [17]    = 0x000b,
        [24]    = 0x02c0, [25]    = 0x02c0, [26]    = 0x02c0, [27]    = 0x02c0,
@@ -126,8 +139,37 @@ struct wm8995_priv {
        int mclk[2];
        int aifclk[2];
        struct fll_config fll[2], fll_suspend[2];
+       struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
+       struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
+       struct snd_soc_codec *codec;
 };
 
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8995_REGULATOR_EVENT(n) \
+static int wm8995_regulator_event_##n(struct notifier_block *nb, \
+                                     unsigned long event, void *data)    \
+{ \
+       struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
+                                    disable_nb[n]); \
+       if (event & REGULATOR_EVENT_DISABLE) { \
+               wm8995->codec->cache_sync = 1; \
+       } \
+       return 0; \
+}
+
+WM8995_REGULATOR_EVENT(0)
+WM8995_REGULATOR_EVENT(1)
+WM8995_REGULATOR_EVENT(2)
+WM8995_REGULATOR_EVENT(3)
+WM8995_REGULATOR_EVENT(4)
+WM8995_REGULATOR_EVENT(5)
+WM8995_REGULATOR_EVENT(6)
+WM8995_REGULATOR_EVENT(7)
+
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
 static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
 static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
@@ -909,7 +951,7 @@ static const struct snd_soc_dapm_route wm8995_intercon[] = {
        { "SPK2R", NULL, "SPK2R Driver" }
 };
 
-static int wm8995_volatile(unsigned int reg)
+static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        /* out of bounds registers are generally considered
         * volatile to support register banks that are partially
@@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
                break;
        case SND_SOC_BIAS_STANDBY:
                if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+                                                   wm8995->supplies);
+                       if (ret)
+                               return ret;
+
                        ret = snd_soc_cache_sync(codec);
                        if (ret) {
                                dev_err(codec->dev,
@@ -1492,12 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
 
                        snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
                                            WM8995_BG_ENA_MASK, WM8995_BG_ENA);
-
                }
                break;
        case SND_SOC_BIAS_OFF:
                snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
                                    WM8995_BG_ENA_MASK, 0);
+               regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
+                                      wm8995->supplies);
                break;
        }
 
@@ -1536,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec)
 static int wm8995_probe(struct snd_soc_codec *codec)
 {
        struct wm8995_priv *wm8995;
+       int i;
        int ret;
 
        codec->dapm.idle_bias_off = 1;
        wm8995 = snd_soc_codec_get_drvdata(codec);
+       wm8995->codec = codec;
 
        ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
        if (ret < 0) {
@@ -1547,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
+       for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
+               wm8995->supplies[i].supply = wm8995_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
+                                wm8995->supplies);
+       if (ret) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               return ret;
+       }
+
+       wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
+       wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
+       wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
+       wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
+       wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
+       wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
+       wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
+       wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
+
+       /* This should really be moved into the regulator core */
+       for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
+               ret = regulator_register_notifier(wm8995->supplies[i].consumer,
+                                                 &wm8995->disable_nb[i]);
+               if (ret) {
+                       dev_err(codec->dev,
+                               "Failed to register regulator notifier: %d\n",
+                               ret);
+               }
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+                                   wm8995->supplies);
+       if (ret) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_reg_get;
+       }
+
        ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
-               return ret;
+               goto err_reg_enable;
        }
 
        if (ret != 0x8995) {
                dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
-               return -EINVAL;
+               goto err_reg_enable;
        }
 
        ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
-               return ret;
+               goto err_reg_enable;
        }
 
        wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1596,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec)
                                ARRAY_SIZE(wm8995_intercon));
 
        return 0;
+
+err_reg_enable:
+       regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+err_reg_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+       return ret;
 }
 
 #define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
index cce704c275c61df460d7056c6beca250e530525f..55cdf29820200fcb6be534a99905c066f2133c70 100644 (file)
@@ -167,10 +167,10 @@ struct wm9081_priv {
        int fll_fref;
        int fll_fout;
        int tdm_width;
-       struct wm9081_retune_mobile_config *retune;
+       struct wm9081_pdata pdata;
 };
 
-static int wm9081_volatile_register(unsigned int reg)
+static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM9081_SOFTWARE_RESET:
@@ -389,27 +389,6 @@ SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0),
 SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0),
 };
 
-static int speaker_event(struct snd_soc_dapm_widget *w,
-                        struct snd_kcontrol *kcontrol, int event)
-{
-       struct snd_soc_codec *codec = w->codec;
-       unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
-
-       switch (event) {
-       case SND_SOC_DAPM_POST_PMU:
-               reg |= WM9081_SPK_ENA;
-               break;
-
-       case SND_SOC_DAPM_PRE_PMD:
-               reg &= ~WM9081_SPK_ENA;
-               break;
-       }
-
-       snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg);
-
-       return 0;
-}
-
 struct _fll_div {
        u16 fll_fratio;
        u16 fll_outdiv;
@@ -747,9 +726,8 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
 
 SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
 
-SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0,
-                  speaker_event,
-                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
 
 SND_SOC_DAPM_OUTPUT("LINEOUT"),
 SND_SOC_DAPM_OUTPUT("SPKN"),
@@ -762,7 +740,7 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0),
 };
 
 
-static const struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
        { "DAC", NULL, "CLK_SYS" },
        { "DAC", NULL, "CLK_DSP" },
 
@@ -780,8 +758,10 @@ static const struct snd_soc_dapm_route audio_paths[] = {
        { "Speaker PGA", NULL, "TOCLK" },
        { "Speaker PGA", NULL, "CLK_SYS" },
 
-       { "SPKN", NULL, "Speaker PGA" },
-       { "SPKP", NULL, "Speaker PGA" },
+       { "Speaker", NULL, "Speaker PGA" },
+
+       { "SPKN", NULL, "Speaker" },
+       { "SPKP", NULL, "Speaker" },
 };
 
 static int wm9081_set_bias_level(struct snd_soc_codec *codec,
@@ -1082,21 +1062,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
        aif4 |= wm9081->bclk / wm9081->fs;
 
        /* Apply a ReTune Mobile configuration if it's in use */
-       if (wm9081->retune) {
-               struct wm9081_retune_mobile_config *retune = wm9081->retune;
+       if (wm9081->pdata.num_retune_configs) {
+               struct wm9081_pdata *pdata = &wm9081->pdata;
                struct wm9081_retune_mobile_setting *s;
                int eq1;
 
                best = 0;
-               best_val = abs(retune->configs[0].rate - wm9081->fs);
-               for (i = 0; i < retune->num_configs; i++) {
-                       cur_val = abs(retune->configs[i].rate - wm9081->fs);
+               best_val = abs(pdata->retune_configs[0].rate - wm9081->fs);
+               for (i = 0; i < pdata->num_retune_configs; i++) {
+                       cur_val = abs(pdata->retune_configs[i].rate -
+                                     wm9081->fs);
                        if (cur_val < best_val) {
                                best_val = cur_val;
                                best = i;
                        }
                }
-               s = &retune->configs[best];
+               s = &pdata->retune_configs[best];
 
                dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
                        s->name, s->rate);
@@ -1139,10 +1120,9 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
        return 0;
 }
 
-static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
+static int wm9081_set_sysclk(struct snd_soc_codec *codec,
                             int clk_id, unsigned int freq, int dir)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
 
        switch (clk_id) {
@@ -1207,7 +1187,6 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
 
 static struct snd_soc_dai_ops wm9081_dai_ops = {
        .hw_params = wm9081_hw_params,
-       .set_sysclk = wm9081_set_sysclk,
        .set_fmt = wm9081_set_dai_fmt,
        .digital_mute = wm9081_digital_mute,
        .set_tdm_slot = wm9081_set_tdm_slot,
@@ -1231,7 +1210,6 @@ static struct snd_soc_dai_driver wm9081_dai = {
 static int wm9081_probe(struct snd_soc_codec *codec)
 {
        struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
        int ret;
        u16 reg;
 
@@ -1255,6 +1233,14 @@ static int wm9081_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
+       reg = 0;
+       if (wm9081->pdata.irq_high)
+               reg |= WM9081_IRQ_POL;
+       if (!wm9081->pdata.irq_cmos)
+               reg |= WM9081_IRQ_OP_CTRL;
+       snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL,
+                           WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg);
+
        wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        /* Enable zero cross by default */
@@ -1266,17 +1252,13 @@ static int wm9081_probe(struct snd_soc_codec *codec)
 
        snd_soc_add_controls(codec, wm9081_snd_controls,
                             ARRAY_SIZE(wm9081_snd_controls));
-       if (!wm9081->retune) {
+       if (!wm9081->pdata.num_retune_configs) {
                dev_dbg(codec->dev,
                        "No ReTune Mobile data, using normal EQ\n");
                snd_soc_add_controls(codec, wm9081_eq_controls,
                                     ARRAY_SIZE(wm9081_eq_controls));
        }
 
-       snd_soc_dapm_new_controls(dapm, wm9081_dapm_widgets,
-                                 ARRAY_SIZE(wm9081_dapm_widgets));
-       snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
-
        return ret;
 }
 
@@ -1320,11 +1302,19 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
        .remove =       wm9081_remove,
        .suspend =      wm9081_suspend,
        .resume =       wm9081_resume,
+
+       .set_sysclk = wm9081_set_sysclk,
        .set_bias_level = wm9081_set_bias_level,
+
        .reg_cache_size = ARRAY_SIZE(wm9081_reg_defaults),
        .reg_word_size = sizeof(u16),
        .reg_cache_default = wm9081_reg_defaults,
        .volatile_register = wm9081_volatile_register,
+
+       .dapm_widgets     = wm9081_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets),
+       .dapm_routes     = wm9081_audio_paths,
+       .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths),
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -1343,8 +1333,8 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c,
        wm9081->control_data = i2c;
 
        if (dev_get_platdata(&i2c->dev))
-               memcpy(&wm9081->retune, dev_get_platdata(&i2c->dev),
-                      sizeof(wm9081->retune));
+               memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev),
+                      sizeof(wm9081->pdata));
 
        ret = snd_soc_register_codec(&i2c->dev,
                        &soc_codec_dev_wm9081, &wm9081_dai, 1);
@@ -1368,7 +1358,7 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
 
 static struct i2c_driver wm9081_i2c_driver = {
        .driver = {
-               .name = "wm9081-codec",
+               .name = "wm9081",
                .owner = THIS_MODULE,
        },
        .probe =    wm9081_i2c_probe,
index a788c4297046270108f7fa504d83c2f4aeebb162..4de12203e6113508712731c1060ed4e8d39cc743 100644 (file)
@@ -144,7 +144,7 @@ struct wm9090_priv {
        void *control_data;
 };
 
-static int wm9090_volatile(unsigned int reg)
+static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM9090_SOFTWARE_RESET:
@@ -518,7 +518,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
                        for (i = 1; i < codec->driver->reg_cache_size; i++) {
                                if (reg_cache[i] == wm9090_reg_defaults[i])
                                        continue;
-                               if (wm9090_volatile(i))
+                               if (wm9090_volatile(codec, i))
                                        continue;
 
                                ret = snd_soc_write(codec, i, reg_cache[i]);
@@ -551,7 +551,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
 static int wm9090_probe(struct snd_soc_codec *codec)
 {
        struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
-       u16 *reg_cache = codec->reg_cache;
        int ret;
 
        codec->control_data = wm9090->control_data;
@@ -576,22 +575,30 @@ static int wm9090_probe(struct snd_soc_codec *codec)
        /* Configure some defaults; they will be written out when we
         * bring the bias up.
         */
-       reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
-               | WM9090_IN1A_ZC;
-       reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
-               | WM9090_IN1B_ZC;
-       reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
-               | WM9090_IN2A_ZC;
-       reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
-               | WM9090_IN2B_ZC;
-       reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
-               WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
-       reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
-               WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
-       reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
-               WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;
-
-       reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;
+       snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
+                           WM9090_IN1_VU | WM9090_IN1A_ZC,
+                           WM9090_IN1_VU | WM9090_IN1A_ZC);
+       snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
+                           WM9090_IN1_VU | WM9090_IN1B_ZC,
+                           WM9090_IN1_VU | WM9090_IN1B_ZC);
+       snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
+                           WM9090_IN2_VU | WM9090_IN2A_ZC,
+                           WM9090_IN2_VU | WM9090_IN2A_ZC);
+       snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
+                           WM9090_IN2_VU | WM9090_IN2B_ZC,
+                           WM9090_IN2_VU | WM9090_IN2B_ZC);
+       snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
+                           WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
+                           WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
+       snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME,
+                           WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
+                           WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
+       snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME,
+                           WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
+                           WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
+
+       snd_soc_update_bits(codec, WM9090_CLOCKING_1,
+                           WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
 
        wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
index 516892706063d9fc62d89b86e5ce3089ab7f1d84..7b6b3c18e2999ae971fe5d5e418289d939d70699 100644 (file)
@@ -82,7 +82,8 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
        } while (reg & op && count < 400);
 
        if (reg & op)
-               dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+               dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
+                       op);
 }
 
 /*
index 9e0e565e6ed9c2e9308e74e80f2bad50d8f58b28..d0d60b8a54d4b7573841b297c8d07b78434af6a9 100644 (file)
@@ -658,7 +658,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+       ioarea = request_mem_region(mem->start, resource_size(mem),
                                    pdev->name);
        if (!ioarea) {
                dev_err(&pdev->dev, "McBSP region already claimed\n");
@@ -694,20 +694,25 @@ static int davinci_i2s_probe(struct platform_device *pdev)
        }
        clk_enable(dev->clk);
 
-       dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+       dev->base = ioremap(mem->start, resource_size(mem));
+       if (!dev->base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_release_clk;
+       }
 
        dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
-           (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
+           (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
 
        dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
-           (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
+           (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
 
        /* first TX, then RX */
        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
        if (!res) {
                dev_err(&pdev->dev, "no DMA resource\n");
                ret = -ENXIO;
-               goto err_free_mem;
+               goto err_iounmap;
        }
        dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start;
 
@@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
        if (!res) {
                dev_err(&pdev->dev, "no DMA resource\n");
                ret = -ENXIO;
-               goto err_free_mem;
+               goto err_iounmap;
        }
        dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
        dev->dev = &pdev->dev;
@@ -724,14 +729,19 @@ static int davinci_i2s_probe(struct platform_device *pdev)
 
        ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai);
        if (ret != 0)
-               goto err_free_mem;
+               goto err_iounmap;
 
        return 0;
 
+err_iounmap:
+       iounmap(dev->base);
+err_release_clk:
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
 err_free_mem:
        kfree(dev);
 err_release_region:
-       release_mem_region(mem->start, (mem->end - mem->start) + 1);
+       release_mem_region(mem->start, resource_size(mem));
 
        return ret;
 }
@@ -747,7 +757,7 @@ static int davinci_i2s_remove(struct platform_device *pdev)
        dev->clk = NULL;
        kfree(dev);
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(mem->start, (mem->end - mem->start) + 1);
+       release_mem_region(mem->start, resource_size(mem));
 
        return 0;
 }
index fb55d2c5d704ff25983fd5ef4d16dfc461e60a76..a5af834c8ef52088c9a735b85cceebe095b95300 100644 (file)
@@ -868,7 +868,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        }
 
        ioarea = request_mem_region(mem->start,
-                       (mem->end - mem->start) + 1, pdev->name);
+                       resource_size(mem), pdev->name);
        if (!ioarea) {
                dev_err(&pdev->dev, "Audio region already claimed\n");
                ret = -EBUSY;
@@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        clk_enable(dev->clk);
        dev->clk_active = 1;
 
-       dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+       dev->base = ioremap(mem->start, resource_size(mem));
+       if (!dev->base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_release_clk;
+       }
+
        dev->op_mode = pdata->op_mode;
        dev->tdm_slots = pdata->tdm_slots;
        dev->num_serializer = pdata->num_serializer;
@@ -899,14 +905,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        dma_data->asp_chan_q = pdata->asp_chan_q;
        dma_data->ram_chan_q = pdata->ram_chan_q;
        dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
-                                                       io_v2p(dev->base));
+                                                       mem->start);
 
        /* first TX, then RX */
        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
        if (!res) {
                dev_err(&pdev->dev, "no DMA resource\n");
                ret = -ENODEV;
-               goto err_release_region;
+               goto err_iounmap;
        }
 
        dma_data->channel = res->start;
@@ -915,13 +921,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        dma_data->asp_chan_q = pdata->asp_chan_q;
        dma_data->ram_chan_q = pdata->ram_chan_q;
        dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
-                                                       io_v2p(dev->base));
+                                                       mem->start);
 
        res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
        if (!res) {
                dev_err(&pdev->dev, "no DMA resource\n");
                ret = -ENODEV;
-               goto err_release_region;
+               goto err_iounmap;
        }
 
        dma_data->channel = res->start;
@@ -929,11 +935,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]);
 
        if (ret != 0)
-               goto err_release_region;
+               goto err_iounmap;
        return 0;
 
+err_iounmap:
+       iounmap(dev->base);
+err_release_clk:
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
 err_release_region:
-       release_mem_region(mem->start, (mem->end - mem->start) + 1);
+       release_mem_region(mem->start, resource_size(mem));
 err_release_data:
        kfree(dev);
 
@@ -951,7 +962,7 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
        dev->clk = NULL;
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(mem->start, (mem->end - mem->start) + 1);
+       release_mem_region(mem->start, resource_size(mem));
 
        kfree(dev);
 
index 57429041189c134abda26547ab723cef54590e6e..91a28de941094dd6b71067311bd0d8447beb2c14 100644 (file)
@@ -30,3 +30,12 @@ config SND_EP93XX_SOC_SIMONE
        help
          Say Y or M here if you want to add support for AC97 audio on the
          Simplemachines Sim.One board.
+
+config SND_EP93XX_SOC_EDB93XX
+       tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
+       depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
+       select SND_EP93XX_SOC_I2S
+       select SND_SOC_CS4271
+       help
+         Say Y or M here if you want to add support for I2S audio on the
+         Cirrus Logic EDB93xx boards.
index 8e7977fb6b7d6c407bd94794ce7cbe04d2c96560..5514146cbdf0ebd5dd4fc7499680745eab8b36aa 100644 (file)
@@ -10,6 +10,8 @@ obj-$(CONFIG_SND_EP93XX_SOC_AC97)             += snd-soc-ep93xx-ac97.o
 # EP93XX Machine Support
 snd-soc-snappercl15-objs                       := snappercl15.o
 snd-soc-simone-objs                            := simone.o
+snd-soc-edb93xx-objs                           := edb93xx.o
 
 obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15)       += snd-soc-snappercl15.o
 obj-$(CONFIG_SND_EP93XX_SOC_SIMONE)            += snd-soc-simone.o
+obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX)           += snd-soc-edb93xx.o
diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c
new file mode 100644 (file)
index 0000000..d3aa151
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * SoC audio for EDB93xx
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include "ep93xx-pcm.h"
+
+#define edb93xx_has_audio() (machine_is_edb9301() ||   \
+                            machine_is_edb9302() ||    \
+                            machine_is_edb9302a() ||   \
+                            machine_is_edb9307a() ||   \
+                            machine_is_edb9315a())
+
+static int edb93xx_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       int err;
+       unsigned int mclk_rate;
+       unsigned int rate = params_rate(params);
+
+       /*
+        * According to CS4271 datasheet we use MCLK/LRCK=256 for
+        * rates below 50kHz and 128 for higher sample rates
+        */
+       if (rate < 50000)
+               mclk_rate = rate * 64 * 4;
+       else
+               mclk_rate = rate * 64 * 2;
+
+       err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_IF |
+                                 SND_SOC_DAIFMT_CBS_CFS);
+       if (err)
+               return err;
+
+       err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_IF |
+                                 SND_SOC_DAIFMT_CBS_CFS);
+       if (err)
+               return err;
+
+       err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
+                                    SND_SOC_CLOCK_IN);
+       if (err)
+               return err;
+
+       return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
+                                     SND_SOC_CLOCK_OUT);
+}
+
+static struct snd_soc_ops edb93xx_ops = {
+       .hw_params      = edb93xx_hw_params,
+};
+
+static struct snd_soc_dai_link edb93xx_dai = {
+       .name           = "CS4271",
+       .stream_name    = "CS4271 HiFi",
+       .platform_name  = "ep93xx-pcm-audio",
+       .cpu_dai_name   = "ep93xx-i2s",
+       .codec_name     = "spi0.0",
+       .codec_dai_name = "cs4271-hifi",
+       .ops            = &edb93xx_ops,
+};
+
+static struct snd_soc_card snd_soc_edb93xx = {
+       .name           = "EDB93XX",
+       .dai_link       = &edb93xx_dai,
+       .num_links      = 1,
+};
+
+static struct platform_device *edb93xx_snd_device;
+
+static int __init edb93xx_init(void)
+{
+       int ret;
+
+       if (!edb93xx_has_audio())
+               return -ENODEV;
+
+       ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
+                                EP93XX_SYSCON_I2SCLKDIV_ORIDE |
+                                EP93XX_SYSCON_I2SCLKDIV_SPOL);
+       if (ret)
+               return ret;
+
+       edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!edb93xx_snd_device) {
+               ret = -ENOMEM;
+               goto free_i2s;
+       }
+
+       platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
+       ret = platform_device_add(edb93xx_snd_device);
+       if (ret)
+               goto device_put;
+
+       return 0;
+
+device_put:
+       platform_device_put(edb93xx_snd_device);
+free_i2s:
+       ep93xx_i2s_release();
+       return ret;
+}
+module_init(edb93xx_init);
+
+static void __exit edb93xx_exit(void)
+{
+       platform_device_unregister(edb93xx_snd_device);
+       ep93xx_i2s_release();
+}
+module_exit(edb93xx_exit);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("ALSA SoC EDB93xx");
+MODULE_LICENSE("GPL");
index 68a0bae1208aa8add69cae8d6026755ba73d9d92..104e95cda0adbcf8b76d513f648a1ab36a22ea7c 100644 (file)
@@ -253,7 +253,6 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
        struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
        unsigned v = 0;
 
-
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
index fff579a1c1344974332e055b7ef5b1eec225552c..042f4e93746f4095cf883c649d90941919e5a43e 100644 (file)
@@ -242,7 +242,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
 {
        struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
        unsigned word_len, div, sdiv, lrdiv;
-       int found = 0, err;
+       int err;
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
@@ -275,15 +275,14 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
         * the codec uses.
         */
        div = clk_get_rate(info->mclk) / params_rate(params);
-       for (sdiv = 2; sdiv <= 4; sdiv += 2)
-               for (lrdiv = 64; lrdiv <= 128; lrdiv <<= 1)
-                       if (sdiv * lrdiv == div) {
-                               found = 1;
-                               goto out;
-                       }
-out:
-       if (!found)
-               return -EINVAL;
+       sdiv = 4;
+       if (div > (256 + 512) / 2) {
+               lrdiv = 128;
+       } else {
+               lrdiv = 64;
+               if (div < (128 + 256) / 2)
+                       sdiv = 2;
+       }
 
        err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
        if (err)
@@ -314,10 +313,12 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
        struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
 
        if (!dai->active)
-               return;
+               return 0;
 
        ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
        ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+       return 0;
 }
 
 static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
@@ -325,10 +326,12 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
        struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
 
        if (!dai->active)
-               return;
+               return 0;
 
        ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
        ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+       return 0;
 }
 #else
 #define ep93xx_i2s_suspend     NULL
@@ -352,13 +355,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = {
        .playback       = {
                .channels_min   = 2,
                .channels_max   = 2,
-               .rates          = SNDRV_PCM_RATE_8000_96000,
+               .rates          = SNDRV_PCM_RATE_8000_192000,
                .formats        = EP93XX_I2S_FORMATS,
        },
        .capture        = {
                 .channels_min  = 2,
                 .channels_max  = 2,
-                .rates         = SNDRV_PCM_RATE_8000_96000,
+                .rates         = SNDRV_PCM_RATE_8000_192000,
                 .formats       = EP93XX_I2S_FORMATS,
        },
        .ops            = &ep93xx_i2s_dai_ops,
index 06670776f6497fe5f5c93b8dcbc82c511c3bbb55..a456e491155f6df55987d4b895cedf25f73f50c6 100644 (file)
@@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
                                   SNDRV_PCM_INFO_INTERLEAVED   |
                                   SNDRV_PCM_INFO_BLOCK_TRANSFER),
                                   
-       .rates                  = SNDRV_PCM_RATE_8000_96000,
+       .rates                  = SNDRV_PCM_RATE_8000_192000,
        .rate_min               = SNDRV_PCM_RATE_8000,
-       .rate_max               = SNDRV_PCM_RATE_96000,
+       .rate_max               = SNDRV_PCM_RATE_192000,
        
        .formats                = (SNDRV_PCM_FMTBIT_S16_LE |
                                   SNDRV_PCM_FMTBIT_S24_LE |
index 7d7847a1e66bbba18bf583137808b2e751e29cd9..c16c6b2eff9574ffcda744a95dbc59e8002d0e9d 100644 (file)
@@ -53,9 +53,8 @@ struct mpc8610_hpcd_data {
  *
  * Here we program the DMACR and PMUXCR registers.
  */
-static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
+static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = platform_get_drvdata(sound_device);
        struct mpc8610_hpcd_data *machine_data =
                container_of(card, struct mpc8610_hpcd_data, card);
        struct ccsr_guts_86xx __iomem *guts;
@@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
  */
-static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
+static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = platform_get_drvdata(sound_device);
        struct mpc8610_hpcd_data *machine_data =
                container_of(card, struct mpc8610_hpcd_data, card);
        struct ccsr_guts_86xx __iomem *guts;
index 026b756961e0eaa84dc995c8b3a2aaf6f1a9b3a2..66e0b68af147705224528e97000b97b94f272d36 100644 (file)
@@ -85,9 +85,8 @@ struct machine_data {
  *
  * Here we program the DMACR and PMUXCR registers.
  */
-static int p1022_ds_machine_probe(struct platform_device *sound_device)
+static int p1022_ds_machine_probe(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = platform_get_drvdata(sound_device);
        struct machine_data *mdata =
                container_of(card, struct machine_data, card);
        struct ccsr_guts_85xx __iomem *guts;
@@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream)
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
  */
-static int p1022_ds_machine_remove(struct platform_device *sound_device)
+static int p1022_ds_machine_remove(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = platform_get_drvdata(sound_device);
        struct machine_data *mdata =
                container_of(card, struct machine_data, card);
        struct ccsr_guts_85xx __iomem *guts;
index 9eeb8f0d67e90109578bbf16f3fc668b0d218cd4..d8f130d39dd904c341d539fbe5457fe976b285b1 100644 (file)
@@ -30,6 +30,16 @@ config SND_MXC_SOC_WM1133_EV1
          Enable support for audio on the i.MX31ADS with the WM1133-EV1
          PMIC board with WM8835x fitted.
 
+config SND_SOC_MX27VIS_AIC32X4
+       tristate "SoC audio support for Visstrim M10 boards"
+       depends on MACH_IMX27_VISSTRIM_M10
+       select SND_SOC_TVL320AIC32X4
+       select SND_MXC_SOC_SSI
+       select SND_MXC_SOC_MX2
+       help
+         Say Y if you want to add support for SoC audio on Visstrim SM10
+         board with TLV320AIC32X4 codec.
+
 config SND_SOC_PHYCORE_AC97
        tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
        depends on MACH_PCM043 || MACH_PCA100
index b67fc02a4eccd781bda485a02e8de2eca26816b7..d6d609ba7e24afa539e0cc9804caee1a43026379 100644 (file)
@@ -10,8 +10,10 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o
 # i.MX Machine Support
 snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
 snd-soc-phycore-ac97-objs := phycore-ac97.o
+snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
+obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
index 30894ea7f33328ad9be1fc47cd3aa81a4118b937..bc92ec620004f16a82feb202ddfc30f57bdf8c8e 100644 (file)
@@ -108,7 +108,7 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
                break;
        case SND_SOC_DAIFMT_DSP_B:
                /* data on rising edge of bclk, frame high with data */
-               strcr |= SSI_STCR_TFSL;
+               strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0;
                break;
        case SND_SOC_DAIFMT_DSP_A:
                /* data on rising edge of bclk, frame high 1clk before data */
@@ -656,6 +656,9 @@ static int imx_ssi_probe(struct platform_device *pdev)
        ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0;
        ssi->dma_params_tx.dma_addr = res->start + SSI_STX0;
 
+       ssi->dma_params_tx.burstsize = 4;
+       ssi->dma_params_rx.burstsize = 4;
+
        res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
        if (res)
                ssi->dma_params_tx.dma = res->start;
diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/imx/mx27vis-aic32x4.c
new file mode 100644 (file)
index 0000000..054110b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * mx27vis-aic32x4.c
+ *
+ * Copyright 2011 Vista Silicon S.L.
+ *
+ * Author: Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+#include <mach/audmux.h>
+
+#include "../codecs/tlv320aic32x4.h"
+#include "imx-ssi.h"
+
+static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       int ret;
+       u32 dai_format;
+
+       dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
+               SND_SOC_DAIFMT_CBM_CFM;
+
+       /* set codec DAI configuration */
+       snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+       /* set cpu DAI configuration */
+       snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+                                    25000000, SND_SOC_CLOCK_OUT);
+       if (ret) {
+               pr_err("%s: failed setting codec sysclk\n", __func__);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
+                               SND_SOC_CLOCK_IN);
+       if (ret) {
+               pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
+       .hw_params      = mx27vis_aic32x4_hw_params,
+};
+
+static struct snd_soc_dai_link mx27vis_aic32x4_dai = {
+       .name           = "tlv320aic32x4",
+       .stream_name    = "TLV320AIC32X4",
+       .codec_dai_name = "tlv320aic32x4-hifi",
+       .platform_name  = "imx-pcm-audio.0",
+       .codec_name     = "tlv320aic32x4.0-0018",
+       .cpu_dai_name   = "imx-ssi.0",
+       .ops            = &mx27vis_aic32x4_snd_ops,
+};
+
+static struct snd_soc_card mx27vis_aic32x4 = {
+       .name           = "visstrim_m10-audio",
+       .dai_link       = &mx27vis_aic32x4_dai,
+       .num_links      = 1,
+};
+
+static struct platform_device *mx27vis_aic32x4_snd_device;
+
+static int __init mx27vis_aic32x4_init(void)
+{
+       int ret;
+
+       mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!mx27vis_aic32x4_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4);
+       ret = platform_device_add(mx27vis_aic32x4_snd_device);
+
+       if (ret) {
+               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+               platform_device_put(mx27vis_aic32x4_snd_device);
+       }
+
+       /* Connect SSI0 as clock slave to SSI1 external pins */
+       mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
+                       MXC_AUDMUX_V1_PCR_SYN |
+                       MXC_AUDMUX_V1_PCR_TFSDIR |
+                       MXC_AUDMUX_V1_PCR_TCLKDIR |
+                       MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) |
+                       MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1)
+       );
+       mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1,
+                       MXC_AUDMUX_V1_PCR_SYN |
+                       MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
+       );
+
+       return ret;
+}
+
+static void __exit mx27vis_aic32x4_exit(void)
+{
+       platform_device_unregister(mx27vis_aic32x4_snd_device);
+}
+
+module_init(mx27vis_aic32x4_init);
+module_exit(mx27vis_aic32x4_exit);
+
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig
new file mode 100644 (file)
index 0000000..2935042
--- /dev/null
@@ -0,0 +1,14 @@
+config SND_MFLD_MACHINE
+       tristate "SOC Machine Audio driver for Intel Medfield MID platform"
+       depends on INTEL_SCU_IPC
+       depends on SND_INTEL_SST
+       select SND_SOC_SN95031
+       select SND_SST_PLATFORM
+       help
+          This adds support for ASoC machine driver for Intel(R) MID Medfield platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SST_PLATFORM
+       tristate
diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile
new file mode 100644 (file)
index 0000000..6398833
--- /dev/null
@@ -0,0 +1,5 @@
+snd-soc-sst-platform-objs := sst_platform.o
+snd-soc-mfld-machine-objs := mfld_machine.o
+
+obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
+obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c
new file mode 100644 (file)
index 0000000..429aa1b
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ *  mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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; version 2 of the License.
+ *
+ *  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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/sn95031.h"
+
+#define MID_MONO 1
+#define MID_STEREO 2
+#define MID_MAX_CAP 5
+#define MFLD_JACK_INSERT 0x04
+
+enum soc_mic_bias_zones {
+       MFLD_MV_START = 0,
+       /* mic bias volutage range for Headphones*/
+       MFLD_MV_HP = 400,
+       /* mic bias volutage range for American Headset*/
+       MFLD_MV_AM_HS = 650,
+       /* mic bias volutage range for Headset*/
+       MFLD_MV_HS = 2000,
+       MFLD_MV_UNDEFINED,
+};
+
+static unsigned int    hs_switch;
+static unsigned int    lo_dac;
+
+struct mfld_mc_private {
+       struct platform_device *socdev;
+       void __iomem *int_base;
+       struct snd_soc_codec *codec;
+       u8 interrupt_status;
+};
+
+struct snd_soc_jack mfld_jack;
+
+/*Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mfld_jack_pins[] = {
+       {
+               .pin = "Headphones",
+               .mask = SND_JACK_HEADPHONE,
+       },
+       {
+               .pin = "AMIC1",
+               .mask = SND_JACK_MICROPHONE,
+       },
+};
+
+/* jack detection voltage zones */
+static struct snd_soc_jack_zone mfld_zones[] = {
+       {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
+       {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
+};
+
+/* sound card controls */
+static const char *headset_switch_text[] = {"Earpiece", "Headset"};
+
+static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+
+static const struct soc_enum headset_enum =
+       SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
+
+static const struct soc_enum lo_enum =
+       SOC_ENUM_SINGLE_EXT(4, lo_text);
+
+static int headset_get_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = hs_switch;
+       return 0;
+}
+
+static int headset_set_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+       if (ucontrol->value.integer.value[0] == hs_switch)
+               return 0;
+
+       if (ucontrol->value.integer.value[0]) {
+               pr_debug("hs_set HS path\n");
+               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+       } else {
+               pr_debug("hs_set EP path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+       }
+       snd_soc_dapm_sync(&codec->dapm);
+       hs_switch = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+static void lo_enable_out_pins(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
+       snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
+       snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
+       snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
+       snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
+       snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
+       if (hs_switch) {
+               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+       } else {
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+       }
+}
+
+static int lo_get_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = lo_dac;
+       return 0;
+}
+
+static int lo_set_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+       if (ucontrol->value.integer.value[0] == lo_dac)
+               return 0;
+
+       /* we dont want to work with last state of lineout so just enable all
+        * pins and then disable pins not required
+        */
+       lo_enable_out_pins(codec);
+       switch (ucontrol->value.integer.value[0]) {
+       case 0:
+               pr_debug("set vibra path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
+               snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
+               break;
+
+       case 1:
+               pr_debug("set hs  path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
+               break;
+
+       case 2:
+               pr_debug("set spkr path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
+               snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
+               break;
+
+       case 3:
+               pr_debug("set null path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
+               snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
+               break;
+       }
+       snd_soc_dapm_sync(&codec->dapm);
+       lo_dac = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static const struct snd_kcontrol_new mfld_snd_controls[] = {
+       SOC_ENUM_EXT("Playback Switch", headset_enum,
+                       headset_get_switch, headset_set_switch),
+       SOC_ENUM_EXT("Lineout Mux", lo_enum,
+                       lo_get_switch, lo_set_switch),
+};
+
+static const struct snd_soc_dapm_widget mfld_widgets[] = {
+       SND_SOC_DAPM_HP("Headphones", NULL),
+       SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mfld_map[] = {
+       {"Headphones", NULL, "HPOUTR"},
+       {"Headphones", NULL, "HPOUTL"},
+       {"Mic", NULL, "AMIC1"},
+};
+
+static void mfld_jack_check(unsigned int intr_status)
+{
+       struct mfld_jack_data jack_data;
+
+       jack_data.mfld_jack = &mfld_jack;
+       jack_data.intr_id = intr_status;
+
+       sn95031_jack_detection(&jack_data);
+       /* TODO: add american headset detection post gpiolib support */
+}
+
+static int mfld_init(struct snd_soc_pcm_runtime *runtime)
+{
+       struct snd_soc_codec *codec = runtime->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int ret_val;
+
+       /* Add jack sense widgets */
+       snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets));
+
+       /* Set up the map */
+       snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));
+
+       /* always connected */
+       snd_soc_dapm_enable_pin(dapm, "Headphones");
+       snd_soc_dapm_enable_pin(dapm, "Mic");
+       snd_soc_dapm_sync(dapm);
+
+       ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
+                               ARRAY_SIZE(mfld_snd_controls));
+       if (ret_val) {
+               pr_err("soc_add_controls failed %d", ret_val);
+               return ret_val;
+       }
+       /* default is earpiece pin, userspace sets it explcitly */
+       snd_soc_dapm_disable_pin(dapm, "Headphones");
+       /* default is lineout NC, userspace sets it explcitly */
+       snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
+       snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
+       lo_dac = 3;
+       hs_switch = 0;
+       /* we dont use linein in this so set to NC */
+       snd_soc_dapm_disable_pin(dapm, "LINEINL");
+       snd_soc_dapm_disable_pin(dapm, "LINEINR");
+       snd_soc_dapm_sync(dapm);
+
+       /* Headset and button jack detection */
+       ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
+                       SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                       SND_JACK_BTN_1, &mfld_jack);
+       if (ret_val) {
+               pr_err("jack creation failed\n");
+               return ret_val;
+       }
+
+       ret_val = snd_soc_jack_add_pins(&mfld_jack,
+                       ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);
+       if (ret_val) {
+               pr_err("adding jack pins failed\n");
+               return ret_val;
+       }
+       ret_val = snd_soc_jack_add_zones(&mfld_jack,
+                       ARRAY_SIZE(mfld_zones), mfld_zones);
+       if (ret_val) {
+               pr_err("adding jack zones failed\n");
+               return ret_val;
+       }
+
+       /* we want to check if anything is inserted at boot,
+        * so send a fake event to codec and it will read adc
+        * to find if anything is there or not */
+       mfld_jack_check(MFLD_JACK_INSERT);
+       return ret_val;
+}
+
+struct snd_soc_dai_link mfld_msic_dailink[] = {
+       {
+               .name = "Medfield Headset",
+               .stream_name = "Headset",
+               .cpu_dai_name = "Headset-cpu-dai",
+               .codec_dai_name = "SN95031 Headset",
+               .codec_name = "sn95031",
+               .platform_name = "sst-platform",
+               .init = mfld_init,
+       },
+       {
+               .name = "Medfield Speaker",
+               .stream_name = "Speaker",
+               .cpu_dai_name = "Speaker-cpu-dai",
+               .codec_dai_name = "SN95031 Speaker",
+               .codec_name = "sn95031",
+               .platform_name = "sst-platform",
+               .init = NULL,
+       },
+       {
+               .name = "Medfield Vibra",
+               .stream_name = "Vibra1",
+               .cpu_dai_name = "Vibra1-cpu-dai",
+               .codec_dai_name = "SN95031 Vibra1",
+               .codec_name = "sn95031",
+               .platform_name = "sst-platform",
+               .init = NULL,
+       },
+       {
+               .name = "Medfield Haptics",
+               .stream_name = "Vibra2",
+               .cpu_dai_name = "Vibra2-cpu-dai",
+               .codec_dai_name = "SN95031 Vibra2",
+               .codec_name = "sn95031",
+               .platform_name = "sst-platform",
+               .init = NULL,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_mfld = {
+       .name = "medfield_audio",
+       .dai_link = mfld_msic_dailink,
+       .num_links = ARRAY_SIZE(mfld_msic_dailink),
+};
+
+static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
+{
+       struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
+
+       memcpy_fromio(&mc_private->interrupt_status,
+                       ((void *)(mc_private->int_base)),
+                       sizeof(u8));
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
+{
+       struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
+
+       if (mfld_jack.codec == NULL)
+               return IRQ_HANDLED;
+       mfld_jack_check(mc_drv_ctx->interrupt_status);
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0, irq;
+       struct mfld_mc_private *mc_drv_ctx;
+       struct resource *irq_mem;
+
+       pr_debug("snd_mfld_mc_probe called\n");
+
+       /* retrive the irq number */
+       irq = platform_get_irq(pdev, 0);
+
+       /* audio interrupt base of SRAM location where
+        * interrupts are stored by System FW */
+       mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
+       if (!mc_drv_ctx) {
+               pr_err("allocation failed\n");
+               return -ENOMEM;
+       }
+
+       irq_mem = platform_get_resource_byname(
+                               pdev, IORESOURCE_MEM, "IRQ_BASE");
+       if (!irq_mem) {
+               pr_err("no mem resource given\n");
+               ret_val = -ENODEV;
+               goto unalloc;
+       }
+       mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
+                                       resource_size(irq_mem));
+       if (!mc_drv_ctx->int_base) {
+               pr_err("Mapping of cache failed\n");
+               ret_val = -ENOMEM;
+               goto unalloc;
+       }
+       /* register for interrupt */
+       ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
+                       snd_mfld_jack_detection,
+                       IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
+       if (ret_val) {
+               pr_err("cannot register IRQ\n");
+               goto unalloc;
+       }
+       /* register the soc card */
+       snd_soc_card_mfld.dev = &pdev->dev;
+       ret_val = snd_soc_register_card(&snd_soc_card_mfld);
+       if (ret_val) {
+               pr_debug("snd_soc_register_card failed %d\n", ret_val);
+               goto freeirq;
+       }
+       platform_set_drvdata(pdev, mc_drv_ctx);
+       pr_debug("successfully exited probe\n");
+       return ret_val;
+
+freeirq:
+       free_irq(irq, mc_drv_ctx);
+unalloc:
+       kfree(mc_drv_ctx);
+       return ret_val;
+}
+
+static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
+{
+       struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
+
+       pr_debug("snd_mfld_mc_remove called\n");
+       free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
+       snd_soc_unregister_card(&snd_soc_card_mfld);
+       kfree(mc_drv_ctx);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct platform_driver snd_mfld_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "msic_audio",
+       },
+       .probe = snd_mfld_mc_probe,
+       .remove = __devexit_p(snd_mfld_mc_remove),
+};
+
+static int __init snd_mfld_driver_init(void)
+{
+       pr_debug("snd_mfld_driver_init called\n");
+       return platform_driver_register(&snd_mfld_mc_driver);
+}
+module_init(snd_mfld_driver_init);
+
+static void __exit snd_mfld_driver_exit(void)
+{
+       pr_debug("snd_mfld_driver_exit called\n");
+       platform_driver_unregister(&snd_mfld_mc_driver);
+}
+module_exit(snd_mfld_driver_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msic-audio");
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c
new file mode 100644 (file)
index 0000000..ee2c224
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ *  sst_platform.c - Intel MID Platform driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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; version 2 of the License.
+ *
+ *  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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h"
+#include "../../../drivers/staging/intel_sst/intel_sst.h"
+#include "sst_platform.h"
+
+static struct snd_pcm_hardware sst_platform_pcm_hw = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_DOUBLE |
+                       SNDRV_PCM_INFO_PAUSE |
+                       SNDRV_PCM_INFO_RESUME |
+                       SNDRV_PCM_INFO_MMAP|
+                       SNDRV_PCM_INFO_MMAP_VALID |
+                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                       SNDRV_PCM_INFO_SYNC_START),
+       .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
+                       SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
+                       SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
+       .rates = (SNDRV_PCM_RATE_8000|
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000),
+       .rate_min = SST_MIN_RATE,
+       .rate_max = SST_MAX_RATE,
+       .channels_min = SST_MIN_CHANNEL,
+       .channels_max = SST_MAX_CHANNEL,
+       .buffer_bytes_max = SST_MAX_BUFFER,
+       .period_bytes_min = SST_MIN_PERIOD_BYTES,
+       .period_bytes_max = SST_MAX_PERIOD_BYTES,
+       .periods_min = SST_MIN_PERIODS,
+       .periods_max = SST_MAX_PERIODS,
+       .fifo_size = SST_FIFO_SIZE,
+};
+
+/* MFLD - MSIC */
+struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+       .name = "Headset-cpu-dai",
+       .id = 0,
+       .playback = {
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 5,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+{
+       .name = "Speaker-cpu-dai",
+       .id = 1,
+       .playback = {
+               .channels_min = SST_MONO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+{
+       .name = "Vibra1-cpu-dai",
+       .id = 2,
+       .playback = {
+               .channels_min = SST_MONO,
+               .channels_max = SST_MONO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+{
+       .name = "Vibra2-cpu-dai",
+       .id = 3,
+       .playback = {
+               .channels_min = SST_MONO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+};
+
+/* helper functions */
+static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
+                                       int state)
+{
+       spin_lock(&stream->status_lock);
+       stream->stream_status = state;
+       spin_unlock(&stream->status_lock);
+}
+
+static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
+{
+       int state;
+
+       spin_lock(&stream->status_lock);
+       state = stream->stream_status;
+       spin_unlock(&stream->status_lock);
+       return state;
+}
+
+static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
+                               struct snd_sst_stream_params *param)
+{
+
+       param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
+       param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+       param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+       param->uc.pcm_params.reserved = 0;
+       param->uc.pcm_params.sfreq = substream->runtime->rate;
+       param->uc.pcm_params.ring_buffer_size =
+                                       snd_pcm_lib_buffer_bytes(substream);
+       param->uc.pcm_params.period_count = substream->runtime->period_size;
+       param->uc.pcm_params.ring_buffer_addr =
+                               virt_to_phys(substream->dma_buffer.area);
+       pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count);
+       pr_debug("sfreq= %d, wd_sz = %d\n",
+                param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz);
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
+{
+       struct sst_runtime_stream *stream =
+                       substream->runtime->private_data;
+       struct snd_sst_stream_params param = {{{0,},},};
+       struct snd_sst_params str_params = {0};
+       int ret_val;
+
+       /* set codec params and inform SST driver the same */
+       sst_fill_pcm_params(substream, &param);
+       substream->runtime->dma_area = substream->dma_buffer.area;
+       str_params.sparams = param;
+       str_params.codec =  param.uc.pcm_params.codec;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               str_params.ops = STREAM_OPS_PLAYBACK;
+               str_params.device_type = substream->pcm->device + 1;
+               pr_debug("Playbck stream,Device %d\n",
+                                       substream->pcm->device);
+       } else {
+               str_params.ops = STREAM_OPS_CAPTURE;
+               str_params.device_type = SND_SST_DEVICE_CAPTURE;
+               pr_debug("Capture stream,Device %d\n",
+                                       substream->pcm->device);
+       }
+       ret_val = stream->sstdrv_ops->pcm_control->open(&str_params);
+       pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+       if (ret_val < 0)
+               return ret_val;
+
+       stream->stream_info.str_id = ret_val;
+       pr_debug("str id :  %d\n", stream->stream_info.str_id);
+       return ret_val;
+}
+
+static void sst_period_elapsed(void *mad_substream)
+{
+       struct snd_pcm_substream *substream = mad_substream;
+       struct sst_runtime_stream *stream;
+       int status;
+
+       if (!substream || !substream->runtime)
+               return;
+       stream = substream->runtime->private_data;
+       if (!stream)
+               return;
+       status = sst_get_stream_status(stream);
+       if (status != SST_PLATFORM_RUNNING)
+               return;
+       snd_pcm_period_elapsed(substream);
+}
+
+static int sst_platform_init_stream(struct snd_pcm_substream *substream)
+{
+       struct sst_runtime_stream *stream =
+                       substream->runtime->private_data;
+       int ret_val;
+
+       pr_debug("setting buffer ptr param\n");
+       sst_set_stream_status(stream, SST_PLATFORM_INIT);
+       stream->stream_info.period_elapsed = sst_period_elapsed;
+       stream->stream_info.mad_substream = substream;
+       stream->stream_info.buffer_ptr = 0;
+       stream->stream_info.sfreq = substream->runtime->rate;
+       ret_val = stream->sstdrv_ops->pcm_control->device_control(
+                       SST_SND_STREAM_INIT, &stream->stream_info);
+       if (ret_val)
+               pr_err("control_set ret error %d\n", ret_val);
+       return ret_val;
+
+}
+/* end -- helper functions */
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime;
+       struct sst_runtime_stream *stream;
+       int ret_val = 0;
+
+       pr_debug("sst_platform_open called\n");
+       runtime = substream->runtime;
+       runtime->hw = sst_platform_pcm_hw;
+       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+       if (!stream)
+               return -ENOMEM;
+       spin_lock_init(&stream->status_lock);
+       stream->stream_info.str_id = 0;
+       sst_set_stream_status(stream, SST_PLATFORM_INIT);
+       stream->stream_info.mad_substream = substream;
+       /* allocate memory for SST API set */
+       stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops),
+                                                       GFP_KERNEL);
+       if (!stream->sstdrv_ops) {
+               pr_err("sst: mem allocation for ops fail\n");
+               kfree(stream);
+               return -ENOMEM;
+       }
+       stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+       /* registering with SST driver to get access to SST APIs to use */
+       ret_val = register_sst_card(stream->sstdrv_ops);
+       if (ret_val) {
+               pr_err("sst: sst card registration failed\n");
+               return ret_val;
+       }
+       runtime->private_data = stream;
+       return snd_pcm_hw_constraint_integer(runtime,
+                        SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int sst_platform_close(struct snd_pcm_substream *substream)
+{
+       struct sst_runtime_stream *stream;
+       int ret_val = 0, str_id;
+
+       pr_debug("sst_platform_close called\n");
+       stream = substream->runtime->private_data;
+       str_id = stream->stream_info.str_id;
+       if (str_id)
+               ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
+       kfree(stream->sstdrv_ops);
+       kfree(stream);
+       return ret_val;
+}
+
+static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct sst_runtime_stream *stream;
+       int ret_val = 0, str_id;
+
+       pr_debug("sst_platform_pcm_prepare called\n");
+       stream = substream->runtime->private_data;
+       str_id = stream->stream_info.str_id;
+       if (stream->stream_info.str_id) {
+               ret_val = stream->sstdrv_ops->pcm_control->device_control(
+                                       SST_SND_DROP, &str_id);
+               return ret_val;
+       }
+
+       ret_val = sst_platform_alloc_stream(substream);
+       if (ret_val < 0)
+               return ret_val;
+       snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+                       "%d", stream->stream_info.str_id);
+
+       ret_val = sst_platform_init_stream(substream);
+       if (ret_val)
+               return ret_val;
+       substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+       return ret_val;
+}
+
+static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
+                                       int cmd)
+{
+       int ret_val = 0, str_id;
+       struct sst_runtime_stream *stream;
+       int str_cmd, status;
+
+       pr_debug("sst_platform_pcm_trigger called\n");
+       stream = substream->runtime->private_data;
+       str_id = stream->stream_info.str_id;
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               pr_debug("sst: Trigger Start\n");
+               str_cmd = SST_SND_START;
+               status = SST_PLATFORM_RUNNING;
+               stream->stream_info.mad_substream = substream;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               pr_debug("sst: in stop\n");
+               str_cmd = SST_SND_DROP;
+               status = SST_PLATFORM_DROPPED;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               pr_debug("sst: in pause\n");
+               str_cmd = SST_SND_PAUSE;
+               status = SST_PLATFORM_PAUSED;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               pr_debug("sst: in pause release\n");
+               str_cmd = SST_SND_RESUME;
+               status = SST_PLATFORM_RUNNING;
+               break;
+       default:
+               return -EINVAL;
+       }
+       ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd,
+                                                               &str_id);
+       if (!ret_val)
+               sst_set_stream_status(stream, status);
+
+       return ret_val;
+}
+
+
+static snd_pcm_uframes_t sst_platform_pcm_pointer
+                       (struct snd_pcm_substream *substream)
+{
+       struct sst_runtime_stream *stream;
+       int ret_val, status;
+       struct pcm_stream_info *str_info;
+
+       stream = substream->runtime->private_data;
+       status = sst_get_stream_status(stream);
+       if (status == SST_PLATFORM_INIT)
+               return 0;
+       str_info = &stream->stream_info;
+       ret_val = stream->sstdrv_ops->pcm_control->device_control(
+                               SST_SND_BUFFER_POINTER, str_info);
+       if (ret_val) {
+               pr_err("sst: error code = %d\n", ret_val);
+               return ret_val;
+       }
+       return stream->stream_info.buffer_ptr;
+}
+
+static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params)
+{
+       snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+
+       return 0;
+}
+
+static struct snd_pcm_ops sst_platform_ops = {
+       .open = sst_platform_open,
+       .close = sst_platform_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .prepare = sst_platform_pcm_prepare,
+       .trigger = sst_platform_pcm_trigger,
+       .pointer = sst_platform_pcm_pointer,
+       .hw_params = sst_platform_pcm_hw_params,
+};
+
+static void sst_pcm_free(struct snd_pcm *pcm)
+{
+       pr_debug("sst_pcm_free called\n");
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+                       struct snd_pcm *pcm)
+{
+       int retval = 0;
+
+       pr_debug("sst_pcm_new called\n");
+       if (dai->driver->playback.channels_min ||
+                       dai->driver->capture.channels_min) {
+               retval =  snd_pcm_lib_preallocate_pages_for_all(pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       snd_dma_continuous_data(GFP_KERNEL),
+                       SST_MIN_BUFFER, SST_MAX_BUFFER);
+               if (retval) {
+                       pr_err("dma buffer allocationf fail\n");
+                       return retval;
+               }
+       }
+       return retval;
+}
+struct snd_soc_platform_driver sst_soc_platform_drv = {
+       .ops            = &sst_platform_ops,
+       .pcm_new        = sst_pcm_new,
+       .pcm_free       = sst_pcm_free,
+};
+
+static int sst_platform_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       pr_debug("sst_platform_probe called\n");
+       ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
+       if (ret) {
+               pr_err("registering soc platform failed\n");
+               return ret;
+       }
+
+       ret = snd_soc_register_dais(&pdev->dev,
+                               sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
+       if (ret) {
+               pr_err("registering cpu dais failed\n");
+               snd_soc_unregister_platform(&pdev->dev);
+       }
+       return ret;
+}
+
+static int sst_platform_remove(struct platform_device *pdev)
+{
+
+       snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
+       snd_soc_unregister_platform(&pdev->dev);
+       pr_debug("sst_platform_remove sucess\n");
+       return 0;
+}
+
+static struct platform_driver sst_platform_driver = {
+       .driver         = {
+               .name           = "sst-platform",
+               .owner          = THIS_MODULE,
+       },
+       .probe          = sst_platform_probe,
+       .remove         = sst_platform_remove,
+};
+
+static int __init sst_soc_platform_init(void)
+{
+       pr_debug("sst_soc_platform_init called\n");
+       return  platform_driver_register(&sst_platform_driver);
+}
+module_init(sst_soc_platform_init);
+
+static void __exit sst_soc_platform_exit(void)
+{
+       platform_driver_unregister(&sst_platform_driver);
+       pr_debug("sst_soc_platform_exit sucess\n");
+}
+module_exit(sst_soc_platform_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-platform");
diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/mid-x86/sst_platform.h
new file mode 100644 (file)
index 0000000..df37028
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  sst_platform.h - Intel MID Platform driver header file
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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; version 2 of the License.
+ *
+ *  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 __SST_PLATFORMDRV_H__
+#define __SST_PLATFORMDRV_H__
+
+#define SST_MONO               1
+#define SST_STEREO             2
+#define SST_MAX_CAP            5
+
+#define SST_MIN_RATE           8000
+#define SST_MAX_RATE           48000
+#define SST_MIN_CHANNEL                1
+#define SST_MAX_CHANNEL                5
+#define SST_MAX_BUFFER         (800*1024)
+#define SST_MIN_BUFFER         (800*1024)
+#define SST_MIN_PERIOD_BYTES   32
+#define SST_MAX_PERIOD_BYTES   SST_MAX_BUFFER
+#define SST_MIN_PERIODS                2
+#define SST_MAX_PERIODS                (1024*2)
+#define SST_FIFO_SIZE          0
+#define SST_CARD_NAMES         "intel_mid_card"
+#define MSIC_VENDOR_ID         3
+
+struct sst_runtime_stream {
+       int     stream_status;
+       struct pcm_stream_info stream_info;
+       struct intel_sst_card_ops *sstdrv_ops;
+       spinlock_t      status_lock;
+};
+
+enum sst_drv_status {
+       SST_PLATFORM_INIT = 1,
+       SST_PLATFORM_STARTED,
+       SST_PLATFORM_RUNNING,
+       SST_PLATFORM_PAUSED,
+       SST_PLATFORM_DROPPED,
+};
+
+#endif
index a088db6d50911799780838c0388cf9435d2c3a31..b5922984eac667008184a1475f1d5dc983f44e1a 100644 (file)
@@ -24,6 +24,7 @@ config SND_OMAP_SOC_RX51
        select OMAP_MCBSP
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
+       select SND_SOC_TPA6130A2
        help
          Say Y if you want to add support for SoC audio on Nokia RX-51
          hardware. This is also known as Nokia N900 product.
index 09fb0df8d416947f01ae2044324e1382ee13e294..d0986220eff97844e3bef90979f329e55f6fa847 100644 (file)
@@ -31,6 +31,7 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <plat/mcbsp.h>
+#include "../codecs/tpa6130a2.h"
 
 #include <asm/mach-types.h>
 
@@ -39,6 +40,7 @@
 
 #define RX51_TVOUT_SEL_GPIO            40
 #define RX51_JACK_DETECT_GPIO          177
+#define RX51_ECI_SW_GPIO               182
 /*
  * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
  * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -47,7 +49,9 @@
 
 enum {
        RX51_JACK_DISABLED,
-       RX51_JACK_TVOUT,                /* tv-out */
+       RX51_JACK_TVOUT,                /* tv-out with stereo output */
+       RX51_JACK_HP,                   /* headphone: stereo output, no mic */
+       RX51_JACK_HS,                   /* headset: stereo output with mic */
 };
 
 static int rx51_spk_func;
@@ -57,6 +61,19 @@ static int rx51_jack_func;
 static void rx51_ext_control(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int hp = 0, hs = 0, tvout = 0;
+
+       switch (rx51_jack_func) {
+       case RX51_JACK_TVOUT:
+               tvout = 1;
+               hp = 1;
+               break;
+       case RX51_JACK_HS:
+               hs = 1;
+       case RX51_JACK_HP:
+               hp = 1;
+               break;
+       }
 
        if (rx51_spk_func)
                snd_soc_dapm_enable_pin(dapm, "Ext Spk");
@@ -66,9 +83,16 @@ static void rx51_ext_control(struct snd_soc_codec *codec)
                snd_soc_dapm_enable_pin(dapm, "DMic");
        else
                snd_soc_dapm_disable_pin(dapm, "DMic");
+       if (hp)
+               snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+       else
+               snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+       if (hs)
+               snd_soc_dapm_enable_pin(dapm, "HS Mic");
+       else
+               snd_soc_dapm_disable_pin(dapm, "HS Mic");
 
-       gpio_set_value(RX51_TVOUT_SEL_GPIO,
-                      rx51_jack_func == RX51_JACK_TVOUT);
+       gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout);
 
        snd_soc_dapm_sync(dapm);
 }
@@ -153,6 +177,19 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int rx51_hp_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->dapm->codec;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               tpa6130a2_stereo_enable(codec, 1);
+       else
+               tpa6130a2_stereo_enable(codec, 0);
+
+       return 0;
+}
+
 static int rx51_get_input(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
@@ -203,7 +240,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
        {
                .gpio = RX51_JACK_DETECT_GPIO,
                .name = "avdet-gpio",
-               .report = SND_JACK_VIDEOOUT,
+               .report = SND_JACK_HEADSET,
                .invert = 1,
                .debounce_time = 200,
        },
@@ -212,19 +249,38 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
 static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
        SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
        SND_SOC_DAPM_MIC("DMic", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event),
+       SND_SOC_DAPM_MIC("HS Mic", NULL),
+       SND_SOC_DAPM_LINE("FM Transmitter", NULL),
+};
+
+static const struct snd_soc_dapm_widget aic34_dapm_widgetsb[] = {
+       SND_SOC_DAPM_SPK("Earphone", NULL),
 };
 
 static const struct snd_soc_dapm_route audio_map[] = {
        {"Ext Spk", NULL, "HPLOUT"},
        {"Ext Spk", NULL, "HPROUT"},
+       {"Headphone Jack", NULL, "LLOUT"},
+       {"Headphone Jack", NULL, "RLOUT"},
+       {"FM Transmitter", NULL, "LLOUT"},
+       {"FM Transmitter", NULL, "RLOUT"},
 
        {"DMic Rate 64", NULL, "Mic Bias 2V"},
        {"Mic Bias 2V", NULL, "DMic"},
 };
 
+static const struct snd_soc_dapm_route audio_mapb[] = {
+       {"b LINE2R", NULL, "MONO_LOUT"},
+       {"Earphone", NULL, "b HPLOUT"},
+
+       {"LINE1L", NULL, "b Mic Bias 2.5V"},
+       {"b Mic Bias 2.5V", NULL, "HS Mic"}
+};
+
 static const char *spk_function[] = {"Off", "On"};
 static const char *input_function[] = {"ADC", "Digital Mic"};
-static const char *jack_function[] = {"Off", "TV-OUT"};
+static const char *jack_function[] = {"Off", "TV-OUT", "Headphone", "Headset"};
 
 static const struct soc_enum rx51_enum[] = {
        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
@@ -239,6 +295,11 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = {
                     rx51_get_input, rx51_set_input),
        SOC_ENUM_EXT("Jack Function", rx51_enum[2],
                     rx51_get_jack, rx51_set_jack),
+       SOC_DAPM_PIN_SWITCH("FM Transmitter"),
+};
+
+static const struct snd_kcontrol_new aic34_rx51_controlsb[] = {
+       SOC_DAPM_PIN_SWITCH("Earphone"),
 };
 
 static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
@@ -265,11 +326,21 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
        /* Set up RX-51 specific audio path audio_map */
        snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 
+       err = tpa6130a2_add_controls(codec);
+       if (err < 0)
+               return err;
+       snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
+
+       err = omap_mcbsp_st_add_controls(codec, 1);
+       if (err < 0)
+               return err;
+
        snd_soc_dapm_sync(dapm);
 
        /* AV jack detection */
        err = snd_soc_jack_new(codec, "AV Jack",
-                              SND_JACK_VIDEOOUT, &rx51_av_jack);
+                              SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
+                              &rx51_av_jack);
        if (err)
                return err;
        err = snd_soc_jack_add_gpios(&rx51_av_jack,
@@ -279,6 +350,24 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
        return err;
 }
 
+static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm)
+{
+       int err;
+
+       err = snd_soc_add_controls(dapm->codec, aic34_rx51_controlsb,
+                                  ARRAY_SIZE(aic34_rx51_controlsb));
+       if (err < 0)
+               return err;
+
+       err = snd_soc_dapm_new_controls(dapm, aic34_dapm_widgetsb,
+                                       ARRAY_SIZE(aic34_dapm_widgetsb));
+       if (err < 0)
+               return 0;
+
+       return snd_soc_dapm_add_routes(dapm, audio_mapb,
+                                      ARRAY_SIZE(audio_mapb));
+}
+
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link rx51_dai[] = {
        {
@@ -293,11 +382,30 @@ static struct snd_soc_dai_link rx51_dai[] = {
        },
 };
 
+struct snd_soc_aux_dev rx51_aux_dev[] = {
+       {
+               .name = "TLV320AIC34b",
+               .codec_name = "tlv320aic3x-codec.2-0019",
+               .init = rx51_aic34b_init,
+       },
+};
+
+static struct snd_soc_codec_conf rx51_codec_conf[] = {
+       {
+               .dev_name = "tlv320aic3x-codec.2-0019",
+               .name_prefix = "b",
+       },
+};
+
 /* Audio card */
 static struct snd_soc_card rx51_sound_card = {
        .name = "RX-51",
        .dai_link = rx51_dai,
        .num_links = ARRAY_SIZE(rx51_dai),
+       .aux_dev = rx51_aux_dev,
+       .num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
+       .codec_conf = rx51_codec_conf,
+       .num_configs = ARRAY_SIZE(rx51_codec_conf),
 };
 
 static struct platform_device *rx51_snd_device;
@@ -309,10 +417,14 @@ static int __init rx51_soc_init(void)
        if (!machine_is_nokia_rx51())
                return -ENODEV;
 
-       err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel");
+       err = gpio_request_one(RX51_TVOUT_SEL_GPIO,
+                              GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel");
        if (err)
                goto err_gpio_tvout_sel;
-       gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0);
+       err = gpio_request_one(RX51_ECI_SW_GPIO,
+                              GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw");
+       if (err)
+               goto err_gpio_eci_sw;
 
        rx51_snd_device = platform_device_alloc("soc-audio", -1);
        if (!rx51_snd_device) {
@@ -330,6 +442,8 @@ static int __init rx51_soc_init(void)
 err2:
        platform_device_put(rx51_snd_device);
 err1:
+       gpio_free(RX51_ECI_SW_GPIO);
+err_gpio_eci_sw:
        gpio_free(RX51_TVOUT_SEL_GPIO);
 err_gpio_tvout_sel:
 
@@ -342,6 +456,7 @@ static void __exit rx51_soc_exit(void)
                                rx51_av_jack_gpios);
 
        platform_device_unregister(rx51_snd_device);
+       gpio_free(RX51_ECI_SW_GPIO);
        gpio_free(RX51_TVOUT_SEL_GPIO);
 }
 
index 0fd60f423036af3cba4518d74a494d45cf61cadc..2afabaf59491ed71ae331f827d40d04545e6b1b3 100644 (file)
@@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = {
        .hw_params = raumfeld_cs4270_hw_params,
 };
 
-static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state)
+static int raumfeld_line_suspend(struct snd_soc_card *card)
 {
        raumfeld_enable_audio(false);
        return 0;
 }
 
-static int raumfeld_line_resume(struct platform_device *pdev)
+static int raumfeld_line_resume(struct snd_soc_card *card)
 {
        raumfeld_enable_audio(true);
        return 0;
@@ -229,19 +229,19 @@ static struct snd_soc_dai_link raumfeld_dai[] = {
 {
        .name           = "ak4104",
        .stream_name    = "Playback",
-       .cpu_dai_name = "pxa-ssp-dai.1",
-       .codec_dai_name = "ak4104-hifi",
-       .platform_name = "pxa-pcm-audio",
+       .cpu_dai_name   = "pxa-ssp-dai.1",
+       .codec_dai_name = "ak4104-hifi",
+       .platform_name  = "pxa-pcm-audio",
        .ops            = &raumfeld_ak4104_ops,
-       .codec_name = "ak4104-codec.0",
+       .codec_name     = "ak4104-codec.0",
 },
 {
        .name           = "CS4270",
        .stream_name    = "CS4270",
-       .cpu_dai_name = "pxa-ssp-dai.0",
-       .platform_name = "pxa-pcm-audio",
-       .codec_dai_name = "cs4270-hifi",
-       .codec_name = "cs4270-codec.0-0048",
+       .cpu_dai_name   = "pxa-ssp-dai.0",
+       .platform_name  = "pxa-pcm-audio",
+       .codec_dai_name = "cs4270-hifi",
+       .codec_name     = "cs4270-codec.0-0048",
        .ops            = &raumfeld_cs4270_ops,
 },};
 
index 4b6e5d608b42e5cbb97742fe10a4c633c45d1443..9a2351366957a20ce85579fff47f376612bce34a 100644 (file)
@@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = {
 },
 };
 
-static int tosa_probe(struct platform_device *dev)
+static int tosa_probe(struct snd_soc_card *card)
 {
        int ret;
 
@@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev)
        return ret;
 }
 
-static int tosa_remove(struct platform_device *dev)
+static int tosa_remove(struct snd_soc_card *card)
 {
        gpio_free(TOSA_GPIO_L_MUTE);
        return 0;
index 3ceaef68e01de7aebab8d7d16a6715d3bcfedfbb..d69d9fc32233f0f8935920fc4fb68dcb4e03f9f2 100644 (file)
@@ -95,6 +95,11 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
                .pin    = "Headphone Jack",
                .mask   = SND_JACK_HEADPHONE,
        },
+       {
+               .pin    = "Ext Spk",
+               .mask   = SND_JACK_HEADPHONE,
+               .invert = 1
+       },
 };
 
 /* Headset jack detection gpios */
@@ -147,7 +152,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_disable_pin(dapm, "LINPUT3");
        snd_soc_dapm_disable_pin(dapm, "RINPUT3");
        snd_soc_dapm_disable_pin(dapm, "OUT3");
-       snd_soc_dapm_disable_pin(dapm, "MONO");
+       snd_soc_dapm_disable_pin(dapm, "MONO1");
 
        /* Add z2 specific widgets */
        snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets,
index 25bba108fea3295295236869c636113a03c76cd5..ac577263b3e3dd7205b7243ae7e434f0aef21575 100644 (file)
@@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = {
 },
 };
 
-static int zylonite_probe(struct platform_device *pdev)
+static int zylonite_probe(struct snd_soc_card *card)
 {
        int ret;
 
@@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int zylonite_remove(struct platform_device *pdev)
+static int zylonite_remove(struct snd_soc_card *card)
 {
        if (clk_pout) {
                clk_disable(pout);
@@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int zylonite_suspend_post(struct platform_device *pdev,
-                                pm_message_t state)
+static int zylonite_suspend_post(struct snd_soc_card *card)
 {
        if (clk_pout)
                clk_disable(pout);
@@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev,
        return 0;
 }
 
-static int zylonite_resume_pre(struct platform_device *pdev)
+static int zylonite_resume_pre(struct snd_soc_card *card)
 {
        int ret = 0;
 
index d6713d5a90e757bac2121c66289b4d43fe34bc62..a3fdfb63146938780e5bd7dfbfb46e5e256e4a36 100644 (file)
@@ -35,23 +35,16 @@ config SND_SAMSUNG_I2S
        tristate
 
 config SND_SOC_SAMSUNG_NEO1973_WM8753
-       tristate "SoC I2S Audio support for NEO1973 - WM8753"
-       depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA01
+       tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)"
+       depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02)
        select SND_S3C24XX_I2S
        select SND_SOC_WM8753
+       select SND_SOC_LM4857 if MACH_NEO1973_GTA01
+       select SND_SOC_DFBMCS320
        help
-         Say Y if you want to add support for SoC audio on smdk2440
-         with the WM8753.
+         Say Y here to enable audio support for the Openmoko Neo1973
+         Smartphones.
 
-config SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753
-       tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
-       depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
-       select SND_S3C24XX_I2S
-       select SND_SOC_WM8753
-       help
-         This driver provides audio support for the Openmoko Neo FreeRunner
-         smartphone.
-         
 config SND_SOC_SAMSUNG_JIVE_WM8750
        tristate "SoC I2S Audio support for Jive"
        depends on SND_SOC_SAMSUNG && MACH_JIVE
index 705d4e8a6724fc599830cd6572c5b2fc4dea61cd..294dec05c26d486e357eabe4ffba7f6574e7e0b0 100644 (file)
@@ -20,7 +20,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
 # S3C24XX Machine Support
 snd-soc-jive-wm8750-objs := jive_wm8750.o
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
-snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
 snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
 snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
@@ -38,7 +37,6 @@ snd-soc-smdk-spdif-objs := smdk_spdif.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
index 4770a9550341a3c5cf86781ea7505254ce045855..f97110e72e855d1da85253ec83212f185a29e152 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
 
 #include <sound/soc.h>
 
-#include <plat/regs-ac97.h>
 #include <mach/dma.h>
+#include <plat/regs-ac97.h>
 #include <plat/audio.h>
 
 #include "dma.h"
-#include "ac97.h"
 
 #define AC_CMD_ADDR(x) (x << 16)
 #define AC_CMD_DATA(x) (x & 0xffff)
 
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
 struct s3c_ac97_info {
        struct clk         *ac97_clk;
        void __iomem       *regs;
diff --git a/sound/soc/samsung/ac97.h b/sound/soc/samsung/ac97.h
deleted file mode 100644 (file)
index 0d0e1b5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* sound/soc/samsung/ac97.h
- *
- * ALSA SoC Audio Layer - S3C AC97 Controller driver
- *     Evolved from s3c2443-ac97.h
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd
- *     Author: Jaswinder Singh <jassi.brar@samsung.com>
- *     Credits: Graeme Gregory, Sean Choi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __S3C_AC97_H_
-#define __S3C_AC97_H_
-
-#define S3C_AC97_DAI_PCM 0
-#define S3C_AC97_DAI_MIC 1
-
-#endif /* __S3C_AC97_H_ */
index 21240198c5d6eacbef92609718dfd71592d8f787..5cb3b880f0d59e84f039dce9c0e519577b451c11 100644 (file)
  *  option) any later version.
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <asm/dma.h>
 #include <mach/hardware.h>
@@ -32,6 +26,9 @@
 
 #include "dma.h"
 
+#define ST_RUNNING             (1<<0)
+#define ST_OPENED              (1<<1)
+
 static const struct snd_pcm_hardware dma_hardware = {
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -313,7 +310,7 @@ dma_pointer(struct snd_pcm_substream *substream)
        /* we seem to be getting the odd error from the pcm library due
         * to out-of-bounds pointers. this is maybe due to the dma engine
         * not having loaded the new values for the channel before being
-        * callled... (todo - fix )
+        * called... (todo - fix )
         */
 
        if (res >= snd_pcm_lib_buffer_bytes(substream)) {
index f8cd2b4223afa6177b18b98f9ed5fdfccd8e009c..c50659269a40353c262d7134d26c8d8aa2a57af4 100644 (file)
@@ -12,9 +12,6 @@
 #ifndef _S3C_AUDIO_H
 #define _S3C_AUDIO_H
 
-#define ST_RUNNING             (1<<0)
-#define ST_OPENED              (1<<1)
-
 struct s3c_dma_params {
        struct s3c2410_dma_client *client;      /* stream identifier */
        int channel;                            /* Channel ID */
@@ -22,9 +19,4 @@ struct s3c_dma_params {
        int dma_size;                   /* Size of the DMA transfer */
 };
 
-#define S3C24XX_DAI_I2S                        0
-
-/* platform data */
-extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
-
 #endif
index 34dd9ef1b9c0f1130bb4f4e7716bf7cc68c08a51..f6b3a3ce59196078e11c670c94a417edc1517b5b 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
+
 #include <asm/mach-types.h>
 #include <mach/gpio.h>
-#include <mach/regs-clock.h>
 
-#include <linux/mfd/wm8994/core.h>
-#include <linux/mfd/wm8994/registers.h>
 #include "../codecs/wm8994.h"
-#include "dma.h"
-#include "i2s.h"
 
 #define MACHINE_NAME   0
 #define CPU_VOICE_DAI  1
index c45f7ce14d6161929123d260bf4527722e1f611f..241f55d0066070b299159c0f92f6f8836fed4ad5 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
 #include <linux/gpio.h>
 
 #include <sound/soc.h>
-#include <sound/uda1380.h>
 #include <sound/jack.h>
 
 #include <plat/regs-iis.h>
-
 #include <mach/h1940-latch.h>
-
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
 
 static unsigned int rates[] = {
        11025,
index d00ac3a7102c892196061af29a5d9bd0d3a5a1af..ffa09b3b2caaeedf98568331ec0825888829e5eb 100644 (file)
@@ -15,9 +15,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 
index 08802520e014e9478c8e65ddb942b5fdb8eb2e01..3b53ad54bc334d1e658eb71e5f7e038c4dee842f 100644 (file)
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c2412-i2s.h"
-
 #include "../codecs/wm8750.h"
 
 static const struct snd_soc_dapm_route audio_map[] = {
diff --git a/sound/soc/samsung/lm4857.h b/sound/soc/samsung/lm4857.h
deleted file mode 100644 (file)
index 0cf5b70..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * lm4857.h  --  ALSA Soc Audio Layer
- *
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- *  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.
- *
- *  Revision history
- *    18th Jun 2007   Initial version.
- */
-
-#ifndef LM4857_H_
-#define LM4857_H_
-
-/* The register offsets in the cache array */
-#define LM4857_MVOL 0
-#define LM4857_LVOL 1
-#define LM4857_RVOL 2
-#define LM4857_CTRL 3
-
-/* the shifts required to set these bits */
-#define LM4857_3D 5
-#define LM4857_WAKEUP 5
-#define LM4857_EPGAIN 4
-
-#endif /*LM4857_H_*/
-
index a2bb34def740e1968d69a39da1cd7a39a4ef1a1e..bd91c19a6c08bb9fca72dffb58b95c5c3b07cb00 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card ln2440sbc;
 
 static struct snd_soc_dai_link ln2440sbc_dai[] = {
diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c
deleted file mode 100644 (file)
index 0d0ae2b..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * neo1973_gta02_wm8753.c  --  SoC audio for Openmoko Freerunner(GTA02)
- *
- * Copyright 2007 Openmoko Inc
- * Author: Graeme Gregory <graeme@openmoko.org>
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory <linux@wolfsonmicro.com>
- * Copyright 2009 Wolfson Microelectronics
- *
- *  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.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-
-#include <plat/regs-iis.h>
-
-#include <mach/regs-clock.h>
-#include <asm/io.h>
-#include <mach/gta02.h>
-#include "../codecs/wm8753.h"
-#include "dma.h"
-#include "s3c24xx-i2s.h"
-
-static struct snd_soc_card neo1973_gta02;
-
-static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       unsigned int pll_out = 0, bclk = 0;
-       int ret = 0;
-       unsigned long iis_clkrate;
-
-       iis_clkrate = s3c24xx_i2s_get_clockrate();
-
-       switch (params_rate(params)) {
-       case 8000:
-       case 16000:
-               pll_out = 12288000;
-               break;
-       case 48000:
-               bclk = WM8753_BCLK_DIV_4;
-               pll_out = 12288000;
-               break;
-       case 96000:
-               bclk = WM8753_BCLK_DIV_2;
-               pll_out = 12288000;
-               break;
-       case 11025:
-               bclk = WM8753_BCLK_DIV_16;
-               pll_out = 11289600;
-               break;
-       case 22050:
-               bclk = WM8753_BCLK_DIV_8;
-               pll_out = 11289600;
-               break;
-       case 44100:
-               bclk = WM8753_BCLK_DIV_4;
-               pll_out = 11289600;
-               break;
-       case 88200:
-               bclk = WM8753_BCLK_DIV_2;
-               pll_out = 11289600;
-               break;
-       }
-
-       /* set codec DAI configuration */
-       ret = snd_soc_dai_set_fmt(codec_dai,
-               SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-               SND_SOC_DAIFMT_CBM_CFM);
-       if (ret < 0)
-               return ret;
-
-       /* set cpu DAI configuration */
-       ret = snd_soc_dai_set_fmt(cpu_dai,
-               SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-               SND_SOC_DAIFMT_CBM_CFM);
-       if (ret < 0)
-               return ret;
-
-       /* set the codec system clock for DAC and ADC */
-       ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
-               SND_SOC_CLOCK_IN);
-       if (ret < 0)
-               return ret;
-
-       /* set MCLK division for sample rate */
-       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
-               S3C2410_IISMOD_32FS);
-       if (ret < 0)
-               return ret;
-
-       /* set codec BCLK division for sample rate */
-       ret = snd_soc_dai_set_clkdiv(codec_dai,
-                                       WM8753_BCLKDIV, bclk);
-       if (ret < 0)
-               return ret;
-
-       /* set prescaler division for sample rate */
-       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
-               S3C24XX_PRESCALE(4, 4));
-       if (ret < 0)
-               return ret;
-
-       /* codec PLL input is PCLK/4 */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
-               iis_clkrate / 4, pll_out);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
-       /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
-}
-
-/*
- * Neo1973 WM8753 HiFi DAI opserations.
- */
-static struct snd_soc_ops neo1973_gta02_hifi_ops = {
-       .hw_params = neo1973_gta02_hifi_hw_params,
-       .hw_free = neo1973_gta02_hifi_hw_free,
-};
-
-static int neo1973_gta02_voice_hw_params(
-       struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       unsigned int pcmdiv = 0;
-       int ret = 0;
-       unsigned long iis_clkrate;
-
-       iis_clkrate = s3c24xx_i2s_get_clockrate();
-
-       if (params_rate(params) != 8000)
-               return -EINVAL;
-       if (params_channels(params) != 1)
-               return -EINVAL;
-
-       pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
-
-       /* todo: gg check mode (DSP_B) against CSR datasheet */
-       /* set codec DAI configuration */
-       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
-               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-       if (ret < 0)
-               return ret;
-
-       /* set the codec system clock for DAC and ADC */
-       ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK,
-               12288000, SND_SOC_CLOCK_IN);
-       if (ret < 0)
-               return ret;
-
-       /* set codec PCM division for sample rate */
-       ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV,
-                                       pcmdiv);
-       if (ret < 0)
-               return ret;
-
-       /* configure and enable PLL for 12.288MHz output */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
-               iis_clkrate / 4, 12288000);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
-       /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
-}
-
-static struct snd_soc_ops neo1973_gta02_voice_ops = {
-       .hw_params = neo1973_gta02_voice_hw_params,
-       .hw_free = neo1973_gta02_voice_hw_free,
-};
-
-#define LM4853_AMP 1
-#define LM4853_SPK 2
-
-static u8 lm4853_state;
-
-/* This has no effect, it exists only to maintain compatibility with
- * existing ALSA state files.
- */
-static int lm4853_set_state(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       int val = ucontrol->value.integer.value[0];
-
-       if (val)
-               lm4853_state |= LM4853_AMP;
-       else
-               lm4853_state &= ~LM4853_AMP;
-
-       return 0;
-}
-
-static int lm4853_get_state(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP;
-
-       return 0;
-}
-
-static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       int val = ucontrol->value.integer.value[0];
-
-       if (val) {
-               lm4853_state |= LM4853_SPK;
-               gpio_set_value(GTA02_GPIO_HP_IN, 0);
-       } else {
-               lm4853_state &= ~LM4853_SPK;
-               gpio_set_value(GTA02_GPIO_HP_IN, 1);
-       }
-
-       return 0;
-}
-
-static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1;
-
-       return 0;
-}
-
-static int lm4853_event(struct snd_soc_dapm_widget *w,
-                       struct snd_kcontrol *k,
-                       int event)
-{
-       gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event));
-
-       return 0;
-}
-
-static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
-       SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
-       SND_SOC_DAPM_LINE("GSM Line Out", NULL),
-       SND_SOC_DAPM_LINE("GSM Line In", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-       SND_SOC_DAPM_MIC("Handset Mic", NULL),
-       SND_SOC_DAPM_SPK("Handset Spk", NULL),
-};
-
-
-/* example machine audio_mapnections */
-static const struct snd_soc_dapm_route audio_map[] = {
-
-       /* Connections to the lm4853 amp */
-       {"Stereo Out", NULL, "LOUT1"},
-       {"Stereo Out", NULL, "ROUT1"},
-
-       /* Connections to the GSM Module */
-       {"GSM Line Out", NULL, "MONO1"},
-       {"GSM Line Out", NULL, "MONO2"},
-       {"RXP", NULL, "GSM Line In"},
-       {"RXN", NULL, "GSM Line In"},
-
-       /* Connections to Headset */
-       {"MIC1", NULL, "Mic Bias"},
-       {"Mic Bias", NULL, "Headset Mic"},
-
-       /* Call Mic */
-       {"MIC2", NULL, "Mic Bias"},
-       {"MIC2N", NULL, "Mic Bias"},
-       {"Mic Bias", NULL, "Handset Mic"},
-
-       /* Call Speaker */
-       {"Handset Spk", NULL, "LOUT2"},
-       {"Handset Spk", NULL, "ROUT2"},
-
-       /* Connect the ALC pins */
-       {"ACIN", NULL, "ACOP"},
-};
-
-static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = {
-       SOC_DAPM_PIN_SWITCH("Stereo Out"),
-       SOC_DAPM_PIN_SWITCH("GSM Line Out"),
-       SOC_DAPM_PIN_SWITCH("GSM Line In"),
-       SOC_DAPM_PIN_SWITCH("Headset Mic"),
-       SOC_DAPM_PIN_SWITCH("Handset Mic"),
-       SOC_DAPM_PIN_SWITCH("Handset Spk"),
-
-       /* This has no effect, it exists only to maintain compatibility with
-        * existing ALSA state files.
-        */
-       SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0,
-               lm4853_get_state,
-               lm4853_set_state),
-       SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0,
-               lm4853_get_spk,
-               lm4853_set_spk),
-};
-
-/*
- * This is an example machine initialisation for a wm8753 connected to a
- * neo1973 GTA02.
- */
-static int neo1973_gta02_wm8753_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int err;
-
-       /* set up NC codec pins */
-       snd_soc_dapm_nc_pin(dapm, "OUT3");
-       snd_soc_dapm_nc_pin(dapm, "OUT4");
-       snd_soc_dapm_nc_pin(dapm, "LINE1");
-       snd_soc_dapm_nc_pin(dapm, "LINE2");
-
-       /* Add neo1973 gta02 specific widgets */
-       snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets,
-                                 ARRAY_SIZE(wm8753_dapm_widgets));
-
-       /* add neo1973 gta02 specific controls */
-       err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls,
-               ARRAY_SIZE(wm8753_neo1973_gta02_controls));
-
-       if (err < 0)
-               return err;
-
-       /* set up neo1973 gta02 specific audio path audio_map */
-       snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
-       /* set endpoints to default off mode */
-       snd_soc_dapm_disable_pin(dapm, "Stereo Out");
-       snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-       snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-       snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-       snd_soc_dapm_disable_pin(dapm, "Handset Mic");
-       snd_soc_dapm_disable_pin(dapm, "Handset Spk");
-
-       /* allow audio paths from the GSM modem to run during suspend */
-       snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
-       snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out");
-       snd_soc_dapm_ignore_suspend(dapm, "GSM Line In");
-       snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
-       snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
-       snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
-
-       snd_soc_dapm_sync(dapm);
-
-       return 0;
-}
-
-/*
- * BT Codec DAI
- */
-static struct snd_soc_dai_driver bt_dai = {
-       .name = "bluetooth-dai",
-       .playback = {
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .capture = {
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-};
-
-static struct snd_soc_dai_link neo1973_gta02_dai[] = {
-{ /* Hifi Playback - for similatious use with voice below */
-       .name = "WM8753",
-       .stream_name = "WM8753 HiFi",
-       .cpu_dai_name = "s3c24xx-iis",
-       .codec_dai_name = "wm8753-hifi",
-       .init = neo1973_gta02_wm8753_init,
-       .platform_name = "samsung-audio",
-       .codec_name = "wm8753-codec.0-001a",
-       .ops = &neo1973_gta02_hifi_ops,
-},
-{ /* Voice via BT */
-       .name = "Bluetooth",
-       .stream_name = "Voice",
-       .cpu_dai_name = "bluetooth-dai",
-       .codec_dai_name = "wm8753-voice",
-       .ops = &neo1973_gta02_voice_ops,
-       .codec_name = "wm8753-codec.0-001a",
-       .platform_name = "samsung-audio",
-},
-};
-
-static struct snd_soc_card neo1973_gta02 = {
-       .name = "neo1973-gta02",
-       .dai_link = neo1973_gta02_dai,
-       .num_links = ARRAY_SIZE(neo1973_gta02_dai),
-};
-
-static struct platform_device *neo1973_gta02_snd_device;
-
-static int __init neo1973_gta02_init(void)
-{
-       int ret;
-
-       if (!machine_is_neo1973_gta02()) {
-               printk(KERN_INFO
-                      "Only GTA02 is supported by this ASoC driver\n");
-               return -ENODEV;
-       }
-
-       neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!neo1973_gta02_snd_device)
-               return -ENOMEM;
-
-       /* register bluetooth DAI here */
-       ret = snd_soc_register_dai(&neo1973_gta02_snd_device->dev, &bt_dai);
-       if (ret)
-               goto err_put_device;
-
-       platform_set_drvdata(neo1973_gta02_snd_device, &neo1973_gta02);
-       ret = platform_device_add(neo1973_gta02_snd_device);
-
-       if (ret)
-               goto err_unregister_dai;
-
-       /* Initialise GPIOs used by amp */
-       ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN");
-       if (ret) {
-               pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN);
-               goto err_del_device;
-       }
-
-       ret = gpio_direction_output(GTA02_GPIO_HP_IN, 1);
-       if (ret) {
-               pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN);
-               goto err_free_gpio_hp_in;
-       }
-
-       ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT");
-       if (ret) {
-               pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT);
-               goto err_free_gpio_hp_in;
-       }
-
-       ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1);
-       if (ret) {
-               pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT);
-               goto err_free_gpio_amp_shut;
-       }
-
-       return 0;
-
-err_free_gpio_amp_shut:
-       gpio_free(GTA02_GPIO_AMP_SHUT);
-err_free_gpio_hp_in:
-       gpio_free(GTA02_GPIO_HP_IN);
-err_del_device:
-       platform_device_del(neo1973_gta02_snd_device);
-err_unregister_dai:
-       snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev);
-err_put_device:
-       platform_device_put(neo1973_gta02_snd_device);
-       return ret;
-}
-module_init(neo1973_gta02_init);
-
-static void __exit neo1973_gta02_exit(void)
-{
-       snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev);
-       platform_device_unregister(neo1973_gta02_snd_device);
-       gpio_free(GTA02_GPIO_HP_IN);
-       gpio_free(GTA02_GPIO_AMP_SHUT);
-}
-module_exit(neo1973_gta02_exit);
-
-/* Module information */
-MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org");
-MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02");
-MODULE_LICENSE("GPL");
index d20815d5ab2eea86317f50c664604f6437269d08..78bfdb3f5d7e0a289be0d11a473e74bdec3edd07 100644 (file)
@@ -1,57 +1,32 @@
 /*
- * neo1973_wm8753.c  --  SoC audio for Neo1973
+ * neo1973_wm8753.c  --  SoC audio for Openmoko Neo1973 and Freerunner devices
  *
+ * Copyright 2007 Openmoko Inc
+ * Author: Graeme Gregory <graeme@openmoko.org>
  * Copyright 2007 Wolfson Microelectronics PLC.
  * Author: Graeme Gregory
  *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Copyright 2009 Wolfson Microelectronics
  *
  *  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.
- *
  */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
+#include <linux/gpio.h>
+
 #include <sound/soc.h>
-#include <sound/tlv.h>
 
 #include <asm/mach-types.h>
-#include <asm/hardware/scoop.h>
-#include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-#include <mach/hardware.h>
-#include <linux/io.h>
-#include <mach/spi-gpio.h>
-
 #include <plat/regs-iis.h>
+#include <mach/gta02.h>
 
 #include "../codecs/wm8753.h"
-#include "lm4857.h"
-#include "dma.h"
 #include "s3c24xx-i2s.h"
 
-/* define the scenarios */
-#define NEO_AUDIO_OFF                  0
-#define NEO_GSM_CALL_AUDIO_HANDSET     1
-#define NEO_GSM_CALL_AUDIO_HEADSET     2
-#define NEO_GSM_CALL_AUDIO_BLUETOOTH   3
-#define NEO_STEREO_TO_SPEAKERS         4
-#define NEO_STEREO_TO_HEADPHONES       5
-#define NEO_CAPTURE_HANDSET            6
-#define NEO_CAPTURE_HEADSET            7
-#define NEO_CAPTURE_BLUETOOTH          8
-
-static struct snd_soc_card neo1973;
-static struct i2c_client *i2c;
-
 static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
@@ -62,8 +37,6 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
        int ret = 0;
        unsigned long iis_clkrate;
 
-       pr_debug("Entered %s\n", __func__);
-
        iis_clkrate = s3c24xx_i2s_get_clockrate();
 
        switch (params_rate(params)) {
@@ -148,8 +121,6 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
-       pr_debug("Entered %s\n", __func__);
-
        /* disable the PLL */
        return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
@@ -171,8 +142,6 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
        int ret = 0;
        unsigned long iis_clkrate;
 
-       pr_debug("Entered %s\n", __func__);
-
        iis_clkrate = s3c24xx_i2s_get_clockrate();
 
        if (params_rate(params) != 8000)
@@ -214,8 +183,6 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
-       pr_debug("Entered %s\n", __func__);
-
        /* disable the PLL */
        return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
@@ -225,335 +192,232 @@ static struct snd_soc_ops neo1973_voice_ops = {
        .hw_free = neo1973_voice_hw_free,
 };
 
-static int neo1973_scenario;
-
-static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       ucontrol->value.integer.value[0] = neo1973_scenario;
-       return 0;
-}
-
-static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
-{
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       pr_debug("Entered %s\n", __func__);
-
-       switch (neo1973_scenario) {
-       case NEO_AUDIO_OFF:
-               snd_soc_dapm_disable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       case NEO_GSM_CALL_AUDIO_HANDSET:
-               snd_soc_dapm_enable_pin(dapm, "Audio Out");
-               snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_enable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_enable_pin(dapm, "Call Mic");
-               break;
-       case NEO_GSM_CALL_AUDIO_HEADSET:
-               snd_soc_dapm_enable_pin(dapm, "Audio Out");
-               snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_enable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_enable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       case NEO_GSM_CALL_AUDIO_BLUETOOTH:
-               snd_soc_dapm_disable_pin(dapm, "Audio Out");
-               snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_enable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       case NEO_STEREO_TO_SPEAKERS:
-               snd_soc_dapm_enable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       case NEO_STEREO_TO_HEADPHONES:
-               snd_soc_dapm_enable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       case NEO_CAPTURE_HANDSET:
-               snd_soc_dapm_disable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_enable_pin(dapm, "Call Mic");
-               break;
-       case NEO_CAPTURE_HEADSET:
-               snd_soc_dapm_disable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_enable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       case NEO_CAPTURE_BLUETOOTH:
-               snd_soc_dapm_disable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-               break;
-       default:
-               snd_soc_dapm_disable_pin(dapm, "Audio Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-               snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-               snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-               snd_soc_dapm_disable_pin(dapm, "Call Mic");
-       }
+/* Shared routes and controls */
 
-       snd_soc_dapm_sync(dapm);
+static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+       SND_SOC_DAPM_LINE("GSM Line In", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Handset Mic", NULL),
+};
 
-       return 0;
-}
+static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {
+       /* Connections to the GSM Module */
+       {"GSM Line Out", NULL, "MONO1"},
+       {"GSM Line Out", NULL, "MONO2"},
+       {"RXP", NULL, "GSM Line In"},
+       {"RXN", NULL, "GSM Line In"},
 
-static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       /* Connections to Headset */
+       {"MIC1", NULL, "Mic Bias"},
+       {"Mic Bias", NULL, "Headset Mic"},
 
-       pr_debug("Entered %s\n", __func__);
+       /* Call Mic */
+       {"MIC2", NULL, "Mic Bias"},
+       {"MIC2N", NULL, "Mic Bias"},
+       {"Mic Bias", NULL, "Handset Mic"},
 
-       if (neo1973_scenario == ucontrol->value.integer.value[0])
-               return 0;
+       /* Connect the ALC pins */
+       {"ACIN", NULL, "ACOP"},
+};
 
-       neo1973_scenario = ucontrol->value.integer.value[0];
-       set_scenario_endpoints(codec, neo1973_scenario);
-       return 1;
-}
+static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
+       SOC_DAPM_PIN_SWITCH("GSM Line Out"),
+       SOC_DAPM_PIN_SWITCH("GSM Line In"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Handset Mic"),
+};
 
-static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
+/* GTA02 specific routes and controlls */
 
-static void lm4857_write_regs(void)
-{
-       pr_debug("Entered %s\n", __func__);
+#ifdef CONFIG_MACH_NEO1973_GTA02
 
-       if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
-               printk(KERN_ERR "lm4857: i2c write failed\n");
-}
+static int gta02_speaker_enabled;
 
-static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
+static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int reg = mc->reg;
-       int shift = mc->shift;
-       int mask = mc->max;
+       gta02_speaker_enabled = ucontrol->value.integer.value[0];
 
-       pr_debug("Entered %s\n", __func__);
+       gpio_set_value(GTA02_GPIO_HP_IN, !gta02_speaker_enabled);
 
-       ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
        return 0;
 }
 
-static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
+static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int reg = mc->reg;
-       int shift = mc->shift;
-       int mask = mc->max;
-
-       if (((lm4857_regs[reg] >> shift) & mask) ==
-               ucontrol->value.integer.value[0])
-               return 0;
-
-       lm4857_regs[reg] &= ~(mask << shift);
-       lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
-       lm4857_write_regs();
-       return 1;
+       ucontrol->value.integer.value[0] = gta02_speaker_enabled;
+       return 0;
 }
 
-static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static int lm4853_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
 {
-       u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
-
-       pr_debug("Entered %s\n", __func__);
-
-       if (value)
-               value -= 5;
+       gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event));
 
-       ucontrol->value.integer.value[0] = value;
        return 0;
 }
 
-static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       u8 value = ucontrol->value.integer.value[0];
-
-       pr_debug("Entered %s\n", __func__);
-
-       if (value)
-               value += 5;
-
-       if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
-               return 0;
-
-       lm4857_regs[LM4857_CTRL] &= 0xF0;
-       lm4857_regs[LM4857_CTRL] |= value;
-       lm4857_write_regs();
-       return 1;
-}
+static const struct snd_soc_dapm_route neo1973_gta02_routes[] = {
+       /* Connections to the amp */
+       {"Stereo Out", NULL, "LOUT1"},
+       {"Stereo Out", NULL, "ROUT1"},
 
-static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
-       SND_SOC_DAPM_LINE("Audio Out", NULL),
-       SND_SOC_DAPM_LINE("GSM Line Out", NULL),
-       SND_SOC_DAPM_LINE("GSM Line In", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-       SND_SOC_DAPM_MIC("Call Mic", NULL),
+       /* Call Speaker */
+       {"Handset Spk", NULL, "LOUT2"},
+       {"Handset Spk", NULL, "ROUT2"},
 };
 
+static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Handset Spk"),
+       SOC_DAPM_PIN_SWITCH("Stereo Out"),
 
-static const struct snd_soc_dapm_route dapm_routes[] = {
-
-       /* Connections to the lm4857 amp */
-       {"Audio Out", NULL, "LOUT1"},
-       {"Audio Out", NULL, "ROUT1"},
-
-       /* Connections to the GSM Module */
-       {"GSM Line Out", NULL, "MONO1"},
-       {"GSM Line Out", NULL, "MONO2"},
-       {"RXP", NULL, "GSM Line In"},
-       {"RXN", NULL, "GSM Line In"},
+       SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0,
+               lm4853_get_spk,
+               lm4853_set_spk),
+};
 
-       /* Connections to Headset */
-       {"MIC1", NULL, "Mic Bias"},
-       {"Mic Bias", NULL, "Headset Mic"},
+static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("Handset Spk", NULL),
+       SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
+};
 
-       /* Call Mic */
-       {"MIC2", NULL, "Mic Bias"},
-       {"MIC2N", NULL, "Mic Bias"},
-       {"Mic Bias", NULL, "Call Mic"},
+static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)
+{
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int ret;
 
-       /* Connect the ALC pins */
-       {"ACIN", NULL, "ACOP"},
-};
+       ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets,
+                       ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets));
+       if (ret)
+               return ret;
 
-static const char *lm4857_mode[] = {
-       "Off",
-       "Call Speaker",
-       "Stereo Speakers",
-       "Stereo Speakers + Headphones",
-       "Headphones"
-};
+       ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes,
+                       ARRAY_SIZE(neo1973_gta02_routes));
+       if (ret)
+               return ret;
 
-static const struct soc_enum lm4857_mode_enum[] = {
-       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
-};
+       ret = snd_soc_add_controls(codec, neo1973_gta02_wm8753_controls,
+                       ARRAY_SIZE(neo1973_gta02_wm8753_controls));
+       if (ret)
+               return ret;
 
-static const char *neo_scenarios[] = {
-       "Off",
-       "GSM Handset",
-       "GSM Headset",
-       "GSM Bluetooth",
-       "Speakers",
-       "Headphones",
-       "Capture Handset",
-       "Capture Headset",
-       "Capture Bluetooth"
-};
+       snd_soc_dapm_disable_pin(dapm, "Stereo Out");
+       snd_soc_dapm_disable_pin(dapm, "Handset Spk");
+       snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
+       snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
 
-static const struct soc_enum neo_scenario_enum[] = {
-       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios),
-};
+       return 0;
+}
 
-static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
-static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
-
-static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
-       SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
-               lm4857_get_reg, lm4857_set_reg, stereo_tlv),
-       SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
-               lm4857_get_reg, lm4857_set_reg, stereo_tlv),
-       SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
-               lm4857_get_reg, lm4857_set_reg, mono_tlv),
-       SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
-               lm4857_get_mode, lm4857_set_mode),
-       SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
-               neo1973_get_scenario, neo1973_set_scenario),
-       SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
-               lm4857_get_reg, lm4857_set_reg),
-       SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
-               lm4857_get_reg, lm4857_set_reg),
-       SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
-               lm4857_get_reg, lm4857_set_reg),
-       SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
-               lm4857_get_reg, lm4857_set_reg),
-};
+#else
+static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; }
+#endif
 
-/*
- * This is an example machine initialisation for a wm8753 connected to a
- * neo1973 II. It is missing logic to detect hp/mic insertions and logic
- * to re-route the audio in such an event.
- */
 static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int err;
-
-       pr_debug("Entered %s\n", __func__);
+       int ret;
 
        /* set up NC codec pins */
-       snd_soc_dapm_nc_pin(dapm, "LOUT2");
-       snd_soc_dapm_nc_pin(dapm, "ROUT2");
+       if (machine_is_neo1973_gta01()) {
+               snd_soc_dapm_nc_pin(dapm, "LOUT2");
+               snd_soc_dapm_nc_pin(dapm, "ROUT2");
+       }
        snd_soc_dapm_nc_pin(dapm, "OUT3");
        snd_soc_dapm_nc_pin(dapm, "OUT4");
        snd_soc_dapm_nc_pin(dapm, "LINE1");
        snd_soc_dapm_nc_pin(dapm, "LINE2");
 
        /* Add neo1973 specific widgets */
-       snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets,
-                                 ARRAY_SIZE(wm8753_dapm_widgets));
-
-       /* set endpoints to default mode */
-       set_scenario_endpoints(codec, NEO_AUDIO_OFF);
+       ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets,
+                       ARRAY_SIZE(neo1973_wm8753_dapm_widgets));
+       if (ret)
+               return ret;
 
        /* add neo1973 specific controls */
-       err = snd_soc_add_controls(codec, wm8753_neo1973_controls,
-                               ARRAY_SIZE(8753_neo1973_controls));
-       if (err < 0)
-               return err;
+       ret = snd_soc_add_controls(codec, neo1973_wm8753_controls,
+                       ARRAY_SIZE(neo1973_wm8753_controls));
+       if (ret)
+               return ret;
 
        /* set up neo1973 specific audio routes */
-       err = snd_soc_dapm_add_routes(dapm, dapm_routes,
-                                     ARRAY_SIZE(dapm_routes));
+       ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes,
+                       ARRAY_SIZE(neo1973_wm8753_routes));
+       if (ret)
+               return ret;
+
+       /* set endpoints to default off mode */
+       snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
+       snd_soc_dapm_disable_pin(dapm, "GSM Line In");
+       snd_soc_dapm_disable_pin(dapm, "Headset Mic");
+       snd_soc_dapm_disable_pin(dapm, "Handset Mic");
+
+       /* allow audio paths from the GSM modem to run during suspend */
+       snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out");
+       snd_soc_dapm_ignore_suspend(dapm, "GSM Line In");
+       snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+       snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+
+       if (machine_is_neo1973_gta02()) {
+               ret = neo1973_gta02_wm8753_init(codec);
+               if (ret)
+                       return ret;
+       }
 
        snd_soc_dapm_sync(dapm);
+
        return 0;
 }
 
-/*
- * BT Codec DAI
- */
-static struct snd_soc_dai bt_dai = {
-       .name = "bluetooth-dai",
-       .playback = {
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .capture = {
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+/* GTA01 specific controlls */
+
+#ifdef CONFIG_MACH_NEO1973_GTA01
+
+static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = {
+       {"Amp IN", NULL, "ROUT1"},
+       {"Amp IN", NULL, "LOUT1"},
+
+       {"Handset Spk", NULL, "Amp EP"},
+       {"Stereo Out", NULL, "Amp LS"},
+       {"Headphone", NULL, "Amp HP"},
+};
+
+static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("Handset Spk", NULL),
+       SND_SOC_DAPM_SPK("Stereo Out", NULL),
+       SND_SOC_DAPM_HP("Headphone", NULL),
 };
 
+static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm)
+{
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets,
+                       ARRAY_SIZE(neo1973_lm4857_dapm_widgets));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes,
+                       ARRAY_SIZE(neo1973_lm4857_routes));
+       if (ret)
+               return ret;
+
+       snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
+       snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
+       snd_soc_dapm_ignore_suspend(dapm, "Headphone");
+
+       snd_soc_dapm_sync(dapm);
+
+       return 0;
+}
+
+#else
+static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; };
+#endif
+
 static struct snd_soc_dai_link neo1973_dai[] = {
 { /* Hifi Playback - for similatious use with voice below */
        .name = "WM8753",
@@ -569,90 +433,49 @@ static struct snd_soc_dai_link neo1973_dai[] = {
        .name = "Bluetooth",
        .stream_name = "Voice",
        .platform_name = "samsung-audio",
-       .cpu_dai_name = "bluetooth-dai",
+       .cpu_dai_name = "dfbmcs320-pcm",
        .codec_dai_name = "wm8753-voice",
        .codec_name = "wm8753-codec.0-001a",
        .ops = &neo1973_voice_ops,
 },
 };
 
-static struct snd_soc_card neo1973 = {
-       .name = "neo1973",
-       .dai_link = neo1973_dai,
-       .num_links = ARRAY_SIZE(neo1973_dai),
+static struct snd_soc_aux_dev neo1973_aux_devs[] = {
+       {
+               .name = "dfbmcs320",
+               .codec_name = "dfbmcs320.0",
+       },
+       {
+               .name = "lm4857",
+               .codec_name = "lm4857.0-007c",
+               .init = neo1973_lm4857_init,
+       },
 };
 
-static int lm4857_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
-{
-       pr_debug("Entered %s\n", __func__);
-
-       i2c = client;
-
-       lm4857_write_regs();
-       return 0;
-}
-
-static int lm4857_i2c_remove(struct i2c_client *client)
-{
-       pr_debug("Entered %s\n", __func__);
-
-       i2c = NULL;
-
-       return 0;
-}
-
-static u8 lm4857_state;
-
-static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
-{
-       pr_debug("Entered %s\n", __func__);
-
-       dev_dbg(&dev->dev, "lm4857_suspend\n");
-       lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
-       if (lm4857_state) {
-               lm4857_regs[LM4857_CTRL] &= 0xf0;
-               lm4857_write_regs();
-       }
-       return 0;
-}
-
-static int lm4857_resume(struct i2c_client *dev)
-{
-       pr_debug("Entered %s\n", __func__);
-
-       if (lm4857_state) {
-               lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
-               lm4857_write_regs();
-       }
-       return 0;
-}
-
-static void lm4857_shutdown(struct i2c_client *dev)
-{
-       pr_debug("Entered %s\n", __func__);
-
-       dev_dbg(&dev->dev, "lm4857_shutdown\n");
-       lm4857_regs[LM4857_CTRL] &= 0xf0;
-       lm4857_write_regs();
-}
+static struct snd_soc_codec_conf neo1973_codec_conf[] = {
+       {
+               .dev_name = "lm4857.0-007c",
+               .name_prefix = "Amp",
+       },
+};
 
-static const struct i2c_device_id lm4857_i2c_id[] = {
-       { "neo1973_lm4857", 0 },
-       { }
+#ifdef CONFIG_MACH_NEO1973_GTA02
+static const struct gpio neo1973_gta02_gpios[] = {
+       { GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" },
+       { GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" },
 };
+#else
+static const struct gpio neo1973_gta02_gpios[] = {};
+#endif
 
-static struct i2c_driver lm4857_i2c_driver = {
-       .driver = {
-               .name = "LM4857 I2C Amp",
-               .owner = THIS_MODULE,
-       },
-       .suspend =        lm4857_suspend,
-       .resume =         lm4857_resume,
-       .shutdown =       lm4857_shutdown,
-       .probe =          lm4857_i2c_probe,
-       .remove =         lm4857_i2c_remove,
-       .id_table =       lm4857_i2c_id,
+static struct snd_soc_card neo1973 = {
+       .name = "neo1973",
+       .dai_link = neo1973_dai,
+       .num_links = ARRAY_SIZE(neo1973_dai),
+       .aux_dev = neo1973_aux_devs,
+       .num_aux_devs = ARRAY_SIZE(neo1973_aux_devs),
+       .codec_conf = neo1973_codec_conf,
+       .num_configs = ARRAY_SIZE(neo1973_codec_conf),
 };
 
 static struct platform_device *neo1973_snd_device;
@@ -661,46 +484,56 @@ static int __init neo1973_init(void)
 {
        int ret;
 
-       pr_debug("Entered %s\n", __func__);
-
-       if (!machine_is_neo1973_gta01()) {
-               printk(KERN_INFO
-                       "Only GTA01 hardware supported by ASoC driver\n");
+       if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02())
                return -ENODEV;
+
+       if (machine_is_neo1973_gta02()) {
+               neo1973.name = "neo1973gta02";
+               neo1973.num_aux_devs = 1;
+
+               ret = gpio_request_array(neo1973_gta02_gpios,
+                               ARRAY_SIZE(neo1973_gta02_gpios));
+               if (ret)
+                       return ret;
        }
 
        neo1973_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!neo1973_snd_device)
-               return -ENOMEM;
+       if (!neo1973_snd_device) {
+               ret = -ENOMEM;
+               goto err_gpio_free;
+       }
 
        platform_set_drvdata(neo1973_snd_device, &neo1973);
        ret = platform_device_add(neo1973_snd_device);
 
-       if (ret) {
-               platform_device_put(neo1973_snd_device);
-               return ret;
-       }
-
-       ret = i2c_add_driver(&lm4857_i2c_driver);
+       if (ret)
+               goto err_put_device;
 
-       if (ret != 0)
-               platform_device_unregister(neo1973_snd_device);
+       return 0;
 
+err_put_device:
+       platform_device_put(neo1973_snd_device);
+err_gpio_free:
+       if (machine_is_neo1973_gta02()) {
+               gpio_free_array(neo1973_gta02_gpios,
+                               ARRAY_SIZE(neo1973_gta02_gpios));
+       }
        return ret;
 }
+module_init(neo1973_init);
 
 static void __exit neo1973_exit(void)
 {
-       pr_debug("Entered %s\n", __func__);
-
-       i2c_del_driver(&lm4857_i2c_driver);
        platform_device_unregister(neo1973_snd_device);
-}
 
-module_init(neo1973_init);
+       if (machine_is_neo1973_gta02()) {
+               gpio_free_array(neo1973_gta02_gpios,
+                               ARRAY_SIZE(neo1973_gta02_gpios));
+       }
+}
 module_exit(neo1973_exit);
 
 /* Module information */
 MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
-MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
+MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner");
 MODULE_LICENSE("GPL");
index 48d0b750406b5bad6ac600c9b97a216594291e65..38aac7d57a59213a2663cb8bbfab8cacd630fe30 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
-#include <linux/gpio.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 #include <plat/dma.h>
 #include "dma.h"
 #include "pcm.h"
 
+/*Register Offsets */
+#define S3C_PCM_CTL            0x00
+#define S3C_PCM_CLKCTL         0x04
+#define S3C_PCM_TXFIFO         0x08
+#define S3C_PCM_RXFIFO         0x0C
+#define S3C_PCM_IRQCTL         0x10
+#define S3C_PCM_IRQSTAT                0x14
+#define S3C_PCM_FIFOSTAT       0x18
+#define S3C_PCM_CLRINT         0x20
+
+/* PCM_CTL Bit-Fields */
+#define S3C_PCM_CTL_TXDIPSTICK_MASK    0x3f
+#define S3C_PCM_CTL_TXDIPSTICK_SHIFT   13
+#define S3C_PCM_CTL_RXDIPSTICK_MASK    0x3f
+#define S3C_PCM_CTL_RXDIPSTICK_SHIFT   7
+#define S3C_PCM_CTL_TXDMA_EN           (0x1 << 6)
+#define S3C_PCM_CTL_RXDMA_EN           (0x1 << 5)
+#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC  (0x1 << 4)
+#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC  (0x1 << 3)
+#define S3C_PCM_CTL_TXFIFO_EN          (0x1 << 2)
+#define S3C_PCM_CTL_RXFIFO_EN          (0x1 << 1)
+#define S3C_PCM_CTL_ENABLE             (0x1 << 0)
+
+/* PCM_CLKCTL Bit-Fields */
+#define S3C_PCM_CLKCTL_SERCLK_EN       (0x1 << 19)
+#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK  (0x1 << 18)
+#define S3C_PCM_CLKCTL_SCLKDIV_MASK    0x1ff
+#define S3C_PCM_CLKCTL_SYNCDIV_MASK    0x1ff
+#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT   9
+#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT   0
+
+/* PCM_TXFIFO Bit-Fields */
+#define S3C_PCM_TXFIFO_DVALID  (0x1 << 16)
+#define S3C_PCM_TXFIFO_DATA_MSK        (0xffff << 0)
+
+/* PCM_RXFIFO Bit-Fields */
+#define S3C_PCM_RXFIFO_DVALID  (0x1 << 16)
+#define S3C_PCM_RXFIFO_DATA_MSK        (0xffff << 0)
+
+/* PCM_IRQCTL Bit-Fields */
+#define S3C_PCM_IRQCTL_IRQEN           (0x1 << 14)
+#define S3C_PCM_IRQCTL_WRDEN           (0x1 << 12)
+#define S3C_PCM_IRQCTL_TXEMPTYEN       (0x1 << 11)
+#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN  (0x1 << 10)
+#define S3C_PCM_IRQCTL_TXFULLEN                (0x1 << 9)
+#define S3C_PCM_IRQCTL_TXALMSTFULLEN   (0x1 << 8)
+#define S3C_PCM_IRQCTL_TXSTARVEN       (0x1 << 7)
+#define S3C_PCM_IRQCTL_TXERROVRFLEN    (0x1 << 6)
+#define S3C_PCM_IRQCTL_RXEMPTEN                (0x1 << 5)
+#define S3C_PCM_IRQCTL_RXALMSTEMPTEN   (0x1 << 4)
+#define S3C_PCM_IRQCTL_RXFULLEN                (0x1 << 3)
+#define S3C_PCM_IRQCTL_RXALMSTFULLEN   (0x1 << 2)
+#define S3C_PCM_IRQCTL_RXSTARVEN       (0x1 << 1)
+#define S3C_PCM_IRQCTL_RXERROVRFLEN    (0x1 << 0)
+
+/* PCM_IRQSTAT Bit-Fields */
+#define S3C_PCM_IRQSTAT_IRQPND         (0x1 << 13)
+#define S3C_PCM_IRQSTAT_WRD_XFER       (0x1 << 12)
+#define S3C_PCM_IRQSTAT_TXEMPTY                (0x1 << 11)
+#define S3C_PCM_IRQSTAT_TXALMSTEMPTY   (0x1 << 10)
+#define S3C_PCM_IRQSTAT_TXFULL         (0x1 << 9)
+#define S3C_PCM_IRQSTAT_TXALMSTFULL    (0x1 << 8)
+#define S3C_PCM_IRQSTAT_TXSTARV                (0x1 << 7)
+#define S3C_PCM_IRQSTAT_TXERROVRFL     (0x1 << 6)
+#define S3C_PCM_IRQSTAT_RXEMPT         (0x1 << 5)
+#define S3C_PCM_IRQSTAT_RXALMSTEMPT    (0x1 << 4)
+#define S3C_PCM_IRQSTAT_RXFULL         (0x1 << 3)
+#define S3C_PCM_IRQSTAT_RXALMSTFULL    (0x1 << 2)
+#define S3C_PCM_IRQSTAT_RXSTARV                (0x1 << 1)
+#define S3C_PCM_IRQSTAT_RXERROVRFL     (0x1 << 0)
+
+/* PCM_FIFOSTAT Bit-Fields */
+#define S3C_PCM_FIFOSTAT_TXCNT_MSK             (0x3f << 14)
+#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY           (0x1 << 13)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY      (0x1 << 12)
+#define S3C_PCM_FIFOSTAT_TXFIFOFULL            (0x1 << 11)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL       (0x1 << 10)
+#define S3C_PCM_FIFOSTAT_RXCNT_MSK             (0x3f << 4)
+#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY           (0x1 << 3)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY      (0x1 << 2)
+#define S3C_PCM_FIFOSTAT_RXFIFOFULL            (0x1 << 1)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL       (0x1 << 0)
+
+/**
+ * struct s3c_pcm_info - S3C PCM Controller information
+ * @dev: The parent device passed to use from the probe.
+ * @regs: The pointer to the device register block.
+ * @dma_playback: DMA information for playback channel.
+ * @dma_capture: DMA information for capture channel.
+ */
+struct s3c_pcm_info {
+       spinlock_t lock;
+       struct device   *dev;
+       void __iomem    *regs;
+
+       unsigned int sclk_per_fs;
+
+       /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
+       unsigned int idleclk;
+
+       struct clk      *pclk;
+       struct clk      *cclk;
+
+       struct s3c_dma_params   *dma_playback;
+       struct s3c_dma_params   *dma_capture;
+};
+
 static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
        .name           = "PCM Stereo out"
 };
index 03393dcf852d737a1935488f40ea79b0babf6489..726baf814613a72696fb7f34775d7a5ece904a06 100644 (file)
@@ -9,116 +9,9 @@
 #ifndef __S3C_PCM_H
 #define __S3C_PCM_H __FILE__
 
-/*Register Offsets */
-#define S3C_PCM_CTL    (0x00)
-#define S3C_PCM_CLKCTL (0x04)
-#define S3C_PCM_TXFIFO (0x08)
-#define S3C_PCM_RXFIFO (0x0C)
-#define S3C_PCM_IRQCTL (0x10)
-#define S3C_PCM_IRQSTAT        (0x14)
-#define S3C_PCM_FIFOSTAT       (0x18)
-#define S3C_PCM_CLRINT (0x20)
-
-/* PCM_CTL Bit-Fields */
-#define S3C_PCM_CTL_TXDIPSTICK_MASK            (0x3f)
-#define S3C_PCM_CTL_TXDIPSTICK_SHIFT   (13)
-#define S3C_PCM_CTL_RXDIPSTICK_MASK            (0x3f)
-#define S3C_PCM_CTL_RXDIPSTICK_SHIFT   (7)
-#define S3C_PCM_CTL_TXDMA_EN           (0x1<<6)
-#define S3C_PCM_CTL_RXDMA_EN           (0x1<<5)
-#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC  (0x1<<4)
-#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC  (0x1<<3)
-#define S3C_PCM_CTL_TXFIFO_EN          (0x1<<2)
-#define S3C_PCM_CTL_RXFIFO_EN          (0x1<<1)
-#define S3C_PCM_CTL_ENABLE                     (0x1<<0)
-
-/* PCM_CLKCTL Bit-Fields */
-#define S3C_PCM_CLKCTL_SERCLK_EN               (0x1<<19)
-#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK  (0x1<<18)
-#define S3C_PCM_CLKCTL_SCLKDIV_MASK            (0x1ff)
-#define S3C_PCM_CLKCTL_SYNCDIV_MASK            (0x1ff)
-#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT   (9)
-#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT   (0)
-
-/* PCM_TXFIFO Bit-Fields */
-#define S3C_PCM_TXFIFO_DVALID  (0x1<<16)
-#define S3C_PCM_TXFIFO_DATA_MSK        (0xffff<<0)
-
-/* PCM_RXFIFO Bit-Fields */
-#define S3C_PCM_RXFIFO_DVALID  (0x1<<16)
-#define S3C_PCM_RXFIFO_DATA_MSK        (0xffff<<0)
-
-/* PCM_IRQCTL Bit-Fields */
-#define S3C_PCM_IRQCTL_IRQEN           (0x1<<14)
-#define S3C_PCM_IRQCTL_WRDEN           (0x1<<12)
-#define S3C_PCM_IRQCTL_TXEMPTYEN               (0x1<<11)
-#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN  (0x1<<10)
-#define S3C_PCM_IRQCTL_TXFULLEN                (0x1<<9)
-#define S3C_PCM_IRQCTL_TXALMSTFULLEN   (0x1<<8)
-#define S3C_PCM_IRQCTL_TXSTARVEN               (0x1<<7)
-#define S3C_PCM_IRQCTL_TXERROVRFLEN            (0x1<<6)
-#define S3C_PCM_IRQCTL_RXEMPTEN                (0x1<<5)
-#define S3C_PCM_IRQCTL_RXALMSTEMPTEN   (0x1<<4)
-#define S3C_PCM_IRQCTL_RXFULLEN                (0x1<<3)
-#define S3C_PCM_IRQCTL_RXALMSTFULLEN   (0x1<<2)
-#define S3C_PCM_IRQCTL_RXSTARVEN               (0x1<<1)
-#define S3C_PCM_IRQCTL_RXERROVRFLEN            (0x1<<0)
-
-/* PCM_IRQSTAT Bit-Fields */
-#define S3C_PCM_IRQSTAT_IRQPND         (0x1<<13)
-#define S3C_PCM_IRQSTAT_WRD_XFER               (0x1<<12)
-#define S3C_PCM_IRQSTAT_TXEMPTY                (0x1<<11)
-#define S3C_PCM_IRQSTAT_TXALMSTEMPTY   (0x1<<10)
-#define S3C_PCM_IRQSTAT_TXFULL         (0x1<<9)
-#define S3C_PCM_IRQSTAT_TXALMSTFULL            (0x1<<8)
-#define S3C_PCM_IRQSTAT_TXSTARV                (0x1<<7)
-#define S3C_PCM_IRQSTAT_TXERROVRFL             (0x1<<6)
-#define S3C_PCM_IRQSTAT_RXEMPT         (0x1<<5)
-#define S3C_PCM_IRQSTAT_RXALMSTEMPT            (0x1<<4)
-#define S3C_PCM_IRQSTAT_RXFULL         (0x1<<3)
-#define S3C_PCM_IRQSTAT_RXALMSTFULL            (0x1<<2)
-#define S3C_PCM_IRQSTAT_RXSTARV                (0x1<<1)
-#define S3C_PCM_IRQSTAT_RXERROVRFL             (0x1<<0)
-
-/* PCM_FIFOSTAT Bit-Fields */
-#define S3C_PCM_FIFOSTAT_TXCNT_MSK             (0x3f<<14)
-#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY   (0x1<<13)
-#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY      (0x1<<12)
-#define S3C_PCM_FIFOSTAT_TXFIFOFULL            (0x1<<11)
-#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL       (0x1<<10)
-#define S3C_PCM_FIFOSTAT_RXCNT_MSK             (0x3f<<4)
-#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY   (0x1<<3)
-#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY      (0x1<<2)
-#define S3C_PCM_FIFOSTAT_RXFIFOFULL            (0x1<<1)
-#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL       (0x1<<0)
-
 #define S3C_PCM_CLKSRC_PCLK    0
 #define S3C_PCM_CLKSRC_MUX     1
 
 #define S3C_PCM_SCLK_PER_FS    0
 
-/**
- * struct s3c_pcm_info - S3C PCM Controller information
- * @dev: The parent device passed to use from the probe.
- * @regs: The pointer to the device register block.
- * @dma_playback: DMA information for playback channel.
- * @dma_capture: DMA information for capture channel.
- */
-struct s3c_pcm_info {
-       spinlock_t lock;
-       struct device   *dev;
-       void __iomem    *regs;
-
-       unsigned int sclk_per_fs;
-
-       /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
-       unsigned int idleclk;
-
-       struct clk      *pclk;
-       struct clk      *cclk;
-
-       struct s3c_dma_params   *dma_playback;
-       struct s3c_dma_params   *dma_capture;
-};
-
 #endif /* __S3C_PCM_H */
index f40027445dda255edffd3637a44602f002683de2..1e574a5d440d0610e77d68c1d9582a2765719d01 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
 #include <linux/gpio.h>
-#include <linux/clk.h>
 
 #include <sound/soc.h>
-#include <sound/uda1380.h>
 #include <sound/jack.h>
 
 #include <plat/regs-iis.h>
-
-#include <mach/regs-clock.h>
-
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
 
 static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
 static int rx1950_startup(struct snd_pcm_substream *substream);
index 094f36e41e8383da4258bb1906870d403a6be19c..52074a2b069600875fc3f2756dc2a25e01fa9163 100644 (file)
@@ -20,9 +20,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <mach/dma.h>
 
index 7ea8378671242ffa32bc8f029d206add2d963662..841ab14c1100a9ca759fede04dae139402325d62 100644 (file)
  * option) any later version.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
-#include <mach/hardware.h>
+#include <sound/pcm_params.h>
 
 #include <mach/regs-gpio.h>
 #include <mach/dma.h>
@@ -39,8 +31,6 @@
 #include "regs-i2s-v2.h"
 #include "s3c2412-i2s.h"
 
-#define S3C2412_I2S_DEBUG 0
-
 static struct s3c2410_dma_client s3c2412_dma_client_out = {
        .name           = "I2S PCM Stereo out"
 };
index 13e41ed8e22bf699cf015be77ff0ce02400f4826..63d8849d80bdc738e667201af435f2ed838a9a9e 100644 (file)
  *  option) any later version.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/jiffies.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
-#include <mach/hardware.h>
 #include <mach/regs-gpio.h>
-#include <mach/regs-clock.h>
-
-#include <asm/dma.h>
 #include <mach/dma.h>
-
 #include <plat/regs-iis.h>
 
 #include "dma.h"
index a434032d18328fa6c51c677f9b90d4db7ca6db0e..349566f0686b7a234aa2db7ef767a712dbc7d603 100644 (file)
@@ -7,20 +7,13 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
-#include <linux/i2c.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <plat/audio-simtec.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
index 08fcaaa66907b3c04cfde7a43327a37c5fca139e..ce6aef6041793b40ac1f8b494feea76ae3f14b90 100644 (file)
@@ -7,18 +7,8 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include <plat/audio-simtec.h>
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
 static const struct snd_soc_dapm_widget dapm_widgets[] = {
index 116e3e6701679778f1761b9474aa8d58dde30a0e..a7ef7db5468734047bf32cf1b7bb1e982b1233ce 100644 (file)
@@ -7,22 +7,10 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include <plat/audio-simtec.h>
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
-#include "../codecs/tlv320aic23.h"
-
 /* supported machines:
  *
  * Machine     Connections             AMP
index 2c09e93dd566f2a66b46dc5886df5119a017bec1..3cb70075107811e76b65a23828e1c140b2c6a485 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
 #include <linux/clk.h>
-#include <linux/mutex.h>
 #include <linux/gpio.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
+
 #include <sound/soc.h>
 #include <sound/s3c24xx_uda134x.h>
-#include <sound/uda134x.h>
 
 #include <plat/regs-iis.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda134x.h"
-
 
 /* #define ENFORCE_RATES 1 */
 /*
index 61e2b5219d421093e7cb771c824f0ec584243ae3..0a2c4f22303811b55921452f783ae485fefb3bdd 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/gpio.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
 
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "i2s.h"
-
 #include "../codecs/wm8750.h"
 
 /*
index 3be7e7e92d6e0ed6be3e25c9a6eefbf333c818dc..3a0dbfc793f0fc5d6c36b9bc924ec11ff07970ff 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card smdk2443;
 
 static struct snd_soc_dai_link smdk2443_dai[] = {
index b5c3fad01bb8cd0a8e1b3efeddf1bf50a1e636db..e8ac961c6ba10140fa62768d8f6c3c29c0c15128 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/clk.h>
 
-#include <plat/devs.h>
-
 #include <sound/soc.h>
 
-#include "dma.h"
 #include "spdif.h"
 
 /* Audio clock settings are belonged to board specific part. Every
index b2cff1a44aed6a0ff592553e44a4354f7e6f8263..8aacf23d6f3a19c1c67e49723da113a9fbb2a283 100644 (file)
  *  option) any later version.
  */
 
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <asm/mach-types.h>
 
 #include "../codecs/wm8580.h"
-#include "dma.h"
 #include "i2s.h"
 
 /*
index ae5fed6f772f6b26af2cc90677df58d41caa9c57..fffe3c1dd1bdbfe399bddee2f12550401279a545 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card smdk;
 
 /*
index f0816404ea3e8a7d64c609ce087fdc6dfa2479d0..28c491dacf7a9aca23463e15bf72c64abf0b0b2c 100644 (file)
@@ -13,9 +13,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 #include <mach/dma.h>
index a14820ac96650294dc0c2e05ee5427e08f54293c..d6f4703b3c0796fd5869f656904d232818e86298 100644 (file)
@@ -18,18 +18,26 @@ struct fsi_ak4642_data {
        const char *cpu_dai;
        const char *codec;
        const char *platform;
+       int id;
 };
 
 static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_dai *dai = rtd->codec_dai;
+       struct snd_soc_dai *codec = rtd->codec_dai;
+       struct snd_soc_dai *cpu = rtd->cpu_dai;
        int ret;
 
-       ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_CBM_CFM);
+       ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J |
+                                        SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
                return ret;
 
-       ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0);
+       ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J |
+                                      SND_SOC_DAIFMT_CBS_CFS);
 
        return ret;
 }
@@ -60,7 +68,7 @@ static int fsi_ak4642_probe(struct platform_device *pdev)
 
        pdata = (struct fsi_ak4642_data *)id_entry->driver_data;
 
-       fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A);
+       fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
        if (!fsi_snd_device)
                goto out;
 
@@ -93,6 +101,7 @@ static struct fsi_ak4642_data fsi_a_ak4642 = {
        .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi.0",
+       .id             = FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi_b_ak4642 = {
@@ -101,6 +110,7 @@ static struct fsi_ak4642_data fsi_b_ak4642 = {
        .cpu_dai        = "fsib-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi.0",
+       .id             = FSI_PORT_B,
 };
 
 static struct fsi_ak4642_data fsi_a_ak4643 = {
@@ -109,6 +119,7 @@ static struct fsi_ak4642_data fsi_a_ak4643 = {
        .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi.0",
+       .id             = FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi_b_ak4643 = {
@@ -117,6 +128,7 @@ static struct fsi_ak4642_data fsi_b_ak4643 = {
        .cpu_dai        = "fsib-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi.0",
+       .id             = FSI_PORT_B,
 };
 
 static struct fsi_ak4642_data fsi2_a_ak4642 = {
@@ -125,6 +137,7 @@ static struct fsi_ak4642_data fsi2_a_ak4642 = {
        .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi2",
+       .id             = FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi2_b_ak4642 = {
@@ -133,6 +146,7 @@ static struct fsi_ak4642_data fsi2_b_ak4642 = {
        .cpu_dai        = "fsib-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi2",
+       .id             = FSI_PORT_B,
 };
 
 static struct fsi_ak4642_data fsi2_a_ak4643 = {
@@ -141,6 +155,7 @@ static struct fsi_ak4642_data fsi2_a_ak4643 = {
        .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi2",
+       .id             = FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi2_b_ak4643 = {
@@ -149,6 +164,7 @@ static struct fsi_ak4642_data fsi2_b_ak4643 = {
        .cpu_dai        = "fsib-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi2",
+       .id             = FSI_PORT_B,
 };
 
 static struct platform_device_id fsi_id_table[] = {
index e8df9da92f713c9c772a9c35e4f4c6b5b033e06e..dbafd7ac559066c6b8db9f82fe55ea23f002f3b4 100644 (file)
 
 static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_dai *dai = rtd->codec_dai;
+       struct snd_soc_dai *codec = rtd->codec_dai;
+       struct snd_soc_dai *cpu = rtd->cpu_dai;
+       int ret;
 
-       return snd_soc_dai_set_fmt(dai,
+       ret = snd_soc_dai_set_fmt(codec,
                                   SND_SOC_DAIFMT_I2S |
                                   SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S |
+                                      SND_SOC_DAIFMT_CBS_CFS);
+
+       return ret;
 }
 
 static struct snd_soc_dai_link fsi_da7210_dai = {
index a52dd8ec71d38d1b7b0c0b48d0707c34e9e28669..9719985eb82d2b4c3169be670cd7138abf41047c 100644 (file)
 #include <linux/platform_device.h>
 #include <sound/sh_fsi.h>
 
+struct fsi_hdmi_data {
+       const char *cpu_dai;
+       const char *card;
+       int id;
+};
+
+static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *cpu = rtd->cpu_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM);
+
+       return ret;
+}
+
 static struct snd_soc_dai_link fsi_dai_link = {
        .name           = "HDMI",
        .stream_name    = "HDMI",
-       .cpu_dai_name   = "fsib-dai", /* fsi B */
        .codec_dai_name = "sh_mobile_hdmi-hifi",
        .platform_name  = "sh_fsi2",
        .codec_name     = "sh-mobile-hdmi",
+       .init           = fsi_hdmi_dai_init,
 };
 
 static struct snd_soc_card fsi_soc_card  = {
-       .name           = "FSI (SH MOBILE HDMI)",
        .dai_link       = &fsi_dai_link,
        .num_links      = 1,
 };
 
 static struct platform_device *fsi_snd_device;
 
-static int __init fsi_hdmi_init(void)
+static int fsi_hdmi_probe(struct platform_device *pdev)
 {
        int ret = -ENOMEM;
+       const struct platform_device_id *id_entry;
+       struct fsi_hdmi_data *pdata;
+
+       id_entry = pdev->id_entry;
+       if (!id_entry) {
+               dev_err(&pdev->dev, "unknown fsi hdmi\n");
+               return -ENODEV;
+       }
 
-       fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B);
+       pdata = (struct fsi_hdmi_data *)id_entry->driver_data;
+
+       fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
        if (!fsi_snd_device)
                goto out;
 
+       fsi_dai_link.cpu_dai_name       = pdata->cpu_dai;
+       fsi_soc_card.name               = pdata->card;
+
        platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
        ret = platform_device_add(fsi_snd_device);
 
@@ -47,9 +75,48 @@ out:
        return ret;
 }
 
-static void __exit fsi_hdmi_exit(void)
+static int fsi_hdmi_remove(struct platform_device *pdev)
 {
        platform_device_unregister(fsi_snd_device);
+       return 0;
+}
+
+static struct fsi_hdmi_data fsi2_a_hdmi = {
+       .cpu_dai        = "fsia-dai",
+       .card           = "FSI2A (SH MOBILE HDMI)",
+       .id             = FSI_PORT_A,
+};
+
+static struct fsi_hdmi_data fsi2_b_hdmi = {
+       .cpu_dai        = "fsib-dai",
+       .card           = "FSI2B (SH MOBILE HDMI)",
+       .id             = FSI_PORT_B,
+};
+
+static struct platform_device_id fsi_id_table[] = {
+       /* FSI 2 */
+       { "sh_fsi2_a_hdmi",     (kernel_ulong_t)&fsi2_a_hdmi },
+       { "sh_fsi2_b_hdmi",     (kernel_ulong_t)&fsi2_b_hdmi },
+       {},
+};
+
+static struct platform_driver fsi_hdmi = {
+       .driver = {
+               .name   = "fsi-hdmi-audio",
+       },
+       .probe          = fsi_hdmi_probe,
+       .remove         = fsi_hdmi_remove,
+       .id_table       = fsi_id_table,
+};
+
+static int __init fsi_hdmi_init(void)
+{
+       return platform_driver_register(&fsi_hdmi);
+}
+
+static void __exit fsi_hdmi_exit(void)
+{
+       platform_driver_unregister(&fsi_hdmi);
 }
 
 module_init(fsi_hdmi_init);
index 2b06402801efd5fe1e49fa7575916804828042c5..0c9997e2d8c05d7407b63cff18b3786ad28b28fa 100644 (file)
@@ -78,6 +78,8 @@
 /* CKG1 */
 #define ACKMD_MASK     0x00007000
 #define BPFMD_MASK     0x00000700
+#define DIMD           (1 << 4)
+#define DOMD           (1 << 0)
 
 /* A/B MST_CTLR */
 #define BP     (1 << 4)        /* Fix the signal of Biphase output */
 
 #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
+typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int enable);
+
 /*
  * FSI driver use below type name for variable
  *
@@ -128,7 +132,6 @@ struct fsi_stream {
        struct snd_pcm_substream *substream;
 
        int fifo_max_num;
-       int chan_num;
 
        int buff_offset;
        int buff_len;
@@ -143,6 +146,7 @@ struct fsi_priv {
        void __iomem *base;
        struct fsi_master *master;
 
+       int chan_num;
        struct fsi_stream playback;
        struct fsi_stream capture;
 
@@ -252,9 +256,8 @@ static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
        return  rtd->cpu_dai;
 }
 
-static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
+static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai)
 {
-       struct snd_soc_dai *dai = fsi_get_dai(substream);
        struct fsi_master *master = snd_soc_dai_get_drvdata(dai);
 
        if (dai->id == 0)
@@ -263,11 +266,27 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
                return &master->fsib;
 }
 
+static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
+{
+       return fsi_get_priv_frm_dai(fsi_get_dai(substream));
+}
+
+static set_rate_func fsi_get_info_set_rate(struct fsi_master *master)
+{
+       if (!master->info)
+               return NULL;
+
+       return master->info->set_rate;
+}
+
 static u32 fsi_get_info_flags(struct fsi_priv *fsi)
 {
        int is_porta = fsi_is_port_a(fsi);
        struct fsi_master *master = fsi_get_master(fsi);
 
+       if (!master->info)
+               return 0;
+
        return is_porta ? master->info->porta_flags :
                master->info->portb_flags;
 }
@@ -288,21 +307,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi,
        return is_play ? &fsi->playback : &fsi->capture;
 }
 
-static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play)
-{
-       u32 mode;
-       u32 flags = fsi_get_info_flags(fsi);
-
-       mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE;
-
-       /* return
-        * 1 : master mode
-        * 0 : slave mode
-        */
-
-       return (mode & flags) != mode;
-}
-
 static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play)
 {
        int is_porta = fsi_is_port_a(fsi);
@@ -357,7 +361,6 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
 static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play)
 {
        u32 status;
-       struct fsi_stream *io = fsi_get_stream(fsi, is_play);
        int data_num;
 
        status = is_play ?
@@ -365,7 +368,7 @@ static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play)
                fsi_reg_read(fsi, DIFF_ST);
 
        data_num = 0x1ff & (status >> 8);
-       data_num *= io->chan_num;
+       data_num *= fsi->chan_num;
 
        return data_num;
 }
@@ -387,7 +390,7 @@ static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play)
        struct snd_pcm_substream *substream = io->substream;
        struct snd_pcm_runtime *runtime = substream->runtime;
 
-       return frames_to_bytes(runtime, 1) / io->chan_num;
+       return frames_to_bytes(runtime, 1) / fsi->chan_num;
 }
 
 static void fsi_count_fifo_err(struct fsi_priv *fsi)
@@ -580,10 +583,10 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
         * 7 channels:  32 ( 32 x 7 = 224)
         * 8 channels:  32 ( 32 x 8 = 256)
         */
-       for (i = 1; i < io->chan_num; i <<= 1)
+       for (i = 1; i < fsi->chan_num; i <<= 1)
                io->fifo_max_num >>= 1;
        dev_dbg(dai->dev, "%d channel %d store\n",
-               io->chan_num, io->fifo_max_num);
+               fsi->chan_num, io->fifo_max_num);
 
        /*
         * set interrupt generation factor
@@ -659,7 +662,7 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
                 * data_num_max : number of FSI fifo free space
                 * data_num     : number of ALSA residue data
                 */
-               data_num_max  = io->fifo_max_num * io->chan_num;
+               data_num_max  = io->fifo_max_num * fsi->chan_num;
                data_num_max -= fsi_get_fifo_data_num(fsi, is_play);
 
                data_num = data_residue_num;
@@ -754,25 +757,12 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
-       struct fsi_master *master = fsi_get_master(fsi);
-       struct fsi_stream *io;
        u32 flags = fsi_get_info_flags(fsi);
-       u32 fmt;
        u32 data;
        int is_play = fsi_is_play(substream);
-       int is_master;
-
-       io = fsi_get_stream(fsi, is_play);
 
        pm_runtime_get_sync(dai->dev);
 
-       /* CKG1 */
-       data = is_play ? (1 << 0) : (1 << 4);
-       is_master = fsi_is_master_mode(fsi, is_play);
-       if (is_master)
-               fsi_reg_mask_set(fsi, CKG1, data, data);
-       else
-               fsi_reg_mask_set(fsi, CKG1, data, 0);
 
        /* clock inversion (CKG2) */
        data = 0;
@@ -787,54 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
 
        fsi_reg_write(fsi, CKG2, data);
 
-       /* do fmt, di fmt */
-       data = 0;
-       fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags);
-       switch (fmt) {
-       case SH_FSI_FMT_MONO:
-               data = CR_MONO;
-               io->chan_num = 1;
-               break;
-       case SH_FSI_FMT_MONO_DELAY:
-               data = CR_MONO_D;
-               io->chan_num = 1;
-               break;
-       case SH_FSI_FMT_PCM:
-               data = CR_PCM;
-               io->chan_num = 2;
-               break;
-       case SH_FSI_FMT_I2S:
-               data = CR_I2S;
-               io->chan_num = 2;
-               break;
-       case SH_FSI_FMT_TDM:
-               io->chan_num = is_play ?
-                       SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
-               data = CR_TDM | (io->chan_num - 1);
-               break;
-       case SH_FSI_FMT_TDM_DELAY:
-               io->chan_num = is_play ?
-                       SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
-               data = CR_TDM_D | (io->chan_num - 1);
-               break;
-       case SH_FSI_FMT_SPDIF:
-               if (master->core->ver < 2) {
-                       dev_err(dai->dev, "This FSI can not use SPDIF\n");
-                       return -EINVAL;
-               }
-               data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
-               io->chan_num = 2;
-               fsi_spdif_clk_ctrl(fsi, 1);
-               fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
-               break;
-       default:
-               dev_err(dai->dev, "unknown format.\n");
-               return -EINVAL;
-       }
-       is_play ?
-               fsi_reg_write(fsi, DO_FMT, data) :
-               fsi_reg_write(fsi, DI_FMT, data);
-
        /* irq clear */
        fsi_irq_disable(fsi, is_play);
        fsi_irq_clear_status(fsi);
@@ -851,12 +793,12 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
        struct fsi_priv *fsi = fsi_get_priv(substream);
        int is_play = fsi_is_play(substream);
        struct fsi_master *master = fsi_get_master(fsi);
-       int (*set_rate)(struct device *dev, int is_porta, int rate, int enable);
+       set_rate_func set_rate;
 
        fsi_irq_disable(fsi, is_play);
        fsi_clk_ctrl(fsi, 0);
 
-       set_rate = master->info->set_rate;
+       set_rate = fsi_get_info_set_rate(master);
        if (set_rate && fsi->rate)
                set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
        fsi->rate = 0;
@@ -889,18 +831,100 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
        return ret;
 }
 
+static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
+{
+       u32 data = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               data = CR_I2S;
+               fsi->chan_num = 2;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               data = CR_PCM;
+               fsi->chan_num = 2;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       fsi_reg_write(fsi, DO_FMT, data);
+       fsi_reg_write(fsi, DI_FMT, data);
+
+       return 0;
+}
+
+static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
+{
+       struct fsi_master *master = fsi_get_master(fsi);
+       u32 data = 0;
+
+       if (master->core->ver < 2)
+               return -EINVAL;
+
+       data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
+       fsi->chan_num = 2;
+       fsi_spdif_clk_ctrl(fsi, 1);
+       fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+
+       fsi_reg_write(fsi, DO_FMT, data);
+       fsi_reg_write(fsi, DI_FMT, data);
+
+       return 0;
+}
+
+static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
+       u32 flags = fsi_get_info_flags(fsi);
+       u32 data = 0;
+       int ret;
+
+       pm_runtime_get_sync(dai->dev);
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               data = DIMD | DOMD;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               ret = -EINVAL;
+               goto set_fmt_exit;
+       }
+       fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
+
+       /* set format */
+       switch (flags & SH_FSI_FMT_MASK) {
+       case SH_FSI_FMT_DAI:
+               ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+               break;
+       case SH_FSI_FMT_SPDIF:
+               ret = fsi_set_fmt_spdif(fsi);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+set_fmt_exit:
+       pm_runtime_put_sync(dai->dev);
+
+       return ret;
+}
+
 static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params,
                             struct snd_soc_dai *dai)
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
        struct fsi_master *master = fsi_get_master(fsi);
-       int (*set_rate)(struct device *dev, int is_porta, int rate, int enable);
+       set_rate_func set_rate;
        int fsi_ver = master->core->ver;
        long rate = params_rate(params);
        int ret;
 
-       set_rate = master->info->set_rate;
+       set_rate = fsi_get_info_set_rate(master);
        if (!set_rate)
                return 0;
 
@@ -975,6 +999,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = {
        .startup        = fsi_dai_startup,
        .shutdown       = fsi_dai_shutdown,
        .trigger        = fsi_dai_trigger,
+       .set_fmt        = fsi_dai_set_fmt,
        .hw_params      = fsi_dai_hw_params,
 };
 
index 8c2a21a978ac3551b2113b0640178f75ff8612ac..5d76da43b14c1ddfa0e53c0c60a358263bb7d488 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/bitmap.h>
 #include <linux/rbtree.h>
 
+#include <trace/events/asoc.h>
+
 static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
                                     unsigned int reg)
 {
@@ -25,7 +27,8 @@ static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
        unsigned int val;
 
        if (reg >= codec->driver->reg_cache_size ||
-               snd_soc_codec_volatile_register(codec, reg)) {
+               snd_soc_codec_volatile_register(codec, reg) ||
+               codec->cache_bypass) {
                        if (codec->cache_only)
                                return -1;
 
@@ -49,7 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
        data[1] = value & 0x00ff;
 
        if (!snd_soc_codec_volatile_register(codec, reg) &&
-               reg < codec->driver->reg_cache_size) {
+               reg < codec->driver->reg_cache_size &&
+               !codec->cache_bypass) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret < 0)
                        return -1;
@@ -106,7 +110,8 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
        unsigned int val;
 
        if (reg >= codec->driver->reg_cache_size ||
-               snd_soc_codec_volatile_register(codec, reg)) {
+               snd_soc_codec_volatile_register(codec, reg) ||
+               codec->cache_bypass) {
                        if (codec->cache_only)
                                return -1;
 
@@ -130,7 +135,8 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
        data[1] = value & 0x00ff;
 
        if (!snd_soc_codec_volatile_register(codec, reg) &&
-               reg < codec->driver->reg_cache_size) {
+               reg < codec->driver->reg_cache_size &&
+               !codec->cache_bypass) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret < 0)
                        return -1;
@@ -191,7 +197,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
        data[1] = value & 0xff;
 
        if (!snd_soc_codec_volatile_register(codec, reg) &&
-               reg < codec->driver->reg_cache_size) {
+               reg < codec->driver->reg_cache_size &&
+               !codec->cache_bypass) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret < 0)
                        return -1;
@@ -216,7 +223,8 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
 
        reg &= 0xff;
        if (reg >= codec->driver->reg_cache_size ||
-               snd_soc_codec_volatile_register(codec, reg)) {
+               snd_soc_codec_volatile_register(codec, reg) ||
+               codec->cache_bypass) {
                        if (codec->cache_only)
                                return -1;
 
@@ -271,7 +279,8 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
        data[2] = value & 0xff;
 
        if (!snd_soc_codec_volatile_register(codec, reg) &&
-               reg < codec->driver->reg_cache_size) {
+               reg < codec->driver->reg_cache_size &&
+               !codec->cache_bypass) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret < 0)
                        return -1;
@@ -295,7 +304,8 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
        unsigned int val;
 
        if (reg >= codec->driver->reg_cache_size ||
-           snd_soc_codec_volatile_register(codec, reg)) {
+           snd_soc_codec_volatile_register(codec, reg) ||
+           codec->cache_bypass) {
                if (codec->cache_only)
                        return -1;
 
@@ -450,7 +460,8 @@ static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
 
        reg &= 0xff;
        if (reg >= codec->driver->reg_cache_size ||
-               snd_soc_codec_volatile_register(codec, reg)) {
+               snd_soc_codec_volatile_register(codec, reg) ||
+               codec->cache_bypass) {
                        if (codec->cache_only)
                                return -1;
 
@@ -476,7 +487,8 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
 
        reg &= 0xff;
        if (!snd_soc_codec_volatile_register(codec, reg) &&
-               reg < codec->driver->reg_cache_size) {
+               reg < codec->driver->reg_cache_size &&
+               !codec->cache_bypass) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret < 0)
                        return -1;
@@ -568,7 +580,8 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
        unsigned int val;
 
        if (reg >= codec->driver->reg_cache_size ||
-           snd_soc_codec_volatile_register(codec, reg)) {
+           snd_soc_codec_volatile_register(codec, reg) ||
+           codec->cache_bypass) {
                if (codec->cache_only)
                        return -1;
 
@@ -595,7 +608,8 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
        data[3] = value & 0xff;
 
        if (!snd_soc_codec_volatile_register(codec, reg) &&
-               reg < codec->driver->reg_cache_size) {
+               reg < codec->driver->reg_cache_size &&
+               !codec->cache_bypass) {
                ret = snd_soc_cache_write(codec, reg, value);
                if (ret < 0)
                        return -1;
@@ -761,6 +775,49 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
 
+static bool snd_soc_set_cache_val(void *base, unsigned int idx,
+                                 unsigned int val, unsigned int word_size)
+{
+       switch (word_size) {
+       case 1: {
+               u8 *cache = base;
+               if (cache[idx] == val)
+                       return true;
+               cache[idx] = val;
+               break;
+       }
+       case 2: {
+               u16 *cache = base;
+               if (cache[idx] == val)
+                       return true;
+               cache[idx] = val;
+               break;
+       }
+       default:
+               BUG();
+       }
+       return false;
+}
+
+static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
+               unsigned int word_size)
+{
+       switch (word_size) {
+       case 1: {
+               const u8 *cache = base;
+               return cache[idx];
+       }
+       case 2: {
+               const u16 *cache = base;
+               return cache[idx];
+       }
+       default:
+               BUG();
+       }
+       /* unreachable */
+       return -1;
+}
+
 struct snd_soc_rbtree_node {
        struct rb_node node;
        unsigned int reg;
@@ -835,7 +892,9 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
                ret = snd_soc_cache_read(codec, rbnode->reg, &val);
                if (ret)
                        return ret;
+               codec->cache_bypass = 1;
                ret = snd_soc_write(codec, rbnode->reg, val);
+               codec->cache_bypass = 0;
                if (ret)
                        return ret;
                dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
@@ -924,7 +983,12 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
 
 static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
 {
+       struct snd_soc_rbtree_node *rbtree_node;
        struct snd_soc_rbtree_ctx *rbtree_ctx;
+       unsigned int val;
+       unsigned int word_size;
+       int i;
+       int ret;
 
        codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
        if (!codec->reg_cache)
@@ -936,53 +1000,25 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
        if (!codec->reg_def_copy)
                return 0;
 
-/*
- * populate the rbtree with the initialized registers.  All other
- * registers will be inserted into the tree when they are first written.
- *
- * The reasoning behind this, is that we need to step through and
- * dereference the cache in u8/u16 increments without sacrificing
- * portability.  This could also be done using memcpy() but that would
- * be slightly more cryptic.
- */
-#define snd_soc_rbtree_populate(cache)                                 \
-({                                                                     \
-       int ret, i;                                                     \
-       struct snd_soc_rbtree_node *rbtree_node;                        \
-                                                                       \
-       ret = 0;                                                        \
-       cache = codec->reg_def_copy;                                    \
-       for (i = 0; i < codec->driver->reg_cache_size; ++i) {           \
-               if (!cache[i])                                          \
-                       continue;                                       \
-               rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); \
-               if (!rbtree_node) {                                     \
-                       ret = -ENOMEM;                                  \
-                       snd_soc_cache_exit(codec);                      \
-                       break;                                          \
-               }                                                       \
-               rbtree_node->reg = i;                                   \
-               rbtree_node->value = cache[i];                          \
-               rbtree_node->defval = cache[i];                         \
-               snd_soc_rbtree_insert(&rbtree_ctx->root,                \
-                                     rbtree_node);                     \
-       }                                                               \
-       ret;                                                            \
-})
-
-       switch (codec->driver->reg_word_size) {
-       case 1: {
-               const u8 *cache;
-
-               return snd_soc_rbtree_populate(cache);
-       }
-       case 2: {
-               const u16 *cache;
-
-               return snd_soc_rbtree_populate(cache);
-       }
-       default:
-               BUG();
+       /*
+        * populate the rbtree with the initialized registers.  All other
+        * registers will be inserted when they are first modified.
+        */
+       word_size = codec->driver->reg_word_size;
+       for (i = 0; i < codec->driver->reg_cache_size; ++i) {
+               val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
+               if (!val)
+                       continue;
+               rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
+               if (!rbtree_node) {
+                       ret = -ENOMEM;
+                       snd_soc_cache_exit(codec);
+                       break;
+               }
+               rbtree_node->reg = i;
+               rbtree_node->value = val;
+               rbtree_node->defval = val;
+               snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
        }
 
        return 0;
@@ -1080,34 +1116,28 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec,
                unsigned int reg)
 {
        const struct snd_soc_codec_driver *codec_drv;
-       size_t reg_size;
 
        codec_drv = codec->driver;
-       reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
        return (reg * codec_drv->reg_word_size) /
-              DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+              DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
 }
 
 static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec,
                unsigned int reg)
 {
        const struct snd_soc_codec_driver *codec_drv;
-       size_t reg_size;
 
        codec_drv = codec->driver;
-       reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
-       return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) /
+       return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) /
                      codec_drv->reg_word_size);
 }
 
 static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec)
 {
        const struct snd_soc_codec_driver *codec_drv;
-       size_t reg_size;
 
        codec_drv = codec->driver;
-       reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
-       return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+       return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
 }
 
 static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
@@ -1122,7 +1152,9 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
                ret = snd_soc_cache_read(codec, i, &val);
                if (ret)
                        return ret;
+               codec->cache_bypass = 1;
                ret = snd_soc_write(codec, i, val);
+               codec->cache_bypass = 0;
                if (ret)
                        return ret;
                dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
@@ -1165,29 +1197,10 @@ static int snd_soc_lzo_cache_write(struct snd_soc_codec *codec,
        }
 
        /* write the new value to the cache */
-       switch (codec->driver->reg_word_size) {
-       case 1: {
-               u8 *cache;
-               cache = lzo_block->dst;
-               if (cache[blkpos] == value) {
-                       kfree(lzo_block->dst);
-                       goto out;
-               }
-               cache[blkpos] = value;
-       }
-       break;
-       case 2: {
-               u16 *cache;
-               cache = lzo_block->dst;
-               if (cache[blkpos] == value) {
-                       kfree(lzo_block->dst);
-                       goto out;
-               }
-               cache[blkpos] = value;
-       }
-       break;
-       default:
-               BUG();
+       if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value,
+                                 codec->driver->reg_word_size)) {
+               kfree(lzo_block->dst);
+               goto out;
        }
 
        /* prepare the source to be the decompressed block */
@@ -1241,25 +1254,10 @@ static int snd_soc_lzo_cache_read(struct snd_soc_codec *codec,
 
        /* decompress the block */
        ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block);
-       if (ret >= 0) {
+       if (ret >= 0)
                /* fetch the value from the cache */
-               switch (codec->driver->reg_word_size) {
-               case 1: {
-                       u8 *cache;
-                       cache = lzo_block->dst;
-                       *value = cache[blkpos];
-               }
-               break;
-               case 2: {
-                       u16 *cache;
-                       cache = lzo_block->dst;
-                       *value = cache[blkpos];
-               }
-               break;
-               default:
-                       BUG();
-               }
-       }
+               *value = snd_soc_get_cache_val(lzo_block->dst, blkpos,
+                                              codec->driver->reg_word_size);
 
        kfree(lzo_block->dst);
        /* restore the pointer and length of the compressed block */
@@ -1301,7 +1299,7 @@ static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec)
 static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
 {
        struct snd_soc_lzo_ctx **lzo_blocks;
-       size_t reg_size, bmp_size;
+       size_t bmp_size;
        const struct snd_soc_codec_driver *codec_drv;
        int ret, tofree, i, blksize, blkcount;
        const char *p, *end;
@@ -1309,7 +1307,6 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
 
        ret = 0;
        codec_drv = codec->driver;
-       reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
 
        /*
         * If we have not been given a default register cache
@@ -1321,8 +1318,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
                tofree = 1;
 
        if (!codec->reg_def_copy) {
-               codec->reg_def_copy = kzalloc(reg_size,
-                                                      GFP_KERNEL);
+               codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL);
                if (!codec->reg_def_copy)
                        return -ENOMEM;
        }
@@ -1370,7 +1366,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
 
        blksize = snd_soc_lzo_get_blksize(codec);
        p = codec->reg_def_copy;
-       end = codec->reg_def_copy + reg_size;
+       end = codec->reg_def_copy + codec->reg_size;
        /* compress the register map and fill the lzo blocks */
        for (i = 0; i < blkcount; ++i, p += blksize) {
                lzo_blocks[i]->src = p;
@@ -1414,28 +1410,10 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
                ret = snd_soc_cache_read(codec, i, &val);
                if (ret)
                        return ret;
-               if (codec_drv->reg_cache_default) {
-                       switch (codec_drv->reg_word_size) {
-                       case 1: {
-                               const u8 *cache;
-
-                               cache = codec_drv->reg_cache_default;
-                               if (cache[i] == val)
-                                       continue;
-                       }
-                       break;
-                       case 2: {
-                               const u16 *cache;
-
-                               cache = codec_drv->reg_cache_default;
-                               if (cache[i] == val)
-                                       continue;
-                       }
-                       break;
-                       default:
-                               BUG();
-                       }
-               }
+               if (codec->reg_def_copy)
+                       if (snd_soc_get_cache_val(codec->reg_def_copy,
+                                                 i, codec_drv->reg_word_size) == val)
+                               continue;
                ret = snd_soc_write(codec, i, val);
                if (ret)
                        return ret;
@@ -1448,50 +1426,16 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
 static int snd_soc_flat_cache_write(struct snd_soc_codec *codec,
                                    unsigned int reg, unsigned int value)
 {
-       switch (codec->driver->reg_word_size) {
-       case 1: {
-               u8 *cache;
-
-               cache = codec->reg_cache;
-               cache[reg] = value;
-       }
-       break;
-       case 2: {
-               u16 *cache;
-
-               cache = codec->reg_cache;
-               cache[reg] = value;
-       }
-       break;
-       default:
-               BUG();
-       }
-
+       snd_soc_set_cache_val(codec->reg_cache, reg, value,
+                             codec->driver->reg_word_size);
        return 0;
 }
 
 static int snd_soc_flat_cache_read(struct snd_soc_codec *codec,
                                   unsigned int reg, unsigned int *value)
 {
-       switch (codec->driver->reg_word_size) {
-       case 1: {
-               u8 *cache;
-
-               cache = codec->reg_cache;
-               *value = cache[reg];
-       }
-       break;
-       case 2: {
-               u16 *cache;
-
-               cache = codec->reg_cache;
-               *value = cache[reg];
-       }
-       break;
-       default:
-               BUG();
-       }
-
+       *value = snd_soc_get_cache_val(codec->reg_cache, reg,
+                                      codec->driver->reg_word_size);
        return 0;
 }
 
@@ -1507,24 +1451,14 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec)
 static int snd_soc_flat_cache_init(struct snd_soc_codec *codec)
 {
        const struct snd_soc_codec_driver *codec_drv;
-       size_t reg_size;
 
        codec_drv = codec->driver;
-       reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
 
-       /*
-        * for flat compression, we don't need to keep a copy of the
-        * original defaults register cache as it will definitely not
-        * be marked as __devinitconst
-        */
-       kfree(codec->reg_def_copy);
-       codec->reg_def_copy = NULL;
-
-       if (codec_drv->reg_cache_default)
-               codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
-                                          reg_size, GFP_KERNEL);
+       if (codec->reg_def_copy)
+               codec->reg_cache = kmemdup(codec->reg_def_copy,
+                                          codec->reg_size, GFP_KERNEL);
        else
-               codec->reg_cache = kzalloc(reg_size, GFP_KERNEL);
+               codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL);
        if (!codec->reg_cache)
                return -ENOMEM;
 
@@ -1669,21 +1603,77 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write);
 int snd_soc_cache_sync(struct snd_soc_codec *codec)
 {
        int ret;
+       const char *name;
 
        if (!codec->cache_sync) {
                return 0;
        }
 
-       if (codec->cache_ops && codec->cache_ops->sync) {
-               if (codec->cache_ops->name)
-                       dev_dbg(codec->dev, "Syncing %s cache for %s codec\n",
-                               codec->cache_ops->name, codec->name);
-               ret = codec->cache_ops->sync(codec);
-               if (!ret)
-                       codec->cache_sync = 0;
-               return ret;
-       }
+       if (!codec->cache_ops || !codec->cache_ops->sync)
+               return -EINVAL;
 
-       return -EINVAL;
+       if (codec->cache_ops->name)
+               name = codec->cache_ops->name;
+       else
+               name = "unknown";
+
+       if (codec->cache_ops->name)
+               dev_dbg(codec->dev, "Syncing %s cache for %s codec\n",
+                       codec->cache_ops->name, codec->name);
+       trace_snd_soc_cache_sync(codec, name, "start");
+       ret = codec->cache_ops->sync(codec);
+       if (!ret)
+               codec->cache_sync = 0;
+       trace_snd_soc_cache_sync(codec, name, "end");
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
+
+static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec,
+                                       unsigned int reg)
+{
+       const struct snd_soc_codec_driver *codec_drv;
+       unsigned int min, max, index;
+
+       codec_drv = codec->driver;
+       min = 0;
+       max = codec_drv->reg_access_size - 1;
+       do {
+               index = (min + max) / 2;
+               if (codec_drv->reg_access_default[index].reg == reg)
+                       return index;
+               if (codec_drv->reg_access_default[index].reg < reg)
+                       min = index + 1;
+               else
+                       max = index;
+       } while (min <= max);
+       return -1;
+}
+
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+                                     unsigned int reg)
+{
+       int index;
+
+       if (reg >= codec->driver->reg_cache_size)
+               return 1;
+       index = snd_soc_get_reg_access_index(codec, reg);
+       if (index < 0)
+               return 0;
+       return codec->driver->reg_access_default[index].vol;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register);
+
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+                                     unsigned int reg)
+{
+       int index;
+
+       if (reg >= codec->driver->reg_cache_size)
+               return 1;
+       index = snd_soc_get_reg_access_index(codec, reg);
+       if (index < 0)
+               return 0;
+       return codec->driver->reg_access_default[index].read;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
index c3f6f1e72790ccc730c60ec9d984e79402cab365..17efacdb248a9d15e3af2e8f46bb1c8489effe61 100644 (file)
@@ -48,7 +48,8 @@ static DEFINE_MUTEX(pcm_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
 #ifdef CONFIG_DEBUG_FS
-static struct dentry *debugfs_root;
+struct dentry *snd_soc_debugfs_root;
+EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
 #endif
 
 static DEFINE_MUTEX(client_mutex);
@@ -57,8 +58,6 @@ static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
 
-static int snd_soc_register_card(struct snd_soc_card *card);
-static int snd_soc_unregister_card(struct snd_soc_card *card);
 static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
 
 /*
@@ -70,10 +69,73 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
+/* returns the minimum number of bytes needed to represent
+ * a particular given value */
+static int min_bytes_needed(unsigned long val)
+{
+       int c = 0;
+       int i;
+
+       for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c)
+               if (val & (1UL << i))
+                       break;
+       c = (sizeof val * 8) - c;
+       if (!c || (c % 8))
+               c = (c + 8) / 8;
+       else
+               c /= 8;
+       return c;
+}
+
+/* fill buf which is 'len' bytes with a formatted
+ * string of the form 'reg: value\n' */
+static int format_register_str(struct snd_soc_codec *codec,
+                              unsigned int reg, char *buf, size_t len)
+{
+       int wordsize = codec->driver->reg_word_size * 2;
+       int regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
+       int ret;
+       char tmpbuf[len + 1];
+       char regbuf[regsize + 1];
+
+       /* since tmpbuf is allocated on the stack, warn the callers if they
+        * try to abuse this function */
+       WARN_ON(len > 63);
+
+       /* +2 for ': ' and + 1 for '\n' */
+       if (wordsize + regsize + 2 + 1 != len)
+               return -EINVAL;
+
+       ret = snd_soc_read(codec , reg);
+       if (ret < 0) {
+               memset(regbuf, 'X', regsize);
+               regbuf[regsize] = '\0';
+       } else {
+               snprintf(regbuf, regsize + 1, "%.*x", regsize, ret);
+       }
+
+       /* prepare the buffer */
+       snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf);
+       /* copy it back to the caller without the '\0' */
+       memcpy(buf, tmpbuf, len);
+
+       return 0;
+}
+
 /* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
+static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
+                                 size_t count, loff_t pos)
 {
-       int ret, i, step = 1, count = 0;
+       int i, step = 1;
+       int wordsize, regsize;
+       int len;
+       size_t total = 0;
+       loff_t p = 0;
+
+       wordsize = codec->driver->reg_word_size * 2;
+       regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
+
+       len = wordsize + regsize + 2 + 1;
 
        if (!codec->driver->reg_cache_size)
                return 0;
@@ -81,55 +143,37 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
        if (codec->driver->reg_cache_step)
                step = codec->driver->reg_cache_step;
 
-       count += sprintf(buf, "%s registers\n", codec->name);
        for (i = 0; i < codec->driver->reg_cache_size; i += step) {
-               if (codec->driver->readable_register && !codec->driver->readable_register(i))
+               if (codec->readable_register && !codec->readable_register(codec, i))
                        continue;
-
-               count += sprintf(buf + count, "%2x: ", i);
-               if (count >= PAGE_SIZE - 1)
-                       break;
-
                if (codec->driver->display_register) {
                        count += codec->driver->display_register(codec, buf + count,
                                                         PAGE_SIZE - count, i);
                } else {
-                       /* If the read fails it's almost certainly due to
-                        * the register being volatile and the device being
-                        * powered off.
-                        */
-                       ret = snd_soc_read(codec, i);
-                       if (ret >= 0)
-                               count += snprintf(buf + count,
-                                                 PAGE_SIZE - count,
-                                                 "%4x", ret);
-                       else
-                               count += snprintf(buf + count,
-                                                 PAGE_SIZE - count,
-                                                 "<no data: %d>", ret);
+                       /* only support larger than PAGE_SIZE bytes debugfs
+                        * entries for the default case */
+                       if (p >= pos) {
+                               if (total + len >= count - 1)
+                                       break;
+                               format_register_str(codec, i, buf + total, len);
+                               total += len;
+                       }
+                       p += len;
                }
-
-               if (count >= PAGE_SIZE - 1)
-                       break;
-
-               count += snprintf(buf + count, PAGE_SIZE - count, "\n");
-               if (count >= PAGE_SIZE - 1)
-                       break;
        }
 
-       /* Truncate count; min() would cause a warning */
-       if (count >= PAGE_SIZE)
-               count = PAGE_SIZE - 1;
+       total = min(total, count - 1);
 
-       return count;
+       return total;
 }
+
 static ssize_t codec_reg_show(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        struct snd_soc_pcm_runtime *rtd =
                        container_of(dev, struct snd_soc_pcm_runtime, dev);
 
-       return soc_codec_reg_show(rtd->codec, buf);
+       return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0);
 }
 
 static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
@@ -168,16 +212,28 @@ static int codec_reg_open_file(struct inode *inode, struct file *file)
 }
 
 static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
-                              size_t count, loff_t *ppos)
+                                  size_t count, loff_t *ppos)
 {
        ssize_t ret;
        struct snd_soc_codec *codec = file->private_data;
-       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       char *buf;
+
+       if (*ppos < 0 || !count)
+               return -EINVAL;
+
+       buf = kmalloc(count, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
-       ret = soc_codec_reg_show(codec, buf);
-       if (ret >= 0)
-               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+       ret = soc_codec_reg_show(codec, buf, count, *ppos);
+       if (ret >= 0) {
+               if (copy_to_user(user_buf, buf, ret)) {
+                       kfree(buf);
+                       return -EFAULT;
+               }
+               *ppos += ret;
+       }
+
        kfree(buf);
        return ret;
 }
@@ -209,6 +265,10 @@ static ssize_t codec_reg_write_file(struct file *file,
                start++;
        if (strict_strtoul(start, 16, &value))
                return -EINVAL;
+
+       /* Userspace has been fiddling around behind the kernel's back */
+       add_taint(TAINT_USER);
+
        snd_soc_write(codec, reg, value);
        return buf_size;
 }
@@ -232,6 +292,11 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
                return;
        }
 
+       debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root,
+                           &codec->cache_sync);
+       debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root,
+                           &codec->cache_only);
+
        codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
                                                 codec->debugfs_codec_root,
                                                 codec, &codec_reg_fops);
@@ -356,7 +421,7 @@ static const struct file_operations platform_list_fops = {
 static void soc_init_card_debugfs(struct snd_soc_card *card)
 {
        card->debugfs_card_root = debugfs_create_dir(card->name,
-                                                    debugfs_root);
+                                                    snd_soc_debugfs_root);
        if (!card->debugfs_card_root) {
                dev_warn(card->dev,
                         "ASoC: Failed to create codec debugfs directory\n");
@@ -435,20 +500,30 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret;
 
-       if (codec_dai->driver->symmetric_rates || cpu_dai->driver->symmetric_rates ||
-                       rtd->dai_link->symmetric_rates) {
-               dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n",
-                               rtd->rate);
+       if (!codec_dai->driver->symmetric_rates &&
+           !cpu_dai->driver->symmetric_rates &&
+           !rtd->dai_link->symmetric_rates)
+               return 0;
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
-                                                  SNDRV_PCM_HW_PARAM_RATE,
-                                                  rtd->rate,
-                                                  rtd->rate);
-               if (ret < 0) {
-                       dev_err(&rtd->dev,
-                               "Unable to apply rate symmetry constraint: %d\n", ret);
-                       return ret;
-               }
+       /* This can happen if multiple streams are starting simultaneously -
+        * the second can need to get its constraints before the first has
+        * picked a rate.  Complain and allow the application to carry on.
+        */
+       if (!rtd->rate) {
+               dev_warn(&rtd->dev,
+                        "Not enforcing symmetric_rates due to race\n");
+               return 0;
+       }
+
+       dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
+
+       ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                          SNDRV_PCM_HW_PARAM_RATE,
+                                          rtd->rate, rtd->rate);
+       if (ret < 0) {
+               dev_err(&rtd->dev,
+                       "Unable to apply rate symmetry constraint: %d\n", ret);
+               return ret;
        }
 
        return 0;
@@ -962,12 +1037,11 @@ static struct snd_pcm_ops soc_pcm_ops = {
        .pointer        = soc_pcm_pointer,
 };
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 /* powers down audio subsystem for suspend */
-static int soc_suspend(struct device *dev)
+int snd_soc_suspend(struct device *dev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = dev_get_drvdata(dev);
        struct snd_soc_codec *codec;
        int i;
 
@@ -1008,7 +1082,7 @@ static int soc_suspend(struct device *dev)
        }
 
        if (card->suspend_pre)
-               card->suspend_pre(pdev, PMSG_SUSPEND);
+               card->suspend_pre(card);
 
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
@@ -1075,10 +1149,11 @@ static int soc_suspend(struct device *dev)
        }
 
        if (card->suspend_post)
-               card->suspend_post(pdev, PMSG_SUSPEND);
+               card->suspend_post(card);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_suspend);
 
 /* deferred resume work, so resume can complete before we finished
  * setting our codec back up, which can be very slow on I2C
@@ -1087,7 +1162,6 @@ static void soc_resume_deferred(struct work_struct *work)
 {
        struct snd_soc_card *card =
                        container_of(work, struct snd_soc_card, deferred_resume_work);
-       struct platform_device *pdev = to_platform_device(card->dev);
        struct snd_soc_codec *codec;
        int i;
 
@@ -1101,7 +1175,7 @@ static void soc_resume_deferred(struct work_struct *work)
        snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2);
 
        if (card->resume_pre)
-               card->resume_pre(pdev);
+               card->resume_pre(card);
 
        /* resume AC97 DAIs */
        for (i = 0; i < card->num_rtd; i++) {
@@ -1176,7 +1250,7 @@ static void soc_resume_deferred(struct work_struct *work)
        }
 
        if (card->resume_post)
-               card->resume_post(pdev);
+               card->resume_post(card);
 
        dev_dbg(card->dev, "resume work completed\n");
 
@@ -1185,10 +1259,9 @@ static void soc_resume_deferred(struct work_struct *work)
 }
 
 /* powers up audio subsystem after a suspend */
-static int soc_resume(struct device *dev)
+int snd_soc_resume(struct device *dev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = dev_get_drvdata(dev);
        int i;
 
        /* AC97 devices might have other drivers hanging off them so
@@ -1210,9 +1283,10 @@ static int soc_resume(struct device *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_resume);
 #else
-#define soc_suspend    NULL
-#define soc_resume     NULL
+#define snd_soc_suspend NULL
+#define snd_soc_resume NULL
 #endif
 
 static struct snd_soc_dai_ops null_dai_ops = {
@@ -1400,31 +1474,44 @@ static int soc_probe_codec(struct snd_soc_card *card,
                           struct snd_soc_codec *codec)
 {
        int ret = 0;
+       const struct snd_soc_codec_driver *driver = codec->driver;
 
        codec->card = card;
        codec->dapm.card = card;
        soc_set_name_prefix(card, codec);
 
-       if (codec->driver->probe) {
-               ret = codec->driver->probe(codec);
+       if (!try_module_get(codec->dev->driver->owner))
+               return -ENODEV;
+
+       if (driver->probe) {
+               ret = driver->probe(codec);
                if (ret < 0) {
                        dev_err(codec->dev,
                                "asoc: failed to probe CODEC %s: %d\n",
                                codec->name, ret);
-                       return ret;
+                       goto err_probe;
                }
        }
 
+       if (driver->dapm_widgets)
+               snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
+                                         driver->num_dapm_widgets);
+       if (driver->dapm_routes)
+               snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
+                                       driver->num_dapm_routes);
+
        soc_init_codec_debugfs(codec);
 
        /* mark codec as probed and add to card codec list */
-       if (!try_module_get(codec->dev->driver->owner))
-               return -ENODEV;
-
        codec->probed = 1;
        list_add(&codec->card_list, &card->codec_dev_list);
        list_add(&codec->dapm.list, &card->dapm_list);
 
+       return 0;
+
+err_probe:
+       module_put(codec->dev->driver->owner);
+
        return ret;
 }
 
@@ -1468,7 +1555,6 @@ static int soc_post_component_init(struct snd_soc_card *card,
 
        /* Make sure all DAPM widgets are instantiated */
        snd_soc_dapm_new_widgets(&codec->dapm);
-       snd_soc_dapm_sync(&codec->dapm);
 
        /* register the rtd device */
        rtd->codec = codec;
@@ -1543,19 +1629,19 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
 
        /* probe the platform */
        if (!platform->probed) {
+               if (!try_module_get(platform->dev->driver->owner))
+                       return -ENODEV;
+
                if (platform->driver->probe) {
                        ret = platform->driver->probe(platform);
                        if (ret < 0) {
                                printk(KERN_ERR "asoc: failed to probe platform %s\n",
                                                platform->name);
+                               module_put(platform->dev->driver->owner);
                                return ret;
                        }
                }
                /* mark platform as probed and add to card platform list */
-
-               if (!try_module_get(platform->dev->driver->owner))
-                       return -ENODEV;
-
                platform->probed = 1;
                list_add(&platform->card_list, &card->platform_dev_list);
        }
@@ -1713,7 +1799,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec,
 
 static void snd_soc_instantiate_card(struct snd_soc_card *card)
 {
-       struct platform_device *pdev = to_platform_device(card->dev);
        struct snd_soc_codec *codec;
        struct snd_soc_codec_conf *codec_conf;
        enum snd_soc_compress_type compress_type;
@@ -1740,6 +1825,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        list_for_each_entry(codec, &codec_list, list) {
                if (codec->cache_init)
                        continue;
+               /* by default we don't override the compress_type */
+               compress_type = 0;
                /* check to see if we need to override the compress_type */
                for (i = 0; i < card->num_configs; ++i) {
                        codec_conf = &card->codec_conf[i];
@@ -1750,18 +1837,6 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                                        break;
                        }
                }
-               if (i == card->num_configs) {
-                       /* no need to override the compress_type so
-                        * go ahead and do the standard thing */
-                       ret = snd_soc_init_codec_cache(codec, 0);
-                       if (ret < 0) {
-                               mutex_unlock(&card->mutex);
-                               return;
-                       }
-                       continue;
-               }
-               /* override the compress_type with the one supplied in
-                * the machine driver */
                ret = snd_soc_init_codec_cache(codec, compress_type);
                if (ret < 0) {
                        mutex_unlock(&card->mutex);
@@ -1780,14 +1855,19 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        }
        card->snd_card->dev = card->dev;
 
-#ifdef CONFIG_PM
+       card->dapm.bias_level = SND_SOC_BIAS_OFF;
+       card->dapm.dev = card->dev;
+       card->dapm.card = card;
+       list_add(&card->dapm.list, &card->dapm_list);
+
+#ifdef CONFIG_PM_SLEEP
        /* deferred resume work */
        INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 #endif
 
        /* initialise the sound card only once */
        if (card->probe) {
-               ret = card->probe(pdev);
+               ret = card->probe(card);
                if (ret < 0)
                        goto card_probe_error;
        }
@@ -1810,11 +1890,37 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
+       if (card->dapm_widgets)
+               snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
+                                         card->num_dapm_widgets);
+       if (card->dapm_routes)
+               snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
+                                       card->num_dapm_routes);
+
+#ifdef CONFIG_DEBUG_FS
+       card->dapm.debugfs_dapm = debugfs_create_dir("dapm",
+                                                    card->debugfs_card_root);
+       if (!card->dapm.debugfs_dapm)
+               printk(KERN_WARNING
+                      "Failed to create card DAPM debugfs directory\n");
+
+       snd_soc_dapm_debugfs_init(&card->dapm);
+#endif
+
        snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
                 "%s",  card->name);
        snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
                 "%s", card->name);
 
+       if (card->late_probe) {
+               ret = card->late_probe(card);
+               if (ret < 0) {
+                       dev_err(card->dev, "%s late_probe() failed: %d\n",
+                               card->name, ret);
+                       goto probe_aux_dev_err;
+               }
+       }
+
        ret = snd_card_register(card->snd_card);
        if (ret < 0) {
                printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
@@ -1848,7 +1954,7 @@ probe_dai_err:
 
 card_probe_error:
        if (card->remove)
-               card->remove(pdev);
+               card->remove(card);
 
        snd_card_free(card->snd_card);
 
@@ -1872,16 +1978,15 @@ static int soc_probe(struct platform_device *pdev)
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        int ret = 0;
 
+       /*
+        * no card, so machine driver should be registering card
+        * we should not be here in that case so ret error
+        */
+       if (!card)
+               return -EINVAL;
+
        /* Bodge while we unpick instantiation */
        card->dev = &pdev->dev;
-       INIT_LIST_HEAD(&card->dai_dev_list);
-       INIT_LIST_HEAD(&card->codec_dev_list);
-       INIT_LIST_HEAD(&card->platform_dev_list);
-       INIT_LIST_HEAD(&card->widgets);
-       INIT_LIST_HEAD(&card->paths);
-       INIT_LIST_HEAD(&card->dapm_list);
-
-       soc_init_card_debugfs(card);
 
        ret = snd_soc_register_card(card);
        if (ret != 0) {
@@ -1892,45 +1997,48 @@ static int soc_probe(struct platform_device *pdev)
        return 0;
 }
 
-/* removes a socdev */
-static int soc_remove(struct platform_device *pdev)
+static int soc_cleanup_card_resources(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
        int i;
 
-               if (card->instantiated) {
+       /* make sure any delayed work runs */
+       for (i = 0; i < card->num_rtd; i++) {
+               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+               flush_delayed_work_sync(&rtd->delayed_work);
+       }
 
-               /* make sure any delayed work runs */
-               for (i = 0; i < card->num_rtd; i++) {
-                       struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
-                       flush_delayed_work_sync(&rtd->delayed_work);
-               }
+       /* remove auxiliary devices */
+       for (i = 0; i < card->num_aux_devs; i++)
+               soc_remove_aux_dev(card, i);
 
-               /* remove auxiliary devices */
-               for (i = 0; i < card->num_aux_devs; i++)
-                       soc_remove_aux_dev(card, i);
+       /* remove and free each DAI */
+       for (i = 0; i < card->num_rtd; i++)
+               soc_remove_dai_link(card, i);
 
-               /* remove and free each DAI */
-               for (i = 0; i < card->num_rtd; i++)
-                       soc_remove_dai_link(card, i);
+       soc_cleanup_card_debugfs(card);
 
-               soc_cleanup_card_debugfs(card);
+       /* remove the card */
+       if (card->remove)
+               card->remove(card);
+
+       kfree(card->rtd);
+       snd_card_free(card->snd_card);
+       return 0;
 
-               /* remove the card */
-               if (card->remove)
-                       card->remove(pdev);
+}
+
+/* removes a socdev */
+static int soc_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-               kfree(card->rtd);
-               snd_card_free(card->snd_card);
-       }
        snd_soc_unregister_card(card);
        return 0;
 }
 
-static int soc_poweroff(struct device *dev)
+int snd_soc_poweroff(struct device *dev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = dev_get_drvdata(dev);
        int i;
 
        if (!card->instantiated)
@@ -1947,11 +2055,12 @@ static int soc_poweroff(struct device *dev)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_poweroff);
 
-static const struct dev_pm_ops soc_pm_ops = {
-       .suspend = soc_suspend,
-       .resume = soc_resume,
-       .poweroff = soc_poweroff,
+const struct dev_pm_ops snd_soc_pm_ops = {
+       .suspend = snd_soc_suspend,
+       .resume = snd_soc_resume,
+       .poweroff = snd_soc_poweroff,
 };
 
 /* ASoC platform driver */
@@ -1959,7 +2068,7 @@ static struct platform_driver soc_driver = {
        .driver         = {
                .name           = "soc-audio",
                .owner          = THIS_MODULE,
-               .pm             = &soc_pm_ops,
+               .pm             = &snd_soc_pm_ops,
        },
        .probe          = soc_probe,
        .remove         = soc_remove,
@@ -2029,10 +2138,11 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
  *
  * Boolean function indiciating if a CODEC register is volatile.
  */
-int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
+                                   unsigned int reg)
 {
-       if (codec->driver->volatile_register)
-               return codec->driver->volatile_register(reg);
+       if (codec->volatile_register)
+               return codec->volatile_register(codec, reg);
        else
                return 0;
 }
@@ -2129,19 +2239,27 @@ EXPORT_SYMBOL_GPL(snd_soc_write);
  *
  * Writes new register value.
  *
- * Returns 1 for change else 0.
+ * Returns 1 for change, 0 for no change, or negative error code.
  */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
                                unsigned int mask, unsigned int value)
 {
        int change;
        unsigned int old, new;
+       int ret;
 
-       old = snd_soc_read(codec, reg);
+       ret = snd_soc_read(codec, reg);
+       if (ret < 0)
+               return ret;
+
+       old = ret;
        new = (old & ~mask) | value;
        change = old != new;
-       if (change)
-               snd_soc_write(codec, reg, new);
+       if (change) {
+               ret = snd_soc_write(codec, reg, new);
+               if (ret < 0)
+                       return ret;
+       }
 
        return change;
 }
@@ -2226,22 +2344,45 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
  * @_template: control template
  * @data: control private data
  * @long_name: control long name
+ * @prefix: control name prefix
  *
  * Create a new mixer control from a template control.
  *
  * Returns 0 for success, else error.
  */
 struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
-       void *data, char *long_name)
+                                 void *data, char *long_name,
+                                 const char *prefix)
 {
        struct snd_kcontrol_new template;
+       struct snd_kcontrol *kcontrol;
+       char *name = NULL;
+       int name_len;
 
        memcpy(&template, _template, sizeof(template));
-       if (long_name)
-               template.name = long_name;
        template.index = 0;
 
-       return snd_ctl_new1(&template, data);
+       if (!long_name)
+               long_name = template.name;
+
+       if (prefix) {
+               name_len = strlen(long_name) + strlen(prefix) + 2;
+               name = kmalloc(name_len, GFP_ATOMIC);
+               if (!name)
+                       return NULL;
+
+               snprintf(name, name_len, "%s %s", prefix, long_name);
+
+               template.name = name;
+       } else {
+               template.name = long_name;
+       }
+
+       kcontrol = snd_ctl_new1(&template, data);
+
+       kfree(name);
+
+       return kcontrol;
 }
 EXPORT_SYMBOL_GPL(snd_soc_cnew);
 
@@ -2260,22 +2401,16 @@ int snd_soc_add_controls(struct snd_soc_codec *codec,
        const struct snd_kcontrol_new *controls, int num_controls)
 {
        struct snd_card *card = codec->card->snd_card;
-       char prefixed_name[44], *name;
        int err, i;
 
        for (i = 0; i < num_controls; i++) {
                const struct snd_kcontrol_new *control = &controls[i];
-               if (codec->name_prefix) {
-                       snprintf(prefixed_name, sizeof(prefixed_name), "%s %s",
-                                codec->name_prefix, control->name);
-                       name = prefixed_name;
-               } else {
-                       name = control->name;
-               }
-               err = snd_ctl_add(card, snd_soc_cnew(control, codec, name));
+               err = snd_ctl_add(card, snd_soc_cnew(control, codec,
+                                                    control->name,
+                                                    codec->name_prefix));
                if (err < 0) {
                        dev_err(codec->dev, "%s: Failed to add %s: %d\n",
-                               codec->name, name, err);
+                               codec->name, control->name, err);
                        return err;
                }
        }
@@ -2956,11 +3091,33 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 {
        if (dai->driver && dai->driver->ops->set_sysclk)
                return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
+       else if (dai->codec && dai->codec->driver->set_sysclk)
+               return dai->codec->driver->set_sysclk(dai->codec, clk_id,
+                                                     freq, dir);
        else
                return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 
+/**
+ * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
+ * @codec: CODEC
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
+ *
+ * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
+ */
+int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+       unsigned int freq, int dir)
+{
+       if (codec->driver->set_sysclk)
+               return codec->driver->set_sysclk(codec, clk_id, freq, dir);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
+
 /**
  * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
  * @dai: DAI
@@ -2997,11 +3154,35 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
        if (dai->driver && dai->driver->ops->set_pll)
                return dai->driver->ops->set_pll(dai, pll_id, source,
                                         freq_in, freq_out);
+       else if (dai->codec && dai->codec->driver->set_pll)
+               return dai->codec->driver->set_pll(dai->codec, pll_id, source,
+                                                  freq_in, freq_out);
        else
                return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
 
+/*
+ * snd_soc_codec_set_pll - configure codec PLL.
+ * @codec: CODEC
+ * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
+ *
+ * Configures and enables PLL to generate output clock based on input clock.
+ */
+int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+                         unsigned int freq_in, unsigned int freq_out)
+{
+       if (codec->driver->set_pll)
+               return codec->driver->set_pll(codec, pll_id, source,
+                                             freq_in, freq_out);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll);
+
 /**
  * snd_soc_dai_set_fmt - configure DAI hardware audio format.
  * @dai: DAI
@@ -3101,17 +3282,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
  *
  * @card: Card to register
  *
- * Note that currently this is an internal only function: it will be
- * exposed to machine drivers after further backporting of ASoC v2
- * registration APIs.
  */
-static int snd_soc_register_card(struct snd_soc_card *card)
+int snd_soc_register_card(struct snd_soc_card *card)
 {
        int i;
 
        if (!card->name || !card->dev)
                return -EINVAL;
 
+       snd_soc_initialize_card_lists(card);
+
+       soc_init_card_debugfs(card);
+
        card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
                            (card->num_links + card->num_aux_devs),
                            GFP_KERNEL);
@@ -3135,18 +3317,18 @@ static int snd_soc_register_card(struct snd_soc_card *card)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_register_card);
 
 /**
  * snd_soc_unregister_card - Unregister a card with the ASoC core
  *
  * @card: Card to unregister
  *
- * Note that currently this is an internal only function: it will be
- * exposed to machine drivers after further backporting of ASoC v2
- * registration APIs.
  */
-static int snd_soc_unregister_card(struct snd_soc_card *card)
+int snd_soc_unregister_card(struct snd_soc_card *card)
 {
+       if (card->instantiated)
+               soc_cleanup_card_resources(card);
        mutex_lock(&client_mutex);
        list_del(&card->list);
        mutex_unlock(&client_mutex);
@@ -3154,6 +3336,7 @@ static int snd_soc_unregister_card(struct snd_soc_card *card)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_unregister_card);
 
 /*
  * Simplify DAI link configuration by removing ".-1" from device names
@@ -3483,9 +3666,12 @@ int snd_soc_register_codec(struct device *dev,
 
        codec->write = codec_drv->write;
        codec->read = codec_drv->read;
+       codec->volatile_register = codec_drv->volatile_register;
+       codec->readable_register = codec_drv->readable_register;
        codec->dapm.bias_level = SND_SOC_BIAS_OFF;
        codec->dapm.dev = dev;
        codec->dapm.codec = codec;
+       codec->dapm.seq_notifier = codec_drv->seq_notifier;
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->num_dai = num_dai;
@@ -3494,20 +3680,30 @@ int snd_soc_register_codec(struct device *dev,
        /* allocate CODEC register cache */
        if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
                reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
+               codec->reg_size = reg_size;
                /* it is necessary to make a copy of the default register cache
                 * because in the case of using a compression type that requires
                 * the default register cache to be marked as __devinitconst the
                 * kernel might have freed the array by the time we initialize
                 * the cache.
                 */
-               codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
-                                             reg_size, GFP_KERNEL);
-               if (!codec->reg_def_copy) {
-                       ret = -ENOMEM;
-                       goto fail;
+               if (codec_drv->reg_cache_default) {
+                       codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
+                                                     reg_size, GFP_KERNEL);
+                       if (!codec->reg_def_copy) {
+                               ret = -ENOMEM;
+                               goto fail;
+                       }
                }
        }
 
+       if (codec_drv->reg_access_size && codec_drv->reg_access_default) {
+               if (!codec->volatile_register)
+                       codec->volatile_register = snd_soc_default_volatile_register;
+               if (!codec->readable_register)
+                       codec->readable_register = snd_soc_default_readable_register;
+       }
+
        for (i = 0; i < num_dai; i++) {
                fixup_codec_formats(&dai_drv[i].playback);
                fixup_codec_formats(&dai_drv[i].capture);
@@ -3574,22 +3770,22 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
-       debugfs_root = debugfs_create_dir("asoc", NULL);
-       if (IS_ERR(debugfs_root) || !debugfs_root) {
+       snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
+       if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
                printk(KERN_WARNING
                       "ASoC: Failed to create debugfs directory\n");
-               debugfs_root = NULL;
+               snd_soc_debugfs_root = NULL;
        }
 
-       if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL,
+       if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
                                 &codec_list_fops))
                pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
 
-       if (!debugfs_create_file("dais", 0444, debugfs_root, NULL,
+       if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
                                 &dai_list_fops))
                pr_warn("ASoC: Failed to create DAI list debugfs file\n");
 
-       if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL,
+       if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
                                 &platform_list_fops))
                pr_warn("ASoC: Failed to create platform list debugfs file\n");
 #endif
@@ -3601,7 +3797,7 @@ module_init(snd_soc_init);
 static void __exit snd_soc_exit(void)
 {
 #ifdef CONFIG_DEBUG_FS
-       debugfs_remove_recursive(debugfs_root);
+       debugfs_remove_recursive(snd_soc_debugfs_root);
 #endif
        platform_driver_unregister(&soc_driver);
 }
index 1790f83ee6651b8051ff4950d2bf7540b4ea8011..81c4052c127ccc150a63ca50780ddcc9357f25f7 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
+#include <linux/async.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/bitops.h>
@@ -125,17 +126,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
 
 /**
  * snd_soc_dapm_set_bias_level - set the bias level for the system
- * @card: audio device
+ * @dapm: DAPM context
  * @level: level to configure
  *
  * Configure the bias (power) levels for the SoC audio device.
  *
  * Returns 0 for success else error.
  */
-static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card,
-                                      struct snd_soc_dapm_context *dapm,
+static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
                                       enum snd_soc_bias_level level)
 {
+       struct snd_soc_card *card = dapm->card;
        int ret = 0;
 
        switch (level) {
@@ -365,9 +366,20 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *w)
 {
        int i, ret = 0;
-       size_t name_len;
+       size_t name_len, prefix_len;
        struct snd_soc_dapm_path *path;
-       struct snd_card *card = dapm->codec->card->snd_card;
+       struct snd_card *card = dapm->card->snd_card;
+       const char *prefix;
+
+       if (dapm->codec)
+               prefix = dapm->codec->name_prefix;
+       else
+               prefix = NULL;
+
+       if (prefix)
+               prefix_len = strlen(prefix) + 1;
+       else
+               prefix_len = 0;
 
        /* add kcontrol */
        for (i = 0; i < w->num_kcontrols; i++) {
@@ -396,8 +408,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
 
                        switch (w->id) {
                        default:
+                               /* The control will get a prefix from
+                                * the control creation process but
+                                * we're also using the same prefix
+                                * for widgets so cut the prefix off
+                                * the front of the widget name.
+                                */
                                snprintf(path->long_name, name_len, "%s %s",
-                                        w->name, w->kcontrols[i].name);
+                                        w->name + prefix_len,
+                                        w->kcontrols[i].name);
                                break;
                        case snd_soc_dapm_mixer_named_ctl:
                                snprintf(path->long_name, name_len, "%s",
@@ -408,7 +427,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
                        path->long_name[name_len - 1] = '\0';
 
                        path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
-                               path->long_name);
+                                                     path->long_name, prefix);
                        ret = snd_ctl_add(card, path->kcontrol);
                        if (ret < 0) {
                                dev_err(dapm->dev,
@@ -429,7 +448,9 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
 {
        struct snd_soc_dapm_path *path = NULL;
        struct snd_kcontrol *kcontrol;
-       struct snd_card *card = dapm->codec->card->snd_card;
+       struct snd_card *card = dapm->card->snd_card;
+       const char *prefix;
+       size_t prefix_len;
        int ret = 0;
 
        if (!w->num_kcontrols) {
@@ -437,7 +458,22 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
                return -EINVAL;
        }
 
-       kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
+       if (dapm->codec)
+               prefix = dapm->codec->name_prefix;
+       else
+               prefix = NULL;
+
+       if (prefix)
+               prefix_len = strlen(prefix) + 1;
+       else
+               prefix_len = 0;
+
+       /* The control will get a prefix from the control creation
+        * process but we're also using the same prefix for widgets so
+        * cut the prefix off the front of the widget name.
+        */
+       kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len,
+                               prefix);
        ret = snd_ctl_add(card, kcontrol);
 
        if (ret < 0)
@@ -479,7 +515,7 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
  */
 static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
 {
-       int level = snd_power_get_state(widget->dapm->codec->card->snd_card);
+       int level = snd_power_get_state(widget->dapm->card->snd_card);
 
        switch (level) {
        case SNDRV_CTL_POWER_D3hot:
@@ -734,10 +770,23 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 
 static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
                            struct snd_soc_dapm_widget *b,
-                           int sort[])
+                           bool power_up)
 {
+       int *sort;
+
+       if (power_up)
+               sort = dapm_up_seq;
+       else
+               sort = dapm_down_seq;
+
        if (sort[a->id] != sort[b->id])
                return sort[a->id] - sort[b->id];
+       if (a->subseq != b->subseq) {
+               if (power_up)
+                       return a->subseq - b->subseq;
+               else
+                       return b->subseq - a->subseq;
+       }
        if (a->reg != b->reg)
                return a->reg - b->reg;
        if (a->dapm != b->dapm)
@@ -749,12 +798,12 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
 /* Insert a widget in order into a DAPM power sequence. */
 static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
                            struct list_head *list,
-                           int sort[])
+                           bool power_up)
 {
        struct snd_soc_dapm_widget *w;
 
        list_for_each_entry(w, list, power_list)
-               if (dapm_seq_compare(new_widget, w, sort) < 0) {
+               if (dapm_seq_compare(new_widget, w, power_up) < 0) {
                        list_add_tail(&new_widget->power_list, &w->power_list);
                        return;
                }
@@ -865,26 +914,42 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
  * handled.
  */
 static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
-                        struct list_head *list, int event, int sort[])
+                        struct list_head *list, int event, bool power_up)
 {
        struct snd_soc_dapm_widget *w, *n;
        LIST_HEAD(pending);
        int cur_sort = -1;
+       int cur_subseq = -1;
        int cur_reg = SND_SOC_NOPM;
        struct snd_soc_dapm_context *cur_dapm = NULL;
-       int ret;
+       int ret, i;
+       int *sort;
+
+       if (power_up)
+               sort = dapm_up_seq;
+       else
+               sort = dapm_down_seq;
 
        list_for_each_entry_safe(w, n, list, power_list) {
                ret = 0;
 
                /* Do we need to apply any queued changes? */
                if (sort[w->id] != cur_sort || w->reg != cur_reg ||
-                   w->dapm != cur_dapm) {
+                   w->dapm != cur_dapm || w->subseq != cur_subseq) {
                        if (!list_empty(&pending))
                                dapm_seq_run_coalesced(cur_dapm, &pending);
 
+                       if (cur_dapm && cur_dapm->seq_notifier) {
+                               for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
+                                       if (sort[i] == cur_sort)
+                                               cur_dapm->seq_notifier(cur_dapm,
+                                                                      i,
+                                                                      cur_subseq);
+                       }
+
                        INIT_LIST_HEAD(&pending);
                        cur_sort = -1;
+                       cur_subseq = -1;
                        cur_reg = SND_SOC_NOPM;
                        cur_dapm = NULL;
                }
@@ -929,6 +994,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
                default:
                        /* Queue it up for application */
                        cur_sort = sort[w->id];
+                       cur_subseq = w->subseq;
                        cur_reg = w->reg;
                        cur_dapm = w->dapm;
                        list_move(&w->power_list, &pending);
@@ -942,6 +1008,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
 
        if (!list_empty(&pending))
                dapm_seq_run_coalesced(cur_dapm, &pending);
+
+       if (cur_dapm && cur_dapm->seq_notifier) {
+               for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
+                       if (sort[i] == cur_sort)
+                               cur_dapm->seq_notifier(cur_dapm,
+                                                      i, cur_subseq);
+       }
 }
 
 static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
@@ -977,7 +1050,62 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
        }
 }
 
+/* Async callback run prior to DAPM sequences - brings to _PREPARE if
+ * they're changing state.
+ */
+static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
+{
+       struct snd_soc_dapm_context *d = data;
+       int ret;
 
+       if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
+               ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
+               if (ret != 0)
+                       dev_err(d->dev,
+                               "Failed to turn on bias: %d\n", ret);
+       }
+
+       /* If we're changing to all on or all off then prepare */
+       if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
+           (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
+               ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
+               if (ret != 0)
+                       dev_err(d->dev,
+                               "Failed to prepare bias: %d\n", ret);
+       }
+}
+
+/* Async callback run prior to DAPM sequences - brings to their final
+ * state.
+ */
+static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
+{
+       struct snd_soc_dapm_context *d = data;
+       int ret;
+
+       /* If we just powered the last thing off drop to standby bias */
+       if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
+               ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
+               if (ret != 0)
+                       dev_err(d->dev, "Failed to apply standby bias: %d\n",
+                               ret);
+       }
+
+       /* If we're in standby and can support bias off then do that */
+       if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) {
+               ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
+               if (ret != 0)
+                       dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
+       }
+
+       /* If we just powered up then move to active bias */
+       if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
+               ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
+               if (ret != 0)
+                       dev_err(d->dev, "Failed to apply active bias: %d\n",
+                               ret);
+       }
+}
 
 /*
  * Scan each dapm widget for complete audio path.
@@ -990,12 +1118,12 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
  */
 static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
 {
-       struct snd_soc_card *card = dapm->codec->card;
+       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
        struct snd_soc_dapm_context *d;
        LIST_HEAD(up_list);
        LIST_HEAD(down_list);
-       int ret = 0;
+       LIST_HEAD(async_domain);
        int power;
 
        trace_snd_soc_dapm_start(card);
@@ -1010,10 +1138,10 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        list_for_each_entry(w, &card->widgets, list) {
                switch (w->id) {
                case snd_soc_dapm_pre:
-                       dapm_seq_insert(w, &down_list, dapm_down_seq);
+                       dapm_seq_insert(w, &down_list, false);
                        break;
                case snd_soc_dapm_post:
-                       dapm_seq_insert(w, &up_list, dapm_up_seq);
+                       dapm_seq_insert(w, &up_list, true);
                        break;
 
                default:
@@ -1033,9 +1161,9 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                        trace_snd_soc_dapm_widget_power(w, power);
 
                        if (power)
-                               dapm_seq_insert(w, &up_list, dapm_up_seq);
+                               dapm_seq_insert(w, &up_list, true);
                        else
-                               dapm_seq_insert(w, &down_list, dapm_down_seq);
+                               dapm_seq_insert(w, &down_list, false);
 
                        w->power = power;
                        break;
@@ -1073,65 +1201,25 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                }
        }
 
-       list_for_each_entry(d, &dapm->card->dapm_list, list) {
-               if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
-                       ret = snd_soc_dapm_set_bias_level(card, d,
-                                                         SND_SOC_BIAS_STANDBY);
-                       if (ret != 0)
-                               dev_err(d->dev,
-                                       "Failed to turn on bias: %d\n", ret);
-               }
-
-               /* If we're changing to all on or all off then prepare */
-               if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
-                   (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
-                       ret = snd_soc_dapm_set_bias_level(card, d,
-                                                         SND_SOC_BIAS_PREPARE);
-                       if (ret != 0)
-                               dev_err(d->dev,
-                                       "Failed to prepare bias: %d\n", ret);
-               }
-       }
+       /* Run all the bias changes in parallel */
+       list_for_each_entry(d, &dapm->card->dapm_list, list)
+               async_schedule_domain(dapm_pre_sequence_async, d,
+                                       &async_domain);
+       async_synchronize_full_domain(&async_domain);
 
        /* Power down widgets first; try to avoid amplifying pops. */
-       dapm_seq_run(dapm, &down_list, event, dapm_down_seq);
+       dapm_seq_run(dapm, &down_list, event, false);
 
        dapm_widget_update(dapm);
 
        /* Now power up. */
-       dapm_seq_run(dapm, &up_list, event, dapm_up_seq);
-
-       list_for_each_entry(d, &dapm->card->dapm_list, list) {
-               /* If we just powered the last thing off drop to standby bias */
-               if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
-                       ret = snd_soc_dapm_set_bias_level(card, d,
-                                                         SND_SOC_BIAS_STANDBY);
-                       if (ret != 0)
-                               dev_err(d->dev,
-                                       "Failed to apply standby bias: %d\n",
-                                       ret);
-               }
+       dapm_seq_run(dapm, &up_list, event, true);
 
-               /* If we're in standby and can support bias off then do that */
-               if (d->bias_level == SND_SOC_BIAS_STANDBY &&
-                   d->idle_bias_off) {
-                       ret = snd_soc_dapm_set_bias_level(card, d,
-                                                         SND_SOC_BIAS_OFF);
-                       if (ret != 0)
-                               dev_err(d->dev,
-                                       "Failed to turn off bias: %d\n", ret);
-               }
-
-               /* If we just powered up then move to active bias */
-               if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
-                       ret = snd_soc_dapm_set_bias_level(card, d,
-                                                         SND_SOC_BIAS_ON);
-                       if (ret != 0)
-                               dev_err(d->dev,
-                                       "Failed to apply active bias: %d\n",
-                                       ret);
-               }
-       }
+       /* Run all the bias changes in parallel */
+       list_for_each_entry(d, &dapm->card->dapm_list, list)
+               async_schedule_domain(dapm_post_sequence_async, d,
+                                       &async_domain);
+       async_synchronize_full_domain(&async_domain);
 
        pop_dbg(dapm->dev, card->pop_time,
                "DAPM sequencing finished, waiting %dms\n", card->pop_time);
@@ -1189,7 +1277,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 
                if (p->connect)
                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
-                                       " in  %s %s\n",
+                                       " in  \"%s\" \"%s\"\n",
                                        p->name ? p->name : "static",
                                        p->source->name);
        }
@@ -1199,7 +1287,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 
                if (p->connect)
                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
-                                       " out %s %s\n",
+                                       " out \"%s\" \"%s\"\n",
                                        p->name ? p->name : "static",
                                        p->sink->name);
        }
@@ -1464,7 +1552,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        char prefixed_source[80];
        int ret = 0;
 
-       if (dapm->codec->name_prefix) {
+       if (dapm->codec && dapm->codec->name_prefix) {
                snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
                         dapm->codec->name_prefix, route->sink);
                sink = prefixed_sink;
@@ -2114,14 +2202,14 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                return -ENOMEM;
 
        name_len = strlen(widget->name) + 1;
-       if (dapm->codec->name_prefix)
+       if (dapm->codec && dapm->codec->name_prefix)
                name_len += 1 + strlen(dapm->codec->name_prefix);
        w->name = kmalloc(name_len, GFP_KERNEL);
        if (w->name == NULL) {
                kfree(w);
                return -ENOMEM;
        }
-       if (dapm->codec->name_prefix)
+       if (dapm->codec && dapm->codec->name_prefix)
                snprintf(w->name, name_len, "%s %s",
                        dapm->codec->name_prefix, widget->name);
        else
@@ -2226,7 +2314,6 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
        mutex_unlock(&codec->mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
 /**
  * snd_soc_dapm_enable_pin - enable pin.
@@ -2393,7 +2480,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
                if (w->dapm != dapm)
                        continue;
                if (w->power) {
-                       dapm_seq_insert(w, &down_list, dapm_down_seq);
+                       dapm_seq_insert(w, &down_list, false);
                        w->power = 0;
                        powerdown = 1;
                }
@@ -2403,9 +2490,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
         * standby.
         */
        if (powerdown) {
-               snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE);
-               dapm_seq_run(dapm, &down_list, 0, dapm_down_seq);
-               snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY);
+               snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE);
+               dapm_seq_run(dapm, &down_list, 0, false);
+               snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY);
        }
 }
 
@@ -2418,7 +2505,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
 
        list_for_each_entry(codec, &card->codec_dev_list, list) {
                soc_dapm_shutdown_codec(&codec->dapm);
-               snd_soc_dapm_set_bias_level(card, &codec->dapm, SND_SOC_BIAS_OFF);
+               snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF);
        }
 }
 
index ac5a5bc7375a235d3bf5e14bd4a9a31e9cb16f1b..fcab80b36a378f4f9089971d1d04a9473f65549c 100644 (file)
@@ -37,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
 {
        jack->codec = codec;
        INIT_LIST_HEAD(&jack->pins);
+       INIT_LIST_HEAD(&jack->jack_zones);
        BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
 
        return snd_jack_new(codec->card->snd_card, id, type, &jack->jack);
@@ -100,7 +101,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
        }
 
        /* Report before the DAPM sync to help users updating micbias status */
-       blocking_notifier_call_chain(&jack->notifier, status, NULL);
+       blocking_notifier_call_chain(&jack->notifier, status, jack);
 
        snd_soc_dapm_sync(dapm);
 
@@ -111,6 +112,51 @@ out:
 }
 EXPORT_SYMBOL_GPL(snd_soc_jack_report);
 
+/**
+ * snd_soc_jack_add_zones - Associate voltage zones with jack
+ *
+ * @jack:  ASoC jack
+ * @count: Number of zones
+ * @zone:  Array of zones
+ *
+ * After this function has been called the zones specified in the
+ * array will be associated with the jack.
+ */
+int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count,
+                         struct snd_soc_jack_zone *zones)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               INIT_LIST_HEAD(&zones[i].list);
+               list_add(&(zones[i].list), &jack->jack_zones);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones);
+
+/**
+ * snd_soc_jack_get_type - Based on the mic bias value, this function returns
+ * the type of jack from the zones delcared in the jack type
+ *
+ * @micbias_voltage:  mic bias voltage at adc channel when jack is plugged in
+ *
+ * Based on the mic bias value passed, this function helps identify
+ * the type of jack from the already delcared jack zones
+ */
+int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage)
+{
+       struct snd_soc_jack_zone *zone;
+
+       list_for_each_entry(zone, &jack->jack_zones, list) {
+               if (micbias_voltage >= zone->min_mv &&
+                       micbias_voltage < zone->max_mv)
+                               return zone->jack_type;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_get_type);
+
 /**
  * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
  *
@@ -194,7 +240,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
        int enable;
        int report;
 
-       enable = gpio_get_value(gpio->gpio);
+       enable = gpio_get_value_cansleep(gpio->gpio);
        if (gpio->invert)
                enable = !enable;
 
@@ -284,6 +330,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                if (ret)
                        goto err;
 
+               if (gpios[i].wake) {
+                       ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1);
+                       if (ret != 0)
+                               printk(KERN_ERR
+                                 "Failed to mark GPIO %d as wake source: %d\n",
+                                       gpios[i].gpio, ret);
+               }
+
 #ifdef CONFIG_GPIO_SYSFS
                /* Expose GPIO value over sysfs for diagnostic purposes */
                gpio_export(gpios[i].gpio, false);
index 1d07b931f3d8993d6d6844e4d2cd2a520e2cbced..3f45e6a439bff6b1e3f8060063c25855350db6ca 100644 (file)
@@ -28,26 +28,9 @@ int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params)
 {
        int sample_size;
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
-       case SNDRV_PCM_FORMAT_S16_BE:
-               sample_size = 16;
-               break;
-       case SNDRV_PCM_FORMAT_S20_3LE:
-       case SNDRV_PCM_FORMAT_S20_3BE:
-               sample_size = 20;
-               break;
-       case SNDRV_PCM_FORMAT_S24_LE:
-       case SNDRV_PCM_FORMAT_S24_BE:
-               sample_size = 24;
-               break;
-       case SNDRV_PCM_FORMAT_S32_LE:
-       case SNDRV_PCM_FORMAT_S32_BE:
-               sample_size = 32;
-               break;
-       default:
-               return -ENOTSUPP;
-       }
+       sample_size = snd_pcm_format_width(params_format(params));
+       if (sample_size < 0)
+               return sample_size;
 
        return snd_soc_calc_frame_size(sample_size, params_channels(params),
                                       1);
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
new file mode 100644 (file)
index 0000000..66b504f
--- /dev/null
@@ -0,0 +1,26 @@
+config SND_TEGRA_SOC
+       tristate "SoC Audio for the Tegra System-on-Chip"
+       depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+       default m
+       help
+         Say Y or M here if you want support for SoC audio on Tegra.
+
+config SND_TEGRA_SOC_I2S
+       tristate
+       depends on SND_TEGRA_SOC
+       default m
+       help
+         Say Y or M if you want to add support for codecs attached to the
+         Tegra I2S interface. You will also need to select the individual
+         machine drivers to support below.
+
+config SND_TEGRA_SOC_HARMONY
+       tristate "SoC Audio support for Tegra Harmony reference board"
+       depends on SND_TEGRA_SOC && MACH_HARMONY && I2C
+       default m
+       select SND_TEGRA_SOC_I2S
+       select SND_SOC_WM8903
+       help
+         Say Y or M here if you want to add support for SoC audio on the
+         Tegra Harmony reference board.
+
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
new file mode 100644 (file)
index 0000000..fd183d3
--- /dev/null
@@ -0,0 +1,15 @@
+# Tegra platform Support
+snd-soc-tegra-das-objs := tegra_das.o
+snd-soc-tegra-pcm-objs := tegra_pcm.o
+snd-soc-tegra-i2s-objs := tegra_i2s.o
+snd-soc-tegra-utils-objs += tegra_asoc_utils.o
+
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o
+obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o
+
+# Tegra machine Support
+snd-soc-tegra-harmony-objs := harmony.o
+
+obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o
diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c
new file mode 100644 (file)
index 0000000..8585957
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * harmony.c - Harmony machine ASoC driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <mach/harmony_audio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8903.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-harmony"
+
+#define GPIO_SPKR_EN    BIT(0)
+#define GPIO_INT_MIC_EN BIT(1)
+#define GPIO_EXT_MIC_EN BIT(2)
+
+struct tegra_harmony {
+       struct tegra_asoc_utils_data util_data;
+       struct harmony_audio_platform_data *pdata;
+       int gpio_requested;
+};
+
+static int harmony_asoc_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = codec->card;
+       struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
+       int srate, mclk, mclk_change;
+       int err;
+
+       srate = params_rate(params);
+       switch (srate) {
+       case 64000:
+       case 88200:
+       case 96000:
+               mclk = 128 * srate;
+               break;
+       default:
+               mclk = 256 * srate;
+               break;
+       }
+       /* FIXME: Codec only requires >= 3MHz if OSR==0 */
+       while (mclk < 6000000)
+               mclk *= 2;
+
+       err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk,
+                                       &mclk_change);
+       if (err < 0) {
+               dev_err(card->dev, "Can't configure clocks\n");
+               return err;
+       }
+
+       err = snd_soc_dai_set_fmt(codec_dai,
+                                       SND_SOC_DAIFMT_I2S |
+                                       SND_SOC_DAIFMT_NB_NF |
+                                       SND_SOC_DAIFMT_CBS_CFS);
+       if (err < 0) {
+               dev_err(card->dev, "codec_dai fmt not set\n");
+               return err;
+       }
+
+       err = snd_soc_dai_set_fmt(cpu_dai,
+                                       SND_SOC_DAIFMT_I2S |
+                                       SND_SOC_DAIFMT_NB_NF |
+                                       SND_SOC_DAIFMT_CBS_CFS);
+       if (err < 0) {
+               dev_err(card->dev, "cpu_dai fmt not set\n");
+               return err;
+       }
+
+       if (mclk_change) {
+               err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+                                            SND_SOC_CLOCK_IN);
+               if (err < 0) {
+                       dev_err(card->dev, "codec_dai clock not set\n");
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops harmony_asoc_ops = {
+       .hw_params = harmony_asoc_hw_params,
+};
+
+static struct snd_soc_jack harmony_hp_jack;
+
+static struct snd_soc_jack_pin harmony_hp_jack_pins[] = {
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = {
+       {
+               .name = "headphone detect",
+               .report = SND_JACK_HEADPHONE,
+               .debounce_time = 150,
+               .invert = 1,
+       }
+};
+
+static struct snd_soc_jack harmony_mic_jack;
+
+static struct snd_soc_jack_pin harmony_mic_jack_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+};
+
+static int harmony_event_int_spk(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_card *card = codec->card;
+       struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
+       struct harmony_audio_platform_data *pdata = harmony->pdata;
+
+       gpio_set_value_cansleep(pdata->gpio_spkr_en,
+                               SND_SOC_DAPM_EVENT_ON(event));
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route harmony_audio_map[] = {
+       {"Headphone Jack", NULL, "HPOUTR"},
+       {"Headphone Jack", NULL, "HPOUTL"},
+       {"Int Spk", NULL, "ROP"},
+       {"Int Spk", NULL, "RON"},
+       {"Int Spk", NULL, "LOP"},
+       {"Int Spk", NULL, "LON"},
+       {"Mic Bias", NULL, "Mic Jack"},
+       {"IN1L", NULL, "Mic Bias"},
+};
+
+static const struct snd_kcontrol_new harmony_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Int Spk"),
+};
+
+static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_card *card = codec->card;
+       struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
+       struct harmony_audio_platform_data *pdata = harmony->pdata;
+       int ret;
+
+       ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
+       if (ret) {
+               dev_err(card->dev, "cannot get spkr_en gpio\n");
+               return ret;
+       }
+       harmony->gpio_requested |= GPIO_SPKR_EN;
+
+       gpio_direction_output(pdata->gpio_spkr_en, 0);
+
+       ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
+       if (ret) {
+               dev_err(card->dev, "cannot get int_mic_en gpio\n");
+               return ret;
+       }
+       harmony->gpio_requested |= GPIO_INT_MIC_EN;
+
+       /* Disable int mic; enable signal is active-high */
+       gpio_direction_output(pdata->gpio_int_mic_en, 0);
+
+       ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
+       if (ret) {
+               dev_err(card->dev, "cannot get ext_mic_en gpio\n");
+               return ret;
+       }
+       harmony->gpio_requested |= GPIO_EXT_MIC_EN;
+
+       /* Enable ext mic; enable signal is active-low */
+       gpio_direction_output(pdata->gpio_ext_mic_en, 0);
+
+       ret = snd_soc_add_controls(codec, harmony_controls,
+                                  ARRAY_SIZE(harmony_controls));
+       if (ret < 0)
+               return ret;
+
+       snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets,
+                                       ARRAY_SIZE(harmony_dapm_widgets));
+
+       snd_soc_dapm_add_routes(dapm, harmony_audio_map,
+                               ARRAY_SIZE(harmony_audio_map));
+
+       harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det;
+       snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
+                        &harmony_hp_jack);
+       snd_soc_jack_add_pins(&harmony_hp_jack,
+                             ARRAY_SIZE(harmony_hp_jack_pins),
+                             harmony_hp_jack_pins);
+       snd_soc_jack_add_gpios(&harmony_hp_jack,
+                              ARRAY_SIZE(harmony_hp_jack_gpios),
+                              harmony_hp_jack_gpios);
+
+       snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
+                        &harmony_mic_jack);
+       snd_soc_jack_add_pins(&harmony_mic_jack,
+                             ARRAY_SIZE(harmony_mic_jack_pins),
+                             harmony_mic_jack_pins);
+       wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0);
+
+       snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+       snd_soc_dapm_nc_pin(dapm, "IN3L");
+       snd_soc_dapm_nc_pin(dapm, "IN3R");
+       snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
+       snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
+
+       snd_soc_dapm_sync(dapm);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link harmony_wm8903_dai = {
+       .name = "WM8903",
+       .stream_name = "WM8903 PCM",
+       .codec_name = "wm8903.0-001a",
+       .platform_name = "tegra-pcm-audio",
+       .cpu_dai_name = "tegra-i2s.0",
+       .codec_dai_name = "wm8903-hifi",
+       .init = harmony_asoc_init,
+       .ops = &harmony_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_harmony = {
+       .name = "tegra-harmony",
+       .dai_link = &harmony_wm8903_dai,
+       .num_links = 1,
+};
+
+static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &snd_soc_harmony;
+       struct tegra_harmony *harmony;
+       struct harmony_audio_platform_data *pdata;
+       int ret;
+
+       if (!machine_is_harmony()) {
+               dev_err(&pdev->dev, "Not running on Tegra Harmony!\n");
+               return -ENODEV;
+       }
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data supplied\n");
+               return -EINVAL;
+       }
+
+       harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL);
+       if (!harmony) {
+               dev_err(&pdev->dev, "Can't allocate tegra_harmony\n");
+               return -ENOMEM;
+       }
+
+       harmony->pdata = pdata;
+
+       ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev);
+       if (ret)
+               goto err_free_harmony;
+
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+       snd_soc_card_set_drvdata(card, harmony);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+                       ret);
+               goto err_clear_drvdata;
+       }
+
+       return 0;
+
+err_clear_drvdata:
+       snd_soc_card_set_drvdata(card, NULL);
+       platform_set_drvdata(pdev, NULL);
+       card->dev = NULL;
+       tegra_asoc_utils_fini(&harmony->util_data);
+err_free_harmony:
+       kfree(harmony);
+       return ret;
+}
+
+static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
+       struct harmony_audio_platform_data *pdata = harmony->pdata;
+
+       snd_soc_unregister_card(card);
+
+       snd_soc_card_set_drvdata(card, NULL);
+       platform_set_drvdata(pdev, NULL);
+       card->dev = NULL;
+
+       tegra_asoc_utils_fini(&harmony->util_data);
+
+       if (harmony->gpio_requested & GPIO_EXT_MIC_EN)
+               gpio_free(pdata->gpio_ext_mic_en);
+       if (harmony->gpio_requested & GPIO_INT_MIC_EN)
+               gpio_free(pdata->gpio_int_mic_en);
+       if (harmony->gpio_requested & GPIO_SPKR_EN)
+               gpio_free(pdata->gpio_spkr_en);
+
+       kfree(harmony);
+
+       return 0;
+}
+
+static struct platform_driver tegra_snd_harmony_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = tegra_snd_harmony_probe,
+       .remove = __devexit_p(tegra_snd_harmony_remove),
+};
+
+static int __init snd_tegra_harmony_init(void)
+{
+       return platform_driver_register(&tegra_snd_harmony_driver);
+}
+module_init(snd_tegra_harmony_init);
+
+static void __exit snd_tegra_harmony_exit(void)
+{
+       platform_driver_unregister(&tegra_snd_harmony_driver);
+}
+module_exit(snd_tegra_harmony_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Harmony machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
new file mode 100644 (file)
index 0000000..52f0a3f
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * tegra_asoc_utils.c - Harmony machine ASoC driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+
+#include "tegra_asoc_utils.h"
+
+int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
+                             int mclk, int *mclk_change)
+{
+       int new_baseclock;
+       int err;
+
+       switch (srate) {
+       case 11025:
+       case 22050:
+       case 44100:
+       case 88200:
+               new_baseclock = 56448000;
+               break;
+       case 8000:
+       case 16000:
+       case 32000:
+       case 48000:
+       case 64000:
+       case 96000:
+               new_baseclock = 73728000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *mclk_change = ((new_baseclock != data->set_baseclock) ||
+                       (mclk != data->set_mclk));
+       if (!*mclk_change)
+           return 0;
+
+       data->set_baseclock = 0;
+       data->set_mclk = 0;
+
+       clk_disable(data->clk_cdev1);
+       clk_disable(data->clk_pll_a_out0);
+       clk_disable(data->clk_pll_a);
+
+       err = clk_set_rate(data->clk_pll_a, new_baseclock);
+       if (err) {
+               dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
+               return err;
+       }
+
+       err = clk_set_rate(data->clk_pll_a_out0, mclk);
+       if (err) {
+               dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
+               return err;
+       }
+
+       /* Don't set cdev1 rate; its locked to pll_a_out0 */
+
+       err = clk_enable(data->clk_pll_a);
+       if (err) {
+               dev_err(data->dev, "Can't enable pll_a: %d\n", err);
+               return err;
+       }
+
+       err = clk_enable(data->clk_pll_a_out0);
+       if (err) {
+               dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
+               return err;
+       }
+
+       err = clk_enable(data->clk_cdev1);
+       if (err) {
+               dev_err(data->dev, "Can't enable cdev1: %d\n", err);
+               return err;
+       }
+
+       data->set_baseclock = new_baseclock;
+       data->set_mclk = mclk;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
+
+int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
+                         struct device *dev)
+{
+       int ret;
+
+       data->dev = dev;
+
+       data->clk_pll_a = clk_get_sys(NULL, "pll_a");
+       if (IS_ERR(data->clk_pll_a)) {
+               dev_err(data->dev, "Can't retrieve clk pll_a\n");
+               ret = PTR_ERR(data->clk_pll_a);
+               goto err;
+       }
+
+       data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+       if (IS_ERR(data->clk_pll_a_out0)) {
+               dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
+               ret = PTR_ERR(data->clk_pll_a_out0);
+               goto err_put_pll_a;
+       }
+
+       data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
+       if (IS_ERR(data->clk_cdev1)) {
+               dev_err(data->dev, "Can't retrieve clk cdev1\n");
+               ret = PTR_ERR(data->clk_cdev1);
+               goto err_put_pll_a_out0;
+       }
+
+       return 0;
+
+err_put_pll_a_out0:
+       clk_put(data->clk_pll_a_out0);
+err_put_pll_a:
+       clk_put(data->clk_pll_a);
+err:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
+
+void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
+{
+       clk_put(data->clk_cdev1);
+       clk_put(data->clk_pll_a_out0);
+       clk_put(data->clk_pll_a);
+}
+EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra ASoC utility code");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
new file mode 100644 (file)
index 0000000..bbba7af
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * tegra_asoc_utils.h - Definitions for Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_ASOC_UTILS_H__
+#define __TEGRA_ASOC_UTILS_H_
+
+struct clk;
+struct device;
+
+struct tegra_asoc_utils_data {
+       struct device *dev;
+       struct clk *clk_pll_a;
+       struct clk *clk_pll_a_out0;
+       struct clk *clk_cdev1;
+       int set_baseclock;
+       int set_mclk;
+};
+
+int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
+                             int mclk, int *mclk_change);
+int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
+                         struct device *dev);
+void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
+
+#endif
+
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
new file mode 100644 (file)
index 0000000..9f24ef7
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * tegra_das.c - Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/soc.h>
+#include "tegra_das.h"
+
+#define DRV_NAME "tegra-das"
+
+static struct tegra_das *das;
+
+static inline void tegra_das_write(u32 reg, u32 val)
+{
+       __raw_writel(val, das->regs + reg);
+}
+
+static inline u32 tegra_das_read(u32 reg)
+{
+       return __raw_readl(das->regs + reg);
+}
+
+int tegra_das_connect_dap_to_dac(int dap, int dac)
+{
+       u32 addr;
+       u32 reg;
+
+       if (!das)
+               return -ENODEV;
+
+       addr = TEGRA_DAS_DAP_CTRL_SEL +
+               (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+       reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
+
+       tegra_das_write(addr, reg);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
+
+int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
+                                       int sdata1rx, int sdata2rx)
+{
+       u32 addr;
+       u32 reg;
+
+       if (!das)
+               return -ENODEV;
+
+       addr = TEGRA_DAS_DAP_CTRL_SEL +
+               (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+       reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
+               !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
+               !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
+               !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
+
+       tegra_das_write(addr, reg);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
+
+int tegra_das_connect_dac_to_dap(int dac, int dap)
+{
+       u32 addr;
+       u32 reg;
+
+       if (!das)
+               return -ENODEV;
+
+       addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
+               (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+       reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
+               dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
+               dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
+
+       tegra_das_write(addr, reg);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_das_show(struct seq_file *s, void *unused)
+{
+       int i;
+       u32 addr;
+       u32 reg;
+
+       for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
+               addr = TEGRA_DAS_DAP_CTRL_SEL +
+                       (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+               reg = tegra_das_read(addr);
+               seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
+       }
+
+       for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
+               addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
+                       (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+               reg = tegra_das_read(addr);
+               seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
+                                i, reg);
+       }
+
+       return 0;
+}
+
+static int tegra_das_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, tegra_das_show, inode->i_private);
+}
+
+static const struct file_operations tegra_das_debug_fops = {
+       .open    = tegra_das_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release,
+};
+
+static void tegra_das_debug_add(struct tegra_das *das)
+{
+       das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
+                                        snd_soc_debugfs_root, das,
+                                        &tegra_das_debug_fops);
+}
+
+static void tegra_das_debug_remove(struct tegra_das *das)
+{
+       if (das->debug)
+               debugfs_remove(das->debug);
+}
+#else
+static inline void tegra_das_debug_add(struct tegra_das *das)
+{
+}
+
+static inline void tegra_das_debug_remove(struct tegra_das *das)
+{
+}
+#endif
+
+static int __devinit tegra_das_probe(struct platform_device *pdev)
+{
+       struct resource *res, *region;
+       int ret = 0;
+
+       if (das)
+               return -ENODEV;
+
+       das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL);
+       if (!das) {
+               dev_err(&pdev->dev, "Can't allocate tegra_das\n");
+               ret = -ENOMEM;
+               goto exit;
+       }
+       das->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "No memory resource\n");
+               ret = -ENODEV;
+               goto err_free;
+       }
+
+       region = request_mem_region(res->start, resource_size(res),
+                                       pdev->name);
+       if (!region) {
+               dev_err(&pdev->dev, "Memory region already claimed\n");
+               ret = -EBUSY;
+               goto err_free;
+       }
+
+       das->regs = ioremap(res->start, resource_size(res));
+       if (!das->regs) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_release;
+       }
+
+       tegra_das_debug_add(das);
+
+       platform_set_drvdata(pdev, das);
+
+       return 0;
+
+err_release:
+       release_mem_region(res->start, resource_size(res));
+err_free:
+       kfree(das);
+       das = 0;
+exit:
+       return ret;
+}
+
+static int __devexit tegra_das_remove(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       if (!das)
+               return -ENODEV;
+
+       platform_set_drvdata(pdev, NULL);
+
+       tegra_das_debug_remove(das);
+
+       iounmap(das->regs);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(das);
+       das = 0;
+
+       return 0;
+}
+
+static struct platform_driver tegra_das_driver = {
+       .probe = tegra_das_probe,
+       .remove = __devexit_p(tegra_das_remove),
+       .driver = {
+               .name = DRV_NAME,
+       },
+};
+
+static int __init tegra_das_modinit(void)
+{
+       return platform_driver_register(&tegra_das_driver);
+}
+module_init(tegra_das_modinit);
+
+static void __exit tegra_das_modexit(void)
+{
+       platform_driver_unregister(&tegra_das_driver);
+}
+module_exit(tegra_das_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra DAS driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h
new file mode 100644 (file)
index 0000000..2c96c7b
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * tegra_das.h - Definitions for Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_DAS_H__
+#define __TEGRA_DAS_H__
+
+/* Register TEGRA_DAS_DAP_CTRL_SEL */
+#define TEGRA_DAS_DAP_CTRL_SEL                         0x00
+#define TEGRA_DAS_DAP_CTRL_SEL_COUNT                   5
+#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE                  4
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P            31
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S            1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P      30
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S      1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P      29
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S      1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P          0
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S          5
+
+/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
+#define TEGRA_DAS_DAP_SEL_DAC1 0
+#define TEGRA_DAS_DAP_SEL_DAC2 1
+#define TEGRA_DAS_DAP_SEL_DAC3 2
+#define TEGRA_DAS_DAP_SEL_DAP1 16
+#define TEGRA_DAS_DAP_SEL_DAP2 17
+#define TEGRA_DAS_DAP_SEL_DAP3 18
+#define TEGRA_DAS_DAP_SEL_DAP4 19
+#define TEGRA_DAS_DAP_SEL_DAP5 20
+
+/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL                       0x40
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT                 3
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE                        4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P      28
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S      4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P      24
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S      4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P         0
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S         4
+
+/*
+ * Values for:
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
+ */
+#define TEGRA_DAS_DAC_SEL_DAP1 0
+#define TEGRA_DAS_DAC_SEL_DAP2 1
+#define TEGRA_DAS_DAC_SEL_DAP3 2
+#define TEGRA_DAS_DAC_SEL_DAP4 3
+#define TEGRA_DAS_DAC_SEL_DAP5 4
+
+/*
+ * Names/IDs of the DACs/DAPs.
+ */
+
+#define TEGRA_DAS_DAP_ID_1 0
+#define TEGRA_DAS_DAP_ID_2 1
+#define TEGRA_DAS_DAP_ID_3 2
+#define TEGRA_DAS_DAP_ID_4 3
+#define TEGRA_DAS_DAP_ID_5 4
+
+#define TEGRA_DAS_DAC_ID_1 0
+#define TEGRA_DAS_DAC_ID_2 1
+#define TEGRA_DAS_DAC_ID_3 2
+
+struct tegra_das {
+       struct device *dev;
+       void __iomem *regs;
+       struct dentry *debug;
+};
+
+/*
+ * Terminology:
+ * DAS: Digital audio switch (HW module controlled by this driver)
+ * DAP: Digital audio port (port/pins on Tegra device)
+ * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
+ * 
+ * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
+ * DAC, or another DAP. When DAPs are connected, one must be the master and
+ * one the slave. Each DAC allows selection of a specific DAP for input, to
+ * cater for the case where N DAPs are connected to 1 DAC for broadcast
+ * output.
+ *
+ * This driver is dumb; no attempt is made to ensure that a valid routing
+ * configuration is programmed.
+ */
+
+/*
+ * Connect a DAP to to a DAC
+ * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
+ * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC*
+ */
+extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel);
+
+/*
+ * Connect a DAP to to another DAP
+ * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
+ * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP*
+ * master: Is this DAP the master (1) or slave (0)
+ * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
+ * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
+ */
+extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
+                                       int master, int sdata1rx,
+                                       int sdata2rx);
+
+/*
+ * Connect a DAC's input to a DAP
+ * (DAC outputs are selected by the DAP)
+ * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_*
+ * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP*
+ */
+extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel);
+
+#endif
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
new file mode 100644 (file)
index 0000000..4f5e2c9
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * tegra_i2s.c - Tegra I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+
+#define DRV_NAME "tegra-i2s"
+
+static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val)
+{
+       __raw_writel(val, i2s->regs + reg);
+}
+
+static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg)
+{
+       return __raw_readl(i2s->regs + reg);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_i2s_show(struct seq_file *s, void *unused)
+{
+#define REG(r) { r, #r }
+       static const struct {
+               int offset;
+               const char *name;
+       } regs[] = {
+               REG(TEGRA_I2S_CTRL),
+               REG(TEGRA_I2S_STATUS),
+               REG(TEGRA_I2S_TIMING),
+               REG(TEGRA_I2S_FIFO_SCR),
+               REG(TEGRA_I2S_PCM_CTRL),
+               REG(TEGRA_I2S_NW_CTRL),
+               REG(TEGRA_I2S_TDM_CTRL),
+               REG(TEGRA_I2S_TDM_TX_RX_CTRL),
+       };
+#undef REG
+
+       struct tegra_i2s *i2s = s->private;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(regs); i++) {
+               u32 val = tegra_i2s_read(i2s, regs[i].offset);
+               seq_printf(s, "%s = %08x\n", regs[i].name, val);
+       }
+
+       return 0;
+}
+
+static int tegra_i2s_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, tegra_i2s_show, inode->i_private);
+}
+
+static const struct file_operations tegra_i2s_debug_fops = {
+       .open    = tegra_i2s_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release,
+};
+
+static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
+{
+       char name[] = DRV_NAME ".0";
+
+       snprintf(name, sizeof(name), DRV_NAME".%1d", id);
+       i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
+                                               i2s, &tegra_i2s_debug_fops);
+}
+
+static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
+{
+       if (i2s->debug)
+               debugfs_remove(i2s->debug);
+}
+#else
+static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
+{
+}
+
+static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
+{
+}
+#endif
+
+static int tegra_i2s_set_fmt(struct snd_soc_dai *dai,
+                               unsigned int fmt)
+{
+       struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE;
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | 
+                               TEGRA_I2S_CTRL_LRCK_MASK);
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S;
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM;
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM;
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+        struct device *dev = substream->pcm->card->dev;
+       struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+       u32 reg;
+       int ret, sample_size, srate, i2sclock, bitcnt;
+
+       i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16;
+               sample_size = 16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24;
+               sample_size = 24;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32;
+               sample_size = 32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       srate = params_rate(params);
+
+       /* Final "* 2" required by Tegra hardware */
+       i2sclock = srate * params_channels(params) * sample_size * 2;
+
+       ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+       if (ret) {
+               dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+               return ret;
+       }
+
+       bitcnt = (i2sclock / (2 * srate)) - 1;
+       if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+               return -EINVAL;
+       reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+       if (i2sclock % (2 * srate))
+               reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
+
+       tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
+
+       tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
+               TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
+               TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
+
+       return 0;
+}
+
+static void tegra_i2s_start_playback(struct tegra_i2s *i2s)
+{
+       i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE;
+       tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_stop_playback(struct tegra_i2s *i2s)
+{
+       i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE;
+       tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_start_capture(struct tegra_i2s *i2s)
+{
+       i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE;
+       tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_stop_capture(struct tegra_i2s *i2s)
+{
+       i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE;
+       tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *dai)
+{
+       struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (!i2s->clk_refs)
+                       clk_enable(i2s->clk_i2s);
+               i2s->clk_refs++;
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra_i2s_start_playback(i2s);
+               else
+                       tegra_i2s_start_capture(i2s);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra_i2s_stop_playback(i2s);
+               else
+                       tegra_i2s_stop_capture(i2s);
+               i2s->clk_refs--;
+               if (!i2s->clk_refs)
+                       clk_disable(i2s->clk_i2s);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int tegra_i2s_probe(struct snd_soc_dai *dai)
+{
+       struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai);
+
+       dai->capture_dma_data = &i2s->capture_dma_data;
+       dai->playback_dma_data = &i2s->playback_dma_data;
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops tegra_i2s_dai_ops = {
+       .set_fmt        = tegra_i2s_set_fmt,
+       .hw_params      = tegra_i2s_hw_params,
+       .trigger        = tegra_i2s_trigger,
+};
+
+struct snd_soc_dai_driver tegra_i2s_dai[] = {
+       {
+               .name = DRV_NAME ".0",
+               .probe = tegra_i2s_probe,
+               .playback = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .capture = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops = &tegra_i2s_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = DRV_NAME ".1",
+               .probe = tegra_i2s_probe,
+               .playback = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .capture = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops = &tegra_i2s_dai_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
+{
+       struct tegra_i2s * i2s;
+       char clk_name[12]; /* tegra-i2s.0 */
+       struct resource *mem, *memregion, *dmareq;
+       int ret;
+
+       if ((pdev->id < 0) ||
+               (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) {
+               dev_err(&pdev->dev, "ID %d out of range\n", pdev->id);
+               return -EINVAL;
+       }
+
+       /*
+        * FIXME: Until a codec driver exists for the tegra DAS, hard-code a
+        * 1:1 mapping between audio controllers and audio ports.
+        */
+       ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id,
+                                       TEGRA_DAS_DAP_SEL_DAC1 + pdev->id);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set up DAP connection\n");
+               return ret;
+       }
+       ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id,
+                                       TEGRA_DAS_DAC_SEL_DAP1 + pdev->id);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set up DAC connection\n");
+               return ret;
+       }
+
+       i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL);
+       if (!i2s) {
+               dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
+               ret = -ENOMEM;
+               goto exit;
+       }
+       dev_set_drvdata(&pdev->dev, i2s);
+
+       snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id);
+       i2s->clk_i2s = clk_get_sys(clk_name, NULL);
+       if (IS_ERR(i2s->clk_i2s)) {
+               dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+               ret = PTR_ERR(i2s->clk_i2s);
+               goto err_free;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "No memory resource\n");
+               ret = -ENODEV;
+               goto err_clk_put;
+       }
+
+       dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!dmareq) {
+               dev_err(&pdev->dev, "No DMA resource\n");
+               ret = -ENODEV;
+               goto err_clk_put;
+       }
+
+       memregion = request_mem_region(mem->start, resource_size(mem),
+                                       DRV_NAME);
+       if (!memregion) {
+               dev_err(&pdev->dev, "Memory region already claimed\n");
+               ret = -EBUSY;
+               goto err_clk_put;
+       }
+
+       i2s->regs = ioremap(mem->start, resource_size(mem));
+       if (!i2s->regs) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_release;
+       }
+
+       i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
+       i2s->capture_dma_data.wrap = 4;
+       i2s->capture_dma_data.width = 32;
+       i2s->capture_dma_data.req_sel = dmareq->start;
+
+       i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
+       i2s->playback_dma_data.wrap = 4;
+       i2s->playback_dma_data.width = 32;
+       i2s->playback_dma_data.req_sel = dmareq->start;
+
+       i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
+
+       ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+               ret = -ENOMEM;
+               goto err_unmap;
+       }
+
+       tegra_i2s_debug_add(i2s, pdev->id);
+
+       return 0;
+
+err_unmap:
+       iounmap(i2s->regs);
+err_release:
+       release_mem_region(mem->start, resource_size(mem));
+err_clk_put:
+       clk_put(i2s->clk_i2s);
+err_free:
+       kfree(i2s);
+exit:
+       return ret;
+}
+
+static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
+{
+       struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
+       struct resource *res;
+
+       snd_soc_unregister_dai(&pdev->dev);
+
+       tegra_i2s_debug_remove(i2s);
+
+       iounmap(i2s->regs);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       clk_put(i2s->clk_i2s);
+
+       kfree(i2s);
+
+       return 0;
+}
+
+static struct platform_driver tegra_i2s_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = tegra_i2s_platform_probe,
+       .remove = __devexit_p(tegra_i2s_platform_remove),
+};
+
+static int __init snd_tegra_i2s_init(void)
+{
+       return platform_driver_register(&tegra_i2s_driver);
+}
+module_init(snd_tegra_i2s_init);
+
+static void __exit snd_tegra_i2s_exit(void)
+{
+       platform_driver_unregister(&tegra_i2s_driver);
+}
+module_exit(snd_tegra_i2s_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
new file mode 100644 (file)
index 0000000..2b38a09
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * tegra_i2s.h - Definitions for Tegra I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_I2S_H__
+#define __TEGRA_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
+
+#define TEGRA_I2S_CTRL                                 0x00
+#define TEGRA_I2S_STATUS                               0x04
+#define TEGRA_I2S_TIMING                               0x08
+#define TEGRA_I2S_FIFO_SCR                             0x0c
+#define TEGRA_I2S_PCM_CTRL                             0x10
+#define TEGRA_I2S_NW_CTRL                              0x14
+#define TEGRA_I2S_TDM_CTRL                             0x20
+#define TEGRA_I2S_TDM_TX_RX_CTRL                       0x24
+#define TEGRA_I2S_FIFO1                                        0x40
+#define TEGRA_I2S_FIFO2                                        0x80
+
+/* Fields in TEGRA_I2S_CTRL */
+
+#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE                 (1 << 30)
+#define TEGRA_I2S_CTRL_FIFO1_ENABLE                    (1 << 29)
+#define TEGRA_I2S_CTRL_FIFO2_ENABLE                    (1 << 28)
+#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE                 (1 << 27)
+#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE                        (1 << 26)
+#define TEGRA_I2S_CTRL_MASTER_ENABLE                   (1 << 25)
+
+#define TEGRA_I2S_LRCK_LEFT_LOW                                0
+#define TEGRA_I2S_LRCK_RIGHT_LOW                       1
+
+#define TEGRA_I2S_CTRL_LRCK_SHIFT                      24
+#define TEGRA_I2S_CTRL_LRCK_MASK                       (1                        << TEGRA_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA_I2S_CTRL_LRCK_L_LOW                      (TEGRA_I2S_LRCK_LEFT_LOW  << TEGRA_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA_I2S_CTRL_LRCK_R_LOW                      (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA_I2S_BIT_FORMAT_I2S                       0
+#define TEGRA_I2S_BIT_FORMAT_RJM                       1
+#define TEGRA_I2S_BIT_FORMAT_LJM                       2
+#define TEGRA_I2S_BIT_FORMAT_DSP                       3
+
+#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT                        10
+#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK                 (3                        << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S                  (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM                  (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM                  (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP                  (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+
+#define TEGRA_I2S_BIT_SIZE_16                          0
+#define TEGRA_I2S_BIT_SIZE_20                          1
+#define TEGRA_I2S_BIT_SIZE_24                          2
+#define TEGRA_I2S_BIT_SIZE_32                          3
+
+#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT                  8
+#define TEGRA_I2S_CTRL_BIT_SIZE_MASK                   (3                     << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_16                     (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_20                     (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_24                     (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_32                     (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+
+#define TEGRA_I2S_FIFO_16_LSB                          0
+#define TEGRA_I2S_FIFO_20_LSB                          1
+#define TEGRA_I2S_FIFO_24_LSB                          2
+#define TEGRA_I2S_FIFO_32                              3
+#define TEGRA_I2S_FIFO_PACKED                          7
+
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT               4
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK                        (7                     << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB              (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB              (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB              (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_32                  (TEGRA_I2S_FIFO_32     << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED              (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+
+#define TEGRA_I2S_CTRL_IE_FIFO1_ERR                    (1 << 3)
+#define TEGRA_I2S_CTRL_IE_FIFO2_ERR                    (1 << 2)
+#define TEGRA_I2S_CTRL_QE_FIFO1                                (1 << 1)
+#define TEGRA_I2S_CTRL_QE_FIFO2                                (1 << 0)
+
+/* Fields in TEGRA_I2S_STATUS */
+
+#define TEGRA_I2S_STATUS_FIFO1_RDY                     (1 << 31)
+#define TEGRA_I2S_STATUS_FIFO2_RDY                     (1 << 30)
+#define TEGRA_I2S_STATUS_FIFO1_BSY                     (1 << 29)
+#define TEGRA_I2S_STATUS_FIFO2_BSY                     (1 << 28)
+#define TEGRA_I2S_STATUS_FIFO1_ERR                     (1 << 3)
+#define TEGRA_I2S_STATUS_FIFO2_ERR                     (1 << 2)
+#define TEGRA_I2S_STATUS_QS_FIFO1                      (1 << 1)
+#define TEGRA_I2S_STATUS_QS_FIFO2                      (1 << 0)
+
+/* Fields in TEGRA_I2S_TIMING */
+
+#define TEGRA_I2S_TIMING_NON_SYM_ENABLE                        (1 << 12)
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT       0
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US     0x7fff
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK                (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA_I2S_FIFO_SCR */
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT        24
+#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT        16
+#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK  0x3f
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR                   (1 << 12)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR                   (1 << 8)
+
+#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT                        0
+#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS              1
+#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS             2
+#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS            3
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT         4
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK          (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT      (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS    (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS   (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS  (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT         0
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK          (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT      (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS    (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS   (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS  (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+
+struct tegra_i2s {
+       struct clk *clk_i2s;
+       int clk_refs;
+       struct tegra_pcm_dma_params capture_dma_data;
+       struct tegra_pcm_dma_params playback_dma_data;
+       void __iomem *regs;
+       struct dentry *debug;
+       u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
new file mode 100644 (file)
index 0000000..3c271f9
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * tegra_pcm.c - Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ * Vijay Mali <vmali@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_pcm.h"
+
+#define DRV_NAME "tegra-pcm-audio"
+
+static const struct snd_pcm_hardware tegra_pcm_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME |
+                                 SNDRV_PCM_INFO_INTERLEAVED,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .period_bytes_min       = 1024,
+       .period_bytes_max       = PAGE_SIZE,
+       .periods_min            = 2,
+       .periods_max            = 8,
+       .buffer_bytes_max       = PAGE_SIZE * 8,
+       .fifo_size              = 4,
+};
+
+static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
+{
+       struct snd_pcm_substream *substream = prtd->substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       struct tegra_dma_req *dma_req;
+       unsigned long addr;
+
+       dma_req = &prtd->dma_req[prtd->dma_req_idx];
+       prtd->dma_req_idx = 1 - prtd->dma_req_idx;
+
+       addr = buf->addr + prtd->dma_pos;
+       prtd->dma_pos += dma_req->size;
+       if (prtd->dma_pos >= prtd->dma_pos_end)
+               prtd->dma_pos = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_req->source_addr = addr;
+       else
+               dma_req->dest_addr = addr;
+
+       tegra_dma_enqueue_req(prtd->dma_chan, dma_req);
+}
+
+static void dma_complete_callback(struct tegra_dma_req *req)
+{
+       struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev;
+       struct snd_pcm_substream *substream = prtd->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       spin_lock(&prtd->lock);
+
+       if (!prtd->running) {
+               spin_unlock(&prtd->lock);
+               return;
+       }
+
+       if (++prtd->period_index >= runtime->periods)
+               prtd->period_index = 0;
+
+       tegra_pcm_queue_dma(prtd);
+
+       spin_unlock(&prtd->lock);
+
+       snd_pcm_period_elapsed(substream);
+}
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+                                       struct tegra_pcm_dma_params * dmap)
+{
+       req->complete = dma_complete_callback;
+       req->to_memory = false;
+       req->dest_addr = dmap->addr;
+       req->dest_wrap = dmap->wrap;
+       req->source_bus_width = 32;
+       req->source_wrap = 0;
+       req->dest_bus_width = dmap->width;
+       req->req_sel = dmap->req_sel;
+}
+
+static void setup_dma_rx_request(struct tegra_dma_req *req,
+                                       struct tegra_pcm_dma_params * dmap)
+{
+       req->complete = dma_complete_callback;
+       req->to_memory = true;
+       req->source_addr = dmap->addr;
+       req->dest_wrap = 0;
+       req->source_bus_width = dmap->width;
+       req->source_wrap = dmap->wrap;
+       req->dest_bus_width = 32;
+       req->req_sel = dmap->req_sel;
+}
+
+static int tegra_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct tegra_runtime_data *prtd;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct tegra_pcm_dma_params * dmap;
+       int ret = 0;
+
+       prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL);
+       if (prtd == NULL)
+               return -ENOMEM;
+
+       runtime->private_data = prtd;
+       prtd->substream = substream;
+
+       spin_lock_init(&prtd->lock);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+               setup_dma_tx_request(&prtd->dma_req[0], dmap);
+               setup_dma_tx_request(&prtd->dma_req[1], dmap);
+       } else {
+               dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+               setup_dma_rx_request(&prtd->dma_req[0], dmap);
+               setup_dma_rx_request(&prtd->dma_req[1], dmap);
+       }
+
+       prtd->dma_req[0].dev = prtd;
+       prtd->dma_req[1].dev = prtd;
+
+       prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+       if (prtd->dma_chan == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       /* Set HW params now that initialization is complete */
+       snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
+
+       /* Ensure that buffer size is a multiple of period size */
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                               SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+
+err:
+       if (prtd->dma_chan) {
+               tegra_dma_free_channel(prtd->dma_chan);
+       }
+
+       kfree(prtd);
+
+       return ret;
+}
+
+static int tegra_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct tegra_runtime_data *prtd = runtime->private_data;
+
+       tegra_dma_free_channel(prtd->dma_chan);
+
+       kfree(prtd);
+
+       return 0;
+}
+
+static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct tegra_runtime_data *prtd = runtime->private_data;
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       prtd->dma_req[0].size = params_period_bytes(params);
+       prtd->dma_req[1].size = prtd->dma_req[0].size;
+
+       return 0;
+}
+
+static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       snd_pcm_set_runtime_buffer(substream, NULL);
+
+       return 0;
+}
+
+static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct tegra_runtime_data *prtd = runtime->private_data;
+       unsigned long flags;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               prtd->dma_pos = 0;
+               prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size);
+               prtd->period_index = 0;
+               prtd->dma_req_idx = 0;
+               /* Fall-through */
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               spin_lock_irqsave(&prtd->lock, flags);
+               prtd->running = 1;
+               spin_unlock_irqrestore(&prtd->lock, flags);
+               tegra_pcm_queue_dma(prtd);
+               tegra_pcm_queue_dma(prtd);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               spin_lock_irqsave(&prtd->lock, flags);
+               prtd->running = 0;
+               spin_unlock_irqrestore(&prtd->lock, flags);
+               tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]);
+               tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct tegra_runtime_data *prtd = runtime->private_data;
+
+       return prtd->period_index * runtime->period_size;
+}
+
+
+static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
+                               struct vm_area_struct *vma)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+                                       runtime->dma_area,
+                                       runtime->dma_addr,
+                                       runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops tegra_pcm_ops = {
+       .open           = tegra_pcm_open,
+       .close          = tegra_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = tegra_pcm_hw_params,
+       .hw_free        = tegra_pcm_hw_free,
+       .trigger        = tegra_pcm_trigger,
+       .pointer        = tegra_pcm_pointer,
+       .mmap           = tegra_pcm_mmap,
+};
+
+static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = tegra_pcm_hardware.buffer_bytes_max;
+
+       buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+                                               &buf->addr, GFP_KERNEL);
+       if (!buf->area)
+               return -ENOMEM;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->bytes = size;
+
+       return 0;
+}
+
+static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+       if (!buf->area)
+               return;
+
+       dma_free_writecombine(pcm->card->dev, buf->bytes,
+                               buf->area, buf->addr);
+       buf->area = NULL;
+}
+
+static u64 tegra_dma_mask = DMA_BIT_MASK(32);
+
+static int tegra_pcm_new(struct snd_card *card,
+                               struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+       int ret = 0;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &tegra_dma_mask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = 0xffffffff;
+
+       if (dai->driver->playback.channels_min) {
+               ret = tegra_pcm_preallocate_dma_buffer(pcm,
+                                               SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       goto err;
+       }
+
+       if (dai->driver->capture.channels_min) {
+               ret = tegra_pcm_preallocate_dma_buffer(pcm,
+                                               SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       goto err_free_play;
+       }
+
+       return 0;
+
+err_free_play:
+       tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+err:
+       return ret;
+}
+
+static void tegra_pcm_free(struct snd_pcm *pcm)
+{
+       tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
+       tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+struct snd_soc_platform_driver tegra_pcm_platform = {
+       .ops            = &tegra_pcm_ops,
+       .pcm_new        = tegra_pcm_new,
+       .pcm_free       = tegra_pcm_free,
+};
+
+static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
+}
+
+static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+       return 0;
+}
+
+static struct platform_driver tegra_pcm_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = tegra_pcm_platform_probe,
+       .remove = __devexit_p(tegra_pcm_platform_remove),
+};
+
+static int __init snd_tegra_pcm_init(void)
+{
+       return platform_driver_register(&tegra_pcm_driver);
+}
+module_init(snd_tegra_pcm_init);
+
+static void __exit snd_tegra_pcm_exit(void)
+{
+       platform_driver_unregister(&tegra_pcm_driver);
+}
+module_exit(snd_tegra_pcm_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PCM ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
new file mode 100644 (file)
index 0000000..dbb9033
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * tegra_pcm.h - Definitions for Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_PCM_H__
+#define __TEGRA_PCM_H__
+
+#include <mach/dma.h>
+
+struct tegra_pcm_dma_params {
+       unsigned long addr;
+       unsigned long wrap;
+       unsigned long width;
+       unsigned long req_sel;
+};
+
+struct tegra_runtime_data {
+       struct snd_pcm_substream *substream;
+       spinlock_t lock;
+       int running;
+       int dma_pos;
+       int dma_pos_end;
+       int period_index;
+       int dma_req_idx;
+       struct tegra_dma_req dma_req[2];
+       struct tegra_dma_channel *dma_chan;
+};
+
+#endif
index 5580aced87308202e00d181194a6d4aa6689b920..6ce277860fd73c9231da9af6f6dfb1048d4bd0c4 100644 (file)
@@ -384,6 +384,9 @@ int register_sound_special_device(const struct file_operations *fops, int unit,
            case 4:
                name = "audio";
                break;
+           case 5:
+               name = "dspW";
+               break;
            case 8:
                name = "sequencer2";
                if (unit >= SOUND_STEP)
diff --git a/sound/usb/6fire/Makefile b/sound/usb/6fire/Makefile
new file mode 100644 (file)
index 0000000..dfce6ec
--- /dev/null
@@ -0,0 +1,3 @@
+snd-usb-6fire-objs += chip.o comm.o midi.o control.o firmware.o pcm.o
+obj-$(CONFIG_SND_USB_6FIRE) += snd-usb-6fire.o
+
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
new file mode 100644 (file)
index 0000000..c7dca7b
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Main routines and module definitions.
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#include "chip.h"
+#include "firmware.h"
+#include "pcm.h"
+#include "control.h"
+#include "comm.h"
+#include "midi.h"
+
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>");
+MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */
+static struct sfire_chip *chips[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static struct usb_device *devices[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the 6fire sound device");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the 6fire sound device.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the 6fire sound device.");
+
+static DEFINE_MUTEX(register_mutex);
+
+static void usb6fire_chip_abort(struct sfire_chip *chip)
+{
+       if (chip) {
+               if (chip->pcm)
+                       usb6fire_pcm_abort(chip);
+               if (chip->midi)
+                       usb6fire_midi_abort(chip);
+               if (chip->comm)
+                       usb6fire_comm_abort(chip);
+               if (chip->control)
+                       usb6fire_control_abort(chip);
+               if (chip->card) {
+                       snd_card_disconnect(chip->card);
+                       snd_card_free_when_closed(chip->card);
+                       chip->card = NULL;
+               }
+       }
+}
+
+static void usb6fire_chip_destroy(struct sfire_chip *chip)
+{
+       if (chip) {
+               if (chip->pcm)
+                       usb6fire_pcm_destroy(chip);
+               if (chip->midi)
+                       usb6fire_midi_destroy(chip);
+               if (chip->comm)
+                       usb6fire_comm_destroy(chip);
+               if (chip->control)
+                       usb6fire_control_destroy(chip);
+               if (chip->card)
+                       snd_card_free(chip->card);
+       }
+}
+
+static int __devinit usb6fire_chip_probe(struct usb_interface *intf,
+               const struct usb_device_id *usb_id)
+{
+       int ret;
+       int i;
+       struct sfire_chip *chip = NULL;
+       struct usb_device *device = interface_to_usbdev(intf);
+       int regidx = -1; /* index in module parameter array */
+       struct snd_card *card = NULL;
+
+       /* look if we already serve this card and return if so */
+       mutex_lock(&register_mutex);
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               if (devices[i] == device) {
+                       if (chips[i])
+                               chips[i]->intf_count++;
+                       usb_set_intfdata(intf, chips[i]);
+                       mutex_unlock(&register_mutex);
+                       return 0;
+               } else if (regidx < 0)
+                       regidx = i;
+       }
+       if (regidx < 0) {
+               mutex_unlock(&register_mutex);
+               snd_printk(KERN_ERR PREFIX "too many cards registered.\n");
+               return -ENODEV;
+       }
+       devices[regidx] = device;
+       mutex_unlock(&register_mutex);
+
+       /* check, if firmware is present on device, upload it if not */
+       ret = usb6fire_fw_init(intf);
+       if (ret < 0)
+               return ret;
+       else if (ret == FW_NOT_READY) /* firmware update performed */
+               return 0;
+
+       /* if we are here, card can be registered in alsa. */
+       if (usb_set_interface(device, 0, 0) != 0) {
+               snd_printk(KERN_ERR PREFIX "can't set first interface.\n");
+               return -EIO;
+       }
+       ret = snd_card_create(index[regidx], id[regidx], THIS_MODULE,
+                       sizeof(struct sfire_chip), &card);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "cannot create alsa card.\n");
+               return ret;
+       }
+       strcpy(card->driver, "6FireUSB");
+       strcpy(card->shortname, "TerraTec DMX6FireUSB");
+       sprintf(card->longname, "%s at %d:%d", card->shortname,
+                       device->bus->busnum, device->devnum);
+       snd_card_set_dev(card, &intf->dev);
+
+       chip = card->private_data;
+       chips[regidx] = chip;
+       chip->dev = device;
+       chip->regidx = regidx;
+       chip->intf_count = 1;
+       chip->card = card;
+
+       ret = usb6fire_comm_init(chip);
+       if (ret < 0) {
+               usb6fire_chip_destroy(chip);
+               return ret;
+       }
+
+       ret = usb6fire_midi_init(chip);
+       if (ret < 0) {
+               usb6fire_chip_destroy(chip);
+               return ret;
+       }
+
+       ret = usb6fire_pcm_init(chip);
+       if (ret < 0) {
+               usb6fire_chip_destroy(chip);
+               return ret;
+       }
+
+       ret = usb6fire_control_init(chip);
+       if (ret < 0) {
+               usb6fire_chip_destroy(chip);
+               return ret;
+       }
+
+       ret = snd_card_register(card);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "cannot register card.");
+               usb6fire_chip_destroy(chip);
+               return ret;
+       }
+       usb_set_intfdata(intf, chip);
+       return 0;
+}
+
+static void usb6fire_chip_disconnect(struct usb_interface *intf)
+{
+       struct sfire_chip *chip;
+       struct snd_card *card;
+
+       chip = usb_get_intfdata(intf);
+       if (chip) { /* if !chip, fw upload has been performed */
+               card = chip->card;
+               chip->intf_count--;
+               if (!chip->intf_count) {
+                       mutex_lock(&register_mutex);
+                       devices[chip->regidx] = NULL;
+                       chips[chip->regidx] = NULL;
+                       mutex_unlock(&register_mutex);
+
+                       chip->shutdown = true;
+                       usb6fire_chip_abort(chip);
+                       usb6fire_chip_destroy(chip);
+               }
+       }
+}
+
+static struct usb_device_id device_table[] = {
+       {
+               .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor = 0x0ccd,
+               .idProduct = 0x0080
+       },
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static struct usb_driver driver = {
+       .name = "snd-usb-6fire",
+       .probe = usb6fire_chip_probe,
+       .disconnect = usb6fire_chip_disconnect,
+       .id_table = device_table,
+};
+
+static int __init usb6fire_chip_init(void)
+{
+       return usb_register(&driver);
+}
+
+static void __exit usb6fire_chip_cleanup(void)
+{
+       usb_deregister(&driver);
+}
+
+module_init(usb6fire_chip_init);
+module_exit(usb6fire_chip_cleanup);
diff --git a/sound/usb/6fire/chip.h b/sound/usb/6fire/chip.h
new file mode 100644 (file)
index 0000000..d11e5cb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+#ifndef USB6FIRE_CHIP_H
+#define USB6FIRE_CHIP_H
+
+#include "common.h"
+
+struct sfire_chip {
+       struct usb_device *dev;
+       struct snd_card *card;
+       int intf_count; /* number of registered interfaces */
+       int regidx; /* index in module parameter arrays */
+       bool shutdown;
+
+       struct midi_runtime *midi;
+       struct pcm_runtime *pcm;
+       struct control_runtime *control;
+       struct comm_runtime *comm;
+};
+#endif /* USB6FIRE_CHIP_H */
+
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
new file mode 100644 (file)
index 0000000..c994daa
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Device communications
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#include "comm.h"
+#include "chip.h"
+#include "midi.h"
+
+enum {
+       COMM_EP = 1,
+       COMM_FPGA_EP = 2
+};
+
+static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
+               u8 *buffer, void *context, void(*handler)(struct urb *urb))
+{
+       usb_init_urb(urb);
+       urb->transfer_buffer = buffer;
+       urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
+       urb->complete = handler;
+       urb->context = context;
+       urb->interval = 1;
+       urb->dev = rt->chip->dev;
+}
+
+static void usb6fire_comm_receiver_handler(struct urb *urb)
+{
+       struct comm_runtime *rt = urb->context;
+       struct midi_runtime *midi_rt = rt->chip->midi;
+
+       if (!urb->status) {
+               if (rt->receiver_buffer[0] == 0x10) /* midi in event */
+                       if (midi_rt)
+                               midi_rt->in_received(midi_rt,
+                                               rt->receiver_buffer + 2,
+                                               rt->receiver_buffer[1]);
+       }
+
+       if (!rt->chip->shutdown) {
+               urb->status = 0;
+               urb->actual_length = 0;
+               if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
+                       snd_printk(KERN_WARNING PREFIX
+                                       "comm data receiver aborted.\n");
+       }
+}
+
+static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request,
+               u8 reg, u8 vl, u8 vh)
+{
+       buffer[0] = 0x01;
+       buffer[2] = request;
+       buffer[3] = id;
+       switch (request) {
+       case 0x02:
+               buffer[1] = 0x05; /* length (starting at buffer[2]) */
+               buffer[4] = reg;
+               buffer[5] = vl;
+               buffer[6] = vh;
+               break;
+
+       case 0x12:
+               buffer[1] = 0x0b; /* length (starting at buffer[2]) */
+               buffer[4] = 0x00;
+               buffer[5] = 0x18;
+               buffer[6] = 0x05;
+               buffer[7] = 0x00;
+               buffer[8] = 0x01;
+               buffer[9] = 0x00;
+               buffer[10] = 0x9e;
+               buffer[11] = reg;
+               buffer[12] = vl;
+               break;
+
+       case 0x20:
+       case 0x21:
+       case 0x22:
+               buffer[1] = 0x04;
+               buffer[4] = reg;
+               buffer[5] = vl;
+               break;
+       }
+}
+
+static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
+{
+       int ret;
+       int actual_len;
+
+       ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
+                       buffer, buffer[1] + 2, &actual_len, HZ);
+       if (ret < 0)
+               return ret;
+       else if (actual_len != buffer[1] + 2)
+               return -EIO;
+       return 0;
+}
+
+static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request,
+               u8 reg, u8 value)
+{
+       u8 buffer[13]; /* 13: maximum length of message */
+
+       usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00);
+       return usb6fire_comm_send_buffer(buffer, rt->chip->dev);
+}
+
+static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
+               u8 reg, u8 vl, u8 vh)
+{
+       u8 buffer[13]; /* 13: maximum length of message */
+
+       usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh);
+       return usb6fire_comm_send_buffer(buffer, rt->chip->dev);
+}
+
+int __devinit usb6fire_comm_init(struct sfire_chip *chip)
+{
+       struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime),
+                       GFP_KERNEL);
+       struct urb *urb = &rt->receiver;
+       int ret;
+
+       if (!rt)
+               return -ENOMEM;
+
+       rt->serial = 1;
+       rt->chip = chip;
+       usb_init_urb(urb);
+       rt->init_urb = usb6fire_comm_init_urb;
+       rt->write8 = usb6fire_comm_write8;
+       rt->write16 = usb6fire_comm_write16;
+
+       /* submit an urb that receives communication data from device */
+       urb->transfer_buffer = rt->receiver_buffer;
+       urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE;
+       urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP);
+       urb->dev = chip->dev;
+       urb->complete = usb6fire_comm_receiver_handler;
+       urb->context = rt;
+       urb->interval = 1;
+       ret = usb_submit_urb(urb, GFP_KERNEL);
+       if (ret < 0) {
+               kfree(rt);
+               snd_printk(KERN_ERR PREFIX "cannot create comm data receiver.");
+               return ret;
+       }
+       chip->comm = rt;
+       return 0;
+}
+
+void usb6fire_comm_abort(struct sfire_chip *chip)
+{
+       struct comm_runtime *rt = chip->comm;
+
+       if (rt)
+               usb_poison_urb(&rt->receiver);
+}
+
+void usb6fire_comm_destroy(struct sfire_chip *chip)
+{
+       kfree(chip->comm);
+       chip->comm = NULL;
+}
diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h
new file mode 100644 (file)
index 0000000..edc5dc8
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+#ifndef USB6FIRE_COMM_H
+#define USB6FIRE_COMM_H
+
+#include "common.h"
+
+enum /* settings for comm */
+{
+       COMM_RECEIVER_BUFSIZE = 64,
+};
+
+struct comm_runtime {
+       struct sfire_chip *chip;
+
+       struct urb receiver;
+       u8 receiver_buffer[COMM_RECEIVER_BUFSIZE];
+
+       u8 serial; /* urb serial */
+
+       void (*init_urb)(struct comm_runtime *rt, struct urb *urb, u8 *buffer,
+                       void *context, void(*handler)(struct urb *urb));
+       /* writes control data to the device */
+       int (*write8)(struct comm_runtime *rt, u8 request, u8 reg, u8 value);
+       int (*write16)(struct comm_runtime *rt, u8 request, u8 reg,
+                       u8 vh, u8 vl);
+};
+
+int __devinit usb6fire_comm_init(struct sfire_chip *chip);
+void usb6fire_comm_abort(struct sfire_chip *chip);
+void usb6fire_comm_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_COMM_H */
+
diff --git a/sound/usb/6fire/common.h b/sound/usb/6fire/common.h
new file mode 100644 (file)
index 0000000..7dbeb4a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#ifndef USB6FIRE_COMMON_H
+#define USB6FIRE_COMMON_H
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+
+#define PREFIX "6fire: "
+
+struct sfire_chip;
+struct midi_runtime;
+struct pcm_runtime;
+struct control_runtime;
+struct comm_runtime;
+#endif /* USB6FIRE_COMMON_H */
+
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c
new file mode 100644 (file)
index 0000000..2484635
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Mixer control
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <sound/control.h>
+
+#include "control.h"
+#include "comm.h"
+#include "chip.h"
+
+static char *opt_coax_texts[2] = { "Optical", "Coax" };
+static char *line_phono_texts[2] = { "Line", "Phono" };
+
+/*
+ * calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$
+ * this is done because the linear values cause rapid degredation
+ * of volume in the uppermost region.
+ */
+static const u8 log_volume_table[128] = {
+       0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34,
+       0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43,
+       0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+       0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56,
+       0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
+       0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62,
+       0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+       0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c,
+       0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+       0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75,
+       0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79,
+       0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
+       0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f };
+
+/*
+ * data that needs to be sent to device. sets up card internal stuff.
+ * values dumped from windows driver and filtered by trial'n'error.
+ */
+static const struct {
+       u8 type;
+       u8 reg;
+       u8 value;
+}
+init_data[] = {
+       { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 },
+       { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 },
+       { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
+       { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
+       { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
+       { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
+       { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
+       { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
+       { 0 } /* TERMINATING ENTRY */
+};
+
+static void usb6fire_control_master_vol_update(struct control_runtime *rt)
+{
+       struct comm_runtime *comm_rt = rt->chip->comm;
+       if (comm_rt) {
+               /* set volume */
+               comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f -
+                               log_volume_table[rt->master_vol]);
+                /* unmute */
+               comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00);
+       }
+}
+
+static void usb6fire_control_line_phono_update(struct control_runtime *rt)
+{
+       struct comm_runtime *comm_rt = rt->chip->comm;
+       if (comm_rt) {
+               comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);
+               comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);
+       }
+}
+
+static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
+{
+       struct comm_runtime *comm_rt = rt->chip->comm;
+       if (comm_rt) {
+               comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);
+               comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);
+       }
+}
+
+static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 127;
+       return 0;
+}
+
+static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+       if (rt->master_vol != ucontrol->value.integer.value[0]) {
+               rt->master_vol = ucontrol->value.integer.value[0];
+               usb6fire_control_master_vol_update(rt);
+               changed = 1;
+       }
+       return changed;
+}
+
+static int usb6fire_control_master_vol_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->master_vol;
+       return 0;
+}
+
+static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+                       line_phono_texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+       if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {
+               rt->line_phono_switch = ucontrol->value.integer.value[0];
+               usb6fire_control_line_phono_update(rt);
+               changed = 1;
+       }
+       return changed;
+}
+
+static int usb6fire_control_line_phono_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->line_phono_switch;
+       return 0;
+}
+
+static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+                       opt_coax_texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+
+       if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {
+               rt->opt_coax_switch = ucontrol->value.enumerated.item[0];
+               usb6fire_control_opt_coax_update(rt);
+               changed = 1;
+       }
+       return changed;
+}
+
+static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;
+       return 0;
+}
+
+static struct __devinitdata snd_kcontrol_new elements[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Volume",
+               .index = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = usb6fire_control_master_vol_info,
+               .get = usb6fire_control_master_vol_get,
+               .put = usb6fire_control_master_vol_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Line/Phono Capture Route",
+               .index = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = usb6fire_control_line_phono_info,
+               .get = usb6fire_control_line_phono_get,
+               .put = usb6fire_control_line_phono_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Opt/Coax Capture Route",
+               .index = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = usb6fire_control_opt_coax_info,
+               .get = usb6fire_control_opt_coax_get,
+               .put = usb6fire_control_opt_coax_put
+       },
+       {}
+};
+
+int __devinit usb6fire_control_init(struct sfire_chip *chip)
+{
+       int i;
+       int ret;
+       struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
+                       GFP_KERNEL);
+       struct comm_runtime *comm_rt = chip->comm;
+
+       if (!rt)
+               return -ENOMEM;
+
+       rt->chip = chip;
+
+       i = 0;
+       while (init_data[i].type) {
+               comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,
+                               init_data[i].value);
+               i++;
+       }
+
+       usb6fire_control_opt_coax_update(rt);
+       usb6fire_control_line_phono_update(rt);
+       usb6fire_control_master_vol_update(rt);
+
+       i = 0;
+       while (elements[i].name) {
+               ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
+               if (ret < 0) {
+                       kfree(rt);
+                       snd_printk(KERN_ERR PREFIX "cannot add control.\n");
+                       return ret;
+               }
+               i++;
+       }
+
+       chip->control = rt;
+       return 0;
+}
+
+void usb6fire_control_abort(struct sfire_chip *chip)
+{}
+
+void usb6fire_control_destroy(struct sfire_chip *chip)
+{
+       kfree(chip->control);
+       chip->control = NULL;
+}
diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h
new file mode 100644 (file)
index 0000000..b534c77
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#ifndef USB6FIRE_CONTROL_H
+#define USB6FIRE_CONTROL_H
+
+#include "common.h"
+
+enum {
+       CONTROL_MAX_ELEMENTS = 32
+};
+
+struct control_runtime {
+       struct sfire_chip *chip;
+
+       struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
+       bool opt_coax_switch;
+       bool line_phono_switch;
+       u8 master_vol;
+};
+
+int __devinit usb6fire_control_init(struct sfire_chip *chip);
+void usb6fire_control_abort(struct sfire_chip *chip);
+void usb6fire_control_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_CONTROL_H */
+
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
new file mode 100644 (file)
index 0000000..9081a54
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * 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
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#include <linux/firmware.h>
+
+#include "firmware.h"
+#include "chip.h"
+
+MODULE_FIRMWARE("6fire/dmx6firel2.ihx");
+MODULE_FIRMWARE("6fire/dmx6fireap.ihx");
+MODULE_FIRMWARE("6fire/dmx6firecf.bin");
+
+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
+ * fpp: frames per isopacket
+ *
+ * CAUTION: keep sizeof <= buffer[] in usb6fire_fw_init
+ */
+static const u8 ep_w_max_packet_size[] = {
+       0xe4, 0x00, 0xe4, 0x00, /* alt 1: 228 EP2 and EP6 (7 fpp) */
+       0xa4, 0x01, 0xa4, 0x01, /* alt 2: 420 EP2 and EP6 (13 fpp)*/
+       0x94, 0x01, 0x5c, 0x02  /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
+};
+
+struct ihex_record {
+       u16 address;
+       u8 len;
+       u8 data[256];
+       char error; /* true if an error occured parsing this record */
+
+       u8 max_len; /* maximum record length in whole ihex */
+
+       /* private */
+       const char *txt_data;
+       unsigned int txt_length;
+       unsigned int txt_offset; /* current position in txt_data */
+};
+
+static u8 usb6fire_fw_ihex_nibble(const u8 n)
+{
+       if (n >= '0' && n <= '9')
+               return n - '0';
+       else if (n >= 'A' && n <= 'F')
+               return n - ('A' - 10);
+       else if (n >= 'a' && n <= 'f')
+               return n - ('a' - 10);
+       return 0;
+}
+
+static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc)
+{
+       u8 val = (usb6fire_fw_ihex_nibble(data[0]) << 4) |
+                       usb6fire_fw_ihex_nibble(data[1]);
+       *crc += val;
+       return val;
+}
+
+/*
+ * returns true if record is available, false otherwise.
+ * iff an error occured, false will be returned and record->error will be true.
+ */
+static bool usb6fire_fw_ihex_next_record(struct ihex_record *record)
+{
+       u8 crc = 0;
+       u8 type;
+       int i;
+
+       record->error = false;
+
+       /* find begin of record (marked by a colon) */
+       while (record->txt_offset < record->txt_length
+                       && record->txt_data[record->txt_offset] != ':')
+               record->txt_offset++;
+       if (record->txt_offset == record->txt_length)
+               return false;
+
+       /* number of characters needed for len, addr and type entries */
+       record->txt_offset++;
+       if (record->txt_offset + 8 > record->txt_length) {
+               record->error = true;
+               return false;
+       }
+
+       record->len = usb6fire_fw_ihex_hex(record->txt_data +
+                       record->txt_offset, &crc);
+       record->txt_offset += 2;
+       record->address = usb6fire_fw_ihex_hex(record->txt_data +
+                       record->txt_offset, &crc) << 8;
+       record->txt_offset += 2;
+       record->address |= usb6fire_fw_ihex_hex(record->txt_data +
+                       record->txt_offset, &crc);
+       record->txt_offset += 2;
+       type = usb6fire_fw_ihex_hex(record->txt_data +
+                       record->txt_offset, &crc);
+       record->txt_offset += 2;
+
+       /* number of characters needed for data and crc entries */
+       if (record->txt_offset + 2 * (record->len + 1) > record->txt_length) {
+               record->error = true;
+               return false;
+       }
+       for (i = 0; i < record->len; i++) {
+               record->data[i] = usb6fire_fw_ihex_hex(record->txt_data
+                               + record->txt_offset, &crc);
+               record->txt_offset += 2;
+       }
+       usb6fire_fw_ihex_hex(record->txt_data + record->txt_offset, &crc);
+       if (crc) {
+               record->error = true;
+               return false;
+       }
+
+       if (type == 1 || !record->len) /* eof */
+               return false;
+       else if (type == 0)
+               return true;
+       else {
+               record->error = true;
+               return false;
+       }
+}
+
+static int usb6fire_fw_ihex_init(const struct firmware *fw,
+               struct ihex_record *record)
+{
+       record->txt_data = fw->data;
+       record->txt_length = fw->size;
+       record->txt_offset = 0;
+       record->max_len = 0;
+       /* read all records, if loop ends, record->error indicates,
+        * whether ihex is valid. */
+       while (usb6fire_fw_ihex_next_record(record))
+               record->max_len = max(record->len, record->max_len);
+       if (record->error)
+               return -EINVAL;
+       record->txt_offset = 0;
+       return 0;
+}
+
+static int usb6fire_fw_ezusb_write(struct usb_device *device,
+               int type, int value, char *data, int len)
+{
+       int ret;
+
+       ret = usb_control_msg(device, usb_sndctrlpipe(device, 0), type,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, 0, data, len, HZ);
+       if (ret < 0)
+               return ret;
+       else if (ret != len)
+               return -EIO;
+       return 0;
+}
+
+static int usb6fire_fw_ezusb_read(struct usb_device *device,
+               int type, int value, char *data, int len)
+{
+       int ret = usb_control_msg(device, usb_rcvctrlpipe(device, 0), type,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value,
+                       0, data, len, HZ);
+       if (ret < 0)
+               return ret;
+       else if (ret != len)
+               return -EIO;
+       return 0;
+}
+
+static int usb6fire_fw_fpga_write(struct usb_device *device,
+               char *data, int len)
+{
+       int actual_len;
+       int ret;
+
+       ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len,
+                       &actual_len, HZ);
+       if (ret < 0)
+               return ret;
+       else if (actual_len != len)
+               return -EIO;
+       return 0;
+}
+
+static int usb6fire_fw_ezusb_upload(
+               struct usb_interface *intf, const char *fwname,
+               unsigned int postaddr, u8 *postdata, unsigned int postlen)
+{
+       int ret;
+       u8 data;
+       struct usb_device *device = interface_to_usbdev(intf);
+       const struct firmware *fw = 0;
+       struct ihex_record *rec = kmalloc(sizeof(struct ihex_record),
+                       GFP_KERNEL);
+
+       if (!rec)
+               return -ENOMEM;
+
+       ret = request_firmware(&fw, fwname, &device->dev);
+       if (ret < 0) {
+               kfree(rec);
+               snd_printk(KERN_ERR PREFIX "error requesting ezusb "
+                               "firmware %s.\n", fwname);
+               return ret;
+       }
+       ret = usb6fire_fw_ihex_init(fw, rec);
+       if (ret < 0) {
+               kfree(rec);
+               snd_printk(KERN_ERR PREFIX "error validating ezusb "
+                               "firmware %s.\n", fwname);
+               return ret;
+       }
+       /* upload firmware image */
+       data = 0x01; /* stop ezusb cpu */
+       ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);
+       if (ret < 0) {
+               kfree(rec);
+               release_firmware(fw);
+               snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
+                               "firmware %s: begin message.\n", fwname);
+               return ret;
+       }
+
+       while (usb6fire_fw_ihex_next_record(rec)) { /* write firmware */
+               ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address,
+                               rec->data, rec->len);
+               if (ret < 0) {
+                       kfree(rec);
+                       release_firmware(fw);
+                       snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
+                                       "firmware %s: data urb.\n", fwname);
+                       return ret;
+               }
+       }
+
+       release_firmware(fw);
+       kfree(rec);
+       if (postdata) { /* write data after firmware has been uploaded */
+               ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr,
+                               postdata, postlen);
+               if (ret < 0) {
+                       snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
+                                       "firmware %s: post urb.\n", fwname);
+                       return ret;
+               }
+       }
+
+       data = 0x00; /* resume ezusb cpu */
+       ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);
+       if (ret < 0) {
+               release_firmware(fw);
+               snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
+                               "firmware %s: end message.\n", fwname);
+               return ret;
+       }
+       return 0;
+}
+
+static int usb6fire_fw_fpga_upload(
+               struct usb_interface *intf, const char *fwname)
+{
+       int ret;
+       int i;
+       struct usb_device *device = interface_to_usbdev(intf);
+       u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL);
+       const char *c;
+       const char *end;
+       const struct firmware *fw;
+
+       if (!buffer)
+               return -ENOMEM;
+
+       ret = request_firmware(&fw, fwname, &device->dev);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "unable to get fpga firmware %s.\n",
+                               fwname);
+               kfree(buffer);
+               return -EIO;
+       }
+
+       c = fw->data;
+       end = fw->data + fw->size;
+
+       ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0);
+       if (ret < 0) {
+               kfree(buffer);
+               release_firmware(fw);
+               snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: "
+                               "begin urb.\n");
+               return ret;
+       }
+
+       while (c != end) {
+               for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
+                       buffer[i] = BIT_REVERSE_TABLE[(u8) *c];
+
+               ret = usb6fire_fw_fpga_write(device, buffer, i);
+               if (ret < 0) {
+                       release_firmware(fw);
+                       kfree(buffer);
+                       snd_printk(KERN_ERR PREFIX "unable to upload fpga "
+                                       "firmware: fw urb.\n");
+                       return ret;
+               }
+       }
+       release_firmware(fw);
+       kfree(buffer);
+
+       ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: "
+                               "end urb.\n");
+               return ret;
+       }
+       return 0;
+}
+
+int usb6fire_fw_init(struct usb_interface *intf)
+{
+       int i;
+       int ret;
+       struct usb_device *device = interface_to_usbdev(intf);
+       /* buffer: 8 receiving bytes from device and
+        * sizeof(EP_W_MAX_PACKET_SIZE) bytes for non-const copy */
+       u8 buffer[12];
+
+       ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "unable to receive device "
+                               "firmware state.\n");
+               return ret;
+       }
+       if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55
+                       || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7]
+                       != 0x00) {
+               snd_printk(KERN_ERR PREFIX "unknown device firmware state "
+                               "received from device: ");
+               for (i = 0; i < 8; i++)
+                       snd_printk("%02x ", buffer[i]);
+               snd_printk("\n");
+               return -EIO;
+       }
+       /* do we need fpga loader ezusb firmware? */
+       if (buffer[3] == 0x01 && buffer[6] == 0x19) {
+               ret = usb6fire_fw_ezusb_upload(intf,
+                               "6fire/dmx6firel2.ihx", 0, NULL, 0);
+               if (ret < 0)
+                       return ret;
+               return FW_NOT_READY;
+       }
+       /* do we need fpga firmware and application ezusb firmware? */
+       else if (buffer[3] == 0x02 && buffer[6] == 0x0b) {
+               ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
+               if (ret < 0)
+                       return ret;
+               memcpy(buffer, ep_w_max_packet_size,
+                               sizeof(ep_w_max_packet_size));
+               ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6fireap.ihx",
+                               0x0003, buffer, sizeof(ep_w_max_packet_size));
+               if (ret < 0)
+                       return ret;
+               return FW_NOT_READY;
+       }
+       /* all fw loaded? */
+       else if (buffer[3] == 0x03 && buffer[6] == 0x0b)
+               return 0;
+       /* unknown data? */
+       else {
+               snd_printk(KERN_ERR PREFIX "unknown device firmware state "
+                               "received from device: ");
+               for (i = 0; i < 8; i++)
+                       snd_printk("%02x ", buffer[i]);
+               snd_printk("\n");
+               return -EIO;
+       }
+       return 0;
+}
+
diff --git a/sound/usb/6fire/firmware.h b/sound/usb/6fire/firmware.h
new file mode 100644 (file)
index 0000000..0085698
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk
+ * Created: Jan 01, 2011
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#ifndef USB6FIRE_FIRMWARE_H
+#define USB6FIRE_FIRMWARE_H
+
+#include "common.h"
+
+enum /* firmware state of device */
+{
+       FW_READY = 0,
+       FW_NOT_READY = 1
+};
+
+int __devinit usb6fire_fw_init(struct usb_interface *intf);
+#endif /* USB6FIRE_FIRMWARE_H */
+
diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c
new file mode 100644 (file)
index 0000000..13f4509
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Rawmidi driver
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#include <sound/rawmidi.h>
+
+#include "midi.h"
+#include "chip.h"
+#include "comm.h"
+
+static void usb6fire_midi_out_handler(struct urb *urb)
+{
+       struct midi_runtime *rt = urb->context;
+       int ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rt->out_lock, flags);
+
+       if (rt->out) {
+               ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
+                               MIDI_BUFSIZE - 4);
+               if (ret > 0) { /* more data available, send next packet */
+                       rt->out_buffer[1] = ret + 2;
+                       rt->out_buffer[3] = rt->out_serial++;
+                       urb->transfer_buffer_length = ret + 4;
+
+                       ret = usb_submit_urb(urb, GFP_ATOMIC);
+                       if (ret < 0)
+                               snd_printk(KERN_ERR PREFIX "midi out urb "
+                                               "submit failed: %d\n", ret);
+               } else /* no more data to transmit */
+                       rt->out = NULL;
+       }
+       spin_unlock_irqrestore(&rt->out_lock, flags);
+}
+
+static void usb6fire_midi_in_received(
+               struct midi_runtime *rt, u8 *data, int length)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&rt->in_lock, flags);
+       if (rt->in)
+               snd_rawmidi_receive(rt->in, data, length);
+       spin_unlock_irqrestore(&rt->in_lock, flags);
+}
+
+static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
+{
+       return 0;
+}
+
+static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub)
+{
+       return 0;
+}
+
+static void usb6fire_midi_out_trigger(
+               struct snd_rawmidi_substream *alsa_sub, int up)
+{
+       struct midi_runtime *rt = alsa_sub->rmidi->private_data;
+       struct urb *urb = &rt->out_urb;
+       __s8 ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rt->out_lock, flags);
+       if (up) { /* start transfer */
+               if (rt->out) { /* we are already transmitting so just return */
+                       spin_unlock_irqrestore(&rt->out_lock, flags);
+                       return;
+               }
+
+               ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
+                               MIDI_BUFSIZE - 4);
+               if (ret > 0) {
+                       rt->out_buffer[1] = ret + 2;
+                       rt->out_buffer[3] = rt->out_serial++;
+                       urb->transfer_buffer_length = ret + 4;
+
+                       ret = usb_submit_urb(urb, GFP_ATOMIC);
+                       if (ret < 0)
+                               snd_printk(KERN_ERR PREFIX "midi out urb "
+                                               "submit failed: %d\n", ret);
+                       else
+                               rt->out = alsa_sub;
+               }
+       } else if (rt->out == alsa_sub)
+               rt->out = NULL;
+       spin_unlock_irqrestore(&rt->out_lock, flags);
+}
+
+static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
+{
+       struct midi_runtime *rt = alsa_sub->rmidi->private_data;
+       int retry = 0;
+
+       while (rt->out && retry++ < 100)
+               msleep(10);
+}
+
+static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
+{
+       return 0;
+}
+
+static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
+{
+       return 0;
+}
+
+static void usb6fire_midi_in_trigger(
+               struct snd_rawmidi_substream *alsa_sub, int up)
+{
+       struct midi_runtime *rt = alsa_sub->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rt->in_lock, flags);
+       if (up)
+               rt->in = alsa_sub;
+       else
+               rt->in = NULL;
+       spin_unlock_irqrestore(&rt->in_lock, flags);
+}
+
+static struct snd_rawmidi_ops out_ops = {
+       .open = usb6fire_midi_out_open,
+       .close = usb6fire_midi_out_close,
+       .trigger = usb6fire_midi_out_trigger,
+       .drain = usb6fire_midi_out_drain
+};
+
+static struct snd_rawmidi_ops in_ops = {
+       .open = usb6fire_midi_in_open,
+       .close = usb6fire_midi_in_close,
+       .trigger = usb6fire_midi_in_trigger
+};
+
+int __devinit usb6fire_midi_init(struct sfire_chip *chip)
+{
+       int ret;
+       struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
+                       GFP_KERNEL);
+       struct comm_runtime *comm_rt = chip->comm;
+
+       if (!rt)
+               return -ENOMEM;
+
+       rt->chip = chip;
+       rt->in_received = usb6fire_midi_in_received;
+       rt->out_buffer[0] = 0x80; /* 'send midi' command */
+       rt->out_buffer[1] = 0x00; /* size of data */
+       rt->out_buffer[2] = 0x00; /* always 0 */
+       spin_lock_init(&rt->in_lock);
+       spin_lock_init(&rt->out_lock);
+
+       comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
+                       usb6fire_midi_out_handler);
+
+       ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
+       if (ret < 0) {
+               kfree(rt);
+               snd_printk(KERN_ERR PREFIX "unable to create midi.\n");
+               return ret;
+       }
+       rt->instance->private_data = rt;
+       strcpy(rt->instance->name, "DMX6FireUSB MIDI");
+       rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+                       SNDRV_RAWMIDI_INFO_INPUT |
+                       SNDRV_RAWMIDI_INFO_DUPLEX;
+       snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                       &out_ops);
+       snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT,
+                       &in_ops);
+
+       chip->midi = rt;
+       return 0;
+}
+
+void usb6fire_midi_abort(struct sfire_chip *chip)
+{
+       struct midi_runtime *rt = chip->midi;
+
+       if (rt)
+               usb_poison_urb(&rt->out_urb);
+}
+
+void usb6fire_midi_destroy(struct sfire_chip *chip)
+{
+       kfree(chip->midi);
+       chip->midi = NULL;
+}
diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h
new file mode 100644 (file)
index 0000000..97a7bf6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#ifndef USB6FIRE_MIDI_H
+#define USB6FIRE_MIDI_H
+
+#include "common.h"
+
+enum {
+       MIDI_BUFSIZE = 64
+};
+
+struct midi_runtime {
+       struct sfire_chip *chip;
+       struct snd_rawmidi *instance;
+
+       struct snd_rawmidi_substream *in;
+       char in_active;
+
+       spinlock_t in_lock;
+       spinlock_t out_lock;
+       struct snd_rawmidi_substream *out;
+       struct urb out_urb;
+       u8 out_serial; /* serial number of out packet */
+       u8 out_buffer[MIDI_BUFSIZE];
+       int buffer_offset;
+
+       void (*in_received)(struct midi_runtime *rt, u8 *data, int length);
+};
+
+int __devinit usb6fire_midi_init(struct sfire_chip *chip);
+void usb6fire_midi_abort(struct sfire_chip *chip);
+void usb6fire_midi_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_MIDI_H */
+
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
new file mode 100644 (file)
index 0000000..ba62c74
--- /dev/null
@@ -0,0 +1,688 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * PCM driver
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#include "pcm.h"
+#include "chip.h"
+#include "comm.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 */
+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
+};
+
+enum { /* pcm streaming states */
+       STREAM_DISABLED, /* no pcm streaming */
+       STREAM_STARTING, /* pcm streaming requested, waiting to become ready */
+       STREAM_RUNNING, /* pcm streaming running */
+       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 |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BATCH,
+
+       .formats = SNDRV_PCM_FMTBIT_S24_LE,
+
+       .rates = SNDRV_PCM_RATE_44100 |
+               SNDRV_PCM_RATE_48000 |
+               SNDRV_PCM_RATE_88200 |
+               SNDRV_PCM_RATE_96000 |
+               SNDRV_PCM_RATE_176400 |
+               SNDRV_PCM_RATE_192000,
+
+       .rate_min = 44100,
+       .rate_max = 192000,
+       .channels_min = 1,
+       .channels_max = 0, /* set in pcm_open, depending on capture/playback */
+       .buffer_bytes_max = MAX_BUFSIZE,
+       .period_bytes_min = PCM_N_PACKETS_PER_URB * (PCM_MAX_PACKET_SIZE - 4),
+       .period_bytes_max = MAX_BUFSIZE,
+       .periods_min = 2,
+       .periods_max = 1024
+};
+
+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;
+
+       if (rt->rate >= ARRAY_SIZE(rates))
+               return -EINVAL;
+       /* disable streaming */
+       ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00);
+       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]);
+       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);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "error initializing analog 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);
+       if (ret < 0) {
+               snd_printk(KERN_ERR PREFIX "error starting streaming while "
+                               "setting samplerate %d.\n", rates[rt->rate]);
+               return ret;
+       }
+
+       rt->in_n_analog = IN_N_CHANNELS;
+       rt->out_n_analog = OUT_N_CHANNELS;
+       rt->in_packet_size = rates_in_packet_size[rt->rate];
+       rt->out_packet_size = rates_out_packet_size[rt->rate];
+       return 0;
+}
+
+static struct pcm_substream *usb6fire_pcm_get_substream(
+               struct snd_pcm_substream *alsa_sub)
+{
+       struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+
+       if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               return &rt->playback;
+       else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE)
+               return &rt->capture;
+       snd_printk(KERN_ERR PREFIX "error getting pcm substream slot.\n");
+       return NULL;
+}
+
+/* call with stream_mutex locked */
+static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
+{
+       int i;
+
+       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);
+               }
+               rt->stream_state = STREAM_DISABLED;
+       }
+}
+
+/* call with stream_mutex locked */
+static int usb6fire_pcm_stream_start(struct pcm_runtime *rt)
+{
+       int ret;
+       int i;
+       int k;
+       struct usb_iso_packet_descriptor *packet;
+
+       if (rt->stream_state == STREAM_DISABLED) {
+               /* submit our in urbs */
+               rt->stream_wait_cond = false;
+               rt->stream_state = STREAM_STARTING;
+               for (i = 0; i < PCM_N_URBS; i++) {
+                       for (k = 0; k < PCM_N_PACKETS_PER_URB; k++) {
+                               packet = &rt->in_urbs[i].packets[k];
+                               packet->offset = k * rt->in_packet_size;
+                               packet->length = rt->in_packet_size;
+                               packet->actual_length = 0;
+                               packet->status = 0;
+                       }
+                       ret = usb_submit_urb(&rt->in_urbs[i].instance,
+                                       GFP_ATOMIC);
+                       if (ret) {
+                               usb6fire_pcm_stream_stop(rt);
+                               return ret;
+                       }
+               }
+
+               /* wait for first out urb to return (sent in in urb handler) */
+               wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond,
+                               HZ);
+               if (rt->stream_wait_cond)
+                       rt->stream_state = STREAM_RUNNING;
+               else {
+                       usb6fire_pcm_stream_stop(rt);
+                       return -EIO;
+               }
+       }
+       return 0;
+}
+
+/* call with substream locked */
+static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
+{
+       int i;
+       int frame;
+       int frame_count;
+       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 *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
+                       * (alsa_rt->frame_bits >> 3));
+       int bytes_per_frame = alsa_rt->channels << 2;
+
+       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 */
+               if (urb->packets[i].actual_length > 4)
+                       frame_count = (urb->packets[i].actual_length - 4)
+                                       / (rt->in_n_analog << 2);
+               else
+                       frame_count = 0;
+
+               src = (u32 *) (urb->buffer + total_length);
+               src++; /* skip leading 4 bytes of every packet */
+               total_length += urb->packets[i].length;
+               for (frame = 0; frame < frame_count; frame++) {
+                       memcpy(dest, src, bytes_per_frame);
+                       dest += alsa_rt->channels;
+                       src += rt->in_n_analog;
+                       sub->dma_off++;
+                       sub->period_off++;
+                       if (dest == dest_end) {
+                               sub->dma_off = 0;
+                               dest = (u32 *) alsa_rt->dma_area;
+                       }
+               }
+       }
+}
+
+/* call with substream locked */
+static void usb6fire_pcm_playback(struct pcm_substream *sub,
+               struct pcm_urb *urb)
+{
+       int i;
+       int frame;
+       int frame_count;
+       struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
+       struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
+       u32 *src = (u32 *) (alsa_rt->dma_area + sub->dma_off
+                       * (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;
+       int bytes_per_frame = alsa_rt->channels << 2;
+
+       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 */
+               if (urb->packets[i].length > 4)
+                       frame_count = (urb->packets[i].length - 4)
+                                       / (rt->out_n_analog << 2);
+               else
+                       frame_count = 0;
+               dest++; /* skip leading 4 bytes of every frame */
+               for (frame = 0; frame < frame_count; frame++) {
+                       memcpy(dest, src, bytes_per_frame);
+                       src += alsa_rt->channels;
+                       dest += rt->out_n_analog;
+                       sub->dma_off++;
+                       sub->period_off++;
+                       if (src == src_end) {
+                               src = (u32 *) alsa_rt->dma_area;
+                               sub->dma_off = 0;
+                       }
+               }
+       }
+}
+
+static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
+{
+       struct pcm_urb *in_urb = usb_urb->context;
+       struct pcm_urb *out_urb = in_urb->peer;
+       struct pcm_runtime *rt = in_urb->chip->pcm;
+       struct pcm_substream *sub;
+       unsigned long flags;
+       int total_length = 0;
+       int frame_count;
+       int frame;
+       int channel;
+       int i;
+       u8 *dest;
+
+       if (usb_urb->status || rt->panic || rt->stream_state == STREAM_STOPPING)
+               return;
+       for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
+               if (in_urb->packets[i].status) {
+                       rt->panic = true;
+                       return;
+               }
+
+       if (rt->stream_state == STREAM_DISABLED) {
+               snd_printk(KERN_ERR PREFIX "internal error: "
+                               "stream disabled in in-urb handler.\n");
+               return;
+       }
+
+       /* receive our capture data */
+       sub = &rt->capture;
+       spin_lock_irqsave(&sub->lock, flags);
+       if (sub->active) {
+               usb6fire_pcm_capture(sub, in_urb);
+               if (sub->period_off >= sub->instance->runtime->period_size) {
+                       sub->period_off %= sub->instance->runtime->period_size;
+                       spin_unlock_irqrestore(&sub->lock, flags);
+                       snd_pcm_period_elapsed(sub->instance);
+               } else
+                       spin_unlock_irqrestore(&sub->lock, flags);
+       } else
+               spin_unlock_irqrestore(&sub->lock, flags);
+
+       /* setup out urb structure */
+       for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
+               out_urb->packets[i].offset = total_length;
+               out_urb->packets[i].length = (in_urb->packets[i].actual_length
+                               - 4) / (rt->in_n_analog << 2)
+                               * (rt->out_n_analog << 2) + 4;
+               out_urb->packets[i].status = 0;
+               total_length += out_urb->packets[i].length;
+       }
+       memset(out_urb->buffer, 0, total_length);
+
+       /* now send our playback data (if a free out urb was found) */
+       sub = &rt->playback;
+       spin_lock_irqsave(&sub->lock, flags);
+       if (sub->active) {
+               usb6fire_pcm_playback(sub, out_urb);
+               if (sub->period_off >= sub->instance->runtime->period_size) {
+                       sub->period_off %= sub->instance->runtime->period_size;
+                       spin_unlock_irqrestore(&sub->lock, flags);
+                       snd_pcm_period_elapsed(sub->instance);
+               } else
+                       spin_unlock_irqrestore(&sub->lock, flags);
+       } else
+               spin_unlock_irqrestore(&sub->lock, flags);
+
+       /* setup the 4th byte of each sample (0x40 for analog channels) */
+       dest = out_urb->buffer;
+       for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
+               if (out_urb->packets[i].length >= 4) {
+                       frame_count = (out_urb->packets[i].length - 4)
+                                       / (rt->out_n_analog << 2);
+                       *(dest++) = 0xaa;
+                       *(dest++) = 0xaa;
+                       *(dest++) = frame_count;
+                       *(dest++) = 0x00;
+                       for (frame = 0; frame < frame_count; frame++)
+                               for (channel = 0;
+                                               channel < rt->out_n_analog;
+                                               channel++) {
+                                       dest += 3; /* skip sample data */
+                                       *(dest++) = 0x40;
+                               }
+               }
+       usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
+       usb_submit_urb(&in_urb->instance, GFP_ATOMIC);
+}
+
+static void usb6fire_pcm_out_urb_handler(struct urb *usb_urb)
+{
+       struct pcm_urb *urb = usb_urb->context;
+       struct pcm_runtime *rt = urb->chip->pcm;
+
+       if (rt->stream_state == STREAM_STARTING) {
+               rt->stream_wait_cond = true;
+               wake_up(&rt->stream_wait_queue);
+       }
+}
+
+static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub)
+{
+       struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+       struct pcm_substream *sub = NULL;
+       struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+
+       if (rt->panic)
+               return -EPIPE;
+
+       mutex_lock(&rt->stream_mutex);
+       alsa_rt->hw = pcm_hw;
+
+       if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (rt->rate >= 0)
+                       alsa_rt->hw.rates = rates_alsaid[rt->rate];
+               alsa_rt->hw.channels_max = OUT_N_CHANNELS;
+               sub = &rt->playback;
+       } else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               if (rt->rate >= 0)
+                       alsa_rt->hw.rates = rates_alsaid[rt->rate];
+               alsa_rt->hw.channels_max = IN_N_CHANNELS;
+               sub = &rt->capture;
+       }
+
+       if (!sub) {
+               mutex_unlock(&rt->stream_mutex);
+               snd_printk(KERN_ERR PREFIX "invalid stream type.\n");
+               return -EINVAL;
+       }
+
+       sub->instance = alsa_sub;
+       sub->active = false;
+       mutex_unlock(&rt->stream_mutex);
+       return 0;
+}
+
+static int usb6fire_pcm_close(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);
+       unsigned long flags;
+
+       if (rt->panic)
+               return 0;
+
+       mutex_lock(&rt->stream_mutex);
+       if (sub) {
+               /* deactivate substream */
+               spin_lock_irqsave(&sub->lock, flags);
+               sub->instance = NULL;
+               sub->active = false;
+               spin_unlock_irqrestore(&sub->lock, flags);
+
+               /* all substreams closed? if so, stop streaming */
+               if (!rt->playback.instance && !rt->capture.instance) {
+                       usb6fire_pcm_stream_stop(rt);
+                       rt->rate = -1;
+               }
+       }
+       mutex_unlock(&rt->stream_mutex);
+       return 0;
+}
+
+static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub,
+               struct snd_pcm_hw_params *hw_params)
+{
+       return snd_pcm_lib_malloc_pages(alsa_sub,
+                       params_buffer_bytes(hw_params));
+}
+
+static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub)
+{
+       return snd_pcm_lib_free_pages(alsa_sub);
+}
+
+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)
+               return -EPIPE;
+       if (!sub)
+               return -ENODEV;
+
+       mutex_lock(&rt->stream_mutex);
+       sub->dma_off = 0;
+       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;
+                               break;
+                       }
+               if (i == ARRAY_SIZE(rates)) {
+                       mutex_unlock(&rt->stream_mutex);
+                       snd_printk("invalid rate %d in prepare.\n",
+                                       alsa_rt->rate);
+                       return -EINVAL;
+               }
+
+               ret = usb6fire_pcm_set_rate(rt);
+               if (ret) {
+                       mutex_unlock(&rt->stream_mutex);
+                       return ret;
+               }
+               ret = usb6fire_pcm_stream_start(rt);
+               if (ret) {
+                       mutex_unlock(&rt->stream_mutex);
+                       snd_printk(KERN_ERR PREFIX
+                                       "could not start pcm stream.\n");
+                       return ret;
+               }
+       }
+       mutex_unlock(&rt->stream_mutex);
+       return 0;
+}
+
+static int usb6fire_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
+{
+       struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
+       struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+       unsigned long flags;
+
+       if (rt->panic)
+               return -EPIPE;
+       if (!sub)
+               return -ENODEV;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               spin_lock_irqsave(&sub->lock, flags);
+               sub->active = true;
+               spin_unlock_irqrestore(&sub->lock, flags);
+               return 0;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               spin_lock_irqsave(&sub->lock, flags);
+               sub->active = false;
+               spin_unlock_irqrestore(&sub->lock, flags);
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static snd_pcm_uframes_t usb6fire_pcm_pointer(
+               struct snd_pcm_substream *alsa_sub)
+{
+       struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
+       struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+       unsigned long flags;
+       snd_pcm_uframes_t ret;
+
+       if (rt->panic || !sub)
+               return SNDRV_PCM_STATE_XRUN;
+
+       spin_lock_irqsave(&sub->lock, flags);
+       ret = sub->dma_off;
+       spin_unlock_irqrestore(&sub->lock, flags);
+       return ret;
+}
+
+static struct snd_pcm_ops pcm_ops = {
+       .open = usb6fire_pcm_open,
+       .close = usb6fire_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = usb6fire_pcm_hw_params,
+       .hw_free = usb6fire_pcm_hw_free,
+       .prepare = usb6fire_pcm_prepare,
+       .trigger = usb6fire_pcm_trigger,
+       .pointer = usb6fire_pcm_pointer,
+};
+
+static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb,
+               struct sfire_chip *chip, bool in, int ep,
+               void (*handler)(struct urb *))
+{
+       urb->chip = chip;
+       usb_init_urb(&urb->instance);
+       urb->instance.transfer_buffer = urb->buffer;
+       urb->instance.transfer_buffer_length =
+                       PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE;
+       urb->instance.dev = chip->dev;
+       urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep)
+                       : usb_sndisocpipe(chip->dev, ep);
+       urb->instance.interval = 1;
+       urb->instance.transfer_flags = URB_ISO_ASAP;
+       urb->instance.complete = handler;
+       urb->instance.context = urb;
+       urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB;
+}
+
+int __devinit usb6fire_pcm_init(struct sfire_chip *chip)
+{
+       int i;
+       int ret;
+       struct snd_pcm *pcm;
+       struct pcm_runtime *rt =
+                       kzalloc(sizeof(struct pcm_runtime), GFP_KERNEL);
+
+       if (!rt)
+               return -ENOMEM;
+
+       rt->chip = chip;
+       rt->stream_state = STREAM_DISABLED;
+       rt->rate = -1;
+       init_waitqueue_head(&rt->stream_wait_queue);
+       mutex_init(&rt->stream_mutex);
+
+       spin_lock_init(&rt->playback.lock);
+       spin_lock_init(&rt->capture.lock);
+
+       for (i = 0; i < PCM_N_URBS; i++) {
+               usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP,
+                               usb6fire_pcm_in_urb_handler);
+               usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP,
+                               usb6fire_pcm_out_urb_handler);
+
+               rt->in_urbs[i].peer = &rt->out_urbs[i];
+               rt->out_urbs[i].peer = &rt->in_urbs[i];
+       }
+
+       ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm);
+       if (ret < 0) {
+               kfree(rt);
+               snd_printk(KERN_ERR PREFIX "cannot create pcm instance.\n");
+               return ret;
+       }
+
+       pcm->private_data = rt;
+       strcpy(pcm->name, "DMX 6Fire USB");
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops);
+
+       ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       snd_dma_continuous_data(GFP_KERNEL),
+                       MAX_BUFSIZE, MAX_BUFSIZE);
+       if (ret) {
+               kfree(rt);
+               snd_printk(KERN_ERR PREFIX
+                               "error preallocating pcm buffers.\n");
+               return ret;
+       }
+       rt->instance = pcm;
+
+       chip->pcm = rt;
+       return 0;
+}
+
+void usb6fire_pcm_abort(struct sfire_chip *chip)
+{
+       struct pcm_runtime *rt = chip->pcm;
+       int i;
+
+       if (rt) {
+               rt->panic = true;
+
+               if (rt->playback.instance)
+                       snd_pcm_stop(rt->playback.instance,
+                                       SNDRV_PCM_STATE_XRUN);
+               if (rt->capture.instance)
+                       snd_pcm_stop(rt->capture.instance,
+                                       SNDRV_PCM_STATE_XRUN);
+
+               for (i = 0; i < PCM_N_URBS; i++) {
+                       usb_poison_urb(&rt->in_urbs[i].instance);
+                       usb_poison_urb(&rt->out_urbs[i].instance);
+               }
+
+       }
+}
+
+void usb6fire_pcm_destroy(struct sfire_chip *chip)
+{
+       kfree(chip->pcm);
+       chip->pcm = NULL;
+}
diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h
new file mode 100644 (file)
index 0000000..2bee813
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author:     Torsten Schenk <torsten.schenk@zoho.com>
+ * Created:    Jan 01, 2011
+ * Version:    0.3.0
+ * Copyright:  (C) Torsten Schenk
+ *
+ * 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.
+ */
+
+#ifndef USB6FIRE_PCM_H
+#define USB6FIRE_PCM_H
+
+#include <sound/pcm.h>
+#include <linux/mutex.h>
+
+#include "common.h"
+
+enum /* settings for pcm */
+{
+       /* maximum of EP_W_MAX_PACKET_SIZE[] (see firmware.c) */
+       PCM_N_URBS = 16, PCM_N_PACKETS_PER_URB = 8, PCM_MAX_PACKET_SIZE = 604
+};
+
+struct pcm_urb {
+       struct sfire_chip *chip;
+
+       /* BEGIN DO NOT SEPARATE */
+       struct urb instance;
+       struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB];
+       /* END DO NOT SEPARATE */
+       u8 buffer[PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE];
+
+       struct pcm_urb *peer;
+};
+
+struct pcm_substream {
+       spinlock_t lock;
+       struct snd_pcm_substream *instance;
+
+       bool active;
+
+       snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */
+       snd_pcm_uframes_t period_off; /* current position in current period */
+};
+
+struct pcm_runtime {
+       struct sfire_chip *chip;
+       struct snd_pcm *instance;
+
+       struct pcm_substream playback;
+       struct pcm_substream capture;
+       bool panic; /* if set driver won't do anymore pcm on device */
+
+       struct pcm_urb in_urbs[PCM_N_URBS];
+       struct pcm_urb out_urbs[PCM_N_URBS];
+       int in_packet_size;
+       int out_packet_size;
+       int in_n_analog; /* number of analog channels soundcard sends */
+       int out_n_analog; /* number of analog channels soundcard receives */
+
+       struct mutex stream_mutex;
+       u8 stream_state; /* one of STREAM_XXX (pcm.c) */
+       u8 rate; /* one of PCM_RATE_XXX */
+       wait_queue_head_t stream_wait_queue;
+       bool stream_wait_cond;
+};
+
+int __devinit usb6fire_pcm_init(struct sfire_chip *chip);
+void usb6fire_pcm_abort(struct sfire_chip *chip);
+void usb6fire_pcm_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_PCM_H */
index 112984f4080f1d5cbd84aa157d0c60fae2be5cd9..97724d8fa9f689fd5cc863cef980b97cdd8de4a5 100644 (file)
@@ -62,6 +62,7 @@ config SND_USB_CAIAQ
            * Native Instruments Audio 2 DJ
            * Native Instruments Audio 4 DJ
            * Native Instruments Audio 8 DJ
+           * Native Instruments Traktor Audio 2
            * Native Instruments Guitar Rig Session I/O
            * Native Instruments Guitar Rig mobile
            * Native Instruments Traktor Kontrol X1
@@ -97,5 +98,21 @@ config SND_USB_US122L
          To compile this driver as a module, choose M here: the module
          will be called snd-usb-us122l.
 
+config SND_USB_6FIRE
+        tristate "TerraTec DMX 6Fire USB"
+        depends on EXPERIMENTAL
+        select FW_LOADER
+        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.
+
 endif  # SND_USB
 
index 1e362bf8834f4e5fdfc6da32cf33a96f53ef7c85..cf9ed66445fad6e6d9ba145bb5a0076bc140e07c 100644 (file)
@@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
 obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
 obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
 
-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/
index 66eabafb1c24554f8c46b7d946d4e3c6cf8fc3bf..d0d493ca28ae2ec5693af9899d0199a0d4433a38 100644 (file)
@@ -805,6 +805,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO2DJ):
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORAUDIO2):
                dev->samplerates |= SNDRV_PCM_RATE_88200;
                break;
        }
index 6480c3283c05a0082e21c31788ea54c0e7ccb7b4..45bc4a2dc6f0dc8065aec1a518df9a8bc59c9201 100644 (file)
@@ -46,6 +46,7 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, Audio 2 DJ},"
                         "{Native Instruments, Audio 4 DJ},"
                         "{Native Instruments, Audio 8 DJ},"
+                        "{Native Instruments, Traktor Audio 2},"
                         "{Native Instruments, Session I/O},"
                         "{Native Instruments, GuitarRig mobile}"
                         "{Native Instruments, Traktor Kontrol X1}"
@@ -140,6 +141,11 @@ static struct usb_device_id snd_usb_id_table[] = {
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
                .idProduct =    USB_PID_TRAKTORKONTROLS4
        },
+       {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     USB_VID_NATIVEINSTRUMENTS,
+               .idProduct =    USB_PID_TRAKTORAUDIO2
+       },
        { /* terminator */ }
 };
 
index e3d8a3efb35b5350de0f60789f4834bf19494f96..b2b310194ffa4c27640ccdb624885500dd562ed5 100644 (file)
@@ -17,6 +17,7 @@
 #define USB_PID_GUITARRIGMOBILE                0x0d8d
 #define USB_PID_TRAKTORKONTROLX1       0x2305
 #define USB_PID_TRAKTORKONTROLS4       0xbaff
+#define USB_PID_TRAKTORAUDIO2          0x041d
 
 #define EP1_BUFSIZE 64
 #define EP4_BUFSIZE 512
index c0f8270bc199e9e91a82cef451d1aa63ead9eb15..40722f8711adc82001001d4fa3f69b8c35b34253 100644 (file)
@@ -65,6 +65,7 @@
 #include "pcm.h"
 #include "urb.h"
 #include "format.h"
+#include "power.h"
 
 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("USB Audio");
@@ -330,6 +331,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
        chip->setup = device_setup[idx];
        chip->nrpacks = nrpacks;
        chip->async_unlink = async_unlink;
+       chip->probing = 1;
 
        chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
                              le16_to_cpu(dev->descriptor.idProduct));
@@ -451,6 +453,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                                goto __error;
                        }
                        chip = usb_chip[i];
+                       chip->probing = 1;
                        break;
                }
        }
@@ -466,6 +469,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                                        goto __error;
                                }
                                snd_card_set_dev(chip->card, &intf->dev);
+                               chip->pm_intf = intf;
                                break;
                        }
                if (!chip) {
@@ -505,6 +509,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 
        usb_chip[chip->index] = chip;
        chip->num_interfaces++;
+       chip->probing = 0;
        mutex_unlock(&register_mutex);
        return chip;
 
@@ -581,29 +586,61 @@ static void usb_audio_disconnect(struct usb_interface *intf)
 }
 
 #ifdef CONFIG_PM
+
+int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+       int err = -ENODEV;
+
+       if (!chip->shutdown && !chip->probing)
+               err = usb_autopm_get_interface(chip->pm_intf);
+
+       return err;
+}
+
+void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+       if (!chip->shutdown && !chip->probing)
+               usb_autopm_put_interface(chip->pm_intf);
+}
+
 static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct snd_usb_audio *chip = usb_get_intfdata(intf);
        struct list_head *p;
        struct snd_usb_stream *as;
+       struct usb_mixer_interface *mixer;
 
        if (chip == (void *)-1L)
                return 0;
 
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
-       if (!chip->num_suspended_intf++) {
-               list_for_each(p, &chip->pcm_list) {
-                       as = list_entry(p, struct snd_usb_stream, list);
-                       snd_pcm_suspend_all(as->pcm);
-               }
+       if (!(message.event & PM_EVENT_AUTO)) {
+               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+               if (!chip->num_suspended_intf++) {
+                       list_for_each(p, &chip->pcm_list) {
+                               as = list_entry(p, struct snd_usb_stream, list);
+                               snd_pcm_suspend_all(as->pcm);
+                       }
+               }
+       } else {
+               /*
+                * otherwise we keep the rest of the system in the dark
+                * to keep this transparent
+                */
+               if (!chip->num_suspended_intf++)
+                       chip->autosuspended = 1;
        }
 
+       list_for_each_entry(mixer, &chip->mixer_list, list)
+               snd_usb_mixer_inactivate(mixer);
+
        return 0;
 }
 
 static int usb_audio_resume(struct usb_interface *intf)
 {
        struct snd_usb_audio *chip = usb_get_intfdata(intf);
+       struct usb_mixer_interface *mixer;
+       int err = 0;
 
        if (chip == (void *)-1L)
                return 0;
@@ -611,12 +648,20 @@ static int usb_audio_resume(struct usb_interface *intf)
                return 0;
        /*
         * ALSA leaves material resumption to user space
-        * we just notify
+        * we just notify and restart the mixers
         */
+       list_for_each_entry(mixer, &chip->mixer_list, list) {
+               err = snd_usb_mixer_activate(mixer);
+               if (err < 0)
+                       goto err_out;
+       }
 
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+       if (!chip->autosuspended)
+               snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+       chip->autosuspended = 0;
 
-       return 0;
+err_out:
+       return err;
 }
 #else
 #define usb_audio_suspend      NULL
@@ -644,6 +689,7 @@ static struct usb_driver usb_audio_driver = {
        .suspend =      usb_audio_suspend,
        .resume =       usb_audio_resume,
        .id_table =     usb_audio_ids,
+       .supports_autosuspend = 1,
 };
 
 static int __init snd_usb_audio_init(void)
index db2dc5ffe6dd822cb18a25f6d34a21b8c04085e9..b4b39c0b6c9ed8b1e698213a88ceb9637a082cef 100644 (file)
@@ -54,6 +54,7 @@
 #include <sound/asequencer.h>
 #include "usbaudio.h"
 #include "midi.h"
+#include "power.h"
 #include "helper.h"
 
 /*
@@ -1044,6 +1045,7 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
        struct snd_usb_midi* umidi = substream->rmidi->private_data;
        struct usbmidi_out_port* port = NULL;
        int i, j;
+       int err;
 
        for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
                if (umidi->endpoints[i].out)
@@ -1056,6 +1058,9 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
                snd_BUG();
                return -ENXIO;
        }
+       err = usb_autopm_get_interface(umidi->iface);
+       if (err < 0)
+               return -EIO;
        substream->runtime->private_data = port;
        port->state = STATE_UNKNOWN;
        substream_open(substream, 1);
@@ -1064,7 +1069,10 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
 
 static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
 {
+       struct snd_usb_midi* umidi = substream->rmidi->private_data;
+
        substream_open(substream, 0);
+       usb_autopm_put_interface(umidi->iface);
        return 0;
 }
 
index 85af6051b52d0e81afcfc8db8ca695e704e3a0c8..5e47757166074833159007d725d6dd2ede025bfd 100644 (file)
@@ -61,6 +61,7 @@
 #include "mixer.h"
 #include "helper.h"
 #include "mixer_quirks.h"
+#include "power.h"
 
 #define MAX_ID_ELEMS   256
 
@@ -295,16 +296,22 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
        int timeout = 10;
+       int err;
 
+       err = snd_usb_autoresume(cval->mixer->chip);
+       if (err < 0)
+               return -EIO;
        while (timeout-- > 0) {
                if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
                                    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
                                    validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
                                    buf, val_len, 100) >= val_len) {
                        *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
+                       snd_usb_autosuspend(cval->mixer->chip);
                        return 0;
                }
        }
+       snd_usb_autosuspend(cval->mixer->chip);
        snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
                    request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
        return -EINVAL;
@@ -328,12 +335,18 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
 
        memset(buf, 0, sizeof(buf));
 
+       ret = snd_usb_autoresume(chip) ? -EIO : 0;
+       if (ret)
+               goto error;
+
        ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
                              USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
                              validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
                              buf, size, 1000);
+       snd_usb_autosuspend(chip);
 
        if (ret < 0) {
+error:
                snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
                           request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
                return ret;
@@ -413,7 +426,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
 {
        struct snd_usb_audio *chip = cval->mixer->chip;
        unsigned char buf[2];
-       int val_len, timeout = 10;
+       int val_len, err, timeout = 10;
 
        if (cval->mixer->protocol == UAC_VERSION_1) {
                val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -433,13 +446,19 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
        value_set = convert_bytes_value(cval, value_set);
        buf[0] = value_set & 0xff;
        buf[1] = (value_set >> 8) & 0xff;
+       err = snd_usb_autoresume(chip);
+       if (err < 0)
+               return -EIO;
        while (timeout-- > 0)
                if (snd_usb_ctl_msg(chip->dev,
                                    usb_sndctrlpipe(chip->dev, 0), request,
                                    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
                                    validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
-                                   buf, val_len, 100) >= 0)
+                                   buf, val_len, 100) >= 0) {
+                       snd_usb_autosuspend(chip);
                        return 0;
+               }
+       snd_usb_autosuspend(chip);
        snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
                    request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type, buf[0], buf[1]);
        return -EINVAL;
@@ -987,6 +1006,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
        struct snd_kcontrol *kctl;
        struct usb_mixer_elem_info *cval;
        const struct usbmix_name_map *map;
+       unsigned int range;
 
        control++; /* change from zero-based to 1-based value */
 
@@ -1121,6 +1141,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                }
                break;
 
+       case USB_ID(0x046d, 0x0808):
        case USB_ID(0x046d, 0x0809):
        case USB_ID(0x046d, 0x0991):
        /* Most audio usb devices lie about volume resolution.
@@ -1136,6 +1157,21 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 
        }
 
+       range = (cval->max - cval->min) / cval->res;
+       /* Are there devices with volume range more than 255? I use a bit more
+        * to be sure. 384 is a resolution magic number found on Logitech
+        * devices. It will definitively catch all buggy Logitech devices.
+        */
+       if (range > 384) {
+               snd_printk(KERN_WARNING "usb_audio: Warning! Unlikely big "
+                          "volume range (=%u), cval->res is probably wrong.",
+                          range);
+               snd_printk(KERN_WARNING "usb_audio: [%d] FU [%s] ch = %d, "
+                          "val = %d/%d/%d", cval->id,
+                          kctl->id.name, cval->channels,
+                          cval->min, cval->max, cval->res);
+       }
+
        snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
                    cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res);
        add_control_to_empty(state, kctl);
@@ -2058,8 +2094,9 @@ static void snd_usb_mixer_interrupt(struct urb *urb)
 {
        struct usb_mixer_interface *mixer = urb->context;
        int len = urb->actual_length;
+       int ustatus = urb->status;
 
-       if (urb->status != 0)
+       if (ustatus != 0)
                goto requeue;
 
        if (mixer->protocol == UAC_VERSION_1) {
@@ -2100,12 +2137,32 @@ static void snd_usb_mixer_interrupt(struct urb *urb)
        }
 
 requeue:
-       if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
+       if (ustatus != -ENOENT && ustatus != -ECONNRESET && ustatus != -ESHUTDOWN) {
                urb->dev = mixer->chip->dev;
                usb_submit_urb(urb, GFP_ATOMIC);
        }
 }
 
+/* stop any bus activity of a mixer */
+void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer)
+{
+       usb_kill_urb(mixer->urb);
+       usb_kill_urb(mixer->rc_urb);
+}
+
+int snd_usb_mixer_activate(struct usb_mixer_interface *mixer)
+{
+       int err;
+
+       if (mixer->urb) {
+               err = usb_submit_urb(mixer->urb, GFP_NOIO);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 /* create the handler for the optional status interrupt endpoint */
 static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
 {
index 26c636c5c93a3101a9a1a946bae52164734a2a89..b4a2c8165e4b2b6e7870fda7b0f3a490e17e97e8 100644 (file)
@@ -52,5 +52,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
 
 int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
                                int request, int validx, int value_set);
+void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer);
+int snd_usb_mixer_activate(struct usb_mixer_interface *mixer);
 
 #endif /* __USBMIXER_H */
index 782f741cd00a4e3bb97317d05827f8c3134299bc..73dcc8256bc0abbeeb2ebed24957e5ae7912b31d 100644 (file)
@@ -346,6 +346,141 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
        return 0;
 }
 
+/* Native Instruments device quirks */
+
+#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
+
+static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       struct usb_device *dev = mixer->chip->dev;
+       u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
+       u16 wIndex = kcontrol->private_value & 0xffff;
+       u8 tmp;
+
+       int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
+                                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+                                 0, cpu_to_le16(wIndex),
+                                 &tmp, sizeof(tmp), 1000);
+
+       if (ret < 0) {
+               snd_printk(KERN_ERR
+                          "unable to issue vendor read request (ret = %d)", ret);
+               return ret;
+       }
+
+       ucontrol->value.integer.value[0] = tmp;
+
+       return 0;
+}
+
+static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       struct usb_device *dev = mixer->chip->dev;
+       u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
+       u16 wIndex = kcontrol->private_value & 0xffff;
+       u16 wValue = ucontrol->value.integer.value[0];
+
+       int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest,
+                                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+                                 cpu_to_le16(wValue), cpu_to_le16(wIndex),
+                                 NULL, 0, 1000);
+
+       if (ret < 0) {
+               snd_printk(KERN_ERR
+                          "unable to issue vendor write request (ret = %d)", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
+       {
+               .name = "Direct Thru Channel A",
+               .private_value = _MAKE_NI_CONTROL(0x01, 0x03),
+       },
+       {
+               .name = "Direct Thru Channel B",
+               .private_value = _MAKE_NI_CONTROL(0x01, 0x05),
+       },
+       {
+               .name = "Phono Input Channel A",
+               .private_value = _MAKE_NI_CONTROL(0x02, 0x03),
+       },
+       {
+               .name = "Phono Input Channel B",
+               .private_value = _MAKE_NI_CONTROL(0x02, 0x05),
+       },
+};
+
+static struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = {
+       {
+               .name = "Direct Thru Channel A",
+               .private_value = _MAKE_NI_CONTROL(0x01, 0x03),
+       },
+       {
+               .name = "Direct Thru Channel B",
+               .private_value = _MAKE_NI_CONTROL(0x01, 0x05),
+       },
+       {
+               .name = "Direct Thru Channel C",
+               .private_value = _MAKE_NI_CONTROL(0x01, 0x07),
+       },
+       {
+               .name = "Direct Thru Channel D",
+               .private_value = _MAKE_NI_CONTROL(0x01, 0x09),
+       },
+       {
+               .name = "Phono Input Channel A",
+               .private_value = _MAKE_NI_CONTROL(0x02, 0x03),
+       },
+       {
+               .name = "Phono Input Channel B",
+               .private_value = _MAKE_NI_CONTROL(0x02, 0x05),
+       },
+       {
+               .name = "Phono Input Channel C",
+               .private_value = _MAKE_NI_CONTROL(0x02, 0x07),
+       },
+       {
+               .name = "Phono Input Channel D",
+               .private_value = _MAKE_NI_CONTROL(0x02, 0x09),
+       },
+};
+
+static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
+                                             const struct snd_kcontrol_new *kc,
+                                             unsigned int count)
+{
+       int i, err = 0;
+       struct snd_kcontrol_new template = {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .get = snd_nativeinstruments_control_get,
+               .put = snd_nativeinstruments_control_put,
+               .info = snd_ctl_boolean_mono_info,
+       };
+
+       for (i = 0; i < count; i++) {
+               struct snd_kcontrol *c;
+
+               template.name = kc[i].name;
+               template.private_value = kc[i].private_value;
+
+               c = snd_ctl_new1(&template, mixer);
+               err = snd_ctl_add(mixer->chip->card, c);
+
+               if (err < 0)
+                       break;
+       }
+
+       return err;
+}
+
 void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
                               unsigned char samplerate_id)
 {
@@ -367,31 +502,44 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
 
 int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
 {
-       int err;
+       int err = 0;
        struct snd_info_entry *entry;
 
        if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
                return err;
 
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
-               if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
-                       return err;
+       switch (mixer->chip->usb_id) {
+       case USB_ID(0x041e, 0x3020):
+       case USB_ID(0x041e, 0x3040):
+       case USB_ID(0x041e, 0x3042):
+       case USB_ID(0x041e, 0x3048):
+               err = snd_audigy2nx_controls_create(mixer);
+               if (err < 0)
+                       break;
                if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry))
                        snd_info_set_text_ops(entry, mixer,
                                              snd_audigy2nx_proc_read);
-       }
+               break;
 
-       if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
-           mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
+       case USB_ID(0x0b05, 0x1739):
+       case USB_ID(0x0b05, 0x1743):
                err = snd_xonar_u1_controls_create(mixer);
-               if (err < 0)
-                       return err;
+               break;
+
+       case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
+               err = snd_nativeinstruments_create_mixer(mixer,
+                               snd_nativeinstruments_ta6_mixers,
+                               ARRAY_SIZE(snd_nativeinstruments_ta6_mixers));
+               break;
+
+       case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */
+               err = snd_nativeinstruments_create_mixer(mixer,
+                               snd_nativeinstruments_ta10_mixers,
+                               ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
+               break;
        }
 
-       return 0;
+       return err;
 }
 
 void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
index e3f680526cb5ce840787d7feba00cdf7e54ff2f5..b8dcbf407bbbbe10bf4daf39b65d4f6e06ec65b7 100644 (file)
@@ -32,6 +32,7 @@
 #include "helper.h"
 #include "pcm.h"
 #include "clock.h"
+#include "power.h"
 
 /*
  * return the current pcm pointer.  just based on the hwptr_done value.
@@ -739,6 +740,9 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
                pt = 125 * (1 << fp->datainterval);
                ptmin = min(ptmin, pt);
        }
+       err = snd_usb_autoresume(subs->stream->chip);
+       if (err < 0)
+               return err;
 
        param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
        if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
@@ -756,21 +760,21 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
                                       SNDRV_PCM_HW_PARAM_CHANNELS,
                                       param_period_time_if_needed,
                                       -1)) < 0)
-               return err;
+               goto rep_err;
        if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
                                       hw_rule_channels, subs,
                                       SNDRV_PCM_HW_PARAM_FORMAT,
                                       SNDRV_PCM_HW_PARAM_RATE,
                                       param_period_time_if_needed,
                                       -1)) < 0)
-               return err;
+               goto rep_err;
        if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
                                       hw_rule_format, subs,
                                       SNDRV_PCM_HW_PARAM_RATE,
                                       SNDRV_PCM_HW_PARAM_CHANNELS,
                                       param_period_time_if_needed,
                                       -1)) < 0)
-               return err;
+               goto rep_err;
        if (param_period_time_if_needed >= 0) {
                err = snd_pcm_hw_rule_add(runtime, 0,
                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
@@ -780,11 +784,15 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
                                          SNDRV_PCM_HW_PARAM_RATE,
                                          -1);
                if (err < 0)
-                       return err;
+                       goto rep_err;
        }
        if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
-               return err;
+               goto rep_err;
        return 0;
+
+rep_err:
+       snd_usb_autosuspend(subs->stream->chip);
+       return err;
 }
 
 static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
@@ -798,6 +806,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
        runtime->hw = snd_usb_hardware;
        runtime->private_data = subs;
        subs->pcm_substream = substream;
+       /* runtime PM is also done there */
        return setup_hw_info(runtime, subs);
 }
 
@@ -811,6 +820,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
                subs->interface = -1;
        }
        subs->pcm_substream = NULL;
+       snd_usb_autosuspend(subs->stream->chip);
        return 0;
 }
 
diff --git a/sound/usb/power.h b/sound/usb/power.h
new file mode 100644 (file)
index 0000000..48ee51d
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __USBAUDIO_POWER_H
+#define __USBAUDIO_POWER_H
+
+#ifdef CONFIG_PM
+int snd_usb_autoresume(struct snd_usb_audio *chip);
+void snd_usb_autosuspend(struct snd_usb_audio *chip);
+#else
+static inline int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+       return 0;
+}
+static inline void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+}
+#endif
+
+#endif /* __USBAUDIO_POWER_H */
index 921a86fd9884d0355ac9a4bb8c13b252c32deb31..c0dcfca9b5b55ed0b2e8737bf33823b915aae690 100644 (file)
@@ -2290,6 +2290,20 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        }
 },
 
+/* Native Instruments MK2 series */
+{
+       /* Traktor Audio 6 */
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+       .idVendor = 0x17cc,
+       .idProduct = 0x1010,
+},
+{
+       /* Traktor Audio 10 */
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+       .idVendor = 0x17cc,
+       .idProduct = 0x1020,
+},
+
 /* Miditech devices */
 {
        USB_DEVICE(0x4752, 0x0011),
index e314cdb85003e116ab2aceefd802bb7e42fc06f8..355759bad581057e8fefb8e78c36806fc6b5a9cc 100644 (file)
@@ -424,6 +424,34 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
        return 0;
 }
 
+/*
+ * Some sound cards from Native Instruments are in fact compliant to the USB
+ * audio standard of version 2 and other approved USB standards, even though
+ * they come up as vendor-specific device when first connected.
+ *
+ * However, they can be told to come up with a new set of descriptors
+ * upon their next enumeration, and the interfaces announced by the new
+ * descriptors will then be handled by the kernel's class drivers. As the
+ * product ID will also change, no further checks are required.
+ */
+
+static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev)
+{
+       int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                 0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                                 cpu_to_le16(1), 0, NULL, 0, 1000);
+
+       if (ret < 0)
+               return ret;
+
+       usb_reset_device(dev);
+
+       /* return -EAGAIN, so the creation of an audio interface for this
+        * temporary device is aborted. The device will reconnect with a
+        * new product ID */
+       return -EAGAIN;
+}
+
 /*
  * Setup quirks
  */
@@ -489,27 +517,33 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
        u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
                        le16_to_cpu(dev->descriptor.idProduct));
 
-       /* SB Extigy needs special boot-up sequence */
-       /* if more models come, this will go to the quirk list. */
-       if (id == USB_ID(0x041e, 0x3000))
+       switch (id) {
+       case USB_ID(0x041e, 0x3000):
+               /* SB Extigy needs special boot-up sequence */
+               /* if more models come, this will go to the quirk list. */
                return snd_usb_extigy_boot_quirk(dev, intf);
 
-       /* SB Audigy 2 NX needs its own boot-up magic, too */
-       if (id == USB_ID(0x041e, 0x3020))
+       case USB_ID(0x041e, 0x3020):
+               /* SB Audigy 2 NX needs its own boot-up magic, too */
                return snd_usb_audigy2nx_boot_quirk(dev);
 
-       /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
-       if (id == USB_ID(0x10f5, 0x0200))
+       case USB_ID(0x10f5, 0x0200):
+               /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
                return snd_usb_cm106_boot_quirk(dev);
 
-       /* C-Media CM6206 / CM106-Like Sound Device */
-       if (id == USB_ID(0x0d8c, 0x0102))
+       case USB_ID(0x0d8c, 0x0102):
+               /* C-Media CM6206 / CM106-Like Sound Device */
                return snd_usb_cm6206_boot_quirk(dev);
 
-       /* Access Music VirusTI Desktop */
-       if (id == USB_ID(0x133e, 0x0815))
+       case USB_ID(0x133e, 0x0815):
+               /* Access Music VirusTI Desktop */
                return snd_usb_accessmusic_boot_quirk(dev);
 
+       case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
+       case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
+               return snd_usb_nativeinstruments_boot_quirk(dev);
+       }
+
        return 0;
 }
 
index 6e66fffe87f57096bbcb9abf1cbe453d075400ab..32f2a97f2f14f7494d07d5c4e84f1230aa283797 100644 (file)
@@ -34,10 +34,14 @@ struct snd_usb_audio {
        int index;
        struct usb_device *dev;
        struct snd_card *card;
+       struct usb_interface *pm_intf;
        u32 usb_id;
-       int shutdown;
        struct mutex shutdown_mutex;
+       unsigned int shutdown:1;
+       unsigned int probing:1;
+       unsigned int autosuspended:1;   
        unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+       
        int num_interfaces;
        int num_suspended_intf;