blob: c56006690912251b5eb1e94227849b9aad445639 [file] [log] [blame]
/*
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Ola Lilja ola.o.lilja@stericsson.com,
* Roger Nilsson roger.xr.nilsson@stericsson.com
* 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 <sound/soc.h>
#include <sound/soc-dai.h>
#include <asm/dma.h>
#include "u8500_msp_dai.h"
#include "u8500_pcm.h"
#include <mach/msp.h>
#include <linux/i2s/i2s.h>
struct u8500_dai_private_t{
spinlock_t lock;
struct i2s_device *i2s;
u8 rx_active;
u8 tx_active;
};
struct u8500_dai_private_t msp_dai_private[U8500_NBR_OF_DAI] =
{
/* DAI 0 */
{
.lock = SPIN_LOCK_UNLOCKED,
.i2s = NULL,
.tx_active = 0,
.rx_active = 0,
},
/* DAI 1 */
{
.lock = SPIN_LOCK_UNLOCKED,
.i2s = NULL,
.tx_active = 0,
.rx_active = 0,
},
};
static void compile_msp_config(struct msp_generic_config *msp_config,struct snd_pcm_substream *substream)
{
int channels;
msp_config->tx_clock_sel = 0;
msp_config->rx_clock_sel = 0;
msp_config->tx_frame_sync_sel = 0;
msp_config->rx_frame_sync_sel = 0;
msp_config->input_clock_freq = MSP_INPUT_FREQ_48MHZ;
msp_config->srg_clock_sel = 0;
msp_config->rx_frame_sync_pol = RX_FIFO_SYNC_HI;
msp_config->tx_frame_sync_pol = TX_FIFO_SYNC_HI;
msp_config->rx_fifo_config = RX_FIFO_ENABLE;
msp_config->tx_fifo_config = TX_FIFO_ENABLE;
msp_config->spi_clk_mode = SPI_CLK_MODE_NORMAL;
msp_config->spi_burst_mode = 0;
msp_config->handler = u8500_pcm_dma_eot_handler;
msp_config->tx_callback_data = substream;
msp_config->tx_data_enable = 0;
msp_config->rx_callback_data = substream;
msp_config->loopback_enable = 0;
msp_config->multichannel_configured = 0;
msp_config->def_elem_len = 0;
msp_config->default_protocol_desc = 0;
msp_config->protocol_desc.rx_phase_mode = MSP_SINGLE_PHASE;
msp_config->protocol_desc.tx_phase_mode = MSP_SINGLE_PHASE;
msp_config->protocol_desc.rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
msp_config->protocol_desc.tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
msp_config->protocol_desc.rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
msp_config->protocol_desc.tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
msp_config->protocol_desc. rx_frame_length_1 = MSP_FRAME_LENGTH_2;
msp_config->protocol_desc. rx_frame_length_2 = MSP_FRAME_LENGTH_2;
msp_config->protocol_desc.tx_frame_length_1 = MSP_FRAME_LENGTH_2;
msp_config->protocol_desc.tx_frame_length_2 = MSP_FRAME_LENGTH_2;
msp_config->protocol_desc. rx_element_length_1 = MSP_ELEM_LENGTH_32;
msp_config->protocol_desc.rx_element_length_2 = MSP_ELEM_LENGTH_32;
msp_config->protocol_desc.tx_element_length_1 = MSP_ELEM_LENGTH_32;
msp_config->protocol_desc.tx_element_length_2 = MSP_ELEM_LENGTH_32;
msp_config->protocol_desc.rx_data_delay = MSP_DELAY_0;
msp_config->protocol_desc.tx_data_delay = MSP_DELAY_0;
msp_config->protocol_desc.rx_clock_pol = MSP_FALLING_EDGE;
msp_config->protocol_desc.tx_clock_pol = MSP_RISING_EDGE;
msp_config->protocol_desc. rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
msp_config->protocol_desc.tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
msp_config->protocol_desc.rx_half_word_swap = MSP_HWS_NO_SWAP;
msp_config->protocol_desc.tx_half_word_swap = MSP_HWS_NO_SWAP;
msp_config->protocol_desc.compression_mode = MSP_COMPRESS_MODE_LINEAR;
msp_config->protocol_desc.expansion_mode = MSP_EXPAND_MODE_LINEAR;
msp_config->protocol_desc.spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
msp_config->protocol_desc.spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
msp_config->protocol_desc.frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
msp_config->protocol_desc.frame_period = 63;
msp_config->protocol_desc.frame_width = 31;
msp_config->protocol_desc.total_clocks_for_one_frame = 64;
msp_config->protocol = MSP_I2S_PROTOCOL; //MSP_PCM_PROTOCOL
msp_config->direction = MSP_BOTH_T_R_MODE;
msp_config->frame_size = 0;
msp_config->frame_freq = 48000;
channels = 2;
msp_config->data_size = (channels == 1) ? MSP_DATA_SIZE_16BIT : MSP_DATA_SIZE_32BIT;
msp_config->work_mode = MSP_DMA_MODE;
msp_config->multichannel_configured = 1;
msp_config->multichannel_config.tx_multichannel_enable = 1;
msp_config->multichannel_config.tx_channel_0_enable = 0x0000003; //Channel 1 and channel 2
msp_config->multichannel_config.tx_channel_1_enable = 0x0000000; //Channel 1 and channel 2
msp_config->multichannel_config.tx_channel_2_enable = 0x0000000; //Channel 1 and channel 2
msp_config->multichannel_config.tx_channel_3_enable = 0x0000000; //Channel 1 and channel 2
msp_config->multichannel_config.rx_multichannel_enable = 1;
msp_config->multichannel_config.rx_channel_0_enable = 0x0000003; //Channel 1 and channel 2
msp_config->multichannel_config.rx_channel_1_enable = 0x0000000; //Channel 1 and channel 2
msp_config->multichannel_config.rx_channel_2_enable = 0x0000000; //Channel 1 and channel 2
msp_config->multichannel_config.rx_channel_3_enable = 0x0000000; //Channel 1 and channel 2
}
static void u8500_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
{
int ret = 0;
unsigned long flags;
struct u8500_dai_private_t *dai_private;
printk(KERN_ALERT "STW4500: u8500_dai_shutdown\n");
if (!(dai_private = msp_dai->private_data))
return;
spin_lock_irqsave(&dai_private->lock, flags);
/* Mark the stopped direction as inactive */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dai_private->tx_active = 0;
} else {
dai_private->rx_active = 0;
}
if (0 == dai_private->tx_active && 0 == dai_private->rx_active) {
msp_dai->private_data = NULL;
ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_ALL);
if (ret)
printk(KERN_WARNING "Error closing i2s\n");
} else {
if(dai_private->tx_active)
ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_RECEIVE);
else
ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_TRANSMIT);
if (ret)
printk(KERN_WARNING "Error closing i2s\n");
}
spin_unlock_irqrestore(&dai_private->lock, flags);
return;
}
static int u8500_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
{
struct u8500_dai_private_t *dai_private;
printk(KERN_ALERT "STW4500: u8500_dai_startup\n");
dai_private=&msp_dai_private[msp_dai->id];
/* Store pointer to private data */
msp_dai->private_data = dai_private;
if (dai_private->i2s == NULL)
{
printk(KERN_ALERT "u8500_pcm_open: Error: i2sdrv.i2s == NULL.\n");
return -1;
}
if (dai_private->i2s->controller == NULL)
{
printk(KERN_ALERT "u8500_pcm_open: Error: i2sdrv.i2s->controller == NULL.\n");
return -1;
}
return 0;
}
static int u8500_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
{
int ret = 0;
unsigned long flags_private;
struct u8500_dai_private_t *dai_private;
struct msp_generic_config msp_config;
printk(KERN_ALERT "STW4500: u8500_dai_prepare\n");
dai_private = msp_dai->private_data;
spin_lock_irqsave(&dai_private->lock, flags_private);
/* Is the other direction already active? */
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
dai_private->rx_active) ||
(substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
dai_private->tx_active)) {
/* TODO: Check if sample rate match ! */
/*
if ( != runtime->rate) {
printk(KERN_ERR "CPU_DAI configuration mismatch with already opened stream!\n");
ret = -EBUSY;
goto cleanup;
}
*/
} else if (!dai_private->rx_active && !dai_private->tx_active) {
ret = 0;
compile_msp_config(&msp_config,substream);
ret = i2s_setup(dai_private->i2s->controller, &msp_config);
if (ret < 0) {
printk(KERN_ALERT "u8500_dai_prepare: i2s_setup failed! ret = %d\n", ret);
goto cleanup;
}
}
/* Mark the newly started stream as active */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dai_private->tx_active = 1;
} else {
dai_private->rx_active = 1;
}
cleanup:
spin_unlock_irqrestore(&dai_private->lock, flags_private);
return ret;
}
static int u8500_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *msp_dai)
{
printk(KERN_ALERT "STW4500: u8500_dai_hw_params\n");
return 0;
}
static int u8500_dai_set_dai_fmt(struct snd_soc_dai *msp_dai, unsigned int fmt)
{
printk(KERN_ALERT "STW4500: u8500_dai_set_dai_fmt\n");
return 0;
}
static int u8500_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *msp_dai)
{
unsigned long flags;
int ret = 0;
struct u8500_dai_private_t *u8500_dai_private = msp_dai->private_data;
printk(KERN_ALERT "STW4500: u8500_dai_trigger\n");
spin_lock_irqsave(&u8500_dai_private->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
ret = 0;
break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = 0;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
ret = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = 0;
break;
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
return ret;
}
int u8500_i2s_send_data(void *data, size_t bytes, int dai_idx)
{
unsigned long flags;
struct u8500_dai_private_t *dai_private;
struct i2s_message message;
struct i2s_device *i2s_dev;
int ret = 0;
dai_private=&msp_dai_private[dai_idx];
spin_lock_irqsave(&dai_private->lock, flags);
i2s_dev=dai_private->i2s;
if(!dai_private->tx_active)
{
printk(KERN_ERR "u8500_i2s_send_data: I2S controller not available\n");
goto cleanup;
}
message.txbytes = bytes;
message.txdata = data;
message.rxbytes = 0;
message.rxdata = NULL;
message.dma_flag = 1;
ret = i2s_transfer(i2s_dev->controller, &message);
if(ret < 0)
{
printk(KERN_ERR "u8500_i2s_send_data: Error: i2s_transfer failed!\n");
goto cleanup;
}
cleanup:
spin_unlock_irqrestore(&dai_private->lock, flags);
return ret;
}
int u8500_i2s_receive_data(void *data, size_t bytes, int dai_idx)
{
unsigned long flags;
struct u8500_dai_private_t *dai_private;
struct i2s_message message;
struct i2s_device *i2s_dev;
int ret = 0;
dai_private=&msp_dai_private[dai_idx];
spin_lock_irqsave(&dai_private->lock, flags);
i2s_dev=dai_private->i2s;
if(!dai_private->rx_active)
{
printk(KERN_ERR "u8500_i2s_receive_data: I2S controller not available\n");
goto cleanup;
}
message.rxbytes = bytes;
message.rxdata = data;
message.txbytes = 0;
message.txdata = NULL;
message.dma_flag = 1;
ret = i2s_transfer(i2s_dev->controller, &message);
if(ret < 0)
{
printk(KERN_ERR "u8500_i2s_receive_data: Error: i2s_transfer failed!\n");
goto cleanup;
}
cleanup:
spin_unlock_irqrestore(&dai_private->lock, flags);
return ret;
}
#define u8500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define u8500_I2S_FORMATS ( SNDRV_PCM_FMTBIT_S16_LE )
#define dai_suspend NULL
#define dai_resume NULL
#define u8500_dai_set_dai_sysclk NULL
struct snd_soc_dai_ops u8500_msp_dai_ops_0 =
{
.set_sysclk=u8500_dai_set_dai_sysclk,
.set_fmt=u8500_dai_set_dai_fmt,
.startup = u8500_dai_startup,
.shutdown = u8500_dai_shutdown,
.prepare = u8500_dai_prepare,
.trigger = u8500_dai_trigger,
.hw_params = u8500_dai_hw_params,
};
struct snd_soc_dai_ops u8500_msp_dai_ops_1 =
{
.set_sysclk=u8500_dai_set_dai_sysclk,
.set_fmt=u8500_dai_set_dai_fmt,
.startup = u8500_dai_startup,
.shutdown = u8500_dai_shutdown,
.prepare = u8500_dai_prepare,
.trigger = u8500_dai_trigger,
.hw_params = u8500_dai_hw_params,
};
struct snd_soc_dai u8500_msp_dai[U8500_NBR_OF_DAI] =
{
{
.name = "u8500_i2s-0",
.id = 0,
//.type = SND_SOC_DAI_I2S,
.suspend = dai_suspend,
.resume = dai_resume,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = u8500_I2S_RATES,
.formats = u8500_I2S_FORMATS,},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = u8500_I2S_RATES,
.formats = u8500_I2S_FORMATS,},
.ops = &u8500_msp_dai_ops_0,
.private_data = &msp_dai_private[0],
},
{
.name = "u8500_i2s-1",
.id = 1,
//.type = SND_SOC_DAI_I2S,
.suspend = dai_suspend,
.resume = dai_resume,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = u8500_I2S_RATES,
.formats = u8500_I2S_FORMATS,},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = u8500_I2S_RATES,
.formats = u8500_I2S_FORMATS,},
.ops = &u8500_msp_dai_ops_1,
.private_data = &msp_dai_private[1],
},
};
EXPORT_SYMBOL(u8500_msp_dai);
static int i2sdrv_probe(struct i2s_device *i2s)
{
unsigned long flags;
struct u8500_dai_private_t *u8500_dai_private;
u8500_dai_private=&msp_dai_private[0];
printk(KERN_DEBUG "i2sdrv_probe: Enter.\n");
spin_lock_irqsave(&u8500_dai_private->lock, flags);
u8500_dai_private->i2s = i2s;
spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
try_module_get(i2s->controller->dev.parent->driver->owner);
i2s_set_drvdata(i2s, (void*)u8500_dai_private);
return 0;
}
static int i2sdrv_remove(struct i2s_device *i2s)
{
unsigned long flags;
struct u8500_dai_private_t *u8500_dai_private = i2s_get_drvdata(i2s);
printk(KERN_DEBUG "U8500_PCM: i2sdrv_remove\n");
spin_lock_irqsave(&u8500_dai_private->lock, flags);
u8500_dai_private->i2s = NULL;
i2s_set_drvdata(i2s, NULL);
spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
printk(KERN_ALERT "i2sdrv_remove: module_put\n");
module_put(i2s->controller->dev.parent->driver->owner);
return 0;
}
static const struct i2s_device_id dev_id_table[] = {
{ "i2s_device.0", 0, 0 },
{ },
};
MODULE_DEVICE_TABLE(i2s, acodec_id_table);
static struct i2s_driver i2sdrv_i2s = {
.driver = {
.name = "nomadik_acodec",
.owner = THIS_MODULE,
},
.probe = i2sdrv_probe,
.remove = __devexit_p(i2sdrv_remove),
.id_table = dev_id_table,
};
int u8500_platform_registerI2S(void)
{
int ret;
printk(KERN_ALERT "u8500_platform_registerI2S: Register I2S-driver.\n");
ret = i2s_register_driver(&i2sdrv_i2s);
if (ret < 0) {
printk(KERN_ALERT "u8500_platform_registerI2S: Unable to register I2S-driver\n");
}
return ret;
}