diff options
author | Philippe Langlais <philippe.langlais@stericsson.com> | 2011-10-20 10:16:38 +0200 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2012-01-05 10:13:04 +0000 |
commit | 72e67c335af7fc745176e677d0f73d349ec20d8c (patch) | |
tree | 7068ff15e8337b0f6244586840cd2362778ebf79 /drivers/misc | |
parent | 9fef6c966353fc6289ad43204ead4662cf84c569 (diff) |
drivers: misc: Modem Audio Driver
Mad is a Character Device Driver to read and write
from special shared memory between APE and ACC side.
ST-Ericsson Linux next: Not tested, ER 332152
ST-Ericsson ID: 332152
ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10006
Change-Id: I087f6eb799f87ebb9ed38b4c011da5f53803b161
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26545
Reviewed-by: Amaresh MULAGE <amaresh.mulage@stericsson.com>
Tested-by: Amaresh MULAGE <amaresh.mulage@stericsson.com>
Reviewed-by: QATEST
Reviewed-by: Henrik CARLING <henrik.carling@stericsson.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/modem_audio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/misc/modem_audio/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/modem_audio/mad.c | 486 |
3 files changed, 494 insertions, 0 deletions
diff --git a/drivers/misc/modem_audio/Kconfig b/drivers/misc/modem_audio/Kconfig new file mode 100644 index 00000000000..5396868a9de --- /dev/null +++ b/drivers/misc/modem_audio/Kconfig @@ -0,0 +1,6 @@ +config MODEM_AUDIO_DRIVER + bool "Modem Audio Driver" + depends on (U5500_MBOX && UX500_SOC_DB5500) + help + This module is used for read and write data between APE and + Access side in u5500 platform. diff --git a/drivers/misc/modem_audio/Makefile b/drivers/misc/modem_audio/Makefile new file mode 100644 index 00000000000..a5c1740ea48 --- /dev/null +++ b/drivers/misc/modem_audio/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MODEM_AUDIO_DRIVER) += mad.o + diff --git a/drivers/misc/modem_audio/mad.c b/drivers/misc/modem_audio/mad.c new file mode 100644 index 00000000000..e9284b66dab --- /dev/null +++ b/drivers/misc/modem_audio/mad.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) ST-Ericsson AB 2011 + * + * Modem Audio Driver + * + * Author:Rahul Venkatram <rahul.venkatram@stericsson.com> for ST-Ericsson + * Haridhar KALVALA<haridhar.kalvala@stericsson.com> for ST-Ericsson + * Amaresh Mulage<amaresh.mulage@stericsson.com> for ST-Ericsson. + * + * License terms:GNU General Public License (GPLv2)version 2 + */ + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/sched.h> +#include <linux/poll.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/fcntl.h> +#include <linux/spinlock.h> +#include <mach/mbox_channels-db5500.h> + +MODULE_DESCRIPTION("Modem Audio Driver"); +MODULE_LICENSE("GPLv2"); + +/** + * ----------------------------------------------------- + * | | | | + * | Data[0] |Data[1] |Data[2] |===>Data word 32 bits + * ----------------------------------------------------- + * | MESSAGE |Data | Index | + * | TYPE |length | number |===>READ/WRITE message + * ----------------------------------------------------- + * ----------------------------------------------------- + * | MESSAGE | DSP SHM addr | max_no_of_buffers |===> READ + * | TYPE | to write data | ||buffersize |WRITE SETUP message + * ----------------------------------------------------- + */ + + +#define MAD_NAME "mad" +/* Bit mask */ +#define MASK_UPPER_WORD 0xFFFF + +/* channel values for each direction */ +#define CHANNEL_NUM_RX 0x500 +#define CHANNEL_NUM_TX 0x900 + +/* + * Maximum number of datawords which can be sent + * in the mailbox each word is 32 bits + */ +#define MAX_NR_OF_DATAWORDS 3 +#define MAX_NUM_RX_BUFF 4 +#define NR_OF_DATAWORDS_REQD_FOR_ACK 1 + +/** + * Message types, must be identical in DSP Side + * VCS_MBOX_MSG_WRITE_IF_SETUP : DSP -> ARM + * VCS_MBOX_MSG_WRITE_IF_SETUP_ACK : ARM -> DSP + * VCS_MBOX_MSG_READ_IF_SETUP : DSP -> ARM + * VCS_MBOX_MSG_READ_IF_SETUP_ACK : ARM -> DSP + * VCS_MBOX_MSG_IF_ENC_DATA : ARM -> DSP + * VCS_MBOX_MSG_IF_DEC_DATA : DSP -> ARM + */ +#define VCS_MBOX_MSG_WRITE_IF_SETUP 0x200 +#define VCS_MBOX_MSG_WRITE_IF_SETUP_ACK 0x201 +#define VCS_MBOX_MSG_READ_IF_SETUP 0x400 +#define VCS_MBOX_MSG_READ_IF_SETUP_ACK 0x401 +#define VCS_MBOX_MSG_IF_ENC_DATA 0x80 +#define VCS_MBOX_MSG_IF_DEC_DATA 0x100 + +/** + * struct mad_data - This structure holds the state of the Modem Audio Driver. + * + * @dsp_shm_write_ptr : Ptr to the first TX buffer in DSP + * @dsp_shm_read_ptr : Ptr to the first RX buffer in DSP + * @max_tx_buffs : No. of DSP buffers available to write + * @max_rx_buffs : No. of DSP buffers available to read + * @write_offset : Size of each buffer in the DSP + * @read_offset : Size of each buffer in the DSP + * @rx_buff : Buffer for incoming data + * @tx_buff : Buffer for outgoing data + * @tx_buffer_num : Buffer counter for writing to DSP + * @rx_buffer_num : Buffer counter for reading to DSP + * @rx_buffer_read : Buffer counter for reading from userspace + * @data_written : RX data message arrival indicator + * @read_setup_msg : flag for opening read data + * @readq : read queue of data message + * @lock : lock for r/w message queue + */ +struct mad_data { + void __iomem *dsp_shm_write_ptr; + void __iomem *dsp_shm_read_ptr; + int max_tx_buffs; + int max_rx_buffs; + int write_offset; + int read_offset; + u32 *rx_buff; + u32 *tx_buff; + int tx_buffer_num; + int rx_buffer_num; + int rx_buffer_read; + u32 data_written; + bool read_setup_msg; + bool open_check; + wait_queue_head_t readq; + spinlock_t lock; +}; + +static struct mad_data *mad; + +/** + * mad_send_cb - This function is default callback for send. + * @data -Pointer to the data buffer + * @len -Data buffer length + * @arg -Private data pointer associated with test + */ +static void mad_send_cb(u32 *data, u32 len, void *arg) +{ + pr_debug("%s", __func__); +} + +/** + * mad_receive_cb - This callback function is for receiving data from mailbox + * @data -Pointer to the data buffer + * @len -length of the Mailbox + * @arg -Private data pointer associated with test + */ +static void mad_receive_cb(u32 *data, u32 length, void *priv) +{ + struct mad_data *mad = priv; + struct mbox_channel_msg msg; + u32 ack_to_dsp; + unsigned long flags; + + /* setup message for write address */ + if (*data == VCS_MBOX_MSG_WRITE_IF_SETUP) { + + ack_to_dsp = VCS_MBOX_MSG_WRITE_IF_SETUP_ACK; + + /* if setup message comes again.unmap */ + if (mad->dsp_shm_write_ptr != NULL) { + iounmap(mad->dsp_shm_write_ptr); + mad->dsp_shm_write_ptr = NULL; + mad->write_offset = 0; + mad->max_tx_buffs = 0; + } + + /* convert offset to uint size */ + mad->write_offset = (data[2] & MASK_UPPER_WORD); + mad->max_tx_buffs = (data[2] >> 16); + + mad->dsp_shm_write_ptr = ioremap(data[1], + mad->max_tx_buffs * mad->write_offset); + if (mad->dsp_shm_write_ptr == NULL) + pr_err("%s :Error write address ioremap failed %X\n", + __func__ ,(u32) mad->dsp_shm_write_ptr); + + /* Initialize all buffer numbers */ + mad->tx_buffer_num = 0; + + /* Send ACK to the DSP */ + msg.channel = CHANNEL_NUM_TX; + msg.data = &ack_to_dsp; + msg.length = NR_OF_DATAWORDS_REQD_FOR_ACK; + msg.cb = mad_send_cb; + msg.priv = mad; + + if (mbox_channel_send(&msg)) + pr_err("%s: cannot send data\n", __func__); + + } /* setup message for reading SHM */ + else if (*data == VCS_MBOX_MSG_READ_IF_SETUP) { + + ack_to_dsp = VCS_MBOX_MSG_READ_IF_SETUP_ACK; + + /* if setup message comes again.unmap */ + if (mad->dsp_shm_read_ptr != NULL) { + iounmap(mad->dsp_shm_read_ptr); + mad->dsp_shm_read_ptr = NULL; + mad->read_offset = 0; + mad->max_rx_buffs = 0; + } + + /*convert offset to uint size*/ + mad->read_offset = (data[2] & MASK_UPPER_WORD); + mad->max_rx_buffs = data[2] >> 16; + + mad->dsp_shm_read_ptr = ioremap(data[1], + mad->max_rx_buffs * mad->read_offset); + + /* Initialize all buffer numbers and flags */ + mad->rx_buffer_num = 0; + mad->rx_buffer_read = 0; + mad->data_written = 0; + + /* Send ACK to the DSP */ + msg.channel = CHANNEL_NUM_TX; + msg.data = &ack_to_dsp; + msg.length = NR_OF_DATAWORDS_REQD_FOR_ACK; + msg.cb = mad_send_cb; + msg.priv = mad; + + if (mbox_channel_send(&msg)) + pr_err("%s: cannot send data\n", __func__); + + /* allow read */ + spin_lock_irqsave(&mad->lock, flags); + mad->read_setup_msg = true; + spin_unlock_irqrestore(&mad->lock, flags); + /* blocked in select() */ + wake_up_interruptible(&mad->readq); + + } else if (*data == VCS_MBOX_MSG_IF_DEC_DATA) { + /* + * Check if you have valid message with proper length in message + * otherwise Dont care + */ + if ((data[1] <= 0) || (mad->rx_buff == NULL)) { + if (mad->rx_buff == NULL) + pr_notice("%s :MAD closed", __func__); + else + pr_notice("%s : Zero size message", __func__); + } else { + mad->rx_buff[mad->rx_buffer_num] = data[1]; + mad->rx_buffer_num++; + + /* store the offset */ + mad->rx_buff[mad->rx_buffer_num] = data[2]; + + if (mad->rx_buffer_num < ((MAX_NUM_RX_BUFF * 2)-1)) + mad->rx_buffer_num++; + else + mad->rx_buffer_num = 0; + + spin_lock_irqsave(&mad->lock, flags); + mad->data_written++; + + if (mad->data_written > MAX_NUM_RX_BUFF) { + pr_notice("%s : Read msg overflow = %u\n", + mad->data_written, __func__); + /* + * Donot exceed MAX_NUM_RX_BUFF size of buffer + * TO DO overflow control + */ + mad->data_written = MAX_NUM_RX_BUFF ; + } + spin_unlock_irqrestore(&mad->lock, flags); + wake_up_interruptible(&mad->readq); + } + } else { + /* received Invalid message */ + pr_err("%s : Invalid Mesasge sent by the DSP\n ", __func__); + } +} + +static int mad_read(struct file *filp, char __user *buff, size_t count, + loff_t *offp) +{ + unsigned long flags; + unsigned int size = 0; + void __iomem *shm_ptr = NULL; + + pr_debug("%s", __func__); + + if (!(mad->data_written > 0)) { + if (wait_event_interruptible(mad->readq, + ((mad->data_written > 0) && + (mad->dsp_shm_read_ptr != NULL)))) + return -ERESTARTSYS; + } + + if (mad->dsp_shm_read_ptr == NULL) { + pr_err("%s :No Read shared memory pointer yet", __func__); + return -EINVAL ; + } + + if (mad->rx_buff[mad->rx_buffer_read] > count) { + /* + * Size of message greater than buffer , this shouldnt happen + * It shouldnt come here : we ensured that message size + * smaller that buffer length + */ + pr_err("%s : Incorrect length", __func__); + return -EFAULT; + } + size = mad->rx_buff[mad->rx_buffer_read]; + mad->rx_buff[mad->rx_buffer_read] = 0; + mad->rx_buffer_read++; + shm_ptr = (u8 *)(mad->dsp_shm_read_ptr + + (mad->rx_buff[mad->rx_buffer_read] * mad->read_offset)); + if (copy_to_user(buff, shm_ptr, size) < 0) { + pr_err("%s :Error in copy to user", __func__); + return -EFAULT; + } + + if (mad->rx_buffer_read < ((MAX_NUM_RX_BUFF*2)-1)) + mad->rx_buffer_read++; + else + mad->rx_buffer_read = 0; + + spin_lock_irqsave(&mad->lock, flags); + mad->data_written--; + if (mad->data_written < 0) { + /* Means wrong read*/ + mad->data_written = 0; + pr_err("%s :Read More than Received", __func__); + } + spin_unlock_irqrestore(&mad->lock, flags); + return size; +} + +static int mad_write(struct file *filp, const char __user *buff, size_t count, + loff_t *offp) +{ + int retval = 0; + void __iomem *dsp_write_address; + struct mbox_channel_msg msg; + + pr_debug("%s", __func__); + + /* check for valid write pointer else skip writing*/ + if (mad->dsp_shm_write_ptr == NULL) { + pr_err("%s : Illegal memory access\n", __func__); + return -EFAULT; + } + + dsp_write_address = (mad->dsp_shm_write_ptr + + (mad->tx_buffer_num * mad->write_offset)); + + if (copy_from_user(dsp_write_address, buff, count)) { + pr_err("%s: Cannot access memory\n", __func__); + return -EFAULT; + } + + mad->tx_buff[0] = VCS_MBOX_MSG_IF_ENC_DATA; + mad->tx_buff[1] = count; + mad->tx_buff[2] = mad->tx_buffer_num; + + if (mad->tx_buffer_num < (mad->max_tx_buffs-1)) + mad->tx_buffer_num++; + else + mad->tx_buffer_num = 0; + + msg.channel = CHANNEL_NUM_TX; + msg.data = mad->tx_buff; + msg.length = MAX_NR_OF_DATAWORDS; + msg.cb = mad_send_cb; + msg.priv = mad; + + retval = mbox_channel_send(&msg); + if (retval) { + pr_err("%s: cannot send data\n", __func__); + return retval; + } + return count; +} + +static unsigned int mad_select(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + unsigned long flags; + + pr_debug("%s", __func__); + + poll_wait(filp, &mad->readq, wait); + spin_lock_irqsave(&mad->lock, flags); + + if ((true == mad->read_setup_msg) && (mad->data_written > 0)) + mask |= POLLIN | POLLRDNORM; /* allow readable */ + spin_unlock_irqrestore(&mad->lock, flags); + + return mask; +} + +static int mad_open(struct inode *ino, struct file *filp) +{ + int err = 0; + + pr_debug("%s", __func__); + + if (mad->open_check == true) { + pr_err("%s :MAD is already opened", __func__); + return -EFAULT; + } + + mad->rx_buff = kzalloc((MAX_NUM_RX_BUFF*2 * + sizeof(mad->rx_buff)), GFP_KERNEL); + + if (mad->rx_buff == NULL) { + pr_err("%s: Cannot allocate RX memory\n", __func__); + err = -ENOMEM; + goto error; + } + + mad->tx_buff = kzalloc(MAX_NR_OF_DATAWORDS, GFP_KERNEL); + if (mad->tx_buff == NULL) { + pr_err("%s: Cannot allocate TX memory\n", __func__); + err = -ENOMEM; + goto error; + } + + /* Init spinlock for critical section access*/ + spin_lock_init(&mad->lock); + init_waitqueue_head(&(mad->readq)); + + err = mbox_channel_register(CHANNEL_NUM_RX, mad_receive_cb, mad); + if (err) { + pr_err("%s: Cannot register channel\n", __func__); + err = -EFAULT; + goto error; + } + mad->open_check = true; + + return 0; +error: + kfree(mad->rx_buff); + kfree(mad->tx_buff); + return err; +} + +static int mad_close(struct inode *ino, struct file *filp) +{ + pr_debug("%s", __func__); + + if (mad->dsp_shm_write_ptr != NULL) { + iounmap(mad->dsp_shm_write_ptr); + mad->dsp_shm_write_ptr = NULL; + } + + if (mad->dsp_shm_read_ptr != NULL) { + iounmap(mad->dsp_shm_read_ptr); + mad->dsp_shm_read_ptr = NULL; + } + + kfree(mad->rx_buff); + kfree(mad->tx_buff); + + mad->open_check = false; + + return 0; +} + +static const struct file_operations mad_fops = { + .release = mad_close, + .open = mad_open, + .read = mad_read, + .write = mad_write, + .poll = mad_select, + .owner = THIS_MODULE, +}; + +static struct miscdevice mad_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = MAD_NAME, + .fops = &mad_fops +}; + +static int __init mad_init(void) +{ + pr_debug("%s", __func__); + + mad = kzalloc(sizeof(*mad), GFP_KERNEL); + if (mad == NULL) { + pr_err("%s :MAD Memory Allocation Failed", __func__); + return -ENOMEM; + } + + return misc_register(&mad_dev); +} +module_init(mad_init); + +static void __exit mad_exit(void) +{ + pr_debug("%s", __func__); + + kfree(mad); + + misc_deregister(&mad_dev); +} |