summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Marklund <robert.marklund@stericsson.com>2011-03-18 15:47:12 +0100
committerRobert Marklund <robert.marklund@stericsson.com>2011-10-05 10:39:27 +0200
commitd8fc840f5898ad13acba96206a3f106e271b5e76 (patch)
treee84f688ea167cbe9bc8858be5811399fb59519be
parentbebe0edaeab0e389fa15c68552d741eeab53f7f6 (diff)
misc: Add i2s driver
Signed-off-by: Robert Marklund <robert.marklund@stericsson.com>
-rw-r--r--Documentation/DocBook/Makefile3
-rw-r--r--Documentation/DocBook/i2s.tmpl97
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile3
-rw-r--r--drivers/misc/i2s/Kconfig29
-rw-r--r--drivers/misc/i2s/Makefile8
-rw-r--r--drivers/misc/i2s/i2s.c597
-rw-r--r--drivers/misc/i2s/i2s_test_protocol_driver.c305
-rw-r--r--drivers/misc/i2s/msp_i2s.c2045
-rw-r--r--drivers/misc/i2s/msp_i2s.h362
-rw-r--r--include/linux/i2s/i2s.h225
-rw-r--r--include/linux/i2s/i2s_test_prot.h44
12 files changed, 3717 insertions, 2 deletions
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 3cebfa0d161..fdbc16c6e33 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -14,7 +14,8 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
80211.xml debugobjects.xml sh.xml regulator.xml \
alsa-driver-api.xml writing-an-alsa-driver.xml \
- tracepoint.xml media.xml drm.xml
+ tracepoint.xml media.xml drm.xml \
+ i2s.xml
###
# The build process is as follows (targets):
diff --git a/Documentation/DocBook/i2s.tmpl b/Documentation/DocBook/i2s.tmpl
new file mode 100644
index 00000000000..6b6c50572e2
--- /dev/null
+++ b/Documentation/DocBook/i2s.tmpl
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="I2S">
+ <bookinfo>
+ <title>I2S</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Sandeep</firstname>
+ <surname>Kaushik</surname>
+ <affiliation>
+ <address>
+ <email>sandeep.kaushik@st.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2008-2009</year>
+ <holder>STMicroelectronics Pvt Ltd</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <!-- Do NOT remove the legal notice below -->
+
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This Documentation describes the APIs provided by the I2S Bus Driver. I2S bus supports different
+ protocols like I2S, PCM, SPI etc.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ This Section lists the functions exported by the I2S bus driver. These functions cater to all the protocols
+ supported namely: I2S, PCM, SPI.
+ </para>
+!Edrivers/misc/i2s/i2s.c
+ </chapter>
+</book>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8719c535781..8dce7fc1e20 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -505,6 +505,7 @@ source "drivers/misc/iwmc3200top/Kconfig"
source "drivers/misc/ti-st/Kconfig"
source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/carma/Kconfig"
+source "drivers/misc/i2s/Kconfig"
source "drivers/misc/audio_io_dev/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 9ad03e8754e..4682fccff1f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -46,5 +46,6 @@ obj-y += ti-st/
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
+obj-$(CONFIG_STM_I2S) += i2s/
obj-$(CONFIG_STE_AUDIO_IO_DEV) += audio_io_dev/
-obj-$(CONFIG_HWMEM) += hwmem/
+obj-$(CONFIG_HWMEM) += hwmem/
diff --git a/drivers/misc/i2s/Kconfig b/drivers/misc/i2s/Kconfig
new file mode 100644
index 00000000000..a2652e9eab3
--- /dev/null
+++ b/drivers/misc/i2s/Kconfig
@@ -0,0 +1,29 @@
+#
+# U8500 I2S HW kernel configuration
+#
+config STM_I2S
+ bool "U8500 I2S hardware driver"
+ depends on ARCH_U8500 && STE_DMA40
+ default y
+ ---help---
+ If you say Y here, you will enable the U8500 I2S hardware driver.
+
+ If unsure, say N.
+config STM_MSP_I2S
+ tristate "U8500 MSP_I2S hardware driver"
+ depends on ARCH_U8500 && STE_DMA40 && STM_I2S
+ default y
+ ---help---
+ If you say Y here, you will enable the U8500 MSP_I2S hardware driver.
+
+ If unsure, say N.
+
+config STM_I2S_TEST_PROTOCOL_DRIVER
+ tristate "U8500 I2S test protocol driver"
+ depends on STM_I2S && STE_DMA40 && STM_MSP_I2S
+ default n
+ ---help---
+ If you say Y here, you will enable the test protocol driver used for testing I2S Rx and Tx controllers
+
+ If unsure, say N.
+
diff --git a/drivers/misc/i2s/Makefile b/drivers/misc/i2s/Makefile
new file mode 100644
index 00000000000..22cfdc07551
--- /dev/null
+++ b/drivers/misc/i2s/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for I2S drivers
+#
+
+nmdk_i2s-objs := i2s.o
+obj-$(CONFIG_STM_I2S) += nmdk_i2s.o
+obj-$(CONFIG_STM_MSP_I2S) += msp_i2s.o
+obj-$(CONFIG_STM_I2S_TEST_PROTOCOL_DRIVER) += i2s_test_protocol_driver.o
diff --git a/drivers/misc/i2s/i2s.c b/drivers/misc/i2s/i2s.c
new file mode 100644
index 00000000000..a77711e3dd4
--- /dev/null
+++ b/drivers/misc/i2s/i2s.c
@@ -0,0 +1,597 @@
+/*----------------------------------------------------------------------------*/
+/* copyright STMicroelectronics, 2007. */
+/* */
+/* This program is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by the Free */
+/* Software Foundation; either version 2.1 of the License, or (at your option)*/
+/* any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY */
+/* or FITNES */
+/* 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, see <http://www.gnu.org/licenses/>. */
+/*----------------------------------------------------------------------------*/
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/cache.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/i2s/i2s.h>
+#include <linux/platform_device.h>
+
+/*******************************************************************************/
+static DEFINE_MUTEX(core_lock);
+
+static void i2sdev_release(struct device *dev)
+{
+ struct i2s_device *i2s = to_i2s_device(dev);
+
+ if (i2s->controller)
+ put_device(&(i2s->controller->dev));
+ kfree(dev);
+}
+static ssize_t
+modalias_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+ const struct i2s_device *i2s = to_i2s_device(dev);
+ return sprintf(buf, "%s\n", i2s->modalias);
+}
+
+static struct device_attribute i2s_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
+ * and the sysfs version makes coldplug work too.
+ */
+static const struct i2s_device_id *i2s_match_id(const struct i2s_device_id *id,
+ const struct i2s_device *device)
+{
+ while (id->name[0]) {
+ if (strcmp(device->modalias, id->name) == 0)
+ return id;
+ id++;
+ }
+ return NULL;
+}
+
+static int i2s_match_device(struct device *dev, struct device_driver *drv)
+{
+ const struct i2s_device *device = to_i2s_device(dev);
+ struct i2s_driver *driver = to_i2s_driver(drv);
+ if (driver->id_table)
+ return i2s_match_id(driver->id_table, device) != NULL;
+ return 0;
+}
+
+static int i2s_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ const struct i2s_device *i2s = to_i2s_device(dev);
+
+ add_uevent_var(env, "MODALIAS=%s", i2s->modalias);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2s_suspend(struct device *dev, pm_message_t message)
+{
+ int value = 0;
+ struct i2s_driver *drv = to_i2s_driver(dev->driver);
+
+ /* suspend will stop irqs and dma; no more i/o */
+ if (drv) {
+ if (drv->suspend)
+ value = drv->suspend(to_i2s_device(dev), message);
+ else
+ dev_dbg(dev, "... can't suspend\n");
+ }
+ return value;
+}
+
+static int i2s_resume(struct device *dev)
+{
+ int value = 0;
+ struct i2s_driver *drv = to_i2s_driver(dev->driver);
+
+ /* resume may restart the i/o queue */
+ if (drv) {
+ if (drv->resume)
+ value = drv->resume(to_i2s_device(dev));
+ else
+ dev_dbg(dev, "... can't resume\n");
+ }
+ return value;
+}
+
+#else
+#define i2s_suspend NULL
+#define i2s_resume NULL
+#endif
+
+/*This bus is designed to handle various protocols supported by the MSP- ARM Primecell IP
+ * such as
+ * I2s, PCM, AC97, TDM .... (refer to the data sheet for the complete list.
+ * Current MSP driver has the above ones coded.
+ * */
+struct bus_type i2s_bus_type = {
+ .name = "i2s",
+ .dev_attrs = i2s_dev_attrs,
+ .match = i2s_match_device,
+ .uevent = i2s_uevent,
+ .suspend = i2s_suspend,
+ .resume = i2s_resume,
+};
+
+EXPORT_SYMBOL_GPL(i2s_bus_type);
+
+static int i2s_drv_probe(struct device *dev)
+{
+ const struct i2s_driver *sdrv = to_i2s_driver(dev->driver);
+
+ return sdrv->probe(to_i2s_device(dev));
+}
+
+static int i2s_drv_remove(struct device *dev)
+{
+ const struct i2s_driver *sdrv = to_i2s_driver(dev->driver);
+
+ return sdrv->remove(to_i2s_device(dev));
+}
+
+static void i2s_drv_shutdown(struct device *dev)
+{
+ const struct i2s_driver *sdrv = to_i2s_driver(dev->driver);
+
+ sdrv->shutdown(to_i2s_device(dev));
+}
+
+/**
+ * i2s_register_driver - register a I2S driver
+ * @sdrv: the driver to register
+ * Context: can sleep
+ */
+int i2s_register_driver(struct i2s_driver *sdrv)
+{
+ sdrv->driver.bus = &i2s_bus_type;
+ if (sdrv->probe)
+ sdrv->driver.probe = i2s_drv_probe;
+ if (sdrv->remove)
+ sdrv->driver.remove = i2s_drv_remove;
+ if (sdrv->shutdown)
+ sdrv->driver.shutdown = i2s_drv_shutdown;
+ return driver_register(&sdrv->driver);
+}
+
+EXPORT_SYMBOL_GPL(i2s_register_driver);
+
+/******************************************************************************/
+struct boardinfo {
+ struct list_head list;
+ unsigned n_board_info;
+ struct i2s_board_info board_info[0];
+};
+
+static LIST_HEAD(board_list);
+static DEFINE_MUTEX(board_lock);
+
+/* I2S devices should normally not be created by I2S device drivers; that
+ * would make them board-specific. Similarly with I2S master drivers.
+ * Device registration normally goes into like arch/.../mach.../board-YYY.c
+ * with other readonly (flashable) information about mainboard devices.
+ */
+struct i2s_device *i2s_alloc_device(struct device *device)
+{
+ struct i2s_device *i2s;
+ struct device *dev = device->parent;
+
+ get_device(device);
+ i2s = kzalloc(sizeof *i2s, GFP_KERNEL);
+ if (!i2s) {
+ dev_err(dev, "cannot alloc i2s_device\n");
+ return NULL;
+ }
+
+ i2s->dev.parent = dev;
+ i2s->dev.bus = &i2s_bus_type;
+ i2s->dev.release = i2sdev_release;
+ device_initialize(&i2s->dev);
+ return i2s;
+}
+
+EXPORT_SYMBOL_GPL(i2s_alloc_device);
+
+/**
+ * i2s_add_device - Add i2s_device allocated with i2s_alloc_device
+ * @i2s: i2s_device to register
+ *
+ * Companion function to i2s_alloc_device. Devices allocated with
+ * i2s_alloc_device can be added onto the i2s bus with this function.
+ *
+ * Returns 0 on success; negative errno on failure
+ */
+int i2s_add_device(struct i2s_device *i2s)
+{
+ static DEFINE_MUTEX(i2s_add_lock);
+ struct device *dev = i2s->dev.parent;
+ int status;
+
+ dev_set_name(&i2s->dev, "%s.%u", "i2s", i2s->chip_select);
+
+ mutex_lock(&i2s_add_lock);
+
+ if (bus_find_device_by_name(&i2s_bus_type, NULL, dev_name(&i2s->dev))
+ != NULL) {
+ dev_err(dev, "chipselect %d already in use\n",
+ i2s->chip_select);
+ status = -EBUSY;
+ goto done;
+ }
+
+ /* Device may be bound to an active driver when this returns */
+ status = device_add(&i2s->dev);
+ if (status < 0)
+ dev_err(dev, "can't %s %s, status %d\n",
+ "add", dev_name(&i2s->dev), status);
+ else
+ dev_dbg(dev, "registered child %s\n", dev_name(&i2s->dev));
+
+ done:
+ mutex_unlock(&i2s_add_lock);
+ return status;
+}
+
+EXPORT_SYMBOL_GPL(i2s_add_device);
+
+/**
+ * i2s_new_device - instantiate one new I2S device
+ * @i2s_cont: Controller to which device is connected
+ * @chip: Describes the I2S device
+ * Context: can sleep
+ *
+ * On typical mainboards, this is purely internal; and it's not needed
+ * after board init creates the hard-wired devices. Some development
+ * platforms may not be able to use i2s_register_board_info though, and
+ * this is exported so that driver could add devices (which it would
+ * learn about out-of-band).
+ *
+ * Returns the new device, or NULL.
+ */
+struct i2s_device *i2s_new_device(struct i2s_controller *i2s_cont,
+ struct i2s_board_info *chip)
+{
+ struct i2s_device *proxy;
+ int status;
+
+ /* NOTE: caller did any chip->bus_num checks necessary.
+ *
+ * Also, unless we change the return value convention to use
+ * error-or-pointer (not NULL-or-pointer), troubleshootability
+ * suggests syslogged diagnostics are best here (ugh).
+ */
+
+ proxy = i2s_alloc_device(&i2s_cont->dev);
+ if (!proxy)
+ return NULL;
+
+ WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
+
+ proxy->chip_select = chip->chip_select;
+ strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
+ proxy->dev.platform_data = (void *)chip->platform_data;
+ proxy->controller = i2s_cont;
+
+ status = i2s_add_device(proxy);
+ if (status < 0) {
+ kfree(proxy);
+ return NULL;
+ }
+
+ return proxy;
+}
+
+EXPORT_SYMBOL_GPL(i2s_new_device);
+
+/**
+ * i2s_register_board_info - register I2S devices for a given board
+ * @info: array of chip descriptors
+ * @n: how many descriptors are provided
+ * Context: can sleep
+ *
+ * Board-specific early init code calls this (probably during arch_initcall)
+ * with segments of the I2S device table. Any device nodes are created later,
+ * after the relevant parent I2S controller (id) is defined. We keep
+ * this table of devices forever, so that reloading a controller driver will
+ * not make Linux forget about these hard-wired devices.
+ *
+ */
+int __init
+i2s_register_board_info(struct i2s_board_info const *info, unsigned n)
+{
+ struct boardinfo *bi;
+
+ bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
+ if (!bi)
+ return -ENOMEM;
+ bi->n_board_info = n;
+ memcpy(bi->board_info, info, n * sizeof *info);
+
+ mutex_lock(&board_lock);
+ list_add_tail(&bi->list, &board_list);
+ mutex_unlock(&board_lock);
+ return 0;
+}
+
+/**
+ * scan_boardinfo - Scan, creates and registered new i2s device structure.
+ * @i2s_cont: i2s controller structure
+ * Context: process
+ *
+ * It will scan the device list that may be registered statically using
+ * register_board_info func in arch specific directory and call
+ * i2s_new_device to create and registered i2s device over i2s bus. It is
+ * called by i2s_add_controller function.
+ *
+ * Returns void.
+ */
+static void scan_boardinfo(struct i2s_controller *i2s_cont)
+{
+ struct boardinfo *bi;
+
+ mutex_lock(&board_lock);
+ list_for_each_entry(bi, &board_list, list) {
+ struct i2s_board_info *chip = bi->board_info;
+ unsigned n;
+
+ for (n = bi->n_board_info; n > 0; n--, chip++) {
+ if (chip->id != i2s_cont->id)
+ continue;
+ /* NOTE: this relies on i2s_new_device to
+ * issue diagnostics when given bogus inputs
+ */
+ (void)i2s_new_device(i2s_cont, chip);
+ }
+ }
+ mutex_unlock(&board_lock);
+}
+
+/******************************************************************************/
+/**I2S Controller inittialization*/
+static void i2s_controller_dev_release(struct device *dev)
+{
+ struct i2s_controller *i2s_cont;
+ i2s_cont = container_of(dev, struct i2s_controller, dev);
+ kfree(i2s_cont);
+}
+
+static ssize_t
+show_controller_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2s_controller *cont = to_i2s_controller(dev);
+ return sprintf(buf, "%s\n", cont->name);
+}
+
+static struct device_attribute i2s_controller_attrs[] = {
+ __ATTR(name, S_IRUGO, show_controller_name, NULL),
+ {},
+};
+
+static struct class i2s_controller_class = {
+ .owner = THIS_MODULE,
+ .name = "i2s-controller",
+ .dev_attrs = i2s_controller_attrs,
+};
+
+static int i2s_register_controller(struct i2s_controller *cont)
+{
+ int res = 0;
+ mutex_init(&cont->bus_lock);
+
+ mutex_lock(&core_lock);
+
+ /* Add the controller to the driver core.
+ * If the parent pointer is not set up,
+ * we add this controller to the host bus.
+ */
+ if (cont->dev.parent == NULL) {
+ cont->dev.parent = &platform_bus;
+ pr_debug("I2S controller driver [%s] forgot to specify "
+ "physical device\n", cont->name);
+ }
+ dev_set_name(&cont->dev, "I2Scrlr-%d", cont->id);
+ cont->dev.release = &i2s_controller_dev_release;
+ cont->dev.class = &i2s_controller_class;
+ res = device_register(&cont->dev);
+ if (res)
+ goto out_unlock;
+
+ dev_dbg(&cont->dev, "controller [%s] registered\n", cont->name);
+ scan_boardinfo(cont);
+ out_unlock:
+ mutex_unlock(&core_lock);
+ return res;
+}
+
+/**
+ * i2s_add_controller - declare i2s controller, use dynamic bus number
+ * @controller: the controller to add
+ * Context: can sleep
+ *
+ */
+int i2s_add_controller(struct i2s_controller *controller)
+{
+ return i2s_register_controller(controller);
+}
+
+EXPORT_SYMBOL(i2s_add_controller);
+
+static int __unregister(struct device *dev, void *controller_dev)
+{
+ /* note: before about 2.6.14-rc1 this would corrupt memory: */
+ if (dev != controller_dev)
+ i2s_unregister_device(to_i2s_device(dev));
+ return 0;
+}
+
+/**
+ * i2s_del_controller - unregister I2S controller
+ * @cont: the controller being unregistered
+ * Context: can sleep
+ *
+ * This unregisters an I2S controller which was previously registered
+ * by @i2s_add_controller.
+ */
+int i2s_del_controller(struct i2s_controller *cont)
+{
+ int res = 0;
+ int dummy;
+ mutex_lock(&core_lock);
+
+ dummy = device_for_each_child(cont->dev.parent, &cont->dev,
+ __unregister);
+ device_unregister(&cont->dev);
+ mutex_unlock(&core_lock);
+ return res;
+}
+
+EXPORT_SYMBOL(i2s_del_controller);
+
+/******************************************************************************/
+/*I2S interface apis*/
+
+/**
+ * i2s_transfer - Main i2s transfer function.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * @message: i2s message structure contains transceive info.
+ * Context: process or interrupt.
+ *
+ * This API is called by client i2s driver as i2s_xfer funtion. It will handle
+ * main i2s transfer over i2s bus. The controller should registered its own
+ * functions using i2s algorithm structure.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+int i2s_transfer(struct i2s_controller *i2s_cont, struct i2s_message *message)
+{
+ return i2s_cont->algo->cont_transfer(i2s_cont, message);
+
+}
+
+EXPORT_SYMBOL(i2s_transfer);
+
+/**
+ * i2s_cleanup - Close the current i2s connection btw controller and client.
+ * @i2s_cont: i2s controller structure
+ * @flag: It indicates the functionality that needs to be disabled.
+ * Context: process
+ *
+ * This API will disable and reset the controller's configuration. Reset the
+ * controller so that i2s client driver can reconfigure with new configuration.
+ * Controller should release all the necessary resources which was acquired
+ * during setup.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+int i2s_cleanup(struct i2s_controller *i2s_cont, i2s_flag flag)
+{
+ int status = 0;
+ status = i2s_cont->algo->cont_cleanup(i2s_cont, flag);
+ if (status)
+ return -1;
+ else
+ return 0;
+}
+
+EXPORT_SYMBOL(i2s_cleanup);
+
+/**
+ * i2s_setup - configures and enables the I2S controller.
+ * @i2s_cont: i2s controller sent by i2s device.
+ * @config: specifies the configuration parameters.
+ *
+ * This function configures the I2S controller with the client configuration.
+ * Controller was already registered on I2S bus by some master controller
+ * driver.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+int i2s_setup(struct i2s_controller *i2s_cont, void *config)
+{
+ return i2s_cont->algo->cont_setup(i2s_cont, config);
+}
+
+EXPORT_SYMBOL(i2s_setup);
+
+/**
+ * i2s_hw_status - Get the current hw status for the i2s controller.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * Context: process or interrupt.
+ *
+ * This API is called by client i2s driver to find out current hw status.
+ * The controller should registered its own functions using i2s algorithm structure.
+ *
+ * Returns current hw status register.
+ */
+int i2s_hw_status(struct i2s_controller *i2s_cont)
+{
+ return i2s_cont->algo->cont_hw_status(i2s_cont);
+}
+
+/**
+ * i2s_get_pointer - Get the current dma_addr_t for the i2s controller.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * @i2s_direction: Specifies TX or RX direction.
+ * Context: process or interrupt.
+ *
+ * This API is called by client i2s driver to return a dma_addr_t corresponding
+ * to the position of the DMA-controller.
+ * The controller should registered its own functions using i2s algorithm structure.
+ *
+ * Returns current hw status register.
+ */
+dma_addr_t i2s_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction)
+{
+ return i2s_cont->algo->cont_get_pointer(i2s_cont, i2s_direction);
+}
+
+/******************************************************************************/
+
+static int __init i2s_init(void)
+{
+ int status;
+
+ status = bus_register(&i2s_bus_type);
+ if (status < 0)
+ goto err0;
+
+ status = class_register(&i2s_controller_class);
+ if (status < 0)
+ goto err1;
+ return 0;
+
+ err1:
+ bus_unregister(&i2s_bus_type);
+ err0:
+ return status;
+}
+
+static void __exit i2s_exit(void)
+{
+ class_unregister(&i2s_controller_class);
+ bus_unregister(&i2s_bus_type);
+}
+
+subsys_initcall(i2s_init);
+module_exit(i2s_exit);
+
+MODULE_AUTHOR("Sandeep Kaushik, <sandeep-mmc.kaushik@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/i2s/i2s_test_protocol_driver.c b/drivers/misc/i2s/i2s_test_protocol_driver.c
new file mode 100644
index 00000000000..639a28454f5
--- /dev/null
+++ b/drivers/misc/i2s/i2s_test_protocol_driver.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009
+ * Author: Sandeep Kaushik, <sandeep-mmc.kaushik@st.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/i2s/i2s.h>
+#include <linux/i2s/i2s_test_prot.h>
+#include <mach/msp.h>
+
+int i2s_drv_offset = 1;
+
+module_param(i2s_drv_offset, int, 1);
+MODULE_PARM_DESC(i2s_drv_offset, "i2s driver to be opened)=(0/1/2/3/4/5)");
+
+#define MAX_I2S_CLIENTS 6
+
+struct i2sdrv_data {
+ spinlock_t i2s_lock;
+ struct i2s_device *i2s;
+ /* flag to show the device is closed or not */
+ bool device_closed;
+ u32 tx_status;
+ u32 rx_status;
+};
+
+static struct i2sdrv_data *i2sdrv[MAX_I2S_CLIENTS];
+
+/*API Interface */
+int i2s_testprot_drv_open(int i2s_device_num)
+{
+ if (!i2sdrv[i2s_device_num])
+ return -EINVAL;
+
+ spin_lock_irq(&i2sdrv[i2s_device_num]->i2s_lock);
+ if (!i2sdrv[i2s_device_num]->device_closed) {
+ spin_unlock_irq(&i2sdrv[i2s_device_num]->i2s_lock);
+ return -EBUSY;
+ }
+ i2sdrv[i2s_device_num]->device_closed = false;
+ spin_unlock_irq(&i2sdrv[i2s_device_num]->i2s_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(i2s_testprot_drv_open);
+
+int i2s_config_default_protocol(int i2s_device_num,
+ struct test_prot_config *config)
+{
+ return __i2s_testprot_drv_configure(i2s_device_num, config, true);
+}
+EXPORT_SYMBOL(i2s_config_default_protocol);
+
+int i2s_testprot_drv_configure(int i2s_device_num,
+ struct test_prot_config *config)
+{
+ return __i2s_testprot_drv_configure(i2s_device_num, config, false);
+}
+EXPORT_SYMBOL(i2s_testprot_drv_configure);
+
+int __i2s_testprot_drv_configure(int i2s_device_num,
+ struct test_prot_config *config, bool use_default)
+{
+ int error_status = 0;
+ struct i2s_device *i2s_dev;
+ struct msp_config msp_config = {
+ .tx_clock_sel = TX_CLK_SEL_SRG,
+ .rx_clock_sel = 0x0,
+ .tx_frame_sync_sel = TX_SYNC_SRG_AUTO,
+ .rx_frame_sync_sel = 0x0,
+ .input_clock_freq = MSP_INPUT_FREQ_48MHZ,
+ .srg_clock_sel = SRG_CLK_SEL_APB,
+ .rx_frame_sync_pol = RX_FIFO_SYNC_HI,
+ .tx_frame_sync_pol = TX_FIFO_SYNC_HI,
+ .rx_fifo_config = RX_FIFO_ENABLE,
+ .tx_fifo_config = TX_FIFO_ENABLE,
+ .spi_clk_mode = SPI_CLK_MODE_NORMAL,
+ .tx_data_enable = 0x8000,
+ .spi_burst_mode = 0,
+ .loopback_enable = 0x80,
+ };
+
+ msp_config.default_protocol_desc = use_default;
+
+ if (!i2sdrv[i2s_device_num])
+ return -EINVAL;
+
+ i2s_dev = i2sdrv[i2s_device_num]->i2s;
+
+ if (i2sdrv[i2s_device_num]->device_closed)
+ return -EINVAL;
+
+ if (!config)
+ return -EINVAL;
+
+ msp_config.handler = config->handler;
+ msp_config.tx_callback_data = config->tx_callback_data;
+ msp_config.rx_callback_data = config->rx_callback_data;
+ msp_config.frame_freq = config->frame_freq;
+ msp_config.frame_size = config->frame_size;
+ msp_config.data_size = config->data_size;
+ msp_config.direction = config->direction;
+ msp_config.protocol = config->protocol;
+ msp_config.work_mode = config->work_mode;
+
+ msp_config.def_elem_len = use_default;
+
+ msp_config.multichannel_configured = 0;
+ msp_config.protocol_desc = config->protocol_desc;
+
+ msp_config.multichannel_configured = config->multichannel_configured;
+ msp_config.multichannel_config.tx_multichannel_enable =
+ config->multichannel_config.tx_multichannel_enable;
+ /* Channel 1 to 3 */
+ msp_config.multichannel_config.tx_channel_0_enable =
+ config->multichannel_config.tx_channel_0_enable;
+ /* Channel 33 to 64 */
+ msp_config.multichannel_config.tx_channel_1_enable =
+ config->multichannel_config.tx_channel_1_enable;
+ /* Channel 65 to 96 */
+ msp_config.multichannel_config.tx_channel_2_enable =
+ config->multichannel_config.tx_channel_2_enable;
+ /* Channel 97 to 128 */
+ msp_config.multichannel_config.tx_channel_3_enable =
+ config->multichannel_config.tx_channel_3_enable;
+ msp_config.multichannel_config.rx_multichannel_enable =
+ config->multichannel_config.rx_multichannel_enable;
+ /* Channel 1 to 32 */
+ msp_config.multichannel_config.rx_channel_0_enable =
+ config->multichannel_config.rx_channel_0_enable;
+ /* Channel 33 to 64 */
+ msp_config.multichannel_config.rx_channel_1_enable =
+ config->multichannel_config.rx_channel_1_enable;
+ /* Channel 65 to 96 */
+ msp_config.multichannel_config.rx_channel_2_enable =
+ config->multichannel_config.rx_channel_2_enable;
+ /* Channel 97 to 128 */
+ msp_config.multichannel_config.rx_channel_3_enable =
+ config->multichannel_config.rx_channel_3_enable;
+ msp_config.multichannel_config.rx_comparison_enable_mode =
+ config->multichannel_config.rx_comparison_enable_mode;
+ msp_config.multichannel_config.comparison_value =
+ config->multichannel_config.comparison_value;
+ msp_config.multichannel_config.comparison_mask =
+ config->multichannel_config.comparison_mask;
+
+ error_status =
+ i2s_setup(i2s_dev->controller, &msp_config);
+ if (error_status < 0)
+ dev_err(&i2s_dev->dev, "error in msp enable, error_status is %d\n",
+ error_status);
+
+ return error_status;
+
+}
+
+int i2s_testprot_drv_transfer(int i2s_device_num,
+ void *txdata, size_t txbytes, void *rxdata, size_t rxbytes,
+ enum i2s_transfer_mode_t transfer_mode)
+{
+ int bytes_transreceive;
+ struct i2s_device *i2s_dev;
+ struct i2s_message message = {};
+
+ if (!i2sdrv[i2s_device_num])
+ return -EINVAL;
+
+ i2s_dev = i2sdrv[i2s_device_num]->i2s;
+
+ if (i2sdrv[i2s_device_num]->device_closed) {
+ dev_info(&i2s_dev->dev, "msp device not opened yet\n");
+ return -EINVAL;
+ }
+
+ message.i2s_transfer_mode = transfer_mode;
+ message.i2s_direction = I2S_DIRECTION_BOTH;
+ message.txbytes = txbytes;
+ message.txdata = txdata;
+ message.rxbytes = rxbytes;
+ message.rxdata = rxdata;
+ message.dma_flag = 1;
+ bytes_transreceive = i2s_transfer(i2s_dev->controller, &message);
+ dev_dbg(&i2s_dev->dev, "bytes transreceived %d\n", bytes_transreceive);
+
+ return bytes_transreceive;
+}
+EXPORT_SYMBOL(i2s_testprot_drv_transfer);
+
+int i2s_testprot_drv_close(int i2s_device_num)
+{
+ int status;
+ struct i2s_device *i2s_dev;
+
+ if (!i2sdrv[i2s_device_num])
+ return -EINVAL;
+
+ i2s_dev = i2sdrv[i2s_device_num]->i2s;
+
+ if (i2sdrv[i2s_device_num]->device_closed)
+ return -EINVAL;
+
+ status = i2s_cleanup(i2s_dev->controller, DISABLE_ALL);
+ if (status)
+ return status;
+
+ /* Mark the device as closed */
+ i2sdrv[i2s_device_num]->device_closed = true;
+
+ return 0;
+}
+EXPORT_SYMBOL(i2s_testprot_drv_close);
+
+static int i2sdrv_probe(struct i2s_device *i2s)
+{
+ int status = 0;
+
+ /* Allocate driver data */
+ if (!try_module_get(i2s->controller->dev.parent->driver->owner))
+ return -ENOENT;
+
+ i2sdrv[i2s->chip_select] = kzalloc(sizeof(*i2sdrv[i2s->chip_select]),
+ GFP_KERNEL);
+
+ if (!i2sdrv[i2s->chip_select])
+ return -ENOMEM;
+
+ /* Initialize the driver data */
+ i2sdrv[i2s->chip_select]->i2s = i2s;
+ i2sdrv[i2s->chip_select]->device_closed = true;
+ i2sdrv[i2s->chip_select]->tx_status = 0;
+ i2sdrv[i2s->chip_select]->rx_status = 0;
+ spin_lock_init(&i2sdrv[i2s->chip_select]->i2s_lock);
+
+ i2s_set_drvdata(i2s, (void *)i2sdrv[i2s->chip_select]);
+ return status;
+}
+
+static int i2sdrv_remove(struct i2s_device *i2s)
+{
+ spin_lock_irq(&i2sdrv[i2s->chip_select]->i2s_lock);
+ i2sdrv[i2s->chip_select]->i2s = NULL;
+ i2s_set_drvdata(i2s, NULL);
+ module_put(i2s->controller->dev.parent->driver->owner);
+ spin_unlock_irq(&i2sdrv[i2s->chip_select]->i2s_lock);
+
+ kfree(i2sdrv[i2s->chip_select]);
+
+ return 0;
+}
+
+static const struct i2s_device_id i2s_test_prot_id_table[] = {
+ { "i2s_device.0", 0, 0 },
+ { "i2s_device.1", 0, 0 },
+ { "i2s_device.2", 0, 0 },
+ { "i2s_device.3", 0, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2s, i2s_test_prot_id_table);
+
+static struct i2s_driver i2sdrv_i2s = {
+ .driver = {
+ .name = "i2s_test_protocol_driver",
+ .owner = THIS_MODULE,
+ },
+ .probe = i2sdrv_probe,
+ .remove = __devexit_p(i2sdrv_remove),
+ .id_table = i2s_test_prot_id_table,
+
+ /*
+ * NOTE: suspend/resume methods are not necessary here.
+ */
+};
+
+static int __init i2sdrv_init(void)
+{
+ int status;
+
+ status = i2s_register_driver(&i2sdrv_i2s);
+ if (status < 0)
+ printk(KERN_ERR "Unable to register i2s driver\n");
+
+ return status;
+}
+module_init(i2sdrv_init);
+
+static void __exit i2sdrv_exit(void)
+{
+ i2s_unregister_driver(&i2sdrv_i2s);
+}
+module_exit(i2sdrv_exit);
+
+MODULE_AUTHOR("Sandeep Kaushik, <sandeep-mmc.kaushik@st.com>");
+MODULE_DESCRIPTION("Test Driver module I2S device interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/i2s/msp_i2s.c b/drivers/misc/i2s/msp_i2s.c
new file mode 100644
index 00000000000..e8455e651b7
--- /dev/null
+++ b/drivers/misc/i2s/msp_i2s.c
@@ -0,0 +1,2045 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pfn.h>
+
+#include <linux/regulator/consumer.h>
+#include <mach/prcmu-fw-api.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <linux/dmaengine.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <linux/i2s/i2s.h>
+#include <mach/msp.h>
+#include <linux/dma-mapping.h>
+
+struct regulator *msp_vape_supply;
+
+#define STM_MSP_NAME "STM_MSP"
+#define MSP_NAME "msp"
+#define DRIVER_DEBUG_PFX "MSP"
+#define DRIVER_DEBUG CONFIG_STM_MSP_DEBUG
+#define DRIVER_DBG "MSP"
+#define NMDK_DBG /* message level */
+
+extern struct driver_debug_st DBG_ST;
+ /* Protocol desciptors */
+static const struct msp_protocol_desc protocol_desc_tab[] = {
+ I2S_PROTOCOL_DESC,
+ PCM_PROTOCOL_DESC,
+ PCM_COMPAND_PROTOCOL_DESC,
+ AC97_PROTOCOL_DESC,
+ SPI_MASTER_PROTOCOL_DESC,
+ SPI_SLAVE_PROTOCOL_DESC,
+};
+
+/* Local static functions */
+static int msp_dma_xfer(struct msp *msp, struct i2s_message *msg);
+static int msp_polling_xfer(struct msp *msp, struct i2s_message *msg);
+static int msp_interrupt_xfer(struct msp *msp, struct i2s_message *msg);
+static int msp_start_dma(struct msp *msp, int transmit, dma_addr_t data,
+ size_t bytes);
+static int configure_protocol(struct msp *msp,
+ struct msp_config *config);
+static int configure_clock(struct msp *msp,
+ struct msp_config *config);
+static int configure_multichannel(struct msp *msp,
+ struct msp_config *config);
+static int stm_msp_configure_enable(struct i2s_controller *i2s_cont,
+ void *configuration);
+static int stm_msp_transceive_data(struct i2s_controller *i2s_cont,
+ struct i2s_message *message);
+
+static int stm_msp_disable(struct msp *msp, int direction,
+ i2s_flag flag);
+static int stm_msp_close(struct i2s_controller *i2s_cont, i2s_flag flag);
+static int stm_msp_hw_status(struct i2s_controller *i2s_cont);
+static dma_addr_t stm_msp_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction);
+
+#define I2S_DEVICE "i2s_device"
+static struct i2s_algorithm i2s_algo = {
+ .cont_setup = stm_msp_configure_enable,
+ .cont_transfer = stm_msp_transceive_data,
+ .cont_cleanup = stm_msp_close,
+ .cont_hw_status = stm_msp_hw_status,
+ .cont_get_pointer = stm_msp_get_pointer,
+};
+
+/**
+ * stm_msp_write - writel a value to specified register
+ * @value: value
+ * @reg: pointer to register' address
+ * Context: atomic(can be both process and interrupt)
+ * Returns void.
+ */
+static inline void stm_msp_write(u32 value, void __iomem *reg)
+{
+ writel(value, reg);
+}
+
+/**
+ * stm_msp_read - readl a value to specified register
+ * @reg: pointer to register' address
+ * Context: atomic(can be both process and interrupt)
+ * Returns u32 register's value.
+ */
+static inline u32 stm_msp_read(void __iomem *reg)
+{
+ return readl(reg);
+}
+
+static void u8_msp_read(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->rx_offset < message->rxbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ RX_FIFO_EMPTY)) {
+ message->rx_offset += 1;
+ *(u8 *) message->rxdata =
+ (u8) stm_msp_read(xfer_data->msp->registers + MSP_DR);
+ message->rxdata += 1;
+ }
+}
+
+static void u16_msp_read(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->rx_offset < message->rxbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ RX_FIFO_EMPTY)) {
+ message->rx_offset += 2;
+ *(u16 *) message->rxdata =
+ (u16) stm_msp_read(xfer_data->msp->registers + MSP_DR);
+ message->rxdata += 2;
+ }
+}
+
+/**
+ * u32_msp_read - Msp 32bit read function.
+ * @xfer_data: transfer data structure.
+ *
+ * It reads 32bit data from msp receive fifo until it gets empty.
+ *
+ * Returns void.
+ */
+static void u32_msp_read(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->rx_offset < message->rxbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ RX_FIFO_EMPTY)) {
+ *(u32 *) message->rxdata =
+ (u32) stm_msp_read(xfer_data->msp->registers + MSP_DR);
+ message->rx_offset += 4;
+ message->rxdata += 4;
+ }
+}
+static void u8_msp_write(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->tx_offset < message->txbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ TX_FIFO_FULL)) {
+ message->tx_offset += 1;
+ stm_msp_write(*(u8 *) message->txdata,
+ xfer_data->msp->registers + MSP_DR);
+ message->txdata += 1;
+ }
+}
+
+static void u16_msp_write(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->tx_offset < message->txbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ TX_FIFO_FULL)) {
+ message->tx_offset += 2;
+ stm_msp_write(*(u16 *) message->txdata,
+ xfer_data->msp->registers + MSP_DR);
+ message->txdata += 2;
+ }
+}
+
+/**
+ * u32_msp_write - Msp 32bit write function.
+ * @xfer_data: transfer data structure.
+ *
+ * It writes 32bit data to msp transmit fifo until it gets full.
+ *
+ * Returns void.
+ */
+static void u32_msp_write(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->tx_offset < message->txbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ TX_FIFO_FULL)) {
+ message->tx_offset += 4;
+ stm_msp_write(*(u32 *) message->txdata,
+ xfer_data->msp->registers + MSP_DR);
+ message->txdata += 4;
+ }
+}
+
+/**
+ * set_transmit_protocol_descriptor - Set the Transmit Configuration register.
+ * @msp: main msp controller structure.
+ * @protocol_desc: pointer to protocol descriptor structure.
+ * @data_size: Run time configurable element length.
+ *
+ * It will setup transmit configuration register of msp.
+ * Various values related to a particular protocol can be set like, elemnet
+ * length, frame length, endianess etc.
+ *
+ * Returns void.
+ */
+static void set_transmit_protocol_descriptor(struct msp *msp,
+ struct msp_protocol_desc
+ *protocol_desc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->tx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->tx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->tx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->tx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |=
+ MSP_P1_ELEM_LEN_BITS(protocol_desc->tx_element_length_1);
+ temp_reg |=
+ MSP_P2_ELEM_LEN_BITS(protocol_desc->tx_element_length_2);
+ if (protocol_desc->tx_element_length_1 ==
+ protocol_desc->tx_element_length_2) {
+ msp->actual_data_size =
+ protocol_desc->tx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->tx_data_delay);
+ temp_reg |=
+ MSP_SET_ENDIANNES_BIT(protocol_desc->tx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->tx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->tx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->compression_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ stm_msp_write(temp_reg, msp->registers + MSP_TCF);
+}
+
+/**
+ * set_receive_protocol_descriptor - Set the Receive Configuration register.
+ * @msp: main msp controller structure.
+ * @protocol_desc: pointer to protocol descriptor structure.
+ * @data_size: Run time configurable element length.
+ *
+ * It will setup receive configuration register of msp.
+ * Various values related to a particular protocol can be set like, elemnet
+ * length, frame length, endianess etc.
+ *
+ * Returns void.
+ */
+static void set_receive_protocol_descriptor(struct msp *msp,
+ struct msp_protocol_desc
+ *protocol_desc,
+ enum msp_data_size
+ data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->rx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->rx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->rx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->rx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |=
+ MSP_P1_ELEM_LEN_BITS(protocol_desc->rx_element_length_1);
+ temp_reg |=
+ MSP_P2_ELEM_LEN_BITS(protocol_desc->rx_element_length_2);
+ if (protocol_desc->rx_element_length_1 ==
+ protocol_desc->rx_element_length_2) {
+ msp->actual_data_size =
+ protocol_desc->rx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->rx_data_delay);
+ temp_reg |=
+ MSP_SET_ENDIANNES_BIT(protocol_desc->rx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->rx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->rx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->expansion_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ stm_msp_write(temp_reg, msp->registers + MSP_RCF);
+
+}
+
+/**
+ * configure_protocol - Configures transmit and receive protocol.
+ * @msp: main msp controller structure.
+ * @config: configuration structure passed by client driver
+ *
+ * This will configure transmit and receive protocol decriptors.
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static int configure_protocol(struct msp *msp,
+ struct msp_config *config)
+{
+ int direction;
+ struct msp_protocol_desc *protocol_desc;
+ enum msp_data_size data_size;
+ u32 temp_reg = 0;
+
+ data_size = config->data_size;
+ msp->def_elem_len = config->def_elem_len;
+ direction = config->direction;
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ printk(KERN_ERR
+ "invalid protocol in configure_protocol()\n");
+ return -EINVAL;
+ }
+ protocol_desc =
+ (struct msp_protocol_desc *)&protocol_desc_tab[config->
+ protocol];
+ } else {
+ protocol_desc =
+ (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ if (data_size < MSP_DATA_BITS_DEFAULT
+ || data_size > MSP_DATA_BITS_32) {
+ printk(KERN_ERR
+ "invalid data size requested in configure_protocol()\n");
+ return -EINVAL;
+ }
+
+ switch (direction) {
+ case MSP_TRANSMIT_MODE:
+ set_transmit_protocol_descriptor(msp, protocol_desc, data_size);
+ break;
+ case MSP_RECEIVE_MODE:
+ set_receive_protocol_descriptor(msp, protocol_desc, data_size);
+ break;
+ case MSP_BOTH_T_R_MODE:
+ set_transmit_protocol_descriptor(msp, protocol_desc, data_size);
+ set_receive_protocol_descriptor(msp, protocol_desc, data_size);
+ break;
+ default:
+ printk(KERN_ERR "Invalid direction given\n");
+ return -EINVAL;
+ }
+ /* The below code is needed for both Rx and Tx path can't separate
+ * them.
+ */
+ temp_reg = stm_msp_read(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+ temp_reg |= MSP_TX_CLKPOL_BIT(protocol_desc->tx_clock_pol);
+ stm_msp_write(temp_reg, msp->registers + MSP_GCR);
+ temp_reg = stm_msp_read(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+ temp_reg |= MSP_RX_CLKPOL_BIT(protocol_desc->rx_clock_pol);
+ stm_msp_write(temp_reg, msp->registers + MSP_GCR);
+
+ return 0;
+}
+
+/**
+ * configure_clock - Set clock in sample rate generator.
+ * @msp: main msp controller structure.
+ * @config: configuration structure passed by client driver
+ *
+ * This will set the frame width and period. Also enable sample rate generator
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static int configure_clock(struct msp *msp,
+ struct msp_config *config)
+{
+
+ u32 dummy;
+ u32 frame_per = 0;
+ u32 sck_div = 0;
+ u32 frame_width = 0;
+ u32 temp_reg = 0;
+ u32 bit_clock = 0;
+ struct msp_protocol_desc *protocol_desc = NULL;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~(SRG_ENABLE))), msp->registers + MSP_GCR);
+
+ if (config->default_protocol_desc) {
+ protocol_desc =
+ (struct msp_protocol_desc *)&protocol_desc_tab[config->
+ protocol];
+ } else {
+ protocol_desc =
+ (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ switch (config->protocol) {
+ case MSP_PCM_PROTOCOL:
+ case MSP_PCM_COMPAND_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div =
+ config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->
+ total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+ break;
+ case MSP_I2S_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div =
+ config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->
+ total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+
+ break;
+ case MSP_AC97_PROTOCOL:
+ /* Not supported */
+ printk(KERN_WARNING "AC97 protocol not supported\n");
+ return -ENOSYS;
+ default:
+ printk(KERN_ERR "Invalid mode attempted for setting clocks\n");
+ return -EINVAL;
+ }
+
+ temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+ temp_reg |= FRAME_WIDTH_BITS(frame_width);
+ temp_reg |= FRAME_PERIOD_BITS(frame_per);
+ stm_msp_write(temp_reg, msp->registers + MSP_SRG);
+
+ /* Input clock frequency value configured is in MHz/1000 */
+ bit_clock = (config->input_clock_freq * 1000)/(sck_div + 1);
+
+ /* If the bit clock is higher than 19.2MHz, Vape should be run in 100% OPP */
+ if (bit_clock > 19200000) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 100);
+ msp->vape_opp_constraint = 1;
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+
+ /* Wait a bit */
+ dummy = ((stm_msp_read(msp->registers + MSP_SRG)) >> FRWID_SHIFT) & 0x0000003F;
+
+ /* Enable clock */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) | ((SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+
+ /* Another wait */
+ dummy =
+ ((stm_msp_read(msp->registers + MSP_SRG)) >> FRWID_SHIFT) &
+ 0x0000003F;
+ return 0;
+}
+
+/**
+ * configure_multichannel - Enable multichannel support for transmit & receive.
+ * @msp: main msp controller structure.
+ * @config: configuration structure passed by client driver
+ *
+ * This will enable multichannel support for transmit and receive.
+ * It will set Receive comparator also if configured.
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static int configure_multichannel(struct msp *msp,
+ struct msp_config *config)
+{
+ struct msp_protocol_desc *protocol_desc;
+ struct msp_multichannel_config *mult_config;
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ printk(KERN_ERR
+ "invalid protocol in configure_protocol()\n");
+ return -EINVAL;
+ }
+ protocol_desc =
+ (struct msp_protocol_desc *)&protocol_desc_tab[config->
+ protocol];
+ } else {
+ protocol_desc =
+ (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+ mult_config = &config->multichannel_config;
+ if (true == mult_config->tx_multichannel_enable) {
+ if (MSP_SINGLE_PHASE == protocol_desc->tx_phase_mode) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_MCR) |
+ ((mult_config->
+ tx_multichannel_enable << TMCEN_BIT) &
+ (0x0000020))),
+ msp->registers + MSP_MCR);
+ stm_msp_write(mult_config->tx_channel_0_enable,
+ msp->registers + MSP_TCE0);
+ stm_msp_write(mult_config->tx_channel_1_enable,
+ msp->registers + MSP_TCE1);
+ stm_msp_write(mult_config->tx_channel_2_enable,
+ msp->registers + MSP_TCE2);
+ stm_msp_write(mult_config->tx_channel_3_enable,
+ msp->registers + MSP_TCE3);
+ } else {
+ printk(KERN_ERR "Not in authorised mode\n");
+ return -1;
+ }
+ }
+ if (true == mult_config->rx_multichannel_enable) {
+ if (MSP_SINGLE_PHASE == protocol_desc->rx_phase_mode) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_MCR) |
+ ((mult_config->
+ rx_multichannel_enable << RMCEN_BIT) &
+ (0x0000001))),
+ msp->registers + MSP_MCR);
+ stm_msp_write(mult_config->rx_channel_0_enable,
+ msp->registers + MSP_RCE0);
+ stm_msp_write(mult_config->rx_channel_1_enable,
+ msp->registers + MSP_RCE1);
+ stm_msp_write(mult_config->rx_channel_2_enable,
+ msp->registers + MSP_RCE2);
+ stm_msp_write(mult_config->rx_channel_3_enable,
+ msp->registers + MSP_RCE3);
+ } else {
+ printk(KERN_ERR "Not in authorised mode\n");
+ return -1;
+ }
+ if (mult_config->rx_comparison_enable_mode) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_MCR) |
+ ((mult_config->
+ rx_comparison_enable_mode << RCMPM_BIT)
+ & (0x0000018))),
+ msp->registers + MSP_MCR);
+
+ stm_msp_write(mult_config->comparison_mask,
+ msp->registers + MSP_RCM);
+ stm_msp_write(mult_config->comparison_value,
+ msp->registers + MSP_RCV);
+
+ }
+ }
+ return 0;
+
+}
+
+/**
+ * configure_dma - configure dma channel for transmit or receive.
+ * @msp: msp structure
+ * @config: configuration structure.
+ * Context: process
+ *
+ * It will configure dma channels and request them in Logical mode for both
+ * transmit and recevie modes.It also register the respective callback handlers
+ * for DMA.
+ *
+ * Returns void.
+ */
+void configure_dma(struct msp *msp, struct msp_config *config)
+{
+ struct stedma40_chan_cfg *rx_dma_info = msp->dma_cfg_rx;
+ struct stedma40_chan_cfg *tx_dma_info = msp->dma_cfg_tx;
+ dma_cap_mask_t mask;
+
+ if (config->direction == MSP_TRANSMIT_MODE
+ || config->direction == MSP_BOTH_T_R_MODE) {
+
+ if (msp->tx_pipeid != NULL) {
+ dma_release_channel(msp->tx_pipeid);
+ msp->tx_pipeid = NULL;
+ }
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ tx_dma_info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ tx_dma_info->src_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ tx_dma_info->src_info.data_width
+ = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ tx_dma_info->dst_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ tx_dma_info->dst_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ tx_dma_info->dst_info.data_width
+ = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ msp->tx_pipeid = dma_request_channel(mask, stedma40_filter,
+ tx_dma_info);
+ }
+ if (config->direction == MSP_RECEIVE_MODE
+ || config->direction == MSP_BOTH_T_R_MODE) {
+
+ if (msp->rx_pipeid != NULL) {
+ dma_release_channel(msp->rx_pipeid);
+ msp->rx_pipeid = NULL;
+ }
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ rx_dma_info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ rx_dma_info->src_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ rx_dma_info->src_info.data_width = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ rx_dma_info->dst_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ rx_dma_info->dst_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ rx_dma_info->dst_info.data_width = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ msp->rx_pipeid = dma_request_channel(mask, stedma40_filter,
+ rx_dma_info);
+ }
+
+}
+
+/**
+ * msp_enable - Setup the msp configuration.
+ * @msp: msp data contains main msp structure.
+ * @config: configuration structure sent by i2s client driver.
+ * Context: process
+ *
+ * Main msp configuring functions to configure msp in accordance with msp
+ * protocol descriptor, configuring msp clock,setup transfer mode selected by
+ * user like DMA, interrupt or polling and in the end enable RX and Tx path.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_enable(struct msp *msp, struct msp_config *config)
+{
+ int status = 0;
+ int state;
+
+ /* Check msp state whether in RUN or CONFIGURED Mode */
+ state = msp->msp_state;
+ if (state == MSP_STATE_IDLE) {
+ if (msp->plat_init) {
+ status = msp->plat_init();
+ if (status) {
+ printk(KERN_ERR "Error in msp_i2s_init,"
+ " status is %d\n", status);
+ return status;
+ }
+ }
+ }
+
+ /* Configure msp with protocol dependent settings */
+ configure_protocol(msp, config);
+ configure_clock(msp, config);
+ if (config->multichannel_configured == 1) {
+ status = configure_multichannel(msp, config);
+ if (status)
+ printk(KERN_ERR "multichannel can't be configured\n");
+ }
+ msp->work_mode = config->work_mode;
+
+ if (msp->work_mode == MSP_DMA_MODE && !msp->dma_cfg_rx) {
+ switch (config->direction) {
+ case MSP_RECEIVE_MODE:
+ case MSP_BOTH_T_R_MODE:
+ dev_err(&msp->i2s_cont->dev, "RX DMA not available");
+ return -EINVAL;
+ }
+ }
+
+ if (msp->work_mode == MSP_DMA_MODE && !msp->dma_cfg_tx) {
+ switch (config->direction) {
+ case MSP_TRANSMIT_MODE:
+ case MSP_BOTH_T_R_MODE:
+ dev_err(&msp->i2s_cont->dev, "TX DMA not available");
+ return -EINVAL;
+ }
+ }
+
+ switch (config->direction) {
+ case MSP_TRANSMIT_MODE:
+ /*Currently they are ignored
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) |
+ TRANSMIT_UNDERRUN_ERR_INT |
+ TRANSMIT_FRAME_SYNC_ERR_INT),
+ msp->registers + MSP_IMSC); */
+ if (config->work_mode == MSP_DMA_MODE) {
+ stm_msp_write(stm_msp_read(msp->registers + MSP_DMACR) |
+ TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ configure_dma(msp, config);
+ }
+ if (config->work_mode == MSP_POLLING_MODE) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (msp->work_mode != MSP_DMA_MODE) {
+ switch (msp->actual_data_size) {
+ case MSP_DATA_BITS_8:
+ msp->write = u8_msp_write;
+ break;
+ case MSP_DATA_BITS_10:
+ case MSP_DATA_BITS_12:
+ case MSP_DATA_BITS_14:
+ case MSP_DATA_BITS_16:
+ msp->write = u16_msp_write;
+ break;
+ case MSP_DATA_BITS_20:
+ case MSP_DATA_BITS_24:
+ case MSP_DATA_BITS_32:
+ default:
+ msp->write = u32_msp_write;
+ break;
+ }
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+ msp->xfer_data.msp = msp;
+ }
+ break;
+ case MSP_RECEIVE_MODE:
+ /*Currently they are ignored
+ stm_msp_write(stm_msp_read(msp->registers + MSP_IMSC) |
+ RECEIVE_OVERRUN_ERROR_INT | RECEIVE_FRAME_SYNC_ERR_INT,
+ msp->registers + MSP_IMSC); */
+ if (config->work_mode == MSP_DMA_MODE) {
+ stm_msp_write(stm_msp_read(msp->registers + MSP_DMACR) |
+ RX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+
+ configure_dma(msp, config);
+ }
+ if (config->work_mode == MSP_POLLING_MODE) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (msp->work_mode != MSP_DMA_MODE) {
+ switch (msp->actual_data_size) {
+ case MSP_DATA_BITS_8:
+ msp->read = u8_msp_read;
+ break;
+ case MSP_DATA_BITS_10:
+ case MSP_DATA_BITS_12:
+ case MSP_DATA_BITS_14:
+ case MSP_DATA_BITS_16:
+ msp->read = u16_msp_read;
+ break;
+ case MSP_DATA_BITS_20:
+ case MSP_DATA_BITS_24:
+ case MSP_DATA_BITS_32:
+ default:
+ msp->read = u32_msp_read;
+ break;
+ }
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+ msp->xfer_data.msp = msp;
+ }
+
+ break;
+ case MSP_BOTH_T_R_MODE:
+ /*Currently they are ignored
+ stm_msp_write(stm_msp_read(msp->registers + MSP_IMSC) |
+ RECEIVE_OVERRUN_ERROR_INT | RECEIVE_FRAME_SYNC_ERR_INT |
+ TRANSMIT_UNDERRUN_ERR_INT | TRANSMIT_FRAME_SYNC_ERR_INT ,
+ msp->registers + MSP_IMSC); */
+ if (config->work_mode == MSP_DMA_MODE) {
+ stm_msp_write(stm_msp_read(msp->registers + MSP_DMACR) |
+ RX_DMA_ENABLE | TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+
+ configure_dma(msp, config);
+ }
+ if (config->work_mode == MSP_POLLING_MODE) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)), msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (msp->work_mode != MSP_DMA_MODE) {
+ switch (msp->actual_data_size) {
+ case MSP_DATA_BITS_8:
+ msp->read = u8_msp_read;
+ msp->write = u8_msp_write;
+ break;
+ case MSP_DATA_BITS_10:
+ case MSP_DATA_BITS_12:
+ case MSP_DATA_BITS_14:
+ case MSP_DATA_BITS_16:
+ msp->read = u16_msp_read;
+ msp->write = u16_msp_write;
+ break;
+ case MSP_DATA_BITS_20:
+ case MSP_DATA_BITS_24:
+ case MSP_DATA_BITS_32:
+ default:
+ msp->read = u32_msp_read;
+ msp->write = u32_msp_write;
+ break;
+ }
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+ msp->xfer_data.msp = msp;
+ }
+
+ break;
+ default:
+ printk(KERN_ERR "Invalid direction parameter\n");
+ if (msp->plat_exit)
+ msp->plat_exit();
+ status = -EINVAL;
+ return status;
+ }
+
+ switch (config->work_mode) {
+ case MSP_DMA_MODE:
+ msp->transfer = msp_dma_xfer;
+ break;
+ case MSP_POLLING_MODE:
+ msp->transfer = msp_polling_xfer;
+ break;
+ case MSP_INTERRUPT_MODE:
+ msp->transfer = msp_interrupt_xfer;
+ break;
+ default:
+ msp->transfer = NULL;
+ }
+
+ stm_msp_write(config->iodelay, msp->registers + MSP_IODLY);
+
+ /* enable frame generation logic */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (FRAME_GEN_ENABLE)), msp->registers + MSP_GCR);
+
+ return status;
+}
+
+/**
+ * flush_rx_fifo - Flush Rx fifo MSP controller.
+ * @msp: msp structure.
+ *
+ * This function flush the rx fifo of msp controller.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static void flush_rx_fifo(struct msp *msp)
+{
+ u32 dummy = 0;
+ u32 limit = 32;
+ u32 cur = stm_msp_read(msp->registers + MSP_GCR);
+ stm_msp_write(cur | RX_ENABLE, msp->registers + MSP_GCR);
+ while (!(stm_msp_read(msp->registers + MSP_FLR) & RX_FIFO_EMPTY)
+ && limit--) {
+ dummy = stm_msp_read(msp->registers + MSP_DR);
+ }
+ stm_msp_write(cur, msp->registers + MSP_GCR);
+}
+
+/**
+ * flush_tx_fifo - Flush Tx fifo MSP controller.
+ * @msp: msp structure.
+ *
+ * This function flush the tx fifo using test intergration register to read data
+ * from tx fifo directly.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static void flush_tx_fifo(struct msp *msp)
+{
+ u32 dummy = 0;
+ u32 limit = 32;
+ u32 cur = stm_msp_read(msp->registers + MSP_GCR);
+ stm_msp_write(cur | TX_ENABLE, msp->registers + MSP_GCR);
+ stm_msp_write(0x3, msp->registers + MSP_ITCR);
+ while (!(stm_msp_read(msp->registers + MSP_FLR) & TX_FIFO_EMPTY)
+ && limit--) {
+ dummy = stm_msp_read(msp->registers + MSP_TSTDR);
+ }
+ stm_msp_write(0x0, msp->registers + MSP_ITCR);
+ stm_msp_write(cur, msp->registers + MSP_GCR);
+}
+
+/**
+ * stm_msp_configure_enable - configures and enables the MSP controller.
+ * @i2s_cont: i2s controller sent by i2s device.
+ * @configuration: specifies the configuration parameters.
+ *
+ * This function configures the msp controller with the client configuration.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static int stm_msp_configure_enable(struct i2s_controller *i2s_cont,
+ void *configuration)
+{
+ u32 old_reg;
+ u32 new_reg;
+ u32 mask;
+ int res;
+ struct msp_config *config =
+ (struct msp_config *)configuration;
+ struct msp *msp = (struct msp *)i2s_cont->data;
+
+ if (in_interrupt()) {
+ printk(KERN_ERR
+ "can't call configure_enable in interrupt context\n");
+ return -1;
+ }
+
+ /* Two simultanous configuring msp is avoidable */
+ down(&msp->lock);
+ switch (msp->users) {
+ case 0:
+ res = regulator_enable(msp_vape_supply);
+ if (res != 0) {
+ dev_err(&msp->i2s_cont->dev,
+ "Failed to enable regulator\n");
+ up(&msp->lock);
+ return res;
+ }
+
+ clk_enable(msp->clk);
+ msp->direction = config->direction;
+ break;
+ case 1:
+ if (msp->direction == MSP_BOTH_T_R_MODE ||
+ config->direction == msp->direction ||
+ config->direction == MSP_BOTH_T_R_MODE) {
+ dev_notice(&i2s_cont->dev, "%s: MSP in use in the "
+ "desired direction.\n", __func__);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->direction = MSP_BOTH_T_R_MODE;
+ break;
+ default:
+ dev_notice(&i2s_cont->dev, "%s: MSP in use in both "
+ "directions.\n", __func__);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->users++;
+
+ /* First do the global config register */
+ mask =
+ RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FRAME_SYNC_MASK |
+ TX_FRAME_SYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+ RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+ LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+ new_reg =
+ (config->tx_clock_sel | config->rx_clock_sel | config->
+ rx_frame_sync_pol | config->tx_frame_sync_pol | config->
+ rx_frame_sync_sel | config->tx_frame_sync_sel | config->
+ rx_fifo_config | config->tx_fifo_config | config->
+ srg_clock_sel | config->loopback_enable | config->tx_data_enable);
+
+ old_reg = stm_msp_read(msp->registers + MSP_GCR);
+ old_reg &= ~mask;
+ new_reg |= old_reg;
+ stm_msp_write(new_reg, msp->registers + MSP_GCR);
+
+ if (msp_enable(msp, config) != 0) {
+ printk(KERN_ERR "error enabling MSP\n");
+ return -EBUSY;
+ }
+ if (config->loopback_enable & 0x80)
+ msp->loopback_enable = 1;
+ /*Sometimes FIFO doesn't gets empty hence limit is provided */
+ flush_tx_fifo(msp);
+ /*This has been added in order to fix fifo flush problem
+ When last xfer occurs some data remains in fifo. In order to
+ flush that data delay is needed */
+ msleep(10);
+ /* wait for fifo to flush */
+ flush_rx_fifo(msp);
+
+ /* RX_BUSY take a while to clear */
+ msleep(10);
+
+ msp->msp_state = MSP_STATE_CONFIGURED;
+ up(&msp->lock);
+ return 0;
+}
+
+static int msp_start_dma(struct msp *msp, int transmit, dma_addr_t data,
+ size_t bytes)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(data)), bytes,
+ offset_in_page(data));
+ sg_dma_address(&sg) = data;
+ sg_dma_len(&sg) = bytes;
+
+ if (transmit) {
+ if (!msp->tx_pipeid)
+ return -EINVAL;
+
+ desc = msp->tx_pipeid->device->
+ device_prep_slave_sg(msp->tx_pipeid,
+ &sg, 1, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT
+ | DMA_CTRL_ACK);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->callback = msp->xfer_data.tx_handler;
+ desc->callback_param = msp->xfer_data.tx_callback_data;
+ desc->tx_submit(desc);
+ dma_async_issue_pending(msp->tx_pipeid);
+ } else {
+ if (!msp->rx_pipeid)
+ return -EINVAL;
+
+ desc = msp->rx_pipeid->device->
+ device_prep_slave_sg(msp->rx_pipeid,
+ &sg, 1, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT
+ | DMA_CTRL_ACK);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = msp->xfer_data.rx_handler;
+ desc->callback_param = msp->xfer_data.rx_callback_data;
+ desc->tx_submit(desc);
+ dma_async_issue_pending(msp->rx_pipeid);
+ }
+
+ return 0;
+}
+
+static int msp_single_dma_tx(struct msp *msp, dma_addr_t data, size_t bytes)
+{
+ int status;
+ status = msp_start_dma(msp, 1, data, bytes);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) | (TX_ENABLE)),
+ msp->registers + MSP_GCR);
+ return status;
+}
+
+static int msp_single_dma_rx(struct msp *msp, dma_addr_t data, size_t bytes)
+{
+ int status;
+ status = msp_start_dma(msp, 0, data, bytes);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) | (RX_ENABLE)),
+ msp->registers + MSP_GCR);
+ return status;
+}
+
+static void msp_cyclic_dma_start(struct msp *msp,
+ struct scatterlist *sg,
+ int sg_len,
+ enum dma_data_direction direction)
+{
+ struct stedma40_cyclic_desc *cdesc;
+ int ret;
+ struct dma_chan *pipeid = (direction == DMA_TO_DEVICE) ?
+ msp->tx_pipeid :
+ msp->rx_pipeid;
+
+ cdesc = stedma40_cyclic_prep_sg(pipeid,
+ sg,
+ sg_len,
+ direction,
+ DMA_PREP_INTERRUPT);
+ if (IS_ERR(cdesc)) {
+ pr_err("%s: Error: stedma40_cyclic_prep_sg failed (%ld)!\n",
+ __func__,
+ PTR_ERR(cdesc));
+ return;
+ }
+
+ cdesc->period_callback = (direction == DMA_TO_DEVICE) ?
+ msp->xfer_data.tx_handler :
+ msp->xfer_data.rx_handler;
+ cdesc->period_callback_param = (direction == DMA_TO_DEVICE) ?
+ msp->xfer_data.tx_callback_data :
+ msp->xfer_data.rx_callback_data;
+
+ ret = stedma40_cyclic_start(pipeid);
+ if (ret) {
+ pr_err("%s: stedma40_cyclic_start failed (%d)!\n", __func__, ret);
+ goto free;
+ }
+
+ msp->infinite = true;
+
+ return;
+
+free:
+ stedma40_cyclic_free(pipeid);
+}
+
+/* Legacy function. Used by HATS driver. */
+static void msp_loopback_inf_start_dma(struct msp *msp,
+ dma_addr_t data,
+ size_t bytes)
+{
+ struct stedma40_cyclic_desc *rxcdesc;
+ struct stedma40_cyclic_desc *txcdesc;
+ struct scatterlist rxsg[2];
+ struct scatterlist txsg[2];
+ size_t len = bytes >> 1;
+ int ret;
+
+ sg_init_table(rxsg, ARRAY_SIZE(rxsg));
+ sg_init_table(txsg, ARRAY_SIZE(txsg));
+
+ sg_dma_len(&rxsg[0]) = len;
+ sg_dma_len(&rxsg[1]) = len;
+ sg_dma_len(&txsg[0]) = len;
+ sg_dma_len(&txsg[1]) = len;
+
+ sg_dma_address(&rxsg[0]) = data;
+ sg_dma_address(&rxsg[1]) = data + len;
+
+ sg_dma_address(&txsg[0]) = data + len;
+ sg_dma_address(&txsg[1]) = data;
+
+ rxcdesc = stedma40_cyclic_prep_sg(msp->rx_pipeid,
+ rxsg, ARRAY_SIZE(rxsg),
+ DMA_FROM_DEVICE, 0);
+ if (IS_ERR(rxcdesc))
+ return;
+
+ txcdesc = stedma40_cyclic_prep_sg(msp->tx_pipeid,
+ txsg, ARRAY_SIZE(txsg),
+ DMA_TO_DEVICE, 0);
+ if (IS_ERR(txcdesc))
+ goto free_rx;
+
+ ret = stedma40_cyclic_start(msp->rx_pipeid);
+ if (ret)
+ goto free_tx;
+
+ ret = stedma40_cyclic_start(msp->tx_pipeid);
+ if (ret)
+ goto stop_rx;
+
+ msp->infinite = true;
+
+ return;
+
+stop_rx:
+ stedma40_cyclic_stop(msp->rx_pipeid);
+free_tx:
+ stedma40_cyclic_free(msp->tx_pipeid);
+free_rx:
+ stedma40_cyclic_free(msp->rx_pipeid);
+}
+
+/**
+ * msp_dma_xfer - Handles DMA transfers over i2s bus.
+ * @msp: main msp structure.
+ * @msg: i2s_message contains info about transmit and receive data.
+ * Context: process
+ *
+ * This will first check whether data buffer is dmaable or not.
+ * Call dma_map_single apis etc to make it dmaable dma. Starts the dma transfer
+ * for TX and RX parallely and wait for it to get completed.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_dma_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ int status = 0;
+
+ switch (msg->i2s_transfer_mode) {
+ default:
+ case I2S_TRANSFER_MODE_SINGLE_DMA:
+ if (msg->i2s_direction == I2S_DIRECTION_TX ||
+ msg->i2s_direction == I2S_DIRECTION_BOTH)
+ if (msg->txdata && (msg->txbytes > 0)) {
+ if (!msg->dma_flag)
+ msg->txdata =
+ (void *)dma_map_single(NULL,
+ msg->txdata,
+ msg->txbytes,
+ DMA_TO_DEVICE);
+ status = msp_single_dma_tx(msp,
+ (dma_addr_t)msg->txdata,
+ msg->txbytes);
+ }
+ if (msg->i2s_direction == I2S_DIRECTION_RX ||
+ msg->i2s_direction == I2S_DIRECTION_BOTH)
+ if (msg->rxdata && (msg->rxbytes > 0)) {
+ if (!msg->dma_flag)
+ msg->rxdata =
+ (void *)dma_map_single(NULL,
+ msg->rxdata,
+ msg->rxbytes,
+ DMA_FROM_DEVICE
+ );
+ status = msp_single_dma_rx(msp,
+ (dma_addr_t)msg->rxdata,
+ msg->rxbytes);
+ }
+ break;
+
+ case I2S_TRANSFER_MODE_CYCLIC_DMA:
+ if (msg->i2s_direction == I2S_DIRECTION_TX) {
+ msp_cyclic_dma_start(msp,
+ msg->sg,
+ msg->sg_len,
+ DMA_TO_DEVICE);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)),
+ msp->registers + MSP_GCR);
+ } else {
+ msp_cyclic_dma_start(msp,
+ msg->sg,
+ msg->sg_len,
+ DMA_FROM_DEVICE);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)),
+ msp->registers + MSP_GCR);
+ }
+ break;
+
+ case I2S_TRANSFER_MODE_INF_LOOPBACK:
+ msp_loopback_inf_start_dma(msp,
+ (dma_addr_t)msg->rxdata,
+ msg->rxbytes);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)),
+ msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)),
+ msp->registers + MSP_GCR);
+ break;
+ }
+
+ return status;
+}
+
+#if 0
+/**
+ * msp_handle_irq - Interrupt handler routine.
+ * @irq: irq no.
+ * @dev_id: device structure registered in request irq.
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static irqreturn_t msp_handle_irq(int irq, void *dev_id)
+{
+ u32 irq_status;
+ struct msp *msp = (struct msp *)dev_id;
+ struct i2s_message *message = &msp->xfer_data.message;
+ u32 irq_mask = 0;
+ irq_status = stm_msp_read(msp->registers + MSP_MIS);
+ irq_mask = stm_msp_read(msp->registers + MSP_IMSC);
+/* Disable the interrupt to prevent immediate recurrence */
+ stm_msp_write(stm_msp_read(msp->registers + MSP_IMSC) & ~irq_status,
+ msp->registers + MSP_IMSC);
+
+/* Clear the interrupt */
+ stm_msp_write(irq_status, msp->registers + MSP_ICR);
+/* Check for an error condition */
+ msp->msp_io_error = irq_status & (RECEIVE_OVERRUN_ERROR_INT |
+ RECEIVE_FRAME_SYNC_ERR_INT |
+ TRANSMIT_UNDERRUN_ERR_INT |
+ TRANSMIT_FRAME_SYNC_ERR_INT);
+
+ /*Currently they are ignored */
+ if (irq_status & RECEIVE_OVERRUN_ERROR_INT)
+ ;
+ if (irq_status & TRANSMIT_UNDERRUN_ERR_INT)
+ ;
+
+ /* This code has been added basically to support loopback mode
+ * Basically Transmit interrupt is not disabled even after its
+ * completion so that receive fifo gets an additional interrupt
+ */
+ if (irq_mask & (RECEIVE_SERVICE_INT)
+ && (irq_mask & (TRANSMIT_SERVICE_INT)) && (msp->loopback_enable)) {
+ if (msp->read)
+ msp->read(&msp->xfer_data);
+ if (msp->write)
+ msp->write(&msp->xfer_data);
+ if (message->rx_offset >= message->rxbytes) {
+ if (msp->xfer_data.rx_handler)
+ msp->xfer_data.rx_handler(msp->
+ xfer_data.
+ rx_callback_data,
+ message->rx_offset);
+ msp->xfer_data.rx_handler = NULL;
+ return IRQ_HANDLED;
+ }
+
+ if (message->tx_offset >= message->txbytes) {
+ if (msp->xfer_data.tx_handler)
+ msp->xfer_data.tx_handler(msp->xfer_data.
+ tx_callback_data,
+ message->tx_offset);
+ msp->xfer_data.tx_handler = NULL;
+ }
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ return IRQ_HANDLED;
+ }
+
+ if (irq_status & RECEIVE_SERVICE_INT) {
+ if (msp->read)
+ msp->read(&msp->xfer_data);
+ if (message->rx_offset >= message->rxbytes) {
+ irq_mask &= ~RECEIVE_SERVICE_INT;
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ if (msp->xfer_data.rx_handler)
+ msp->xfer_data.rx_handler(msp->
+ xfer_data.
+ rx_callback_data,
+ message->rx_offset);
+ if (!(irq_status & TRANSMIT_SERVICE_INT))
+ return IRQ_HANDLED;
+ }
+ }
+ if (irq_status & TRANSMIT_SERVICE_INT) {
+ if (msp->write)
+ msp->write(&msp->xfer_data);
+ if (message->tx_offset >= message->txbytes) {
+ irq_mask &= ~TRANSMIT_SERVICE_INT;
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ if (msp->xfer_data.tx_handler)
+ msp->xfer_data.tx_handler(msp->xfer_data.
+ tx_callback_data,
+ message->tx_offset);
+ return IRQ_HANDLED;
+ }
+ }
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ return IRQ_HANDLED;
+
+}
+#endif
+
+/**
+ * msp_interrupt_xfer - Handles Interrupt transfers over i2s bus.
+ * @msp: main msp structure.
+ * @msg: i2s_message contains info about transmit and receive data.
+ * Context: Process or interrupt.
+ *
+ * This implements transfer and receive functions used in interrupt mode.
+ * This can be used in interrupt context if a callback handler is registered
+ * by client driver. This has been to improve performance in interrupt mode.
+ * Hence can't use sleep in this function.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_interrupt_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ struct i2s_message *message;
+ u32 irq_mask = 0;
+
+ if (msg->i2s_transfer_mode != I2S_TRANSFER_MODE_NON_DMA)
+ return -EINVAL;
+
+ if (msg->txbytes) {
+ msp->xfer_data.message.txbytes = msg->txbytes;
+ msp->xfer_data.message.txdata = msg->txdata;
+ msp->xfer_data.message.tx_offset = 0;
+ }
+ if (msg->rxbytes) {
+ msp->xfer_data.message.rxbytes = msg->rxbytes;
+ msp->xfer_data.message.rxdata = msg->rxdata;
+ msp->xfer_data.message.rx_offset = 0;
+ }
+ message = &msp->xfer_data.message;
+ if ((message->txdata == NULL || message->txbytes == 0)
+ && (message->rxdata == NULL || message->rxbytes == 0)) {
+ printk(KERN_ERR
+ "transmit_receive_data is NULL with bytes > 0\n");
+ return -EINVAL;
+ }
+
+ msp->msp_io_error = 0;
+
+ if (message->tx_offset < message->txbytes) {
+ irq_mask |= TRANSMIT_SERVICE_INT;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (message->rx_offset < message->rxbytes) {
+ irq_mask |= RECEIVE_SERVICE_INT;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) |
+ irq_mask), msp->registers + MSP_IMSC);
+ return 0;
+}
+
+/**
+ * func_notify_timer - Handles Polling hang issue over i2s bus.
+ * @data: main msp data address
+ * Context: Interrupt.
+ *
+ * This is used to handle error condition in transfer and receive function used
+ * in polling mode.
+ * Sometimes due to passing wrong protocol desc , polling transfer may hang.
+ * To prevent this, timer is added.
+ *
+ * Returns void.
+ */
+static void func_notify_timer(unsigned long data)
+{
+ struct msp *msp = (struct msp *)data;
+ if (msp->polling_flag) {
+ msp->msp_io_error = 1;
+ printk(KERN_ERR
+ "Polling is taking two much time, may be it got hang\n");
+ del_timer(&msp->notify_timer);
+ }
+}
+
+/**
+ * msp_polling_xfer - Handles Polling transfers over i2s bus.
+ * @msp: main msp structure.
+ * @msg: i2s_message contains info about transmit and receive data.
+ * Context: Process.
+ *
+ * This implements transfer and receive functions used in polling mode. This is
+ * blocking fucntion.
+ * It is recommended to use interrupt or dma mode for better performance rather
+ * than the polling mode.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_polling_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ struct i2s_message *message;
+ u32 time_expire = 0;
+ u32 tr_ex = 0, rr_ex = 0;
+ u32 msec_jiffies = 0;
+
+ if (msg->i2s_transfer_mode != I2S_TRANSFER_MODE_NON_DMA)
+ return -EINVAL;
+
+ if (msg->txbytes) {
+ msp->xfer_data.message.txbytes = msg->txbytes;
+ msp->xfer_data.message.txdata = msg->txdata;
+ msp->xfer_data.message.tx_offset = 0;
+ tr_ex = msg->txbytes;
+ }
+ if (msg->rxbytes) {
+ msp->xfer_data.message.rxbytes = msg->rxbytes;
+ msp->xfer_data.message.rxdata = msg->rxdata;
+ msp->xfer_data.message.rx_offset = 0;
+ rr_ex = msg->rxbytes;
+ }
+ message = &msp->xfer_data.message;
+ time_expire = (tr_ex + rr_ex) / 1024;
+ if (!time_expire)
+ msec_jiffies = 500;
+ else
+ msec_jiffies = time_expire * 500;
+ msp->notify_timer.expires = jiffies + msecs_to_jiffies(msec_jiffies);
+ down(&msp->lock);
+ if (message->txdata == NULL && message->txbytes > 0) {
+ printk(KERN_ERR
+ "transmit_receive_data is NULL with bytes > 0\n");
+ return -EINVAL;
+ }
+
+ if (message->rxdata == NULL && message->rxbytes > 0) {
+ printk(KERN_ERR
+ "transmit_receive_data is NULL with bytes > 0\n");
+ return -EINVAL;
+ }
+ msp->msp_io_error = 0;
+ msp->polling_flag = 1;
+ add_timer(&msp->notify_timer);
+ while (message->tx_offset < message->txbytes
+ || message->rx_offset < message->rxbytes) {
+ if (msp->msp_io_error)
+ break;
+ if (msp->read)
+ msp->read(&msp->xfer_data);
+ if (msp->write)
+ msp->write(&msp->xfer_data);
+ }
+ msp->polling_flag = 0;
+ del_timer(&msp->notify_timer);
+ up(&msp->lock);
+ return message->txbytes + message->rxbytes;
+}
+
+/**
+ * stm_msp_transceive_data - Main i2s transfer function.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * @message: i2s message structure contains transceive info.
+ * Context: process or interrupt.
+ *
+ * This function is registered over i2s_xfer funtions. It will handle main i2s
+ * transfer over i2s bus in various modes.It call msp transfer function on which
+ * suitable transfer function is already registered i.e dma ,interrupt or
+ * polling function.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int stm_msp_transceive_data(struct i2s_controller *i2s_cont,
+ struct i2s_message *message)
+{
+ int status = 0;
+ struct msp *msp = (struct msp *)i2s_cont->data;
+
+ if (!message || (msp->msp_state == MSP_STATE_IDLE)) {
+ printk(KERN_ERR "Message is NULL\n");
+ return -EPERM;
+ }
+
+ msp->msp_state = MSP_STATE_RUN;
+ if (msp->transfer)
+ status = msp->transfer(msp, message);
+
+ if (msp->msp_state == MSP_STATE_RUN)
+ msp->msp_state = MSP_STATE_CONFIGURED;
+
+ return status;
+}
+
+/**
+ * msp_disable_receive - Disable receive functionality.
+ * @msp: main msp structure.
+ * Context: process.
+ *
+ * This function will disable msp controller's receive functionality like dma,
+ * interrupt receive data buffer all are disabled.
+ *
+ * Returns void.
+ */
+static void msp_disable_receive(struct msp *msp)
+{
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~RX_ENABLE)), msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_DMACR) &
+ (~RX_DMA_ENABLE)), msp->registers + MSP_DMACR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) &
+ (~
+ (RECEIVE_SERVICE_INT |
+ RECEIVE_OVERRUN_ERROR_INT))),
+ msp->registers + MSP_IMSC);
+ msp->xfer_data.message.rxbytes = 0;
+ msp->xfer_data.message.rx_offset = 0;
+ msp->xfer_data.message.rxdata = NULL;
+ msp->read = NULL;
+
+}
+
+/**
+ * msp_disable_transmit - Disable transmit functionality.
+ * @msp: main msp structure.
+ * Context: process.
+ *
+ * This function will disable msp controller's transmit functionality like dma,
+ * interrupt transmit data buffer all are disabled.
+ *
+ * Returns void.
+ */
+static void msp_disable_transmit(struct msp *msp)
+{
+
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_DMACR) &
+ (~TX_DMA_ENABLE)), msp->registers + MSP_DMACR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) &
+ (~
+ (TRANSMIT_SERVICE_INT |
+ TRANSMIT_UNDERRUN_ERR_INT))),
+ msp->registers + MSP_IMSC);
+ msp->xfer_data.message.txbytes = 0;
+ msp->xfer_data.message.tx_offset = 0;
+ msp->xfer_data.message.txdata = NULL;
+ msp->write = NULL;
+
+}
+
+/**
+ * stm_msp_disable - disable the given msp controller
+ * @msp: specifies the msp contoller data
+ * @direction: specifies the transmit/receive direction
+ * @flag: It indicates the functionality that needs to be disabled.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static int stm_msp_disable(struct msp *msp, int direction, i2s_flag flag)
+{
+ int limit = 32;
+ u32 dummy = 0;
+ int status = 0;
+ if (!
+ (stm_msp_read(msp->registers + MSP_GCR) &
+ ((TX_ENABLE | RX_ENABLE)))) {
+ return 0;
+ }
+ if (msp->work_mode == MSP_DMA_MODE) {
+ if (flag == DISABLE_ALL || flag == DISABLE_TRANSMIT) {
+ if (msp->tx_pipeid != NULL) {
+ if (msp->infinite) {
+ stedma40_cyclic_stop(msp->tx_pipeid);
+ stedma40_cyclic_free(msp->tx_pipeid);
+ }
+ msp->tx_pipeid->device->
+ device_control(msp->tx_pipeid,
+ DMA_TERMINATE_ALL, 0);
+ dma_release_channel(msp->tx_pipeid);
+ msp->tx_pipeid = NULL;
+ }
+ }
+ if ((flag == DISABLE_ALL || flag == DISABLE_RECEIVE)) {
+ if (msp->rx_pipeid != NULL) {
+ if (msp->infinite) {
+ stedma40_cyclic_stop(msp->rx_pipeid);
+ stedma40_cyclic_free(msp->rx_pipeid);
+ }
+
+ msp->rx_pipeid->device->
+ device_control(msp->rx_pipeid,
+ DMA_TERMINATE_ALL, 0);
+ dma_release_channel(msp->rx_pipeid);
+ msp->rx_pipeid = NULL;
+ }
+ }
+
+ msp->infinite = false;
+ }
+ if (flag == DISABLE_TRANSMIT)
+ msp_disable_transmit(msp);
+ else if (flag == DISABLE_RECEIVE)
+ msp_disable_receive(msp);
+ else {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (LOOPBACK_MASK)), msp->registers + MSP_GCR);
+ /* Flush Tx fifo */
+ while ((!
+ (stm_msp_read(msp->registers + MSP_FLR) &
+ TX_FIFO_EMPTY)) && limit--)
+ dummy = stm_msp_read(msp->registers + MSP_DR);
+
+ /* Disable Transmit channel */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+ limit = 32;
+ /* Flush Rx Fifo */
+ while ((!
+ (stm_msp_read(msp->registers + MSP_FLR) &
+ RX_FIFO_EMPTY)) && limit--)
+ dummy = stm_msp_read(msp->registers + MSP_DR);
+ /* Disable Loopback and Receive channel */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~(RX_ENABLE | LOOPBACK_MASK))),
+ msp->registers + MSP_GCR);
+ /*This has been added in order to fix fifo flush problem
+ When last xfer occurs some data remains in fifo. In order to
+ flush that data delay is needed */
+ msleep(10);
+ msp_disable_transmit(msp);
+ msp_disable_receive(msp);
+
+ }
+
+ /* disable sample rate and frame generators */
+ if (flag == DISABLE_ALL) {
+ msp->msp_state = MSP_STATE_IDLE;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+ memset(&msp->xfer_data, 0, sizeof(struct trans_data));
+ if (msp->plat_exit)
+ status = msp->plat_exit();
+ if (status)
+ printk(KERN_ERR "Error in msp_i2s_exit\n");
+ if (msp->work_mode == MSP_POLLING_MODE
+ && msp->msp_state == MSP_STATE_RUN) {
+ up(&msp->lock);
+ }
+ msp->transfer = NULL;
+ stm_msp_write(0, msp->registers + MSP_GCR);
+ stm_msp_write(0, msp->registers + MSP_TCF);
+ stm_msp_write(0, msp->registers + MSP_RCF);
+ stm_msp_write(0, msp->registers + MSP_DMACR);
+ stm_msp_write(0, msp->registers + MSP_SRG);
+ stm_msp_write(0, msp->registers + MSP_MCR);
+ stm_msp_write(0, msp->registers + MSP_RCM);
+ stm_msp_write(0, msp->registers + MSP_RCV);
+ stm_msp_write(0, msp->registers + MSP_TCE0);
+ stm_msp_write(0, msp->registers + MSP_TCE1);
+ stm_msp_write(0, msp->registers + MSP_TCE2);
+ stm_msp_write(0, msp->registers + MSP_TCE3);
+ stm_msp_write(0, msp->registers + MSP_RCE0);
+ stm_msp_write(0, msp->registers + MSP_RCE1);
+ stm_msp_write(0, msp->registers + MSP_RCE2);
+ stm_msp_write(0, msp->registers + MSP_RCE3);
+ }
+ return status;
+}
+
+/**
+ * stm_msp_close - Close the current i2s connection btw controller and client.
+ * @i2s_cont: i2s controller structure
+ * @flag: It indicates the functionality that needs to be disabled.
+ * Context: process
+ *
+ * It will call msp_disable and reset the msp configuration. Disables Rx and Tx
+ * channels, free gpio irqs and interrupt pins.
+ * Called by i2s client driver to indicate the completion of use of i2s bus.
+ * It is registered on i2s_close function.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int stm_msp_close(struct i2s_controller *i2s_cont, i2s_flag flag)
+{
+ int status = 0;
+ struct msp *msp = (struct msp *)i2s_cont->data;
+ down(&msp->lock);
+ if (msp->users == 0) {
+ pr_err("MSP already closed!\n");
+ status = -EINVAL;
+ goto end;
+ }
+ dev_dbg(&i2s_cont->dev, "%s: users = %d, flag = %d.\n",
+ __func__, msp->users, flag);
+ /* We need to call it twice for DISABLE_ALL*/
+ msp->users = flag == DISABLE_ALL ? 0 : msp->users - 1;
+ if (msp->users)
+ status = stm_msp_disable(msp, MSP_BOTH_T_R_MODE, flag);
+ else {
+ status = stm_msp_disable(msp, MSP_BOTH_T_R_MODE, DISABLE_ALL);
+ clk_disable(msp->clk);
+ status = regulator_disable(msp_vape_supply);
+ if (status != 0) {
+ dev_err(&msp->i2s_cont->dev,
+ "Failed to disable regulator\n");
+ clk_enable(msp->clk);
+ goto end;
+ }
+ }
+ if (status)
+ goto end;
+ if (msp->users)
+ msp->direction = flag == DISABLE_TRANSMIT ?
+ MSP_RECEIVE_MODE : MSP_TRANSMIT_MODE;
+
+ if (msp->vape_opp_constraint == 1) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+end:
+ up(&msp->lock);
+ return status;
+
+}
+
+static int stm_msp_hw_status(struct i2s_controller *i2s_cont)
+{
+ struct msp *msp = (struct msp *)i2s_cont->data;
+
+ int status = stm_msp_read(msp->registers + MSP_RIS) & 0xee;
+ if (status)
+ stm_msp_write(status, msp->registers + MSP_ICR);
+
+ return status;
+}
+
+static dma_addr_t stm_msp_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction)
+{
+ struct msp *msp = (struct msp *)i2s_cont->data;
+ return (i2s_direction == I2S_DIRECTION_TX) ?
+ stedma40_get_src_addr(msp->tx_pipeid) :
+ stedma40_get_dst_addr(msp->rx_pipeid);
+}
+
+ /*Platform driver's functions */
+/**
+ * msp_probe - Probe function
+ * @pdev: platform device structure.
+ * Context: process
+ *
+ * Probe function of msp platform driver.Handles allocation of memory and irq
+ * resource. It creates i2s_controller and one i2s_device per msp controller.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+int msp_probe(struct platform_device *pdev)
+{
+ int status = 0;
+ struct device *dev;
+ s16 platform_num = 0;
+ struct resource *res = NULL;
+ int irq;
+ struct i2s_controller *i2s_cont;
+ struct msp_i2s_platform_data *platform_data;
+ struct msp *msp;
+
+ if (!pdev)
+ return -EPERM;
+ msp = kzalloc(sizeof(*msp), GFP_KERNEL);
+
+ platform_data = (struct msp_i2s_platform_data *)pdev->dev.platform_data;
+
+ msp->id = platform_data->id;
+ msp->plat_init = platform_data->msp_i2s_init;
+ msp->plat_exit = platform_data->msp_i2s_exit;
+
+ msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+ msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+ dev = &pdev->dev;
+ platform_num = msp->id - 1;
+
+ sema_init(&msp->lock, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "probe - MEM resources not defined\n");
+ status = -EINVAL;
+ goto free_msp;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ status = -EINVAL;
+ goto free_msp;
+ }
+ msp->irq = irq;
+
+ msp->registers = ioremap(res->start, (res->end - res->start + 1));
+ if (msp->registers == NULL) {
+ status = -EINVAL;
+ goto free_msp;
+ }
+
+ msp_vape_supply = regulator_get(NULL, "v-ape");
+ if (IS_ERR(msp_vape_supply)) {
+ status = PTR_ERR(msp_vape_supply);
+ printk(KERN_WARNING "msp i2s : failed to get v-ape supply\n");
+ goto free_irq;
+ }
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 50);
+ msp->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(msp->clk)) {
+ status = PTR_ERR(msp->clk);
+ goto free_irq;
+ }
+
+ init_timer(&msp->notify_timer);
+ msp->notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ msp->notify_timer.function = func_notify_timer;
+ msp->notify_timer.data = (unsigned long)msp;
+
+ msp->rx_pipeid = NULL;
+ msp->tx_pipeid = NULL;
+ msp->read = NULL;
+ msp->write = NULL;
+ msp->transfer = NULL;
+ msp->msp_state = MSP_STATE_IDLE;
+ msp->loopback_enable = 0;
+
+ dev_set_drvdata(&pdev->dev, msp);
+ /* I2S Controller is allocated and added in I2S controller class. */
+ i2s_cont =
+ (struct i2s_controller *)kzalloc(sizeof(*i2s_cont), GFP_KERNEL);
+ if (!i2s_cont) {
+ dev_err(&pdev->dev, "i2s controller alloc failed \n");
+ status = -EINVAL;
+ goto del_timer;
+ }
+ i2s_cont->dev.parent = dev;
+ i2s_cont->algo = &i2s_algo;
+ i2s_cont->data = (void *)msp;
+ i2s_cont->id = platform_num;
+ snprintf(i2s_cont->name, sizeof(i2s_cont->name),
+ "MSP_I2S.%04x", platform_num);
+
+ status = i2s_add_controller(i2s_cont);
+ if (status) {
+ dev_err(&pdev->dev, "i2s add controller failed (%d)\n", status);
+ goto free_cont;
+ }
+ msp->i2s_cont = i2s_cont;
+ return status;
+free_cont:
+ kfree(msp->i2s_cont);
+del_timer:
+ del_timer_sync(&msp->notify_timer);
+ clk_put(msp->clk);
+free_irq:
+ iounmap(msp->registers);
+free_msp:
+ kfree(msp);
+ return status;
+}
+
+/**
+ * msp_remove - remove function
+ * @pdev: platform device structure.
+ * Context: process
+ *
+ * remove function of msp platform driver.Handles dellocation of memory and irq
+ * resource. It deletes i2s_controller and one i2s_device per msp controller
+ * created in msp_probe.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_remove(struct platform_device *pdev)
+{
+ struct msp *msp =
+ (struct msp *)dev_get_drvdata(&pdev->dev);
+ int status = 0;
+ i2s_del_controller(msp->i2s_cont);
+ del_timer_sync(&msp->notify_timer);
+ clk_put(msp->clk);
+ iounmap(msp->registers);
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "msp_i2s");
+ regulator_put(msp_vape_supply);
+ kfree(msp);
+ return status;
+}
+#ifdef CONFIG_PM
+/**
+ * msp_suspend - MSP suspend function registered with PM framework.
+ * @pdev: Reference to platform device structure of the device
+ * @state: power mgmt state.
+ *
+ * This function is invoked when the system is going into sleep, called
+ * by the power management framework of the linux kernel.
+ * Nothing is required as controller is configured with every transfer.
+ * It is assumed that no active tranfer is in progress at this time.
+ * Client driver should make sure of this.
+ *
+ */
+
+int msp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct msp *msp =
+ (struct msp *)dev_get_drvdata(&pdev->dev);
+
+ down(&msp->lock);
+ if (msp->users > 0) {
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ up(&msp->lock);
+
+ return 0;
+}
+/**
+ * msp_resume - MSP Resume function registered with PM framework.
+ * @pdev: Reference to platform device structure of the device
+ *
+ * This function is invoked when the system is coming out of sleep, called
+ * by the power management framework of the linux kernel.
+ * Nothing is required.
+ *
+ */
+
+int msp_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define msp_suspend NULL
+#define msp_resume NULL
+#endif
+
+static struct platform_driver msp_i2s_driver = {
+ .probe = msp_probe,
+ .remove = msp_remove,
+ .suspend = msp_suspend,
+ .resume = msp_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MSP_I2S",
+ },
+};
+
+static int __init stm_msp_mod_init(void)
+{
+ return platform_driver_register(&msp_i2s_driver);
+}
+
+static void __exit stm_msp_exit(void)
+{
+ platform_driver_unregister(&msp_i2s_driver);
+ return;
+}
+
+module_init(stm_msp_mod_init);
+module_exit(stm_msp_exit);
+
+MODULE_AUTHOR("Sandeep Kaushik");
+MODULE_DESCRIPTION("STM MSP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/i2s/msp_i2s.h b/drivers/misc/i2s/msp_i2s.h
new file mode 100644
index 00000000000..2ac86e7700e
--- /dev/null
+++ b/drivers/misc/i2s/msp_i2s.h
@@ -0,0 +1,362 @@
+/*----------------------------------------------------------------------------------*/
+/* copyright STMicroelectronics, 2007. */
+/* */
+/* This program is free software; you can redistribute it and/or modify it under */
+/* the terms of the GNU General Public License as published by the Free */
+/* Software Foundation; either version 2.1 of the License, or (at your option) */
+/* any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but WITHOUT */
+/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */
+/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
+/*----------------------------------------------------------------------------------*/
+
+
+#ifndef STM_MSP_HEADER
+#define STM_MSP_HEADER
+
+#define MSP_DR 0x00
+#define MSP_GCR 0x04
+#define MSP_TCF 0x08
+#define MSP_RCF 0x0c
+#define MSP_SRG 0x10
+#define MSP_FLR 0x14
+#define MSP_DMACR 0x18
+
+#define MSP_IMSC 0x20
+#define MSP_RI 0x24
+#define MSP_MIS 0x28
+#define MSP_ICR 0x2c
+#define MSP_MCR 0x30
+#define MSP_RCV 0x34
+#define MSP_RCM 0x38
+
+#define MSP_TCE0 0x40
+#define MSP_TCE1 0x44
+#define MSP_TCE2 0x48
+#define MSP_TCE3 0x4c
+
+#define MSP_RCE0 0x60
+#define MSP_RCE1 0x64
+#define MSP_RCE2 0x68
+#define MSP_RCE3 0x6c
+
+#define MSP_ITCR 0x80
+#define MSP_ITIP 0x84
+#define MSP_ITOP 0x88
+#define MSP_TSTDR 0x8c
+
+#define MSP_PID0 0xfe0
+#define MSP_PID1 0xfe4
+#define MSP_PID2 0xfe8
+#define MSP_PID3 0xfec
+
+#define MSP_CID0 0xff0
+#define MSP_CID1 0xff4
+#define MSP_CID2 0xff8
+#define MSP_CID3 0xffc
+
+
+/* Single or dual phase mode */
+enum
+{
+ MSP_SINGLE_PHASE,
+ MSP_DUAL_PHASE
+};
+
+
+/* Transmit/Receive shifter status
+-----------------------------------*/
+enum
+{
+ MSP_SxHIFTER_IDLE = 0,
+ MSP_SHIFTER_WORKING = 1
+};
+
+
+/* Transmit/Receive FIFO status
+---------------------------------*/
+enum
+{
+ MSP_FIFO_FULL,
+ MSP_FIFO_PART_FILLED,
+ MSP_FIFO_EMPTY
+};
+
+
+/* Frame length
+------------------*/
+enum
+{
+ MSP_FRAME_LENGTH_1 = 0,
+ MSP_FRAME_LENGTH_2 = 1,
+ MSP_FRAME_LENGTH_4 = 3,
+ MSP_FRAME_LENGTH_8 = 7,
+ MSP_FRAME_LENGTH_12 = 11,
+ MSP_FRAME_LENGTH_16 = 15,
+ MSP_FRAME_LENGTH_20 = 19,
+ MSP_FRAME_LENGTH_32 = 31,
+ MSP_FRAME_LENGTH_48 = 47,
+ MSP_FRAME_LENGTH_64 = 63
+};
+
+/* Element length */
+enum
+{
+ MSP_ELEM_LENGTH_8 = 0,
+ MSP_ELEM_LENGTH_10 = 1,
+ MSP_ELEM_LENGTH_12 = 2,
+ MSP_ELEM_LENGTH_14 = 3,
+ MSP_ELEM_LENGTH_16 = 4,
+ MSP_ELEM_LENGTH_20 = 5,
+ MSP_ELEM_LENGTH_24 = 6,
+ MSP_ELEM_LENGTH_32 = 7
+};
+
+
+/* Data delay (in bit clock cycles)
+---------------------------------------*/
+enum
+{
+ MSP_DELAY_0 = 0,
+ MSP_DELAY_1 = 1,
+ MSP_DELAY_2 = 2,
+ MSP_DELAY_3 = 3
+};
+
+
+/* Configurations of clocks (transmit, receive or sample rate generator)
+-------------------------------------------------------------------------*/
+enum
+{
+ MSP_RISING_EDGE = 0,
+ MSP_FALLING_EDGE = 1
+};
+
+/* Protocol dependant parameters list */
+struct msp_protocol_desc
+{
+ u32 phase_mode;
+ u32 frame_len_1;
+ u32 frame_len_2;
+ u32 element_len_1;
+ u32 element_len_2;
+ u32 data_delay;
+ u32 tx_clock_edge;
+ u32 rx_clock_edge;
+};
+#define RX_ENABLE_MASK 0x00000001
+#define RX_FIFO_ENABLE_MASK 0x00000002
+#define RX_FRAME_SYNC_MASK 0x00000004
+#define DIRECT_COMPANDING_MASK 0x00000008
+#define RX_SYNC_SEL_MASK 0x00000010
+#define RX_CLK_POL_MASK 0x00000020
+#define RX_CLK_SEL_MASK 0x00000040
+#define LOOPBACK_MASK 0x00000080
+#define TX_ENABLE_MASK 0x00000100
+#define TX_FIFO_ENABLE_MASK 0x00000200
+#define TX_FRAME_SYNC_MASK 0x00000400
+#define TX_MSP_TDR_TSR 0x00000800
+#define TX_SYNC_SEL_MASK 0x00001800
+#define TX_CLK_POL_MASK 0x00002000
+#define TX_CLK_SEL_MASK 0x00004000
+#define TX_EXTRA_DELAY_MASK 0x00008000
+#define SRG_ENABLE_MASK 0x00010000
+#define SRG_CLK_POL_MASK 0x00020000
+#define SRG_CLK_SEL_MASK 0x000C0000
+#define FRAME_GEN_EN_MASK 0x00100000
+#define SPI_CLK_MODE_MASK 0x00600000
+#define SPI_BURST_MODE_MASK 0x00800000
+
+#define RXEN_BIT 0
+#define RFFEN_BIT 1
+#define RFSPOL_BIT 2
+#define DCM_BIT 3
+#define RFSSEL_BIT 4
+#define RCKPOL_BIT 5
+#define RCKSEL_BIT 6
+#define LBM_BIT 7
+#define TXEN_BIT 8
+#define TFFEN_BIT 9
+#define TFSPOL_BIT 10
+#define TFSSEL_BIT 11
+#define TCKPOL_BIT 13
+#define TCKSEL_BIT 14
+#define TXDDL_BIT 15
+#define SGEN_BIT 16
+#define SCKPOL_BIT 17
+#define SCKSEL_BIT 18
+#define FGEN_BIT 20
+#define SPICKM_BIT 21
+
+#define msp_rx_clkpol_bit(n) ((n & 1) << RCKPOL_BIT)
+#define msp_tx_clkpol_bit(n) ((n & 1) << TCKPOL_BIT)
+#define msp_spi_clk_mode_bits(n) ((n & 3) << SPICKM_BIT)
+
+
+/* Use this to clear the clock mode bits to non-spi */
+#define MSP_NON_SPI_CLK_MASK 0x00600000
+
+#define P1ELEN_BIT 0
+#define P1FLEN_BIT 3
+#define DTYP_BIT 10
+#define ENDN_BIT 12
+#define DDLY_BIT 13
+#define FSIG_BIT 15
+#define P2ELEN_BIT 16
+#define P2FLEN_BIT 19
+#define P2SM_BIT 26
+#define P2EN_BIT 27
+
+#define msp_p1_elem_len_bits(n) (n & 0x00000007)
+#define msp_p2_elem_len_bits(n) (((n) << P2ELEN_BIT) & 0x00070000)
+#define msp_p1_frame_len_bits(n) (((n) << P1FLEN_BIT) & 0x00000378)
+#define msp_p2_frame_len_bits(n) (((n) << P2FLEN_BIT) & 0x03780000)
+#define msp_data_delay_bits(n) (((n) << DDLY_BIT) & 0x00003000)
+#define msp_data_type_bits(n) (((n) << DTYP_BIT) & 0x00000600)
+#define msp_p2_start_mode_bit(n) (n << P2SM_BIT)
+#define msp_p2_enable_bit(n) (n << P2EN_BIT)
+
+/* Flag register
+--------------------*/
+#define RX_BUSY 0x00000001
+#define RX_FIFO_EMPTY 0x00000002
+#define RX_FIFO_FULL 0x00000004
+#define TX_BUSY 0x00000008
+#define TX_FIFO_EMPTY 0x00000010
+#define TX_FIFO_FULL 0x00000020
+
+#define RBUSY_BIT 0
+#define RFE_BIT 1
+#define RFU_BIT 2
+#define TBUSY_BIT 3
+#define TFE_BIT 4
+#define TFU_BIT 5
+
+/* Multichannel control register
+---------------------------------*/
+#define RMCEN_BIT 0
+#define RMCSF_BIT 1
+#define RCMPM_BIT 3
+#define TMCEN_BIT 5
+#define TNCSF_BIT 6
+
+/* Sample rate generator register
+------------------------------------*/
+#define SCKDIV_BIT 0
+#define FRWID_BIT 10
+#define FRPER_BIT 16
+
+#define SCK_DIV_MASK 0x0000003FF
+#define frame_width_bits(n) (((n) << FRWID_BIT) &0x0000FC00)
+#define frame_period_bits(n) (((n) << FRPER_BIT) &0x1FFF0000)
+
+
+/* DMA controller register
+---------------------------*/
+#define RX_DMA_ENABLE 0x00000001
+#define TX_DMA_ENABLE 0x00000002
+
+#define RDMAE_BIT 0
+#define TDMAE_BIT 1
+
+/*Interrupt Register
+-----------------------------------------*/
+#define RECEIVE_SERVICE_INT 0x00000001
+#define RECEIVE_OVERRUN_ERROR_INT 0x00000002
+#define RECEIVE_FRAME_SYNC_ERR_INT 0x00000004
+#define RECEIVE_FRAME_SYNC_INT 0x00000008
+#define TRANSMIT_SERVICE_INT 0x00000010
+#define TRANSMIT_UNDERRUN_ERR_INT 0x00000020
+#define TRANSMIT_FRAME_SYNC_ERR_INT 0x00000040
+#define TRANSMIT_FRAME_SYNC_INT 0x00000080
+#define ALL_INT 0x000000ff
+
+/* Protocol configuration values
+* I2S: Single phase, 16 bits, 2 words per frame
+-----------------------------------------------*/
+#define I2S_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_32, \
+ MSP_ELEM_LENGTH_32, \
+ MSP_DELAY_1, \
+ MSP_FALLING_EDGE, \
+ MSP_FALLING_EDGE \
+}
+
+#define PCM_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_16, \
+ MSP_ELEM_LENGTH_16, \
+ MSP_DATA_DELAY, \
+ MSP_TX_CLOCK_EDGE, \
+ MSP_RX_CLOCK_EDGE \
+}
+
+/* Companded PCM: Single phase, 8 bits, 1 word per frame
+--------------------------------------------------------*/
+#define PCM_COMPAND_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_DELAY_0, \
+ MSP_RISING_EDGE, \
+ MSP_FALLING_EDGE \
+}
+
+/* AC97: Double phase, 1 element of 16 bits during first phase,
+* 12 elements of 20 bits in second phase.
+--------------------------------------------------------------*/
+#define AC97_PROTOCOL_DESC \
+{ \
+ MSP_DUAL_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_12, \
+ MSP_ELEM_LENGTH_16, \
+ MSP_ELEM_LENGTH_20, \
+ MSP_DELAY_1, \
+ MSP_RISING_EDGE, \
+ MSP_FALLING_EDGE \
+}
+
+#define SPI_MASTER_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_DELAY_1, \
+ MSP_FALLING_EDGE, \
+ MSP_RISING_EDGE \
+}
+#define SPI_SLAVE_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_DELAY_1, \
+ MSP_FALLING_EDGE, \
+ MSP_RISING_EDGE \
+}
+
+#define MSP_FRAME_PERIOD_IN_MONO_MODE 256
+#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32
+#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16
+
+#endif
+
diff --git a/include/linux/i2s/i2s.h b/include/linux/i2s/i2s.h
new file mode 100644
index 00000000000..b67fdb2d80d
--- /dev/null
+++ b/include/linux/i2s/i2s.h
@@ -0,0 +1,225 @@
+/*----------------------------------------------------------------------------*/
+/* copyright STMicroelectronics, 2007. */
+/* */
+/* This program is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by the Free */
+/* Software Foundation; either version 2.1 of the License, or (at your option)*/
+/* any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY */
+/* or FITNES */
+/* 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, see <http://www.gnu.org/licenses/>. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef __LINUX_I2S_H
+#define __LINUX_I2S_H
+
+/*
+ * INTERFACES between I2S controller-side drivers and I2S infrastructure.
+ */
+extern struct bus_type i2s_bus_type;
+#define I2S_NAME_SIZE 48
+/**
+ * struct i2s_device - Controller side proxy for an I2S slave device
+ * @dev: Driver model representation of the device.
+ * @controller: I2S controller used with the device.
+ * @chip_select: Chipselect, distinguishing chips handled by @controller.
+ * @modalias: Name of the driver to use with this device, or an alias
+ * for that name. This appears in the sysfs "modalias" attribute
+ * for driver coldplugging, and in uevents used for hotplugging
+ *
+ * A @i2s_device is used to interchange data between an I2S slave
+ *
+ * In @dev, the platform_data is used to hold information about this
+ * device that's meaningful to the device's protocol driver, but not
+ * to its controller.
+ */
+struct i2s_device {
+ struct device dev;
+ struct i2s_controller *controller;
+ u8 chip_select;
+ char modalias[32];
+};
+struct i2s_board_info {
+ /* the device name and module name are coupled, like platform_bus;
+ * "modalias" is normally the driver name.
+ *
+ * platform_data goes to i2s_device.dev.platform_data,
+ */
+ char modalias[32];
+ const void *platform_data;
+ u16 id;
+ u16 chip_select;
+};
+
+#ifdef CONFIG_STM_I2S
+extern int
+i2s_register_board_info(struct i2s_board_info const *info, unsigned n);
+#else
+/* board init code may ignore whether I2S is configured or not */
+static inline int
+i2s_register_board_info(struct i2s_board_info const *info, unsigned n)
+{
+ return 0;
+}
+#endif
+
+static inline struct i2s_device *to_i2s_device(struct device *dev)
+{
+ return dev ? container_of(dev, struct i2s_device, dev) : NULL;
+}
+
+static inline struct i2s_device *i2s_dev_get(struct i2s_device *i2s)
+{
+ return (i2s && get_device(&i2s->dev)) ? i2s : NULL;
+}
+
+static inline void i2s_dev_put(struct i2s_device *i2s)
+{
+ if (i2s)
+ put_device(&i2s->dev);
+}
+
+static inline void i2s_set_drvdata(struct i2s_device *i2s, void *data)
+{
+ dev_set_drvdata(&i2s->dev, data);
+}
+
+static inline void *i2s_get_drvdata(struct i2s_device *i2s)
+{
+ return dev_get_drvdata(&i2s->dev);
+}
+
+struct i2s_device_id {
+ char name[I2S_NAME_SIZE];
+ /*currently not used may be used in future */
+ u32 device_id;
+ u32 vendor_id;
+};
+
+/**
+ * struct i2s_driver - Host side "protocol" driver
+ */
+struct i2s_driver {
+ int (*probe) (struct i2s_device *i2s);
+ int (*remove) (struct i2s_device *i2s);
+ void (*shutdown) (struct i2s_device *i2s);
+ int (*suspend) (struct i2s_device *i2s, pm_message_t mesg);
+ int (*resume) (struct i2s_device *i2s);
+ struct device_driver driver;
+ const struct i2s_device_id *id_table;
+
+};
+
+static inline struct i2s_driver *to_i2s_driver(struct device_driver *drv)
+{
+ return drv ? container_of(drv, struct i2s_driver, driver) : NULL;
+}
+
+extern int i2s_register_driver(struct i2s_driver *sdrv);
+
+/**
+ * i2s_unregister_driver - reverse effect of i2s_register_driver
+ * @sdrv: the driver to unregister
+ * Context: can sleep
+ */
+static inline void i2s_unregister_driver(struct i2s_driver *sdrv)
+{
+ if (sdrv)
+ driver_unregister(&sdrv->driver);
+}
+
+/**I2S controller parameters*/
+
+enum i2s_direction_t {
+ I2S_DIRECTION_TX = 0,
+ I2S_DIRECTION_RX = 1,
+ I2S_DIRECTION_BOTH = 2
+};
+
+enum i2s_transfer_mode_t {
+ I2S_TRANSFER_MODE_SINGLE_DMA = 0,
+ I2S_TRANSFER_MODE_CYCLIC_DMA = 1,
+ I2S_TRANSFER_MODE_INF_LOOPBACK = 2,
+ I2S_TRANSFER_MODE_NON_DMA = 4,
+};
+
+struct i2s_message {
+ enum i2s_transfer_mode_t i2s_transfer_mode;
+ enum i2s_direction_t i2s_direction;
+ void *txdata;
+ void *rxdata;
+ size_t txbytes;
+ size_t rxbytes;
+ int dma_flag;
+ int tx_offset;
+ int rx_offset;
+ bool cyclic_dma;
+ struct scatterlist *sg;
+ int sg_len;
+};
+
+typedef enum {
+ DISABLE_ALL = 0,
+ DISABLE_TRANSMIT = 1,
+ DISABLE_RECEIVE = 2,
+} i2s_flag;
+
+struct i2s_algorithm {
+ int (*cont_setup) (struct i2s_controller *i2s_cont, void *config);
+ int (*cont_transfer) (struct i2s_controller *i2s_cont,
+ struct i2s_message *message);
+ int (*cont_cleanup) (struct i2s_controller *i2s_cont, i2s_flag flag);
+ int (*cont_hw_status) (struct i2s_controller *i2s_cont);
+ dma_addr_t (*cont_get_pointer) (struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction);
+};
+
+struct i2s_controller {
+ struct module *owner;
+ unsigned int id;
+ unsigned int class;
+ const struct i2s_algorithm *algo; /* the algorithm to access the bus */
+ void *data;
+ struct mutex bus_lock;
+ struct device dev; /* the controller device */
+ char name[48];
+};
+#define to_i2s_controller(d) container_of(d, struct i2s_controller, dev)
+
+static inline void *i2s_get_contdata(struct i2s_controller *dev)
+{
+ return dev_get_drvdata(&dev->dev);
+}
+
+static inline void i2s_set_contdata(struct i2s_controller *dev, void *data)
+{
+ dev_set_drvdata(&dev->dev, data);
+}
+
+extern int i2s_add_controller(struct i2s_controller *controller);
+extern int i2s_del_controller(struct i2s_controller *controller);
+extern int i2s_setup(struct i2s_controller *i2s_cont, void *config);
+extern int i2s_transfer(struct i2s_controller *i2s_cont,
+ struct i2s_message *message);
+extern int i2s_cleanup(struct i2s_controller *i2s_cont, i2s_flag flag);
+extern int i2s_hw_status(struct i2s_controller *i2s_cont);
+extern dma_addr_t i2s_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction);
+
+extern struct i2s_device *i2s_alloc_device(struct device *dev);
+
+extern int i2s_add_device(struct i2s_device *i2s);
+
+static inline void i2s_unregister_device(struct i2s_device *i2s)
+{
+ if (i2s)
+ device_unregister(&i2s->dev);
+}
+
+#endif /* __LINUX_I2S_H */
diff --git a/include/linux/i2s/i2s_test_prot.h b/include/linux/i2s/i2s_test_prot.h
new file mode 100644
index 00000000000..003d0e6e3ab
--- /dev/null
+++ b/include/linux/i2s/i2s_test_prot.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef __I2S_TEST_PROT_DRIVER_IF_H__
+#define __I2S_TEST_PROT_DRIVER_IF_H__
+#include <mach/msp.h>
+struct test_prot_config {
+ u32 tx_config_desc;
+ u32 rx_config_desc;
+ u32 frame_freq;
+ u32 frame_size;
+ u32 data_size;
+ u32 direction;
+ u32 protocol;
+ u32 work_mode;
+ struct msp_protocol_desc protocol_desc;
+ void (*handler) (void *data);
+ void *tx_callback_data;
+ void *rx_callback_data;
+ int multichannel_configured;
+ struct msp_multichannel_config multichannel_config;
+};
+
+int i2s_testprot_drv_open(int i2s_device_num);
+int i2s_testprot_drv_configure(int i2s_device_num,
+ struct test_prot_config *config);
+int i2s_config_default_protocol(int i2s_device_num,
+ struct test_prot_config *config);
+int __i2s_testprot_drv_configure(int i2s_device_num,
+ struct test_prot_config *config,
+ bool use_default);
+int i2s_testprot_drv_transfer(int i2s_device_num, void *txdata, size_t txbytes,
+ void *rxdata, size_t rxbytes,
+ enum i2s_transfer_mode_t transfer_mode);
+int i2s_testprot_drv_close(int i2s_device_num);
+
+#endif