Commit 165879a9 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/fsl_esai', 'asoc/topic/fsl_ssi',...

Merge remote-tracking branches 'asoc/topic/fsl_esai', 'asoc/topic/fsl_ssi', 'asoc/topic/fsl_utils', 'asoc/topic/generic-dmaengine' and 'asoc/topic/gtm601' into asoc-next
...@@ -118,6 +118,8 @@ void snd_dmaengine_pcm_set_config_from_dai_data( ...@@ -118,6 +118,8 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
* PCM substream. Will be called from the PCM drivers hwparams callback. * PCM substream. Will be called from the PCM drivers hwparams callback.
* @compat_request_channel: Callback to request a DMA channel for platforms * @compat_request_channel: Callback to request a DMA channel for platforms
* which do not use devicetree. * which do not use devicetree.
* @process: Callback used to apply processing on samples transferred from/to
* user space.
* @compat_filter_fn: Will be used as the filter function when requesting a * @compat_filter_fn: Will be used as the filter function when requesting a
* channel for platforms which do not use devicetree. The filter parameter * channel for platforms which do not use devicetree. The filter parameter
* will be the DAI's DMA data. * will be the DAI's DMA data.
...@@ -140,6 +142,9 @@ struct snd_dmaengine_pcm_config { ...@@ -140,6 +142,9 @@ struct snd_dmaengine_pcm_config {
struct dma_chan *(*compat_request_channel)( struct dma_chan *(*compat_request_channel)(
struct snd_soc_pcm_runtime *rtd, struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_substream *substream); struct snd_pcm_substream *substream);
int (*process)(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
void *buf, unsigned long bytes);
dma_filter_fn compat_filter_fn; dma_filter_fn compat_filter_fn;
struct device *dma_dev; struct device *dma_dev;
const char *chan_names[SNDRV_PCM_STREAM_LAST + 1]; const char *chan_names[SNDRV_PCM_STREAM_LAST + 1];
......
...@@ -242,6 +242,7 @@ typedef int __bitwise snd_pcm_format_t; ...@@ -242,6 +242,7 @@ typedef int __bitwise snd_pcm_format_t;
#define SNDRV_PCM_FORMAT_DSD_U16_BE ((__force snd_pcm_format_t) 51) /* DSD, 2-byte samples DSD (x16), big endian */ #define SNDRV_PCM_FORMAT_DSD_U16_BE ((__force snd_pcm_format_t) 51) /* DSD, 2-byte samples DSD (x16), big endian */
#define SNDRV_PCM_FORMAT_DSD_U32_BE ((__force snd_pcm_format_t) 52) /* DSD, 4-byte samples DSD (x32), big endian */ #define SNDRV_PCM_FORMAT_DSD_U32_BE ((__force snd_pcm_format_t) 52) /* DSD, 4-byte samples DSD (x32), big endian */
#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_BE #define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_BE
#define SNDRV_PCM_FORMAT_FIRST SNDRV_PCM_FORMAT_S8
#ifdef SNDRV_LITTLE_ENDIAN #ifdef SNDRV_LITTLE_ENDIAN
#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/device.h> #include <linux/device.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/soc.h> #include <sound/soc.h>
...@@ -51,25 +50,21 @@ static struct snd_soc_dai_driver gtm601_dai = { ...@@ -51,25 +50,21 @@ static struct snd_soc_dai_driver gtm601_dai = {
}, },
}; };
static const struct snd_soc_codec_driver soc_codec_dev_gtm601 = { static const struct snd_soc_component_driver soc_component_dev_gtm601 = {
.component_driver = {
.dapm_widgets = gtm601_dapm_widgets, .dapm_widgets = gtm601_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets), .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets),
.dapm_routes = gtm601_dapm_routes, .dapm_routes = gtm601_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes), .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes),
}, .idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
}; };
static int gtm601_platform_probe(struct platform_device *pdev) static int gtm601_platform_probe(struct platform_device *pdev)
{ {
return snd_soc_register_codec(&pdev->dev, return devm_snd_soc_register_component(&pdev->dev,
&soc_codec_dev_gtm601, &gtm601_dai, 1); &soc_component_dev_gtm601, &gtm601_dai, 1);
}
static int gtm601_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
} }
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
...@@ -86,7 +81,6 @@ static struct platform_driver gtm601_codec_driver = { ...@@ -86,7 +81,6 @@ static struct platform_driver gtm601_codec_driver = {
.of_match_table = of_match_ptr(gtm601_codec_of_match), .of_match_table = of_match_ptr(gtm601_codec_of_match),
}, },
.probe = gtm601_platform_probe, .probe = gtm601_platform_probe,
.remove = gtm601_platform_remove,
}; };
module_platform_driver(gtm601_codec_driver); module_platform_driver(gtm601_codec_driver);
......
...@@ -788,7 +788,7 @@ static int fsl_esai_probe(struct platform_device *pdev) ...@@ -788,7 +788,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct fsl_esai *esai_priv; struct fsl_esai *esai_priv;
struct resource *res; struct resource *res;
const uint32_t *iprop; const __be32 *iprop;
void __iomem *regs; void __iomem *regs;
int irq, ret; int irq, ret;
......
...@@ -56,6 +56,10 @@ ...@@ -56,6 +56,10 @@
#include "fsl_ssi.h" #include "fsl_ssi.h"
#include "imx-pcm.h" #include "imx-pcm.h"
/* Define RX and TX to index ssi->regvals array; Can be 0 or 1 only */
#define RX 0
#define TX 1
/** /**
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
* *
...@@ -86,6 +90,16 @@ ...@@ -86,6 +90,16 @@
SNDRV_PCM_FMTBIT_S24_LE) SNDRV_PCM_FMTBIT_S24_LE)
#endif #endif
/*
* In AC97 mode, TXDIR bit is forced to 0 and TFDIR bit is forced to 1:
* - SSI inputs external bit clock and outputs frame sync clock -- CBM_CFS
* - Also have NB_NF to mark these two clocks will not be inverted
*/
#define FSLSSI_AC97_DAIFMT \
(SND_SOC_DAIFMT_AC97 | \
SND_SOC_DAIFMT_CBM_CFS | \
SND_SOC_DAIFMT_NB_NF)
#define FSLSSI_SIER_DBG_RX_FLAGS \ #define FSLSSI_SIER_DBG_RX_FLAGS \
(SSI_SIER_RFF0_EN | \ (SSI_SIER_RFF0_EN | \
SSI_SIER_RLS_EN | \ SSI_SIER_RLS_EN | \
...@@ -201,7 +215,9 @@ struct fsl_ssi_soc_data { ...@@ -201,7 +215,9 @@ struct fsl_ssi_soc_data {
* @cpu_dai_drv: CPU DAI driver for this device * @cpu_dai_drv: CPU DAI driver for this device
* *
* @dai_fmt: DAI configuration this device is currently used with * @dai_fmt: DAI configuration this device is currently used with
* @streams: Mask of current active streams: BIT(TX) and BIT(RX)
* @i2s_net: I2S and Network mode configurations of SCR register * @i2s_net: I2S and Network mode configurations of SCR register
* @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
* @use_dma: DMA is used or FIQ with stream filter * @use_dma: DMA is used or FIQ with stream filter
* @use_dual_fifo: DMA with support for dual FIFO mode * @use_dual_fifo: DMA with support for dual FIFO mode
* @has_ipg_clk_name: If "ipg" is in the clock name list of device tree * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
...@@ -223,8 +239,12 @@ struct fsl_ssi_soc_data { ...@@ -223,8 +239,12 @@ struct fsl_ssi_soc_data {
* *
* @fiq_params: FIQ stream filtering parameters * @fiq_params: FIQ stream filtering parameters
* *
* @pdev: Pointer to pdev when using fsl-ssi as sound card (ppc only) * @card_pdev: Platform_device pointer to register a sound card for PowerPC or
* TODO: Should be replaced with simple-sound-card * to register a CODEC platform device for AC97
* @card_name: Platform_device name to register a sound card for PowerPC or
* to register a CODEC platform device for AC97
* @card_idx: The index of SSI to register a sound card for PowerPC or
* to register a CODEC platform device for AC97
* *
* @dbg_stats: Debugging statistics * @dbg_stats: Debugging statistics
* *
...@@ -245,7 +265,9 @@ struct fsl_ssi { ...@@ -245,7 +265,9 @@ struct fsl_ssi {
struct snd_soc_dai_driver cpu_dai_drv; struct snd_soc_dai_driver cpu_dai_drv;
unsigned int dai_fmt; unsigned int dai_fmt;
u8 streams;
u8 i2s_net; u8 i2s_net;
bool synchronous;
bool use_dma; bool use_dma;
bool use_dual_fifo; bool use_dual_fifo;
bool has_ipg_clk_name; bool has_ipg_clk_name;
...@@ -267,7 +289,9 @@ struct fsl_ssi { ...@@ -267,7 +289,9 @@ struct fsl_ssi {
struct imx_pcm_fiq_params fiq_params; struct imx_pcm_fiq_params fiq_params;
struct platform_device *pdev; struct platform_device *card_pdev;
char card_name[32];
u32 card_idx;
struct fsl_ssi_dbg dbg_stats; struct fsl_ssi_dbg dbg_stats;
...@@ -376,181 +400,172 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) ...@@ -376,181 +400,172 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
} }
/** /**
* Enable or disable all rx/tx config flags at once * Set SCR, SIER, STCR and SRCR registers with cached values in regvals
*
* Notes:
* 1) For offline_config SoCs, enable all necessary bits of both streams
* when 1st stream starts, even if the opposite stream will not start
* 2) It also clears FIFO before setting regvals; SOR is safe to set online
*/ */
static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) static void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx)
{ {
struct regmap *regs = ssi->regs;
struct fsl_ssi_regvals *vals = ssi->regvals; struct fsl_ssi_regvals *vals = ssi->regvals;
int dir = tx ? TX : RX;
u32 sier, srcr, stcr;
/* Clear dirty data in the FIFO; It also prevents channel slipping */
regmap_update_bits(ssi->regs, REG_SSI_SOR,
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
if (enable) { /*
regmap_update_bits(regs, REG_SSI_SIER, * On offline_config SoCs, SxCR and SIER are already configured when
vals[RX].sier | vals[TX].sier, * the previous stream started. So skip all SxCR and SIER settings
vals[RX].sier | vals[TX].sier); * to prevent online reconfigurations, then jump to set SCR directly
regmap_update_bits(regs, REG_SSI_SRCR, */
vals[RX].srcr | vals[TX].srcr, if (ssi->soc->offline_config && ssi->streams)
vals[RX].srcr | vals[TX].srcr); goto enable_scr;
regmap_update_bits(regs, REG_SSI_STCR,
vals[RX].stcr | vals[TX].stcr, if (ssi->soc->offline_config) {
vals[RX].stcr | vals[TX].stcr); /*
* Online reconfiguration not supported, so enable all bits for
* both streams at once to avoid necessity of reconfigurations
*/
srcr = vals[RX].srcr | vals[TX].srcr;
stcr = vals[RX].stcr | vals[TX].stcr;
sier = vals[RX].sier | vals[TX].sier;
} else { } else {
regmap_update_bits(regs, REG_SSI_SRCR, /* Otherwise, only set bits for the current stream */
vals[RX].srcr | vals[TX].srcr, 0); srcr = vals[dir].srcr;
regmap_update_bits(regs, REG_SSI_STCR, stcr = vals[dir].stcr;
vals[RX].stcr | vals[TX].stcr, 0); sier = vals[dir].sier;
regmap_update_bits(regs, REG_SSI_SIER,
vals[RX].sier | vals[TX].sier, 0);
} }
}
/** /* Configure SRCR, STCR and SIER at once */
* Clear remaining data in the FIFO to avoid dirty data or channel slipping regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr);
regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
enable_scr:
/*
* Start DMA before setting TE to avoid FIFO underrun
* which may cause a channel slip or a channel swap
*
* TODO: FIQ cases might also need this upon testing
*/ */
static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) if (ssi->use_dma && tx) {
{ int try = 100;
bool tx = !is_rx; u32 sfcsr;
regmap_update_bits(ssi->regs, REG_SSI_SOR, /* Enable SSI first to send TX DMA request */
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); regmap_update_bits(ssi->regs, REG_SSI_SCR,
SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* Busy wait until TX FIFO not empty -- DMA working */
do {
regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr);
if (SSI_SFCSR_TFCNT0(sfcsr))
break;
} while (--try);
/* FIFO still empty -- something might be wrong */
if (!SSI_SFCSR_TFCNT0(sfcsr))
dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
}
/* Enable all remaining bits in SCR */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
vals[dir].scr, vals[dir].scr);
/* Log the enabled stream to the mask */
ssi->streams |= BIT(dir);
} }
/** /**
* Calculate the bits that have to be disabled for the current stream that is * Exclude bits that are used by the opposite stream
* getting disabled. This keeps the bits enabled that are necessary for the
* second stream to work if 'stream_active' is true.
* *
* Detailed calculation: * When both streams are active, disabling some bits for the current stream
* These are the values that need to be active after disabling. For non-active * might break the other stream if these bits are used by it.
* second stream, this is 0:
* vals_stream * !!stream_active
* *
* The following computes the overall differences between the setup for the * @vals : regvals of the current stream
* to-disable stream and the active stream, a simple XOR: * @avals: regvals of the opposite stream
* vals_disable ^ (vals_stream * !!(stream_active)) * @aactive: active state of the opposite stream
* *
* The full expression adds a mask on all values we care about * 1) XOR vals and avals to get the differences if the other stream is active;
* Otherwise, return current vals if the other stream is not active
* 2) AND the result of 1) with the current vals
*/ */
#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \ #define _ssi_xor_shared_bits(vals, avals, aactive) \
((vals_disable) & \ ((vals) ^ ((avals) * (aactive)))
((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
#define ssi_excl_shared_bits(vals, avals, aactive) \
((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
/** /**
* Enable or disable SSI configuration. * Unset SCR, SIER, STCR and SRCR registers with cached values in regvals
*
* Notes:
* 1) For offline_config SoCs, to avoid online reconfigurations, disable all
* bits of both streams at once when the last stream is abort to end
* 2) It also clears FIFO after unsetting regvals; SOR is safe to set online
*/ */
static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, static void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx)
struct fsl_ssi_regvals *vals)
{ {
struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *vals, *avals;
struct fsl_ssi_regvals *avals; u32 sier, srcr, stcr, scr;
int nr_active_streams; int adir = tx ? RX : TX;
u32 scr; int dir = tx ? TX : RX;
int keep_active; bool aactive;
regmap_read(regs, REG_SSI_SCR, &scr); /* Check if the opposite stream is active */
aactive = ssi->streams & BIT(adir);
nr_active_streams = !!(scr & SSI_SCR_TE) + !!(scr & SSI_SCR_RE); vals = &ssi->regvals[dir];
if (nr_active_streams - 1 > 0) /* Get regvals of the opposite stream to keep opposite stream safe */
keep_active = 1; avals = &ssi->regvals[adir];
else
keep_active = 0;
/* Get the opposite direction to keep its values untouched */
if (&ssi->regvals[RX] == vals)
avals = &ssi->regvals[TX];
else
avals = &ssi->regvals[RX];
if (!enable) {
/* /*
* To keep the other stream safe, exclude shared bits between * To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream * both streams, and get safe bits to disable current stream
*/ */
u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr, scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
keep_active);
/* Safely disable SCR register for the stream */
regmap_update_bits(regs, REG_SSI_SCR, scr, 0);
}
/* /* Disable safe bits of SCR register for the current stream */
* For cases where online configuration is not supported, regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0);
* 1) Enable all necessary bits of both streams when 1st stream starts
* even if the opposite stream will not start
* 2) Disable all remaining bits of both streams when last stream ends
*/
if (ssi->soc->offline_config) {
if ((enable && !nr_active_streams) || (!enable && !keep_active))
fsl_ssi_rxtx_config(ssi, enable);
goto config_done; /* Log the disabled stream to the mask */
} ssi->streams &= ~BIT(dir);
/* Online configure single direction while SSI is running */ /*
if (enable) { * On offline_config SoCs, if the other stream is active, skip
fsl_ssi_fifo_clear(ssi, vals->scr & SSI_SCR_RE); * SxCR and SIER settings to prevent online reconfigurations
*/
if (ssi->soc->offline_config && aactive)
goto fifo_clear;
regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr); if (ssi->soc->offline_config) {
regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr); /* Now there is only current stream active, disable all bits */
regmap_update_bits(regs, REG_SSI_SIER, vals->sier, vals->sier); srcr = vals->srcr | avals->srcr;
stcr = vals->stcr | avals->stcr;
sier = vals->sier | avals->sier;
} else { } else {
u32 sier;
u32 srcr;
u32 stcr;
/* /*
* To keep the other stream safe, exclude shared bits between * To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream * both streams, and get safe bits to disable current stream
*/ */
sier = fsl_ssi_disable_val(vals->sier, avals->sier, sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive);
keep_active); srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive);
srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr, stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive);
keep_active);
stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
keep_active);
/* Safely disable other control registers for the stream */
regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0);
regmap_update_bits(regs, REG_SSI_STCR, stcr, 0);
regmap_update_bits(regs, REG_SSI_SIER, sier, 0);
} }
config_done: /* Clear configurations of SRCR, STCR and SIER at once */
/* Enabling of subunits is done after configuration */ regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0);
if (enable) { regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0);
/* regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0);
* Start DMA before setting TE to avoid FIFO underrun
* which may cause a channel slip or a channel swap
*
* TODO: FIQ cases might also need this upon testing
*/
if (ssi->use_dma && (vals->scr & SSI_SCR_TE)) {
int i;
int max_loop = 100;
/* Enable SSI first to send TX DMA request */
regmap_update_bits(regs, REG_SSI_SCR,
SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* Busy wait until TX FIFO not empty -- DMA working */ fifo_clear:
for (i = 0; i < max_loop; i++) { /* Clear remaining data in the FIFO */
u32 sfcsr; regmap_update_bits(ssi->regs, REG_SSI_SOR,
regmap_read(regs, REG_SSI_SFCSR, &sfcsr); SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
if (SSI_SFCSR_TFCNT0(sfcsr))
break;
}
if (i == max_loop) {
dev_err(ssi->dev,
"Timeout waiting TX FIFO filling\n");
}
}
/* Enable all remaining bits */
regmap_update_bits(regs, REG_SSI_SCR, vals->scr, vals->scr);
}
}
static void fsl_ssi_rx_config(struct fsl_ssi *ssi, bool enable)
{
fsl_ssi_config(ssi, enable, &ssi->regvals[RX]);
} }
static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
...@@ -566,21 +581,6 @@ static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) ...@@ -566,21 +581,6 @@ static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
} }
} }
static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable)
{
/*
* SACCST might be modified via AC Link by a CODEC if it sends
* extra bits in their SLOTREQ requests, which'll accidentally
* send valid data to slots other than normal playback slots.
*
* To be safe, configure SACCST right before TX starts.
*/
if (enable && fsl_ssi_is_ac97(ssi))
fsl_ssi_tx_ac97_saccst_setup(ssi);
fsl_ssi_config(ssi, enable, &ssi->regvals[TX]);
}
/** /**
* Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely * Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely
*/ */
...@@ -588,17 +588,20 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) ...@@ -588,17 +588,20 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi)
{ {
struct fsl_ssi_regvals *vals = ssi->regvals; struct fsl_ssi_regvals *vals = ssi->regvals;
vals[RX].sier = SSI_SIER_RFF0_EN; vals[RX].sier = SSI_SIER_RFF0_EN | FSLSSI_SIER_DBG_RX_FLAGS;
vals[RX].srcr = SSI_SRCR_RFEN0; vals[RX].srcr = SSI_SRCR_RFEN0;
vals[RX].scr = 0; vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE;
vals[TX].sier = SSI_SIER_TFE0_EN; vals[TX].sier = SSI_SIER_TFE0_EN | FSLSSI_SIER_DBG_TX_FLAGS;
vals[TX].stcr = SSI_STCR_TFEN0; vals[TX].stcr = SSI_STCR_TFEN0;
vals[TX].scr = 0; vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
/* AC97 has already enabled SSIEN, RE and TE, so ignore them */ /* AC97 has already enabled SSIEN, RE and TE, so ignore them */
if (!fsl_ssi_is_ac97(ssi)) { if (fsl_ssi_is_ac97(ssi))
vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE; vals[RX].scr = vals[TX].scr = 0;
vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
if (ssi->use_dual_fifo) {
vals[RX].srcr |= SSI_SRCR_RFEN1;
vals[TX].stcr |= SSI_STCR_TFEN1;
} }
if (ssi->use_dma) { if (ssi->use_dma) {
...@@ -608,9 +611,6 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) ...@@ -608,9 +611,6 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi)
vals[RX].sier |= SSI_SIER_RIE; vals[RX].sier |= SSI_SIER_RIE;
vals[TX].sier |= SSI_SIER_TIE; vals[TX].sier |= SSI_SIER_TIE;
} }
vals[RX].sier |= FSLSSI_SIER_DBG_RX_FLAGS;
vals[TX].sier |= FSLSSI_SIER_DBG_TX_FLAGS;
} }
static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi) static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
...@@ -681,7 +681,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -681,7 +681,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
struct regmap *regs = ssi->regs; struct regmap *regs = ssi->regs;
int synchronous = ssi->cpu_dai_drv.symmetric_rates, ret;
u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
unsigned long clkrate, baudrate, tmprate; unsigned long clkrate, baudrate, tmprate;
unsigned int slots = params_channels(hw_params); unsigned int slots = params_channels(hw_params);
...@@ -689,6 +688,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -689,6 +688,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
u64 sub, savesub = 100000; u64 sub, savesub = 100000;
unsigned int freq; unsigned int freq;
bool baudclk_is_used; bool baudclk_is_used;
int ret;
/* Override slots and slot_width if being specifically set... */ /* Override slots and slot_width if being specifically set... */
if (ssi->slots) if (ssi->slots)
...@@ -767,7 +767,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -767,7 +767,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR; mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
/* STCCR is used for RX in synchronous mode */ /* STCCR is used for RX in synchronous mode */
tx2 = tx || synchronous; tx2 = tx || ssi->synchronous;
regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr); regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
if (!baudclk_is_used) { if (!baudclk_is_used) {
...@@ -803,11 +803,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, ...@@ -803,11 +803,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
unsigned int sample_size = params_width(hw_params); unsigned int sample_size = params_width(hw_params);
u32 wl = SSI_SxCCR_WL(sample_size); u32 wl = SSI_SxCCR_WL(sample_size);
int ret; int ret;
u32 scr;
int enabled;
regmap_read(regs, REG_SSI_SCR, &scr);
enabled = scr & SSI_SCR_SSIEN;
/* /*
* SSI is properly configured if it is enabled and running in * SSI is properly configured if it is enabled and running in
...@@ -815,7 +810,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, ...@@ -815,7 +810,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
* that should set separate configurations for STCCR and SRCCR * that should set separate configurations for STCCR and SRCCR
* despite running in the synchronous mode. * despite running in the synchronous mode.
*/ */
if (enabled && ssi->cpu_dai_drv.symmetric_rates) if (ssi->streams && ssi->synchronous)
return 0; return 0;
if (fsl_ssi_is_i2s_master(ssi)) { if (fsl_ssi_is_i2s_master(ssi)) {
...@@ -834,20 +829,20 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, ...@@ -834,20 +829,20 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
} }
if (!fsl_ssi_is_ac97(ssi)) { if (!fsl_ssi_is_ac97(ssi)) {
u8 i2s_net;
/* Normal + Network mode to send 16-bit data in 32-bit frames */ /* Normal + Network mode to send 16-bit data in 32-bit frames */
if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16)
i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
else
i2s_net = ssi->i2s_net; /* Use Normal mode to send mono data at 1st slot of 2 slots */
if (channels == 1)
ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL;
regmap_update_bits(regs, REG_SSI_SCR, regmap_update_bits(regs, REG_SSI_SCR,
SSI_SCR_I2S_NET_MASK, SSI_SCR_I2S_NET_MASK, ssi->i2s_net);
channels == 1 ? 0 : i2s_net);
} }
/* In synchronous mode, the SSI uses STCCR for capture */ /* In synchronous mode, the SSI uses STCCR for capture */
tx2 = tx || ssi->cpu_dai_drv.symmetric_rates; tx2 = tx || ssi->synchronous;
regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl); regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
return 0; return 0;
...@@ -868,45 +863,31 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, ...@@ -868,45 +863,31 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int _fsl_ssi_set_dai_fmt(struct device *dev, static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt)
struct fsl_ssi *ssi, unsigned int fmt)
{ {
struct regmap *regs = ssi->regs; u32 strcr = 0, scr = 0, stcr, srcr, mask;
u32 strcr = 0, stcr, srcr, scr, mask;
u8 wm;
ssi->dai_fmt = fmt; ssi->dai_fmt = fmt;
if (fsl_ssi_is_i2s_master(ssi) && IS_ERR(ssi->baudclk)) {
dev_err(dev, "missing baudclk for master mode\n");
return -EINVAL;
}
fsl_ssi_setup_regvals(ssi);
regmap_read(regs, REG_SSI_SCR, &scr);
scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK);
/* Synchronize frame sync clock for TE to avoid data slipping */ /* Synchronize frame sync clock for TE to avoid data slipping */
scr |= SSI_SCR_SYNC_TX_FS; scr |= SSI_SCR_SYNC_TX_FS;
mask = SSI_STCR_TXBIT0 | SSI_STCR_TFDIR | SSI_STCR_TXDIR | /* Set to default shifting settings: LSB_ALIGNED */
SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS; strcr |= SSI_STCR_TXBIT0;
regmap_read(regs, REG_SSI_STCR, &stcr);
regmap_read(regs, REG_SSI_SRCR, &srcr);
stcr &= ~mask;
srcr &= ~mask;
/* Use Network mode as default */ /* Use Network mode as default */
ssi->i2s_net = SSI_SCR_NET; ssi->i2s_net = SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
regmap_update_bits(regs, REG_SSI_STCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
regmap_update_bits(regs, REG_SSI_SRCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
if (IS_ERR(ssi->baudclk)) {
dev_err(ssi->dev,
"missing baudclk for master mode\n");
return -EINVAL;
}
/* fall through */
case SND_SOC_DAIFMT_CBM_CFS:
ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER; ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER;
break; break;
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
...@@ -916,30 +897,34 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, ...@@ -916,30 +897,34 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(ssi->regs, REG_SSI_STCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
regmap_update_bits(ssi->regs, REG_SSI_SRCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
/* Data on rising edge of bclk, frame low, 1clk before data */ /* Data on rising edge of bclk, frame low, 1clk before data */
strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS;
SSI_STCR_TXBIT0 | SSI_STCR_TEFS;
break; break;
case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_LEFT_J:
/* Data on rising edge of bclk, frame high */ /* Data on rising edge of bclk, frame high */
strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; strcr |= SSI_STCR_TSCKP;
break; break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A:
/* Data on rising edge of bclk, frame high, 1clk before data */ /* Data on rising edge of bclk, frame high, 1clk before data */
strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS;
SSI_STCR_TXBIT0 | SSI_STCR_TEFS;
break; break;
case SND_SOC_DAIFMT_DSP_B: case SND_SOC_DAIFMT_DSP_B:
/* Data on rising edge of bclk, frame high */ /* Data on rising edge of bclk, frame high */
strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TXBIT0; strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP;
break; break;
case SND_SOC_DAIFMT_AC97: case SND_SOC_DAIFMT_AC97:
/* Data on falling edge of bclk, frame high, 1clk before data */ /* Data on falling edge of bclk, frame high, 1clk before data */
ssi->i2s_net |= SSI_SCR_I2S_MODE_NORMAL; strcr |= SSI_STCR_TEFS;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
scr |= ssi->i2s_net; scr |= ssi->i2s_net;
/* DAI clock inversion */ /* DAI clock inversion */
...@@ -973,49 +958,33 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, ...@@ -973,49 +958,33 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
break; break;
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
/* Input bit or frame sync clocks */ /* Input bit or frame sync clocks */
scr &= ~SSI_SCR_SYS_CLK_EN;
break; break;
case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFS:
/* Input bit clock but output frame sync clock */ /* Input bit clock but output frame sync clock */
strcr &= ~SSI_STCR_TXDIR;
strcr |= SSI_STCR_TFDIR; strcr |= SSI_STCR_TFDIR;
scr &= ~SSI_SCR_SYS_CLK_EN;
break; break;
default: default:
if (!fsl_ssi_is_ac97(ssi))
return -EINVAL; return -EINVAL;
} }
stcr |= strcr; stcr = strcr;
srcr |= strcr; srcr = strcr;
/* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) { if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) {
srcr &= ~SSI_SRCR_RXDIR; srcr &= ~SSI_SRCR_RXDIR;
scr |= SSI_SCR_SYN; scr |= SSI_SCR_SYN;
} }
regmap_write(regs, REG_SSI_STCR, stcr); mask = SSI_STCR_TFDIR | SSI_STCR_TXDIR | SSI_STCR_TSCKP |
regmap_write(regs, REG_SSI_SRCR, srcr); SSI_STCR_TFSL | SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
regmap_write(regs, REG_SSI_SCR, scr);
wm = ssi->fifo_watermark; regmap_update_bits(ssi->regs, REG_SSI_STCR, mask, stcr);
regmap_update_bits(ssi->regs, REG_SSI_SRCR, mask, srcr);
regmap_write(regs, REG_SSI_SFCSR, mask = SSI_SCR_SYNC_TX_FS | SSI_SCR_I2S_MODE_MASK |
SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | SSI_SCR_SYS_CLK_EN | SSI_SCR_SYN;
SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm)); regmap_update_bits(ssi->regs, REG_SSI_SCR, mask, scr);
if (ssi->use_dual_fifo) {
regmap_update_bits(regs, REG_SSI_SRCR,
SSI_SRCR_RFEN1, SSI_SRCR_RFEN1);
regmap_update_bits(regs, REG_SSI_STCR,
SSI_STCR_TFEN1, SSI_STCR_TFEN1);
regmap_update_bits(regs, REG_SSI_SCR,
SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
}
if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97)
fsl_ssi_setup_ac97(ssi);
return 0; return 0;
} }
...@@ -1031,7 +1000,7 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -1031,7 +1000,7 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
if (fsl_ssi_is_ac97(ssi)) if (fsl_ssi_is_ac97(ssi))
return 0; return 0;
return _fsl_ssi_set_dai_fmt(dai->dev, ssi, fmt); return _fsl_ssi_set_dai_fmt(ssi, fmt);
} }
/** /**
...@@ -1051,9 +1020,7 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, ...@@ -1051,9 +1020,7 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
} }
/* The slot number should be >= 2 if using Network mode or I2S mode */ /* The slot number should be >= 2 if using Network mode or I2S mode */
regmap_read(regs, REG_SSI_SCR, &val); if (ssi->i2s_net && slots < 2) {
val &= SSI_SCR_I2S_MODE_MASK | SSI_SCR_NET;
if (val && slots < 2) {
dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n"); dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n");
return -EINVAL; return -EINVAL;
} }
...@@ -1063,9 +1030,8 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, ...@@ -1063,9 +1030,8 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
regmap_update_bits(regs, REG_SSI_SRCCR, regmap_update_bits(regs, REG_SSI_SRCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
/* Save SSIEN bit of the SCR register */ /* Save the SCR register value */
regmap_read(regs, REG_SSI_SCR, &val); regmap_read(regs, REG_SSI_SCR, &val);
val &= SSI_SCR_SSIEN;
/* Temporarily enable SSI to allow SxMSKs to be configurable */ /* Temporarily enable SSI to allow SxMSKs to be configurable */
regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN); regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
...@@ -1092,39 +1058,34 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -1092,39 +1058,34 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct regmap *regs = ssi->regs; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) /*
fsl_ssi_tx_config(ssi, true); * SACCST might be modified via AC Link by a CODEC if it sends
else * extra bits in their SLOTREQ requests, which'll accidentally
fsl_ssi_rx_config(ssi, true); * send valid data to slots other than normal playback slots.
*
* To be safe, configure SACCST right before TX starts.
*/
if (tx && fsl_ssi_is_ac97(ssi))
fsl_ssi_tx_ac97_saccst_setup(ssi);
fsl_ssi_config_enable(ssi, tx);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) fsl_ssi_config_disable(ssi, tx);
fsl_ssi_tx_config(ssi, false);
else
fsl_ssi_rx_config(ssi, false);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
/* Clear corresponding FIFO */
if (fsl_ssi_is_ac97(ssi)) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_write(regs, REG_SSI_SOR, SSI_SOR_TX_CLR);
else
regmap_write(regs, REG_SSI_SOR, SSI_SOR_RX_CLR);
}
return 0; return 0;
} }
...@@ -1132,10 +1093,9 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) ...@@ -1132,10 +1093,9 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
{ {
struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
if (ssi->soc->imx && ssi->use_dma) { if (ssi->soc->imx && ssi->use_dma)
dai->playback_dma_data = &ssi->dma_params_tx; snd_soc_dai_init_dma_data(dai, &ssi->dma_params_tx,
dai->capture_dma_data = &ssi->dma_params_rx; &ssi->dma_params_rx);
}
return 0; return 0;
} }
...@@ -1175,6 +1135,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = { ...@@ -1175,6 +1135,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.bus_control = true, .bus_control = true,
.symmetric_channels = 1,
.probe = fsl_ssi_dai_probe, .probe = fsl_ssi_dai_probe,
.playback = { .playback = {
.stream_name = "AC97 Playback", .stream_name = "AC97 Playback",
...@@ -1271,6 +1232,53 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { ...@@ -1271,6 +1232,53 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
.write = fsl_ssi_ac97_write, .write = fsl_ssi_ac97_write,
}; };
/**
* Initialize SSI registers
*/
static int fsl_ssi_hw_init(struct fsl_ssi *ssi)
{
u32 wm = ssi->fifo_watermark;
/* Initialize regvals */
fsl_ssi_setup_regvals(ssi);
/* Set watermarks */
regmap_write(ssi->regs, REG_SSI_SFCSR,
SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) |
SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm));
/* Enable Dual FIFO mode */
if (ssi->use_dual_fifo)
regmap_update_bits(ssi->regs, REG_SSI_SCR,
SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
/* AC97 should start earlier to communicate with CODECs */
if (fsl_ssi_is_ac97(ssi)) {
_fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt);
fsl_ssi_setup_ac97(ssi);
}
return 0;
}
/**
* Clear SSI registers
*/
static void fsl_ssi_hw_clean(struct fsl_ssi *ssi)
{
/* Disable registers for AC97 */
if (fsl_ssi_is_ac97(ssi)) {
/* Disable TE and RE bits first */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
SSI_SCR_TE | SSI_SCR_RE, 0);
/* Disable AC97 mode */
regmap_write(ssi->regs, REG_SSI_SACNT, 0);
/* Unset WAIT bits */
regmap_write(ssi->regs, REG_SSI_SOR, 0);
/* Disable SSI -- software reset */
regmap_update_bits(ssi->regs, REG_SSI_SCR, SSI_SCR_SSIEN, 0);
}
}
/** /**
* Make every character in a string lower-case * Make every character in a string lower-case
*/ */
...@@ -1285,9 +1293,7 @@ static void make_lowercase(char *s) ...@@ -1285,9 +1293,7 @@ static void make_lowercase(char *s)
static int fsl_ssi_imx_probe(struct platform_device *pdev, static int fsl_ssi_imx_probe(struct platform_device *pdev,
struct fsl_ssi *ssi, void __iomem *iomem) struct fsl_ssi *ssi, void __iomem *iomem)
{ {
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
u32 dmas[4];
int ret; int ret;
/* Backward compatible for a DT without ipg clock name assigned */ /* Backward compatible for a DT without ipg clock name assigned */
...@@ -1321,14 +1327,8 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, ...@@ -1321,14 +1327,8 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0; ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0;
ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0; ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
/* Set to dual FIFO mode according to the SDMA sciprt */ /* Use even numbers to avoid channel swap due to SDMA script design */
ret = of_property_read_u32_array(np, "dmas", dmas, 4); if (ssi->use_dual_fifo) {
if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
ssi->use_dual_fifo = true;
/*
* Use even numbers to avoid channel swap due to SDMA
* script design
*/
ssi->dma_params_tx.maxburst &= ~0x1; ssi->dma_params_tx.maxburst &= ~0x1;
ssi->dma_params_rx.maxburst &= ~0x1; ssi->dma_params_rx.maxburst &= ~0x1;
} }
...@@ -1369,41 +1369,109 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi) ...@@ -1369,41 +1369,109 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
clk_disable_unprepare(ssi->clk); clk_disable_unprepare(ssi->clk);
} }
static int fsl_ssi_probe(struct platform_device *pdev) static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi)
{ {
struct fsl_ssi *ssi; struct device *dev = ssi->dev;
int ret = 0; struct device_node *np = dev->of_node;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
const struct of_device_id *of_id; const struct of_device_id *of_id;
const char *p, *sprop; const char *p, *sprop;
const uint32_t *iprop; const __be32 *iprop;
struct resource *res; u32 dmas[4];
void __iomem *iomem; int ret;
char name[64];
struct regmap_config regconfig = fsl_ssi_regconfig;
of_id = of_match_device(fsl_ssi_ids, dev); of_id = of_match_device(fsl_ssi_ids, dev);
if (!of_id || !of_id->data) if (!of_id || !of_id->data)
return -EINVAL; return -EINVAL;
ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
if (!ssi)
return -ENOMEM;
ssi->soc = of_id->data; ssi->soc = of_id->data;
ssi->dev = dev;
ret = of_property_match_string(np, "clock-names", "ipg");
/* Get error code if not found */
ssi->has_ipg_clk_name = ret >= 0;
/* Check if being used in AC97 mode */ /* Check if being used in AC97 mode */
sprop = of_get_property(np, "fsl,mode", NULL); sprop = of_get_property(np, "fsl,mode", NULL);
if (sprop) { if (sprop && !strcmp(sprop, "ac97-slave")) {
if (!strcmp(sprop, "ac97-slave")) ssi->dai_fmt = FSLSSI_AC97_DAIFMT;
ssi->dai_fmt = SND_SOC_DAIFMT_AC97;
ret = of_property_read_u32(np, "cell-index", &ssi->card_idx);
if (ret) {
dev_err(dev, "failed to get SSI index property\n");
return -EINVAL;
}
strcpy(ssi->card_name, "ac97-codec");
} else if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
/*
* In synchronous mode, STCK and STFS ports are used by RX
* as well. So the software should limit the sample rates,
* sample bits and channels to be symmetric.
*
* This is exclusive with FSLSSI_AC97_FORMATS as AC97 runs
* in the SSI synchronous mode however it does not have to
* limit symmetric sample rates and sample bits.
*/
ssi->synchronous = true;
} }
/* Select DMA or FIQ */ /* Select DMA or FIQ */
ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
/* Fetch FIFO depth; Set to 8 for older DT without this property */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
if (iprop)
ssi->fifo_depth = be32_to_cpup(iprop);
else
ssi->fifo_depth = 8;
/* Use dual FIFO mode depending on the support from SDMA script */
ret = of_property_read_u32_array(np, "dmas", dmas, 4);
if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL)
ssi->use_dual_fifo = true;
/*
* Backward compatible for older bindings by manually triggering the
* machine driver's probe(). Use /compatible property, including the
* address of CPU DAI driver structure, as the name of machine driver
*
* If card_name is set by AC97 earlier, bypass here since it uses a
* different name to register the device.
*/
if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) {
sprop = of_get_property(of_find_node_by_path("/"),
"compatible", NULL);
/* Strip "fsl," in the compatible name if applicable */
p = strrchr(sprop, ',');
if (p)
sprop = p + 1;
snprintf(ssi->card_name, sizeof(ssi->card_name),
"snd-soc-%s", sprop);
make_lowercase(ssi->card_name);
ssi->card_idx = 0;
}
return 0;
}
static int fsl_ssi_probe(struct platform_device *pdev)
{
struct regmap_config regconfig = fsl_ssi_regconfig;
struct device *dev = &pdev->dev;
struct fsl_ssi *ssi;
struct resource *res;
void __iomem *iomem;
int ret = 0;
ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
if (!ssi)
return -ENOMEM;
ssi->dev = dev;
/* Probe from DT */
ret = fsl_ssi_probe_from_dt(ssi);
if (ret)
return ret;
if (fsl_ssi_is_ac97(ssi)) { if (fsl_ssi_is_ac97(ssi)) {
memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai, memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai,
sizeof(fsl_ssi_ac97_dai)); sizeof(fsl_ssi_ac97_dai));
...@@ -1427,15 +1495,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1427,15 +1495,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
REG_SSI_SRMSK / sizeof(uint32_t) + 1; REG_SSI_SRMSK / sizeof(uint32_t) + 1;
} }
ret = of_property_match_string(np, "clock-names", "ipg"); if (ssi->has_ipg_clk_name)
if (ret < 0) {
ssi->has_ipg_clk_name = false;
ssi->regs = devm_regmap_init_mmio(dev, iomem, &regconfig);
} else {
ssi->has_ipg_clk_name = true;
ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem, ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem,
&regconfig); &regconfig);
} else
ssi->regs = devm_regmap_init_mmio(dev, iomem, &regconfig);
if (IS_ERR(ssi->regs)) { if (IS_ERR(ssi->regs)) {
dev_err(dev, "failed to init register map\n"); dev_err(dev, "failed to init register map\n");
return PTR_ERR(ssi->regs); return PTR_ERR(ssi->regs);
...@@ -1447,23 +1511,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1447,23 +1511,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
return ssi->irq; return ssi->irq;
} }
/* Set software limitations for synchronous mode */ /* Set software limitations for synchronous mode except AC97 */
if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) {
if (!fsl_ssi_is_ac97(ssi)) {
ssi->cpu_dai_drv.symmetric_rates = 1; ssi->cpu_dai_drv.symmetric_rates = 1;
ssi->cpu_dai_drv.symmetric_samplebits = 1;
}
ssi->cpu_dai_drv.symmetric_channels = 1; ssi->cpu_dai_drv.symmetric_channels = 1;
ssi->cpu_dai_drv.symmetric_samplebits = 1;
} }
/* Fetch FIFO depth; Set to 8 for older DT without this property */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
if (iprop)
ssi->fifo_depth = be32_to_cpup(iprop);
else
ssi->fifo_depth = 8;
/* /*
* Configure TX and RX DMA watermarks -- when to send a DMA request * Configure TX and RX DMA watermarks -- when to send a DMA request
* *
...@@ -1528,50 +1582,27 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1528,50 +1582,27 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (ret) if (ret)
goto error_asoc_register; goto error_asoc_register;
/* Bypass it if using newer DT bindings of ASoC machine drivers */ /* Initially configures SSI registers */
if (!of_get_property(np, "codec-handle", NULL)) fsl_ssi_hw_init(ssi);
goto done;
/* Register a platform device for older bindings or AC97 */
if (ssi->card_name[0]) {
struct device *parent = dev;
/* /*
* Backward compatible for older bindings by manually triggering the * Do not set SSI dev as the parent of AC97 CODEC device since
* machine driver's probe(). Use /compatible property, including the * it does not have a DT node. Otherwise ASoC core will assume
* address of CPU DAI driver structure, as the name of machine driver. * CODEC has the same DT node as the SSI, so it may bypass the
* dai_probe() of SSI and then cause NULL DMA data pointers.
*/ */
sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL); if (fsl_ssi_is_ac97(ssi))
/* Sometimes the compatible name has a "fsl," prefix, so we strip it. */ parent = NULL;
p = strrchr(sprop, ',');
if (p) ssi->card_pdev = platform_device_register_data(parent,
sprop = p + 1; ssi->card_name, ssi->card_idx, NULL, 0);
snprintf(name, sizeof(name), "snd-soc-%s", sprop); if (IS_ERR(ssi->card_pdev)) {
make_lowercase(name); ret = PTR_ERR(ssi->card_pdev);
dev_err(dev, "failed to register %s: %d\n",
ssi->pdev = platform_device_register_data(dev, name, 0, NULL, 0); ssi->card_name, ret);
if (IS_ERR(ssi->pdev)) {
ret = PTR_ERR(ssi->pdev);
dev_err(dev, "failed to register platform: %d\n", ret);
goto error_sound_card;
}
done:
if (ssi->dai_fmt)
_fsl_ssi_set_dai_fmt(dev, ssi, ssi->dai_fmt);
if (fsl_ssi_is_ac97(ssi)) {
u32 ssi_idx;
ret = of_property_read_u32(np, "cell-index", &ssi_idx);
if (ret) {
dev_err(dev, "failed to get SSI index property\n");
goto error_sound_card;
}
ssi->pdev = platform_device_register_data(NULL, "ac97-codec",
ssi_idx, NULL, 0);
if (IS_ERR(ssi->pdev)) {
ret = PTR_ERR(ssi->pdev);
dev_err(dev,
"failed to register AC97 codec platform: %d\n",
ret);
goto error_sound_card; goto error_sound_card;
} }
} }
...@@ -1599,8 +1630,11 @@ static int fsl_ssi_remove(struct platform_device *pdev) ...@@ -1599,8 +1630,11 @@ static int fsl_ssi_remove(struct platform_device *pdev)
fsl_ssi_debugfs_remove(&ssi->dbg_stats); fsl_ssi_debugfs_remove(&ssi->dbg_stats);
if (ssi->pdev) if (ssi->card_pdev)
platform_device_unregister(ssi->pdev); platform_device_unregister(ssi->card_pdev);
/* Clean up SSI registers */
fsl_ssi_hw_clean(ssi);
if (ssi->soc->imx) if (ssi->soc->imx)
fsl_ssi_imx_clean(pdev, ssi); fsl_ssi_imx_clean(pdev, ssi);
......
...@@ -12,9 +12,6 @@ ...@@ -12,9 +12,6 @@
#ifndef _MPC8610_I2S_H #ifndef _MPC8610_I2S_H
#define _MPC8610_I2S_H #define _MPC8610_I2S_H
#define RX 0
#define TX 1
/* -- SSI Register Map -- */ /* -- SSI Register Map -- */
/* SSI Transmit Data Register 0 */ /* SSI Transmit Data Register 0 */
......
...@@ -36,7 +36,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, ...@@ -36,7 +36,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
{ {
struct resource res; struct resource res;
struct device_node *dma_channel_np, *dma_np; struct device_node *dma_channel_np, *dma_np;
const u32 *iprop; const __be32 *iprop;
int ret; int ret;
dma_channel_np = of_parse_phandle(ssi_np, name, 0); dma_channel_np = of_parse_phandle(ssi_np, name, 0);
......
...@@ -132,7 +132,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea ...@@ -132,7 +132,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
int i, ret; snd_pcm_format_t i;
int ret;
if (pcm->config && pcm->config->pcm_hardware) if (pcm->config && pcm->config->pcm_hardware)
return snd_soc_set_runtime_hwparams(substream, return snd_soc_set_runtime_hwparams(substream,
...@@ -182,7 +183,7 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea ...@@ -182,7 +183,7 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
* default assumption is that it supports 1, 2 and 4 bytes * default assumption is that it supports 1, 2 and 4 bytes
* widths. * widths.
*/ */
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) {
int bits = snd_pcm_format_physical_width(i); int bits = snd_pcm_format_physical_width(i);
/* /*
...@@ -340,6 +341,41 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( ...@@ -340,6 +341,41 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
return snd_dmaengine_pcm_pointer(substream); return snd_dmaengine_pcm_pointer(substream);
} }
static int dmaengine_copy_user(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
void *buf, unsigned long bytes)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *component =
snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME);
struct snd_pcm_runtime *runtime = substream->runtime;
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
int (*process)(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
void *buf, unsigned long bytes) = pcm->config->process;
bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
void *dma_ptr = runtime->dma_area + hwoff +
channel * (runtime->dma_bytes / runtime->channels);
int ret;
if (is_playback)
if (copy_from_user(dma_ptr, (void __user *)buf, bytes))
return -EFAULT;
if (process) {
ret = process(substream, channel, hwoff,
(void __user *)buf, bytes);
if (ret < 0)
return ret;
}
if (!is_playback)
if (copy_to_user((void __user *)buf, dma_ptr, bytes))
return -EFAULT;
return 0;
}
static const struct snd_pcm_ops dmaengine_pcm_ops = { static const struct snd_pcm_ops dmaengine_pcm_ops = {
.open = dmaengine_pcm_open, .open = dmaengine_pcm_open,
.close = snd_dmaengine_pcm_close, .close = snd_dmaengine_pcm_close,
...@@ -350,6 +386,17 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = { ...@@ -350,6 +386,17 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
.pointer = dmaengine_pcm_pointer, .pointer = dmaengine_pcm_pointer,
}; };
static const struct snd_pcm_ops dmaengine_pcm_process_ops = {
.open = dmaengine_pcm_open,
.close = snd_dmaengine_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = dmaengine_pcm_hw_params,
.hw_free = snd_pcm_lib_free_pages,
.trigger = snd_dmaengine_pcm_trigger,
.pointer = dmaengine_pcm_pointer,
.copy_user = dmaengine_copy_user,
};
static const struct snd_soc_component_driver dmaengine_pcm_component = { static const struct snd_soc_component_driver dmaengine_pcm_component = {
.name = SND_DMAENGINE_PCM_DRV_NAME, .name = SND_DMAENGINE_PCM_DRV_NAME,
.probe_order = SND_SOC_COMP_ORDER_LATE, .probe_order = SND_SOC_COMP_ORDER_LATE,
...@@ -357,6 +404,13 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = { ...@@ -357,6 +404,13 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = {
.pcm_new = dmaengine_pcm_new, .pcm_new = dmaengine_pcm_new,
}; };
static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
.name = SND_DMAENGINE_PCM_DRV_NAME,
.probe_order = SND_SOC_COMP_ORDER_LATE,
.ops = &dmaengine_pcm_process_ops,
.pcm_new = dmaengine_pcm_new,
};
static const char * const dmaengine_pcm_dma_channel_names[] = { static const char * const dmaengine_pcm_dma_channel_names[] = {
[SNDRV_PCM_STREAM_PLAYBACK] = "tx", [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
[SNDRV_PCM_STREAM_CAPTURE] = "rx", [SNDRV_PCM_STREAM_CAPTURE] = "rx",
...@@ -442,6 +496,9 @@ int snd_dmaengine_pcm_register(struct device *dev, ...@@ -442,6 +496,9 @@ int snd_dmaengine_pcm_register(struct device *dev,
if (!pcm) if (!pcm)
return -ENOMEM; return -ENOMEM;
#ifdef CONFIG_DEBUG_FS
pcm->component.debugfs_prefix = "dma";
#endif
pcm->config = config; pcm->config = config;
pcm->flags = flags; pcm->flags = flags;
...@@ -449,6 +506,11 @@ int snd_dmaengine_pcm_register(struct device *dev, ...@@ -449,6 +506,11 @@ int snd_dmaengine_pcm_register(struct device *dev,
if (ret) if (ret)
goto err_free_dma; goto err_free_dma;
if (config && config->process)
ret = snd_soc_add_component(dev, &pcm->component,
&dmaengine_pcm_component_process,
NULL, 0);
else
ret = snd_soc_add_component(dev, &pcm->component, ret = snd_soc_add_component(dev, &pcm->component,
&dmaengine_pcm_component, NULL, 0); &dmaengine_pcm_component, NULL, 0);
if (ret) if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment