diff options
Diffstat (limited to 'drivers/misc/i2s/i2s_test_protocol_driver.c')
-rw-r--r-- | drivers/misc/i2s/i2s_test_protocol_driver.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/drivers/misc/i2s/i2s_test_protocol_driver.c b/drivers/misc/i2s/i2s_test_protocol_driver.c new file mode 100644 index 00000000000..639a28454f5 --- /dev/null +++ b/drivers/misc/i2s/i2s_test_protocol_driver.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) ST-Ericsson SA 2009 + * Author: Sandeep Kaushik, <sandeep-mmc.kaushik@st.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/i2s/i2s.h> +#include <linux/i2s/i2s_test_prot.h> +#include <mach/msp.h> + +int i2s_drv_offset = 1; + +module_param(i2s_drv_offset, int, 1); +MODULE_PARM_DESC(i2s_drv_offset, "i2s driver to be opened)=(0/1/2/3/4/5)"); + +#define MAX_I2S_CLIENTS 6 + +struct i2sdrv_data { + spinlock_t i2s_lock; + struct i2s_device *i2s; + /* flag to show the device is closed or not */ + bool device_closed; + u32 tx_status; + u32 rx_status; +}; + +static struct i2sdrv_data *i2sdrv[MAX_I2S_CLIENTS]; + +/*API Interface */ +int i2s_testprot_drv_open(int i2s_device_num) +{ + if (!i2sdrv[i2s_device_num]) + return -EINVAL; + + spin_lock_irq(&i2sdrv[i2s_device_num]->i2s_lock); + if (!i2sdrv[i2s_device_num]->device_closed) { + spin_unlock_irq(&i2sdrv[i2s_device_num]->i2s_lock); + return -EBUSY; + } + i2sdrv[i2s_device_num]->device_closed = false; + spin_unlock_irq(&i2sdrv[i2s_device_num]->i2s_lock); + + return 0; +} +EXPORT_SYMBOL(i2s_testprot_drv_open); + +int i2s_config_default_protocol(int i2s_device_num, + struct test_prot_config *config) +{ + return __i2s_testprot_drv_configure(i2s_device_num, config, true); +} +EXPORT_SYMBOL(i2s_config_default_protocol); + +int i2s_testprot_drv_configure(int i2s_device_num, + struct test_prot_config *config) +{ + return __i2s_testprot_drv_configure(i2s_device_num, config, false); +} +EXPORT_SYMBOL(i2s_testprot_drv_configure); + +int __i2s_testprot_drv_configure(int i2s_device_num, + struct test_prot_config *config, bool use_default) +{ + int error_status = 0; + struct i2s_device *i2s_dev; + struct msp_config msp_config = { + .tx_clock_sel = TX_CLK_SEL_SRG, + .rx_clock_sel = 0x0, + .tx_frame_sync_sel = TX_SYNC_SRG_AUTO, + .rx_frame_sync_sel = 0x0, + .input_clock_freq = MSP_INPUT_FREQ_48MHZ, + .srg_clock_sel = SRG_CLK_SEL_APB, + .rx_frame_sync_pol = RX_FIFO_SYNC_HI, + .tx_frame_sync_pol = TX_FIFO_SYNC_HI, + .rx_fifo_config = RX_FIFO_ENABLE, + .tx_fifo_config = TX_FIFO_ENABLE, + .spi_clk_mode = SPI_CLK_MODE_NORMAL, + .tx_data_enable = 0x8000, + .spi_burst_mode = 0, + .loopback_enable = 0x80, + }; + + msp_config.default_protocol_desc = use_default; + + if (!i2sdrv[i2s_device_num]) + return -EINVAL; + + i2s_dev = i2sdrv[i2s_device_num]->i2s; + + if (i2sdrv[i2s_device_num]->device_closed) + return -EINVAL; + + if (!config) + return -EINVAL; + + msp_config.handler = config->handler; + msp_config.tx_callback_data = config->tx_callback_data; + msp_config.rx_callback_data = config->rx_callback_data; + msp_config.frame_freq = config->frame_freq; + msp_config.frame_size = config->frame_size; + msp_config.data_size = config->data_size; + msp_config.direction = config->direction; + msp_config.protocol = config->protocol; + msp_config.work_mode = config->work_mode; + + msp_config.def_elem_len = use_default; + + msp_config.multichannel_configured = 0; + msp_config.protocol_desc = config->protocol_desc; + + msp_config.multichannel_configured = config->multichannel_configured; + msp_config.multichannel_config.tx_multichannel_enable = + config->multichannel_config.tx_multichannel_enable; + /* Channel 1 to 3 */ + msp_config.multichannel_config.tx_channel_0_enable = + config->multichannel_config.tx_channel_0_enable; + /* Channel 33 to 64 */ + msp_config.multichannel_config.tx_channel_1_enable = + config->multichannel_config.tx_channel_1_enable; + /* Channel 65 to 96 */ + msp_config.multichannel_config.tx_channel_2_enable = + config->multichannel_config.tx_channel_2_enable; + /* Channel 97 to 128 */ + msp_config.multichannel_config.tx_channel_3_enable = + config->multichannel_config.tx_channel_3_enable; + msp_config.multichannel_config.rx_multichannel_enable = + config->multichannel_config.rx_multichannel_enable; + /* Channel 1 to 32 */ + msp_config.multichannel_config.rx_channel_0_enable = + config->multichannel_config.rx_channel_0_enable; + /* Channel 33 to 64 */ + msp_config.multichannel_config.rx_channel_1_enable = + config->multichannel_config.rx_channel_1_enable; + /* Channel 65 to 96 */ + msp_config.multichannel_config.rx_channel_2_enable = + config->multichannel_config.rx_channel_2_enable; + /* Channel 97 to 128 */ + msp_config.multichannel_config.rx_channel_3_enable = + config->multichannel_config.rx_channel_3_enable; + msp_config.multichannel_config.rx_comparison_enable_mode = + config->multichannel_config.rx_comparison_enable_mode; + msp_config.multichannel_config.comparison_value = + config->multichannel_config.comparison_value; + msp_config.multichannel_config.comparison_mask = + config->multichannel_config.comparison_mask; + + error_status = + i2s_setup(i2s_dev->controller, &msp_config); + if (error_status < 0) + dev_err(&i2s_dev->dev, "error in msp enable, error_status is %d\n", + error_status); + + return error_status; + +} + +int i2s_testprot_drv_transfer(int i2s_device_num, + void *txdata, size_t txbytes, void *rxdata, size_t rxbytes, + enum i2s_transfer_mode_t transfer_mode) +{ + int bytes_transreceive; + struct i2s_device *i2s_dev; + struct i2s_message message = {}; + + if (!i2sdrv[i2s_device_num]) + return -EINVAL; + + i2s_dev = i2sdrv[i2s_device_num]->i2s; + + if (i2sdrv[i2s_device_num]->device_closed) { + dev_info(&i2s_dev->dev, "msp device not opened yet\n"); + return -EINVAL; + } + + message.i2s_transfer_mode = transfer_mode; + message.i2s_direction = I2S_DIRECTION_BOTH; + message.txbytes = txbytes; + message.txdata = txdata; + message.rxbytes = rxbytes; + message.rxdata = rxdata; + message.dma_flag = 1; + bytes_transreceive = i2s_transfer(i2s_dev->controller, &message); + dev_dbg(&i2s_dev->dev, "bytes transreceived %d\n", bytes_transreceive); + + return bytes_transreceive; +} +EXPORT_SYMBOL(i2s_testprot_drv_transfer); + +int i2s_testprot_drv_close(int i2s_device_num) +{ + int status; + struct i2s_device *i2s_dev; + + if (!i2sdrv[i2s_device_num]) + return -EINVAL; + + i2s_dev = i2sdrv[i2s_device_num]->i2s; + + if (i2sdrv[i2s_device_num]->device_closed) + return -EINVAL; + + status = i2s_cleanup(i2s_dev->controller, DISABLE_ALL); + if (status) + return status; + + /* Mark the device as closed */ + i2sdrv[i2s_device_num]->device_closed = true; + + return 0; +} +EXPORT_SYMBOL(i2s_testprot_drv_close); + +static int i2sdrv_probe(struct i2s_device *i2s) +{ + int status = 0; + + /* Allocate driver data */ + if (!try_module_get(i2s->controller->dev.parent->driver->owner)) + return -ENOENT; + + i2sdrv[i2s->chip_select] = kzalloc(sizeof(*i2sdrv[i2s->chip_select]), + GFP_KERNEL); + + if (!i2sdrv[i2s->chip_select]) + return -ENOMEM; + + /* Initialize the driver data */ + i2sdrv[i2s->chip_select]->i2s = i2s; + i2sdrv[i2s->chip_select]->device_closed = true; + i2sdrv[i2s->chip_select]->tx_status = 0; + i2sdrv[i2s->chip_select]->rx_status = 0; + spin_lock_init(&i2sdrv[i2s->chip_select]->i2s_lock); + + i2s_set_drvdata(i2s, (void *)i2sdrv[i2s->chip_select]); + return status; +} + +static int i2sdrv_remove(struct i2s_device *i2s) +{ + spin_lock_irq(&i2sdrv[i2s->chip_select]->i2s_lock); + i2sdrv[i2s->chip_select]->i2s = NULL; + i2s_set_drvdata(i2s, NULL); + module_put(i2s->controller->dev.parent->driver->owner); + spin_unlock_irq(&i2sdrv[i2s->chip_select]->i2s_lock); + + kfree(i2sdrv[i2s->chip_select]); + + return 0; +} + +static const struct i2s_device_id i2s_test_prot_id_table[] = { + { "i2s_device.0", 0, 0 }, + { "i2s_device.1", 0, 0 }, + { "i2s_device.2", 0, 0 }, + { "i2s_device.3", 0, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2s, i2s_test_prot_id_table); + +static struct i2s_driver i2sdrv_i2s = { + .driver = { + .name = "i2s_test_protocol_driver", + .owner = THIS_MODULE, + }, + .probe = i2sdrv_probe, + .remove = __devexit_p(i2sdrv_remove), + .id_table = i2s_test_prot_id_table, + + /* + * NOTE: suspend/resume methods are not necessary here. + */ +}; + +static int __init i2sdrv_init(void) +{ + int status; + + status = i2s_register_driver(&i2sdrv_i2s); + if (status < 0) + printk(KERN_ERR "Unable to register i2s driver\n"); + + return status; +} +module_init(i2sdrv_init); + +static void __exit i2sdrv_exit(void) +{ + i2s_unregister_driver(&i2sdrv_i2s); +} +module_exit(i2sdrv_exit); + +MODULE_AUTHOR("Sandeep Kaushik, <sandeep-mmc.kaushik@st.com>"); +MODULE_DESCRIPTION("Test Driver module I2S device interface"); +MODULE_LICENSE("GPL"); |