From 89becb08b9dffd67d543c086254c53c1a98bb909 Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Wed, 7 Dec 2011 13:29:06 +0100 Subject: ASoC: Ux500: Move i2s into ASoC and remove old i2s-layer The old multi-client i2s driver-layer located in drivers/misc/i2s is removed and the MSP-operation for i2s is moved into the file ux500_msp_i2s.c in the Ux500 ASoC-folder. ST-Ericsson ID: - ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I11c9021bb7b2385afba9a3e658b5bef7fe9fdb68 Signed-off-by: Ola Lilja Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41704 --- arch/arm/mach-ux500/board-mop500-msp.c | 27 - arch/arm/mach-ux500/devices-common.h | 10 + arch/arm/mach-ux500/include/mach/msp.h | 73 +- drivers/misc/Makefile | 1 - drivers/misc/i2s/Kconfig | 19 - drivers/misc/i2s/Makefile | 7 - drivers/misc/i2s/i2s.c | 632 ---------- drivers/misc/i2s/msp_i2s.c | 2022 -------------------------------- include/linux/i2s/i2s.h | 228 ---- sound/soc/ux500/Kconfig | 2 +- sound/soc/ux500/Makefile | 35 +- sound/soc/ux500/u5500.c | 10 +- sound/soc/ux500/u8500.c | 12 +- sound/soc/ux500/ux500_msp_dai.c | 141 +-- sound/soc/ux500/ux500_msp_dai.h | 5 +- sound/soc/ux500/ux500_msp_i2s.c | 1013 ++++++++++++++++ sound/soc/ux500/ux500_msp_i2s.h | 41 + sound/soc/ux500/ux500_pcm.c | 2 +- 18 files changed, 1230 insertions(+), 3050 deletions(-) delete mode 100644 drivers/misc/i2s/Kconfig delete mode 100755 drivers/misc/i2s/Makefile delete mode 100755 drivers/misc/i2s/i2s.c delete mode 100644 drivers/misc/i2s/msp_i2s.c delete mode 100644 include/linux/i2s/i2s.h mode change 100755 => 100644 sound/soc/ux500/ux500_msp_dai.h create mode 100644 sound/soc/ux500/ux500_msp_i2s.c create mode 100644 sound/soc/ux500/ux500_msp_i2s.h diff --git a/arch/arm/mach-ux500/board-mop500-msp.c b/arch/arm/mach-ux500/board-mop500-msp.c index 35d8480143f..7a5a23baf87 100644 --- a/arch/arm/mach-ux500/board-mop500-msp.c +++ b/arch/arm/mach-ux500/board-mop500-msp.c @@ -5,7 +5,6 @@ */ #include -#include #include #include #include @@ -185,36 +184,10 @@ static struct msp_i2s_platform_data msp3_platform_data = { .msp_i2s_exit = msp13_i2s_exit, }; -static struct i2s_board_info stm_i2s_board_info[] __initdata = { - { - .modalias = "i2s_device.0", - .id = 0, - .chip_select = 0, - }, - { - .modalias = "i2s_device.1", - .id = 1, - .chip_select = 1, - }, - { - .modalias = "i2s_device.2", - .id = 2, - .chip_select = 2, - }, - { - .modalias = "i2s_device.3", - .id = 3, - .chip_select = 3, - }, -}; - void __init mop500_msp_init(void) { db8500_add_msp0_i2s(&msp0_platform_data); db8500_add_msp1_i2s(&msp1_platform_data); db8500_add_msp2_i2s(&msp2_platform_data); db8500_add_msp3_i2s(&msp3_platform_data); - - i2s_register_board_info(stm_i2s_board_info, - ARRAY_SIZE(stm_i2s_board_info)); } diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h index 39c74ec82ad..c9c88132a52 100644 --- a/arch/arm/mach-ux500/devices-common.h +++ b/arch/arm/mach-ux500/devices-common.h @@ -82,6 +82,16 @@ dbx500_add_i2c(struct device *parent, int id, resource_size_t base, int irq, return platform_device_register_full(&pdevinfo); } +struct msp_i2s_platform_data; + +static inline struct platform_device * +dbx500_add_msp_i2s(int id, resource_size_t base, int irq, + struct msp_i2s_platform_data *pdata) +{ + return dbx500_add_platform_device_4k1irq("ux500-msp-i2s", id, base, irq, + pdata); +} + static inline struct amba_device * dbx500_add_rtc(struct device *parent, resource_size_t base, int irq) { diff --git a/arch/arm/mach-ux500/include/mach/msp.h b/arch/arm/mach-ux500/include/mach/msp.h index 09f6a98a8fc..6f42cca48c6 100644 --- a/arch/arm/mach-ux500/include/mach/msp.h +++ b/arch/arm/mach-ux500/include/mach/msp.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -131,6 +130,53 @@ struct msp_protocol_desc { u32 total_clocks_for_one_frame; }; +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_direction_t i2s_direction; + void *txdata; + void *rxdata; + size_t txbytes; + size_t rxbytes; + int dma_flag; + int tx_offset; + int rx_offset; + /* cyclic dma */ + bool cyclic_dma; + dma_addr_t buf_addr; + size_t buf_len; + size_t period_len; +}; + +enum i2s_flag { + DISABLE_ALL = 0, + DISABLE_TRANSMIT = 1, + DISABLE_RECEIVE = 2, +}; + +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) + /** * struct trans_data - MSP transfer data structure used during xfer. * @message: i2s message. @@ -645,15 +691,19 @@ enum msp_expand_mode { #define TDMAE_SHIFT 1 /* Interrupt Register */ -#define RECEIVE_SERVICE_INT BIT(0) -#define RECEIVE_OVERRUN_ERROR_INT BIT(1) -#define RECEIVE_FRAME_SYNC_ERR_INT BIT(2) -#define RECEIVE_FRAME_SYNC_INT BIT(3) -#define TRANSMIT_SERVICE_INT BIT(4) -#define TRANSMIT_UNDERRUN_ERR_INT BIT(5) -#define TRANSMIT_FRAME_SYNC_ERR_INT BIT(6) -#define TRANSMIT_FRAME_SYNC_INT BIT(7) -#define ALL_INT 0x000000ff +#define RECEIVE_SERVICE_INT BIT(0) +#define RECEIVE_OVERRUN_ERROR_INT BIT(1) +#define RECEIVE_FRAME_SYNC_ERR_INT BIT(2) +#define RECEIVE_FRAME_SYNC_INT BIT(3) +#define TRANSMIT_SERVICE_INT BIT(4) +#define TRANSMIT_UNDERRUN_ERR_INT BIT(5) +#define TRANSMIT_FRAME_SYNC_ERR_INT BIT(6) +#define TRANSMIT_FRAME_SYNC_INT BIT(7) +#define ALL_INT 0x000000ff + +/* MSP test control register */ +#define MSP_ITCR_ITEN BIT(0) +#define MSP_ITCR_TESTFIFO BIT(1) /* * Protocol configuration values I2S: @@ -879,7 +929,7 @@ enum msp_expand_mode { #define MAX_MSP_BACKUP_REGS 36 enum enum_i2s_controller { - MSP_0_I2S_CONTROLLER = 1, + MSP_0_I2S_CONTROLLER = 0, MSP_1_I2S_CONTROLLER, MSP_2_I2S_CONTROLLER, MSP_3_I2S_CONTROLLER, @@ -923,6 +973,7 @@ struct msp { int msp_io_error; void __iomem *registers; enum msp_data_size actual_data_size; + struct device *dev; int irq; struct i2s_controller *i2s_cont; struct semaphore lock; diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 366ff7f0ff1..3e1d80106f0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -46,7 +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_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o diff --git a/drivers/misc/i2s/Kconfig b/drivers/misc/i2s/Kconfig deleted file mode 100644 index 569818caa5d..00000000000 --- a/drivers/misc/i2s/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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. diff --git a/drivers/misc/i2s/Makefile b/drivers/misc/i2s/Makefile deleted file mode 100755 index 75d361d5deb..00000000000 --- a/drivers/misc/i2s/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for I2S drivers -# - -nmdk_i2s-objs := i2s.o -obj-$(CONFIG_STM_I2S) += nmdk_i2s.o -obj-$(CONFIG_STM_MSP_I2S) += msp_i2s.o diff --git a/drivers/misc/i2s/i2s.c b/drivers/misc/i2s/i2s.c deleted file mode 100755 index 9d14088cebc..00000000000 --- a/drivers/misc/i2s/i2s.c +++ /dev/null @@ -1,632 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* 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 . */ -/*----------------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*******************************************************************************/ -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 board_i2s_combined_info { - struct i2s_board_info board_info; - struct i2s_device *i2s_dev_p; -}; -struct boardinfo { - struct list_head list; - unsigned n_board_info; - struct board_i2s_combined_info board_i2s_info[0]; -}; - -static LIST_HEAD(board_list); -static DEFINE_MUTEX(board_lock); - -/* - * Get an i2s device. Used in MSP LTP tests. - */ -struct i2s_device *i2s_get_device_from_boardinfo(int chip_select) -{ - struct boardinfo *bi; - struct i2s_device *i2s_dev_p = NULL; - - mutex_lock(&board_lock); - list_for_each_entry(bi, &board_list, list) { - struct board_i2s_combined_info *chip = bi->board_i2s_info; - unsigned n; - - for (n = bi->n_board_info; n > 0; n--, chip++) - if (chip->board_info.chip_select == chip_select) { - i2s_dev_p = chip->i2s_dev_p; - break; - } - if (i2s_dev_p != NULL) - break; - } - mutex_unlock(&board_lock); - - return i2s_dev_p; -} - -EXPORT_SYMBOL_GPL(i2s_get_device_from_boardinfo); - -/* 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) -{ - int i; - struct boardinfo *bi; - - bi = kmalloc(sizeof(*bi) + (n * sizeof(struct board_i2s_combined_info)), GFP_KERNEL); - if (!bi) - return -ENOMEM; - bi->n_board_info = n; - - for (i = 0; i < n; i++) - memcpy(&bi->board_i2s_info[i].board_info, &info[i], 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 board_i2s_combined_info *chip = bi->board_i2s_info; - unsigned n; - - for (n = bi->n_board_info; n > 0; n--, chip++) { - if (chip->board_info.chip_select != i2s_cont->id) - continue; - /* NOTE: this relies on i2s_new_device to - * issue diagnostics when given bogus inputs - */ - chip->i2s_dev_p = i2s_new_device(i2s_cont, &chip->board_info); - } - } - 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, "); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/i2s/msp_i2s.c b/drivers/misc/i2s/msp_i2s.c deleted file mode 100644 index f5e3e00b894..00000000000 --- a/drivers/misc/i2s/msp_i2s.c +++ /dev/null @@ -1,2022 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -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); - -#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, -}; - -/** - * 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); - - bit_clock = (config->input_clock_freq)/(sck_div + 1); - /* If the bit clock is higher than 19.2MHz, Vape should be run in 100% OPP */ - /* Only consider OPP 100% when bit-clock is used, i.e. MSP master mode */ - if ((bit_clock > 19200000) && ((config->tx_clock_sel != 0) || (config->rx_clock_sel != 0))) { - 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); - - /* Don't enable regulator if its msp1 or msp3 */ - if (!(msp->reg_enabled) && msp->id != MSP_1_I2S_CONTROLLER - && msp->id != MSP_3_I2S_CONTROLLER) { - 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; - } - msp->reg_enabled = 1; - } - - switch (msp->users) { - case 0: - 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; - - /* Flush MSP-FIFOs */ - flush_tx_fifo(msp); - flush_rx_fifo(msp); - - 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, - dma_addr_t buf_addr, - size_t buf_len, - size_t period_len, - enum dma_data_direction direction) -{ - int ret; - struct dma_async_tx_descriptor *cdesc; - struct dma_chan *pipeid = (direction == DMA_TO_DEVICE) ? - msp->tx_pipeid : - msp->rx_pipeid; - - pr_debug("%s: buf_addr = %p\n", __func__, (void *) buf_addr); - pr_debug("%s: buf_len = %d\n", __func__, buf_len); - pr_debug("%s: perios_len = %d\n", __func__, period_len); - - /* setup the cyclic description */ - cdesc = pipeid->device->device_prep_dma_cyclic(pipeid, - buf_addr, /* reuse the sq list for the moment */ - buf_len, - period_len, - direction); - - if (IS_ERR(cdesc)) { - pr_err("%s: Error: device_prep_dma_cyclic failed (%ld)!\n", - __func__, - PTR_ERR(cdesc)); - return; - } - - cdesc->callback = (direction == DMA_TO_DEVICE) ? - msp->xfer_data.tx_handler : - msp->xfer_data.rx_handler; - cdesc->callback_param = (direction == DMA_TO_DEVICE) ? - msp->xfer_data.tx_callback_data : - msp->xfer_data.rx_callback_data; - - /* submit to the dma */ - ret = dmaengine_submit(cdesc); - - /* start the dma */ - dma_async_issue_pending(pipeid); - - return; -} - -/* Legacy function. Used by HATS driver. */ -static void msp_loopback_inf_start_dma(struct msp *msp, - dma_addr_t data, - size_t bytes) -{ -#if 0 - 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); -#endif - return; -} - -/** - * 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_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); - } - 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); - } - break; - - case I2S_TRANSFER_MODE_CYCLIC_DMA: - if (msg->i2s_direction == I2S_DIRECTION_TX) { - msp_cyclic_dma_start(msp, - msg->buf_addr, - msg->buf_len, - msg->period_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->buf_addr, - msg->buf_len, - msg->period_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) { - dmaengine_terminate_all(msp->tx_pipeid); - dma_release_channel(msp->tx_pipeid); - msp->tx_pipeid = NULL; - } - } - if ((flag == DISABLE_ALL || flag == DISABLE_RECEIVE)) { - if (msp->rx_pipeid != NULL) { - dmaengine_terminate_all(msp->rx_pipeid); - dma_release_channel(msp->rx_pipeid); - msp->rx_pipeid = NULL; - } - } - } - 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); - - 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); - if (msp->reg_enabled) { - status = regulator_disable(msp_vape_supply); - msp->reg_enabled = 0; - } - 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; -} - - /*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/include/linux/i2s/i2s.h b/include/linux/i2s/i2s.h deleted file mode 100644 index 79df549d6bd..00000000000 --- a/include/linux/i2s/i2s.h +++ /dev/null @@ -1,228 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* 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 . */ -/*----------------------------------------------------------------------------*/ - -#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; - /* cyclic dma */ - bool cyclic_dma; - dma_addr_t buf_addr; - size_t buf_len; - size_t period_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_get_device_from_boardinfo(int chip_select); /* used in MSP LTP tests */ -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/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig index 0078beb72ec..1a25fb6a0d6 100644 --- a/sound/soc/ux500/Kconfig +++ b/sound/soc/ux500/Kconfig @@ -4,7 +4,7 @@ menuconfig SND_SOC_UX500 bool "SoC Audio support for Ux500 platform" - depends on SND_SOC && STM_I2S && STM_MSP_I2S + depends on SND_SOC default n help Say Y if you want to add support for the Ux500 platform. diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile index 255c0ecefdb..262e44a2812 100644 --- a/sound/soc/ux500/Makefile +++ b/sound/soc/ux500/Makefile @@ -1,47 +1,46 @@ # Ux500 Platform Support ifdef CONFIG_SND_SOC_UX500_DEBUG +CFLAGS_u8500.o := -DDEBUG CFLAGS_ux500_pcm.o := -DDEBUG CFLAGS_ux500_msp_dai.o := -DDEBUG CFLAGS_ux500_ab3550.o := -DDEBUG CFLAGS_ux500_ab8500.o := -DDEBUG CFLAGS_ux500_av8100.o := -DDEBUG CFLAGS_ux500_cg29xx.o := -DDEBUG +CFLAGS_ux500_msp_i2s.o := -DDEBUG endif -ifdef CONFIG_SND_SOC_UX500_AB3550 -snd-soc-ux500-ab3550-objs := ux500_ab3550.o -obj-$(CONFIG_SND_SOC_UX500_AB3550) += ux500_ab3550.o -endif - -ifdef CONFIG_SND_SOC_UX500_AB5500 -snd-soc-ux500-ab5500-objs := ux500_ab5500.o -obj-$(CONFIG_SND_SOC_UX500_AB5500) += ux500_ab5500.o +ifdef CONFIG_UX500_SOC_DBX500 +snd-soc-ux500-platform-objs := ux500_pcm.o ux500_msp_dai.o ux500_msp_i2s.o +obj-y += snd-soc-ux500-platform.o endif ifdef CONFIG_SND_SOC_UX500_AB8500 -snd-soc-ux500-ab8500-objs := ux500_ab8500.o -obj-$(CONFIG_SND_SOC_UX500_AB8500) += ux500_ab8500.o +snd-soc-ux500-machine-objs += ux500_ab8500.o endif ifdef CONFIG_SND_SOC_UX500_AV8100 -snd-soc-ux500-av8100-objs := ux500_av8100.o -obj-$(CONFIG_SND_SOC_UX500_AV8100) += ux500_av8100.o +snd-soc-ux500-machine-objs += ux500_av8100.o endif ifdef CONFIG_SND_SOC_UX500_CG29XX -snd-soc-ux500-cg29xx-objs := ux500_cg29xx.o -obj-$(CONFIG_SND_SOC_UX500_CG29XX) += ux500_cg29xx.o +snd-soc-ux500-machine-objs += ux500_cg29xx.o +endif + +ifdef CONFIG_SND_SOC_UX500_AB5500 +snd-soc-ux500-machine-objs += ux500_ab5500.o endif -snd-soc-ux500-objs := ux500_pcm.o ux500_msp_dai.o +obj-y += snd-soc-ux500-machine.o ifdef CONFIG_UX500_SOC_DB8500 -snd-soc-ux500-objs += u8500.o +snd-soc-u8500-objs := u8500.o +obj-y += snd-soc-u8500.o endif ifdef CONFIG_UX500_SOC_DB5500 -snd-soc-ux500-objs += u5500.o +snd-soc-u5500-objs := u5500.o +obj-y += snd-soc-u5500.o endif -obj-$(CONFIG_UX500_SOC_DBX500) += snd-soc-ux500.o diff --git a/sound/soc/ux500/u5500.c b/sound/soc/ux500/u5500.c index 1d17830c1c7..6787daa9de5 100755 --- a/sound/soc/ux500/u5500.c +++ b/sound/soc/ux500/u5500.c @@ -66,7 +66,7 @@ struct snd_soc_dai_link u5500_dai_links[] = { { .name = "ab5500_0", .stream_name = "ab5500_0", - .cpu_dai_name = "i2s.0", + .cpu_dai_name = "ux500-msp-i2s.0", .codec_dai_name = "ab5500-codec-dai.0", .platform_name = "ux500-pcm.0", .codec_name = "ab5500-codec.0", @@ -83,7 +83,7 @@ struct snd_soc_dai_link u5500_dai_links[] = { { .name = "cg29xx_0", .stream_name = "cg29xx_0", - .cpu_dai_name = "i2s.1", + .cpu_dai_name = "ux500-msp-i2s.1", .codec_dai_name = "cg29xx-codec-dai.0", .platform_name = "ux500-pcm.0", .codec_name = "cg29xx-codec.0", @@ -93,7 +93,7 @@ struct snd_soc_dai_link u5500_dai_links[] = { { .name = "cg29xx_1", .stream_name = "cg29xx_1", - .cpu_dai_name = "i2s.0", + .cpu_dai_name = "ux500-msp-i2s.0", .codec_dai_name = "cg29xx-codec-dai.1", .platform_name = "ux500-pcm.0", .codec_name = "cg29xx-codec.0", @@ -104,7 +104,7 @@ struct snd_soc_dai_link u5500_dai_links[] = { { .name = "ab5500_1", .stream_name = "ab5500_1", - .cpu_dai_name = "i2s.1", + .cpu_dai_name = "ux500-msp-i2s.1", .codec_dai_name = "ab5500-codec-dai.1", .platform_name = "ux500-pcm.0", .codec_name = "ab5500-codec.0", @@ -121,7 +121,7 @@ struct snd_soc_dai_link u5500_dai_links[] = { { .name = "hdmi", .stream_name = "hdmi", - .cpu_dai_name = "i2s.2", + .cpu_dai_name = "ux500-msp-i2s.2", .codec_dai_name = "av8100-codec-dai", .platform_name = "ux500-pcm.0", .codec_name = "av8100-codec.0", diff --git a/sound/soc/ux500/u8500.c b/sound/soc/ux500/u8500.c index c27409f1009..516008fcc6a 100644 --- a/sound/soc/ux500/u8500.c +++ b/sound/soc/ux500/u8500.c @@ -77,7 +77,7 @@ struct snd_soc_dai_link u8500_dai_links[] = { { .name = "hdmi", .stream_name = "hdmi", - .cpu_dai_name = "i2s.2", + .cpu_dai_name = "ux500-msp-i2s.2", .codec_dai_name = "av8100-codec-dai", .platform_name = "ux500-pcm.0", .codec_name = "av8100-codec.0", @@ -89,7 +89,7 @@ struct snd_soc_dai_link u8500_dai_links[] = { { .name = "ab3550_0", .stream_name = "ab3550_0", - .cpu_dai_name = "i2s.0", + .cpu_dai_name = "ux500-msp-i2s.0", .codec_dai_name = "ab3550-codec-dai.0", .platform_name = "ux500-pcm.0", .codec_name = "ab3550-codec.11", @@ -99,7 +99,7 @@ struct snd_soc_dai_link u8500_dai_links[] = { { .name = "ab3550_1", .stream_name = "ab3550_1", - .cpu_dai_name = "i2s.1", + .cpu_dai_name = "ux500-msp-i2s.1", .codec_dai_name = "ab3550-codec-dai.1", .platform_name = "ux500-pcm.0", .codec_name = "ab3550-codec.11", @@ -111,7 +111,7 @@ struct snd_soc_dai_link u8500_dai_links[] = { { .name = "ab8500_0", .stream_name = "ab8500_0", - .cpu_dai_name = "i2s.1", + .cpu_dai_name = "ux500-msp-i2s.1", .codec_dai_name = "ab8500-codec-dai.0", .platform_name = "ux500-pcm.0", .codec_name = "ab8500-codec.0", @@ -121,7 +121,7 @@ struct snd_soc_dai_link u8500_dai_links[] = { { .name = "ab8500_1", .stream_name = "ab8500_1", - .cpu_dai_name = "i2s.3", + .cpu_dai_name = "ux500-msp-i2s.3", .codec_dai_name = "ab8500-codec-dai.1", .platform_name = "ux500-pcm.0", .codec_name = "ab8500-codec.0", @@ -133,7 +133,7 @@ struct snd_soc_dai_link u8500_dai_links[] = { { .name = "cg29xx_0", .stream_name = "cg29xx_0", - .cpu_dai_name = "i2s.0", + .cpu_dai_name = "ux500-msp-i2s.0", .codec_dai_name = "cg29xx-codec-dai.1", .platform_name = "ux500-pcm.0", .codec_name = "cg29xx-codec.0", diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 6cdc96734ed..2af5bf14193 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson SA 2010 + * Copyright (C) ST-Ericsson SA 2011 * * Author: Ola Lilja , * Roger Nilsson @@ -14,21 +14,22 @@ #include #include -#include #include +#include + #include #include -#include -#include #include #include + +#include "ux500_msp_i2s.h" #include "ux500_msp_dai.h" #include "ux500_pcm.h" static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = { { - .i2s = NULL, + .msp_i2s_drvdata = NULL, .fmt = 0, .slots = 1, .tx_mask = 0x01, @@ -41,7 +42,7 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = { .master_clk = UX500_MSP_INTERNAL_CLOCK_FREQ, }, { - .i2s = NULL, + .msp_i2s_drvdata = NULL, .fmt = 0, .slots = 1, .tx_mask = 0x01, @@ -54,7 +55,7 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = { .master_clk = UX500_MSP1_INTERNAL_CLOCK_FREQ, }, { - .i2s = NULL, + .msp_i2s_drvdata = NULL, .fmt = 0, .slots = 1, .tx_mask = 0x01, @@ -67,7 +68,7 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = { .master_clk = UX500_MSP_INTERNAL_CLOCK_FREQ, }, { - .i2s = NULL, + .msp_i2s_drvdata = NULL, .fmt = 0, .slots = 1, .tx_mask = 0x01, @@ -84,14 +85,14 @@ static struct ux500_platform_drvdata platform_drvdata[UX500_NBR_OF_DAI] = { bool ux500_msp_dai_i2s_get_underrun_status(int dai_idx) { struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx]; - int status = i2s_hw_status(drvdata->i2s->controller); + int status = ux500_msp_i2s_hw_status(drvdata->msp_i2s_drvdata); return (bool)(status & TRANSMIT_UNDERRUN_ERR_INT); } dma_addr_t ux500_msp_dai_i2s_get_pointer(int dai_idx, int stream_id) { struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx]; - return i2s_get_pointer(drvdata->i2s->controller, + return ux500_msp_i2s_get_pointer(drvdata->msp_i2s_drvdata, (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ? I2S_DIRECTION_TX : I2S_DIRECTION_RX); @@ -105,7 +106,6 @@ int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr, { struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai_idx]; struct i2s_message message; - struct i2s_device *i2s_dev; int ret = 0; bool playback_req_valid = (drvdata->playback_active && @@ -128,9 +128,6 @@ int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr, return ret; } - i2s_dev = drvdata->i2s; - - message.i2s_transfer_mode = I2S_TRANSFER_MODE_CYCLIC_DMA; message.i2s_direction = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ? I2S_DIRECTION_TX : I2S_DIRECTION_RX; @@ -138,7 +135,7 @@ int ux500_msp_dai_i2s_configure_sg(dma_addr_t dma_addr, message.buf_len = period_cnt * period_len; message.period_len = period_len; - ret = i2s_transfer(i2s_dev->controller, &message); + ret = ux500_msp_i2s_transfer(drvdata->msp_i2s_drvdata, &message); if (ret < 0) { pr_err("%s: Error: i2s_transfer failed. MSP index: %d\n", __func__, @@ -197,7 +194,7 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, else drvdata->capture_active = false; - if (i2s_cleanup(drvdata->i2s->controller, + if (ux500_msp_i2s_close(drvdata->msp_i2s_drvdata, mode_playback ? DISABLE_TRANSMIT : DISABLE_RECEIVE)) { pr_err("%s: Error: MSP %d (%s): Unable to close i2s.\n", __func__, @@ -468,7 +465,7 @@ static void ux500_msp_dai_compile_msp_config(struct snd_pcm_substream *substream msp_config->work_mode = MSP_DMA_MODE; msp_config->frame_freq = rate; - printk(KERN_INFO "%s: input_clock_freq = %u, frame_freq = %u.\n", + pr_debug("%s: input_clock_freq = %u, frame_freq = %u.\n", __func__, msp_config->input_clock_freq, msp_config->frame_freq); /* To avoid division by zero in I2S-driver (i2s_setup) */ prot_desc->total_clocks_for_one_frame = 1; @@ -548,9 +545,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, runtime->rate, &msp_config); - ret = i2s_setup(drvdata->i2s->controller, &msp_config); + ret = ux500_msp_i2s_open(drvdata->msp_i2s_drvdata, &msp_config); if (ret < 0) { - pr_err("%s: Error: i2s_setup failed (ret = %d)!\n", __func__, ret); + pr_err("%s: Error: msp_setup failed (ret = %d)!\n", __func__, ret); goto cleanup; } @@ -764,11 +761,11 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, int ret = 0; struct ux500_platform_drvdata *drvdata = &platform_drvdata[dai->id]; - pr_debug("%s: MSP %d (%s): Enter (chip_select = %d, cmd = %d).\n", + pr_debug("%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n", __func__, dai->id, stream_str(substream), - (int)drvdata->i2s->chip_select, + (int)drvdata->msp_i2s_drvdata->id, cmd); switch (cmd) { @@ -796,7 +793,7 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { { - .name = "ux500-msp.0", + .name = "ux500-msp-i2s.0", .id = 0, .suspend = NULL, .resume = NULL, @@ -826,7 +823,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { }, }, { - .name = "ux500-msp.1", + .name = "ux500-msp-i2s.1", .id = 1, .suspend = NULL, .resume = NULL, @@ -856,7 +853,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { }, }, { - .name = "ux500-msp.2", + .name = "ux500-msp-i2s.2", .id = 2, .suspend = NULL, .resume = NULL, @@ -886,7 +883,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { }, }, { - .name = "ux500-msp.3", + .name = "ux500-msp-i2s.3", .id = 3, .suspend = NULL, .resume = NULL, @@ -918,84 +915,90 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { }; EXPORT_SYMBOL(ux500_msp_dai_drv); -static int ux500_msp_drv_probe(struct i2s_device *i2s_dev) +static int ux500_msp_drv_probe(struct platform_device *pdev) { - int ret = 0; + struct ux500_msp_i2s_drvdata *msp_i2s_drvdata; struct ux500_platform_drvdata *drvdata; - int msp_idx = i2s_dev->chip_select; + struct msp_i2s_platform_data *platform_data; + int id; + int ret = 0; - pr_info("%s: Enter (idx: %d, dev-name: %s, drv-name: %s).\n", - __func__, - msp_idx, - dev_name(&i2s_dev->dev), - i2s_dev->dev.driver->name); + pr_err("%s: Enter (pdev->name = %s).\n", __func__, pdev->name); - drvdata = &platform_drvdata[msp_idx]; - drvdata->i2s = i2s_dev; + platform_data = (struct msp_i2s_platform_data *)pdev->dev.platform_data; + msp_i2s_drvdata = ux500_msp_i2s_init(pdev, platform_data); + if (!msp_i2s_drvdata) { + pr_err("%s: ERROR: ux500_msp_i2s_init failed!", __func__); + return -EINVAL; + } - try_module_get(i2s_dev->controller->dev.parent->driver->owner); - i2s_set_drvdata(i2s_dev, drvdata); + id = msp_i2s_drvdata->id; + drvdata = &platform_drvdata[id]; + drvdata->msp_i2s_drvdata = msp_i2s_drvdata; - pr_debug("%s: Register MSP %d.\n", __func__, msp_idx); - ret = snd_soc_register_dai(&i2s_dev->dev, &ux500_msp_dai_drv[msp_idx]); + pr_info("%s: Registering ux500-msp-dai SoC CPU-DAI.\n", __func__); + ret = snd_soc_register_dai(&pdev->dev, &ux500_msp_dai_drv[id]); if (ret < 0) { - pr_err("Error: %s: Failed to register MSP %d.\n", __func__, msp_idx); + pr_err("Error: %s: Failed to register MSP %d.\n", __func__, id); return ret; } return ret; } -static int ux500_msp_drv_remove(struct i2s_device *i2s_dev) +static int ux500_msp_drv_remove(struct platform_device *pdev) { - struct ux500_platform_drvdata *drvdata = i2s_get_drvdata(i2s_dev); - int msp_idx = i2s_dev->chip_select; + struct ux500_msp_i2s_drvdata *msp_i2s_drvdata = dev_get_drvdata(&pdev->dev); + struct ux500_platform_drvdata *drvdata = &platform_drvdata[msp_i2s_drvdata->id]; - pr_info("%s: Enter (idx: %d, dev-name: %s, drv-name: %s).\n", - __func__, - msp_idx, - dev_name(&i2s_dev->dev), - i2s_dev->dev.driver->name); + pr_info("%s: Unregister ux500-msp-dai ASoC CPU-DAI.\n", __func__); + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv)); - drvdata->i2s = NULL; - i2s_set_drvdata(i2s_dev, NULL); + ux500_msp_i2s_exit(msp_i2s_drvdata); + drvdata->msp_i2s_drvdata = NULL; - pr_debug("%s: Calling module_put.\n", __func__); - module_put(i2s_dev->controller->dev.parent->driver->owner); + return 0; +} - pr_debug("%s: Unregister ux500-pcm SoC platform driver.\n", __func__); - snd_soc_unregister_dais(&i2s_dev->dev, ARRAY_SIZE(ux500_msp_dai_drv)); +int ux500_msp_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ux500_msp_i2s_drvdata *msp_i2s_drvdata = dev_get_drvdata(&pdev->dev); - return 0; + pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name); + + return ux500_msp_i2s_suspend(msp_i2s_drvdata); } -static const struct i2s_device_id dev_id_table[] = { - { "i2s_device.0", 0, 0 }, - { "i2s_device.1", 1, 0 }, - { "i2s_device.2", 2, 0 }, - { "i2s_device.3", 3, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2s, dev_id_table); +int ux500_msp_drv_resume(struct platform_device *pdev) +{ + struct ux500_msp_i2s_drvdata *msp_i2s_drvdata = dev_get_drvdata(&pdev->dev); + + pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name); + + return ux500_msp_i2s_resume(msp_i2s_drvdata); +} -static struct i2s_driver i2sdrv_i2s = { +static struct platform_driver msp_i2s_driver = { .driver = { - .name = "i2s", + .name = "ux500-msp-i2s", .owner = THIS_MODULE, }, .probe = ux500_msp_drv_probe, - .remove = __devexit_p(ux500_msp_drv_remove), - .id_table = dev_id_table, + .remove = ux500_msp_drv_remove, + .suspend = ux500_msp_drv_suspend, + .resume = ux500_msp_drv_resume, }; static int __init ux500_msp_init(void) { - return i2s_register_driver(&i2sdrv_i2s); + pr_info("%s: Register ux500-msp-dai platform driver.\n", __func__); + return platform_driver_register(&msp_i2s_driver); } static void __exit ux500_msp_exit(void) { - i2s_unregister_driver(&i2sdrv_i2s); + pr_info("%s: Unregister ux500-msp-dai platform driver.\n", __func__); + platform_driver_unregister(&msp_i2s_driver); } module_init(ux500_msp_init); diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h old mode 100755 new mode 100644 index cff6749760d..c44894526f2 --- a/sound/soc/ux500/ux500_msp_dai.h +++ b/sound/soc/ux500/ux500_msp_dai.h @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson SA 2010 + * Copyright (C) ST-Ericsson SA 2011 * * Author: Ola Lilja , * Roger Nilsson @@ -17,7 +17,6 @@ #include #include -#include #include #define UX500_NBR_OF_DAI 4 @@ -54,7 +53,7 @@ enum ux500_msp_clock_id { }; struct ux500_platform_drvdata { - struct i2s_device *i2s; + struct ux500_msp_i2s_drvdata *msp_i2s_drvdata; unsigned int fmt; unsigned int tx_mask; unsigned int rx_mask; diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c new file mode 100644 index 00000000000..1fa7a947fe1 --- /dev/null +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -0,0 +1,1013 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Ola Lilja , + * Sandeep Kaushik + * for ST-Ericsson. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ux500_msp_i2s.h" + + /* Protocol desciptors */ +static const struct msp_protocol_desc prot_descs[] = { + I2S_PROTOCOL_DESC, + PCM_PROTOCOL_DESC, + PCM_COMPAND_PROTOCOL_DESC, + AC97_PROTOCOL_DESC, + SPI_MASTER_PROTOCOL_DESC, + SPI_SLAVE_PROTOCOL_DESC, +}; + +static void ux500_msp_i2s_set_prot_desc_tx(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); + + writel(temp_reg, msp->registers + MSP_TCF); +} + +static void ux500_msp_i2s_set_prot_desc_rx(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); + + writel(temp_reg, msp->registers + MSP_RCF); + +} + +static int ux500_msp_i2s_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) { + pr_err("%s: ERROR: Invalid protocol!\n", __func__); + return -EINVAL; + } + protocol_desc = + (struct msp_protocol_desc *)&prot_descs[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) { + pr_err("%s: ERROR: Invalid data-size requested (data_size = %d)!\n", + __func__, data_size); + return -EINVAL; + } + + switch (direction) { + case MSP_TRANSMIT_MODE: + ux500_msp_i2s_set_prot_desc_tx(msp, protocol_desc, data_size); + break; + case MSP_RECEIVE_MODE: + ux500_msp_i2s_set_prot_desc_rx(msp, protocol_desc, data_size); + break; + case MSP_BOTH_T_R_MODE: + ux500_msp_i2s_set_prot_desc_tx(msp, protocol_desc, data_size); + ux500_msp_i2s_set_prot_desc_rx(msp, protocol_desc, data_size); + break; + default: + pr_err("%s: ERROR: Invalid direction requested (direction = %d)!\n", + __func__, direction); + return -EINVAL; + } + + /* The below code is needed for both Rx and Tx path. Can't separate them. */ + temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING; + temp_reg |= MSP_TX_CLKPOL_BIT(~protocol_desc->tx_clock_pol); + writel(temp_reg, msp->registers + MSP_GCR); + temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING; + temp_reg |= MSP_RX_CLKPOL_BIT(protocol_desc->rx_clock_pol); + writel(temp_reg, msp->registers + MSP_GCR); + + return 0; +} + +static int ux500_msp_i2s_configure_clock(struct msp *msp, struct msp_config *config) +{ + u32 reg_val_GCR; + 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; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR); + + if (config->default_protocol_desc) + protocol_desc = + (struct msp_protocol_desc *)&prot_descs[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 */ + pr_err("%s: ERROR: AC97 protocol not supported!\n", __func__); + return -ENOSYS; + default: + pr_err("%s: ERROR: Unknown protocol (%d)!\n", + __func__, + config->protocol); + return -EINVAL; + } + + temp_reg = (sck_div - 1) & SCK_DIV_MASK; + temp_reg |= FRAME_WIDTH_BITS(frame_width); + temp_reg |= FRAME_PERIOD_BITS(frame_per); + writel(temp_reg, msp->registers + MSP_SRG); + + bit_clock = (config->input_clock_freq)/(sck_div + 1); + /* If the bit clock is higher than 19.2MHz, Vape should be run in 100% OPP + * Only consider OPP 100% when bit-clock is used, i.e. MSP master mode + */ + if ((bit_clock > 19200000) && ((config->tx_clock_sel != 0) || (config->rx_clock_sel != 0))) { + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 100); + msp->vape_opp_constraint = 1; + } else { + prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 50); + msp->vape_opp_constraint = 0; + } + + /* Enable clock */ + udelay(100); + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR); + udelay(100); + + return 0; +} + +static int ux500_msp_i2s_configure_multichannel(struct msp *msp, struct msp_config *config) +{ + struct msp_protocol_desc *protocol_desc; + struct msp_multichannel_config *mcfg; + u32 reg_val_MCR; + + if (config->default_protocol_desc == 1) { + if (config->protocol >= MSP_INVALID_PROTOCOL) { + pr_err("%s: ERROR: Invalid protocol (%d)!\n", + __func__, + config->protocol); + return -EINVAL; + } + protocol_desc = (struct msp_protocol_desc *) + &prot_descs[config->protocol]; + } else { + protocol_desc = (struct msp_protocol_desc *)&config->protocol_desc; + } + + mcfg = &config->multichannel_config; + if (mcfg->tx_multichannel_enable) { + if (protocol_desc->tx_phase_mode == MSP_SINGLE_PHASE) { + reg_val_MCR = readl(msp->registers + MSP_MCR); + writel(reg_val_MCR | + (mcfg->tx_multichannel_enable ? 1 << TMCEN_BIT : 0), + msp->registers + MSP_MCR); + writel(mcfg->tx_channel_0_enable, + msp->registers + MSP_TCE0); + writel(mcfg->tx_channel_1_enable, + msp->registers + MSP_TCE1); + writel(mcfg->tx_channel_2_enable, + msp->registers + MSP_TCE2); + writel(mcfg->tx_channel_3_enable, + msp->registers + MSP_TCE3); + } else { + pr_err("%s: ERROR: Only single-phase supported (TX-mode: %d)!\n", + __func__, protocol_desc->tx_phase_mode); + return -EINVAL; + } + } + if (mcfg->rx_multichannel_enable) { + if (protocol_desc->rx_phase_mode == MSP_SINGLE_PHASE) { + reg_val_MCR = readl(msp->registers + MSP_MCR); + writel(reg_val_MCR | + (mcfg->rx_multichannel_enable ? 1 << RMCEN_BIT : 0), + msp->registers + MSP_MCR); + writel(mcfg->rx_channel_0_enable, + msp->registers + MSP_RCE0); + writel(mcfg->rx_channel_1_enable, + msp->registers + MSP_RCE1); + writel(mcfg->rx_channel_2_enable, + msp->registers + MSP_RCE2); + writel(mcfg->rx_channel_3_enable, + msp->registers + MSP_RCE3); + } else { + pr_err("%s: ERROR: Only single-phase supported (RX-mode: %d)!\n", + __func__, protocol_desc->rx_phase_mode); + return -EINVAL; + } + if (mcfg->rx_comparison_enable_mode) { + reg_val_MCR = readl(msp->registers + MSP_MCR); + writel(reg_val_MCR | + (mcfg->rx_comparison_enable_mode << RCMPM_BIT), + msp->registers + MSP_MCR); + + writel(mcfg->comparison_mask, + msp->registers + MSP_RCM); + writel(mcfg->comparison_value, + msp->registers + MSP_RCV); + + } + } + + return 0; +} + +void ux500_msp_i2s_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; + u16 word_width; + bool rx_active, tx_active; + + if (msp->tx_pipeid != NULL) { + dma_release_channel(msp->tx_pipeid); + msp->tx_pipeid = NULL; + } + + switch (config->data_size) { + case MSP_DATA_BITS_32: + word_width = STEDMA40_WORD_WIDTH; + break; + case MSP_DATA_BITS_16: + word_width = STEDMA40_HALFWORD_WIDTH; + break; + case MSP_DATA_BITS_8: + word_width = STEDMA40_BYTE_WIDTH; + break; + default: + word_width = STEDMA40_WORD_WIDTH; + pr_warn("%s: Unknown data-size (%d)! Assuming 32 bits.\n", + __func__, config->data_size); + } + + rx_active = (config->direction == MSP_RECEIVE_MODE || + config->direction == MSP_BOTH_T_R_MODE); + tx_active = (config->direction == MSP_TRANSMIT_MODE || + config->direction == MSP_BOTH_T_R_MODE); + + if (rx_active) { + rx_dma_info->src_info.data_width = word_width; + rx_dma_info->dst_info.data_width = word_width; + } + if (tx_active) { + tx_dma_info->src_info.data_width = word_width; + tx_dma_info->dst_info.data_width = word_width; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + if (rx_active) + msp->rx_pipeid = dma_request_channel(mask, stedma40_filter, rx_dma_info); + + if (tx_active) + msp->tx_pipeid = dma_request_channel(mask, stedma40_filter, tx_dma_info); +} + +static int ux500_msp_i2s_dma_xfer(struct msp *msp, struct i2s_message *msg) +{ + dma_cookie_t status_submit; + int direction, enable_bit; + u32 reg_val_GCR; + struct dma_chan *pipeid; + struct dma_async_tx_descriptor *cdesc; + + if (msg->i2s_direction == I2S_DIRECTION_TX) { + direction = DMA_TO_DEVICE; + pipeid = msp->tx_pipeid; + enable_bit = TX_ENABLE; + pr_debug("%s: Direction: TX\n", __func__); + } else { + direction = DMA_FROM_DEVICE; + pipeid = msp->rx_pipeid; + enable_bit = RX_ENABLE; + pr_debug("%s: Direction: RX\n", __func__); + } + + pr_debug("%s: msg->buf_addr = %p\n", __func__, (void *)msg->buf_addr); + pr_debug("%s: buf_len = %d\n", __func__, msg->buf_len); + pr_debug("%s: perios_len = %d\n", __func__, msg->period_len); + + /* setup the cyclic description */ + cdesc = pipeid->device->device_prep_dma_cyclic(pipeid, + msg->buf_addr, + msg->buf_len, + msg->period_len, + direction); + if (IS_ERR(cdesc)) { + pr_err("%s: ERROR: device_prep_dma_cyclic failed (%ld)!\n", + __func__, + PTR_ERR(cdesc)); + return -EINVAL; + } + + /* Submit to the dma */ + if (msg->i2s_direction == I2S_DIRECTION_TX) { + cdesc->callback = msp->xfer_data.tx_handler; + cdesc->callback_param = msp->xfer_data.tx_callback_data; + } else { + cdesc->callback = msp->xfer_data.rx_handler; + cdesc->callback_param = msp->xfer_data.rx_callback_data; + } + status_submit = dmaengine_submit(cdesc); + if (dma_submit_error(status_submit)) { + pr_err("%s: ERROR: dmaengine_submit failed!\n", __func__); + return -EINVAL; + } + + /* Start the dma */ + dma_async_issue_pending(pipeid); + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR); + + return 0; +} + +static int ux500_msp_i2s_enable(struct msp *msp, struct msp_config *config) +{ + int status = 0; + u32 reg_val_DMACR, reg_val_GCR; + + if (config->work_mode != MSP_DMA_MODE) { + pr_err("%s: ERROR: Only DMA-mode is supported (msp->work_mode = %d)\n", + __func__, + msp->work_mode); + return -EINVAL; + } + msp->work_mode = config->work_mode; + + /* Check msp state whether in RUN or CONFIGURED Mode */ + if (msp->msp_state == MSP_STATE_IDLE) { + if (msp->plat_init) { + status = msp->plat_init(); + if (status) { + pr_err("%s: ERROR: Failed to init MSP (%d)!\n", + __func__, + status); + return status; + } + } + } + + /* Configure msp with protocol dependent settings */ + ux500_msp_i2s_configure_protocol(msp, config); + ux500_msp_i2s_configure_clock(msp, config); + if (config->multichannel_configured == 1) { + status = ux500_msp_i2s_configure_multichannel(msp, config); + if (status) + pr_warn("%s: WARN: ux500_msp_i2s_configure_multichannel failed (%d)!\n", + __func__, status); + } + + /* Make sure the correct DMA-directions are configured */ + if ((config->direction == MSP_RECEIVE_MODE) || + (config->direction == MSP_BOTH_T_R_MODE)) + if (!msp->dma_cfg_rx) { + pr_err("%s: ERROR: MSP RX-mode is not configured!", __func__); + return -EINVAL; + } + if ((config->direction == MSP_TRANSMIT_MODE) || + (config->direction == MSP_BOTH_T_R_MODE)) + if (!msp->dma_cfg_tx) { + pr_err("%s: ERROR: MSP TX-mode is not configured!", __func__); + return -EINVAL; + } + + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + switch (config->direction) { + case MSP_TRANSMIT_MODE: + writel(reg_val_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; + + break; + case MSP_RECEIVE_MODE: + writel(reg_val_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; + + break; + case MSP_BOTH_T_R_MODE: + writel(reg_val_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; + + break; + default: + pr_err("%s: ERROR: Illegal MSP direction (config->direction = %d)!", + __func__, + config->direction); + if (msp->plat_exit) + msp->plat_exit(); + return -EINVAL; + } + ux500_msp_i2s_configure_dma(msp, config); + + msp->transfer = ux500_msp_i2s_dma_xfer; + + writel(config->iodelay, msp->registers + MSP_IODLY); + + /* Enable frame generation logic */ + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR); + + return status; +} + +static void flush_fifo_rx(struct msp *msp) +{ + u32 reg_val_DR, reg_val_GCR, reg_val_FLR; + u32 limit = 32; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR); + + reg_val_FLR = readl(msp->registers + MSP_FLR); + while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) { + reg_val_DR = readl(msp->registers + MSP_DR); + reg_val_FLR = readl(msp->registers + MSP_FLR); + } + + writel(reg_val_GCR, msp->registers + MSP_GCR); +} + +static void flush_fifo_tx(struct msp *msp) +{ + u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR; + u32 limit = 32; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR); + writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR); + + reg_val_FLR = readl(msp->registers + MSP_FLR); + while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) { + reg_val_TSTDR = readl(msp->registers + MSP_TSTDR); + reg_val_FLR = readl(msp->registers + MSP_FLR); + } + writel(0x0, msp->registers + MSP_ITCR); + writel(reg_val_GCR, msp->registers + MSP_GCR); +} + +int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config *msp_config) +{ + struct msp *msp = drvdata->msp; + u32 old_reg, new_reg, mask; + int res; + + if (in_interrupt()) { + pr_err("%s: ERROR: Open called in interrupt context!\n", __func__); + return -1; + } + + /* Two simultanous configuring msp is avoidable */ + down(&msp->lock); + + /* Don't enable regulator if its msp1 or msp3 */ + if (!(msp->reg_enabled) && msp->id != MSP_1_I2S_CONTROLLER + && msp->id != MSP_3_I2S_CONTROLLER) { + res = regulator_enable(drvdata->reg_vape); + if (res != 0) { + pr_err("%s: Failed to enable regulator!\n", __func__); + up(&msp->lock); + return res; + } + msp->reg_enabled = 1; + } + + switch (msp->users) { + case 0: + clk_enable(msp->clk); + msp->direction = msp_config->direction; + break; + case 1: + if (msp->direction == MSP_BOTH_T_R_MODE || + msp_config->direction == msp->direction || + msp_config->direction == MSP_BOTH_T_R_MODE) { + pr_warn("%s: WARN: MSP is in use (direction = %d)!\n", + __func__, msp_config->direction); + up(&msp->lock); + return -EBUSY; + } + msp->direction = MSP_BOTH_T_R_MODE; + break; + default: + pr_warn("%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 = (msp_config->tx_clock_sel | msp_config->rx_clock_sel | + msp_config->rx_frame_sync_pol | msp_config->tx_frame_sync_pol | + msp_config->rx_frame_sync_sel | msp_config->tx_frame_sync_sel | + msp_config->rx_fifo_config | msp_config->tx_fifo_config | + msp_config->srg_clock_sel | msp_config->loopback_enable | + msp_config->tx_data_enable); + + old_reg = readl(msp->registers + MSP_GCR); + old_reg &= ~mask; + new_reg |= old_reg; + writel(new_reg, msp->registers + MSP_GCR); + + if (ux500_msp_i2s_enable(msp, msp_config) != 0) { + pr_err("%s: ERROR: ux500_msp_i2s_enable failed!\n", __func__); + return -EBUSY; + } + if (msp_config->loopback_enable & 0x80) + msp->loopback_enable = 1; + + /* Flush FIFOs */ + flush_fifo_tx(msp); + flush_fifo_rx(msp); + + msp->msp_state = MSP_STATE_CONFIGURED; + up(&msp->lock); + return 0; +} + +static void func_notify_timer(unsigned long data) +{ + struct msp *msp = (struct msp *)data; + if (msp->polling_flag) { + msp->msp_io_error = 1; + pr_err("%s: ERROR: Polling timeout!\n", __func__); + del_timer(&msp->notify_timer); + } +} + +int ux500_msp_i2s_transfer(struct ux500_msp_i2s_drvdata *drvdata, struct i2s_message *message) +{ + struct msp *msp = drvdata->msp; + int status = 0; + + if (!message || (msp->msp_state == MSP_STATE_IDLE)) { + pr_err("%s: ERROR: i2s_message == NULL!\n", __func__); + return -EINVAL; + } + if (msp->msp_state == MSP_STATE_IDLE) { + pr_err("%s: ERROR: MSP in idle-state!\n", __func__); + 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; +} + +static void ux500_msp_i2s_disable_rx(struct msp *msp) +{ + u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR); + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR); + reg_val_IMSC = readl(msp->registers + MSP_IMSC); + writel(reg_val_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; +} + +static void ux500_msp_i2s_disable_tx(struct msp *msp) +{ + u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR); + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR); + reg_val_IMSC = readl(msp->registers + MSP_IMSC); + writel(reg_val_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; +} + +static int ux500_msp_i2s_disable(struct msp *msp, int direction, enum i2s_flag flag) +{ + u32 reg_val_GCR; + int status = 0; + + reg_val_GCR = readl(msp->registers + MSP_GCR); + if (!(reg_val_GCR & (TX_ENABLE | RX_ENABLE))) + return 0; + + if (flag == DISABLE_ALL || flag == DISABLE_TRANSMIT) { + if (msp->tx_pipeid != NULL) { + dmaengine_terminate_all(msp->tx_pipeid); + dma_release_channel(msp->tx_pipeid); + msp->tx_pipeid = NULL; + } + } + if ((flag == DISABLE_ALL || flag == DISABLE_RECEIVE)) { + if (msp->rx_pipeid != NULL) { + dmaengine_terminate_all(msp->rx_pipeid); + dma_release_channel(msp->rx_pipeid); + msp->rx_pipeid = NULL; + } + } + + if (flag == DISABLE_TRANSMIT) + ux500_msp_i2s_disable_tx(msp); + else if (flag == DISABLE_RECEIVE) + ux500_msp_i2s_disable_rx(msp); + else { + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR | LOOPBACK_MASK, + msp->registers + MSP_GCR); + + /* Flush TX-FIFO */ + flush_fifo_tx(msp); + + /* Disable TX-channel */ + writel((readl(msp->registers + MSP_GCR) & + (~TX_ENABLE)), msp->registers + MSP_GCR); + + /* Flush RX-FIFO */ + flush_fifo_rx(msp); + + /* Disable Loopback and Receive channel */ + writel((readl(msp->registers + MSP_GCR) & + (~(RX_ENABLE | LOOPBACK_MASK))), + msp->registers + MSP_GCR); + + ux500_msp_i2s_disable_tx(msp); + ux500_msp_i2s_disable_rx(msp); + + } + + /* disable sample rate and frame generators */ + if (flag == DISABLE_ALL) { + msp->msp_state = MSP_STATE_IDLE; + writel((readl(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) + pr_warn("%s: WARN: ux500_msp_i2s_exit failed (%d)!\n", + __func__, status); + msp->transfer = NULL; + writel(0, msp->registers + MSP_GCR); + writel(0, msp->registers + MSP_TCF); + writel(0, msp->registers + MSP_RCF); + writel(0, msp->registers + MSP_DMACR); + writel(0, msp->registers + MSP_SRG); + writel(0, msp->registers + MSP_MCR); + writel(0, msp->registers + MSP_RCM); + writel(0, msp->registers + MSP_RCV); + writel(0, msp->registers + MSP_TCE0); + writel(0, msp->registers + MSP_TCE1); + writel(0, msp->registers + MSP_TCE2); + writel(0, msp->registers + MSP_TCE3); + writel(0, msp->registers + MSP_RCE0); + writel(0, msp->registers + MSP_RCE1); + writel(0, msp->registers + MSP_RCE2); + writel(0, msp->registers + MSP_RCE3); + } + + return status; +} + +int ux500_msp_i2s_close(struct ux500_msp_i2s_drvdata *drvdata, enum i2s_flag flag) +{ + struct msp *msp = drvdata->msp; + int status = 0; + + pr_debug("%s: Enter.\n", __func__); + + down(&msp->lock); + + if (msp->users == 0) { + pr_err("%s: ERROR: MSP already closed!\n", __func__); + status = -EINVAL; + goto end; + } + pr_debug("%s: msp->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 = ux500_msp_i2s_disable(msp, MSP_BOTH_T_R_MODE, flag); + else { + status = ux500_msp_i2s_disable(msp, MSP_BOTH_T_R_MODE, DISABLE_ALL); + clk_disable(msp->clk); + if (msp->reg_enabled) { + status = regulator_disable(drvdata->reg_vape); + msp->reg_enabled = 0; + } + if (status != 0) { + pr_err("%s: ERROR: Failed to disable regulator (%d)!\n", + __func__, status); + 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, "ux500_msp_i2s", 50); + msp->vape_opp_constraint = 0; + } +end: + up(&msp->lock); + return status; + +} + +int ux500_msp_i2s_hw_status(struct ux500_msp_i2s_drvdata *drvdata) +{ + struct msp *msp = drvdata->msp; + int status; + + pr_debug("%s: Enter.\n", __func__); + + status = readl(msp->registers + MSP_RIS) & 0xee; + if (status) + writel(status, msp->registers + MSP_ICR); + + return status; +} + +dma_addr_t ux500_msp_i2s_get_pointer(struct ux500_msp_i2s_drvdata *drvdata, + enum i2s_direction_t i2s_direction) +{ + struct msp *msp = drvdata->msp; + + pr_debug("%s: Enter.\n", __func__); + + return (i2s_direction == I2S_DIRECTION_TX) ? + stedma40_get_src_addr(msp->tx_pipeid) : + stedma40_get_dst_addr(msp->rx_pipeid); +} + +struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev, + struct msp_i2s_platform_data *platform_data) +{ + struct ux500_msp_i2s_drvdata *msp_i2s_drvdata; + int irq; + struct resource *res = NULL; + struct i2s_controller *i2s_cont; + struct msp *msp; + + pr_debug("%s: Enter (pdev->name = %s).\n", __func__, pdev->name); + + msp_i2s_drvdata = kzalloc(sizeof(struct ux500_msp_i2s_drvdata), GFP_KERNEL); + msp_i2s_drvdata->msp = kzalloc(sizeof(struct msp), GFP_KERNEL); + msp = msp_i2s_drvdata->msp; + + msp->id = platform_data->id; + msp_i2s_drvdata->id = msp->id; + pr_debug("msp_i2s_drvdata->id = %d\n", msp_i2s_drvdata->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; + + sema_init(&msp->lock, 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + pr_err("%s: ERROR: Unable to get resource!\n", __func__); + goto free_msp; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto free_msp; + msp->irq = irq; + + msp->registers = ioremap(res->start, (res->end - res->start + 1)); + if (msp->registers == NULL) + goto free_msp; + + msp_i2s_drvdata->reg_vape = regulator_get(NULL, "v-ape"); + if (IS_ERR(msp_i2s_drvdata->reg_vape)) { + pr_err("%s: ERROR: Failed to get Vape supply (%d)!\n", + __func__, (int)PTR_ERR(msp_i2s_drvdata->reg_vape)); + goto free_irq; + } + dev_set_drvdata(&pdev->dev, msp_i2s_drvdata); + + prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); + msp->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(msp->clk)) { + pr_err("%s: ERROR: clk_get failed (%d)!\n", + __func__, (int)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; + + /* I2S Controller is allocated and added in I2S controller class. */ + i2s_cont = kzalloc(sizeof(*i2s_cont), GFP_KERNEL); + if (!i2s_cont) { + pr_err("%s: ERROR: Failed to allocate struct i2s_cont (kzalloc)!\n", + __func__); + goto del_timer; + } + i2s_cont->dev.parent = &pdev->dev; + i2s_cont->data = (void *)msp; + i2s_cont->id = (s16)msp->id; + snprintf(i2s_cont->name, + sizeof(i2s_cont->name), + "ux500-msp-i2s.%04x", + msp->id); + pr_debug("I2S device-name :%s\n", i2s_cont->name); + msp->i2s_cont = i2s_cont; + + return msp_i2s_drvdata; + +del_timer: + del_timer_sync(&msp->notify_timer); + clk_put(msp->clk); +free_irq: + iounmap(msp->registers); +free_msp: + kfree(msp); + return NULL; +} + +int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata) +{ + struct msp *msp = drvdata->msp; + int status = 0; + + pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id); + + device_unregister(&msp->i2s_cont->dev); + del_timer_sync(&msp->notify_timer); + clk_put(msp->clk); + iounmap(msp->registers); + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); + regulator_put(drvdata->reg_vape); + kfree(msp); + + return status; +} + +int ux500_msp_i2s_suspend(struct ux500_msp_i2s_drvdata *drvdata) +{ + struct msp *msp = drvdata->msp; + + pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id); + + down(&msp->lock); + if (msp->users > 0) { + up(&msp->lock); + return -EBUSY; + } + up(&msp->lock); + + return 0; +} + +int ux500_msp_i2s_resume(struct ux500_msp_i2s_drvdata *drvdata) +{ + pr_debug("%s: Enter (drvdata->id = %d).\n", __func__, drvdata->id); + + return 0; +} + +MODULE_LICENSE("GPLv2"); diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h new file mode 100644 index 00000000000..db88d0ca5de --- /dev/null +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Ola Lilja , + * for ST-Ericsson. + * + * 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 UX500_MSP_I2S_H +#define UX500_MSP_I2S_H + +#include +#include + +struct ux500_msp_i2s_drvdata { + int id; + struct msp *msp; + struct regulator *reg_vape; +}; + +struct ux500_msp_i2s_drvdata *ux500_msp_i2s_init(struct platform_device *pdev, + struct msp_i2s_platform_data *platform_data); +int ux500_msp_i2s_exit(struct ux500_msp_i2s_drvdata *drvdata); +int ux500_msp_i2s_open(struct ux500_msp_i2s_drvdata *drvdata, struct msp_config *msp_config); +int ux500_msp_i2s_close(struct ux500_msp_i2s_drvdata *drvdata, enum i2s_flag flag); +int ux500_msp_i2s_transfer(struct ux500_msp_i2s_drvdata *drvdata, struct i2s_message *message); +int ux500_msp_i2s_hw_status(struct ux500_msp_i2s_drvdata *drvdata); +dma_addr_t ux500_msp_i2s_get_pointer(struct ux500_msp_i2s_drvdata *drvdata, + enum i2s_direction_t i2s_direction); + +int ux500_msp_i2s_suspend(struct ux500_msp_i2s_drvdata *drvdata); +int ux500_msp_i2s_resume(struct ux500_msp_i2s_drvdata *drvdata); + +#endif + diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 0161ffdf3f4..29b3f5e0ffb 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -272,7 +272,7 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd) private->msp_id, stream_id); if (ret) { - pr_err("%s: Failed to configure sg-list!\n", __func__); + pr_err("%s: Failed to configure I2S!\n", __func__); return -EINVAL; } break; -- cgit v1.2.3