linux-kirkwood: added sound driver for openrd-client
authorFrans Meulenbroeks <fransmeulenbroeks@gmail.com>
Sun, 29 Nov 2009 20:29:37 +0000 (21:29 +0100)
committerFrans Meulenbroeks <fransmeulenbroeks@gmail.com>
Sun, 29 Nov 2009 20:29:37 +0000 (21:29 +0100)
conf/machine/include/kirkwood.inc
recipes/linux/linux-kirkwood/0003-ARM-Kirkwood-Sound-Sound-driver-added.patch [new file with mode: 0644]
recipes/linux/linux-kirkwood_2.6.32-rc4.bb
recipes/php/php_5.2.6.bb

index f91607c..2d48c8b 100644 (file)
@@ -11,7 +11,7 @@ USE_DEVFS = "0"
 PREFERRED_PROVIDER_virtual/bootloader = ""
 PREFERRED_PROVIDER_virtual/kernel = "linux-kirkwood"
 
-MACHINE_KERNEL_PR = "r12"
+MACHINE_KERNEL_PR = "r13"
 
 IMAGE_FSTYPES += "tar.gz ubi"
 SERIAL_CONSOLE = "ttyS0 115200"
diff --git a/recipes/linux/linux-kirkwood/0003-ARM-Kirkwood-Sound-Sound-driver-added.patch b/recipes/linux/linux-kirkwood/0003-ARM-Kirkwood-Sound-Sound-driver-added.patch
new file mode 100644 (file)
index 0000000..fc17a08
--- /dev/null
@@ -0,0 +1,3514 @@
+From 89aa6dd15306a1ce11da0f2cb67bda74999e178e Mon Sep 17 00:00:00 2001
+From: Tanmay Upadhyay <tanmay.upadhyay@einfochips.com>
+Date: Tue, 24 Nov 2009 21:49:24 +0530
+Subject: [PATCH] ARM: Kirkwood: Sound: Sound driver added
+
+The driver is based on the Marvell kirkwood sound driver available in
+2.6.22.18 kernel.
+
+Signed-off-by: Tanmay Upadhyay <tanmay.upadhyay@einfochips.com>
+---
+ arch/arm/mach-kirkwood/common.c                |   39 +
+ arch/arm/mach-kirkwood/common.h                |    2 +
+ arch/arm/mach-kirkwood/include/mach/kirkwood.h |    3 +
+ arch/arm/mach-kirkwood/openrd_client-setup.c   |   27 +
+ include/linux/mv88fx_audio.h                   |  111 ++
+ sound/soc/Kconfig                              |    1 +
+ sound/soc/Makefile                             |    1 +
+ sound/soc/kirkwood/Kconfig                     |   29 +
+ sound/soc/kirkwood/Makefile                    |    7 +
+ sound/soc/kirkwood/cs42l51.c                   |  304 +++++
+ sound/soc/kirkwood/cs42l51.h                   |   59 +
+ sound/soc/kirkwood/kirkwood_audio_hal.c        |  821 +++++++++++++
+ sound/soc/kirkwood/kirkwood_audio_hal.h        |  109 ++
+ sound/soc/kirkwood/kirkwood_audio_regs.h       |  310 +++++
+ sound/soc/kirkwood/kirkwood_pcm.c              | 1505 ++++++++++++++++++++++++
+ 15 files changed, 3328 insertions(+), 0 deletions(-)
+ create mode 100644 include/linux/mv88fx_audio.h
+ create mode 100644 sound/soc/kirkwood/Kconfig
+ create mode 100644 sound/soc/kirkwood/Makefile
+ create mode 100644 sound/soc/kirkwood/cs42l51.c
+ create mode 100644 sound/soc/kirkwood/cs42l51.h
+ create mode 100644 sound/soc/kirkwood/kirkwood_audio_hal.c
+ create mode 100644 sound/soc/kirkwood/kirkwood_audio_hal.h
+ create mode 100644 sound/soc/kirkwood/kirkwood_audio_regs.h
+ create mode 100644 sound/soc/kirkwood/kirkwood_pcm.c
+
+diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
+index 0acb61f..4d66c06 100644
+--- a/arch/arm/mach-kirkwood/common.c
++++ b/arch/arm/mach-kirkwood/common.c
+@@ -15,6 +15,7 @@
+ #include <linux/mbus.h>
+ #include <linux/mv643xx_eth.h>
+ #include <linux/mv643xx_i2c.h>
++#include <linux/mv88fx_audio.h>
+ #include <linux/ata_platform.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/spi/orion_spi.h>
+@@ -969,3 +970,41 @@ static int __init kirkwood_clock_gate(void)
+       return 0;
+ }
+ late_initcall(kirkwood_clock_gate);
++
++/*****************************************************************************
++ * Audio
++ ****************************************************************************/
++
++static struct resource kirkwood_audio_resources[] = {
++      [0] = {
++              .start  = AUDIO_PHYS_BASE,
++              .end    = AUDIO_PHYS_BASE + SZ_16K - 1,
++              .flags  = IORESOURCE_MEM,
++      },
++      [1] = {
++              .start  = IRQ_KIRKWOOD_I2S,
++              .end    = IRQ_KIRKWOOD_I2S,
++              .flags  = IORESOURCE_IRQ,
++      },
++};
++
++static u64 kirkwood_audio_dmamask = 0xFFFFFFFFUL;
++
++static struct platform_device kirkwood_audio = {
++      .name           = MV88FX_AUDIO_NAME,
++      .id             = -1,
++      .num_resources  = ARRAY_SIZE(kirkwood_audio_resources),
++      .resource       = kirkwood_audio_resources,
++      .dev            = {
++              .dma_mask = &kirkwood_audio_dmamask,
++              .coherent_dma_mask = 0xffffffff,
++      },
++};
++
++void __init kirkwood_audio_init(struct mv88fx_snd_platform_data *audio_data)
++{
++      kirkwood_clk_ctrl |= CGC_AUDIO;
++      kirkwood_audio.dev.platform_data = audio_data;
++
++      platform_device_register(&kirkwood_audio);
++}
+diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h
+index d7de434..b79a25c 100644
+--- a/arch/arm/mach-kirkwood/common.h
++++ b/arch/arm/mach-kirkwood/common.h
+@@ -16,6 +16,7 @@ struct mv643xx_eth_platform_data;
+ struct mv_sata_platform_data;
+ struct mvsdio_platform_data;
+ struct mtd_partition;
++struct mv88fx_snd_platform_data;
+ /*
+  * Basic Kirkwood init functions used early by machine-setup.
+@@ -41,6 +42,7 @@ void kirkwood_i2c_init(void);
+ void kirkwood_uart0_init(void);
+ void kirkwood_uart1_init(void);
+ void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay);
++void kirkwood_audio_init(struct mv88fx_snd_platform_data *audio_data);
+ extern int kirkwood_tclk;
+ extern struct sys_timer kirkwood_timer;
+diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
+index 54c1327..90ced65 100644
+--- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h
++++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
+@@ -95,6 +95,9 @@
+ #define SDIO_PHYS_BASE                (KIRKWOOD_REGS_PHYS_BASE | 0x90000)
++#define AUDIO_PHYS_BASE               (KIRKWOOD_REGS_PHYS_BASE | 0xA0000)
++#define AUDIO_VIRT_BASE               (KIRKWOOD_REGS_VIRT_BASE | 0xA0000)
++
+ /*
+  * Supported devices and revisions.
+  */
+diff --git a/arch/arm/mach-kirkwood/openrd_client-setup.c b/arch/arm/mach-kirkwood/openrd_client-setup.c
+index a55a1bc..72acc22 100644
+--- a/arch/arm/mach-kirkwood/openrd_client-setup.c
++++ b/arch/arm/mach-kirkwood/openrd_client-setup.c
+@@ -14,11 +14,13 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/ata_platform.h>
+ #include <linux/mv643xx_eth.h>
++#include <linux/mv88fx_audio.h>
+ #include <linux/gpio.h>
+ #include <asm/mach-types.h>
+ #include <asm/mach/arch.h>
+ #include <mach/kirkwood.h>
+ #include <plat/mvsdio.h>
++#include <linux/autoconf.h>
+ #include "common.h"
+ #include "mpp.h"
+@@ -59,6 +61,21 @@ static unsigned int openrd_client_mpp_config[] __initdata = {
+       0
+ };
++static struct mv88fx_snd_platform_data openrd_client_audio_data = {
++      .i2c_bus_no  = 0,
++      .i2c_address = 0x4A,
++/* 0 - NA, 1 - mono, 2 - stereo */
++#ifdef CONFIG_SND_MV88FX_SOC_I2S
++      .i2s_rec     = 1,
++      .i2s_play    = 2,
++#else
++      .spdif_rec   = 1,
++      .spdif_play  = 2,
++#endif
++      .dram        = &kirkwood_mbus_dram_info,
++      .base_offset = AUDIO_PHYS_BASE - KIRKWOOD_REGS_PHYS_BASE,
++};
++
+ static void __init openrd_client_init(void)
+ {
+       /*
+@@ -78,6 +95,16 @@ static void __init openrd_client_init(void)
+       kirkwood_sata_init(&openrd_client_sata_data);
+       kirkwood_sdio_init(&openrd_client_mvsdio_data);
++
++      /* initialize i2c */
++      kirkwood_i2c_init();
++
++#if defined(CONFIG_SND_MV88FX_SOC) || defined(CONFIG_SND_MV88FX_SOC_MODULE)
++      /* If built as a part of kernel or as a module
++       * initialize audio */
++      openrd_client_audio_data.tclk = kirkwood_tclk,
++      kirkwood_audio_init(&openrd_client_audio_data);
++#endif
+ }
+ MACHINE_START(OPENRD_CLIENT, "Marvell OpenRD Client Board")
+diff --git a/include/linux/mv88fx_audio.h b/include/linux/mv88fx_audio.h
+new file mode 100644
+index 0000000..6d36a3f
+--- /dev/null
++++ b/include/linux/mv88fx_audio.h
+@@ -0,0 +1,111 @@
++/*
++ *
++ *    Marvell Orion Alsa Sound driver
++ *
++ *    Author: Maen Suleiman
++ *    Copyright (C) 2008 Marvell Ltd.
++ *
++ *
++ * 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 __LINUX_MV88FX_SND_H
++#define __LINUX_MV88FX_SND_H
++
++#include <linux/mbus.h>
++
++#define MV88FX_AUDIO_NAME   "mv88fx_snd"
++
++#undef MV88FX_SND_DEBUG
++#ifdef MV88FX_SND_DEBUG
++#define mv88fx_snd_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
++#else
++      #define mv88fx_snd_debug(a...)
++#endif
++
++#define MV_AUDIO_MAX_ADDR_DECODE_WIN      2
++#define MV_AUDIO_RECORD_WIN_NUM           0
++#define MV_AUDIO_PLAYBACK_WIN_NUM         1
++
++#define MV_AUDIO_WIN_CTRL_REG(win)        (0xA04 + ((win)<<3))
++#define MV_AUDIO_WIN_BASE_REG(win)        (0xA00 + ((win)<<3))
++
++struct mv88fx_snd_platform_data {
++      u8  i2c_bus_no;
++      u16 i2c_address;
++      u32 spdif_rec;
++      u32 spdif_play;
++      u32 i2s_rec;
++      u32 i2s_play;
++      u32 tclk;
++      u32 base_offset;
++      struct mbus_dram_target_info *dram;
++};
++
++struct mv88fx_snd_stream {
++      struct snd_pcm_substream *substream;
++      struct device          *dev;
++      int    direction;            /* playback or capture */
++      #define PLAYBACK        0
++      #define CAPTURE         1
++      unsigned int    dig_mode;    /* i2s,spdif,both */
++      #define I2S             1
++      #define SPDIF           2
++      int stereo;                  /* mono, stereo */
++      int mono_mode;               /* both mono, left mono, right mono */
++      #define MONO_BOTH       0
++      #define MONO_LEFT       1
++      #define MONO_RIGHT      2
++      int clock_src;
++      #define DCO_CLOCK       0
++      #define SPCR_CLOCK      1
++      #define EXTERN_CLOCK    2
++      int rate;
++      int stat_mem;                /* Channel status source*/
++      int format;
++      #define SAMPLE_32IN32   0
++      #define SAMPLE_24IN32   1
++      #define SAMPLE_20IN32   2
++      #define SAMPLE_16IN32   3
++      #define SAMPLE_16IN16   4
++      unsigned int  dma_addr;
++      unsigned int  dma_size;
++      unsigned int  period_size;
++      unsigned int  spdif_status[4]; /* SPDIF status */
++      unsigned char *area;           /* virtual pointer */
++      dma_addr_t    addr;            /* physical address */
++};
++
++struct mv88fx_snd_chip {
++      struct mv88fx_snd_stream *stream[2];          /* run time values*/
++      struct mv88fx_snd_stream *stream_defaults[2]; /* default values*/
++      spinlock_t      reg_lock;       /* Register access spinlock */
++      struct resource *res;           /* resource for IRQ and base*/
++      void __iomem    *base;          /* Audio base address of the host */
++      unsigned int    audio_offset;   /* Offset to audio base register
++                                       * from internal base register */
++      int irq;
++      int loopback;                   /* When Loopback is enabled, playback
++                                       * data is looped back to be recorded */
++      int ch_stat_valid;              /* Playback SPDIF channel validity bit
++                                       * value when REG selected */
++      int burst;                      /* DMA Burst Size */
++
++      #define SPDIF_MEM_STAT         0
++      #define SPDIF_REG_STAT         1
++      unsigned int dco_ctrl_offst;
++      int     pcm_mode;               /* pcm, nonpcm*/
++      #define PCM             0
++      #define NON_PCM         1
++      int     stereo;
++};
++
++#define MV88FX_SND_MIN_PERIODS                8
++#define MV88FX_SND_MAX_PERIODS                16
++#define       MV88FX_SND_MIN_PERIOD_BYTES     0x4000
++#define       MV88FX_SND_MAX_PERIOD_BYTES     0x4000
++
++#endif /* __LINUX_MV88FX_SND_H */
+diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
+index b1749bc..9fc88d8 100644
+--- a/sound/soc/Kconfig
++++ b/sound/soc/Kconfig
+@@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig"
+ source "sound/soc/s6000/Kconfig"
+ source "sound/soc/sh/Kconfig"
+ source "sound/soc/txx9/Kconfig"
++source "sound/soc/kirkwood/Kconfig"
+ # Supported codecs
+ source "sound/soc/codecs/Kconfig"
+diff --git a/sound/soc/Makefile b/sound/soc/Makefile
+index 0c5eac0..664850d 100644
+--- a/sound/soc/Makefile
++++ b/sound/soc/Makefile
+@@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC)        += s3c24xx/
+ obj-$(CONFIG_SND_SOC) += s6000/
+ obj-$(CONFIG_SND_SOC) += sh/
+ obj-$(CONFIG_SND_SOC) += txx9/
++obj-$(CONFIG_SND_SOC) += kirkwood/
+diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
+new file mode 100644
+index 0000000..d6a7e2f
+--- /dev/null
++++ b/sound/soc/kirkwood/Kconfig
+@@ -0,0 +1,29 @@
++config SND_MV88FX_SOC
++      tristate "SoC Audio for the Marvell 88FX chip"
++      depends on ARCH_KIRKWOOD
++      help
++        Say Y or M if you want to add support for codecs attached to
++        the MV88FX I2S or SPD interface. You will also need
++        to select the audio interfaces to support below.
++
++choice
++      prompt "Audio Interface"
++      default SND_MV88FX_SOC_I2S
++      depends on SND_MV88FX_SOC
++
++config SND_MV88FX_SOC_I2S
++      bool "I2S"
++
++config SND_MV88FX_SOC_SPDIF
++      bool "SPDIF"
++
++endchoice
++
++choice
++      prompt "Codec IC"
++      default SND_SOC_CS42L51
++      depends on SND_MV88FX_SOC
++
++config SND_SOC_CS42L51
++      bool "CS42L51"
++endchoice
+diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile
+new file mode 100644
+index 0000000..57674ad
+--- /dev/null
++++ b/sound/soc/kirkwood/Makefile
+@@ -0,0 +1,7 @@
++
++snd-soc-kirkwood-objs := kirkwood_pcm.o kirkwood_audio_hal.o
++ifdef CONFIG_SND_SOC_CS42L51
++snd-soc-kirkwood-objs += cs42l51.o
++endif
++
++obj-$(CONFIG_SND_MV88FX_SOC) += snd-soc-kirkwood.o
+diff --git a/sound/soc/kirkwood/cs42l51.c b/sound/soc/kirkwood/cs42l51.c
+new file mode 100644
+index 0000000..f5a22f9
+--- /dev/null
++++ b/sound/soc/kirkwood/cs42l51.c
+@@ -0,0 +1,304 @@
++/*
++ *
++ *    Marvell Orion Alsa Sound driver
++ *
++ *    Author: Maen Suleiman
++ *    Copyright (C) 2008 Marvell Ltd.
++ *
++ *
++ * 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/ioport.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/version.h>
++#include <linux/i2c.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/control.h>
++#include <sound/pcm.h>
++#include <sound/asoundef.h>
++#include <sound/asound.h>
++
++#include "cs42l51.h"
++
++/* FIXME: This code is not written in driver module style. This is written as
++ *        helper for SOC driver */
++
++struct i2c_client *client;
++
++static int cs42l51_add_i2c_device(unsigned char i2c_bus_no,
++                                unsigned short i2c_add)
++{
++      struct i2c_board_info info;
++      struct i2c_adapter *adapter;
++
++      memset(&info, 0, sizeof(struct i2c_board_info));
++      info.addr = i2c_add;
++      strlcpy(info.type, "cs42l51", I2C_NAME_SIZE);
++
++      adapter = i2c_get_adapter(i2c_bus_no);
++      if (!adapter) {
++              snd_printk("can't get i2c adapter\n");
++              return -ENODEV;
++      }
++
++      client = i2c_new_device(adapter, &info);
++      i2c_put_adapter(adapter);
++      if (!client) {
++              snd_printk("can't add i2c device\n");
++              return -ENODEV;
++      }
++
++      return 0;
++}
++
++void cs42l51_del_i2c_device(void)
++{
++      if (client)
++              i2c_unregister_device(client);
++      client = NULL;
++}
++
++/*
++ * offset: Register offset to start reading with
++ * buf   : Pointer to the buffer to store the read data
++ * num   : Number of registers to read
++ *
++ * Returns -ve errorno else number of registers read
++ */
++
++int cs42l51_reg_read(unsigned char offset, unsigned char *buf, int num)
++{
++      int ret = 0;
++
++      /* Set autoincrement bit */
++      offset |= CODEC_INCR_ADDR;
++
++      /* Send register offset */
++      ret = i2c_master_send(client, &offset, 1);
++      if (ret != 1) {
++              snd_printd("Could not write register offset\n");
++              return 0;
++      }
++
++      return i2c_master_recv(client, buf, num);
++}
++
++/*
++ * offset: Register offset to write
++ * data  : Data to be written
++ *
++ * Returns -ve errorno else number of registers written (=1)
++ */
++
++int cs42l51_reg_write(unsigned char offset, unsigned char data)
++{
++      int ret = 0;
++      unsigned char buf[2];
++
++      buf[0] = offset;
++      buf[1] = data;
++
++      /* Send register offset & data */
++      ret = i2c_master_send(client, &buf[0], 2);
++
++      return (ret == 2) ? 1 : ret;
++}
++
++int codec_init(int adc_mode, int digital_if_format,
++             unsigned char i2c_bus_no, unsigned short i2c_add)
++{
++      unsigned char reg_data;
++
++      if (cs42l51_add_i2c_device(i2c_bus_no, i2c_add))
++              return 1;
++
++      if (cs42l51_reg_read(CODEC_ID_REG, &reg_data, 1) < 0)
++              goto codec_init_error;
++
++      if (CODEC_CHIP_ID != (reg_data >> 3) ||
++          CODEC_REV_ID  != (reg_data & 0x7)) {
++              snd_printd("Error: Invalid Cirrus Logic chip/rev ID!\n");
++              return 1;
++      }
++
++      if (cs42l51_reg_read(CODEC_IF_CTRL_REG, &reg_data, 1) < 0)
++              goto codec_init_error;
++
++      reg_data = (reg_data & ~(0x7<<3)) | (digital_if_format << 3);
++
++      if (LEFT_JUSTIFIED_MODE == adc_mode)
++              reg_data &= (~0x4);
++      else
++              reg_data |= 0x4;
++
++      if (cs42l51_reg_write(CODEC_IF_CTRL_REG, reg_data) < 0)
++              goto codec_init_error;
++
++      return 0;
++
++codec_init_error:
++      snd_printd("I2C error\n");
++      return 1;
++}
++
++/*
++ * Initialize the audio decoder.
++ */
++
++int cs42l51_init(int adc_mode, int digital_if_format, int rec,
++               unsigned char i2c_bus_no, unsigned short i2c_add)
++{
++      if (codec_init(adc_mode, digital_if_format, i2c_bus_no, i2c_add)) {
++              snd_printk("Error: Audio Codec init failed\n");
++              return 1;
++      }
++
++      /* Use the signal processor */
++      if (cs42l51_reg_write(0x9, 0x40) < 0)
++              goto error;
++
++      /* Unmute PCM-A & PCM-B and set default */
++      if (cs42l51_reg_write(0x10, 0x60) < 0)
++              goto error;
++      if (cs42l51_reg_write(0x11, 0x60) < 0)
++              goto error;
++
++      /* default for AOUTx */
++      if (cs42l51_reg_write(0x16, 0x05) < 0)
++              goto error;
++      if (cs42l51_reg_write(0x17, 0x05) < 0)
++              goto error;
++
++      /* swap channels */
++      if (cs42l51_reg_write(0x18, 0xff) < 0)
++              goto error;
++
++      /* MIC Power Control: power down mIC in channel B, power on channel A
++       * Recommended seq. in datasheet:
++       *  1. Enable the PDN bit
++       *  2. Enable power-down for the selected channels
++       *  3. Disable the PDN bit */
++
++      /* Note: Tested for mono recording only */
++      if (!rec) {
++              /* Enable power down */
++              if (cs42l51_reg_write(0x2, 0x11) < 0)
++                      goto error;
++
++              /* No record - Power down both channels */
++              if (cs42l51_reg_write(0x2, 0x17) < 0)
++                      goto error;
++
++              /* Disable power down */
++              if (cs42l51_reg_write(0x2, 0x16) < 0)
++                      goto error;
++      } else {
++              if (rec == 2) {
++                      /* Setreo recording - by default both channels are up */
++
++                      /* MIC In channel selection - Select channel 3
++                       *                            unmute both channels */
++                      if (cs42l51_reg_write(0x7, 0xF0) < 0)
++                              goto error;
++
++                      /* Power up mic pre-amplifier for both channels */
++                      if (cs42l51_reg_write(0x3, 0xA0) < 0)
++                              goto error;
++              } else {
++                      /* Enable power down */
++                      if (cs42l51_reg_write(0x2, 0x11) < 0)
++                              goto error;
++
++                      /* Mono recording - Power down Channel B */
++                      if (cs42l51_reg_write(0x2, 0x15) < 0)
++                              goto error;
++
++                      /* Disable power down */
++                      if (cs42l51_reg_write(0x2, 0x14) < 0)
++                              goto error;
++
++                      /* MIC In channel selection - Select channel 3
++                       *                            Mute Channel B */
++                      if (cs42l51_reg_write(0x7, 0xF2) < 0)
++                              goto error;
++
++                      /* Power down mic pre-amplifier for Channel B*/
++                      if (cs42l51_reg_write(0x3, 0xA8) < 0)
++                              goto error;
++              }
++      }
++
++      return 0;
++error:
++      snd_printk("I2C error\n");
++      return 1;
++}
++
++#define AUD_NUM_VOLUME_STEPS  (40)
++static unsigned char auddec_volume_mapping[AUD_NUM_VOLUME_STEPS] =
++{
++      0x19,   0xB2,   0xB7,   0xBD,   0xC3,   0xC9,   0xCF,   0xD5,
++      0xD8,   0xE1,   0xE7,   0xED,   0xF3,   0xF9,   0xFF,   0x00,
++      0x01,   0x02,   0x03,   0x04,   0x05,   0x06,   0x07,   0x08,
++      0x09,   0x0A,   0x0B,   0x0C,   0x0D,   0x0E,   0x0F,   0x10,
++      0x11,   0x12,   0x13,   0x14,   0x15,   0x16,   0x17,   0x18
++};
++
++
++/*
++ * Get the audio decoder volume for both channels.
++ * 0 is lowest volume, AUD_NUM_VOLUME_STEPS-1 is the highest volume.
++ */
++
++void cs42l51_vol_get(unsigned char *vol_list)
++{
++      unsigned char reg_data[2];
++      unsigned char i, vol_idx = 0;
++
++      if (cs42l51_reg_read(0x16 + vol_idx, reg_data, 2) < 0) {
++              snd_printd("I2C error\n");
++              snd_printk("Couldn't get volume\n");
++              return;
++      }
++
++      for (; vol_idx < 2; vol_idx++) {
++              /* Look for the index that mapps to this dB value.  */
++              for (i = 0; i < AUD_NUM_VOLUME_STEPS; i++) {
++                      if (reg_data[vol_idx] == auddec_volume_mapping[i])
++                              break;
++                      if ((auddec_volume_mapping[i] >
++                            auddec_volume_mapping[AUD_NUM_VOLUME_STEPS-1])
++                          && (reg_data[vol_idx] > auddec_volume_mapping[i])
++                          && (reg_data[vol_idx] < auddec_volume_mapping[i+1]))
++                              break;
++              }
++              vol_list[vol_idx] = i;
++      }
++}
++
++/*
++ * Set the audio decoder volume for both channels.
++ * 0 is lowest volume, AUD_NUM_VOLUME_STEPS-1 is the highest volume.
++ */
++void cs42l51_vol_set(unsigned char *vol_list)
++{
++      unsigned int  vol_idx;
++
++      for (vol_idx = 0; vol_idx < 2; vol_idx++) {
++              if (vol_list[vol_idx] >= AUD_NUM_VOLUME_STEPS)
++                      vol_list[vol_idx] = AUD_NUM_VOLUME_STEPS - 1;
++
++              if (cs42l51_reg_write(0x16 + vol_idx,
++                  auddec_volume_mapping[vol_list[vol_idx]]) < 0) {
++                      snd_printd("I2C error\n");
++                      snd_printk("Couldn't set volume\n");
++                      return;
++              }
++      }
++}
+diff --git a/sound/soc/kirkwood/cs42l51.h b/sound/soc/kirkwood/cs42l51.h
+new file mode 100644
+index 0000000..f4e7951
+--- /dev/null
++++ b/sound/soc/kirkwood/cs42l51.h
+@@ -0,0 +1,59 @@
++/*
++ * Audio codec CS42L51 data definition file
++ */
++
++#ifndef _CS42L51_H_
++#define _CS42L51_H_
++
++#define CODEC_CHIP_ID   0x1B
++#define CODEC_REV_ID    0x1
++
++#define CODEC_ID_REG                    0x1
++#define CODEC_IF_CTRL_REG               0x4
++#define CODEC_ADC_INPUT_INV_MUTE_REG    0x7
++#define CODEC_DAC_OUTPUT_CTRL_REG       0x8
++#define CODEC_DAC_CTRL_REG              0x9
++#define CODEC_PGAA_VOL_CTRL_REG         0xa
++#define CODEC_TONE_CTRL_REG             0x15
++#define CODEC_VOL_OUTA_CTRL_REG         0x16
++
++/* Set bit # 7 to 1 to get into auto incremental addressing mode */
++#define CODEC_INCR_ADDR                 0x80
++
++#define FALSE 0
++#define TRUE  1
++
++/* Selects the digital interface format used for the data in on SDIN. */
++enum dac_digital_if_format {
++      L_JUSTIFIED_UP_TO_24_BIT,
++      I2S_UP_TO_24_BIT,
++      R_JUSTIFIED_UP_TO_24_BIT,
++      R_JUSTIFIED_20_BIT,
++      R_JUSTIFIED_18_BIT,
++      R_JUSTIFIED_16_BIT
++
++};
++
++/* Selects either the I2S or Left-Justified digital interface format for the
++   data on SDOUT. */
++enum adc_mode {
++      LEFT_JUSTIFIED_MODE,
++      I2S_MODE
++};
++
++/* Initialize the Cirrus Logic device */
++int cs42l51_init(int adc_mode, int digital_if_format, int rec,
++               unsigned char i2c_bus_no, unsigned short i2c_add);
++
++/* Function to control output volume (playback) */
++void cs42l51_vol_get(unsigned char *vol_list);
++void cs42l51_vol_set(unsigned char *vol_list);
++
++/* Function to access the Cirrus Logic CODEC registers */
++int cs42l51_reg_read(unsigned char offset, unsigned char *buf, int num);
++int cs42l51_reg_write(unsigned char offset, unsigned char data);
++
++
++void cs42l51_del_i2c_device(void);
++#endif  /* _CS42L51_H_ */
++
+diff --git a/sound/soc/kirkwood/kirkwood_audio_hal.c b/sound/soc/kirkwood/kirkwood_audio_hal.c
+new file mode 100644
+index 0000000..28305e3
+--- /dev/null
++++ b/sound/soc/kirkwood/kirkwood_audio_hal.c
+@@ -0,0 +1,821 @@
++/*
++ * Sound driver for Marvell Kirkwood family SOCs
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <linux/io.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <linux/spinlock.h>
++#include <linux/mv88fx_audio.h>
++#include "kirkwood_audio_hal.h"
++
++static void mv_audio_init(void __iomem *base);
++static void audio_setup_wins(void __iomem *base,
++                           struct mbus_dram_target_info *dram);
++static int set_window_as_per_baseadd(void __iomem *base, unsigned int baseadd,
++                                   unsigned int audio_offset, int win_num);
++
++/* Clocks Control and Status related*/
++static int mv_audio_dco_ctrl_set(struct mv_audio_freq_data *dcoCtrl,
++                        void __iomem *base);
++
++/* Audio PlayBack related*/
++static int mv_audio_playback_control_set(void __iomem *base, unsigned int
++                       audio_offset, struct mv_audio_playback_ctrl *ctrl);
++
++/* Audio SPDIF PlayBack related*/
++static void mv_spdif_playback_ctrl_set(void __iomem *base,
++                                      struct mv_spdif_playback_ctrl *ctrl);
++
++/* Audio I2S PlayBack related*/
++static int mv_i2s_playback_ctrl_set(void __iomem *base,
++                              struct mv_i2s_playback_ctrl *ctrl);
++
++/* Audio Recording*/
++static int  mv_audio_record_control_set(struct mv_audio_record_ctrl *ctrl,
++                              unsigned int audio_offset, void __iomem *base);
++
++/* SPDIF Recording Related*/
++static int spdif_record_tclock_set(void __iomem *base, unsigned int tclk);
++
++/* I2S Recording Related*/
++static int mv_i2s_record_cntrl_set(struct mv_i2s_record_ctrl *ctrl,
++                                 void __iomem *base);
++
++static inline int audio_burst_bytes_num_get(int burst)
++{
++      switch (burst) {
++      case AUDIO_32BYTE_BURST:
++              return 32;
++      case AUDIO_128BYTE_BURST:
++              return 128;
++      default:
++              return 0xffffffff;
++      }
++}
++
++int mv88fx_snd_hw_init(struct snd_card *card)
++{
++      void __iomem *base = chip->base;
++      struct mv88fx_snd_platform_data *platform_data =
++       card->dev->platform_data;
++
++      if (platform_data->i2s_rec || platform_data->i2s_play)
++              if (codec_init(I2S_MODE, I2S_UP_TO_24_BIT,
++                  platform_data->i2s_rec, platform_data->i2c_bus_no,
++                  platform_data->i2c_address)) {
++                      snd_printk("Initializing CS42L51 failed\n");
++                      return 1;
++      }
++
++      writel(0xffffffff, (base + MV_AUDIO_INT_CAUSE_REG));
++      writel(0, (base + MV_AUDIO_INT_MASK_REG));
++      writel(0, (base + MV_AUDIO_SPDIF_REC_INT_CAUSE_MASK_REG));
++
++      mv_audio_init(base);
++
++      audio_setup_wins(base, platform_data->dram);
++
++      /* Disable all playback/recording */
++      writel(readl(base + MV_AUDIO_PLAYBACK_CTRL_REG) &
++             (~(APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK)),
++             (base + MV_AUDIO_PLAYBACK_CTRL_REG));
++
++      writel(readl(base + MV_AUDIO_RECORD_CTRL_REG) &
++             (~(ARCR_RECORD_SPDIF_EN_MASK | ARCR_RECORD_I2S_EN_MASK)),
++             (base + MV_AUDIO_RECORD_CTRL_REG));
++
++      if (spdif_record_tclock_set(base, platform_data->tclk)) {
++              snd_printk("Marvell ALSA driver ERR. SPDIF clock set failed\n");
++              return 1;
++      }
++
++      return 0;
++}
++
++int mv88fx_snd_hw_playback_set(struct mv88fx_snd_chip *chip)
++{
++      struct mv88fx_snd_stream *audio_stream = chip->stream[PLAYBACK];
++      struct snd_pcm_substream *substream = audio_stream->substream;
++      struct snd_pcm_runtime *runtime = substream->runtime;
++
++      struct mv_audio_playback_ctrl   pcm_play_ctrl;
++      struct mv_i2s_playback_ctrl    i2s_play_ctrl;
++      struct mv_spdif_playback_ctrl  spdif_play_ctrl;
++      struct mv_audio_freq_data      dco_ctrl;
++
++      dco_ctrl.offset = chip->dco_ctrl_offst;
++
++      switch (audio_stream->rate) {
++      case 44100:
++              dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH;
++              break;
++      case 48000:
++              dco_ctrl.baseFreq = AUDIO_FREQ_48KH;
++              break;
++      case 96000:
++              dco_ctrl.baseFreq = AUDIO_FREQ_96KH;
++              break;
++      default:
++              snd_printk("Requested rate %d is not supported\n",
++                         runtime->rate); return -1;
++      }
++
++      pcm_play_ctrl.burst = (chip->burst == 128) ? AUDIO_128BYTE_BURST :
++                                                   AUDIO_32BYTE_BURST;
++
++      pcm_play_ctrl.loopBack = chip->loopback;
++
++      if (audio_stream->stereo) {
++              pcm_play_ctrl.monoMode = AUDIO_PLAY_MONO_OFF;
++      } else {
++              switch (audio_stream->mono_mode) {
++              case MONO_LEFT:
++                      pcm_play_ctrl.monoMode = AUDIO_PLAY_LEFT_MONO;
++                      break;
++              case MONO_RIGHT:
++                      pcm_play_ctrl.monoMode = AUDIO_PLAY_RIGHT_MONO;
++                      break;
++              case MONO_BOTH:
++              default:
++                      pcm_play_ctrl.monoMode = AUDIO_PLAY_BOTH_MONO;
++                      break;
++              }
++      }
++
++      if (audio_stream->format == SAMPLE_16IN16) {
++              pcm_play_ctrl.sampleSize = SAMPLE_16BIT;
++              i2s_play_ctrl.sampleSize = SAMPLE_16BIT;
++      } else if (audio_stream->format == SAMPLE_24IN32) {
++              pcm_play_ctrl.sampleSize = SAMPLE_24BIT;
++              i2s_play_ctrl.sampleSize = SAMPLE_24BIT;
++      } else if (audio_stream->format == SAMPLE_32IN32) {
++              pcm_play_ctrl.sampleSize = SAMPLE_32BIT;
++              i2s_play_ctrl.sampleSize = SAMPLE_32BIT;
++      } else {
++              snd_printk("Requested format %d is not supported\n",
++                         runtime->format);
++              return -1;
++      }
++
++      /* buffer and period sizes in frame */
++      pcm_play_ctrl.bufferPhyBase = audio_stream->dma_addr;
++      pcm_play_ctrl.bufferSize = audio_stream->dma_size;
++      pcm_play_ctrl.intByteCount = audio_stream->period_size;
++
++      /* I2S playback streem stuff */
++      /*i2s_play_ctrl.sampleSize = pcm_play_ctrl.sampleSize;*/
++      i2s_play_ctrl.justification = I2S_JUSTIFIED;
++      i2s_play_ctrl.sendLastFrame = 0;
++
++      spdif_play_ctrl.nonPcm = FALSE;
++
++      spdif_play_ctrl.validity = chip->ch_stat_valid;
++
++      if (audio_stream->stat_mem) {
++              spdif_play_ctrl.userBitsFromMemory = TRUE;
++              spdif_play_ctrl.validityFromMemory = TRUE;
++              spdif_play_ctrl.blockStartInternally = FALSE;
++      } else {
++              spdif_play_ctrl.userBitsFromMemory = FALSE;
++              spdif_play_ctrl.validityFromMemory = FALSE;
++              spdif_play_ctrl.blockStartInternally = TRUE;
++      }
++
++      /* If this is non-PCM sound, mute I2S channel */
++      spin_lock_irq(&chip->reg_lock);
++
++      if (!(readl(chip->base + MV_AUDIO_PLAYBACK_CTRL_REG) &
++            (APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK))) {
++
++              if (mv_audio_dco_ctrl_set(&dco_ctrl, chip->base)) {
++                      snd_printk("Failed to initialize DCO clock control.\n");
++                      goto error;
++              }
++      }
++
++      if (audio_stream->clock_src == DCO_CLOCK)
++              while ((readl(chip->base + MV_AUDIO_SPCR_DCO_STATUS_REG) &
++              ASDSR_DCO_LOCK_MASK) == 0)
++                      cpu_relax();
++      else if (audio_stream->clock_src == SPCR_CLOCK)
++              while ((readl(chip->base + MV_AUDIO_SPCR_DCO_STATUS_REG) &
++              ASDSR_SPCR_LOCK_MASK) == 0)
++                      cpu_relax();
++
++      if (mv_audio_playback_control_set(chip->base, chip->audio_offset,
++                                      &pcm_play_ctrl)) {
++              snd_printk("Failed to initialize PCM playback control.\n");
++              goto error;
++      }
++
++      if (mv_i2s_playback_ctrl_set(chip->base, &i2s_play_ctrl)) {
++              snd_printk("Failed to initialize I2S playback control.\n");
++              goto error;
++      }
++
++      mv_spdif_playback_ctrl_set(chip->base, &spdif_play_ctrl);
++
++      spin_unlock_irq(&chip->reg_lock);
++
++      return 0;
++error:
++      spin_unlock_irq(&chip->reg_lock);
++      return -1;
++}
++
++int mv88fx_snd_hw_capture_set(struct mv88fx_snd_chip *chip)
++{
++      struct mv88fx_snd_stream *audio_stream = chip->stream[CAPTURE];
++      struct snd_pcm_substream *substream = audio_stream->substream;
++      struct snd_pcm_runtime *runtime = substream->runtime;
++
++      struct mv_audio_record_ctrl pcm_rec_ctrl;
++      struct mv_i2s_record_ctrl   i2s_rec_ctrl;
++      struct mv_audio_freq_data   dco_ctrl;
++
++      dco_ctrl.offset = chip->dco_ctrl_offst;
++
++      switch (audio_stream->rate) {
++      case 44100:
++              dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH;
++              break;
++      case 48000:
++              dco_ctrl.baseFreq = AUDIO_FREQ_48KH;
++              break;
++      case 96000:
++              dco_ctrl.baseFreq = AUDIO_FREQ_96KH;
++              break;
++      default:
++              snd_printk("Requested rate %d is not supported\n",
++                         runtime->rate); return -1;
++      }
++
++      pcm_rec_ctrl.burst = (chip->burst == 128) ? AUDIO_128BYTE_BURST :
++                                                  AUDIO_32BYTE_BURST;
++
++      if (audio_stream->format == SAMPLE_16IN16) {
++              pcm_rec_ctrl.sampleSize = SAMPLE_16BIT;
++      } else if (audio_stream->format == SAMPLE_24IN32) {
++              pcm_rec_ctrl.sampleSize = SAMPLE_24BIT;
++      } else if (audio_stream->format == SAMPLE_32IN32) {
++              pcm_rec_ctrl.sampleSize = SAMPLE_32BIT;
++      } else {
++              snd_printk("Requested format %d is not supported\n",
++                         runtime->format);
++              return -1;
++      }
++
++      /* If request for tereo record comes on the boards that doesn't
++       * support stereo recording */
++      if ((!chip->stereo) && audio_stream->stereo) {
++              snd_printk("Stereo recording is not supported\n");
++              return -1;
++      }
++
++      pcm_rec_ctrl.mono = (audio_stream->stereo) ? FALSE : TRUE;
++
++      if (pcm_rec_ctrl.mono) {
++              switch (audio_stream->mono_mode) {
++              case MONO_LEFT:
++                      pcm_rec_ctrl.monoChannel = AUDIO_REC_LEFT_MONO;
++                      break;
++              default:
++              case MONO_RIGHT:
++                      pcm_rec_ctrl.monoChannel = AUDIO_REC_RIGHT_MONO;
++                      break;
++              }
++
++      } else {
++              pcm_rec_ctrl.monoChannel = AUDIO_REC_LEFT_MONO;
++      }
++
++
++      pcm_rec_ctrl.bufferPhyBase = audio_stream->dma_addr;
++      pcm_rec_ctrl.bufferSize = audio_stream->dma_size;
++
++      pcm_rec_ctrl.intByteCount = audio_stream->period_size;
++
++      /* I2S record streem stuff */
++      i2s_rec_ctrl.sample = pcm_rec_ctrl.sampleSize;
++      i2s_rec_ctrl.justf = I2S_JUSTIFIED;
++
++      spin_lock_irq(&chip->reg_lock);
++
++      /* set clock only if record is not enabled*/
++      if (!(readl(chip->base + MV_AUDIO_RECORD_CTRL_REG) &
++           (ARCR_RECORD_SPDIF_EN_MASK | ARCR_RECORD_I2S_EN_MASK))) {
++
++              if (mv_audio_dco_ctrl_set(&dco_ctrl, chip->base)) {
++                      snd_printk("Failed to initialize DCO clock control.\n");
++                      return -1;
++              }
++      }
++
++      if (mv_audio_record_control_set(&pcm_rec_ctrl, chip->audio_offset,
++                                      chip->base)) {
++              snd_printk("Failed to initialize PCM record control.\n");
++              return -1;
++      }
++
++      if (mv_i2s_record_cntrl_set(&i2s_rec_ctrl, chip->base)) {
++              snd_printk("Failed to initialize I2S record control.\n");
++              return -1;
++      }
++      spin_unlock_irq(&chip->reg_lock);
++
++      return 0;
++}
++
++static void mv_audio_init(void __iomem *base)
++{
++      int timeout = 10000000;
++      unsigned int reg_data;
++
++      reg_data = readl(base + 0x1200);
++      reg_data &= (~(0x333FF8));
++      reg_data |= 0x111D18;
++
++      writel(reg_data, base + 0x1200);
++
++      do {
++              timeout--;
++      } while (timeout);
++
++      reg_data = readl(base + 0x1200);
++      reg_data &= (~(0x333FF8));
++      reg_data |= 0x111D18;
++
++      writel(reg_data, base + 0x1200);
++}
++
++static void audio_setup_wins(void __iomem *base,
++                           struct mbus_dram_target_info *dram)
++{
++      int win_num;
++
++      /* First disable and clear windows */
++      for (win_num = 0; win_num < MV_AUDIO_MAX_ADDR_DECODE_WIN; win_num++) {
++              writel(0, base + MV_AUDIO_WIN_CTRL_REG(win_num));
++              writel(0, base + MV_AUDIO_WIN_BASE_REG(win_num));
++      }
++
++      /* Setup windows for DDR */
++      for (win_num = 0; win_num < MV_AUDIO_MAX_ADDR_DECODE_WIN; win_num++) {
++              /* We will set the Window to DRAM_CS1 in default */
++              struct mbus_dram_window *cs = &dram->cs[1];
++
++              writel(cs->base & 0xffff0000,
++                     base + MV_AUDIO_WIN_BASE_REG(win_num));
++              writel(((cs->size - 1) & 0xffff0000) |
++                     (cs->mbus_attr << 8) |
++                     (dram->mbus_dram_target_id << 4) | 1,
++                      base + MV_AUDIO_WIN_CTRL_REG(win_num));
++      }
++}
++
++#define MV_BOARD_TCLK_133MHZ    133333333
++#define MV_BOARD_TCLK_150MHZ    150000000
++#define MV_BOARD_TCLK_166MHZ    166666667
++#define MV_BOARD_TCLK_200MHZ    200000000
++
++static int spdif_record_tclock_set(void __iomem *base, unsigned int tclk)
++{
++      unsigned int reg_data;
++
++      reg_data = readl(base + MV_AUDIO_SPDIF_REC_GEN_REG);
++
++      switch (tclk) {
++      case MV_BOARD_TCLK_133MHZ:
++              reg_data |= ASRGR_CORE_CLK_FREQ_133MHZ;
++              break;
++      case MV_BOARD_TCLK_150MHZ:
++              reg_data |= ASRGR_CORE_CLK_FREQ_150MHZ;
++              break;
++      case MV_BOARD_TCLK_166MHZ:
++              reg_data |= ASRGR_CORE_CLK_FREQ_166MHZ;
++              break;
++      case MV_BOARD_TCLK_200MHZ:
++              reg_data |= ASRGR_CORE_CLK_FREQ_200MHZ;
++              break;
++      default:
++              snd_printk("Not supported core clock %d\n", tclk);
++              return 1;
++      }
++
++      writel(reg_data, base + MV_AUDIO_SPDIF_REC_GEN_REG);
++
++      return 0;
++}
++
++static int  mv_audio_record_control_set(struct mv_audio_record_ctrl *ctrl,
++                              unsigned int audio_offset, void __iomem *base)
++{
++      unsigned int reg, buff_start, buff_end;
++      unsigned int win_base, win_size;
++
++      if (ctrl->monoChannel > AUDIO_REC_RIGHT_MONO) {
++              snd_printk("Error: Illegal monoChannel %x\n",
++                         ctrl->monoChannel);
++
++              return 1;
++      }
++
++      if ((ctrl->burst != AUDIO_32BYTE_BURST) &&
++          (ctrl->burst != AUDIO_128BYTE_BURST)) {
++              snd_printk("Error: Illegal burst %x\n",
++                         ctrl->burst);
++
++              return 1;
++      }
++
++      if (ctrl->bufferPhyBase & (MV_AUDIO_BUFFER_MIN_ALIGN - 1)) {
++              snd_printk("Error bufferPhyBase is not aligned to 0x%x"\
++                        " bytes\n", MV_AUDIO_BUFFER_MIN_ALIGN);
++
++              return 1;
++      }
++
++      if ((ctrl->bufferSize <= audio_burst_bytes_num_get(ctrl->burst)) |
++          (ctrl->bufferSize & (audio_burst_bytes_num_get(ctrl->burst) - 1)) ||
++          (ctrl->bufferSize > AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX))) {
++              snd_printk("Error bufferSize smaller than or not multiple "\
++                        "of 0x%x bytes or larger than 0x%x\n",
++                        audio_burst_bytes_num_get(ctrl->burst),
++                        AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX));
++
++              return 1;
++      }
++
++      reg = readl(base + MV_AUDIO_RECORD_CTRL_REG);
++      reg &= ~(ARCR_RECORD_BURST_SIZE_MASK | ARCR_RECORDED_MONO_CHNL_MASK |
++               ARCR_RECORD_SAMPLE_SIZE_MASK);
++
++      switch (ctrl->sampleSize) {
++      case SAMPLE_16BIT:
++      case SAMPLE_16BIT_NON_COMPACT:
++      case SAMPLE_20BIT:
++      case SAMPLE_24BIT:
++      case SAMPLE_32BIT:
++              reg |= ctrl->sampleSize << ARCR_RECORD_SAMPLE_SIZE_OFFS;
++              break;
++      default:
++              snd_printk("Error: Illegal sampleSize %x\n",
++                         ctrl->sampleSize);
++
++              return 1;
++      }
++
++      reg |= ctrl->burst << ARCR_RECORD_BURST_SIZE_OFFS;
++      reg |= ctrl->monoChannel << ARCR_RECORDED_MONO_CHNL_OFFS;
++
++      if (ctrl->mono)
++              reg |= ARCR_RECORD_MONO_MASK;
++      else
++              reg &= (~ARCR_RECORD_MONO_MASK);
++
++      writel(reg, base + MV_AUDIO_RECORD_CTRL_REG);
++
++      /* Get the details of the Record address window*/
++      win_base = readl(base + MV_AUDIO_WIN_BASE_REG(MV_AUDIO_RECORD_WIN_NUM));
++      win_size = readl(base + MV_AUDIO_WIN_CTRL_REG(MV_AUDIO_RECORD_WIN_NUM));
++
++      /* Window size bits are 31:16. Where size =
++       * (2 ^ no of ones) * 64 KB. e.g. 0x0FF says 16MB */
++      win_size = ((win_size >> 16) + 1) << 16;
++
++      buff_start = ctrl->bufferPhyBase;
++      buff_end   = buff_start + ctrl->bufferSize - 1;
++
++      /* If buffer address is not within window boundries then try to set a
++       * new value to the Record window by geting the target of where the
++       * buffer exist, if the buffer is within the window of the new target
++       * then set the Record window to that target else return Fail
++       */
++
++      if (!(((buff_start >= win_base) &&
++           (buff_start <= (win_base + win_size - 1))) ||
++           ((buff_end >= win_base) &&
++           (buff_end <= (win_base + win_size - 1))))) {
++              snd_printd("Audio record buffer is not within window");
++
++              /* Set the window for the buffer that user require
++                 for the palyback\recording window to the target window */
++              if (set_window_as_per_baseadd(base, ctrl->bufferPhyBase,
++                              audio_offset, MV_AUDIO_RECORD_WIN_NUM)) {
++                      snd_printk("Playback buffer (%#x) is not "
++                                "within a valid target\n",
++                                ctrl->bufferPhyBase);
++                      return 1;
++              }
++      }
++
++      /* Set the interrupt byte count */
++      reg = ctrl->intByteCount & ARBCI_BYTE_COUNT_MASK;
++      writel(reg, base + MV_AUDIO_RECORD_BYTE_CNTR_INT_REG);
++
++      writel(ctrl->bufferPhyBase, base + MV_AUDIO_RECORD_START_ADDR_REG);
++      writel(AUDIO_SIZE_TO_REG(ctrl->bufferSize),
++             base + MV_AUDIO_RECORD_BUFF_SIZE_REG);
++
++
++      return 0;
++}
++
++static int mv_i2s_record_cntrl_set(struct mv_i2s_record_ctrl *ctrl,
++                                 void __iomem *base)
++{
++      unsigned int reg;
++
++      reg = readl(base + MV_AUDIO_I2S_REC_CTRL_REG);
++      reg &= ~(AIRCR_I2S_RECORD_JUSTF_MASK|AIRCR_I2S_SAMPLE_SIZE_MASK);
++
++      switch (ctrl->justf) {
++      case I2S_JUSTIFIED:
++      case LEFT_JUSTIFIED:
++      case RIGHT_JUSTIFIED:
++      case RISE_BIT_CLCK_JUSTIFIED:
++              reg |= ctrl->justf << AIRCR_I2S_RECORD_JUSTF_OFFS;
++              break;
++      default:
++              return 1;
++      }
++
++      reg |= ctrl->sample << AIRCR_I2S_SAMPLE_SIZE_OFFS;
++
++      writel(reg, base + MV_AUDIO_I2S_REC_CTRL_REG);
++      return 0;
++}
++
++/* We trust the value set in the window registers and don't check them.
++ * As there is some value already present in the register, we assume
++ * base and size are aligned */
++
++static int set_window_as_per_baseadd(void __iomem *base, unsigned int baseadd,
++                                   unsigned int audio_offset, int win_num)
++{
++      int dram_cs, win;
++      unsigned int win_base, win_size, size;
++      unsigned char dram_attr[4] = {0x0E, 0x0D, 0x0B, 0x07};
++
++      /* Base passed is Audio base address. Audio base address is
++       * Internal register base address + audio_offset */
++      void __iomem *internal_reg_base = base - audio_offset;
++
++      for (dram_cs = 0; dram_cs < 4; dram_cs++) {
++              win_base = readl(internal_reg_base + 0x1500 + (8 * dram_cs));
++              win_size = readl(internal_reg_base + 0x1504 + (8 * dram_cs));
++
++              /* skip if window is disabled */
++              if (!(win_size & 1))
++                      continue;
++
++              /* Window size bits are 31:24. Where size =
++               * (2 ^ no of ones) * 16 MB. e.g. 0x0F says 256MB */
++              size = ((win_size >> 24) + 1) << 24;
++
++              if ((baseadd >= win_base) && (baseadd < (win_base + size))) {
++                      snd_printd("DRAM window %d set for %s window",
++                                dram_cs, win_num ? "plaback" : "record");
++                      writel(win_base,
++                      base + MV_AUDIO_WIN_BASE_REG(win_num));
++
++                      /* DRAM window ctrl regs are bit different than Audio
++                       * Set size, attribute and target ID */
++                      win_size = ((size - 1) & 0xffff0000) | 1;
++                      win_size |= (dram_attr[dram_cs] << 8); /* Atribute */
++                      win_size &= (~(0xF << 4));             /* Target ID */
++
++                      writel(win_size,
++                      base + MV_AUDIO_WIN_CTRL_REG(win_num));
++                      snd_printd("win_base 0x%08x\twin_size 0x%08x",
++                                win_base, win_size);
++                      return 0;
++              }
++      }
++
++      for (win = 0; win < 8; win++) {
++              win_base = readl(internal_reg_base + 0x2004 + (0x10 * win));
++              win_size = readl(internal_reg_base + 0x2008 + (0x10 * win));
++
++              /* skip if window is disabled */
++              if (!(win_size & 1))
++                      continue;
++
++              /* Window size bits are 31:16. Where size =
++               * (2 ^ no of ones) * 64 KB. e.g. 0x0FF says 16MB */
++              size = ((win_size >> 16) + 1) << 16;
++              if ((baseadd >= win_base) && (baseadd < (win_base + size))) {
++                      snd_printd("CPU window %d set for %s window",
++                                win_base, win_num ? "plaback" : "record");
++                      writel(win_base,
++                      base + MV_AUDIO_WIN_BASE_REG(win_num));
++                      writel(win_size,
++                      base + MV_AUDIO_WIN_CTRL_REG(win_num));
++                      return 0;
++              }
++      }
++
++      return 1;
++}
++
++static int mv_audio_playback_control_set(void __iomem *base, unsigned int
++                       audio_offset, struct mv_audio_playback_ctrl *ctrl)
++{
++      unsigned int reg, buff_start, buff_end;
++      unsigned int win_base, win_size;
++
++      if (ctrl->monoMode >= AUDIO_PLAY_OTHER_MONO) {
++              snd_printk("Error: Illegal monoMode %x\n", ctrl->monoMode);
++              return 1;
++      }
++
++      if ((ctrl->burst != AUDIO_32BYTE_BURST) &&
++          (ctrl->burst != AUDIO_128BYTE_BURST)) {
++              snd_printk("Error: Illegal burst %x\n", ctrl->burst);
++              return 1;
++      }
++
++      if (ctrl->bufferPhyBase & (MV_AUDIO_BUFFER_MIN_ALIGN - 1)) {
++              snd_printk("Error, bufferPhyBase is not aligned to 0x%x "\
++                        "bytes\n", MV_AUDIO_BUFFER_MIN_ALIGN);
++              return 1;
++      }
++
++      if ((ctrl->bufferSize <= audio_burst_bytes_num_get(ctrl->burst)) ||
++          (ctrl->bufferSize & (audio_burst_bytes_num_get(ctrl->burst) - 1)) ||
++          (ctrl->bufferSize > AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX))) {
++              snd_printk("Error, bufferSize smaller than or not multiple "\
++                        "of 0x%x bytes or larger than 0x%x",
++                        audio_burst_bytes_num_get(ctrl->burst),
++                        AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX));
++              return 1;
++      }
++
++      reg = readl(base + MV_AUDIO_PLAYBACK_CTRL_REG);
++      reg &= ~(APCR_PLAY_BURST_SIZE_MASK | APCR_LOOPBACK_MASK |
++               APCR_PLAY_MONO_MASK | APCR_PLAY_SAMPLE_SIZE_MASK);
++      reg |= ctrl->burst << APCR_PLAY_BURST_SIZE_OFFS;
++      reg |= ctrl->loopBack << APCR_LOOPBACK_OFFS;
++      reg |= ctrl->monoMode << APCR_PLAY_MONO_OFFS;
++      reg |= ctrl->sampleSize << APCR_PLAY_SAMPLE_SIZE_OFFS;
++      writel(reg, base + MV_AUDIO_PLAYBACK_CTRL_REG);
++
++      /* Get the details of the Playback address window*/
++      win_base = readl(base +
++                       MV_AUDIO_WIN_BASE_REG(MV_AUDIO_PLAYBACK_WIN_NUM));
++      win_size = readl(base +
++                       MV_AUDIO_WIN_CTRL_REG(MV_AUDIO_PLAYBACK_WIN_NUM));
++
++      /* Window size bits are 31:16. Where size =
++       * (2 ^ no of ones) * 64 KB. e.g. 0x0FF says 16MB */
++      win_size = ((win_size >> 16) + 1) << 16;
++
++      buff_start = ctrl->bufferPhyBase;
++      buff_end   = buff_start + ctrl->bufferSize - 1;
++
++      /* If Playback window is not enabled or buffer address is not within
++       * window boundries then try to set a new value to the Playback window*/
++
++      if (!(((buff_start >= win_base) &&
++           (buff_start <= (win_base + win_size - 1))) ||
++           ((buff_end >= win_base) &&
++           (buff_end <= (win_base + win_size - 1))))) {
++              snd_printd("Audio playback buffer is not within window\n");
++
++              /* Set the window for the buffer that user require
++                 for the palyback\recording window to the target window */
++              if (set_window_as_per_baseadd(base, ctrl->bufferPhyBase,
++                              audio_offset, MV_AUDIO_PLAYBACK_WIN_NUM)) {
++                      snd_printk("Record buffer (%#x) is not "
++                                "within a valid target\n",
++                                ctrl->bufferPhyBase);
++                      return 1;
++              }
++      }
++
++      /* Set the interrupt byte count */
++      reg = ctrl->intByteCount & APBCI_BYTE_COUNT_MASK;
++      writel(reg, base + MV_AUDIO_PLAYBACK_BYTE_CNTR_INT_REG);
++
++      writel(ctrl->bufferPhyBase,
++             base + MV_AUDIO_PLAYBACK_BUFF_START_REG);
++      writel(AUDIO_SIZE_TO_REG(ctrl->bufferSize),
++             base + MV_AUDIO_PLAYBACK_BUFF_SIZE_REG);
++
++      return 0;
++}
++
++static int mv_i2s_playback_ctrl_set(void __iomem *base,
++                                  struct mv_i2s_playback_ctrl *ctrl)
++{
++      unsigned int reg_data;
++
++      reg_data = readl(base + MV_AUDIO_I2S_PLAY_CTRL_REG);
++      reg_data &= ~(AIPCR_I2S_PB_JUSTF_MASK | AIPCR_I2S_PB_SAMPLE_SIZE_MASK);
++
++      if (ctrl->sampleSize > SAMPLE_16BIT) {
++              snd_printk("Illigal sample size\n");
++              return 1;
++      }
++
++      reg_data |= ctrl->sampleSize << AIPCR_I2S_PB_SAMPLE_SIZE_OFFS;
++
++      if (ctrl->sendLastFrame)
++              reg_data |= AIPCR_I2S_SEND_LAST_FRM_MASK;
++      else
++              reg_data &= ~AIPCR_I2S_SEND_LAST_FRM_MASK;
++
++      switch (ctrl->justification) {
++      case I2S_JUSTIFIED:
++      case LEFT_JUSTIFIED:
++      case RIGHT_JUSTIFIED:
++              reg_data |= ctrl->justification << AIPCR_I2S_PB_JUSTF_OFFS;
++              break;
++      default:
++              snd_printk("Illigal justification value\n");
++              return 1;
++      }
++
++      writel(reg_data, base + MV_AUDIO_I2S_PLAY_CTRL_REG);
++
++      return 0;
++}
++
++static void mv_spdif_playback_ctrl_set(void __iomem *base,
++                                      struct mv_spdif_playback_ctrl *ctrl)
++{
++      unsigned int reg_data;
++
++      reg_data = readl(base + MV_AUDIO_SPDIF_PLAY_CTRL_REG);
++
++      if (ctrl->blockStartInternally)
++              reg_data |= ASPCR_SPDIF_BLOCK_START_MASK;
++      else
++              reg_data &= ~ASPCR_SPDIF_BLOCK_START_MASK;
++
++      if (ctrl->validityFromMemory)
++              reg_data |= ASPCR_SPDIF_PB_EN_MEM_VALIDITY_MASK;
++      else
++              reg_data &= ~ASPCR_SPDIF_PB_EN_MEM_VALIDITY_MASK;
++
++      if (ctrl->userBitsFromMemory)
++              reg_data |= ASPCR_SPDIF_PB_MEM_USR_EN_MASK;
++      else
++              reg_data &= ~ASPCR_SPDIF_PB_MEM_USR_EN_MASK;
++
++      if (ctrl->validity)
++              reg_data |= ASPCR_SPDIF_PB_REG_VALIDITY_MASK;
++      else
++              reg_data &= ~ASPCR_SPDIF_PB_REG_VALIDITY_MASK;
++
++      if (ctrl->nonPcm)
++              reg_data |= ASPCR_SPDIF_PB_NONPCM_MASK;
++      else
++              reg_data &= ~ASPCR_SPDIF_PB_NONPCM_MASK;
++
++      writel(reg_data, base + MV_AUDIO_SPDIF_PLAY_CTRL_REG);
++}
++
++static int mv_audio_dco_ctrl_set(struct mv_audio_freq_data *dcoCtrl,
++                               void __iomem *base)
++{
++      unsigned int reg;
++
++      /* Check parameters*/
++      if (dcoCtrl->baseFreq > AUDIO_FREQ_96KH) {
++              snd_printk("dcoCtrl->baseFreq value (0x%x) invalid\n",
++                         dcoCtrl->baseFreq);
++              return 1;
++      }
++
++      if ((dcoCtrl->offset > 0xFD0) || (dcoCtrl->offset < 0x20)) {
++              snd_printk("dcoCtrl->offset value (0x%x) invalid\n",
++                         dcoCtrl->baseFreq);
++              return 1;
++      }
++
++      reg = readl(base + MV_AUDIO_DCO_CTRL_REG);
++
++      reg &= ~(ADCR_DCO_CTRL_FS_MASK | ADCR_DCO_CTRL_OFFSET_MASK);
++      reg |= ((dcoCtrl->baseFreq << ADCR_DCO_CTRL_FS_OFFS)  |
++              (dcoCtrl->offset << ADCR_DCO_CTRL_OFFSET_OFFS));
++
++      writel(reg, base + MV_AUDIO_DCO_CTRL_REG);
++
++      return 0;
++}
+diff --git a/sound/soc/kirkwood/kirkwood_audio_hal.h b/sound/soc/kirkwood/kirkwood_audio_hal.h
+new file mode 100644
+index 0000000..ce08102
+--- /dev/null
++++ b/sound/soc/kirkwood/kirkwood_audio_hal.h
+@@ -0,0 +1,109 @@
++/*
++ * Sound driver data definition file for Marvell Kirkwood family SOCs
++ */
++
++#ifndef __AUDIO_HAL_H
++#define __AUDIO_HAL_H
++
++#include "kirkwood_audio_regs.h"
++
++#ifdef CONFIG_SND_SOC_CS42L51
++#include "cs42l51.h"
++
++#define codec_init           cs42l51_init
++#define codec_vol_get        cs42l51_vol_get
++#define codec_vol_set        cs42l51_vol_set
++#define codec_del_i2c_device cs42l51_del_i2c_device
++#endif
++
++#define CODEC_I2C_BUS_NO 0
++#define CODEC_I2C_ADD    0x4A
++
++/*********************************/
++/* General enums and structures */
++/*******************************/
++/* Type of Audio operations*/
++enum mv_audio_operation {
++      AUDIO_PLAYBACK = 0,
++      AUDIO_RECORD = 1
++};
++
++struct mv_audio_freq_data{
++      int baseFreq; /* Control FS, selects the base frequency
++                     * of the DCO */
++      u32 offset;   /* Offset control in which each step equals to
++                     * 0.9536 ppm */
++};
++
++
++/***********************************/
++/* Play Back related structures   */
++/*********************************/
++
++struct mv_audio_playback_ctrl {
++      int          burst;         /* Specifies the Burst Size of the DMA */
++      bool         loopBack;      /* When Loopback is enabled, playback data
++                                   * is looped back to be recorded */
++      int          monoMode;      /* Mono Mode is used */
++      unsigned int bufferPhyBase; /* Physical Address of DMA buffer */
++      unsigned int bufferSize;    /* Size of DMA buffer */
++      unsigned int intByteCount;  /* Number of bytes after which an
++                                   * interrupt will be issued.*/
++      int    sampleSize;          /* Playback Sample Size*/
++};
++
++struct mv_spdif_playback_ctrl {
++      bool nonPcm;                /* PCM or non-PCM mode*/
++      bool validity;              /* Validity bit value when using
++                                   * registers (userBitsFromMemory=0) */
++      bool underrunData;          /* If true send last frame on mute/pause/
++                                   * underrun otherwise send 24 binary */
++      bool userBitsFromMemory;    /* otherwise from intenal registers */
++      bool validityFromMemory;    /* otherwise from internal registers */
++      bool blockStartInternally;  /* When user and valid bits are form
++                                   * registers then this bit should be zero */
++};
++
++struct mv_i2s_playback_ctrl {
++      int  sampleSize;
++      int  justification;
++      bool sendLastFrame; /* If true send last frame on
++                           * mute/pause/underrun
++                           * otherwise send 64 binary*/
++};
++
++
++/*********************************/
++/* Recording  related structures */
++/*********************************/
++
++struct mv_audio_record_ctrl {
++      int  burst;          /* Recording DMA Burst Size */
++      int  sampleSize;     /*Recording Sample Size */
++      bool mono;           /* If true then recording mono else
++                            * recording stereo */
++      int  monoChannel;    /* Left or right moono */
++      u32  bufferPhyBase;  /* Physical Address of DMA buffer */
++      u32  bufferSize;     /* Size of DMA buffer */
++
++      u32  intByteCount;   /* Number of bytes after which an
++                            * interrupt will be issued.*/
++
++};
++
++struct mv_i2s_record_ctrl {
++      int  sample; /* I2S Recording Sample Size*/
++      int  justf;
++};
++
++/******************/
++/* Functions API */
++/****************/
++
++extern struct mv88fx_snd_chip *chip;
++
++int mv88fx_snd_hw_init(struct snd_card *card);
++int mv88fx_snd_hw_capture_set(struct mv88fx_snd_chip *chip);
++int mv88fx_snd_hw_playback_set(struct mv88fx_snd_chip *chip);
++
++#endif
+diff --git a/sound/soc/kirkwood/kirkwood_audio_regs.h b/sound/soc/kirkwood/kirkwood_audio_regs.h
+new file mode 100644
+index 0000000..1d3df15
+--- /dev/null
++++ b/sound/soc/kirkwood/kirkwood_audio_regs.h
+@@ -0,0 +1,310 @@
++/*
++ * Audio registers for Marvell Kirkwood family SOCs
++ */
++
++#ifndef __KW_AUDIO_REGS_H
++#define __KW_AUDIO_REGS_H
++
++enum mv_audio_freq {
++      AUDIO_FREQ_44_1KH = 0,        /* 11.2896Mhz */
++      AUDIO_FREQ_48KH = 1,          /* 12.288Mhz */
++      AUDIO_FREQ_96KH = 2,          /* 24.576Mhz */
++      AUDIO_FREQ_LOWER_44_1KH = 3 , /* Lower than 11.2896MHz */
++      AUDIO_FREQ_HIGHER_96KH = 4,   /* Higher than 24.576MHz */
++      AUDIO_FREQ_OTHER = 7,         /* Other frequency */
++};
++
++enum mv_audio_burst_size {
++      AUDIO_32BYTE_BURST = 1,
++      AUDIO_128BYTE_BURST = 2,
++};
++
++enum mv_audio_playback_mono {
++      AUDIO_PLAY_MONO_OFF = 0,
++      AUDIO_PLAY_LEFT_MONO = 1,
++      AUDIO_PLAY_RIGHT_MONO = 2,
++      AUDIO_PLAY_BOTH_MONO = 3,
++      AUDIO_PLAY_OTHER_MONO = 4
++};
++
++enum mv_audio_record_mono {
++      AUDIO_REC_LEFT_MONO = 0,
++      AUDIO_REC_RIGHT_MONO = 1,
++};
++
++enum mv_audio_sample_size {
++      SAMPLE_32BIT = 0,
++      SAMPLE_24BIT = 1,
++      SAMPLE_20BIT = 2,
++      SAMPLE_16BIT = 3,
++      SAMPLE_16BIT_NON_COMPACT = 7
++};
++
++enum mv_audio_i2s_justification {
++      LEFT_JUSTIFIED = 0,
++      I2S_JUSTIFIED = 5,
++      RISE_BIT_CLCK_JUSTIFIED = 7,
++      RIGHT_JUSTIFIED = 8,
++};
++
++#define APBBCR_SIZE_MAX                    0x3FFFFF
++#define APBBCR_SIZE_SHIFT                  0x2
++
++#define AUDIO_REG_TO_SIZE(reg)             (((reg) + 1) << APBBCR_SIZE_SHIFT)
++#define AUDIO_SIZE_TO_REG(size)            (((size) >> APBBCR_SIZE_SHIFT) - 1)
++
++#define MV_AUDIO_BUFFER_MIN_ALIGN          0x8
++
++/********************/
++/* Clocking Control*/
++/*******************/
++
++#define MV_AUDIO_DCO_CTRL_REG              0x1204
++#define MV_AUDIO_SPCR_DCO_STATUS_REG       0x120c
++#define MV_AUDIO_SAMPLE_CNTR_CTRL_REG      0x1220
++#define MV_AUDIO_PLAYBACK_SAMPLE_CNTR_REG  0x1224
++#define MV_AUDIO_RECORD_SAMPLE_CNTR_REG    0x1228
++#define MV_AUDIO_CLOCK_CTRL_REG            0x1230
++
++/* MV_AUDIO_DCO_CTRL_REG */
++#define ADCR_DCO_CTRL_FS_OFFS              0
++#define ADCR_DCO_CTRL_FS_MASK              (0x3 << ADCR_DCO_CTRL_FS_OFFS)
++#define ADCR_DCO_CTRL_FS_44_1KHZ           (0x0 << ADCR_DCO_CTRL_FS_OFFS)
++#define ADCR_DCO_CTRL_FS_48KHZ             (0x1 << ADCR_DCO_CTRL_FS_OFFS)
++#define ADCR_DCO_CTRL_FS_96KHZ             (0x2 << ADCR_DCO_CTRL_FS_OFFS)
++
++
++#define ADCR_DCO_CTRL_OFFSET_OFFS          2
++#define ADCR_DCO_CTRL_OFFSET_MASK          (0xfff << ADCR_DCO_CTRL_OFFSET_OFFS)
++
++/* MV_AUDIO_SPCR_DCO_STATUS_REG */
++#define ASDSR_SPCR_CTRLFS_OFFS             0
++#define ASDSR_SPCR_CTRLFS_MASK             (0x7 << ASDSR_SPCR_CTRLFS_OFFS)
++#define ASDSR_SPCR_CTRLFS_44_1KHZ          (0x0 << ASDSR_SPCR_CTRLFS_OFFS)
++#define ASDSR_SPCR_CTRLFS_48KHZ            (0x1 << ASDSR_SPCR_CTRLFS_OFFS)
++#define ASDSR_SPCR_CTRLFS_96KHZ            (0x2 << ASDSR_SPCR_CTRLFS_OFFS)
++#define ASDSR_SPCR_CTRLFS_44_1KHZ_LESS     (0x3 << ASDSR_SPCR_CTRLFS_OFFS)
++#define ASDSR_SPCR_CTRLFS_96KHZ_MORE       (0x4 << ASDSR_SPCR_CTRLFS_OFFS)
++#define ASDSR_SPCR_CTRLFS_OTHER            (0x7 << ASDSR_SPCR_CTRLFS_OFFS)
++
++
++#define ASDSR_SPCR_CTRLOFFSET_OFFS         3
++#define ASDSR_SPCR_CTRLOFFSET_MASK         (0xfff << ASDSR_SPCR_CTRLOFFSET_OFFS)
++
++#define ASDSR_SPCR_LOCK_OFFS               15
++#define ASDSR_SPCR_LOCK_MASK               (0x1 << ASDSR_SPCR_LOCK_OFFS)
++
++#define ASDSR_DCO_LOCK_OFFS                16
++#define ASDSR_DCO_LOCK_MASK                (0x1 << ASDSR_DCO_LOCK_OFFS)
++
++#define ASDSR_PLL_LOCK_OFFS                17
++#define ASDSR_PLL_LOCK_MASK                (0x1 << ASDSR_PLL_LOCK_OFFS)
++
++/*MV_AUDIO_SAMPLE_CNTR_CTRL_REG */
++
++#define ASCCR_CLR_PLAY_CNTR_OFFS           9
++#define ASCCR_CLR_PLAY_CNTR_MASK           (0x1 << ASCCR_CLR_PLAY_CNTR_OFFS)
++
++#define ASCCR_CLR_REC_CNTR_OFFS            8
++#define ASCCR_CLR_REC_CNTR_MASK            (0x1 << ASCCR_CLR_REC_CNTR_OFFS)
++
++#define ASCCR_ACTIVE_PLAY_CNTR_OFFS        1
++#define ASCCR_ACTIVE_PLAY_CNTR_MASK        (0x1 << ASCCR_ACTIVE_PLAY_CNTR_OFFS)
++
++#define ASCCR_ACTIVE_REC_CNTR_OFFS         0
++#define ASCCR_ACTIVE_REC_CNTR_MASK         (0x1 << ASCCR_ACTIVE_REC_CNTR_OFFS)
++
++/* MV_AUDIO_CLOCK_CTRL_REG */
++#define ACCR_MCLK_SOURCE_OFFS              0
++#define ACCR_MCLK_SOURCE_MASK              (0x3 << ACCR_MCLK_SOURCE_OFFS)
++#define ACCR_MCLK_SOURCE_DCO               (0x0 << ACCR_MCLK_SOURCE_OFFS)
++#define ACCR_MCLK_SOURCE_SPCR              (0x2 << ACCR_MCLK_SOURCE_OFFS)
++#define ACCR_MCLK_SOURCE_EXT               (0x3 << ACCR_MCLK_SOURCE_OFFS)
++
++
++/*********************/
++/* Interrupts       */
++/*******************/
++#define MV_AUDIO_ERROR_CAUSE_REG            0x1300
++#define MV_AUDIO_ERROR_MASK_REG             0x1304
++#define MV_AUDIO_INT_CAUSE_REG              0x1308
++#define MV_AUDIO_INT_MASK_REG               0x130C
++#define MV_AUDIO_RECORD_BYTE_CNTR_INT_REG   0x1310
++#define MV_AUDIO_PLAYBACK_BYTE_CNTR_INT_REG 0x1314
++
++/* MV_AUDIO_INT_CAUSE_REG*/
++#define AICR_RECORD_BYTES_INT               (0x1 << 13)
++#define AICR_PLAY_BYTES_INT                 (0x1 << 14)
++
++#define ARBCI_BYTE_COUNT_MASK               0xFFFFFF
++#define APBCI_BYTE_COUNT_MASK               0xFFFFFF
++
++/*********************/
++/* Audio Playback   */
++/*******************/
++/* General */
++#define MV_AUDIO_PLAYBACK_CTRL_REG           0x1100
++#define MV_AUDIO_PLAYBACK_BUFF_START_REG     0x1104
++#define MV_AUDIO_PLAYBACK_BUFF_SIZE_REG      0x1108
++#define MV_AUDIO_PLAYBACK_BUFF_BYTE_CNTR_REG 0x110c
++
++/* SPDIF */
++#define MV_AUDIO_SPDIF_PLAY_CTRL_REG         0x2204
++#define MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(ind) \
++      (0x2280 + (ind << 2))
++#define MV_AUDIO_SPDIF_PLAY_CH_STATUS_RIGHT_REG(ind) \
++      (0x22a0 + (ind << 2))
++#define MV_AUDIO_SPDIF_PLAY_USR_BITS_LEFT_REG(ind) \
++      (0x22c0 + (ind << 2))
++#define MV_AUDIO_SPDIF_PLAY_USR_BITS_RIGHT_REG(ind) \
++      (0x22e0 + (ind << 2))
++
++/* I2S */
++#define MV_AUDIO_I2S_PLAY_CTRL_REG           0x2508
++
++
++/* MV_AUDIO_PLAYBACK_CTRL_REG */
++#define APCR_PLAY_SAMPLE_SIZE_OFFS  0
++#define APCR_PLAY_SAMPLE_SIZE_MASK  (0x7 << APCR_PLAY_SAMPLE_SIZE_OFFS)
++
++#define APCR_PLAY_I2S_ENABLE_OFFS   3
++#define APCR_PLAY_I2S_ENABLE_MASK   (0x1 << APCR_PLAY_I2S_ENABLE_OFFS)
++
++#define APCR_PLAY_SPDIF_ENABLE_OFFS 4
++#define APCR_PLAY_SPDIF_ENABLE_MASK (0x1 << APCR_PLAY_SPDIF_ENABLE_OFFS)
++
++#define APCR_PLAY_MONO_OFFS         5
++#define APCR_PLAY_MONO_MASK         (0x3 << APCR_PLAY_MONO_OFFS)
++
++#define APCR_PLAY_I2S_MUTE_OFFS     7
++#define APCR_PLAY_I2S_MUTE_MASK     (0x1 << APCR_PLAY_I2S_MUTE_OFFS)
++
++#define APCR_PLAY_SPDIF_MUTE_OFFS   8
++#define APCR_PLAY_SPDIF_MUTE_MASK   (0x1 << APCR_PLAY_SPDIF_MUTE_OFFS)
++
++#define APCR_PLAY_PAUSE_OFFS        9
++#define APCR_PLAY_PAUSE_MASK        (0x1 << APCR_PLAY_PAUSE_OFFS)
++
++#define APCR_LOOPBACK_OFFS          10
++#define APCR_LOOPBACK_MASK          (0x1 << APCR_LOOPBACK_OFFS)
++
++#define APCR_PLAY_BURST_SIZE_OFFS   11
++#define APCR_PLAY_BURST_SIZE_MASK   (0x3 << APCR_PLAY_BURST_SIZE_OFFS)
++
++#define APCR_PLAY_BUSY_OFFS         16
++#define APCR_PLAY_BUSY_MASK         (0x1 << APCR_PLAY_BUSY_OFFS)
++
++/* MV_AUDIO_PLAYBACK_BUFF_BYTE_CNTR_REG */
++#define APBBCR_SIZE_MAX             0x3FFFFF
++#define APBBCR_SIZE_SHIFT           0x2
++
++
++/* MV_AUDIO_SPDIF_PLAY_CTRL_REG */
++#define ASPCR_SPDIF_BLOCK_START_OFFS  0x0
++#define ASPCR_SPDIF_BLOCK_START_MASK  (0x1 << ASPCR_SPDIF_BLOCK_START_OFFS)
++
++#define ASPCR_SPDIF_PB_EN_MEM_VALIDITY_OFFS  0x1
++#define ASPCR_SPDIF_PB_EN_MEM_VALIDITY_MASK  (0x1 << \
++                                      ASPCR_SPDIF_PB_EN_MEM_VALIDITY_OFFS)
++
++#define ASPCR_SPDIF_PB_MEM_USR_EN_OFFS 0x2
++#define ASPCR_SPDIF_PB_MEM_USR_EN_MASK (0x1 << ASPCR_SPDIF_PB_MEM_USR_EN_OFFS)
++
++#define ASPCR_SPDIF_UNDERRUN_DATA_OFFS 0x5
++#define ASPCR_SPDIF_UNDERRUN_DATA_MASK (0x1 << ASPCR_SPDIF_UNDERRUN_DATA_OFFS)
++
++#define ASPCR_SPDIF_PB_REG_VALIDITY_OFFS 16
++#define ASPCR_SPDIF_PB_REG_VALIDITY_MASK (0x1 << \
++                                      ASPCR_SPDIF_PB_REG_VALIDITY_OFFS)
++
++#define ASPCR_SPDIF_PB_NONPCM_OFFS     17
++#define ASPCR_SPDIF_PB_NONPCM_MASK     (0x1 << ASPCR_SPDIF_PB_NONPCM_OFFS)
++
++
++/* MV_AUDIO_I2S_PLAY_CTRL_REG */
++#define AIPCR_I2S_SEND_LAST_FRM_OFFS   23
++#define AIPCR_I2S_SEND_LAST_FRM_MASK   (1 << AIPCR_I2S_SEND_LAST_FRM_OFFS)
++
++#define AIPCR_I2S_PB_JUSTF_OFFS        26
++#define AIPCR_I2S_PB_JUSTF_MASK        (0xf << AIPCR_I2S_PB_JUSTF_OFFS)
++
++#define AIPCR_I2S_PB_SAMPLE_SIZE_OFFS  30
++#define AIPCR_I2S_PB_SAMPLE_SIZE_MASK  (0x3 << AIPCR_I2S_PB_SAMPLE_SIZE_OFFS)
++
++/*********************/
++/* Audio Recordnig  */
++/*******************/
++/* General */
++#define MV_AUDIO_RECORD_CTRL_REG             0x1000
++#define MV_AUDIO_RECORD_START_ADDR_REG       0x1004
++#define MV_AUDIO_RECORD_BUFF_SIZE_REG        0x1008
++#define MV_AUDIO_RECORD_BUF_BYTE_CNTR_REG    0x100C
++
++/* SPDIF */
++#define MV_AUDIO_SPDIF_REC_GEN_REG            0x2004
++#define MV_AUDIO_SPDIF_REC_INT_CAUSE_MASK_REG 0x2008
++#define MV_AUDIO_SPDIF_REC_CH_STATUS_LEFT_REG(ind) \
++      (0x2180 + ((ind) << 2))
++#define MV_AUDIO_SPDIF_REC_CH_STATUS_RIGHT_REG(ind) \
++      (0x21a0 + ((ind) << 2))
++#define MV_AUDIO_SPDIF_REC_USR_BITS_LEFT_REG(ind) \
++      (0x21c0 + ((ind) << 2))
++#define MV_AUDIO_SPDIF_REC_USR_BITS_RIGHT_REG(ind) \
++      (0x21e0 + ((ind) << 2))
++
++/* I2S */
++#define MV_AUDIO_I2S_REC_CTRL_REG            0x2408
++
++
++/* MV_AUDIO_RECORD_CTRL_REG*/
++#define ARCR_RECORD_SAMPLE_SIZE_OFFS 0
++#define ARCR_RECORD_SAMPLE_SIZE_MASK (0x7 << ARCR_RECORD_SAMPLE_SIZE_OFFS)
++
++#define ARCR_RECORDED_MONO_CHNL_OFFS 3
++#define ARCR_RECORDED_MONO_CHNL_MASK (0x1 << ARCR_RECORDED_MONO_CHNL_OFFS)
++
++#define ARCR_RECORD_MONO_OFFS        4
++#define ARCR_RECORD_MONO_MASK        (0x1 << ARCR_RECORD_MONO_OFFS)
++
++#define ARCR_RECORD_BURST_SIZE_OFFS  5
++#define ARCR_RECORD_BURST_SIZE_MASK  (0x3 << ARCR_RECORD_BURST_SIZE_OFFS)
++
++#define ARCR_RECORD_MUTE_OFFS        8
++#define ARCR_RECORD_MUTE_MASK        (0x1 << ARCR_RECORD_MUTE_OFFS)
++
++#define ARCR_RECORD_PAUSE_OFFS       9
++#define ARCR_RECORD_PAUSE_MASK       (0x1 << ARCR_RECORD_PAUSE_OFFS)
++
++#define ARCR_RECORD_I2S_EN_OFFS      10
++#define ARCR_RECORD_I2S_EN_MASK      (0x1 << ARCR_RECORD_I2S_EN_OFFS)
++
++#define ARCR_RECORD_SPDIF_EN_OFFS    11
++#define ARCR_RECORD_SPDIF_EN_MASK    (0x1 << ARCR_RECORD_SPDIF_EN_OFFS)
++
++
++/* MV_AUDIO_SPDIF_REC_GEN_REG*/
++#define ASRGR_CORE_CLK_FREQ_OFFS     1
++#define ASRGR_CORE_CLK_FREQ_MASK     (0x3 << ASRGR_CORE_CLK_FREQ_OFFS)
++#define ASRGR_CORE_CLK_FREQ_133MHZ   (0x0 << ASRGR_CORE_CLK_FREQ_OFFS)
++#define ASRGR_CORE_CLK_FREQ_150MHZ   (0x1 << ASRGR_CORE_CLK_FREQ_OFFS)
++#define ASRGR_CORE_CLK_FREQ_166MHZ   (0x2 << ASRGR_CORE_CLK_FREQ_OFFS)
++#define ASRGR_CORE_CLK_FREQ_200MHZ   (0x3 << ASRGR_CORE_CLK_FREQ_OFFS)
++
++#define ASRGR_VALID_PCM_INFO_OFFS    7
++#define ASRGR_VALID_PCM_INFO_MASK    (0x1 << ASRGR_VALID_PCM_INFO_OFFS)
++
++#define ASRGR_SAMPLE_FREQ_OFFS       8
++#define ASRGR_SAMPLE_FREQ_MASK       (0xf << ASRGR_SAMPLE_FREQ_OFFS)
++
++#define ASRGR_NON_PCM_OFFS           14
++#define ASRGR_NON_PCM_MASK           (1 << ASRGR_NON_PCM_OFFS)
++
++/* MV_AUDIO_I2S_REC_CTRL_REG*/
++#define AIRCR_I2S_RECORD_JUSTF_OFFS  26
++#define AIRCR_I2S_RECORD_JUSTF_MASK  (0xf << AIRCR_I2S_RECORD_JUSTF_OFFS)
++
++#define AIRCR_I2S_SAMPLE_SIZE_OFFS   30
++#define AIRCR_I2S_SAMPLE_SIZE_MASK   (0x3 << AIRCR_I2S_SAMPLE_SIZE_OFFS)
++
++#endif /* __KW_AUDIO_REGS_H */
++
+diff --git a/sound/soc/kirkwood/kirkwood_pcm.c b/sound/soc/kirkwood/kirkwood_pcm.c
+new file mode 100644
+index 0000000..ed35851
+--- /dev/null
++++ b/sound/soc/kirkwood/kirkwood_pcm.c
+@@ -0,0 +1,1505 @@
++/*
++ *
++ *    Marvell Orion Alsa Sound driver
++ *
++ *    Author: Maen Suleiman
++ *    Copyright (C) 2008 Marvell Ltd.
++ *
++ *
++ * 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/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/platform_device.h>
++#include <linux/mv88fx_audio.h>
++
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/pcm.h>
++#include <sound/asoundef.h>
++
++#include "kirkwood_audio_hal.h"
++
++struct mv88fx_snd_chip *chip;
++
++static int test_memory(struct mbus_dram_target_info *dram_info,
++                      unsigned int base, unsigned int size)
++{
++      unsigned int i;
++
++      for (i = 0; i <= dram_info->num_cs; i++) {
++
++              /* check if we get to end */
++              if ((dram_info->cs[i].base == 0) &&
++                  (dram_info->cs[i].size == 0))
++                      break;
++
++              /* check if we fit into one memory window only */
++              if ((base >= dram_info->cs[i].base) &&
++                  ((base + size) <= dram_info->cs[i].base +
++                                    dram_info->cs[i].size))
++                      return 1;
++      }
++
++      return 0;
++}
++
++static void devdma_hw_free(struct device *dev, struct snd_pcm_substream
++                         *substream)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      struct snd_dma_buffer *buf = runtime->dma_buffer_p;
++
++      if (runtime->dma_area == NULL)
++              return;
++
++      if (buf != &substream->dma_buffer)
++              kfree(runtime->dma_buffer_p);
++
++      snd_pcm_set_runtime_buffer(substream, NULL);
++}
++
++static int devdma_hw_alloc(struct device *dev, struct snd_pcm_substream
++                         *substream, size_t size)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      struct snd_dma_buffer *buf = runtime->dma_buffer_p;
++      struct mv88fx_snd_stream *audio_stream =
++             snd_pcm_substream_chip(substream);
++
++      int ret = 0;
++
++      if (buf) {
++              if (buf->bytes >= size) {
++                      snd_printd("buf->bytes >= size\n");
++                      goto out;
++              }
++              devdma_hw_free(dev, substream);
++      }
++
++      if (substream->dma_buffer.area != NULL &&
++          substream->dma_buffer.bytes >= size) {
++              buf = &substream->dma_buffer;
++      } else {
++              buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
++              if (!buf) {
++                      snd_printk("buf == NULL\n");
++                      goto nomem;
++              }
++
++              buf->dev.type = SNDRV_DMA_TYPE_DEV;
++              buf->dev.dev = dev;
++              buf->area = audio_stream->area;
++              buf->addr = audio_stream->addr;
++              buf->bytes = size;
++              buf->private_data = NULL;
++
++              if (!buf->area) {
++                      snd_printk("buf->area == NULL\n");
++                      goto free;
++              }
++      }
++
++      snd_pcm_set_runtime_buffer(substream, buf);
++      ret = 1;
++out:
++      runtime->dma_bytes = size;
++      return ret;
++
++free:
++      kfree(buf);
++nomem:
++      return -ENOMEM;
++}
++
++static int devdma_mmap(struct device *dev, struct snd_pcm_substream *substream,
++                      struct vm_area_struct *vma)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++      return dma_mmap_coherent(dev, vma, runtime->dma_area,
++                    runtime->dma_addr, runtime->dma_bytes);
++}
++
++/*
++ * hw preparation for spdif
++ */
++
++static int mv88fx_snd_spdif_mask_info(struct snd_kcontrol *kcontrol,
++                                    struct snd_ctl_elem_info *uinfo)
++{
++      uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++      uinfo->count = 1;
++      return 0;
++}
++
++static int mv88fx_snd_spdif_mask_get(struct snd_kcontrol *kcontrol,
++                                   struct snd_ctl_elem_value *ucontrol)
++{
++      ucontrol->value.iec958.status[0] = 0xff;
++      ucontrol->value.iec958.status[1] = 0xff;
++      ucontrol->value.iec958.status[2] = 0xff;
++      ucontrol->value.iec958.status[3] = 0xff;
++      return 0;
++}
++
++static struct snd_kcontrol_new mv88fx_snd_spdif_mask = {
++      .access = SNDRV_CTL_ELEM_ACCESS_READ,
++      .iface  = SNDRV_CTL_ELEM_IFACE_PCM,
++      .name   = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
++      .info   = mv88fx_snd_spdif_mask_info,
++      .get    = mv88fx_snd_spdif_mask_get,
++};
++
++static int mv88fx_snd_spdif_stream_info(struct snd_kcontrol *kcontrol,
++                                      struct snd_ctl_elem_info *uinfo)
++{
++      uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++      uinfo->count = 1;
++      return 0;
++}
++
++static int mv88fx_snd_spdif_stream_get(struct snd_kcontrol *kcontrol,
++                                     struct snd_ctl_elem_value *ucontrol)
++{
++      struct mv88fx_snd_chip *chip = snd_kcontrol_chip(kcontrol);
++      int i, word;
++
++      spin_lock_irq(&chip->reg_lock);
++
++      for (word = 0; word < 4; word++) {
++              chip->stream[PLAYBACK]->spdif_status[word] =
++              readl(chip->base +
++              MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(word));
++
++              for (i = 0; i < 4; i++)
++                      ucontrol->value.iec958.status[word + i] =
++                      (chip->stream[PLAYBACK]->spdif_status[word] >>
++                       (i * 8)) & 0xff;
++      }
++
++      spin_unlock_irq(&chip->reg_lock);
++      return 0;
++}
++
++static int mv88fx_snd_spdif_stream_put(struct snd_kcontrol *kcontrol,
++                                     struct snd_ctl_elem_value *ucontrol)
++{
++      struct mv88fx_snd_chip *chip = snd_kcontrol_chip(kcontrol);
++      int i, change = 0, word;
++
++      spin_lock_irq(&chip->reg_lock);
++
++      for (word = 0; word < 4; word++) {
++              for (i = 0; i < 4; i++) {
++                      chip->stream[PLAYBACK]->spdif_status[word] |=
++                      ucontrol->value.iec958.status[word + i] << (i * 8);
++              }
++
++              writel(chip->stream[PLAYBACK]->spdif_status[word],
++              chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(word));
++
++              writel(chip->stream[PLAYBACK]->spdif_status[word],
++              chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_RIGHT_REG(word));
++      }
++
++      if (chip->stream[PLAYBACK]->spdif_status[0] & IEC958_AES0_NONAUDIO)
++              chip->pcm_mode = NON_PCM;
++
++      spin_unlock_irq(&chip->reg_lock);
++
++      return change;
++}
++
++static struct snd_kcontrol_new mv88fx_snd_spdif_stream = {
++      .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
++                SNDRV_CTL_ELEM_ACCESS_INACTIVE,
++      .iface  = SNDRV_CTL_ELEM_IFACE_PCM,
++      .name   = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
++      .info   = mv88fx_snd_spdif_stream_info,
++      .get    = mv88fx_snd_spdif_stream_get,
++      .put    = mv88fx_snd_spdif_stream_put
++};
++
++
++static int mv88fx_snd_spdif_default_info(struct snd_kcontrol *kcontrol,
++                                       struct snd_ctl_elem_info *uinfo)
++{
++      uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++      uinfo->count = 1;
++      return 0;
++}
++
++static int mv88fx_snd_spdif_default_get(struct snd_kcontrol *kcontrol,
++                                      struct snd_ctl_elem_value *ucontrol)
++{
++      struct mv88fx_snd_chip *chip = snd_kcontrol_chip(kcontrol);
++      int i, word;
++
++      spin_lock_irq(&chip->reg_lock);
++
++      for (word = 0; word < 4; word++) {
++              chip->stream_defaults[PLAYBACK]->spdif_status[word] =
++              readl(chip->base +
++              MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(word));
++
++              for (i = 0; i < 4; i++)
++                      ucontrol->value.iec958.status[word + i] =
++                      (chip->stream_defaults[PLAYBACK]->spdif_status[word] >>
++                       (i * 8)) & 0xff;
++      }
++
++      spin_unlock_irq(&chip->reg_lock);
++
++      return 0;
++}
++
++static int mv88fx_snd_spdif_default_put(struct snd_kcontrol *kcontrol,
++                                      struct snd_ctl_elem_value *ucontrol)
++{
++      struct mv88fx_snd_chip *chip = snd_kcontrol_chip(kcontrol);
++      int i, change = 0, word;
++
++      spin_lock_irq(&chip->reg_lock);
++
++      for (word = 0; word < 4; word++) {
++              for (i = 0; i < 4; i++) {
++                      chip->stream_defaults[PLAYBACK]->spdif_status[word] |=
++                      ucontrol->value.iec958.status[word + i] << (i * 8);
++              }
++
++              writel(chip->stream_defaults[PLAYBACK]->spdif_status[word],
++              chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(word));
++
++              writel(chip->stream_defaults[PLAYBACK]->spdif_status[word],
++              chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_RIGHT_REG(word));
++      }
++
++      if (chip->stream_defaults[PLAYBACK]->spdif_status[0] &
++          IEC958_AES0_NONAUDIO)
++              chip->pcm_mode = NON_PCM;
++
++      spin_unlock_irq(&chip->reg_lock);
++
++      return change;
++}
++
++/* static struct snd_kcontrol_new mv88fx_snd_spdif_default __devinitdata = */
++static struct snd_kcontrol_new mv88fx_snd_spdif_default = {
++      .iface = SNDRV_CTL_ELEM_IFACE_PCM,
++      .name  = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
++      .info  = mv88fx_snd_spdif_default_info,
++      .get   = mv88fx_snd_spdif_default_get,
++      .put   = mv88fx_snd_spdif_default_put
++};
++
++unsigned char mv88fx_snd_vol[2];
++
++static int mv88fx_snd_mixer_vol_info(struct snd_kcontrol *kcontrol,
++                                   struct snd_ctl_elem_info *uinfo)
++{
++      uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++      uinfo->count = 2;
++      uinfo->value.integer.min = 0;
++      uinfo->value.integer.max = 39;
++      return 0;
++}
++
++static int mv88fx_snd_mixer_vol_get(struct snd_kcontrol *kcontrol,
++                                  struct snd_ctl_elem_value *ucontrol)
++{
++      codec_vol_get(mv88fx_snd_vol);
++
++      ucontrol->value.integer.value[0] = (long)mv88fx_snd_vol[0];
++      ucontrol->value.integer.value[1] = (long)mv88fx_snd_vol[1];
++
++      return 0;
++}
++
++static int mv88fx_snd_mixer_vol_put(struct snd_kcontrol *kcontrol,
++                                  struct snd_ctl_elem_value *ucontrol)
++{
++      mv88fx_snd_vol[0] = (unsigned char)ucontrol->value.integer.value[0];
++      mv88fx_snd_vol[1] = (unsigned char)ucontrol->value.integer.value[1];
++
++      codec_vol_set(mv88fx_snd_vol);
++
++      return 0;
++}
++
++static struct snd_kcontrol_new mv88fx_snd_dac_vol = {
++      .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++      .name  = "Playback DAC Volume",
++      .info  = mv88fx_snd_mixer_vol_info,
++      .get   = mv88fx_snd_mixer_vol_get,
++      .put   = mv88fx_snd_mixer_vol_put
++};
++
++struct mv88fx_snd_mixer_enum {
++      char **names; /* enum names*/
++      int  *values; /* values to be updated*/
++      int  count;   /* number of elements */
++      void *rec;    /* field to be updated*/
++};
++
++int mv88fx_snd_mixer_enum_info(struct snd_kcontrol *kcontrol,
++                              struct snd_ctl_elem_info *uinfo)
++{
++      struct mv88fx_snd_mixer_enum *mixer_enum =
++      (struct mv88fx_snd_mixer_enum *)kcontrol->private_value;
++
++      uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
++      uinfo->count = 1;
++      uinfo->value.enumerated.items = mixer_enum->count;
++
++      if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
++              uinfo->value.enumerated.item--;
++
++      strcpy(uinfo->value.enumerated.name,
++                      mixer_enum->names[uinfo->value.enumerated.item]);
++
++      return 0;
++}
++
++int mv88fx_snd_mixer_enum_get(struct snd_kcontrol *kcontrol,
++                             struct snd_ctl_elem_value *ucontrol)
++{
++      struct mv88fx_snd_mixer_enum *mixer_enum =
++      (struct mv88fx_snd_mixer_enum *)kcontrol->private_value;
++      int i;
++      unsigned int val;
++
++      val = *(unsigned int *)mixer_enum->rec;
++
++      for (i = 0; i < mixer_enum->count; i++) {
++
++              if (val == (unsigned int)mixer_enum->values[i]) {
++                      ucontrol->value.enumerated.item[0] = i;
++                      break;
++              }
++      }
++
++      return 0;
++}
++
++int mv88fx_snd_mixer_enum_put(struct snd_kcontrol *kcontrol,
++                             struct snd_ctl_elem_value *ucontrol)
++{
++      unsigned int val, *rec;
++      struct mv88fx_snd_mixer_enum *mixer_enum =
++      (struct mv88fx_snd_mixer_enum *)kcontrol->private_value;
++      int i;
++
++      rec = (unsigned int *)mixer_enum->rec;
++      val = ucontrol->value.enumerated.item[0];
++
++      if (val < 0)
++              val = 0;
++      if (val > mixer_enum->count)
++              val = mixer_enum->count;
++
++      for (i = 0; i < mixer_enum->count; i++) {
++
++              if (val ==  i) {
++                      *rec = (unsigned int)mixer_enum->values[i];
++                      break;
++              }
++      }
++
++      return 0;
++}
++
++#define MV88FX_PCM_MIXER_ENUM(xname, xindex, value) \
++{ .iface         = SNDRV_CTL_ELEM_IFACE_MIXER, \
++  .name          = xname, \
++  .index         = xindex, \
++  .info          = mv88fx_snd_mixer_enum_info, \
++  .get           = mv88fx_snd_mixer_enum_get, \
++  .put           = mv88fx_snd_mixer_enum_put, \
++  .private_value = (unsigned long)value, \
++}
++
++char *playback_src_mixer_names[] = {"SPDIF", "I2S", "SPDIF And I2S"};
++int playback_src_mixer_values[] = { SPDIF, I2S, (SPDIF | I2S)};
++
++struct mv88fx_snd_mixer_enum playback_src_mixer       = {
++      .names  = playback_src_mixer_names,
++      .values = playback_src_mixer_values,
++      .count  = 3,
++};
++
++char *playback_mono_mixer_names[] = {"Mono Both", "Mono Left", "Mono Right"};
++int playback_mono_mixer_values[] = { MONO_BOTH, MONO_LEFT, MONO_RIGHT};
++
++struct mv88fx_snd_mixer_enum playback_mono_mixer = {
++      .names  = playback_mono_mixer_names,
++      .values = playback_mono_mixer_values,
++      .count  = 3,
++};
++
++char *capture_src_mixer_names[] = {"SPDIF", "I2S"};
++int capture_src_mixer_values[] = { SPDIF, I2S};
++
++struct mv88fx_snd_mixer_enum capture_src_mixer = {
++      .names  = capture_src_mixer_names,
++      .values = capture_src_mixer_values,
++      .count  = 2,
++};
++
++char *capture_mono_mixer_names[] = {"Mono Left", "Mono Right"};
++int capture_mono_mixer_values[] = { MONO_LEFT, MONO_RIGHT};
++
++struct mv88fx_snd_mixer_enum capture_mono_mixer       = {
++      .names  = capture_mono_mixer_names,
++      .values = capture_mono_mixer_values,
++      .count  = 2,
++};
++
++static struct snd_kcontrol_new mv88fx_snd_mixers[] = {
++      MV88FX_PCM_MIXER_ENUM("Playback output type", 0, &playback_src_mixer),
++
++      MV88FX_PCM_MIXER_ENUM("Playback mono type", 0, &playback_mono_mixer),
++
++      MV88FX_PCM_MIXER_ENUM("Capture input Type", 0, &capture_src_mixer),
++
++      MV88FX_PCM_MIXER_ENUM("Capture mono type", 0, &capture_mono_mixer),
++};
++
++#define       PLAYBACK_MIX_INDX       0
++#define PLAYBACK_MONO_MIX_INDX  1
++#define CAPTURE_MIX_INDX        2
++#define       CAPTURE_MONO_MIX_INDX   3
++
++static int mv88fx_snd_ctrl_new(struct snd_card *card)
++{
++      struct mv88fx_snd_platform_data *pdata = card->dev->platform_data;
++      int err = 0;
++
++      playback_src_mixer.rec = &chip->stream_defaults[PLAYBACK]->dig_mode;
++      playback_mono_mixer.rec = &chip->stream_defaults[PLAYBACK]->mono_mode;
++
++      capture_src_mixer.rec = &chip->stream_defaults[CAPTURE]->dig_mode;
++      capture_mono_mixer.rec = &chip->stream_defaults[CAPTURE]->mono_mode;
++
++      if ((pdata->i2s_play) && (pdata->spdif_play)) {
++              err = snd_ctl_add(card,
++                    snd_ctl_new1(&mv88fx_snd_mixers[PLAYBACK_MIX_INDX],
++                    chip));
++              if (err < 0)
++                      return err;
++      }
++
++      err = snd_ctl_add(card,
++            snd_ctl_new1(&mv88fx_snd_mixers[PLAYBACK_MONO_MIX_INDX], chip));
++      if (err < 0)
++              return err;
++
++      if ((pdata->i2s_rec) && (pdata->spdif_rec)) {
++              err = snd_ctl_add(card,
++              snd_ctl_new1(&mv88fx_snd_mixers[CAPTURE_MIX_INDX], chip));
++              if (err < 0)
++                      return err;
++      }
++
++      err = snd_ctl_add(card, snd_ctl_new1(
++              &mv88fx_snd_mixers[CAPTURE_MONO_MIX_INDX], chip));
++      if (err < 0)
++              return err;
++
++      if (pdata->i2s_play) {
++              err = snd_ctl_add(card, snd_ctl_new1(&mv88fx_snd_dac_vol,
++                                chip));
++              if (err < 0)
++                      return err;
++      }
++
++      err = snd_ctl_add(card, snd_ctl_new1(&mv88fx_snd_spdif_mask,
++                        chip));
++      if (err < 0)
++              return err;
++
++      err = snd_ctl_add(card, snd_ctl_new1(&mv88fx_snd_spdif_default,
++                        chip));
++      if (err < 0)
++              return err;
++
++      err = snd_ctl_add(card, snd_ctl_new1(&mv88fx_snd_spdif_stream,
++                        chip));
++      return err;
++}
++
++static struct snd_pcm_hardware mv88fx_snd_capture_hw = {
++      .info = (SNDRV_PCM_INFO_INTERLEAVED |
++               SNDRV_PCM_INFO_MMAP |
++               SNDRV_PCM_INFO_MMAP_VALID |
++               SNDRV_PCM_INFO_BLOCK_TRANSFER |
++               SNDRV_PCM_INFO_PAUSE),
++
++      .formats = (SNDRV_PCM_FMTBIT_S16_LE |
++                  SNDRV_PCM_FMTBIT_S24_LE |
++                  SNDRV_PCM_FMTBIT_S32_LE),
++
++      .rates = (SNDRV_PCM_RATE_44100 |
++                SNDRV_PCM_RATE_48000 |
++                SNDRV_PCM_RATE_96000),
++
++      .rate_min = 44100,
++      .rate_max = 96000,
++      .channels_min = 1,
++      .channels_max = 2,
++      .buffer_bytes_max = (16*1024*1024),
++      .period_bytes_min = MV88FX_SND_MIN_PERIOD_BYTES,
++      .period_bytes_max = MV88FX_SND_MAX_PERIOD_BYTES,
++      .periods_min = MV88FX_SND_MIN_PERIODS,
++      .periods_max = MV88FX_SND_MAX_PERIODS,
++      .fifo_size = 0,
++};
++
++static int mv88fx_snd_capture_open(struct snd_pcm_substream *substream)
++{
++      int err;
++
++      chip->stream_defaults[CAPTURE]->substream = substream;
++      chip->stream_defaults[CAPTURE]->direction = CAPTURE;
++      substream->private_data = chip->stream_defaults[CAPTURE];
++      substream->runtime->hw = mv88fx_snd_capture_hw;
++
++      if (chip->stream_defaults[CAPTURE]->dig_mode & SPDIF)
++              substream->runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_S32_LE;
++
++      /* check if playback is already running with specific rate */
++      if (chip->stream[PLAYBACK]->rate) {
++              switch (chip->stream[PLAYBACK]->rate) {
++              case 44100:
++                      substream->runtime->hw.rates = SNDRV_PCM_RATE_44100;
++                      break;
++              case 48000:
++                      substream->runtime->hw.rates = SNDRV_PCM_RATE_48000;
++                      break;
++              case 96000:
++                      substream->runtime->hw.rates = SNDRV_PCM_RATE_96000;
++                      break;
++              }
++      }
++
++      err = snd_pcm_hw_constraint_minmax(substream->runtime,
++                                         SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++                                         chip->burst * 2,
++                                         AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX));
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_step(substream->runtime, 0,
++                                       SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++                                       chip->burst);
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_step(substream->runtime, 0,
++                                       SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++                                       chip->burst);
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_minmax(substream->runtime,
++                                         SNDRV_PCM_HW_PARAM_PERIODS,
++                                         MV88FX_SND_MIN_PERIODS,
++                                         MV88FX_SND_MAX_PERIODS);
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_integer(substream->runtime,
++                                          SNDRV_PCM_HW_PARAM_PERIODS);
++      if (err < 0)
++              return err;
++
++      return 0;
++}
++
++static int mv88fx_snd_capture_close(struct snd_pcm_substream *substream)
++{
++      chip->stream_defaults[CAPTURE]->substream = NULL;
++      memset(chip->stream[CAPTURE], 0, sizeof(struct mv88fx_snd_stream));
++
++      return 0;
++}
++
++static int mv88fx_snd_capture_hw_params(struct snd_pcm_substream *substream,
++                                      struct snd_pcm_hw_params *params)
++{
++      struct mv88fx_snd_stream *audio_stream =
++                                snd_pcm_substream_chip(substream);
++
++      return devdma_hw_alloc(audio_stream->dev, substream,
++                             params_buffer_bytes(params));
++}
++
++static int mv88fx_snd_capture_hw_free(struct snd_pcm_substream *substream)
++{
++      struct mv88fx_snd_stream *audio_stream =
++                               snd_pcm_substream_chip(substream);
++
++      /*
++       * Clear out the DMA and any allocated buffers.
++       */
++      devdma_hw_free(audio_stream->dev, substream);
++      return 0;
++}
++
++static int mv88fx_snd_capture_prepare(struct snd_pcm_substream *substream)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++
++      struct mv88fx_snd_stream *audio_stream =
++                                snd_pcm_substream_chip(substream);
++
++      audio_stream->rate = runtime->rate;
++      audio_stream->stereo = (runtime->channels == 1) ? 0 : 1;
++
++      if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
++              audio_stream->format = SAMPLE_16IN16;
++      } else if (runtime->format == SNDRV_PCM_FORMAT_S24_LE) {
++              audio_stream->format = SAMPLE_24IN32;
++      } else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE) {
++              audio_stream->format = SAMPLE_32IN32;
++      } else {
++              snd_printk("Requested format %d is not supported\n",
++                              runtime->format);
++              return -1;
++      }
++
++      /* buffer and period sizes in frame */
++      audio_stream->dma_addr = runtime->dma_addr;
++      audio_stream->dma_size = frames_to_bytes(runtime, runtime->buffer_size);
++      audio_stream->period_size =
++                               frames_to_bytes(runtime, runtime->period_size);
++
++      memcpy(chip->stream[CAPTURE], chip->stream_defaults[CAPTURE],
++             sizeof(struct mv88fx_snd_stream));
++
++      return mv88fx_snd_hw_capture_set(chip);
++}
++
++static int mv88fx_snd_capture_trigger(struct snd_pcm_substream *substream,
++                                    int cmd)
++{
++      struct mv88fx_snd_stream *audio_stream =
++                               snd_pcm_substream_chip(substream);
++      int result = 0;
++      unsigned int reg_data;
++
++      spin_lock(chip->reg_lock);
++      switch (cmd) {
++      case SNDRV_PCM_TRIGGER_START:
++              /* FIXME: should check if busy before */
++
++              /* make sure the dma in pause state*/
++              reg_data = readl(chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              reg_data |= ARCR_RECORD_PAUSE_MASK;
++              writel(reg_data, chip->base + MV_AUDIO_RECORD_CTRL_REG);
++
++              /* enable interrupt */
++              reg_data = readl(chip->base + MV_AUDIO_INT_MASK_REG);
++              reg_data |= AICR_RECORD_BYTES_INT;
++              writel(reg_data, chip->base + MV_AUDIO_INT_MASK_REG);
++
++              reg_data = readl(chip->base + MV_AUDIO_RECORD_CTRL_REG);
++
++              /* enable dma */
++              if (audio_stream->dig_mode & I2S)
++                      reg_data |= ARCR_RECORD_I2S_EN_MASK;
++
++              if (audio_stream->dig_mode & SPDIF)
++                      reg_data |= ARCR_RECORD_SPDIF_EN_MASK;
++
++              /* start dma */
++              reg_data &= (~ARCR_RECORD_PAUSE_MASK);
++              writel(reg_data, chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              break;
++
++      case SNDRV_PCM_TRIGGER_STOP:
++
++              /* make sure the dma in pause state */
++              reg_data = readl(chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              reg_data |= ARCR_RECORD_PAUSE_MASK;
++              writel(reg_data, chip->base + MV_AUDIO_RECORD_CTRL_REG);
++
++              /* disable interrupt */
++              reg_data = readl(chip->base + MV_AUDIO_INT_MASK_REG);
++              reg_data &= (~AICR_RECORD_BYTES_INT);
++              writel(reg_data, chip->base + MV_AUDIO_INT_MASK_REG);
++
++              /* always stop both I2S and SPDIF */
++              reg_data = readl(chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              reg_data &= (~(ARCR_RECORD_I2S_EN_MASK |
++                             ARCR_RECORD_SPDIF_EN_MASK));
++              writel(reg_data, chip->base + MV_AUDIO_RECORD_CTRL_REG);
++
++              /* FIXME: should check if busy after */
++
++              break;
++
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++              reg_data = readl(chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              reg_data |= ARCR_RECORD_PAUSE_MASK;
++              writel(reg_data, chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              break;
++
++      case SNDRV_PCM_TRIGGER_RESUME:
++      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++              reg_data = readl(chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              reg_data &= (~ARCR_RECORD_PAUSE_MASK);
++              writel(reg_data, chip->base + MV_AUDIO_RECORD_CTRL_REG);
++              break;
++
++      default:
++              result = -EINVAL;
++              break;
++      }
++
++      spin_unlock(&chip->reg_lock);
++      return result;
++}
++
++static snd_pcm_uframes_t mv88fx_snd_capture_pointer(struct snd_pcm_substream
++                                                  *substream)
++{
++      return bytes_to_frames(substream->runtime,
++      (ssize_t)readl(chip->base + MV_AUDIO_RECORD_BUF_BYTE_CNTR_REG));
++}
++
++int mv88fx_snd_capture_mmap(struct snd_pcm_substream *substream,
++                          struct vm_area_struct *vma)
++{
++      return devdma_mmap(NULL, substream, vma);
++}
++
++static struct snd_pcm_ops mv88fx_snd_capture_ops = {
++      .open      = mv88fx_snd_capture_open,
++      .close     = mv88fx_snd_capture_close,
++      .ioctl     = snd_pcm_lib_ioctl,
++      .hw_params = mv88fx_snd_capture_hw_params,
++      .hw_free   = mv88fx_snd_capture_hw_free,
++      .prepare   = mv88fx_snd_capture_prepare,
++      .trigger   = mv88fx_snd_capture_trigger,
++      .pointer   = mv88fx_snd_capture_pointer,
++      .mmap      = mv88fx_snd_capture_mmap,
++};
++
++struct snd_pcm_hardware mv88fx_snd_playback_hw = {
++      .info = (SNDRV_PCM_INFO_INTERLEAVED |
++               SNDRV_PCM_INFO_MMAP |
++               SNDRV_PCM_INFO_MMAP_VALID |
++               SNDRV_PCM_INFO_BLOCK_TRANSFER |
++               SNDRV_PCM_INFO_PAUSE),
++      .formats = (SNDRV_PCM_FMTBIT_S16_LE |
++                  SNDRV_PCM_FMTBIT_S24_LE |
++                  SNDRV_PCM_FMTBIT_S32_LE),
++      .rates = (SNDRV_PCM_RATE_44100 |
++                SNDRV_PCM_RATE_48000 |
++                SNDRV_PCM_RATE_96000),
++
++      .rate_min = 44100,
++      .rate_max = 96000,
++      .channels_min = 1,
++      .channels_max = 2,
++      .buffer_bytes_max = (16*1024*1024),
++      .period_bytes_min = MV88FX_SND_MIN_PERIOD_BYTES,
++      .period_bytes_max = MV88FX_SND_MAX_PERIOD_BYTES,
++      .periods_min = MV88FX_SND_MIN_PERIODS,
++      .periods_max = MV88FX_SND_MAX_PERIODS,
++      .fifo_size = 0,
++};
++
++int mv88fx_snd_playback_open(struct snd_pcm_substream *substream)
++{
++      int err = 0;
++
++      chip->stream_defaults[PLAYBACK]->substream = substream;
++      chip->stream_defaults[PLAYBACK]->direction = PLAYBACK;
++      substream->private_data = chip->stream_defaults[PLAYBACK];
++      substream->runtime->hw = mv88fx_snd_playback_hw;
++
++      if (chip->stream_defaults[PLAYBACK]->dig_mode & SPDIF)
++              substream->runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_S32_LE;
++
++      /* check if capture is already running with specific rate */
++      if (chip->stream[CAPTURE]->rate) {
++              switch (chip->stream[CAPTURE]->rate) {
++              case 44100:
++                      substream->runtime->hw.rates = SNDRV_PCM_RATE_44100;
++                      break;
++              case 48000:
++                      substream->runtime->hw.rates = SNDRV_PCM_RATE_48000;
++                      break;
++              case 96000:
++                      substream->runtime->hw.rates = SNDRV_PCM_RATE_96000;
++                      break;
++
++              }
++      }
++
++      err = snd_pcm_hw_constraint_minmax(substream->runtime,
++                                         SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++                                         chip->burst * 2,
++                                         AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX));
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_step(substream->runtime, 0,
++                                       SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++                                       chip->burst);
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_step(substream->runtime, 0,
++                                       SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++                                       chip->burst);
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_minmax(substream->runtime,
++                                         SNDRV_PCM_HW_PARAM_PERIODS,
++                                         MV88FX_SND_MIN_PERIODS,
++                                         MV88FX_SND_MAX_PERIODS);
++      if (err < 0)
++              return err;
++
++      err = snd_pcm_hw_constraint_integer(substream->runtime,
++                                          SNDRV_PCM_HW_PARAM_PERIODS);
++
++      if (err < 0)
++              return err;
++
++      return 0;
++}
++
++int mv88fx_snd_playback_close(struct snd_pcm_substream *substream)
++{
++      int i;
++
++      chip->stream_defaults[PLAYBACK]->substream = NULL;
++      chip->pcm_mode = PCM;
++
++      for (i = 0; i < 4; i++) {
++              chip->stream_defaults[PLAYBACK]->spdif_status[i] = 0;
++              chip->stream[PLAYBACK]->spdif_status[i] = 0;
++
++              writel(chip->stream_defaults[PLAYBACK]->spdif_status[i],
++                     chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(i));
++              writel(chip->stream_defaults[PLAYBACK]->spdif_status[i],
++                     chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_RIGHT_REG(i));
++      }
++
++      memset(chip->stream[PLAYBACK], 0, sizeof(struct mv88fx_snd_stream));
++
++      return 0;
++}
++
++int mv88fx_snd_playback_hw_params(struct snd_pcm_substream *substream,
++                                       struct snd_pcm_hw_params *params)
++{
++      struct mv88fx_snd_stream *audio_stream =
++                               snd_pcm_substream_chip(substream);
++
++      return devdma_hw_alloc(audio_stream->dev, substream,
++                      params_buffer_bytes(params));
++}
++
++int mv88fx_snd_playback_hw_free(struct snd_pcm_substream *substream)
++{
++      struct mv88fx_snd_stream *audio_stream =
++                               snd_pcm_substream_chip(substream);
++
++      /*
++       * Clear out the DMA and any allocated buffers.
++       */
++      devdma_hw_free(audio_stream->dev, substream);
++      return 0;
++}
++
++int mv88fx_snd_playback_prepare(struct snd_pcm_substream *substream)
++{
++      struct snd_pcm_runtime *runtime = substream->runtime;
++
++      struct mv88fx_snd_stream *audio_stream =
++                                snd_pcm_substream_chip(substream);
++
++      if ((audio_stream->dig_mode == I2S) &&
++          (chip->pcm_mode == NON_PCM))
++              return -1;
++
++      audio_stream->rate = runtime->rate;
++      audio_stream->stereo = (runtime->channels == 1) ? 0 : 1;
++
++      if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
++              audio_stream->format = SAMPLE_16IN16;
++      } else if (runtime->format == SNDRV_PCM_FORMAT_S24_LE) {
++              audio_stream->format = SAMPLE_24IN32;
++      } else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE) {
++              audio_stream->format = SAMPLE_32IN32;
++      } else {
++              snd_printk("Requested format %d is not supported\n",
++                              runtime->format);
++              return -1;
++      }
++
++      /* buffer and period sizes in frame */
++      audio_stream->dma_addr = runtime->dma_addr;
++      audio_stream->dma_size = frames_to_bytes(runtime, runtime->buffer_size);
++      audio_stream->period_size =
++                               frames_to_bytes(runtime, runtime->period_size);
++
++      memcpy(chip->stream[PLAYBACK], chip->stream_defaults[PLAYBACK],
++             sizeof(struct mv88fx_snd_stream));
++
++      return mv88fx_snd_hw_playback_set(chip);
++}
++
++int mv88fx_snd_playback_trigger(struct snd_pcm_substream *substream,
++                                     int cmd)
++{
++      struct mv88fx_snd_stream *audio_stream =
++                               snd_pcm_substream_chip(substream);
++      int result = 0;
++      unsigned int reg_data;
++
++      spin_lock(chip->reg_lock);
++      switch (cmd) {
++      case SNDRV_PCM_TRIGGER_START:
++              /* enable interrupt */
++              reg_data = readl(chip->base + MV_AUDIO_INT_MASK_REG);
++              reg_data |= AICR_PLAY_BYTES_INT;
++              writel(reg_data, chip->base + MV_AUDIO_INT_MASK_REG);
++
++              /* make sure the dma in pause state*/
++              reg_data = readl(chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++              reg_data |= APCR_PLAY_PAUSE_MASK;
++              writel(reg_data, chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++
++              /* enable dma */
++              if ((audio_stream->dig_mode & I2S) &&
++                  (chip->pcm_mode == PCM))
++                      reg_data |= APCR_PLAY_I2S_ENABLE_MASK;
++
++              if (audio_stream->dig_mode & SPDIF)
++                      reg_data |= APCR_PLAY_SPDIF_ENABLE_MASK;
++
++              /* start dma */
++              reg_data &= (~APCR_PLAY_PAUSE_MASK);
++              writel(reg_data, chip->base + MV_AUDIO_PLAYBACK_CTRL_REG);
++
++              break;
++
++      case SNDRV_PCM_TRIGGER_STOP:
++
++              /* disable interrupt */
++              reg_data = readl(chip->base + MV_AUDIO_INT_MASK_REG);
++              reg_data &= (~AICR_PLAY_BYTES_INT);
++              writel(reg_data, chip->base + MV_AUDIO_INT_MASK_REG);
++
++              /* make sure the dma in pause state*/
++              reg_data = readl(chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++              reg_data |= APCR_PLAY_PAUSE_MASK;
++
++              /* always stop both I2S and SPDIF*/
++              reg_data &= (~(APCR_PLAY_I2S_ENABLE_MASK |
++                             APCR_PLAY_SPDIF_ENABLE_MASK));
++              writel(reg_data, chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++
++              /* check if busy twice*/
++              while (readl(chip->base + MV_AUDIO_PLAYBACK_CTRL_REG) &
++              APCR_PLAY_BUSY_MASK)
++                      cpu_relax();
++              while (readl(chip->base + MV_AUDIO_PLAYBACK_CTRL_REG) &
++              APCR_PLAY_BUSY_MASK)
++                      cpu_relax();
++              break;
++
++      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++      case SNDRV_PCM_TRIGGER_SUSPEND:
++              reg_data = readl(chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++              reg_data |= APCR_PLAY_PAUSE_MASK;
++              writel(reg_data, chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++              break;
++
++      case SNDRV_PCM_TRIGGER_RESUME:
++      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++              reg_data = readl(chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++              reg_data &= (~APCR_PLAY_PAUSE_MASK);
++              writel(reg_data, chip->base +
++                               MV_AUDIO_PLAYBACK_CTRL_REG);
++
++              break;
++
++      default:
++              result = -EINVAL;
++      }
++
++      spin_unlock(&chip->reg_lock);
++      return result;
++}
++
++
++snd_pcm_uframes_t mv88fx_snd_playback_pointer(struct snd_pcm_substream
++                                                   *substream)
++{
++      return bytes_to_frames(substream->runtime,
++      (ssize_t)readl(chip->base + MV_AUDIO_PLAYBACK_BUFF_BYTE_CNTR_REG));
++}
++
++int mv88fx_snd_playback_mmap(struct snd_pcm_substream *substream,
++                           struct vm_area_struct *vma)
++{
++      return devdma_mmap(NULL, substream, vma);
++}
++
++
++struct snd_pcm_ops mv88fx_snd_playback_ops = {
++      .open      = mv88fx_snd_playback_open,
++      .close     = mv88fx_snd_playback_close,
++      .ioctl     = snd_pcm_lib_ioctl,
++      .hw_params = mv88fx_snd_playback_hw_params,
++      .hw_free   = mv88fx_snd_playback_hw_free,
++      .prepare   = mv88fx_snd_playback_prepare,
++      .trigger   = mv88fx_snd_playback_trigger,
++      .pointer   = mv88fx_snd_playback_pointer,
++      .mmap      = mv88fx_snd_playback_mmap,
++};
++
++int __init mv88fx_snd_pcm_new(struct snd_card *card)
++{
++      struct snd_pcm *pcm;
++      struct mv88fx_snd_platform_data *pdata = card->dev->platform_data;
++      int err, i;
++
++      snd_printd("card->dev=0x%x\n", (unsigned int)card->dev);
++
++      err = snd_pcm_new(card, "Marvell mv88fx_snd IEC958 and I2S", 0, 1, 1,
++                        &pcm);
++      if (err < 0)
++              return err;
++
++      snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++                      &mv88fx_snd_playback_ops);
++
++      snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++                      &mv88fx_snd_capture_ops);
++
++      if ((pdata->i2s_play) && (pdata->spdif_play))
++              chip->stream_defaults[PLAYBACK]->dig_mode = (SPDIF | I2S);
++      else if (pdata->i2s_play)
++              chip->stream_defaults[PLAYBACK]->dig_mode = I2S;
++      else if (pdata->spdif_play)
++              chip->stream_defaults[PLAYBACK]->dig_mode = SPDIF;
++      else
++              chip->stream_defaults[PLAYBACK]->dig_mode = 0;
++
++      chip->stream_defaults[PLAYBACK]->mono_mode = MONO_BOTH;
++      chip->pcm_mode = PCM;
++      chip->stream_defaults[PLAYBACK]->stat_mem = 0;
++      chip->stream_defaults[PLAYBACK]->clock_src = DCO_CLOCK;
++
++      if (pdata->i2s_rec)
++              chip->stream_defaults[CAPTURE]->dig_mode = I2S;
++      else if (pdata->spdif_rec)
++              chip->stream_defaults[CAPTURE]->dig_mode = SPDIF;
++      else
++              chip->stream_defaults[CAPTURE]->dig_mode = 0;
++
++      chip->stream_defaults[CAPTURE]->mono_mode = MONO_LEFT;
++      chip->pcm_mode = PCM;
++      chip->stream_defaults[CAPTURE]->stat_mem = 0;
++      chip->stream_defaults[CAPTURE]->clock_src = DCO_CLOCK;
++
++      for (i = 0; i < 4; i++) {
++              chip->stream_defaults[PLAYBACK]->spdif_status[i] = 0;
++              chip->stream[PLAYBACK]->spdif_status[i] = 0;
++
++              writel(chip->stream_defaults[PLAYBACK]->spdif_status[i],
++                     chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_LEFT_REG(i));
++
++              writel(chip->stream_defaults[PLAYBACK]->spdif_status[i],
++                     chip->base + MV_AUDIO_SPDIF_PLAY_CH_STATUS_RIGHT_REG(i));
++
++              writel(0, chip->base +
++                        MV_AUDIO_SPDIF_PLAY_USR_BITS_LEFT_REG(i));
++
++              writel(0, chip->base +
++                        MV_AUDIO_SPDIF_PLAY_USR_BITS_RIGHT_REG(i));
++      }
++
++      pcm->private_data = chip;
++      pcm->info_flags = 0;
++      strcpy(pcm->name, "Marvell mv88fx_snd IEC958 and I2S");
++
++      return 0;
++}
++
++irqreturn_t mv88fx_snd_interrupt(int irq, void *dev_id)
++{
++      struct mv88fx_snd_chip *chip = dev_id;
++      struct mv88fx_snd_stream *play_stream = chip->stream_defaults[PLAYBACK];
++      struct mv88fx_snd_stream *capture_stream =
++                                              chip->stream_defaults[CAPTURE];
++
++      unsigned int status, mask;
++
++      spin_lock(&chip->reg_lock);
++
++      /* read the active interrupt */
++      mask = readl(chip->base + MV_AUDIO_INT_MASK_REG);
++      status = readl(chip->base + MV_AUDIO_INT_CAUSE_REG) & mask;
++
++      do {
++              if (status & ~(AICR_RECORD_BYTES_INT|AICR_PLAY_BYTES_INT)) {
++                      spin_unlock(&chip->reg_lock);
++                      snd_BUG(); /* FIXME: should enable error interrupts*/
++                      return IRQ_NONE;
++              }
++
++              /* acknowledge interrupt */
++              writel(status, chip->base + MV_AUDIO_INT_CAUSE_REG);
++
++              /* This is record event */
++              if (status & AICR_RECORD_BYTES_INT)
++                      snd_pcm_period_elapsed(capture_stream->substream);
++
++              /* This is play event */
++              if (status & AICR_PLAY_BYTES_INT)
++                      snd_pcm_period_elapsed(play_stream->substream);
++
++              /* read the active interrupt */
++              mask = readl(chip->base + MV_AUDIO_INT_MASK_REG);
++              status = readl(chip->base + MV_AUDIO_INT_CAUSE_REG) & mask;
++      } while (status);
++
++      spin_unlock(&chip->reg_lock);
++
++      return IRQ_HANDLED;
++}
++
++void mv88fx_snd_free(struct snd_card *card)
++{
++      struct mv88fx_snd_chip *chip = card->private_data;
++
++      /* free irq */
++      free_irq(chip->irq, (void *)chip);
++
++      snd_printd("chip->res =0x%x\n", (unsigned int)chip->res);
++
++      if (chip->base)
++              iounmap(chip->base);
++
++      if (chip->res)
++              release_resource(chip->res);
++
++      chip->res = NULL;
++
++      /* Free memory allocated for streems */
++      if (chip->stream_defaults[PLAYBACK]->area)
++              dma_free_coherent(card->dev,
++                       MV88FX_SND_MAX_PERIODS * MV88FX_SND_MAX_PERIOD_BYTES,
++                       chip->stream_defaults[PLAYBACK]->area,
++                       chip->stream_defaults[PLAYBACK]->addr);
++
++      if (chip->stream_defaults[CAPTURE]->area)
++              dma_free_coherent(card->dev,
++                       MV88FX_SND_MAX_PERIODS * MV88FX_SND_MAX_PERIOD_BYTES,
++                       chip->stream_defaults[CAPTURE]->area,
++                       chip->stream_defaults[CAPTURE]->addr);
++
++      kfree(chip->stream_defaults[PLAYBACK]);
++      chip->stream_defaults[PLAYBACK] = NULL;
++
++      kfree(chip->stream[PLAYBACK]);
++      chip->stream[PLAYBACK] = NULL;
++
++      kfree(chip->stream_defaults[CAPTURE]);
++      chip->stream_defaults[CAPTURE] = NULL;
++
++      kfree(chip->stream[CAPTURE]);
++      chip->stream[CAPTURE] = NULL;
++
++      chip = NULL;
++}
++
++int mv88fx_snd_probe(struct platform_device *dev)
++{
++      int err = 0, irq = NO_IRQ;
++      struct snd_card *card = NULL;
++      struct resource *r = NULL;
++      static struct snd_device_ops ops = {
++              .dev_free =     NULL,
++      };
++      struct mv88fx_snd_platform_data *pdata = NULL;
++
++      err = snd_card_create(-1, "mv88fx_snd", THIS_MODULE,
++                     sizeof(struct mv88fx_snd_chip), &card);
++
++      if (err) {
++              snd_printk("snd_card_create failed\n");
++              return err;
++      }
++
++      card->dev = &dev->dev;
++      chip = card->private_data;
++      card->private_free = mv88fx_snd_free;
++
++      pdata = (struct mv88fx_snd_platform_data *)dev->dev.platform_data;
++
++      if (pdata->i2s_rec == 2 || pdata->spdif_rec == 2)
++              chip->stereo = 1;
++      else
++              chip->stereo = 0;
++
++      chip->audio_offset = pdata->base_offset;
++
++      r = platform_get_resource(dev, IORESOURCE_MEM, 0);
++      if (!r) {
++              snd_printk("platform_get_resource failed\n");
++              err = -ENXIO;
++              goto error;
++      }
++
++      snd_printd("chip->res =0x%x\n", (unsigned int)chip->res);
++
++      r = request_mem_region(r->start, SZ_16K, MV88FX_AUDIO_NAME);
++      if (!r) {
++              snd_printk("request_mem_region failed\n");
++              err = -EBUSY;
++              goto error;
++      }
++      chip->res = r;
++
++      chip->base = ioremap(r->start, SZ_16K);
++
++      if (!chip->base) {
++              snd_printk("ioremap failed\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      snd_printd("chip->base=0x%x r->start0x%x\n",
++                       (unsigned int)chip->base, r->start);
++
++      irq = platform_get_irq(dev, 0);
++      if (irq == NO_IRQ) {
++              snd_printk("platform_get_irq failed\n");
++              err = -ENXIO;
++              goto error;
++      }
++
++      snd_printd("card = 0x%x dev 0x%x\n",
++                       (unsigned int)card, (unsigned int)dev);
++      strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
++
++      /* Allocate memory for our device */
++      chip->stream_defaults[PLAYBACK] =
++                        kzalloc(sizeof(struct mv88fx_snd_stream), GFP_KERNEL);
++
++      if (chip->stream_defaults[PLAYBACK] == NULL) {
++              snd_printk("kzalloc failed for default playback\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      chip->stream_defaults[PLAYBACK]->direction = PLAYBACK;
++      chip->stream_defaults[PLAYBACK]->dev = card->dev;
++
++      chip->stream[PLAYBACK] = kzalloc(sizeof(struct mv88fx_snd_stream),
++                                       GFP_KERNEL);
++
++      if (chip->stream[PLAYBACK] == NULL) {
++              snd_printk("kzalloc failed for runtime playback\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      chip->stream_defaults[CAPTURE] =
++                       kzalloc(sizeof(struct mv88fx_snd_stream), GFP_KERNEL);
++
++      if (chip->stream_defaults[CAPTURE] == NULL) {
++              snd_printk("kzalloc failed for capture\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      chip->stream_defaults[CAPTURE]->direction = CAPTURE;
++      chip->stream_defaults[CAPTURE]->dev = card->dev;
++
++      chip->stream[CAPTURE] = kzalloc(sizeof(struct mv88fx_snd_stream),
++                                      GFP_KERNEL);
++
++      if (chip->stream[CAPTURE] == NULL) {
++              snd_printk("kzalloc failed for runtime capture\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      chip->irq = irq;
++      chip->stream_defaults[PLAYBACK]->area =
++      dma_alloc_coherent(&dev->dev,
++                         MV88FX_SND_MAX_PERIODS * MV88FX_SND_MAX_PERIOD_BYTES,
++                         &chip->stream_defaults[PLAYBACK]->addr,
++                         GFP_KERNEL);
++
++      if (!chip->stream_defaults[PLAYBACK]->area) {
++              snd_printk("dma_alloc_coherent failed for playback buffer\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      if (0 == test_memory(pdata->dram,
++        (unsigned int)chip->stream_defaults[PLAYBACK]->addr,
++        (unsigned int)MV88FX_SND_MAX_PERIODS * MV88FX_SND_MAX_PERIOD_BYTES)) {
++
++              snd_printk("error: playback buffer not in one memory window\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      chip->stream_defaults[CAPTURE]->area =
++      dma_alloc_coherent(&dev->dev,
++                         MV88FX_SND_MAX_PERIODS * MV88FX_SND_MAX_PERIOD_BYTES,
++                         &chip->stream_defaults[CAPTURE]->addr,
++                         GFP_KERNEL);
++
++      if (!chip->stream_defaults[CAPTURE]->area) {
++              snd_printk("dma_alloc_coherent failed for capture buffer\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      if (0 == test_memory(pdata->dram,
++        (unsigned int)chip->stream_defaults[CAPTURE]->addr,
++        (unsigned int)MV88FX_SND_MAX_PERIODS * MV88FX_SND_MAX_PERIOD_BYTES)) {
++
++              snd_printk("error: playback buffer not in one memory window\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      if (request_irq(chip->irq, mv88fx_snd_interrupt, 0, MV88FX_AUDIO_NAME,
++          (void *)chip)) {
++
++              snd_printk("Unable to grab IRQ %d\n", chip->irq);
++              err = -ENOMEM;
++              goto error;
++      }
++
++      chip->ch_stat_valid = 1;
++      chip->burst = 128;
++      chip->loopback = 0;
++      chip->dco_ctrl_offst = 0x800;
++
++      err = mv88fx_snd_hw_init(card);
++      if (err) {
++              snd_printk("mv88fx_snd_hw_init failed\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      /* Set default values */
++      err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
++      if (err < 0) {
++              snd_printk("Creating chip device failed.\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      /* create pcm devices */
++      err = mv88fx_snd_pcm_new(card);
++      if (err < 0) {
++              snd_printk("Creating PCM device failed.\n");
++              err = -ENOMEM;
++              goto error;
++      }
++      /* create controll interfaces & switches */
++      err = mv88fx_snd_ctrl_new(card);
++      if (err < 0) {
++              snd_printk("Creating non-PCM device failed.\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      strcpy(card->driver, "mv88fx_snd");
++      strcpy(card->shortname, "Marvell mv88fx_snd");
++      sprintf(card->longname, "Marvell mv88fx_snd ALSA driver");
++
++      err = snd_card_register(card);
++      if (err < 0) {
++              snd_printk("Card registeration failed.\n");
++              err = -ENOMEM;
++              goto error;
++      }
++
++      /* if (dma_set_mask(&dev->dev, 0xFFFFFFUL) < 0) { */
++      if (dma_set_mask(&dev->dev, 0xFFFFFFFFUL) < 0) {
++              snd_printk("Could not set DMA mask\n");
++              goto error;
++      }
++
++      platform_set_drvdata(dev, card);
++      return 0;
++error:
++      if (card)
++              snd_card_free(card);
++      platform_set_drvdata(dev, NULL);
++      return err;
++}
++
++int mv88fx_snd_remove(struct platform_device *dev)
++{
++      struct snd_card *card = platform_get_drvdata(dev);
++
++      if (card)
++              snd_card_free(card);
++
++      /* FIXME: Once "../codecs/cs42l51.c" is fixed to behave as a module
++       *        this should be removed */
++      codec_del_i2c_device();
++
++      platform_set_drvdata(dev, NULL);
++      return 0;
++}
++
++#define mv88fx_snd_resume     NULL
++#define mv88fx_snd_suspend    NULL
++
++struct platform_driver mv88fx_snd_driver = {
++      .probe   = mv88fx_snd_probe,
++      .remove  = mv88fx_snd_remove,
++      .suspend = mv88fx_snd_suspend,
++      .resume  = mv88fx_snd_resume,
++      .driver  = { .name = MV88FX_AUDIO_NAME,},
++};
++
++int __init mv88fx_snd_init(void)
++{
++      return platform_driver_register(&mv88fx_snd_driver);
++}
++
++void __exit mv88fx_snd_exit(void)
++{
++      platform_driver_unregister(&mv88fx_snd_driver);
++}
++
++MODULE_AUTHOR("Maen Suleiman <maen@marvell.com>");
++MODULE_DESCRIPTION("Marvell MV88Fx Alsa Sound driver");
++MODULE_LICENSE("GPL");
++
++module_init(mv88fx_snd_init);
++module_exit(mv88fx_snd_exit);
++
+-- 
+1.6.5.2
+
index bf33146..7791dc8 100644 (file)
@@ -14,6 +14,8 @@ SRCREV = "1f76e2412659d2619ccd933d0ffd15e6253b1c4e"
 SRC_URI = "git://repo.or.cz/linux-2.6/linux-2.6-openrd.git;protocol=git \
            file://defconfig"
 
+SRC_URI_append_openrd-client += "file://0003-ARM-Kirkwood-Sound-Sound-driver-added.patch;patch=1"
+
 # update machine types list for ARM architecture, only for machines that need it
 do_arm_mach_types() {
 #  if test ${MACHINE} == openrd-base || test ${MACHINE} == openrd-client; then
index 38e8656..0d9e1f4 100644 (file)
@@ -13,7 +13,8 @@ export LIBS=" -lpthread "
 EXTRA_OECONF = "    --without-iconv \
                    --enable-discard-path \
                    --enable-sockets \
-                    --enable-pcntl \
+                   --enable-shared \
+#                    --enable-pcntl \
                    --enable-memory-limit \
                    --enable-wddx \
                     --enable-embedded-mysqli \
@@ -21,7 +22,7 @@ EXTRA_OECONF = "    --without-iconv \
                    --enable-fastcgi \
                    --with-zlib --with-zlib-dir=${STAGING_LIBDIR}/.. \
                     --with-libxml-dir=${STAGING_BINDIR_CROSS} \
-#                   --with-mysql="${STAGING_DIR_TARGET}${layout_exec_prefix}" \
+                    --with-mysql="${STAGING_DIR_TARGET}${layout_exec_prefix}" \
 #                   --with-mysqli = "${STAGING_BINDIR_NATIVE}/mysql_config" \
                "