PATCH[1/1] Add I2S common driver for Intel MID platform

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,
        This patch set is for new I2S SSP common driver that supports I2S audio on Intel MID platforms.

        It is used for sending/receving I2S audio samples on Intel MID platform.

        This interface is mostly used on Intel MID platforms and provide the low level interface for some upper layer drivers such as Alsa sound card driver for Bluetooth/FM peripherals, char device interfaces for modem (cmt_speech),...
        As this is used to transfer speech frames from/to cellular modem that have specific timing constraints (and modem also uses the I2S path to send some side-info), the Alsa SoC interface cannot be used.
        It is based on low level part of cmt_speech driver that can be found in N900 (see http://lists.meego.com/pipermail/meego-kernel/2010-September/000078.html ). The intel_mid_i2s have been separated from cmt_speech in order to be more generic and to be used on other ports (for instance, to provide Alsa sound card driver for Bluetooth and FM).
        Upper level drivers cmt_speech and Alsa sound card driver that uses intel_mid_i2s will be up-streamed in coming days and is planned to use the same folder path (sound/pci/intel_mid_i2s/). So once these files will be upstream, this will provides Alsa sound driver interface for Bluetooth and FM for Intel Mid platform, and specific speech audio interface (char device) that is used by lib cmt speech data.

        I will provide soon some draft version of example driver that uses intel_mid_i2s so reviewers can have better understanding of how it is used and how it fits together.

Best Regards,
Louis Le Gall


Here is a simple diagram that explain where is the intel_mid_i2s in software architecture.

             +-----------+
             |           |
             |Pulse Audio|
             |           |
             |           |
             +-----------+
                   |
                   |
      +------------+------------+
      |                         |
      |                         |
      |                         |
+-----------+             +-----------+
|           |             |Lib cmt    |
|Alsa Lib   |------+      |speech     |------+
|           |      |      |data       |      |
|           |      |      |           |      |
+-----------+      |      +-----------+      |
      |            |                         |
      |            |                         |                          User
......|............|.........................|.................................
      |            |                         |                         Kernel
      |            |                         |
      |            |                         |
      |            |                         |
      |            |                         |
      |            |                         |
      |            |                         |
     \|/          \|/                       \|/
      V            V                         V
+---------+  ++---------++             ++---------++
|         |  ||Alsa snd ||             ||cmt      ||
|Intel SST|  ||driver   ||             ||speech   ||
| "MAD"   |  ||SSP      ||             ||driver   ||
|         |  ||BT/FM    ||             ||         ||
+---------+  ++---------++             ++---------++
      |            |                         |
      |            |                         |
      |            |                         |
      |            |                         |
     \|/           |                        \|/
      V            |                         V
+-----------+      |                   ++---------++
|           |      |                 \ ||intel mid||
|LPE        |      +------------------>||i2s      ||
|           |                        / ||unified  ||
|           |                          ||driver   ||
+-----------+                          ++---------++
      |                                      |
      |                                      |                         Kernel
......|......................................|.................................
      |                                      |                        Hardware
     \|/                                    \|/
      V                                      V
+--       --+                          +--       --+
|           |                          |           |
|MISC       |                          |SSP in I2S |
|           |                          |config     |
|           |                          |           |
+--       --+                          +--       --+



>From 16da3ebffad026e44d74538b7773a51044254920 Mon Sep 17 00:00:00 2001
From: Louis LE GALL <louis.le.gall@xxxxxxxxx>
Date: Fri, 15 Oct 2010 15:30:49 +0200
Subject: [PATCH] New low level driver "Intel_mid_i2s" provide support for I2S SSP device on Intel MID Platforms
 Signed-off-by: Louis LE GALL <louis.le.gall@xxxxxxxxx>

Change-Id: I17052a3b464c5682d2ab17a9b6e0fad30137243f

Signed-off-by: Louis LE GALL <louis.le.gall@xxxxxxxxx>
---
 include/linux/intel_mid_i2s_if.h        |  280 ++++++
 sound/pci/Kconfig                       |   18 +
 sound/pci/Makefile                      |    4 +-
 sound/pci/intel_mid_i2s/Makefile        |   18 +
 sound/pci/intel_mid_i2s/intel_mid_i2s.c | 1428 +++++++++++++++++++++++++++++++
 sound/pci/intel_mid_i2s/intel_mid_i2s.h |  500 +++++++++++
 6 files changed, 2247 insertions(+), 1 deletions(-)
 create mode 100644 include/linux/intel_mid_i2s_if.h
 create mode 100644 sound/pci/intel_mid_i2s/Makefile
 create mode 100644 sound/pci/intel_mid_i2s/intel_mid_i2s.c
 create mode 100644 sound/pci/intel_mid_i2s/intel_mid_i2s.h

diff --git a/include/linux/intel_mid_i2s_if.h b/include/linux/intel_mid_i2s_if.h
new file mode 100644
index 0000000..1563577
--- /dev/null
+++ b/include/linux/intel_mid_i2s_if.h
@@ -0,0 +1,280 @@
+/*
+  * <Driver for I2S protocol on SSP (Moorestown and Medfield hardware)>
+  * Copyright (c) 2010, Intel Corporation.
+  * Louis LE GALL <louis.le.gall intel.com>
+  *
+  * This program is free software; you can redistribute it and/or modify it
+  * under the terms and conditions of the GNU General Public License,
+  * version 2, as published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope it will be useful, but WITHOUT
+  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  * more details.
+  *
+  * You should have received a copy of the GNU General Public License along with
+  * this program; if not, write to the Free Software Foundation, Inc.,
+  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+  */
+
+#ifndef MID_I2S_EXTERNAL_H_
+#define MID_I2S_EXTERNAL_H_
+
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/intel_mid_dma.h>
+
+#include <linux/interrupt.h>
+
+/*
+ *     Structures Definition
+ */
+
+
+/*
+ *     SSCR0 settings
+ */
+enum mrst_ssp_mode {
+       SSP_IN_NORMAL_MODE = 0x0,
+       SSP_IN_NETWORK_MODE
+};
+
+enum mrst_ssp_rx_fifo_over_run_int_mask {
+       SSP_RX_FIFO_OVER_INT_ENABLE = 0x0,
+       SSP_RX_FIFO_OVER_INT_DISABLE
+};
+
+enum mrst_ssp_tx_fifo_under_run_int_mask {
+       SSP_TX_FIFO_UNDER_INT_ENABLE = 0x0,
+       SSP_TX_FIFO_UNDER_INT_DISABLE
+};
+
+enum mrst_ssp_frame_format {
+       MOTOROLA_SPI_FORMAT = 0x0,
+       TI_SSP_FORMAT,
+       MICROWIRE_FORMAT,
+       PSP_FORMAT
+
+};
+
+enum mrst_ssp_master_mode_clock_selection {
+       SSP_ONCHIP_CLOCK = 0x0,
+       SSP_NETWORK_CLOCK,
+       SSP_EXTERNAL_CLOCK,
+       SSP_ONCHIP_AUDIO_CLOCK,
+       SSP_MASTER_CLOCK_UNDEFINED = 0xFF
+};
+
+/*
+ *     SSCR1 settings
+ */
+enum mrst_ssp_txd_tristate_last_phase {
+       TXD_TRISTATE_LAST_PHASE_OFF =  0x0,
+       TXD_TRISTATE_LAST_PHASE_ON
+};
+
+enum mrst_ssp_txd_tristate_enable {
+       TXD_TRISTATE_OFF =  0x0,
+       TXD_TRISTATE_ON
+};
+
+enum mrst_ssp_slave_sspclk_free_running {
+       SLAVE_SSPCLK_ON_ALWAYS =  0x0,
+       SLAVE_SSPCLK_ON_DURING_TRANSFER_ONLY
+};
+
+enum mrst_ssp_sspsclk_direction {
+       SSPSCLK_MASTER_MODE = 0x0,
+       SSPSCLK_SLAVE_MODE
+};
+
+enum mrst_ssp_sspsfrm_direction {
+       SSPSFRM_MASTER_MODE = 0x0,
+       SSPSFRM_SLAVE_MODE
+};
+
+enum mrst_ssp_rx_without_tx {
+       RX_AND_TX_MODE = 0x0,
+       RX_WITHOUT_TX_MODE
+};
+
+enum mrst_trailing_byte_mode {
+       SSP_TRAILING_BYTE_HDL_BY_IA = 0x0,
+       SSP_TRAILING_BYTE_HDL_BY_DMA
+};
+
+enum mrst_ssp_tx_dma_status {
+       SSP_TX_DMA_MASK = 0x0,
+       SSP_TX_DMA_ENABLE
+};
+
+enum mrst_ssp_rx_dma_status {
+       SSP_RX_DMA_MASK = 0x0,
+       SSP_RX_DMA_ENABLE
+};
+
+enum mrst_ssp_rx_timeout_int_status {
+       SSP_RX_TIMEOUT_INT_DISABLE = 0x0,
+       SSP_RX_TIMEOUT_INT_ENABLE
+};
+
+enum mrst_ssp_trailing_byte_int_status {
+       SSP_TRAILING_BYTE_INT_DISABLE = 0x0,
+       SSP_TRAILING_BYTE_INT_ENABLE
+};
+
+enum mrst_ssp_loopback_mode_status {
+       SSP_LOOPBACK_OFF = 0x0,
+       SSP_LOOPBACK_ON
+};
+
+
+/*
+ *     SSPSP settings: for PSP Format ONLY!!!!!!!!
+ */
+
+enum mrst_ssp_frame_sync_relative_timing_bit {
+       NEXT_FRMS_ASS_AFTER_END_OF_T4 =  0x0,
+       NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM
+};
+
+enum mrst_ssp_frame_sync_polarity_bit {
+       SSP_FRMS_ACTIVE_LOW =  0x0,
+       SSP_FRMS_ACTIVE_HIGH
+};
+
+enum mrst_ssp_end_of_transfer_data_state {
+       SSP_END_DATA_TRANSFER_STATE_LOW = 0x0,
+       SSP_END_DATA_TRANSFER_STATE_PEVIOUS_BIT
+};
+
+enum mrst_ssp_clk_mode {
+       SSP_CLK_MODE_0 = 0x0,
+       SSP_CLK_MODE_1,
+       SSP_CLK_MODE_2,
+       SSP_CLK_MODE_3
+};
+
+
+/*
+ *     list of differents types of SSP, value depends of adid entry of
+ *     capability ID of the PCI
+ */
+
+/*
+ *
+ * The PCI header associated to SSP devices now includes a configuration
+ * register. It provides information to a driver which is probed for the
+ * SSP, specifying in which way the SSP is supposed to be used. Here is
+ * the format of this byte register:
+ *
+ *     bits 2..0: Mode
+ *             000=0x0 : Invalid, the register should be ignored
+ *             001=0x1 : SSP to be used as SPI controller
+ *             010=0x2: SSP to be used in I2S/ISS mode
+ *             other: Reserved
+ *
+ *     bits 5..3: Configuration
+ *     In I2S/ISS mode:
+ *             000=0x0: Invalid
+ *             001=0x1: Bluetooth
+ *             010=0x2: Modem
+ *             other: Reserved
+ *     In SPI mode:
+ *             Value is the SPI bus number connected to the SSP.
+ *             To be used for registration to the Linux SPI
+ *             framework.
+ *     bit 6: SPI slave
+ *     Relevant in SPI mode only. If set, indicates the SPI clock
+ *     is not provided by the SSP: SPI slave mode.
+ *
+ *     bit 7: Reserved (0)
+ *
+ * This configuration register is implemented in the adid field of the
+ * Vendor Specific PCI capability associated to the SSP.
+ *
+ */
+
+#define PCI_ADID_SSP_MODE_SPI  (1)
+#define PCI_ADID_SSP_MODE_I2S  (2)
+
+#define PCI_ADID_SSP_CONF_BT_FM  (1<<3)
+#define PCI_ADID_SSP_CONF_MODEM  (2<<3)
+
+
+#define PCI_CAP_ADID_I2S_BT_FM  ((PCI_ADID_SSP_CONF_BT_FM) | (PCI_ADID_SSP_MODE_I2S))
+#define PCI_CAP_ADID_I2S_MODEM  ((PCI_ADID_SSP_CONF_MODEM) | (PCI_ADID_SSP_MODE_I2S))
+
+enum intel_mid_i2s_ssp_usage {
+       SSP_USAGE_UNASSIGNED = 0x00,
+       SSP_USAGE_BLUETOOTH_FM = 0x01,
+       SSP_USAGE_MODEM = 0x02
+};
+
+/*
+ *     Structure used to configure the SSP Port
+ *     Please note that only the PSP format and the DMA transfer are supported
+ */
+
+struct intel_mid_i2s_settings {
+       enum mrst_ssp_mode                         mode;
+       enum mrst_ssp_rx_fifo_over_run_int_mask    rx_fifo_interrupt;
+       enum mrst_ssp_tx_fifo_under_run_int_mask   tx_fifo_interrupt;
+       enum mrst_ssp_frame_format                 frame_format;
+       enum mrst_ssp_master_mode_clock_selection  master_mode_clk_selection;              /* for Master Mode Only */
+       u8                                         frame_rate_divider_control;
+       u16                                        master_mode_serial_clock_rate;          /* for Master Mode Only */
+       u16                                        data_size;
+
+       enum mrst_ssp_txd_tristate_last_phase      tx_tristate_phase;
+       enum mrst_ssp_txd_tristate_enable          tx_tristate_enable;
+       enum mrst_ssp_slave_sspclk_free_running    slave_clk_free_running_status;
+       enum mrst_ssp_sspsclk_direction            sspslclk_direction;
+       enum mrst_ssp_sspsfrm_direction            sspsfrm_direction;
+       enum mrst_ssp_rx_without_tx                ssp_duplex_mode;
+       enum mrst_trailing_byte_mode               ssp_trailing_byte_mode;
+       enum mrst_ssp_tx_dma_status                ssp_tx_dma;
+       enum mrst_ssp_rx_dma_status                ssp_rx_dma;
+       enum mrst_ssp_rx_timeout_int_status        ssp_rx_timeout_interrupt_status;
+       enum mrst_ssp_trailing_byte_int_status     ssp_trailing_byte_interrupt_status;
+       enum mrst_ssp_loopback_mode_status         ssp_loopback_mode_status;
+       u8                                         ssp_rx_fifo_threshold;
+       u8                                         ssp_tx_fifo_threshold;
+
+
+       enum mrst_ssp_frame_sync_relative_timing_bit  ssp_frmsync_timing_bit;
+       enum mrst_ssp_frame_sync_polarity_bit      ssp_frmsync_pol_bit;
+       enum mrst_ssp_end_of_transfer_data_state   ssp_end_transfer_state;
+       enum mrst_ssp_clk_mode                     ssp_serial_clk_mode;
+       u8                                         ssp_psp_T1;
+       u8                                         ssp_psp_T2;
+       u8                                         ssp_psp_T4;   /* DMYSTOP */
+       u8                                         ssp_psp_T5;   /* SFRMDLY */
+       u8                                         ssp_psp_T6;   /* SFRMWDTH */
+
+       u8                                         ssp_active_tx_slots_map;
+       u8                                         ssp_active_rx_slots_map;
+};
+
+/*
+ *     Provided Interface
+ */
+
+
+struct intel_mid_i2s_hdl *intel_mid_i2s_open(enum intel_mid_i2s_ssp_usage usage, const struct intel_mid_i2s_settings *ps_settings);
+void  intel_mid_i2s_close(struct intel_mid_i2s_hdl *handle);
+
+int intel_mid_i2s_rd_req(struct intel_mid_i2s_hdl *handle, u32 *dst, size_t len, void *param);
+int intel_mid_i2s_wr_req(struct intel_mid_i2s_hdl *handle, u32 *src, size_t len, void *param);
+int intel_mid_i2s_enable_ssp(struct intel_mid_i2s_hdl *handle);
+
+
+int intel_mid_i2s_set_wr_cb(struct intel_mid_i2s_hdl *handle, int (*write_callback)(void *param));
+int intel_mid_i2s_set_rd_cb(struct intel_mid_i2s_hdl *handle, int (*read_callback)(void *param));
+
+
+int intel_mid_i2s_get_tx_fifo_level(struct intel_mid_i2s_hdl *handle);
+int intel_mid_i2s_get_rx_fifo_level(struct intel_mid_i2s_hdl *handle);
+int intel_mid_i2s_flush(struct intel_mid_i2s_hdl *handle);
+
+#endif /*MID_I2S_EXTERNAL_H_*/
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 12e3465..0b0a524 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -853,4 +853,22 @@ config SND_YMFPCI
          To compile this driver as a module, choose M here: the module
          will be called snd-ymfpci.

+config SND_INTEL_MID_I2S
+       tristate "Intel mid I2S hardware driver"
+       depends on EXPERIMENTAL && PCI && INTEL_MID_DMAC
+       default n
+       help
+         Say Y here if you want to build low level driver to support
+         sending/receving I2S audio samples on Intel MID SSP device.
+         This interface is mostly used on Intel MID platforms and provide
+         the low level interface for some upper layer drivers such as
+         Alsa SoC, char device interfaces... depending of peripheral connected.
+         PCI Header should have ADID field set to I2S BT_FM or
+         I2S MODEM to be used by this driver (so it know connected peripheral).
+         Note this is a prototype driver and support for continuous
+         flow is still working-in-progress.
+         This driver can also be built as a module. If so, the module
+         will be called intel_mid_i2s.ko
+         If unsure, say N here.
+
 endif  # SND_PCI
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 9cf4348..46613a2 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -78,4 +78,6 @@ obj-$(CONFIG_SND) += \
        rme9652/ \
        trident/ \
        ymfpci/ \
-       vx222/
+       vx222/ \
+       intel_mid_i2s/
+
diff --git a/sound/pci/intel_mid_i2s/Makefile b/sound/pci/intel_mid_i2s/Makefile
new file mode 100644
index 0000000..bebac1c
--- /dev/null
+++ b/sound/pci/intel_mid_i2s/Makefile
@@ -0,0 +1,18 @@
+# SSP audio driver
+# Copyright (c) 2010, Intel Corporation.
+
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+obj-$(CONFIG_SND_INTEL_MID_I2S)    +=  intel_mid_i2s.o
+
diff --git a/sound/pci/intel_mid_i2s/intel_mid_i2s.c b/sound/pci/intel_mid_i2s/intel_mid_i2s.c
new file mode 100644
index 0000000..572abde
--- /dev/null
+++ b/sound/pci/intel_mid_i2s/intel_mid_i2s.c
@@ -0,0 +1,1428 @@
+/*
+  * <Driver for I2S protocol on SSP (Moorestown and Medfield hardware)>
+  * Copyright (c) 2010, Intel Corporation.
+  * Louis LE GALL <louis.le.gall intel.com>
+  *
+  * This program is free software; you can redistribute it and/or modify it
+  * under the terms and conditions of the GNU General Public License,
+  * version 2, as published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope it will be useful, but WITHOUT
+  * ANY WARRANTY; without evenp the implied warranty of MERCHANTABILITY or
+  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  * more details.
+  *
+  * You should have received a copy of the GNU General Public License along with
+  * this program; if not, write to the Free Software Foundation, Inc.,
+  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+  */
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/pci_regs.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include <linux/device.h>
+
+#include <linux/intel_mid_i2s_if.h>
+#include "intel_mid_i2s.h"
+
+MODULE_AUTHOR("Louis LE GALL <louis.le.gall intel.com>");
+MODULE_DESCRIPTION("Intel MID I2S/PCM SSP Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.0");
+
+
+/*
+ * structures for pci probing
+ */
+static const struct dev_pm_ops intel_mid_i2s_pm_ops = {
+       .runtime_suspend = intel_mid_i2s_runtime_suspend,
+       .runtime_resume = intel_mid_i2s_runtime_resume,
+};
+static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, MFLD_SSP1_DEVICE_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, MFLD_SSP0_DEVICE_ID) },
+       { 0, }, /* terminate list */
+};
+static struct pci_driver intel_mid_i2s_driver = {
+       .driver = {
+               .pm = &intel_mid_i2s_pm_ops,
+       },
+       .name = DRIVER_NAME,
+       .id_table = pci_ids,
+       .probe = intel_mid_i2s_probe,
+       .remove = __devexit_p(intel_mid_i2s_remove),
+#ifdef CONFIG_PM
+       .suspend = intel_mid_i2s_driver_suspend,
+       .resume = intel_mid_i2s_driver_resume,
+#endif
+};
+
+
+/*
+ * POWER MANAGEMENT FUNCTIONS
+ */
+
+#ifdef CONFIG_PM
+/**
+ * intel_mid_i2s_driver_suspend - driver power management suspend activity
+ * @device_ptr : pointer of the device to resume
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+static int  intel_mid_i2s_driver_suspend(struct pci_dev *dev, pm_message_t state)
+{
+       struct intel_mid_i2s_hdl *drv_data = pci_get_drvdata(dev);
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return 0;
+       dev_dbg(&drv_data->pdev->dev, "SUSPEND SSP ID %d\n", drv_data->pdev->device);
+       pci_save_state(dev);
+       pci_disable_device(dev);
+       pci_set_power_state(dev, PCI_D3hot);
+       return 0;
+}
+
+/**
+ * intel_mid_i2s_driver_resume - driver power management suspend activity
+ * @device_ptr : pointer of the device to resume
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+static int intel_mid_i2s_driver_resume(struct pci_dev *dev)
+{
+       int err;
+       int ret = 0;
+       struct intel_mid_i2s_hdl *drv_data = pci_get_drvdata(dev);
+
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       dev_dbg(&drv_data->pdev->dev, "RESUME SSP ID %d\n", drv_data->pdev->device);
+
+       err = pci_enable_device(dev);
+       if (err)
+               dev_err(&drv_data->pdev->dev, "Unable to re-enable device, trying to continue.\n");
+       dev_dbg(&drv_data->pdev->dev, "resuming\n");
+       pci_set_power_state(dev, PCI_D0);
+       pci_restore_state(dev);
+       ret = pci_enable_device(dev);
+       if (ret)
+               dev_err(&drv_data->pdev->dev, "I2S: device can't be enabled");
+       dev_dbg(&drv_data->pdev->dev, "resumed in D3\n");
+       return ret;
+}
+
+/**
+ * intel_mid_i2s_runtime_suspend - runtime power management suspend activity
+ * @device_ptr : pointer of the device to resume
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+static int intel_mid_i2s_runtime_suspend(struct device *device_ptr)
+{
+       struct pci_dev *pdev;
+       struct intel_mid_i2s_hdl *drv_data;
+       void __iomem *reg;
+
+       pdev = to_pci_dev(device_ptr);
+       WARN(!pdev, "Pci dev=NULL\n");
+       if (!pdev)
+               return -EFAULT;
+       drv_data = (struct intel_mid_i2s_hdl *) pci_get_drvdata(pdev);
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       if (test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_err(device_ptr, "Trying to suspend a device that is opened\n");
+               return -ENODEV;
+       }
+       reg = drv_data->ioaddr;
+       dev_dbg(&drv_data->pdev->dev, "Suspend of SSP requested !!\n");
+       return intel_mid_i2s_driver_suspend(to_pci_dev(device_ptr), PMSG_SUSPEND);
+}
+#endif
+
+/**
+ * intel_mid_i2s_runtime_resume - runtime power management resume activity
+ * @device_ptr : pointer of the device to resume
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+static int intel_mid_i2s_runtime_resume(struct device *device_ptr)
+{
+       struct pci_dev *pdev;
+       struct intel_mid_i2s_hdl *drv_data;
+       pdev = to_pci_dev(device_ptr);
+       WARN(!pdev, "Pci dev=NULL\n");
+       if (!pdev)
+               return -EFAULT;
+       drv_data = (struct intel_mid_i2s_hdl *) pci_get_drvdata(pdev);
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       dev_dbg(&drv_data->pdev->dev, "RT RESUME SSP ID\n");
+       return intel_mid_i2s_driver_resume(to_pci_dev(device_ptr));
+}
+
+/*
+ * INTERFACE FUNCTIONS
+ */
+
+/**
+ * intel_mid_i2s_flush - This is the I2S flush request
+ * @drv_data : pointer on private i2s driver data (by i2s_open function)
+ *
+ * It will flush the TX FIFO
+ * WARNING: this function is used in a Burst Mode context where it is called
+ * between Bursts i.e. when there is no FMSYNC, no transfer ongoing at
+ * that time
+ * If you need a flush while SSP configured in I2S is BUSY and FMSYNC are
+ * generated, you have to write another function
+ * (loop on BUSY bit and do not limit the flush to at most 16 samples)
+ *
+ * Output parameters
+ *      int : number of samples flushed
+ */
+int intel_mid_i2s_flush(struct intel_mid_i2s_hdl *drv_data)
+{
+       u32 sssr, data;
+       u32 num = 0;
+       void __iomem *reg;
+
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return 0;
+       reg = drv_data->ioaddr;
+       sssr = read_SSSR(reg);
+       dev_dbg(&drv_data->pdev->dev, "in flush sssr=0x%08X\n", sssr);
+       /*
+        * Flush "by hand" was generating spurious DMA SERV REQUEST
+        * from SSP to DMA => then buggy retrieval of data for next dma_req
+        * Disable: RX Service Request from RX fifo to DMA
+        * as we will flush by hand
+        */
+       clear_SSCR1_reg(reg, RSRE);
+       /* i2s_flush is called in between 2 bursts
+        * => no FMSYNC at that time (i.e. SSP not busy)
+        * => at most 16 samples in the FIFO */
+       while ((read_SSSR(reg) & (SSSR_RNE_MASK<<SSSR_RNE_SHIFT))
+                       && (num < FIFO_SIZE)) {
+               data = read_SSDR(reg);
+               num++;
+               dev_warn(&drv_data->pdev->dev, "flush(%u)=%u\n", num, data);
+       }
+       /* Enable: RX Service Request from RX fifo to DMA
+        * as flush by hand is done
+        */
+       set_SSCR1_reg(reg, RSRE);
+       sssr = read_SSSR(reg);
+       dev_dbg(&drv_data->pdev->dev, "out flush sssr=0x%08X\n", sssr);
+       return num;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_flush);
+
+/**
+ * intel_mid_i2s_get_rx_fifo_level - returns I2S rx fifo level
+ * @drv_data : pointer on private i2s driver data (by i2s_open function)
+ *
+ * Output parameters
+ *      int : Number of samples currently in the RX FIFO (negative = error)
+ */
+int intel_mid_i2s_get_rx_fifo_level(struct intel_mid_i2s_hdl *drv_data)
+{
+       u32 sssr;
+       u32 rne, rfl;
+       void __iomem *reg;
+
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       reg = drv_data->ioaddr;
+       sssr = read_SSSR(reg);
+       rfl = GET_SSSR_val(sssr, RFL);
+       rne = GET_SSSR_val(sssr, RNE);
+       if (!rne)
+               return 0;
+       else
+               return rfl+1;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_get_rx_fifo_level);
+
+/**
+ * intel_mid_i2s_get_tx_fifo_level - returns I2S tx fifo level
+ * @drv_data : pointer on private i2s driver data (by i2s_open function)
+ *
+ * Output parameters
+ *      int : number of samples currently in the TX FIFO (negative = error)
+ */
+int intel_mid_i2s_get_tx_fifo_level(struct intel_mid_i2s_hdl *drv_data)
+{
+       u32 sssr;
+       u32 tnf, tfl;
+       void __iomem *reg;
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       reg = drv_data->ioaddr;
+       sssr = read_SSSR(reg);
+       tfl = GET_SSSR_val(sssr, TFL);
+       tnf = GET_SSSR_val(sssr, TFN);
+       if (!tnf)
+               return 16;
+       else
+               return tfl;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_get_tx_fifo_level);
+
+/**
+ * intel_mid_i2s_set_rd_cb - set the callback function after read is done
+ * @drv_data : handle of corresponding ssp i2s (given by i2s_open function)
+ * @read_callback : pointer of callback function
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+int intel_mid_i2s_set_rd_cb(struct intel_mid_i2s_hdl *drv_data, int (*read_callback)(void *param))
+{
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       mutex_lock(&drv_data->mutex);
+       if (!test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_WARN(&drv_data->pdev->dev, "set WR CB I2S_PORT NOT_OPENED");
+               mutex_unlock(&drv_data->mutex);
+               return -EPERM;
+       }
+       /* Do not change read parameters in the middle of a READ request */
+       if (test_bit(I2S_PORT_READ_BUSY, &drv_data->flags)) {
+               dev_WARN(&drv_data->pdev->dev, "CB reject I2S_PORT_READ_BUSY");
+               mutex_unlock(&drv_data->mutex);
+               return -EBUSY;
+       }
+       drv_data->read_callback = read_callback;
+       drv_data->read_len = 0;
+       mutex_unlock(&drv_data->mutex);
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_set_rd_cb);
+
+/**
+ * intel_mid_i2s_set_wr_cb - set the callback function after write is done
+ * @drv_data : handle of corresponding ssp i2s (given by i2s_open  function)
+ * @write_callback : pointer of callback function
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+int intel_mid_i2s_set_wr_cb(struct intel_mid_i2s_hdl *drv_data, int (*write_callback)(void *param))
+{
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       mutex_lock(&drv_data->mutex);
+       if (!test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_warn(&drv_data->pdev->dev, "set WR CB I2S_PORT NOT_OPENED");
+               mutex_unlock(&drv_data->mutex);
+               return -EPERM;
+       }
+       /* Do not change write parameters in the middle of a WRITE request */
+       if (test_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags)) {
+               dev_warn(&drv_data->pdev->dev, "CB reject I2S_PORT_WRITE_BUSY");
+               mutex_unlock(&drv_data->mutex);
+               return -EBUSY;
+       }
+       drv_data->write_callback = write_callback;
+       drv_data->write_len = 0;
+       mutex_unlock(&drv_data->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_set_wr_cb);
+
+/**
+ * intel_mid_i2s_enable_ssp() : start the ssp right after the read/write req
+ * @drv_data : handle of corresponding ssp i2s (given by i2s_open function)
+ *
+ * This enable the read/write to be started synchronously
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+int intel_mid_i2s_enable_ssp(struct intel_mid_i2s_hdl *drv_data)
+{
+       void __iomem *reg = drv_data->ioaddr;
+       set_SSCR0_reg(reg, SSE);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_enable_ssp);
+
+/**
+ * intel_mid_i2s_rd_req - request a read from i2s peripheral
+ * @drv_data : handle of corresponding ssp i2s (given by i2s_open function)
+ * @dst : destination buffer where the read sample should be put
+ * @len : number of sample to be read (160 samples only right now)
+ * @param : private context parameter to give back to read callback
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+int intel_mid_i2s_rd_req(struct intel_mid_i2s_hdl *drv_data, u32 *destination, size_t len, void *param)
+{
+       struct dma_async_tx_descriptor *rxdesc = NULL;
+       struct dma_chan *rxchan = drv_data->rxchan;
+       enum dma_ctrl_flags flag;
+       dma_addr_t ssdr_addr;
+       dma_addr_t dst;
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       mutex_lock(&drv_data->mutex);
+       if (!rxchan) {
+               dev_WARN(&(drv_data->pdev->dev), "rd_req FAILED no rxchan\n");
+               mutex_unlock(&drv_data->mutex);
+               return -EINVAL;
+       }
+       if (!len) {
+               dev_WARN(&drv_data->pdev->dev, "rd req invalid len=0");
+               mutex_unlock(&drv_data->mutex);
+               return -EINVAL;
+       }
+       if (!test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_WARN(&drv_data->pdev->dev, "RD reject I2S_PORT NOT_OPENED");
+               mutex_unlock(&drv_data->mutex);
+               return -EPERM;
+       }
+       if (test_bit(I2S_PORT_CLOSING, &drv_data->flags)) {
+               dev_WARN(&drv_data->pdev->dev, "RD reject I2S_PORT CLOSING");
+               mutex_unlock(&drv_data->mutex);
+               return -EPIPE;
+       }
+
+       dev_dbg(&drv_data->pdev->dev, "I2S_READ() dst=%p, len=%d, drv_data=%p", destination, len, drv_data);
+       dst = dma_map_single(NULL, destination, len, DMA_FROM_DEVICE);
+       if (!dst) {
+               dev_WARN(&drv_data->pdev->dev, "can't map DMA address %p", destination);
+               mutex_unlock(&drv_data->mutex);
+               return -ENOMEM;
+       }
+
+       drv_data->read_dst = dst;
+       drv_data->read_len = len;
+       /* get Data Read/Write address */
+       ssdr_addr = (drv_data->paddr + OFFSET_SSDR);
+       set_SSCR1_reg((drv_data->ioaddr), RSRE);
+       change_SSCR0_reg((drv_data->ioaddr), RIM,
+                        ((drv_data->current_settings).rx_fifo_interrupt));
+       flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+       /* Start the RX dma transfer */
+       rxdesc = rxchan->device->device_prep_dma_memcpy(
+                       rxchan,         /* DMA Channel */
+                       dst,            /* DAR */
+                       ssdr_addr,      /* SAR */
+                       len,            /* Data Length */
+                       flag);          /* Flag */
+       if (!rxdesc) {
+               dev_WARN(&drv_data->pdev->dev, "can not prep dma memcpy");
+               mutex_unlock(&drv_data->mutex);
+               return -EFAULT;
+       }
+       /* Only 1 READ at a time allowed. do it at end to avoid clear&wakeup*/
+       if (test_and_set_bit(I2S_PORT_READ_BUSY, &drv_data->flags)) {
+               dma_unmap_single(NULL, dst, len, DMA_FROM_DEVICE);
+               dev_WARN(&drv_data->pdev->dev, "RD reject I2S_PORT READ_BUSY");
+               mutex_unlock(&drv_data->mutex);
+               return -EBUSY;
+       }
+       dev_dbg(&(drv_data->pdev->dev), "RD dma tx submit\n");
+       rxdesc->callback = i2s_read_done;
+       drv_data->read_param = param;
+       rxdesc->callback_param = drv_data;
+       rxdesc->tx_submit(rxdesc);
+       mutex_unlock(&drv_data->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_rd_req);
+
+/**
+ * intel_mid_i2s_wr_req - request a write to i2s peripheral
+ * @drv_data : handle of corresponding ssp i2s (given by i2s_open function)
+ * @src : source buffer where the samples to wrote should be get
+ * @len : number of sample to be read (160 samples only right now)
+ * @param : private context parameter to give back to write callback
+ *
+ * Output parameters
+ *      error : 0 means no error
+ */
+int intel_mid_i2s_wr_req(struct intel_mid_i2s_hdl *drv_data, u32 *source, size_t len, void *param)
+{
+       dma_addr_t ssdr_addr;
+       struct dma_async_tx_descriptor *txdesc = NULL;
+       struct dma_chan *txchan = drv_data->txchan;
+       enum dma_ctrl_flags flag;
+       dma_addr_t src;
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return -EFAULT;
+       mutex_lock(&drv_data->mutex);
+       if (!txchan) {
+               dev_WARN(&(drv_data->pdev->dev), "wr_req but no txchan\n");
+               mutex_unlock(&drv_data->mutex);
+               return -EINVAL;
+       }
+       if (!len) {
+               dev_WARN(&drv_data->pdev->dev, "invalid len 0");
+               mutex_unlock(&drv_data->mutex);
+               return -EINVAL;
+       }
+       if (!test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_WARN(&drv_data->pdev->dev, "WR reject I2S_PORT NOT_OPENED");
+               mutex_unlock(&drv_data->mutex);
+               return -EPERM;
+       }
+       if (test_bit(I2S_PORT_CLOSING, &drv_data->flags)) {
+               dev_WARN(&drv_data->pdev->dev, "WR reject I2S_PORT CLOSING");
+               mutex_unlock(&drv_data->mutex);
+               return -EPIPE;
+       }
+
+       dev_dbg(&drv_data->pdev->dev, "I2S_WRITE() src=%p, len=%d, drv_data=%p", source, len, drv_data);
+
+       src = dma_map_single(NULL, source, len, DMA_TO_DEVICE);
+       if (!src) {
+               dev_WARN(&drv_data->pdev->dev, "can't map DMA address %p", source);
+               mutex_unlock(&drv_data->mutex);
+               return -EFAULT;
+       }
+       drv_data->write_src = src;
+       drv_data->write_len = len;
+       /* get Data Read/Write address */
+       ssdr_addr = (dma_addr_t)(u32)(drv_data->paddr + OFFSET_SSDR);
+       set_SSCR1_reg((drv_data->ioaddr), TSRE);
+       change_SSCR0_reg((drv_data->ioaddr), TIM,
+                        ((drv_data->current_settings).tx_fifo_interrupt));
+       flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+       txdesc = txchan->device->device_prep_dma_memcpy(
+                       txchan,         /* DMA Channel */
+                       ssdr_addr,      /* DAR */
+                       src,            /* SAR */
+                       len,            /* Data Length */
+                       flag);          /* Flag */
+       if (!txdesc) {
+               dev_WARN(&(drv_data->pdev->dev),
+                       "wr_req dma memcpy FAILED(src=%08x,len=%d,txchan=%p)\n",
+                       src, len, txchan);
+               mutex_unlock(&drv_data->mutex);
+               return -1;
+       }
+       dev_dbg(&(drv_data->pdev->dev), "WR dma tx summit\n");
+       /* Only 1 WRITE at a time allowed */
+       if (test_and_set_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags)) {
+               dma_unmap_single(NULL, src, len, DMA_TO_DEVICE);
+               dev_WARN(&drv_data->pdev->dev, "WR reject I2S_PORT WRITE_BUSY");
+               mutex_unlock(&drv_data->mutex);
+               return -EBUSY;
+       }
+       txdesc->callback = i2s_write_done;
+       drv_data->write_param = param;
+       txdesc->callback_param = drv_data;
+       txdesc->tx_submit(txdesc);
+       dev_dbg(&(drv_data->pdev->dev), "wr dma req programmed\n");
+       mutex_unlock(&drv_data->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_wr_req);
+
+/**
+ * intel_mid_i2s_open - reserve and start a SSP depending of it's usage
+ * @usage : select which ssp i2s you need by giving usage (BT,MODEM...)
+ * @ps_settings : hardware settings to configure the SSP module
+ *
+ * May sleep (driver_find_device) : no lock permitted when called.
+ *
+ * Output parameters
+ *      handle : handle of the selected SSP, or NULL if not found
+ */
+struct intel_mid_i2s_hdl *intel_mid_i2s_open(enum intel_mid_i2s_ssp_usage usage,
+                        const struct intel_mid_i2s_settings *ps_settings)
+{
+       struct pci_dev *pdev;
+       struct intel_mid_i2s_hdl *drv_data = NULL;
+       struct device *found_device = NULL;
+       pr_debug("%s : open called,searching for device with usage=%x !\n", DRIVER_NAME, usage);
+       found_device = driver_find_device(&(intel_mid_i2s_driver.driver), NULL, &usage, check_device);
+       if (!found_device) {
+               pr_debug("%s : open can not found with usage=0x%02X\n", DRIVER_NAME, (int)usage);
+               return NULL;
+       }
+       pdev = to_pci_dev(found_device);
+       drv_data = pci_get_drvdata(pdev);
+
+       if (!drv_data) {
+               dev_err(found_device, "no drv_data in open pdev=%p\n", pdev);
+               put_device(found_device);
+               return NULL;
+       }
+       mutex_lock(&drv_data->mutex);
+       dev_dbg(found_device, "Open found pdevice=0x%p\n", pdev);
+       /* pm_runtime */
+       pm_runtime_get_sync(&drv_data->pdev->dev);
+       /* dmac1 is already set during probe */
+       if (i2s_dma_start(drv_data) != 0) {
+               dev_err(found_device, "Can not start DMA\n");
+               goto open_error;
+       }
+       /* if we restart after stop without suspend, we start ssp faster */
+       drv_data->current_settings = *ps_settings;
+       set_ssp_i2s_hw(drv_data, ps_settings);
+
+       drv_data->mask_sr = ((SSSR_BCE_MASK << SSSR_BCE_SHIFT) |
+                       (SSSR_EOC_MASK << SSSR_EOC_SHIFT) |
+                       (SSSR_ROR_MASK << SSSR_ROR_SHIFT) |
+                       (SSSR_TUR_MASK << SSSR_TUR_SHIFT) |
+                       (SSSR_TINT_MASK << SSSR_TINT_SHIFT) |
+                       (SSSR_PINT_MASK << SSSR_PINT_SHIFT));
+       if (test_bit(I2S_PORT_CLOSING, &drv_data->flags)) {
+               dev_err(&drv_data->pdev->dev, "Opening a closing I2S!");
+               goto open_error;
+       }
+       /* there is no need to "wake up" as we can not close an opening i2s */
+       clear_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags);
+       clear_bit(I2S_PORT_READ_BUSY, &drv_data->flags);
+       mutex_unlock(&drv_data->mutex);
+       return drv_data;
+
+open_error:
+       put_device(found_device);
+       mutex_unlock(&drv_data->mutex);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_open);
+
+/**
+ * intel_mid_i2s_close - release and stop the SSP
+ * @drv_data : handle of corresponding ssp i2s (given by i2s_open function)
+ *
+ * WARNING: This is not -yet- allowed to call close from a read/write callback !
+ *
+ * Output parameters
+ *      none
+ */
+void intel_mid_i2s_close(struct intel_mid_i2s_hdl *drv_data)
+{
+       void __iomem *reg;
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return;
+       mutex_lock(&drv_data->mutex);
+       if (!test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_err(&drv_data->pdev->dev, "not opened but closing?");
+               mutex_unlock(&drv_data->mutex);
+               return;
+       }
+       /* to be modified to wait_event_interruptible_timeout */
+       set_bit(I2S_PORT_CLOSING, &drv_data->flags);
+       dev_dbg(&drv_data->pdev->dev, "Status bit pending write=%d read=%d\n",
+                       test_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags),
+                       test_bit(I2S_PORT_READ_BUSY, &drv_data->flags));
+       if (test_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags) ||
+            test_bit(I2S_PORT_READ_BUSY, &drv_data->flags)) {
+               dev_dbg(&drv_data->pdev->dev, "Pending callback in close...\n");
+       }
+
+       /* waiting below. To be replaced when "DMA_TERMINATE_ALL" fix available */
+       wait_event(drv_data->wq_chan_closing,
+               ((!test_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags)) &&
+                (!test_bit(I2S_PORT_READ_BUSY,  &drv_data->flags))));
+       /* release DMA Channel.. */
+       i2s_dma_stop(drv_data);
+       reg = drv_data->ioaddr;
+       dev_dbg(&drv_data->pdev->dev, "Stopping the SSP\n");
+       i2s_ssp_stop(drv_data);
+       put_device(&drv_data->pdev->dev);
+       write_SSCR0(0, reg);
+       /* pm runtime */
+       pm_runtime_put(&drv_data->pdev->dev);
+       dev_dbg(&(drv_data->pdev->dev), "SSP Stopped.\n");
+       clear_bit(I2S_PORT_CLOSING, &drv_data->flags);
+       clear_bit(I2S_PORT_OPENED, &drv_data->flags);
+       mutex_unlock(&drv_data->mutex);
+}
+EXPORT_SYMBOL_GPL(intel_mid_i2s_close);
+/*
+ * INTERNAL FUNCTIONS
+ */
+
+/**
+ * check_device -  return if the device is the usage we want (usage =*data)
+ * @device_ptr : pointer on device struct
+ * @data : pointer pointer on usage we are looking for
+ *
+ * this is called for each device by find_device() from intel_mid_i2s_open()
+ * Info : when found, the flag of driver is set to I2S_PORT_OPENED
+ *
+ * Output parameters
+ *      integer : return 0 means not the device or already started. go next
+ *               return != 0 means stop the search and return this device
+ */
+static int
+check_device(struct device *device_ptr, void *data)
+{
+       struct pci_dev *pdev;
+       struct intel_mid_i2s_hdl *drv_data;
+       enum intel_mid_i2s_ssp_usage usage;
+       enum intel_mid_i2s_ssp_usage usage_to_find;
+
+       pdev = to_pci_dev(device_ptr);
+       WARN(!pdev, "Pci device=NULL\n");
+       if (!pdev)
+               return 0;
+       drv_data = (struct intel_mid_i2s_hdl *) pci_get_drvdata(pdev);
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return 0;
+       dev_dbg(&(pdev->dev), "Check device pci_dev ptr = 0X%p\n", pdev);
+       usage_to_find = *((enum intel_mid_i2s_ssp_usage *) data);
+       usage = drv_data->usage;
+
+       /* Can be done in one "if" but separated in purpose : take care of
+        * test_and_set_bit that need to be done AFTER the check on usage.
+        */
+       if (usage == usage_to_find) {
+               if (!test_and_set_bit(I2S_PORT_OPENED, &drv_data->flags))
+                       return 1;  /* Already opened, do not use this result */
+       };
+       return 0; /* not usage we look for, or already opened */
+}
+
+/**
+ * i2s_read_done - callback from the _dma tasklet_ after read
+ * @arg : void pointer to that should be driver data (context)
+ *
+ * Output parameters
+ *      none
+ */
+static void i2s_read_done(void *arg)
+{
+       int status = 0;
+
+       struct intel_mid_i2s_hdl *drv_data = arg;
+       void *param_complete;
+       void __iomem *reg ;
+
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return;
+       mutex_lock(&drv_data->mutex);
+       if (!test_bit(I2S_PORT_READ_BUSY, &drv_data->flags))
+               dev_WARN(&drv_data->pdev->dev, "spurious read dma complete");
+
+       dma_unmap_single(NULL, drv_data->read_dst,
+                        drv_data->read_len, DMA_FROM_DEVICE);
+       drv_data->read_len = 0;
+       reg = drv_data->ioaddr;
+       /* Rx fifo overrun Interrupt */
+       change_SSCR0_reg(reg, RIM, SSP_RX_FIFO_OVER_INT_DISABLE);
+       param_complete = drv_data->read_param;
+       /* Do not change order sequence:
+        * READ_BUSY clear, then test PORT_CLOSING
+        * wakeup for close() function
+        */
+       clear_bit(I2S_PORT_READ_BUSY, &drv_data->flags);
+       wake_up(&drv_data->wq_chan_closing);
+       if (test_bit(I2S_PORT_CLOSING, &drv_data->flags)) {
+               dev_dbg(&drv_data->pdev->dev, "read done waking up close");
+               mutex_unlock(&drv_data->mutex);
+               return;
+       }
+       mutex_unlock(&drv_data->mutex);
+       if (drv_data->read_callback != NULL)
+               status = drv_data->read_callback(param_complete);
+       else
+               dev_warn(&drv_data->pdev->dev, "RD done but not callback set");
+
+}
+
+/**
+ * i2s_write_done() : callback from the _dma tasklet_ after write
+ * @arg : void pointer to that should be driver data (context)
+ *
+ * Output parameters
+ *      none
+ */
+static void i2s_write_done(void *arg)
+{
+       int status = 0;
+       void *param_complete;
+       struct intel_mid_i2s_hdl *drv_data = arg;
+       void __iomem *reg ;
+
+       WARN(!drv_data, "Driver data=NULL\n");
+       if (!drv_data)
+               return;
+       mutex_lock(&drv_data->mutex);
+       if (!test_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags))
+               dev_warn(&drv_data->pdev->dev, "spurious write dma complete");
+       dma_unmap_single(NULL, drv_data->read_dst,
+                        drv_data->read_len, DMA_TO_DEVICE);
+       drv_data->read_len = 0;
+       reg = drv_data->ioaddr;
+       change_SSCR0_reg(reg, TIM, SSP_TX_FIFO_UNDER_INT_DISABLE);
+       dev_dbg(&(drv_data->pdev->dev), "DMA channel disable..\n");
+       param_complete = drv_data->write_param;
+       /* Do not change order sequence:
+        * WRITE_BUSY clear, then test PORT_CLOSING
+        * wakeup for close() function
+        */
+       clear_bit(I2S_PORT_WRITE_BUSY, &drv_data->flags);
+       wake_up(&drv_data->wq_chan_closing);
+       if (test_bit(I2S_PORT_CLOSING, &drv_data->flags)) {
+               mutex_unlock(&drv_data->mutex);
+               dev_dbg(&drv_data->pdev->dev, "write done waking up close");
+               return;
+       }
+       mutex_unlock(&drv_data->mutex);
+       if (drv_data->write_callback != NULL)
+               status = drv_data->write_callback(param_complete);
+       else
+               dev_warn(&drv_data->pdev->dev, "WR done but no callback set");
+}
+
+static bool chan_filter(struct dma_chan *chan, void *param)
+{
+       struct intel_mid_i2s_hdl *drv_data = (struct intel_mid_i2s_hdl *)param;
+       bool ret = false;
+
+       if (!drv_data->dmac1)
+               goto out;
+       if (chan->device->dev == &drv_data->dmac1->dev)
+               ret = true;
+out:
+       return ret;
+}
+
+/**
+ * i2s_dma_start - prepare and reserve dma channels
+ * @arg : intel_mid_i2s_hdl pointer to that should be driver data (context)
+ *
+ * "ssp open" context and dmac1 should already be filled in drv_data
+ *
+ * Output parameters
+ *      int : should be zero, else it means error code
+ */
+static int i2s_dma_start(struct intel_mid_i2s_hdl *drv_data)
+{
+       struct intel_mid_dma_slave *rxs, *txs;
+       dma_cap_mask_t mask;
+       int retval = 0;
+       struct pci_dev *l_pdev;
+
+       dev_dbg(&drv_data->pdev->dev, "DMAC1 start\n");
+       drv_data->txchan = NULL;
+       drv_data->rxchan = NULL;
+       l_pdev = drv_data->pdev;
+       /* 1. init rx channel */
+       rxs = &drv_data->dmas_rx;
+       rxs->dirn = DMA_FROM_DEVICE;
+       rxs->hs_mode = LNW_DMA_HW_HS;
+       rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
+       rxs->src_width = LNW_DMA_WIDTH_16BIT;
+       rxs->dst_width = LNW_DMA_WIDTH_32BIT;
+       rxs->src_msize = LNW_DMA_MSIZE_8;
+       rxs->dst_msize = LNW_DMA_MSIZE_8;
+       rxs->device_instance = drv_data->device_instance;
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_MEMCPY, mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       drv_data->rxchan = dma_request_channel(mask, chan_filter, drv_data);
+       if (!drv_data->rxchan) {
+               dev_err(&(drv_data->pdev->dev),
+                       "Could not get Rx channel\n");
+               retval = -2;
+               goto err_exit;
+       }
+       drv_data->rxchan->private = rxs;
+       /* 2. init tx channel */
+       txs = &drv_data->dmas_tx;
+       txs->dirn = DMA_TO_DEVICE;
+       txs->hs_mode = LNW_DMA_HW_HS;
+       txs->cfg_mode = LNW_DMA_MEM_TO_PER;
+       txs->src_width = LNW_DMA_WIDTH_32BIT;
+       txs->dst_width = LNW_DMA_WIDTH_16BIT;
+       txs->src_msize = LNW_DMA_MSIZE_8;
+       txs->dst_msize = LNW_DMA_MSIZE_8;
+       txs->device_instance = drv_data->device_instance;
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_MEMCPY, mask);
+       drv_data->txchan = dma_request_channel(mask, chan_filter, drv_data);
+       if (!drv_data->txchan) {
+               dev_err(&(drv_data->pdev->dev),
+                       "Could not get Tx channel\n");
+               retval = -3;
+               goto free_rxchan;
+       }
+       drv_data->txchan->private = txs;
+       return retval;
+free_rxchan:
+       dma_release_channel(drv_data->rxchan);
+err_exit:
+       return retval;
+}
+
+/**
+ * i2s_dma_stop - release dma channels
+ * @arg : struct intel_mid_i2s_hdl pointer to that should be driver data (context)
+ *
+ * called by intel_mid_i2s_close() context
+ *
+ * Output parameters
+ *      none
+ */
+static void i2s_dma_stop(struct intel_mid_i2s_hdl *drv_data)
+{
+       dev_dbg(&drv_data->pdev->dev, "DMAC1 stop\n");
+       dma_release_channel(drv_data->txchan);
+       dma_release_channel(drv_data->rxchan);
+}
+
+static void i2s_ssp_stop(struct intel_mid_i2s_hdl *drv_data)
+{
+       void __iomem *reg = drv_data->ioaddr;
+       dev_dbg(&drv_data->pdev->dev, "Stop SSP\n");
+       clear_SSCR0_reg(reg, SSE);
+}
+
+static void ssp1_dump_registers(struct intel_mid_i2s_hdl *drv_data)
+{
+       u32 irq_status;
+       void __iomem *reg = drv_data->ioaddr;
+       struct device *ddbg = &(drv_data->pdev->dev);
+       u32 status;
+       irq_status = read_SSSR(reg);
+       dev_dbg(ddbg, "dump SSSR=0x%08X\n", irq_status);
+       status = read_SSCR0(reg);
+       dev_dbg(ddbg, "dump SSCR0=0x%08X\n", status);
+       status = read_SSCR1(reg);
+       dev_dbg(ddbg, "dump SSCR1=0x%08X\n", status);
+       status = read_SSPSP(reg);
+       dev_dbg(ddbg, "dump SSPSP=0x%08X\n", status);
+       status = read_SSTSA(reg);
+       dev_dbg(ddbg, "dump SSTSA=0x%08X\n", status);
+       status = read_SSRSA(reg);
+       dev_dbg(ddbg, "dump SSRSA=0x%08X\n", status);
+       status = read_SSTO(reg);
+       dev_dbg(ddbg, "dump SSTO=0x%08X\n", status);
+       status = read_SSITR(reg);
+       dev_dbg(ddbg, "dump SSITR=0x%08X\n", status);
+       status = read_SSTSS(reg);
+       dev_dbg(ddbg, "dump SSTSS=0x%08X\n", status);
+       status = read_SSACD(reg);
+       dev_dbg(ddbg, "dump SSACD=0x%08X\n", status);
+}
+
+/**
+ * i2s_int(): function that handles the SSP Interrupts (errors)
+ * @irq : IRQ Number
+ * @dev_id : structure that contains driver information
+ *
+ * This interrupts do nothing but warnings in case there is some problems
+ * in I2S connection (underruns, overruns...). This may be reported by adding a
+ * new interface to the driver, but not yet requested by "users" of this driver
+ *
+ * Output parameters
+ *      NA
+ */
+static irqreturn_t i2s_int(int irq, void *dev_id)
+{
+       struct intel_mid_i2s_hdl *drv_data = dev_id;
+       void __iomem *reg;
+       u32 irq_status = 0;
+       u32 mask_status = 0;
+       struct device *ddbg = &(drv_data->pdev->dev);
+       reg = drv_data->ioaddr;
+       irq_status = read_SSSR(reg);
+
+       if (!(irq_status & (drv_data->mask_sr))) {
+               return IRQ_NONE;
+       } else {
+               /* may be improved by using a tasklet to send the error
+                * (underrun,...) to client by using callback
+                */
+               if (irq_status & (SSSR_ROR_MASK << SSSR_ROR_SHIFT)) {
+                       dev_warn(ddbg,
+                               "ssp_int RX FIFO OVER RUN SSSR=0x%08X\n",
+                               irq_status);
+                       mask_status |= (SSSR_ROR_MASK << SSSR_ROR_SHIFT);
+
+               }
+               if (irq_status & (SSSR_TUR_MASK << SSSR_TUR_SHIFT)) {
+                       dev_warn(ddbg,
+                               "ssp_int TX FIFO UNDER RUN SSSR=0x%08X\n",
+                               irq_status);
+                       mask_status |= (SSSR_TUR_MASK << SSSR_TUR_SHIFT);
+
+               }
+               if (irq_status & (SSSR_TINT_MASK << SSSR_TINT_SHIFT)) {
+                       dev_warn(ddbg,
+                               "ssp_int RX TIME OUT SSSR=0x%08X\n",
+                               irq_status);
+                       mask_status |= (SSSR_TINT_MASK << SSSR_TINT_SHIFT);
+
+               }
+               if (irq_status & (SSSR_PINT_MASK << SSSR_PINT_SHIFT)) {
+                       dev_warn(ddbg,
+                               "ssp_int TRAILING BYTE SSSR=0x%08X\n",
+                               irq_status);
+                       mask_status |= (SSSR_PINT_MASK << SSSR_PINT_SHIFT);
+               }
+               if (irq_status & (SSSR_EOC_MASK << SSSR_EOC_SHIFT)) {
+                       dev_warn(ddbg,
+                               "ssp_int END OF CHAIN SSSR=0x%08X\n",
+                               irq_status);
+                       mask_status |= (SSSR_EOC_MASK << SSSR_EOC_SHIFT);
+               }
+               /* clear sticky bits */
+               write_SSSR((irq_status & mask_status), reg);
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * calculate_sspsp_psp - separate function that calculate sspsp register
+ * @ps_settings : pointer of the settings struct
+ *
+ * this function is to simplify/clarify set_ssp_i2s_hw function
+ *
+ *
+ * Output parameters
+ *      u32 : calculated SSPSP register
+ */
+u32 calculate_sspsp_psp(const struct intel_mid_i2s_settings *ps_settings)
+{
+       u32 sspsp;
+       sspsp = SSPSP_reg(FSRT, ps_settings->ssp_frmsync_timing_bit)
+               |SSPSP_reg(ETDS,        ps_settings->ssp_end_transfer_state)
+               |SSPSP_reg(SCMODE,      ps_settings->ssp_serial_clk_mode)
+               |SSPSP_reg(DMYSTOP,     ps_settings->ssp_psp_T4)
+               |SSPSP_reg(SFRMDLY,     ps_settings->ssp_psp_T5)
+               |SSPSP_reg(SFRMWDTH,    ps_settings->ssp_psp_T6)
+               |SSPSP_reg(SFRMP,       ps_settings->ssp_frmsync_pol_bit);
+       return sspsp;
+}
+
+/*
+ * calculate_sscr0_psp: separate function that calculate sscr0 register
+ * @ps_settings : pointer of the settings struct
+ *
+ * this function is to simplify/clarify set_ssp_i2s_hw function
+ *
+ * Output parameters
+ *      u32 : calculated SSCR0 register
+ */
+u32 calculate_sscr0_psp(const struct intel_mid_i2s_settings *ps_settings)
+{
+       u16 l_ssp_data_size = ps_settings->data_size;
+       u32 sscr0;
+       if (l_ssp_data_size > 16) {
+               sscr0 =   SSCR0_reg(DSS, SSCR0_DataSize(l_ssp_data_size - 16))
+                       | SSCR0_reg(EDSS, 1);
+       } else {
+               sscr0 =   SSCR0_reg(DSS, SSCR0_DataSize(l_ssp_data_size))
+                       | SSCR0_reg(EDSS, 0);
+       }
+/*
+Can be replaced by code below :
+sscr0 = SSCR0_reg(DSS, (l_ssp_data_size - 1) & 0x0F)
+| SSCR0_reg(EDSS, ((l_ssp_data_size - 1) & 0x10) >> 8);
+*/
+       sscr0 |= SSCR0_reg(MOD, ps_settings->mode)
+               |SSCR0_reg(FRF, ps_settings->frame_format)
+               |SSCR0_reg(RIM, SSP_RX_FIFO_OVER_INT_DISABLE)
+               |SSCR0_reg(TIM, SSP_TX_FIFO_UNDER_INT_DISABLE);
+       return sscr0;
+}
+
+/**
+ * calculate_sscr1_psp - separate function that calculate sscr1 register
+ * @ps_settings : pointer of the settings struct
+ *
+ * this function is to simplify/clarify set_ssp_i2s_hw function
+ *
+ * Output parameters
+ *      u32 : calculated SSCR1 register
+ */
+u32 calculate_sscr1_psp(const struct intel_mid_i2s_settings *ps_settings)
+{
+       u32 sscr1;
+       sscr1 = SSCR1_reg(SFRMDIR,      ps_settings->sspsfrm_direction)
+               |SSCR1_reg(SCLKDIR,     ps_settings->sspslclk_direction)
+               |SSCR1_reg(TTELP,       ps_settings->tx_tristate_phase)
+               |SSCR1_reg(TTE, ps_settings->tx_tristate_enable)
+               |SSCR1_reg(TRAIL,       ps_settings->ssp_trailing_byte_mode)
+               |SSCR1_reg(TINTE,       ps_settings->ssp_rx_timeout_interrupt_status)
+               |SSCR1_reg(PINTE,       ps_settings->ssp_trailing_byte_interrupt_status)
+               |SSCR1_reg(LBM, ps_settings->ssp_loopback_mode_status)
+               |SSCR1_reg(RWOT,        ps_settings->ssp_duplex_mode)
+               |SSCR1_reg(RFT, SSCR1_RxTresh(ps_settings->ssp_rx_fifo_threshold))
+               |SSCR1_reg(TFT, SSCR1_TxTresh(ps_settings->ssp_tx_fifo_threshold));
+       return sscr1;
+}
+
+/**
+ * set_ssp_i2s_hw - configure the SSP driver according to the ps_settings
+ * @drv_data : structure that contains all details about the SSP Driver
+ * @ps_settings : structure that contains SSP Hardware settings
+ *
+ * it also store ps_settings the drv_data
+ *
+ * Output parameters
+ *      NA
+ */
+static void set_ssp_i2s_hw(struct intel_mid_i2s_hdl *drv_data,
+                       const struct intel_mid_i2s_settings *ps_settings)
+{
+       u32 sscr0 = 0;
+       u32 sscr1 = 0;
+       u32 sstsa = 0;
+       u32 ssrsa = 0;
+       u32 sspsp = 0;
+       u32 sssr = 0;
+       /* Get the SSP Settings */
+       u16 l_ssp_clk_frm_mode = 0xFF;
+       void __iomem *reg = drv_data->ioaddr;
+       struct device *ddbg = &(drv_data->pdev->dev);
+       dev_dbg(ddbg,
+               "setup SSP I2S PCM1 configuration\n");
+       if ((ps_settings->sspsfrm_direction == SSPSFRM_MASTER_MODE)
+          && (ps_settings->sspslclk_direction == SSPSCLK_MASTER_MODE)) {
+               l_ssp_clk_frm_mode = SSP_IN_MASTER_MODE;
+       } else if ((ps_settings->sspsfrm_direction == SSPSFRM_SLAVE_MODE)
+          && (ps_settings->sspslclk_direction == SSPSCLK_SLAVE_MODE)) {
+               l_ssp_clk_frm_mode = SSP_IN_SLAVE_MODE;
+       } else {
+               dev_err(ddbg, "Unsupported I2S PCM1 configuration\n");
+               goto leave;
+       }
+       dev_dbg(ddbg, "SSPSFRM_DIRECTION:%d:\n",
+               ps_settings->sspsfrm_direction);
+       dev_dbg(ddbg, "SSPSCLK_DIRECTION:%d:\n",
+               ps_settings->sspslclk_direction);
+       if (ps_settings->frame_format != PSP_FORMAT) {
+               dev_err(ddbg, "UNSUPPORTED FRAME FORMAT:%d:\n", ps_settings->frame_format);
+               goto leave;
+       }
+       if ((ps_settings->ssp_tx_dma != SSP_TX_DMA_ENABLE)
+       || (ps_settings->ssp_rx_dma != SSP_RX_DMA_ENABLE)) {
+               dev_err(ddbg, "ONLY DMA MODE IS SUPPORTED");
+               goto leave;
+       }
+       /*********** DMA Transfer Mode ***********/
+       dev_dbg(ddbg, "FORMAT :%d:\n", ps_settings->frame_format);
+       sscr0 = calculate_sscr0_psp(ps_settings);
+       dev_dbg(ddbg, " sscr0 :0x%08X\n", sscr0);
+       sscr1 = calculate_sscr1_psp(ps_settings);
+       dev_dbg(ddbg, " sscr1 :0x%08X\n", sscr1);
+       if (ps_settings->mode == SSP_IN_NETWORK_MODE) {
+               dev_dbg(ddbg, "MODE :%d:\n", ps_settings->mode);
+               sscr0 |= SSCR0_reg(FRDC, SSCR0_SlotsPerFrm(ps_settings->frame_rate_divider_control));
+               dev_dbg(ddbg, "sscr0 :0x%08X\n", sscr0);
+               sspsp = calculate_sspsp_psp(ps_settings);
+               dev_dbg(ddbg, "sspsp :0x%08X\n", sspsp);
+               /* set the active TX time slot (bitmap) */
+               sstsa = SSTSA_reg(TTSA, ps_settings->ssp_active_tx_slots_map);
+               /* set the active RX time slot (bitmap) */
+               ssrsa = SSRSA_reg(RTSA, ps_settings->ssp_active_rx_slots_map);
+               if (l_ssp_clk_frm_mode == SSP_IN_MASTER_MODE) {
+                       switch (ps_settings->master_mode_clk_selection) {
+                       case SSP_ONCHIP_CLOCK:
+                               break;
+                       case SSP_NETWORK_CLOCK:
+                               sscr0 |= SSCR0_reg(NCS, 1);
+                               break;
+                       case SSP_EXTERNAL_CLOCK:
+                               sscr0 |= SSCR0_reg(ECS, 1);
+                               break;
+                       case SSP_ONCHIP_AUDIO_CLOCK:
+                               sscr0 |= SSCR0_reg(ACS, 1);
+                               break;
+                       default:
+                               dev_err(ddbg, "Master Mode clk selection UNKNOWN");
+                               break;
+                       }
+                       sspsp |= SSPSP_reg(STRTDLY, ps_settings->ssp_psp_T1)
+                               |SSPSP_reg(DMYSTRT, ps_settings->ssp_psp_T2);
+               } else {        /* Set the Slave Clock Free Running Status */
+                       sscr1 |= SSCR1_reg(SCFR, ps_settings->slave_clk_free_running_status);
+               }
+       } else {  /* SSP_IN_NORMAL_MODE */
+               dev_err(ddbg, "UNSUPPORTED MODE");
+               goto leave;
+       }
+
+       /* Clear status */
+       sssr = (SSSR_BCE_MASK << SSSR_BCE_SHIFT)
+            | (SSSR_TUR_MASK << SSSR_TUR_SHIFT)
+            | (SSSR_TINT_MASK << SSSR_TINT_SHIFT)
+            | (SSSR_PINT_MASK << SSSR_PINT_SHIFT)
+            | (SSSR_ROR_MASK << SSSR_ROR_SHIFT);
+       /* disable SSP */
+       clear_SSCR0_reg(reg, SSE);
+       dev_dbg(ddbg, "WRITE SSCR0 DISABLE\n");
+       /* Clear status */
+       write_SSSR(sssr, reg);
+       dev_dbg(ddbg, "WRITE SSSR: 0x%08X\n", sssr);
+       write_SSCR0(sscr0, reg);
+       dev_dbg(ddbg, "WRITE SSCR0\n");
+       /* first set CR1 without interrupt and service enables */
+       write_SSCR1(sscr1, reg);
+       write_SSPSP(sspsp, reg);
+       write_SSTSA(sstsa, reg);
+       write_SSRSA(ssrsa, reg);
+       /* set the time out for the reception */
+       write_SSTO(0, reg);
+       ssp1_dump_registers(drv_data);
+leave:
+       return;
+}
+
+static int
+intel_mid_i2s_find_usage(struct pci_dev *pdev,
+                        struct intel_mid_i2s_hdl *drv_data,
+                        enum intel_mid_i2s_ssp_usage *usage)
+{
+       int pos;
+       u8  adid;
+       int status = 0;
+
+       *usage = SSP_USAGE_UNASSIGNED;
+       pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+       dev_info((&pdev->dev),
+               "Probe/find capability (VNDR %d pos=0x%x)\n",
+               PCI_CAP_ID_VNDR, pos);
+       if (pos > 0) {
+               pos += PCI_CAP_OFFSET_ADID;
+               pci_read_config_byte(pdev, pos, &adid);
+               dev_info(&(pdev->dev), "Vendor capability adid = 0x%x\n", adid);
+               if (adid == PCI_CAP_ADID_I2S_BT_FM)
+                       *usage  = SSP_USAGE_BLUETOOTH_FM;
+               else if (adid == PCI_CAP_ADID_I2S_MODEM)
+                       *usage  = SSP_USAGE_MODEM;
+               else
+                       *usage  = SSP_USAGE_UNASSIGNED;
+       }
+       /* If there is no capability, check with old PCI_ID */
+#ifdef BYPASS_ADID
+       if (*usage == SSP_USAGE_UNASSIGNED) {
+               dev_warn(&(pdev->dev), "Vendor capability not present/invalid\n");
+               switch (pdev->device) {
+               case MFLD_SSP1_DEVICE_ID:
+                       *usage  = SSP_USAGE_BLUETOOTH_FM;
+                       break;
+               case MFLD_SSP0_DEVICE_ID:
+                       *usage  = SSP_USAGE_MODEM;
+                       break;
+               }
+       }
+#endif
+       if (*usage == SSP_USAGE_UNASSIGNED) {
+               dev_info((&pdev->dev),
+                       "No probe for I2S PCI-ID: %04x:%04x, ADID(0x%x)=0x%x\n",
+                       pdev->vendor, pdev->device, pos, adid);
+               status = -ENODEV;
+               goto err_find_usage;
+       }
+       dev_dbg(&(pdev->dev),
+               "Detected PCI SSP (ID: %04x:%04x) usage =%x\n",
+               pdev->vendor, pdev->device, *usage);
+       dev_dbg(&(pdev->dev),
+               " found PCI SSP controller(ID: %04x:%04x)\n",
+               pdev->vendor, pdev->device);
+       /* Init the driver data structure fields*/
+       switch (pdev->device) {
+       case MFLD_SSP1_DEVICE_ID:
+               drv_data->device_instance = DMA1C_DEVICE_INSTANCE_SSP1;
+               break;
+       case MFLD_SSP0_DEVICE_ID:
+               drv_data->device_instance = DMA1C_DEVICE_INSTANCE_SSP0;
+               break;
+       default:
+               dev_err(&(pdev->dev),
+                       "Can not determine dma device instance (PCI ID:%04x)\n",
+                       pdev->device);
+               status = -ENODEV;
+               goto err_find_usage;
+       }
+       status = pci_enable_device(pdev);
+       if (status)
+               dev_err((&pdev->dev), "Can not enable device.Err=%d\n", status);
+err_find_usage:
+       return status;
+}
+
+/**
+ * intel_mid_i2s_probe - probing function for the pci selected
+ * @pdev : pci_dev pointer that is probed
+ * @ent : pci_device_id
+ *
+ * Output parameters
+ *      NA
+ */
+static int intel_mid_i2s_probe(struct pci_dev *pdev,
+                               const struct pci_device_id *ent)
+{
+       struct intel_mid_i2s_hdl *drv_data;
+       int status = 0;
+       enum intel_mid_i2s_ssp_usage usage;
+
+       drv_data = kzalloc(sizeof(struct intel_mid_i2s_hdl), GFP_KERNEL);
+       dev_dbg(&(pdev->dev), "%s Probe, drv_data =%p\n", DRIVER_NAME, drv_data);
+       if (!drv_data) {
+               dev_err((&pdev->dev), "Can't alloc driver data in probe\n");
+               status = -ENOMEM;
+               goto leave;
+       }
+       dev_info((&pdev->dev), "Detected PCI SSP (ID: %04x:%04x)\n", pdev->vendor, pdev->device);
+       status = intel_mid_i2s_find_usage(pdev, drv_data, &usage);
+       if (status)
+               goto err_i2s_probe0;
+       mutex_init(&drv_data->mutex);
+       drv_data->pdev = pdev;
+       drv_data->usage = usage;
+       /*
+        * Get basic io resource and map it for SSP1 [BAR=0]
+        */
+       if ((pdev->device == MFLD_SSP1_DEVICE_ID) ||
+           (pdev->device == MFLD_SSP0_DEVICE_ID)) {
+               drv_data->paddr = pci_resource_start(pdev, MRST_SSP_BAR);
+               drv_data->iolen = pci_resource_len(pdev, MRST_SSP_BAR);
+               status = pci_request_region(pdev, MRST_SSP_BAR, dev_name(&pdev->dev));
+               /* map bus memory into CPU space */
+               drv_data->ioaddr = pci_ioremap_bar(pdev, MRST_SSP_BAR);
+       } else {
+               dev_err(&pdev->dev,
+                       "Don't know which BAR to usefor this SSP PCDID=%x\n",
+                       pdev->device);
+               status = -ENODEV;
+               goto err_i2s_probe1;
+       }
+       dev_dbg(&(pdev->dev), "paddr = : %x\n", drv_data->paddr);
+       dev_dbg(&(pdev->dev), "iolen = : %d\n", drv_data->iolen);
+       if (status) {
+               dev_err((&pdev->dev), "Can't request region. err=%d\n", status);
+               goto err_i2s_probe1;
+       }
+       if (!drv_data->ioaddr) {
+               dev_err((&pdev->dev), "ioremap_nocache error\n");
+               status = -ENOMEM;
+               goto err_i2s_probe2;
+       }
+       dev_dbg(&(pdev->dev), "ioaddr = : %p\n", drv_data->ioaddr);
+       /* prepare for DMA channel allocation */
+       /* get the pci_dev structure pointer */
+       /* Check the SSP, if SSP3, then another DMA is used (GPDMA..) */
+       if ((pdev->device == MFLD_SSP1_DEVICE_ID) ||
+           (pdev->device == MFLD_SSP0_DEVICE_ID)) {
+               drv_data->dmac1 = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                                MFLD_LPE_DMA_DEVICE_ID,
+                                                NULL);
+       } else {
+               dev_err(&pdev->dev,
+                       "Don't know dma device ID for this SSP PCDID=%x\n",
+                       pdev->device);
+               goto err_i2s_probe3;
+       }
+       /* in case the stop dma have to wait for end of callbacks   */
+       /* This will be removed when TERMINATE_ALL available in DMA */
+       init_waitqueue_head(&drv_data->wq_chan_closing);
+       if (!drv_data->dmac1) {
+               dev_err(&(drv_data->pdev->dev), "Can't find DMAC1, dma init failed\n");
+               status = -ENODEV;
+               goto err_i2s_probe3;
+       }
+       /* increment ref count of pci device structure already done by */
+       /* pci_get_device. will do a pci_dev_put when exiting the module */
+       pci_set_drvdata(pdev, drv_data);
+       /* set SSP FrameSync and CLK direction in INPUT mode in order
+        * to avoid disturbing peripherals
+        */
+       write_SSCR1((SSCR1_SFRMDIR_MASK<<SSCR1_SFRMDIR_SHIFT)
+                 | (SSCR1_SCLKDIR_MASK<<SSCR1_SCLKDIR_SHIFT),
+       drv_data->ioaddr);
+       /* Attach to IRQ */
+       drv_data->irq = pdev->irq;
+       dev_dbg(&(pdev->dev), "attaching to IRQ: %04x\n", pdev->irq);
+       status = request_irq(drv_data->irq, i2s_int, IRQF_SHARED, "i2s ssp", drv_data);
+       if (status < 0) {
+               dev_err(&pdev->dev, "can not get IRQ. status err=%d\n", status);
+               goto err_i2s_probe3;
+       }
+       pm_runtime_enable(&(drv_data->pdev->dev));
+       goto leave;
+err_i2s_probe3:
+       iounmap(drv_data->ioaddr);
+err_i2s_probe2:
+       pci_release_region(pdev, MRST_SSP_BAR);
+err_i2s_probe1:
+       pci_disable_device(pdev);
+err_i2s_probe0:
+       kfree(drv_data);
+leave:
+       return status;
+}
+
+static void __devexit intel_mid_i2s_remove(struct pci_dev *pdev)
+{
+       struct intel_mid_i2s_hdl *drv_data;
+
+       drv_data = pci_get_drvdata(pdev);
+       if (!drv_data) {
+               dev_err(&pdev->dev, "no drv_data in pci device to remove!\n");
+               goto leave;
+       }
+       if (test_bit(I2S_PORT_OPENED, &drv_data->flags)) {
+               dev_warn(&pdev->dev, "Not closed before removing pci_dev!\n");
+               intel_mid_i2s_close(drv_data);
+       }
+       pci_set_drvdata(pdev, NULL);
+       /* Stop DMA is already done during close()  */
+       pci_dev_put(drv_data->dmac1);
+       /* Disable the SSP at the peripheral and SOC level */
+       write_SSCR0(0, drv_data->ioaddr);
+       free_irq(drv_data->irq, drv_data);
+       iounmap(drv_data->ioaddr);
+       pci_release_region(pdev, MRST_SSP_BAR);
+       pci_release_region(pdev, MRST_LPE_BAR);
+       pci_disable_device(pdev);
+       kfree(drv_data);
+leave:
+       return;
+}
+
+/**
+ * intel_mid_i2s_init - register pci driver
+ *
+ */
+static int __init intel_mid_i2s_init(void)
+{
+       return pci_register_driver(&intel_mid_i2s_driver);
+}
+
+static void __exit intel_mid_i2s_exit(void)
+{
+       pci_unregister_driver(&intel_mid_i2s_driver);
+}
+
+
+module_init(intel_mid_i2s_init);
+module_exit(intel_mid_i2s_exit);
+
+
+
diff --git a/sound/pci/intel_mid_i2s/intel_mid_i2s.h b/sound/pci/intel_mid_i2s/intel_mid_i2s.h
new file mode 100644
index 0000000..ef29ce7
--- /dev/null
+++ b/sound/pci/intel_mid_i2s/intel_mid_i2s.h
@@ -0,0 +1,500 @@
+/*
+  * <Driver for I2S protocol on SSP (Moorestown and Medfield hardware)>
+  * Copyright (c) 2010, Intel Corporation.
+  * Louis LE GALL <louis.le.gall intel.com>
+  *
+  * This program is free software; you can redistribute it and/or modify it
+  * under the terms and conditions of the GNU General Public License,
+  * version 2, as published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope it will be useful, but WITHOUT
+  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  * more details.
+  *
+  * You should have received a copy of the GNU General Public License along with
+  * this program; if not, write to the Free Software Foundation, Inc.,
+  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+  */
+#define DRIVER_NAME "I2S SSP Driver"
+/*
+ * Defines
+ */
+#define MFLD_SSP1_DEVICE_ID 0x0825     /* FOR MFLD */
+#define MRST_SSP0_DEVICE_ID 0x0815     /* FOR MRST */
+#define MFLD_SSP0_DEVICE_ID 0x0832     /* FOR MFLD */
+
+#define MRST_LPE_DMA_DEVICE_ID 0x0814
+#define MFLD_LPE_DMA_DEVICE_ID 0x0830
+
+/* SSP1 PCI device Base Address Register */
+#define MRST_SSP_BAR   0
+#define MRST_LPE_BAR   1
+#define DMA1C_DEVICE_INSTANCE_SSP0 0
+#define DMA1C_DEVICE_INSTANCE_SSP1 1
+#define OFFSET_SSCR0   0x00
+#define OFFSET_SSCR1   0x04
+#define OFFSET_SSSR            0x08
+#define OFFSET_SSITR   0x0c
+#define OFFSET_SSDR            0x10
+#define OFFSET_SSTO            0x28
+#define OFFSET_SSPSP   0x2c
+#define OFFSET_SSTSA   0x30    /* SSP Tx Timeslot Active */
+#define OFFSET_SSRSA   0x34    /* SSP Rx Timeslot Active */
+/* SST register map */
+#define OFFSET_LPE_CSR                 0x00
+#define OFFSET_LPE_PISR                        0x08
+#define OFFSET_LPE_PIMR                        0x10
+#define OFFSET_LPE_ISRX                        0x18
+#define OFFSET_LPE_IMRX                        0x28
+#define OFFSET_LPE_IPCX                        0x38    /* IPC IA-SST */
+#define OFFSET_LPE_IPCD                        0x40    /* IPC SST-IA */
+#define OFFSET_LPE_ISRD                        0x20    /* dummy register for*/
+                                               /* shim workaround   */
+#define OFFSET_LPE_SHIM_SIZE   0X44
+
+#define SSP_IN_MASTER_MODE             0x0
+#define SSP_IN_SLAVE_MODE              0x1
+
+/*
+ *     Macros
+ */
+#define DEFINE_SSP_REG(reg, off) \
+static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \
+static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); }
+DEFINE_SSP_REG(SSCR0, 0x00)
+DEFINE_SSP_REG(SSCR1, 0x04)
+DEFINE_SSP_REG(SSSR, 0x08)
+DEFINE_SSP_REG(SSITR, 0x0c)
+DEFINE_SSP_REG(SSDR, 0x10)
+DEFINE_SSP_REG(SSTO, 0x28)
+DEFINE_SSP_REG(SSPSP, 0x2c)
+DEFINE_SSP_REG(SSTSA, 0x30)
+DEFINE_SSP_REG(SSRSA, 0x34)
+DEFINE_SSP_REG(SSTSS, 0x38)
+DEFINE_SSP_REG(SSACD, 0x3C)
+DEFINE_SSP_REG(I2CCTRL, 0x00);
+DEFINE_SSP_REG(I2CDATA, 0x04);
+/*
+ * Langwell SSP serial port register definitions
+ */
+#define SSCR0_DSS_MASK   0x0F  /* Data Size Select [4..16] */
+#define SSCR0_DSS_SHIFT  0
+#define SSCR0_FRF_MASK   0x03  /* FRame Format */
+#define SSCR0_FRF_SHIFT  4
+#define SSCR0_ECS_MASK   0x01  /* External clock select */
+#define SSCR0_ECS_SHIFT  6
+#define SSCR0_SSE_MASK   0x01  /* Synchronous Serial Port Enable */
+#define SSCR0_SSE_SHIFT  7
+#define SSCR0_SCR_MASK   0xFFF /* Not implemented */
+#define SSCR0_SCR_SHIFT  8
+#define SSCR0_EDSS_MASK  0x1   /* Extended data size select */
+#define SSCR0_EDSS_SHIFT 20
+#define SSCR0_NCS_MASK   0x1   /* Network clock select */
+#define SSCR0_NCS_SHIFT  21
+#define SSCR0_RIM_MASK   0x1   /* Receive FIFO overrrun int mask */
+#define SSCR0_RIM_SHIFT  22
+#define SSCR0_TIM_MASK   0x1   /* Transmit FIFO underrun int mask */
+#define SSCR0_TIM_SHIFT  23
+#define SSCR0_FRDC_MASK  0x7   /* Frame Rate Divider Control */
+#define SSCR0_FRDC_SHIFT 24
+#define SSCR0_ACS_MASK   0x1   /* Audio clock select */
+#define SSCR0_ACS_SHIFT  30
+#define SSCR0_MOD_MASK   0x1   /* Mode (normal or network) */
+#define SSCR0_MOD_SHIFT  31
+
+#define SSCR0_DataSize(x)     ((x) - 1)        /* Data Size Select [4..16] */
+#define SSCR0_SlotsPerFrm(x)  ((x) - 1)        /* Time slots per frame */
+#define SSCR0_SerClkDiv(x)    ((x) - 1)        /* Divisor [1..4096],... */
+                                        /*...not implemented on Langwell */
+#define SSCR1_TTELP_MASK     0x1       /* TXD Tristate Enable on Last Phase */
+#define SSCR1_TTELP_SHIFT    31
+#define SSCR1_TTE_MASK      0x1        /* TXD Tristate Enable */
+#define SSCR1_TTE_SHIFT      30
+#define SSCR1_EBCEI_MASK     0x1       /* Enable Bit Count Error Interrupt */
+#define SSCR1_EBCEI_SHIFT    29
+#define SSCR1_SCFR_MASK      0x1       /* Slave Clock Running */
+#define SSCR1_SCFR_SHIFT     28
+#define SSCR1_ECRA_MASK      0x1       /* Enable Clock Request A */
+#define SSCR1_ECRA_SHIFT     27
+#define SSCR1_ECRB_MASK      0x1       /* Enable Clock Request B */
+#define SSCR1_ECRB_SHIFT     26
+#define SSCR1_SCLKDIR_MASK   0x1       /* SSPCLK Direction */
+#define SSCR1_SCLKDIR_SHIFT  25
+#define SSCR1_SFRMDIR_MASK   0x1       /* SSPFRM Direction */
+#define SSCR1_SFRMDIR_SHIFT  24
+#define SSCR1_RWOT_MASK      0x1       /* Receive without Transmit */
+#define SSCR1_RWOT_SHIFT     23
+#define SSCR1_TRAIL_MASK     0x1       /* Trailing Byte */
+#define SSCR1_TRAIL_SHIFT    22
+#define SSCR1_TSRE_MASK      0x1       /* DMA Transmit Service Request Enable*/
+#define SSCR1_TSRE_SHIFT     21
+#define SSCR1_RSRE_MASK      0x1       /* DMA Receive Service Request Enable */
+#define SSCR1_RSRE_SHIFT     20
+#define SSCR1_TINTE_MASK     0x1       /* Receiver Time-out Interrupt Enable */
+#define SSCR1_TINTE_SHIFT    19
+#define SSCR1_PINTE_MASK     0x1       /* Periph. Trailing Byte Int. Enable */
+#define SSCR1_PINTE_SHIFT    18
+#define SSCR1_IFS_MASK       0x1       /* Invert Frame Signal */
+#define SSCR1_IFS_SHIFT      16
+#define SSCR1_STFR_MASK      0x1       /* Select FIFO for EFWR: test mode */
+#define SSCR1_STFR_SHIFT     15
+#define SSCR1_EFWR_MASK      0x1       /* Enable FIFO Write/Read: test mode */
+#define SSCR1_EFWR_SHIFT     14
+#define SSCR1_RFT_MASK       0xF       /* Receive FIFO Trigger Threshold */
+#define SSCR1_RFT_SHIFT      10
+#define SSCR1_TFT_MASK       0xF       /* Transmit FIFO Trigger Threshold */
+#define SSCR1_TFT_SHIFT      6
+#define SSCR1_MWDS_MASK      0x1       /* Microwire Transmit Data Size */
+#define SSCR1_MWDS_SHIFT     5
+#define SSCR1_SPH_MASK       0x1       /* Motorola SPI SSPSCLK phase setting */
+#define SSCR1_SPH_SHIFT      4
+#define SSCR1_SPO_MASK       0x1       /* Motorola SPI SSPSCLK polarity */
+#define SSCR1_SPO_SHIFT      3
+#define SSCR1_LBM_MASK       0x1       /* Loopback mode: test mode */
+#define SSCR1_LBM_SHIFT      2
+#define SSCR1_TIE_MASK       0x1       /* Transmit FIFO Interrupt Enable */
+#define SSCR1_TIE_SHIFT      1
+#define SSCR1_RIE_MASK       0x1       /* Receive FIFO Interrupt Enable */
+#define SSCR1_RIE_SHIFT      0
+
+#define SSCR1_RxTresh(x) ((x) - 1)     /* level [1..16] */
+#define SSCR1_TxTresh(x) ((x) - 1)     /* level [1..16] */
+
+#define SSPSP_FSRT_MASK      0x1       /* Frame Sync Relative Timing Bit */
+#define SSPSP_FSRT_SHIFT     25
+#define SSPSP_DMYSTOP_MASK   0x3       /* Dummy Stop in Number of SSPSCLKs:T4*/
+#define SSPSP_DMYSTOP_SHIFT  23
+#define SSPSP_SFRMWDTH_MASK  0x3F      /* Serial Frame width : T6 */
+#define SSPSP_SFRMWDTH_SHIFT 16
+#define SSPSP_SFRMDLY_MASK   0x7F      /* Serial Fr. Delay in 1/2SSPSCLKs:T5 */
+#define SSPSP_SFRMDLY_SHIFT  9
+#define SSPSP_DMYSTRT_MASK   0x3       /* Dummy Start in Number of SSPSCLKs..*/
+#define SSPSP_DMYSTRT_SHIFT  7     /*...after STRTDLY, T2 (master mode only) */
+#define SSPSP_STRTDLY_MASK   0x7       /* Start Delay, T1 (master mode only) */
+#define SSPSP_STRTDLY_SHIFT  4
+#define SSPSP_ETDS_MASK      0x1       /* End of Transfer Data State */
+#define SSPSP_ETDS_SHIFT     3
+#define SSPSP_SFRMP_MASK     0x1       /* Serial Frame Polarity */
+#define SSPSP_SFRMP_SHIFT    2
+#define SSPSP_SCMODE_MASK    0x3       /* Serial bit-rate Clock Mode */
+#define SSPSP_SCMODE_SHIFT   0
+
+#define SSTSA_TTSA_MASK      0xFF
+#define SSTSA_TTSA_SHIFT     0
+
+#define SSRSA_RTSA_MASK      0xFF
+#define SSRSA_RTSA_SHIFT     0
+
+#define SSSR_BCE_MASK   0x1    /* Bit Count Error: Read/Write 1 to Clear */
+#define SSSR_BCE_SHIFT  23
+#define SSSR_CSS_MASK   0x1    /* Clock Synchronization Status */
+#define SSSR_CSS_SHIFT  22
+#define SSSR_TUR_MASK   0x1    /* Transmit FIFO UnderRun: Rd/Wr 1 to Clear */
+#define SSSR_TUR_SHIFT  21
+#define SSSR_EOC_MASK   0x1    /* End Of Chain: Read/Write 1 to Clear */
+#define SSSR_EOC_SHIFT  20
+#define SSSR_TINT_MASK  0x1    /* Receiver Time-out Interrupt:... */
+#define SSSR_TINT_SHIFT 19     /* ...Read/Write 1 to Clear */
+#define SSSR_PINT_MASK  0x1    /* Peripheral Trailing Byte Interrupt:... */
+#define SSSR_PINT_SHIFT 18     /* ...Read/Write 1 to Clear */
+#define SSSR_RFL_MASK   0xF    /* Receive FIFO Level */
+#define SSSR_RFL_SHIFT  12
+#define SSSR_TFL_MASK   0xF    /* Transmit FIFO Level */
+#define SSSR_TFL_SHIFT  8
+#define SSSR_ROR_MASK   0x1    /* Receive FIFO Overrun: Read/Write 1 to Clear*/
+#define SSSR_ROR_SHIFT  7
+#define SSSR_RFS_MASK   0x1    /* Receive FIFO Service Request */
+#define SSSR_RFS_SHIFT  6
+#define SSSR_TFS_MASK   0x1    /* Transmit FIFO Service Request */
+#define SSSR_TFS_SHIFT  5
+#define SSSR_BSY_MASK   0x1    /* SSP Busy */
+#define SSSR_BSY_SHIFT  4
+#define SSSR_RNE_MASK   0x1    /* Receive FIFO not empty */
+#define SSSR_RNE_SHIFT  3
+#define SSSR_TFN_MASK   0x1    /* Transmit FIFO not Full */
+#define SSSR_TFN_SHIFT  2
+
+
+#define SSP_OFF 0
+#define SSP_ON  1
+
+/* bit I2S_PORT_OPENED lock for open/close
+ * bit I2S_PORT_READ_BUSY lock for read requests (serialized)
+ * bit I2S_PORT_WRITE_BUSY lock for write requests (serialized)
+ * bit I2S_PORT_CLOSING means close on going, waiting for pending callbacks.
+ */
+
+enum i2s_flags {
+       I2S_PORT_OPENED,
+       I2S_PORT_WRITE_BUSY,
+       I2S_PORT_READ_BUSY,
+       I2S_PORT_CLOSING
+};
+
+#define FIFO_SIZE 16
+/*
+ *     Structures Definition
+ */
+
+/**
+ * struct intel_mid_i2s_data - context struct to keep SSP I2S data
+ * @pdev: pci dev pointer corresponding to context
+ * @paddr:
+ * @ioaddr:
+ * @iolen:
+ * @irq:
+ * @clear_sr:
+ * @mask_sr:
+ * @dmac1:
+ * @dmas_tx: dma slave structure for transmit
+ * @dmas_rx: dma slave structure for receive
+ * @txchan: Dma channel for transmit
+ * @rxchan: Dma channel for receive
+ *
+ * @read_done:
+ * @read_dst:
+ * @read_len:
+ *
+ * @write_done:
+ * @write_src:
+ * @write_len:
+ *
+ * @mutex:  a mutex to make sure we have once-at-time critical functions.
+ *
+ * Longer description
+ */
+
+/* Locking rules:
+ *
+ * All the fields, not listed below, are set during probe, and then read only
+ * So they do not require locking
+ *
+ * The fields that require locking are related to the I2S read and write
+ * requests.
+ *
+ * We allow only 1 read at a time, and 1 write at a time.
+ * We allow read in parallel of write but use separate variables.
+ * We allow only 1 user per SSP/I2S port.
+ * Typically this user will be a dedicated PulseAudio RT thread communicating
+ * with cmt-speech driver which in turns communicates with intel_mid_ssp
+ * driver.
+ * PCM mixing is done before access to kernel drivers;typically within
+ * PulseAudio or after; typically within the modem.
+ * So no concurrent users, per I2S channel, to this driver are allowed
+ * The read & write are triggered from a USER context
+ * The read & write callbacks are called from a BH context
+ * You should have not callback pending before calling close, close will wait
+ * for remaining callback calls.
+ * It is not allowed to call close function from read/write callback threads.
+ *
+ * Locking is handled via drv_data->flags & atomic bitwise operations
+ *
+ * I2S0 is dedicated for PCM transfer to/from the modem module
+ * I2S1 is dedicated for PCM transfer to/from the Bluetooth or FM module
+ *
+ * read_done:
+ * read_len:
+ * read_dst:
+ *
+ * write_done:
+ * write_src:
+ * write_len:
+ *
+ * mutex:  a mutex to make sure we have once-at-time critical functions.
+ *             once-at-a-time actions functions are:
+ *                     -intel_mid_i2s_open
+ *                     -intel_mid_i2s_close
+ *                     -intel_mid_i2s_rd_req
+ *                     -intel_mid_i2s_wr_req
+ *                     -intel_mid_i2s_set_rd_cb
+ *                     -intel_mid_i2s_set_wr_cb
+ * These functions should not be called during a lock() neither in interrupt.
+ */
+
+struct intel_mid_i2s_hdl {
+       /* Driver model hookup */
+       struct pci_dev *pdev;
+       /* register addresses */
+       dma_addr_t paddr;
+       void __iomem *ioaddr;
+       u32 iolen;
+       int irq;
+
+       /* SSP masks */
+       u32 clear_sr;
+       u32 mask_sr;
+
+       /* SSP Configuration */
+       /* DMA info */
+       struct pci_dev *dmac1;
+       wait_queue_head_t wq_chan_closing;
+
+       struct intel_mid_dma_slave dmas_tx;
+       struct intel_mid_dma_slave dmas_rx;
+       struct dma_chan *txchan;
+       struct dma_chan *rxchan;
+
+       unsigned int device_instance;
+       /* Call back functions */
+       int (*read_callback)(void *param);
+       dma_addr_t read_dst;
+       size_t read_len;     /* read_len > 0 <=> read_dma_running */
+       void *read_param;        /* context param for callback */
+       int (*write_callback)(void *param);
+       dma_addr_t write_src;
+       size_t write_len;       /* write_len > 0 <=> read_dma_running */
+       void *write_param;      /* context param for callback */
+
+       unsigned long flags;
+       struct mutex mutex;
+       enum intel_mid_i2s_ssp_usage usage;
+
+       struct intel_mid_i2s_settings current_settings;
+
+};
+
+static void i2s_read_done(void *arg);
+static void i2s_write_done(void *arg);
+static bool chan_filter(struct dma_chan *chan, void *param);
+static void i2s_dma_stop(struct intel_mid_i2s_hdl *drv_data);
+static int i2s_dma_start(struct intel_mid_i2s_hdl *drv_data);
+static void ssp1_dump_registers(struct intel_mid_i2s_hdl *);
+static irqreturn_t i2s_int(int irq, void *dev_id);
+static void set_ssp_i2s_hw(struct intel_mid_i2s_hdl *drv_data,
+               const struct intel_mid_i2s_settings *ps_settings);
+static int check_device(struct device *device_ptr, void *data);
+static int intel_mid_i2s_runtime_resume(struct device *device_ptr);
+static int intel_mid_i2s_runtime_suspend(struct device *device_ptr);
+static int intel_mid_i2s_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent);
+static void intel_mid_i2s_remove(struct pci_dev *pdev);
+static void i2s_ssp_stop(struct intel_mid_i2s_hdl *drv_data);
+/*static int bt_pcm_dma_init(struct intel_mid_i2s_hdl *drv_data);*/
+
+
+#ifdef CONFIG_PM
+static int  intel_mid_i2s_driver_suspend(struct pci_dev *dev,
+                                         pm_message_t state);
+static int intel_mid_i2s_driver_resume(struct pci_dev *dev);
+#endif
+
+/*
+ * These define will clarify source code when accessing SSCRx registers
+ */
+
+#define SSCR0_reg(regbit, value)                                       \
+       (((value) & SSCR0_##regbit##_MASK) << SSCR0_##regbit##_SHIFT)
+
+#define SSCR1_reg(regbit, value)                                       \
+       (((value) & SSCR1_##regbit##_MASK) << SSCR1_##regbit##_SHIFT)
+
+#define SSPSP_reg(regbit, value)                                       \
+       (((value) & SSPSP_##regbit##_MASK) << SSPSP_##regbit##_SHIFT)
+
+#define SSRSA_reg(regbit, value)                                       \
+       (((value) & SSRSA_##regbit##_MASK) << SSRSA_##regbit##_SHIFT)
+#define SSTSA_reg(regbit, value)                                       \
+       (((value) & SSTSA_##regbit##_MASK) << SSTSA_##regbit##_SHIFT)
+
+
+#define change_SSCR0_reg(reg_pointer, regbit, value)                     \
+       write_SSCR0((read_SSCR0(reg_pointer)                              \
+       & (~((SSCR0_##regbit##_MASK << SSCR0_##regbit##_SHIFT))))         \
+       | (((value) & SSCR0_##regbit##_MASK) << SSCR0_##regbit##_SHIFT),  \
+       reg_pointer);
+
+#define set_SSCR0_reg(reg_pointer, regbit)                               \
+       write_SSCR0(read_SSCR0(reg_pointer)                               \
+       | (SSCR0_##regbit##_MASK << SSCR0_##regbit##_SHIFT),            \
+       reg_pointer);
+
+#define clear_SSCR0_reg(reg_pointer, regbit)                             \
+       write_SSCR0((read_SSCR0(reg_pointer)                              \
+       & (~((SSCR0_##regbit##_MASK << SSCR0_##regbit##_SHIFT)))),        \
+       reg_pointer);
+
+#define change_SSCR1_reg(reg_pointer, regbit, value)                     \
+       write_SSCR1((read_SSCR1(reg_pointer)                              \
+       & (~((SSCR1_##regbit##_MASK << SSCR1_##regbit##_SHIFT))))         \
+       | (((value) & SSCR1_##regbit##_MASK) << SSCR1_##regbit##_SHIFT),  \
+       reg_pointer);
+
+#define set_SSCR1_reg(reg_pointer, regbit)                               \
+       write_SSCR1(read_SSCR1(reg_pointer)                               \
+       | (SSCR1_##regbit##_MASK << SSCR1_##regbit##_SHIFT),              \
+       reg_pointer);
+
+#define clear_SSCR1_reg(reg_pointer, regbit)                             \
+       write_SSCR1((read_SSCR1(reg_pointer)                              \
+       & (~((SSCR1_##regbit##_MASK << SSCR1_##regbit##_SHIFT)))),        \
+       reg_pointer);
+
+/* RX FIFO level */
+#define GET_SSSR_val(x, regb)                                            \
+       ((x & (SSSR_##regb##_MASK<<SSSR_##regb##_SHIFT))>>SSSR_##regb##_SHIFT)
+
+
+/*
+ * SSP hardware can be configured as I2S, PCM, SPI...
+ * In order to allow flexibility without modifying the software driver, the
+ * PCI header uses the configuration register 'adid':
+ *
+ * The PCI header associated to SSP devices includes a configuration register.
+ * It provides information to a driver which is probed for the SSP, specifying
+ * in which way the SSP is supposed to be used.
+ * Here is the format of this configuration register (8 bits):
+ *
+ *   bits 2..0: Mode
+ *       000: Invalid, the register should be ignored
+ *       001: SSP to be used as SPI controller
+ *       010: SSP to be used in I2S/ISS mode
+ *       other: Reserved
+ *
+ *   bits 5..3: Configuration
+ *       In I2S/ISS mode:
+ *               000: Invalid
+ *               001: Bluetooth
+ *               010: Modem
+ *               other: Reserved
+ *       In SPI mode:
+ *               Value is the SPI bus number connected to the SSP.
+ *               To be used for registration to the Linux SPI
+ *               framework.
+ *
+ *   bit 6: SPI slave
+ *       Relevant in SPI mode only. If set, indicates the SPI clock
+ *       is not provided by the SSP: SPI slave mode.
+ *
+ *   bit 7: Reserved (0)
+ *
+ *   This configuration register is implemented in the adid field of the
+ *   Vendor Specific PCI capability associated to the SSP. The format of
+ *   this capability is:
+ *
+ *   uint8_t     capId;              < Capability ID (vendor-specific)
+ *   uint8_t     nextCap;            < Next Item Ptr
+ *   uint8_t     length;             < Size of this capability (7)
+ *   uint8_t     version;            < Version of this capability (1)
+ *   uint8_t     lss;                < Logical subsystem info
+ *                                         Bit 7 = PMU (0 = NC, 1 = SC)
+ *                                         Bits 6:0 = LSS ID
+ *   uint8_t     apmc;               < Additional PM capabilities
+ *                                         Bit 7 = Rsvd
+ *                                         Bit 6 = Wake capable
+ *                                         Bit 5 = D3 support
+ *                                         Bit 4 = D2 support
+ *                                         Bit 3 = D1 support
+ *                                         Bit 2 = D0i3 support
+ *                                         Bit 1 = D0i2 support
+ *                                         Bit 0 = D0i1 support
+ *   uint8_t     adid;               < Additional device ID (dev-specific)
+ *   uint8_t     rsvd;               < Reserved for future use
+ *
+ *   The capability data are in the PCI configuration space and the
+ *   adid field can be modified using BMP tool.
+ */
+/* ADDID = Additional Device ID */
+#define PCI_CAP_OFFSET_ADID 6
+
+
--
1.6.6.1

---------------------------------------------------------------------
Intel Corporation SAS (French simplified joint stock company)
Registered headquarters: "Les Montalets"- 2, rue de Paris, 
92196 Meudon Cedex, France
Registration Number:  302 456 199 R.C.S. NANTERRE
Capital: 4,572,000 Euros

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

Attachment: 0001-New-low-level-driver-Intel_mid_i2s-provide-support-alsa.patch
Description: 0001-New-low-level-driver-Intel_mid_i2s-provide-support-alsa.patch

_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux