ASoC: Ux500: Add MSP I2S-driver

Add driver for running I2S with the MSP-block.

Signed-off-by: Ola Lilja <ola.o.lilja@stericsson.com>
[Fixed trailing whitespace -- broonie]
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
Ola Lilja 2012-05-08 15:57:18 +02:00 committed by Mark Brown
parent 41a41eaca4
commit 3592b7f69a
8 changed files with 2238 additions and 0 deletions

View File

@ -47,6 +47,7 @@ source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"

View File

@ -25,3 +25,4 @@ obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/

15
sound/soc/ux500/Kconfig Normal file
View File

@ -0,0 +1,15 @@
#
# Ux500 SoC audio configuration
#
menuconfig SND_SOC_UX500
tristate "SoC Audio support for Ux500 platform"
depends on SND_SOC
help
Say Y if you want to enable ASoC-support for
any of the Ux500 platforms (e.g. U8500).
config SND_SOC_UX500_PLAT_MSP_I2S
tristate "Platform - MSP (I2S)"
depends on SND_SOC_UX500
help
Say Y if you want to enable the Ux500 MSP I2S-driver.

4
sound/soc/ux500/Makefile Normal file
View File

@ -0,0 +1,4 @@
# Ux500 Platform Support
snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o
obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o

View File

@ -0,0 +1,843 @@
/*
* Copyright (C) ST-Ericsson SA 2012
*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
#include <mach/board-mop500-msp.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "ux500_msp_i2s.h"
#include "ux500_msp_dai.h"
static int setup_pcm_multichan(struct snd_soc_dai *dai,
struct ux500_msp_config *msp_config)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
struct msp_multichannel_config *multi =
&msp_config->multichannel_config;
if (drvdata->slots > 1) {
msp_config->multichannel_configured = 1;
multi->tx_multichannel_enable = true;
multi->rx_multichannel_enable = true;
multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED;
multi->tx_channel_0_enable = drvdata->tx_mask;
multi->tx_channel_1_enable = 0;
multi->tx_channel_2_enable = 0;
multi->tx_channel_3_enable = 0;
multi->rx_channel_0_enable = drvdata->rx_mask;
multi->rx_channel_1_enable = 0;
multi->rx_channel_2_enable = 0;
multi->rx_channel_3_enable = 0;
dev_dbg(dai->dev,
"%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n",
__func__, drvdata->slots, multi->tx_channel_0_enable,
multi->rx_channel_0_enable);
}
return 0;
}
static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate,
struct msp_protdesc *prot_desc)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
switch (drvdata->slots) {
case 1:
switch (rate) {
case 8000:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_8_KHZ;
break;
case 16000:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_16_KHZ;
break;
case 44100:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_44_1_KHZ;
break;
case 48000:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_48_KHZ;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported sample-rate (freq = %d)!\n",
__func__, rate);
return -EINVAL;
}
break;
case 2:
prot_desc->frame_period = FRAME_PER_2_SLOTS;
break;
case 8:
prot_desc->frame_period = FRAME_PER_8_SLOTS;
break;
case 16:
prot_desc->frame_period = FRAME_PER_16_SLOTS;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported slot-count (slots = %d)!\n",
__func__, drvdata->slots);
return -EINVAL;
}
prot_desc->clocks_per_frame =
prot_desc->frame_period+1;
dev_dbg(dai->dev, "%s: Clocks per frame: %u\n",
__func__,
prot_desc->clocks_per_frame);
return 0;
}
static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
struct msp_protdesc *prot_desc)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
u32 frame_length = MSP_FRAME_LEN_1;
prot_desc->frame_width = 0;
switch (drvdata->slots) {
case 1:
frame_length = MSP_FRAME_LEN_1;
break;
case 2:
frame_length = MSP_FRAME_LEN_2;
break;
case 8:
frame_length = MSP_FRAME_LEN_8;
break;
case 16:
frame_length = MSP_FRAME_LEN_16;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported slot-count (slots = %d)!\n",
__func__, drvdata->slots);
return -EINVAL;
}
prot_desc->tx_frame_len_1 = frame_length;
prot_desc->rx_frame_len_1 = frame_length;
prot_desc->tx_frame_len_2 = frame_length;
prot_desc->rx_frame_len_2 = frame_length;
prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
return setup_frameper(dai, rate, prot_desc);
}
static int setup_clocking(struct snd_soc_dai *dai,
unsigned int fmt,
struct ux500_msp_config *msp_config)
{
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsopported inversion (fmt = 0x%x)!\n",
__func__, fmt);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
dev_dbg(dai->dev, "%s: Codec is master.\n", __func__);
msp_config->iodelay = 0x20;
msp_config->rx_fsync_sel = 0;
msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT;
msp_config->tx_clk_sel = 0;
msp_config->rx_clk_sel = 0;
msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT;
break;
case SND_SOC_DAIFMT_CBS_CFS:
dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__);
msp_config->tx_clk_sel = TX_CLK_SEL_SRG;
msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG;
msp_config->rx_clk_sel = RX_CLK_SEL_SRG;
msp_config->rx_fsync_sel = RX_SYNC_SRG;
msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT;
break;
default:
dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n",
__func__, fmt);
return -EINVAL;
}
return 0;
}
static int setup_pcm_protdesc(struct snd_soc_dai *dai,
unsigned int fmt,
struct msp_protdesc *prot_desc)
{
prot_desc->rx_phase_mode = MSP_SINGLE_PHASE;
prot_desc->tx_phase_mode = MSP_SINGLE_PHASE;
prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI);
prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT;
if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) {
dev_dbg(dai->dev, "%s: DSP_A.\n", __func__);
prot_desc->rx_clk_pol = MSP_RISING_EDGE;
prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
prot_desc->rx_data_delay = MSP_DELAY_1;
prot_desc->tx_data_delay = MSP_DELAY_1;
} else {
dev_dbg(dai->dev, "%s: DSP_B.\n", __func__);
prot_desc->rx_clk_pol = MSP_FALLING_EDGE;
prot_desc->tx_clk_pol = MSP_RISING_EDGE;
prot_desc->rx_data_delay = MSP_DELAY_0;
prot_desc->tx_data_delay = MSP_DELAY_0;
}
prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
return 0;
}
static int setup_i2s_protdesc(struct msp_protdesc *prot_desc)
{
prot_desc->rx_phase_mode = MSP_DUAL_PHASE;
prot_desc->tx_phase_mode = MSP_DUAL_PHASE;
prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO);
prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT;
prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1;
prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1;
prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1;
prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1;
prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
prot_desc->rx_clk_pol = MSP_RISING_EDGE;
prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
prot_desc->rx_data_delay = MSP_DELAY_0;
prot_desc->tx_data_delay = MSP_DELAY_0;
prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
return 0;
}
static int setup_msp_config(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai,
struct ux500_msp_config *msp_config)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
struct msp_protdesc *prot_desc = &msp_config->protdesc;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int fmt = drvdata->fmt;
int ret;
memset(msp_config, 0, sizeof(*msp_config));
msp_config->f_inputclk = drvdata->master_clk;
msp_config->tx_fifo_config = TX_FIFO_ENABLE;
msp_config->rx_fifo_config = RX_FIFO_ENABLE;
msp_config->def_elem_len = 1;
msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
MSP_DIR_TX : MSP_DIR_RX;
msp_config->data_size = MSP_DATA_BITS_32;
msp_config->frame_freq = runtime->rate;
dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n",
__func__, msp_config->f_inputclk, msp_config->frame_freq);
/* To avoid division by zero */
prot_desc->clocks_per_frame = 1;
dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__,
runtime->rate, runtime->channels);
switch (fmt &
(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
msp_config->default_protdesc = 1;
msp_config->protocol = MSP_I2S_PROTOCOL;
break;
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
msp_config->data_size = MSP_DATA_BITS_16;
msp_config->protocol = MSP_I2S_PROTOCOL;
ret = setup_i2s_protdesc(prot_desc);
if (ret < 0)
return ret;
break;
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
dev_dbg(dai->dev, "%s: PCM format.\n", __func__);
msp_config->data_size = MSP_DATA_BITS_16;
msp_config->protocol = MSP_PCM_PROTOCOL;
ret = setup_pcm_protdesc(dai, fmt, prot_desc);
if (ret < 0)
return ret;
ret = setup_pcm_multichan(dai, msp_config);
if (ret < 0)
return ret;
ret = setup_pcm_framing(dai, runtime->rate, prot_desc);
if (ret < 0)
return ret;
break;
default:
dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n",
__func__, fmt);
return -EINVAL;
}
return setup_clocking(dai, fmt, msp_config);
}
static int ux500_msp_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret = 0;
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
snd_pcm_stream_str(substream));
/* Enable regulator */
ret = regulator_enable(drvdata->reg_vape);
if (ret != 0) {
dev_err(drvdata->msp->dev,
"%s: Failed to enable regulator!\n", __func__);
return ret;
}
/* Enable clock */
dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__);
clk_enable(drvdata->clk);
return 0;
}
static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret;
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
snd_pcm_stream_str(substream));
if (drvdata->vape_opp_constraint == 1) {
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
"ux500_msp_i2s", 50);
drvdata->vape_opp_constraint = 0;
}
if (ux500_msp_i2s_close(drvdata->msp,
is_playback ? MSP_DIR_TX : MSP_DIR_RX)) {
dev_err(dai->dev,
"%s: Error: MSP %d (%s): Unable to close i2s.\n",
__func__, dai->id, snd_pcm_stream_str(substream));
}
/* Disable clock */
clk_disable(drvdata->clk);
/* Disable regulator */
ret = regulator_disable(drvdata->reg_vape);
if (ret < 0)
dev_err(dai->dev,
"%s: ERROR: Failed to disable regulator (%d)!\n",
__func__, ret);
}
static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret = 0;
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
struct snd_pcm_runtime *runtime = substream->runtime;
struct ux500_msp_config msp_config;
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__,
dai->id, snd_pcm_stream_str(substream), runtime->rate);
setup_msp_config(substream, dai, &msp_config);
ret = ux500_msp_i2s_open(drvdata->msp, &msp_config);
if (ret < 0) {
dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n",
__func__, ret);
return ret;
}
/* Set OPP-level */
if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) &&
(drvdata->msp->f_bitclk > 19200000)) {
/* If the bit-clock is higher than 19.2MHz, Vape should be
* run in 100% OPP. Only when bit-clock is used (MSP master) */
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
"ux500-msp-i2s", 100);
drvdata->vape_opp_constraint = 1;
} else {
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
"ux500-msp-i2s", 50);
drvdata->vape_opp_constraint = 0;
}
return ret;
}
static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
unsigned int mask, slots_active;
struct snd_pcm_runtime *runtime = substream->runtime;
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
__func__, dai->id, snd_pcm_stream_str(substream));
switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
1, 2);
break;
case SND_SOC_DAIFMT_DSP_B:
case SND_SOC_DAIFMT_DSP_A:
mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
drvdata->tx_mask :
drvdata->rx_mask;
slots_active = hweight32(mask);
dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
slots_active, slots_active);
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported protocol (fmt = 0x%x)!\n",
__func__, drvdata->fmt);
return -EINVAL;
}
return 0;
}
static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id);
switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
SND_SOC_DAIFMT_MASTER_MASK)) {
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n",
__func__, drvdata->fmt);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
case SND_SOC_DAIFMT_NB_IF:
case SND_SOC_DAIFMT_IB_IF:
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
__func__, drvdata->fmt);
return -EINVAL;
}
drvdata->fmt = fmt;
return 0;
}
static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask,
unsigned int rx_mask,
int slots, int slot_width)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
unsigned int cap;
switch (slots) {
case 1:
cap = 0x01;
break;
case 2:
cap = 0x03;
break;
case 8:
cap = 0xFF;
break;
case 16:
cap = 0xFFFF;
break;
default:
dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n",
__func__, slots);
return -EINVAL;
}
drvdata->slots = slots;
if (!(slot_width == 16)) {
dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n",
__func__, slot_width);
return -EINVAL;
}
drvdata->slot_width = slot_width;
drvdata->tx_mask = tx_mask & cap;
drvdata->rx_mask = rx_mask & cap;
return 0;
}
static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n",
__func__, dai->id, clk_id, freq);
switch (clk_id) {
case UX500_MSP_MASTER_CLOCK:
drvdata->master_clk = freq;
break;
default:
dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n",
__func__, dai->id, clk_id);
return -EINVAL;
}
return 0;
}
static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
int ret = 0;
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n",
__func__, dai->id, snd_pcm_stream_str(substream),
(int)drvdata->msp->id, cmd);
ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream);
return ret;
}
static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx;
drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx;
dai->playback_dma_data = &drvdata->playback_dma_data;
dai->capture_dma_data = &drvdata->capture_dma_data;
drvdata->playback_dma_data.data_size = drvdata->slot_width;
drvdata->capture_dma_data.data_size = drvdata->slot_width;
return 0;
}
static struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
{
.set_sysclk = ux500_msp_dai_set_dai_sysclk,
.set_fmt = ux500_msp_dai_set_dai_fmt,
.set_tdm_slot = ux500_msp_dai_set_tdm_slot,
.startup = ux500_msp_dai_startup,
.shutdown = ux500_msp_dai_shutdown,
.prepare = ux500_msp_dai_prepare,
.trigger = ux500_msp_dai_trigger,
.hw_params = ux500_msp_dai_hw_params,
}
};
static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
{
.name = "ux500-msp-i2s.0",
.probe = ux500_msp_dai_probe,
.id = 0,
.suspend = NULL,
.resume = NULL,
.playback = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.capture = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.ops = ux500_msp_dai_ops,
},
{
.name = "ux500-msp-i2s.1",
.probe = ux500_msp_dai_probe,
.id = 1,
.suspend = NULL,
.resume = NULL,
.playback = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.capture = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.ops = ux500_msp_dai_ops,
},
{
.name = "ux500-msp-i2s.2",
.id = 2,
.probe = ux500_msp_dai_probe,
.suspend = NULL,
.resume = NULL,
.playback = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.capture = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.ops = ux500_msp_dai_ops,
},
{
.name = "ux500-msp-i2s.3",
.probe = ux500_msp_dai_probe,
.id = 3,
.suspend = NULL,
.resume = NULL,
.playback = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.capture = {
.channels_min = UX500_MSP_MIN_CHANNELS,
.channels_max = UX500_MSP_MAX_CHANNELS,
.rates = UX500_I2S_RATES,
.formats = UX500_I2S_FORMATS,
},
.ops = ux500_msp_dai_ops,
},
};
static int __devinit ux500_msp_drv_probe(struct platform_device *pdev)
{
struct ux500_msp_i2s_drvdata *drvdata;
int ret = 0;
dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__,
pdev->name);
drvdata = devm_kzalloc(&pdev->dev,
sizeof(struct ux500_msp_i2s_drvdata),
GFP_KERNEL);
drvdata->fmt = 0;
drvdata->slots = 1;
drvdata->tx_mask = 0x01;
drvdata->rx_mask = 0x01;
drvdata->slot_width = 16;
drvdata->master_clk = MSP_INPUT_FREQ_APB;
drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape");
if (IS_ERR(drvdata->reg_vape)) {
ret = (int)PTR_ERR(drvdata->reg_vape);
dev_err(&pdev->dev,
"%s: ERROR: Failed to get Vape supply (%d)!\n",
__func__, ret);
return ret;
}
prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
drvdata->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(drvdata->clk)) {
ret = (int)PTR_ERR(drvdata->clk);
dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n",
__func__, ret);
goto err_clk;
}
ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp,
pdev->dev.platform_data);
if (!drvdata->msp) {
dev_err(&pdev->dev,
"%s: ERROR: Failed to init MSP-struct (%d)!",
__func__, ret);
goto err_init_msp;
}
dev_set_drvdata(&pdev->dev, drvdata);
ret = snd_soc_register_dai(&pdev->dev,
&ux500_msp_dai_drv[drvdata->msp->id]);
if (ret < 0) {
dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
__func__, drvdata->msp->id);
goto err_init_msp;
}
return 0;
err_init_msp:
clk_put(drvdata->clk);
err_clk:
devm_regulator_put(drvdata->reg_vape);
return ret;
}
static int __devexit ux500_msp_drv_remove(struct platform_device *pdev)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
devm_regulator_put(drvdata->reg_vape);
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
clk_put(drvdata->clk);
ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
return 0;
}
static struct platform_driver msp_i2s_driver = {
.driver = {
.name = "ux500-msp-i2s",
.owner = THIS_MODULE,
},
.probe = ux500_msp_drv_probe,
.remove = ux500_msp_drv_remove,
};
module_platform_driver(msp_i2s_driver);
MODULE_LICENSE("GPLv2");

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) ST-Ericsson SA 2012
*
* 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.
*/
#ifndef UX500_msp_dai_H
#define UX500_msp_dai_H
#include <linux/types.h>
#include <linux/spinlock.h>
#include "ux500_msp_i2s.h"
#define UX500_NBR_OF_DAI 4
#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
#define FRAME_PER_SINGLE_SLOT_8_KHZ 31
#define FRAME_PER_SINGLE_SLOT_16_KHZ 124
#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63
#define FRAME_PER_SINGLE_SLOT_48_KHZ 49
#define FRAME_PER_2_SLOTS 31
#define FRAME_PER_8_SLOTS 138
#define FRAME_PER_16_SLOTS 277
#ifndef CONFIG_SND_SOC_UX500_AB5500
#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000
#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ
#else
#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000
#define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2)
#endif
#define UX500_MSP_MIN_CHANNELS 1
#define UX500_MSP_MAX_CHANNELS 8
#define PLAYBACK_CONFIGURED 1
#define CAPTURE_CONFIGURED 2
enum ux500_msp_clock_id {
UX500_MSP_MASTER_CLOCK,
};
struct ux500_msp_i2s_drvdata {
struct ux500_msp *msp;
struct regulator *reg_vape;
struct ux500_msp_dma_params playback_dma_data;
struct ux500_msp_dma_params capture_dma_data;
unsigned int fmt;
unsigned int tx_mask;
unsigned int rx_mask;
int slots;
int slot_width;
u8 configured;
int data_delay;
/* Clocks */
unsigned int master_clk;
struct clk *clk;
/* Regulators */
int vape_opp_constraint;
};
int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay);
#endif

View File

@ -0,0 +1,742 @@
/*
* Copyright (C) ST-Ericsson SA 2012
*
* Author: Ola Lilja <ola.o.lilja@stericsson.com>,
* Roger Nilsson <roger.xr.nilsson@stericsson.com>,
* Sandeep Kaushik <sandeep.kaushik@st.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 <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <mach/hardware.h>
#include <mach/board-mop500-msp.h>
#include <sound/soc.h>
#include "ux500_msp_i2s.h"
/* Protocol desciptors */
static const struct msp_protdesc prot_descs[] = {
{ /* I2S */
MSP_SINGLE_PHASE,
MSP_SINGLE_PHASE,
MSP_PHASE2_START_MODE_IMEDIATE,
MSP_PHASE2_START_MODE_IMEDIATE,
MSP_BTF_MS_BIT_FIRST,
MSP_BTF_MS_BIT_FIRST,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_ELEM_LEN_32,
MSP_ELEM_LEN_32,
MSP_ELEM_LEN_32,
MSP_ELEM_LEN_32,
MSP_DELAY_1,
MSP_DELAY_1,
MSP_RISING_EDGE,
MSP_FALLING_EDGE,
MSP_FSYNC_POL_ACT_LO,
MSP_FSYNC_POL_ACT_LO,
MSP_SWAP_NONE,
MSP_SWAP_NONE,
MSP_COMPRESS_MODE_LINEAR,
MSP_EXPAND_MODE_LINEAR,
MSP_FSYNC_IGNORE,
31,
15,
32,
}, { /* PCM */
MSP_DUAL_PHASE,
MSP_DUAL_PHASE,
MSP_PHASE2_START_MODE_FSYNC,
MSP_PHASE2_START_MODE_FSYNC,
MSP_BTF_MS_BIT_FIRST,
MSP_BTF_MS_BIT_FIRST,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_ELEM_LEN_16,
MSP_ELEM_LEN_16,
MSP_ELEM_LEN_16,
MSP_ELEM_LEN_16,
MSP_DELAY_0,
MSP_DELAY_0,
MSP_RISING_EDGE,
MSP_FALLING_EDGE,
MSP_FSYNC_POL_ACT_HI,
MSP_FSYNC_POL_ACT_HI,
MSP_SWAP_NONE,
MSP_SWAP_NONE,
MSP_COMPRESS_MODE_LINEAR,
MSP_EXPAND_MODE_LINEAR,
MSP_FSYNC_IGNORE,
255,
0,
256,
}, { /* Companded PCM */
MSP_SINGLE_PHASE,
MSP_SINGLE_PHASE,
MSP_PHASE2_START_MODE_FSYNC,
MSP_PHASE2_START_MODE_FSYNC,
MSP_BTF_MS_BIT_FIRST,
MSP_BTF_MS_BIT_FIRST,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_FRAME_LEN_1,
MSP_ELEM_LEN_8,
MSP_ELEM_LEN_8,
MSP_ELEM_LEN_8,
MSP_ELEM_LEN_8,
MSP_DELAY_0,
MSP_DELAY_0,
MSP_RISING_EDGE,
MSP_RISING_EDGE,
MSP_FSYNC_POL_ACT_HI,
MSP_FSYNC_POL_ACT_HI,
MSP_SWAP_NONE,
MSP_SWAP_NONE,
MSP_COMPRESS_MODE_LINEAR,
MSP_EXPAND_MODE_LINEAR,
MSP_FSYNC_IGNORE,
255,
0,
256,
},
};
static void set_prot_desc_tx(struct ux500_msp *msp,
struct msp_protdesc *protdesc,
enum msp_data_size data_size)
{
u32 temp_reg = 0;
temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode);
temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode);
temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1);
temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2);
if (msp->def_elem_len) {
temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1);
temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2);
} else {
temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
}
temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay);
temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order);
temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol);
temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap);
temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode);
temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
writel(temp_reg, msp->registers + MSP_TCF);
}
static void set_prot_desc_rx(struct ux500_msp *msp,
struct msp_protdesc *protdesc,
enum msp_data_size data_size)
{
u32 temp_reg = 0;
temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode);
temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode);
temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1);
temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2);
if (msp->def_elem_len) {
temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1);
temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2);
} else {
temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
}
temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay);
temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order);
temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol);
temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap);
temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode);
temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
writel(temp_reg, msp->registers + MSP_RCF);
}
static int configure_protocol(struct ux500_msp *msp,
struct ux500_msp_config *config)
{
struct msp_protdesc *protdesc;
enum msp_data_size data_size;
u32 temp_reg = 0;
data_size = config->data_size;
msp->def_elem_len = config->def_elem_len;
if (config->default_protdesc == 1) {
if (config->protocol >= MSP_INVALID_PROTOCOL) {
dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n",
__func__);
return -EINVAL;
}
protdesc =
(struct msp_protdesc *)&prot_descs[config->protocol];
} else {
protdesc = (struct msp_protdesc *)&config->protdesc;
}
if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
dev_err(msp->dev,
"%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
__func__, data_size);
return -EINVAL;
}
if (config->direction & MSP_DIR_TX)
set_prot_desc_tx(msp, protdesc, data_size);
if (config->direction & MSP_DIR_RX)
set_prot_desc_rx(msp, protdesc, data_size);
/* The code below should not be separated. */
temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_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(protdesc->rx_clk_pol);
writel(temp_reg, msp->registers + MSP_GCR);
return 0;
}
static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
{
u32 reg_val_GCR;
u32 frame_per = 0;
u32 sck_div = 0;
u32 frame_width = 0;
u32 temp_reg = 0;
struct msp_protdesc *protdesc = NULL;
reg_val_GCR = readl(msp->registers + MSP_GCR);
writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
if (config->default_protdesc)
protdesc =
(struct msp_protdesc *)&prot_descs[config->protocol];
else
protdesc = (struct msp_protdesc *)&config->protdesc;
switch (config->protocol) {
case MSP_PCM_PROTOCOL:
case MSP_PCM_COMPAND_PROTOCOL:
frame_width = protdesc->frame_width;
sck_div = config->f_inputclk / (config->frame_freq *
(protdesc->clocks_per_frame));
frame_per = protdesc->frame_period;
break;
case MSP_I2S_PROTOCOL:
frame_width = protdesc->frame_width;
sck_div = config->f_inputclk / (config->frame_freq *
(protdesc->clocks_per_frame));
frame_per = protdesc->frame_period;
break;
default:
dev_err(msp->dev, "%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);
msp->f_bitclk = (config->f_inputclk)/(sck_div + 1);
/* Enable bit-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 configure_multichannel(struct ux500_msp *msp,
struct ux500_msp_config *config)
{
struct msp_protdesc *protdesc;
struct msp_multichannel_config *mcfg;
u32 reg_val_MCR;
if (config->default_protdesc == 1) {
if (config->protocol >= MSP_INVALID_PROTOCOL) {
dev_err(msp->dev,
"%s: ERROR: Invalid protocol (%d)!\n",
__func__, config->protocol);
return -EINVAL;
}
protdesc = (struct msp_protdesc *)
&prot_descs[config->protocol];
} else {
protdesc = (struct msp_protdesc *)&config->protdesc;
}
mcfg = &config->multichannel_config;
if (mcfg->tx_multichannel_enable) {
if (protdesc->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 {
dev_err(msp->dev,
"%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
__func__, protdesc->tx_phase_mode);
return -EINVAL;
}
}
if (mcfg->rx_multichannel_enable) {
if (protdesc->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 {
dev_err(msp->dev,
"%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
__func__, protdesc->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;
}
static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
{
int status = 0;
u32 reg_val_DMACR, reg_val_GCR;
/* Check msp state whether in RUN or CONFIGURED Mode */
if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) {
status = msp->plat_init();
if (status) {
dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n",
__func__, status);
return status;
}
}
/* Configure msp with protocol dependent settings */
configure_protocol(msp, config);
setup_bitclk(msp, config);
if (config->multichannel_configured == 1) {
status = configure_multichannel(msp, config);
if (status)
dev_warn(msp->dev,
"%s: WARN: configure_multichannel failed (%d)!\n",
__func__, status);
}
/* Make sure the correct DMA-directions are configured */
if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) {
dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!",
__func__);
return -EINVAL;
}
if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) {
dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
__func__);
return -EINVAL;
}
reg_val_DMACR = readl(msp->registers + MSP_DMACR);
if (config->direction & MSP_DIR_RX)
reg_val_DMACR |= RX_DMA_ENABLE;
if (config->direction & MSP_DIR_TX)
reg_val_DMACR |= TX_DMA_ENABLE;
writel(reg_val_DMACR, msp->registers + MSP_DMACR);
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 ux500_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 ux500_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 *msp,
struct ux500_msp_config *config)
{
u32 old_reg, new_reg, mask;
int res;
unsigned int tx_sel, rx_sel, tx_busy, rx_busy;
if (in_interrupt()) {
dev_err(msp->dev,
"%s: ERROR: Open called in interrupt context!\n",
__func__);
return -1;
}
tx_sel = (config->direction & MSP_DIR_TX) > 0;
rx_sel = (config->direction & MSP_DIR_RX) > 0;
if (!tx_sel && !rx_sel) {
dev_err(msp->dev, "%s: Error: No direction selected!\n",
__func__);
return -EINVAL;
}
tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0;
rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0;
if (tx_busy && tx_sel) {
dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__);
return -EBUSY;
}
if (rx_busy && rx_sel) {
dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__);
return -EBUSY;
}
msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0);
/* First do the global config register */
mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
TX_FSYNC_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_clk_sel | config->rx_clk_sel |
config->rx_fsync_pol | config->tx_fsync_pol |
config->rx_fsync_sel | config->tx_fsync_sel |
config->rx_fifo_config | config->tx_fifo_config |
config->srg_clk_sel | config->loopback_enable |
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);
res = enable_msp(msp, config);
if (res < 0) {
dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n",
__func__, res);
return -EBUSY;
}
if (config->loopback_enable & 0x80)
msp->loopback_enable = 1;
/* Flush FIFOs */
flush_fifo_tx(msp);
flush_fifo_rx(msp);
msp->msp_state = MSP_STATE_CONFIGURED;
return 0;
}
static void disable_msp_rx(struct ux500_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 &
~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT),
msp->registers + MSP_IMSC);
msp->dir_busy &= ~MSP_DIR_RX;
}
static void disable_msp_tx(struct ux500_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 &
~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT),
msp->registers + MSP_IMSC);
msp->dir_busy &= ~MSP_DIR_TX;
}
static int disable_msp(struct ux500_msp *msp, unsigned int dir)
{
u32 reg_val_GCR;
int status = 0;
unsigned int disable_tx, disable_rx;
reg_val_GCR = readl(msp->registers + MSP_GCR);
disable_tx = dir & MSP_DIR_TX;
disable_rx = dir & MSP_DIR_TX;
if (disable_tx && disable_rx) {
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);
disable_msp_tx(msp);
disable_msp_rx(msp);
} else if (disable_tx)
disable_msp_tx(msp);
else if (disable_rx)
disable_msp_rx(msp);
return status;
}
int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
{
u32 reg_val_GCR, enable_bit;
if (msp->msp_state == MSP_STATE_IDLE) {
dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n",
__func__);
return -EINVAL;
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
enable_bit = TX_ENABLE;
else
enable_bit = RX_ENABLE;
reg_val_GCR = readl(msp->registers + MSP_GCR);
writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
disable_msp_tx(msp);
else
disable_msp_rx(msp);
break;
default:
return -EINVAL;
break;
}
return 0;
}
int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
{
int status = 0;
dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
status = disable_msp(msp, dir);
if (msp->dir_busy == 0) {
/* disable sample rate and frame generators */
msp->msp_state = MSP_STATE_IDLE;
writel((readl(msp->registers + MSP_GCR) &
(~(FRAME_GEN_ENABLE | SRG_ENABLE))),
msp->registers + MSP_GCR);
if (msp->plat_exit)
status = msp->plat_exit();
if (status)
dev_warn(msp->dev,
"%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
__func__, status);
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_init_msp(struct platform_device *pdev,
struct ux500_msp **msp_p,
struct msp_i2s_platform_data *platform_data)
{
int ret = 0;
struct resource *res = NULL;
struct i2s_controller *i2s_cont;
struct ux500_msp *msp;
dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
pdev->name, platform_data->id);
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
msp = *msp_p;
msp->id = platform_data->id;
msp->dev = &pdev->dev;
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;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
__func__);
ret = -ENOMEM;
goto err_res;
}
msp->registers = ioremap(res->start, (res->end - res->start + 1));
if (msp->registers == NULL) {
dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
ret = -ENOMEM;
goto err_res;
}
msp->msp_state = MSP_STATE_IDLE;
msp->loopback_enable = 0;
/* I2S-controller is allocated and added in I2S controller class. */
i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL);
if (!i2s_cont) {
dev_err(&pdev->dev,
"%s: ERROR: Failed to allocate I2S-controller!\n",
__func__);
goto err_i2s_cont;
}
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);
dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name);
msp->i2s_cont = i2s_cont;
return 0;
err_i2s_cont:
iounmap(msp->registers);
err_res:
devm_kfree(&pdev->dev, msp);
return ret;
}
void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
struct ux500_msp *msp)
{
dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
device_unregister(&msp->i2s_cont->dev);
devm_kfree(&pdev->dev, msp->i2s_cont);
iounmap(msp->registers);
devm_kfree(&pdev->dev, msp);
}
MODULE_LICENSE("GPLv2");

View File

@ -0,0 +1,553 @@
/*
* Copyright (C) ST-Ericsson SA 2012
*
* Author: Ola Lilja <ola.o.lilja@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.
*/
#ifndef UX500_MSP_I2S_H
#define UX500_MSP_I2S_H
#include <linux/platform_device.h>
#include <mach/board-mop500-msp.h>
#define MSP_INPUT_FREQ_APB 48000000
/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono),
* 32 bits accesses (stereo).
***/
enum msp_stereo_mode {
MSP_MONO,
MSP_STEREO
};
/* Direction (Transmit/Receive mode) */
enum msp_direction {
MSP_TX = 1,
MSP_RX = 2
};
/* Transmit and receive configuration register */
#define MSP_BIG_ENDIAN 0x00000000
#define MSP_LITTLE_ENDIAN 0x00001000
#define MSP_UNEXPECTED_FS_ABORT 0x00000000
#define MSP_UNEXPECTED_FS_IGNORE 0x00008000
#define MSP_NON_MODE_BIT_MASK 0x00009000
/* Global configuration register */
#define RX_ENABLE 0x00000001
#define RX_FIFO_ENABLE 0x00000002
#define RX_SYNC_SRG 0x00000010
#define RX_CLK_POL_RISING 0x00000020
#define RX_CLK_SEL_SRG 0x00000040
#define TX_ENABLE 0x00000100
#define TX_FIFO_ENABLE 0x00000200
#define TX_SYNC_SRG_PROG 0x00001800
#define TX_SYNC_SRG_AUTO 0x00001000
#define TX_CLK_POL_RISING 0x00002000
#define TX_CLK_SEL_SRG 0x00004000
#define TX_EXTRA_DELAY_ENABLE 0x00008000
#define SRG_ENABLE 0x00010000
#define FRAME_GEN_ENABLE 0x00100000
#define SRG_CLK_SEL_APB 0x00000000
#define RX_FIFO_SYNC_HI 0x00000000
#define TX_FIFO_SYNC_HI 0x00000000
#define SPI_CLK_MODE_NORMAL 0x00000000
#define MSP_FRAME_SIZE_AUTO -1
#define MSP_DR 0x00
#define MSP_GCR 0x04
#define MSP_TCF 0x08
#define MSP_RCF 0x0c
#define MSP_SRG 0x10
#define MSP_FLR 0x14
#define MSP_DMACR 0x18
#define MSP_IMSC 0x20
#define MSP_RIS 0x24
#define MSP_MIS 0x28
#define MSP_ICR 0x2c
#define MSP_MCR 0x30
#define MSP_RCV 0x34
#define MSP_RCM 0x38
#define MSP_TCE0 0x40
#define MSP_TCE1 0x44
#define MSP_TCE2 0x48
#define MSP_TCE3 0x4c
#define MSP_RCE0 0x60
#define MSP_RCE1 0x64
#define MSP_RCE2 0x68
#define MSP_RCE3 0x6c
#define MSP_IODLY 0x70
#define MSP_ITCR 0x80
#define MSP_ITIP 0x84
#define MSP_ITOP 0x88
#define MSP_TSTDR 0x8c
#define MSP_PID0 0xfe0
#define MSP_PID1 0xfe4
#define MSP_PID2 0xfe8
#define MSP_PID3 0xfec
#define MSP_CID0 0xff0
#define MSP_CID1 0xff4
#define MSP_CID2 0xff8
#define MSP_CID3 0xffc
/* Protocol dependant parameters list */
#define RX_ENABLE_MASK BIT(0)
#define RX_FIFO_ENABLE_MASK BIT(1)
#define RX_FSYNC_MASK BIT(2)
#define DIRECT_COMPANDING_MASK BIT(3)
#define RX_SYNC_SEL_MASK BIT(4)
#define RX_CLK_POL_MASK BIT(5)
#define RX_CLK_SEL_MASK BIT(6)
#define LOOPBACK_MASK BIT(7)
#define TX_ENABLE_MASK BIT(8)
#define TX_FIFO_ENABLE_MASK BIT(9)
#define TX_FSYNC_MASK BIT(10)
#define TX_MSP_TDR_TSR BIT(11)
#define TX_SYNC_SEL_MASK (BIT(12) | BIT(11))
#define TX_CLK_POL_MASK BIT(13)
#define TX_CLK_SEL_MASK BIT(14)
#define TX_EXTRA_DELAY_MASK BIT(15)
#define SRG_ENABLE_MASK BIT(16)
#define SRG_CLK_POL_MASK BIT(17)
#define SRG_CLK_SEL_MASK (BIT(19) | BIT(18))
#define FRAME_GEN_EN_MASK BIT(20)
#define SPI_CLK_MODE_MASK (BIT(22) | BIT(21))
#define SPI_BURST_MODE_MASK BIT(23)
#define RXEN_SHIFT 0
#define RFFEN_SHIFT 1
#define RFSPOL_SHIFT 2
#define DCM_SHIFT 3
#define RFSSEL_SHIFT 4
#define RCKPOL_SHIFT 5
#define RCKSEL_SHIFT 6
#define LBM_SHIFT 7
#define TXEN_SHIFT 8
#define TFFEN_SHIFT 9
#define TFSPOL_SHIFT 10
#define TFSSEL_SHIFT 11
#define TCKPOL_SHIFT 13
#define TCKSEL_SHIFT 14
#define TXDDL_SHIFT 15
#define SGEN_SHIFT 16
#define SCKPOL_SHIFT 17
#define SCKSEL_SHIFT 18
#define FGEN_SHIFT 20
#define SPICKM_SHIFT 21
#define TBSWAP_SHIFT 28
#define RCKPOL_MASK BIT(0)
#define TCKPOL_MASK BIT(0)
#define SPICKM_MASK (BIT(1) | BIT(0))
#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
#define P1ELEN_SHIFT 0
#define P1FLEN_SHIFT 3
#define DTYP_SHIFT 10
#define ENDN_SHIFT 12
#define DDLY_SHIFT 13
#define FSIG_SHIFT 15
#define P2ELEN_SHIFT 16
#define P2FLEN_SHIFT 19
#define P2SM_SHIFT 26
#define P2EN_SHIFT 27
#define FSYNC_SHIFT 15
#define P1ELEN_MASK 0x00000007
#define P2ELEN_MASK 0x00070000
#define P1FLEN_MASK 0x00000378
#define P2FLEN_MASK 0x03780000
#define DDLY_MASK 0x00003000
#define DTYP_MASK 0x00000600
#define P2SM_MASK 0x04000000
#define P2EN_MASK 0x08000000
#define ENDN_MASK 0x00001000
#define TFSPOL_MASK 0x00000400
#define TBSWAP_MASK 0x30000000
#define COMPANDING_MODE_MASK 0x00000c00
#define FSYNC_MASK 0x00008000
#define MSP_P1_ELEM_LEN_BITS(n) (n & P1ELEN_MASK)
#define MSP_P2_ELEM_LEN_BITS(n) (((n) << P2ELEN_SHIFT) & P2ELEN_MASK)
#define MSP_P1_FRAME_LEN_BITS(n) (((n) << P1FLEN_SHIFT) & P1FLEN_MASK)
#define MSP_P2_FRAME_LEN_BITS(n) (((n) << P2FLEN_SHIFT) & P2FLEN_MASK)
#define MSP_DATA_DELAY_BITS(n) (((n) << DDLY_SHIFT) & DDLY_MASK)
#define MSP_DATA_TYPE_BITS(n) (((n) << DTYP_SHIFT) & DTYP_MASK)
#define MSP_P2_START_MODE_BIT(n) ((n << P2SM_SHIFT) & P2SM_MASK)
#define MSP_P2_ENABLE_BIT(n) ((n << P2EN_SHIFT) & P2EN_MASK)
#define MSP_SET_ENDIANNES_BIT(n) ((n << ENDN_SHIFT) & ENDN_MASK)
#define MSP_FSYNC_POL(n) ((n << TFSPOL_SHIFT) & TFSPOL_MASK)
#define MSP_DATA_WORD_SWAP(n) ((n << TBSWAP_SHIFT) & TBSWAP_MASK)
#define MSP_SET_COMPANDING_MODE(n) ((n << DTYP_SHIFT) & \
COMPANDING_MODE_MASK)
#define MSP_SET_FSYNC_IGNORE(n) ((n << FSYNC_SHIFT) & FSYNC_MASK)
/* Flag register */
#define RX_BUSY BIT(0)
#define RX_FIFO_EMPTY BIT(1)
#define RX_FIFO_FULL BIT(2)
#define TX_BUSY BIT(3)
#define TX_FIFO_EMPTY BIT(4)
#define TX_FIFO_FULL BIT(5)
#define RBUSY_SHIFT 0
#define RFE_SHIFT 1
#define RFU_SHIFT 2
#define TBUSY_SHIFT 3
#define TFE_SHIFT 4
#define TFU_SHIFT 5
/* Multichannel control register */
#define RMCEN_SHIFT 0
#define RMCSF_SHIFT 1
#define RCMPM_SHIFT 3
#define TMCEN_SHIFT 5
#define TNCSF_SHIFT 6
/* Sample rate generator register */
#define SCKDIV_SHIFT 0
#define FRWID_SHIFT 10
#define FRPER_SHIFT 16
#define SCK_DIV_MASK 0x0000003FF
#define FRAME_WIDTH_BITS(n) (((n) << FRWID_SHIFT) & 0x0000FC00)
#define FRAME_PERIOD_BITS(n) (((n) << FRPER_SHIFT) & 0x1FFF0000)
/* DMA controller register */
#define RX_DMA_ENABLE BIT(0)
#define TX_DMA_ENABLE BIT(1)
#define RDMAE_SHIFT 0
#define TDMAE_SHIFT 1
/* Interrupt Register */
#define RX_SERVICE_INT BIT(0)
#define RX_OVERRUN_ERROR_INT BIT(1)
#define RX_FSYNC_ERR_INT BIT(2)
#define RX_FSYNC_INT BIT(3)
#define TX_SERVICE_INT BIT(4)
#define TX_UNDERRUN_ERR_INT BIT(5)
#define TX_FSYNC_ERR_INT BIT(6)
#define TX_FSYNC_INT BIT(7)
#define ALL_INT 0x000000ff
/* MSP test control register */
#define MSP_ITCR_ITEN BIT(0)
#define MSP_ITCR_TESTFIFO BIT(1)
#define RMCEN_BIT 0
#define RMCSF_BIT 1
#define RCMPM_BIT 3
#define TMCEN_BIT 5
#define TNCSF_BIT 6
/* Single or dual phase mode */
enum msp_phase_mode {
MSP_SINGLE_PHASE,
MSP_DUAL_PHASE
};
/* Frame length */
enum msp_frame_length {
MSP_FRAME_LEN_1 = 0,
MSP_FRAME_LEN_2 = 1,
MSP_FRAME_LEN_4 = 3,
MSP_FRAME_LEN_8 = 7,
MSP_FRAME_LEN_12 = 11,
MSP_FRAME_LEN_16 = 15,
MSP_FRAME_LEN_20 = 19,
MSP_FRAME_LEN_32 = 31,
MSP_FRAME_LEN_48 = 47,
MSP_FRAME_LEN_64 = 63
};
/* Element length */
enum msp_elem_length {
MSP_ELEM_LEN_8 = 0,
MSP_ELEM_LEN_10 = 1,
MSP_ELEM_LEN_12 = 2,
MSP_ELEM_LEN_14 = 3,
MSP_ELEM_LEN_16 = 4,
MSP_ELEM_LEN_20 = 5,
MSP_ELEM_LEN_24 = 6,
MSP_ELEM_LEN_32 = 7
};
enum msp_data_xfer_width {
MSP_DATA_TRANSFER_WIDTH_BYTE,
MSP_DATA_TRANSFER_WIDTH_HALFWORD,
MSP_DATA_TRANSFER_WIDTH_WORD
};
enum msp_frame_sync {
MSP_FSYNC_UNIGNORE = 0,
MSP_FSYNC_IGNORE = 1,
};
enum msp_phase2_start_mode {
MSP_PHASE2_START_MODE_IMEDIATE,
MSP_PHASE2_START_MODE_FSYNC
};
enum msp_btf {
MSP_BTF_MS_BIT_FIRST = 0,
MSP_BTF_LS_BIT_FIRST = 1
};
enum msp_fsync_pol {
MSP_FSYNC_POL_ACT_HI = 0,
MSP_FSYNC_POL_ACT_LO = 1
};
/* Data delay (in bit clock cycles) */
enum msp_delay {
MSP_DELAY_0 = 0,
MSP_DELAY_1 = 1,
MSP_DELAY_2 = 2,
MSP_DELAY_3 = 3
};
/* Configurations of clocks (transmit, receive or sample rate generator) */
enum msp_edge {
MSP_FALLING_EDGE = 0,
MSP_RISING_EDGE = 1,
};
enum msp_hws {
MSP_SWAP_NONE = 0,
MSP_SWAP_BYTE_PER_WORD = 1,
MSP_SWAP_BYTE_PER_HALF_WORD = 2,
MSP_SWAP_HALF_WORD_PER_WORD = 3
};
enum msp_compress_mode {
MSP_COMPRESS_MODE_LINEAR = 0,
MSP_COMPRESS_MODE_MU_LAW = 2,
MSP_COMPRESS_MODE_A_LAW = 3
};
enum msp_spi_burst_mode {
MSP_SPI_BURST_MODE_DISABLE = 0,
MSP_SPI_BURST_MODE_ENABLE = 1
};
enum msp_expand_mode {
MSP_EXPAND_MODE_LINEAR = 0,
MSP_EXPAND_MODE_LINEAR_SIGNED = 1,
MSP_EXPAND_MODE_MU_LAW = 2,
MSP_EXPAND_MODE_A_LAW = 3
};
#define MSP_FRAME_PERIOD_IN_MONO_MODE 256
#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32
#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16
enum msp_protocol {
MSP_I2S_PROTOCOL,
MSP_PCM_PROTOCOL,
MSP_PCM_COMPAND_PROTOCOL,
MSP_INVALID_PROTOCOL
};
/*
* No of registers to backup during
* suspend resume
*/
#define MAX_MSP_BACKUP_REGS 36
enum enum_i2s_controller {
MSP_0_I2S_CONTROLLER = 0,
MSP_1_I2S_CONTROLLER,
MSP_2_I2S_CONTROLLER,
MSP_3_I2S_CONTROLLER,
};
enum i2s_direction_t {
MSP_DIR_TX = 0x01,
MSP_DIR_RX = 0x02,
};
enum msp_data_size {
MSP_DATA_BITS_DEFAULT = -1,
MSP_DATA_BITS_8 = 0x00,
MSP_DATA_BITS_10,
MSP_DATA_BITS_12,
MSP_DATA_BITS_14,
MSP_DATA_BITS_16,
MSP_DATA_BITS_20,
MSP_DATA_BITS_24,
MSP_DATA_BITS_32,
};
enum msp_state {
MSP_STATE_IDLE = 0,
MSP_STATE_CONFIGURED = 1,
MSP_STATE_RUNNING = 2,
};
enum msp_rx_comparison_enable_mode {
MSP_COMPARISON_DISABLED = 0,
MSP_COMPARISON_NONEQUAL_ENABLED = 2,
MSP_COMPARISON_EQUAL_ENABLED = 3
};
struct msp_multichannel_config {
bool rx_multichannel_enable;
bool tx_multichannel_enable;
enum msp_rx_comparison_enable_mode rx_comparison_enable_mode;
u8 padding;
u32 comparison_value;
u32 comparison_mask;
u32 rx_channel_0_enable;
u32 rx_channel_1_enable;
u32 rx_channel_2_enable;
u32 rx_channel_3_enable;
u32 tx_channel_0_enable;
u32 tx_channel_1_enable;
u32 tx_channel_2_enable;
u32 tx_channel_3_enable;
};
struct msp_protdesc {
u32 rx_phase_mode;
u32 tx_phase_mode;
u32 rx_phase2_start_mode;
u32 tx_phase2_start_mode;
u32 rx_byte_order;
u32 tx_byte_order;
u32 rx_frame_len_1;
u32 rx_frame_len_2;
u32 tx_frame_len_1;
u32 tx_frame_len_2;
u32 rx_elem_len_1;
u32 rx_elem_len_2;
u32 tx_elem_len_1;
u32 tx_elem_len_2;
u32 rx_data_delay;
u32 tx_data_delay;
u32 rx_clk_pol;
u32 tx_clk_pol;
u32 rx_fsync_pol;
u32 tx_fsync_pol;
u32 rx_half_word_swap;
u32 tx_half_word_swap;
u32 compression_mode;
u32 expansion_mode;
u32 frame_sync_ignore;
u32 frame_period;
u32 frame_width;
u32 clocks_per_frame;
};
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;
bool cyclic_dma;
dma_addr_t buf_addr;
size_t buf_len;
size_t period_len;
};
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];
};
struct ux500_msp_config {
unsigned int f_inputclk;
unsigned int rx_clk_sel;
unsigned int tx_clk_sel;
unsigned int srg_clk_sel;
unsigned int rx_fsync_pol;
unsigned int tx_fsync_pol;
unsigned int rx_fsync_sel;
unsigned int tx_fsync_sel;
unsigned int rx_fifo_config;
unsigned int tx_fifo_config;
unsigned int spi_clk_mode;
unsigned int spi_burst_mode;
unsigned int loopback_enable;
unsigned int tx_data_enable;
unsigned int default_protdesc;
struct msp_protdesc protdesc;
int multichannel_configured;
struct msp_multichannel_config multichannel_config;
unsigned int direction;
unsigned int protocol;
unsigned int frame_freq;
unsigned int frame_size;
enum msp_data_size data_size;
unsigned int def_elem_len;
unsigned int iodelay;
void (*handler) (void *data);
void *tx_callback_data;
void *rx_callback_data;
};
struct ux500_msp {
enum enum_i2s_controller id;
void __iomem *registers;
struct device *dev;
struct i2s_controller *i2s_cont;
struct stedma40_chan_cfg *dma_cfg_rx;
struct stedma40_chan_cfg *dma_cfg_tx;
struct dma_chan *tx_pipeid;
struct dma_chan *rx_pipeid;
enum msp_state msp_state;
int (*transfer) (struct ux500_msp *msp, struct i2s_message *message);
int (*plat_init) (void);
int (*plat_exit) (void);
struct timer_list notify_timer;
int def_elem_len;
unsigned int dir_busy;
int loopback_enable;
u32 backup_regs[MAX_MSP_BACKUP_REGS];
unsigned int f_bitclk;
};
struct ux500_msp_dma_params {
unsigned int data_size;
struct stedma40_chan_cfg *dma_cfg;
};
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
struct ux500_msp **msp_p,
struct msp_i2s_platform_data *platform_data);
void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
struct ux500_msp *msp);
int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config);
int ux500_msp_i2s_close(struct ux500_msp *msp,
unsigned int dir);
int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd,
int direction);
#endif