Commit 04162779 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/eukrea-tlv320', 'asoc/topic/fsl',...

Merge remote-tracking branches 'asoc/topic/eukrea-tlv320', 'asoc/topic/fsl', 'asoc/topic/fsl-ssi' and 'asoc/topic/fsl_asrc' into asoc-next
...@@ -169,6 +169,10 @@ struct snd_pcm_ops { ...@@ -169,6 +169,10 @@ struct snd_pcm_ops {
#define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM) #define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM)
#define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG) #define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG)
#define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM) #define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM)
#define SNDRV_PCM_FMTBIT_S20_LE _SNDRV_PCM_FMTBIT(S20_LE)
#define SNDRV_PCM_FMTBIT_U20_LE _SNDRV_PCM_FMTBIT(U20_LE)
#define SNDRV_PCM_FMTBIT_S20_BE _SNDRV_PCM_FMTBIT(S20_BE)
#define SNDRV_PCM_FMTBIT_U20_BE _SNDRV_PCM_FMTBIT(U20_BE)
#define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL) #define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL)
#define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE) #define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE)
#define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE) #define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE)
...@@ -202,6 +206,8 @@ struct snd_pcm_ops { ...@@ -202,6 +206,8 @@ struct snd_pcm_ops {
#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_LE #define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_LE
#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_LE #define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_LE
#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE #define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
#define SNDRV_PCM_FMTBIT_S20 SNDRV_PCM_FMTBIT_S20_LE
#define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_LE
#endif #endif
#ifdef SNDRV_BIG_ENDIAN #ifdef SNDRV_BIG_ENDIAN
#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_BE #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_BE
...@@ -213,6 +219,8 @@ struct snd_pcm_ops { ...@@ -213,6 +219,8 @@ struct snd_pcm_ops {
#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_BE #define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_BE
#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_BE #define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_BE
#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE #define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
#define SNDRV_PCM_FMTBIT_S20 SNDRV_PCM_FMTBIT_S20_BE
#define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_BE
#endif #endif
struct snd_pcm_file { struct snd_pcm_file {
......
...@@ -102,6 +102,8 @@ struct snd_compr_stream; ...@@ -102,6 +102,8 @@ struct snd_compr_stream;
SNDRV_PCM_FMTBIT_S16_BE |\ SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S20_3BE |\ SNDRV_PCM_FMTBIT_S20_3BE |\
SNDRV_PCM_FMTBIT_S20_LE |\
SNDRV_PCM_FMTBIT_S20_BE |\
SNDRV_PCM_FMTBIT_S24_3LE |\ SNDRV_PCM_FMTBIT_S24_3LE |\
SNDRV_PCM_FMTBIT_S24_3BE |\ SNDRV_PCM_FMTBIT_S24_3BE |\
SNDRV_PCM_FMTBIT_S32_LE |\ SNDRV_PCM_FMTBIT_S32_LE |\
......
...@@ -214,6 +214,11 @@ typedef int __bitwise snd_pcm_format_t; ...@@ -214,6 +214,11 @@ typedef int __bitwise snd_pcm_format_t;
#define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22) #define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22)
#define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23) #define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23)
#define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24) #define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24)
#define SNDRV_PCM_FORMAT_S20_LE ((__force snd_pcm_format_t) 25) /* in four bytes, LSB justified */
#define SNDRV_PCM_FORMAT_S20_BE ((__force snd_pcm_format_t) 26) /* in four bytes, LSB justified */
#define SNDRV_PCM_FORMAT_U20_LE ((__force snd_pcm_format_t) 27) /* in four bytes, LSB justified */
#define SNDRV_PCM_FORMAT_U20_BE ((__force snd_pcm_format_t) 28) /* in four bytes, LSB justified */
/* gap in the numbering for a future standard linear format */
#define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31) #define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31)
#define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32) /* in three bytes */ #define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32) /* in three bytes */
#define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33) /* in three bytes */ #define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33) /* in three bytes */
...@@ -248,6 +253,8 @@ typedef int __bitwise snd_pcm_format_t; ...@@ -248,6 +253,8 @@ typedef int __bitwise snd_pcm_format_t;
#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_LE #define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_LE
#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_LE #define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_LE
#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE #define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
#define SNDRV_PCM_FORMAT_S20 SNDRV_PCM_FORMAT_S20_LE
#define SNDRV_PCM_FORMAT_U20 SNDRV_PCM_FORMAT_U20_LE
#endif #endif
#ifdef SNDRV_BIG_ENDIAN #ifdef SNDRV_BIG_ENDIAN
#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE
...@@ -259,6 +266,8 @@ typedef int __bitwise snd_pcm_format_t; ...@@ -259,6 +266,8 @@ typedef int __bitwise snd_pcm_format_t;
#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_BE #define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_BE
#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_BE #define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_BE
#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE #define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
#define SNDRV_PCM_FORMAT_S20 SNDRV_PCM_FORMAT_S20_BE
#define SNDRV_PCM_FORMAT_U20 SNDRV_PCM_FORMAT_U20_BE
#endif #endif
typedef int __bitwise snd_pcm_subformat_t; typedef int __bitwise snd_pcm_subformat_t;
......
...@@ -163,13 +163,30 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { ...@@ -163,13 +163,30 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
.width = 32, .phys = 32, .le = 0, .signd = 0, .width = 32, .phys = 32, .le = 0, .signd = 0,
.silence = { 0x69, 0x69, 0x69, 0x69 }, .silence = { 0x69, 0x69, 0x69, 0x69 },
}, },
/* FIXME: the following three formats are not defined properly yet */ /* FIXME: the following two formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = { [SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1, .le = -1, .signd = -1,
}, },
[SNDRV_PCM_FORMAT_GSM] = { [SNDRV_PCM_FORMAT_GSM] = {
.le = -1, .signd = -1, .le = -1, .signd = -1,
}, },
[SNDRV_PCM_FORMAT_S20_LE] = {
.width = 20, .phys = 32, .le = 1, .signd = 1,
.silence = {},
},
[SNDRV_PCM_FORMAT_S20_BE] = {
.width = 20, .phys = 32, .le = 0, .signd = 1,
.silence = {},
},
[SNDRV_PCM_FORMAT_U20_LE] = {
.width = 20, .phys = 32, .le = 1, .signd = 0,
.silence = { 0x00, 0x00, 0x08, 0x00 },
},
[SNDRV_PCM_FORMAT_U20_BE] = {
.width = 20, .phys = 32, .le = 0, .signd = 0,
.silence = { 0x00, 0x08, 0x00, 0x00 },
},
/* FIXME: the following format is not defined properly yet */
[SNDRV_PCM_FORMAT_SPECIAL] = { [SNDRV_PCM_FORMAT_SPECIAL] = {
.le = -1, .signd = -1, .le = -1, .signd = -1,
}, },
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include "../codecs/tlv320aic23.h" #include "../codecs/tlv320aic23.h"
#include "imx-ssi.h" #include "imx-ssi.h"
#include "fsl_ssi.h"
#include "imx-audmux.h" #include "imx-audmux.h"
#define CODEC_CLOCK 12000000 #define CODEC_CLOCK 12000000
......
...@@ -442,8 +442,8 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ...@@ -442,8 +442,8 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
if (fsl_asoc_card_is_ac97(priv)) { if (fsl_asoc_card_is_ac97(priv)) {
#if IS_ENABLED(CONFIG_SND_AC97_CODEC) #if IS_ENABLED(CONFIG_SND_AC97_CODEC)
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_component *component = rtd->codec_dai->component;
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
/* /*
* Use slots 3/4 for S/PDIF so SSI won't try to enable * Use slots 3/4 for S/PDIF so SSI won't try to enable
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
#define REG_ASRDOC 0x74 #define REG_ASRDOC 0x74
#define REG_ASRDI(i) (REG_ASRDIA + (i << 3)) #define REG_ASRDI(i) (REG_ASRDIA + (i << 3))
#define REG_ASRDO(i) (REG_ASRDOA + (i << 3)) #define REG_ASRDO(i) (REG_ASRDOA + (i << 3))
#define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i)) #define REG_ASRDx(x, i) ((x) == IN ? REG_ASRDI(i) : REG_ASRDO(i))
#define REG_ASRIDRHA 0x80 #define REG_ASRIDRHA 0x80
#define REG_ASRIDRLA 0x84 #define REG_ASRIDRLA 0x84
......
...@@ -913,8 +913,8 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) ...@@ -913,8 +913,8 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
dma->dai.pcm_free = fsl_dma_free_dma_buffers; dma->dai.pcm_free = fsl_dma_free_dma_buffers;
/* Store the SSI-specific information that we need */ /* Store the SSI-specific information that we need */
dma->ssi_stx_phys = res.start + CCSR_SSI_STX0; dma->ssi_stx_phys = res.start + REG_SSI_STX0;
dma->ssi_srx_phys = res.start + CCSR_SSI_SRX0; dma->ssi_srx_phys = res.start + REG_SSI_SRX0;
iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
if (iprop) if (iprop)
......
...@@ -69,21 +69,35 @@ ...@@ -69,21 +69,35 @@
* samples will be written to STX properly. * samples will be written to STX properly.
*/ */
#ifdef __BIG_ENDIAN #ifdef __BIG_ENDIAN
#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ #define FSLSSI_I2S_FORMATS \
SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \ (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE) SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_BE)
#else #else
#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ #define FSLSSI_I2S_FORMATS \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
#endif #endif
#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \ #define FSLSSI_SIER_DBG_RX_FLAGS \
CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \ (SSI_SIER_RFF0_EN | \
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN) SSI_SIER_RLS_EN | \
#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \ SSI_SIER_RFS_EN | \
CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \ SSI_SIER_ROE0_EN | \
CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN) SSI_SIER_RFRC_EN)
#define FSLSSI_SIER_DBG_TX_FLAGS \
(SSI_SIER_TFE0_EN | \
SSI_SIER_TLS_EN | \
SSI_SIER_TFS_EN | \
SSI_SIER_TUE0_EN | \
SSI_SIER_TFRC_EN)
enum fsl_ssi_type { enum fsl_ssi_type {
FSL_SSI_MCP8610, FSL_SSI_MCP8610,
...@@ -92,23 +106,18 @@ enum fsl_ssi_type { ...@@ -92,23 +106,18 @@ enum fsl_ssi_type {
FSL_SSI_MX51, FSL_SSI_MX51,
}; };
struct fsl_ssi_reg_val { struct fsl_ssi_regvals {
u32 sier; u32 sier;
u32 srcr; u32 srcr;
u32 stcr; u32 stcr;
u32 scr; u32 scr;
}; };
struct fsl_ssi_rxtx_reg_val {
struct fsl_ssi_reg_val rx;
struct fsl_ssi_reg_val tx;
};
static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case CCSR_SSI_SACCEN: case REG_SSI_SACCEN:
case CCSR_SSI_SACCDIS: case REG_SSI_SACCDIS:
return false; return false;
default: default:
return true; return true;
...@@ -118,18 +127,18 @@ static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) ...@@ -118,18 +127,18 @@ static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case CCSR_SSI_STX0: case REG_SSI_STX0:
case CCSR_SSI_STX1: case REG_SSI_STX1:
case CCSR_SSI_SRX0: case REG_SSI_SRX0:
case CCSR_SSI_SRX1: case REG_SSI_SRX1:
case CCSR_SSI_SISR: case REG_SSI_SISR:
case CCSR_SSI_SFCSR: case REG_SSI_SFCSR:
case CCSR_SSI_SACNT: case REG_SSI_SACNT:
case CCSR_SSI_SACADD: case REG_SSI_SACADD:
case CCSR_SSI_SACDAT: case REG_SSI_SACDAT:
case CCSR_SSI_SATAG: case REG_SSI_SATAG:
case CCSR_SSI_SACCST: case REG_SSI_SACCST:
case CCSR_SSI_SOR: case REG_SSI_SOR:
return true; return true;
default: default:
return false; return false;
...@@ -139,12 +148,12 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) ...@@ -139,12 +148,12 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg) static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case CCSR_SSI_SRX0: case REG_SSI_SRX0:
case CCSR_SSI_SRX1: case REG_SSI_SRX1:
case CCSR_SSI_SISR: case REG_SSI_SISR:
case CCSR_SSI_SACADD: case REG_SSI_SACADD:
case CCSR_SSI_SACDAT: case REG_SSI_SACDAT:
case CCSR_SSI_SATAG: case REG_SSI_SATAG:
return true; return true;
default: default:
return false; return false;
...@@ -154,9 +163,9 @@ static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg) ...@@ -154,9 +163,9 @@ static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg)
static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case CCSR_SSI_SRX0: case REG_SSI_SRX0:
case CCSR_SSI_SRX1: case REG_SSI_SRX1:
case CCSR_SSI_SACCST: case REG_SSI_SACCST:
return false; return false;
default: default:
return true; return true;
...@@ -164,12 +173,12 @@ static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) ...@@ -164,12 +173,12 @@ static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
} }
static const struct regmap_config fsl_ssi_regconfig = { static const struct regmap_config fsl_ssi_regconfig = {
.max_register = CCSR_SSI_SACCDIS, .max_register = REG_SSI_SACCDIS,
.reg_bits = 32, .reg_bits = 32,
.val_bits = 32, .val_bits = 32,
.reg_stride = 4, .reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_NATIVE, .val_format_endian = REGMAP_ENDIAN_NATIVE,
.num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1, .num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1,
.readable_reg = fsl_ssi_readable_reg, .readable_reg = fsl_ssi_readable_reg,
.volatile_reg = fsl_ssi_volatile_reg, .volatile_reg = fsl_ssi_volatile_reg,
.precious_reg = fsl_ssi_precious_reg, .precious_reg = fsl_ssi_precious_reg,
...@@ -185,78 +194,79 @@ struct fsl_ssi_soc_data { ...@@ -185,78 +194,79 @@ struct fsl_ssi_soc_data {
}; };
/** /**
* fsl_ssi_private: per-SSI private data * fsl_ssi: per-SSI private data
* *
* @reg: Pointer to the regmap registers * @regs: Pointer to the regmap registers
* @irq: IRQ of this SSI * @irq: IRQ of this SSI
* @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
* @i2s_mode: i2s and network mode configuration of the device. Is used to * @i2s_net: I2S and Network mode configurations of SCR register
* switch between normal and i2s/network mode
* mode depending on the number of channels
* @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 both FIFOs used * @use_dual_fifo: DMA with support for dual FIFO mode
* @fifo_deph: Depth of the SSI FIFOs * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
* @slot_width: width of each DAI slot * @fifo_depth: Depth of the SSI FIFOs
* @slots: number of slots * @slot_width: Width of each DAI slot
* @rxtx_reg_val: Specific register settings for receive/transmit configuration * @slots: Number of slots
* @regvals: Specific RX/TX register settings
* *
* @clk: SSI clock * @clk: Clock source to access register
* @baudclk: SSI baud clock for master mode * @baudclk: Clock source to generate bit and frame-sync clocks
* @baudclk_streams: Active streams that are using baudclk * @baudclk_streams: Active streams that are using baudclk
* *
* @regcache_sfcsr: Cache sfcsr register value during suspend and resume
* @regcache_sacnt: Cache sacnt register value during suspend and resume
*
* @dma_params_tx: DMA transmit parameters * @dma_params_tx: DMA transmit parameters
* @dma_params_rx: DMA receive parameters * @dma_params_rx: DMA receive parameters
* @ssi_phys: physical address of the SSI registers * @ssi_phys: physical address of the SSI registers
* *
* @fiq_params: FIQ stream filtering parameters * @fiq_params: FIQ stream filtering parameters
* *
* @pdev: Pointer to pdev used for deprecated fsl-ssi sound card * @pdev: Pointer to pdev when using fsl-ssi as sound card (ppc only)
* TODO: Should be replaced with simple-sound-card
* *
* @dbg_stats: Debugging statistics * @dbg_stats: Debugging statistics
* *
* @soc: SoC specific data * @soc: SoC specific data
* @dev: Pointer to &pdev->dev
* *
* @fifo_watermark: the FIFO watermark setting. Notifies DMA when * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are
* there are @fifo_watermark or fewer words in TX fifo or * @fifo_watermark or fewer words in TX fifo or
* @fifo_watermark or more empty words in RX fifo. * @fifo_watermark or more empty words in RX fifo.
* @dma_maxburst: max number of words to transfer in one go. So far, * @dma_maxburst: Max number of words to transfer in one go. So far,
* this is always the same as fifo_watermark. * this is always the same as fifo_watermark.
*
* @ac97_reg_lock: Mutex lock to serialize AC97 register access operations
*/ */
struct fsl_ssi_private { struct fsl_ssi {
struct regmap *regs; struct regmap *regs;
int irq; int irq;
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 i2s_mode; u8 i2s_net;
bool use_dma; bool use_dma;
bool use_dual_fifo; bool use_dual_fifo;
bool has_ipg_clk_name; bool has_ipg_clk_name;
unsigned int fifo_depth; unsigned int fifo_depth;
unsigned int slot_width; unsigned int slot_width;
unsigned int slots; unsigned int slots;
struct fsl_ssi_rxtx_reg_val rxtx_reg_val; struct fsl_ssi_regvals regvals[2];
struct clk *clk; struct clk *clk;
struct clk *baudclk; struct clk *baudclk;
unsigned int baudclk_streams; unsigned int baudclk_streams;
/* regcache for volatile regs */
u32 regcache_sfcsr; u32 regcache_sfcsr;
u32 regcache_sacnt; u32 regcache_sacnt;
/* DMA params */
struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_rx;
dma_addr_t ssi_phys; dma_addr_t ssi_phys;
/* params for non-dma FIQ stream filtered mode */
struct imx_pcm_fiq_params fiq_params; struct imx_pcm_fiq_params fiq_params;
/* Used when using fsl-ssi as sound-card. This is only used by ppc and
* should be replaced with simple-sound-card. */
struct platform_device *pdev; struct platform_device *pdev;
struct fsl_ssi_dbg dbg_stats; struct fsl_ssi_dbg dbg_stats;
...@@ -271,27 +281,27 @@ struct fsl_ssi_private { ...@@ -271,27 +281,27 @@ struct fsl_ssi_private {
}; };
/* /*
* imx51 and later SoCs have a slightly different IP that allows the * SoC specific data
* SSI configuration while the SSI unit is running.
*
* More important, it is necessary on those SoCs to configure the
* sperate TX/RX DMA bits just before starting the stream
* (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
* sends any DMA requests to the SDMA unit, otherwise it is not defined
* how the SDMA unit handles the DMA request.
* *
* SDMA units are present on devices starting at imx35 but the imx35 * Notes:
* reference manual states that the DMA bits should not be changed * 1) SSI in earlier SoCS has critical bits in control registers that
* while the SSI unit is running (SSIEN). So we support the necessary * cannot be changed after SSI starts running -- a software reset
* online configuration of fsl-ssi starting at imx51. * (set SSIEN to 0) is required to change their values. So adding
* an offline_config flag for these SoCs.
* 2) SDMA is available since imx35. However, imx35 does not support
* DMA bits changing when SSI is running, so set offline_config.
* 3) imx51 and later versions support register configurations when
* SSI is running (SSIEN); For these versions, DMA needs to be
* configured before SSI sends DMA request to avoid an undefined
* DMA request on the SDMA side.
*/ */
static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
.imx = false, .imx = false,
.offline_config = true, .offline_config = true,
.sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC | .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | SSI_SISR_ROE0 | SSI_SISR_ROE1 |
CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1, SSI_SISR_TUE0 | SSI_SISR_TUE1,
}; };
static struct fsl_ssi_soc_data fsl_ssi_imx21 = { static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
...@@ -304,16 +314,16 @@ static struct fsl_ssi_soc_data fsl_ssi_imx21 = { ...@@ -304,16 +314,16 @@ static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
static struct fsl_ssi_soc_data fsl_ssi_imx35 = { static struct fsl_ssi_soc_data fsl_ssi_imx35 = {
.imx = true, .imx = true,
.offline_config = true, .offline_config = true,
.sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC | .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC |
CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | SSI_SISR_ROE0 | SSI_SISR_ROE1 |
CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1, SSI_SISR_TUE0 | SSI_SISR_TUE1,
}; };
static struct fsl_ssi_soc_data fsl_ssi_imx51 = { static struct fsl_ssi_soc_data fsl_ssi_imx51 = {
.imx = true, .imx = true,
.offline_config = false, .offline_config = false,
.sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | .sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 |
CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1, SSI_SISR_TUE0 | SSI_SISR_TUE1,
}; };
static const struct of_device_id fsl_ssi_ids[] = { static const struct of_device_id fsl_ssi_ids[] = {
...@@ -325,108 +335,86 @@ static const struct of_device_id fsl_ssi_ids[] = { ...@@ -325,108 +335,86 @@ static const struct of_device_id fsl_ssi_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, fsl_ssi_ids); MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private) static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi)
{ {
return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
SND_SOC_DAIFMT_AC97; SND_SOC_DAIFMT_AC97;
} }
static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private) static bool fsl_ssi_is_i2s_master(struct fsl_ssi *ssi)
{ {
return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
SND_SOC_DAIFMT_CBS_CFS; SND_SOC_DAIFMT_CBS_CFS;
} }
static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi_private *ssi_private) static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi)
{ {
return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
SND_SOC_DAIFMT_CBM_CFS; SND_SOC_DAIFMT_CBM_CFS;
} }
/** /**
* fsl_ssi_isr: SSI interrupt handler * Interrupt handler to gather states
*
* Although it's possible to use the interrupt handler to send and receive
* data to/from the SSI, we use the DMA instead. Programming is more
* complicated, but the performance is much better.
*
* This interrupt handler is used only to gather statistics.
*
* @irq: IRQ of the SSI device
* @dev_id: pointer to the ssi_private structure for this SSI device
*/ */
static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
{ {
struct fsl_ssi_private *ssi_private = dev_id; struct fsl_ssi *ssi = dev_id;
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
__be32 sisr; __be32 sisr;
__be32 sisr2; __be32 sisr2;
/* We got an interrupt, so read the status register to see what we regmap_read(regs, REG_SSI_SISR, &sisr);
were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in.
*/
regmap_read(regs, CCSR_SSI_SISR, &sisr);
sisr2 = sisr & ssi_private->soc->sisr_write_mask; sisr2 = sisr & ssi->soc->sisr_write_mask;
/* Clear the bits that we set */ /* Clear the bits that we set */
if (sisr2) if (sisr2)
regmap_write(regs, CCSR_SSI_SISR, sisr2); regmap_write(regs, REG_SSI_SISR, sisr2);
fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr); fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/* /**
* Enable/Disable all rx/tx config flags at once. * Enable or disable all rx/tx config flags at once
*/ */
static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable)
bool enable)
{ {
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val; struct fsl_ssi_regvals *vals = ssi->regvals;
if (enable) { if (enable) {
regmap_update_bits(regs, CCSR_SSI_SIER, regmap_update_bits(regs, REG_SSI_SIER,
vals->rx.sier | vals->tx.sier, vals[RX].sier | vals[TX].sier,
vals->rx.sier | vals->tx.sier); vals[RX].sier | vals[TX].sier);
regmap_update_bits(regs, CCSR_SSI_SRCR, regmap_update_bits(regs, REG_SSI_SRCR,
vals->rx.srcr | vals->tx.srcr, vals[RX].srcr | vals[TX].srcr,
vals->rx.srcr | vals->tx.srcr); vals[RX].srcr | vals[TX].srcr);
regmap_update_bits(regs, CCSR_SSI_STCR, regmap_update_bits(regs, REG_SSI_STCR,
vals->rx.stcr | vals->tx.stcr, vals[RX].stcr | vals[TX].stcr,
vals->rx.stcr | vals->tx.stcr); vals[RX].stcr | vals[TX].stcr);
} else { } else {
regmap_update_bits(regs, CCSR_SSI_SRCR, regmap_update_bits(regs, REG_SSI_SRCR,
vals->rx.srcr | vals->tx.srcr, 0); vals[RX].srcr | vals[TX].srcr, 0);
regmap_update_bits(regs, CCSR_SSI_STCR, regmap_update_bits(regs, REG_SSI_STCR,
vals->rx.stcr | vals->tx.stcr, 0); vals[RX].stcr | vals[TX].stcr, 0);
regmap_update_bits(regs, CCSR_SSI_SIER, regmap_update_bits(regs, REG_SSI_SIER,
vals->rx.sier | vals->tx.sier, 0); vals[RX].sier | vals[TX].sier, 0);
} }
} }
/* /**
* Clear RX or TX FIFO to remove samples from the previous * Clear remaining data in the FIFO to avoid dirty data or channel slipping
* stream session which may be still present in the FIFO and
* may introduce bad samples and/or channel slipping.
*
* Note: The SOR is not documented in recent IMX datasheet, but
* is described in IMX51 reference manual at section 56.3.3.15.
*/ */
static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private, static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx)
bool is_rx)
{ {
if (is_rx) { bool tx = !is_rx;
regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR); regmap_update_bits(ssi->regs, REG_SSI_SOR,
} else { SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR);
}
} }
/* /**
* Calculate the bits that have to be disabled for the current stream that is * Calculate the bits that have to be disabled for the current stream that is
* getting disabled. This keeps the bits enabled that are necessary for the * getting disabled. This keeps the bits enabled that are necessary for the
* second stream to work if 'stream_active' is true. * second stream to work if 'stream_active' is true.
...@@ -446,261 +434,239 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private, ...@@ -446,261 +434,239 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private,
((vals_disable) & \ ((vals_disable) & \
((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active)))) ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
/* /**
* Enable/Disable a ssi configuration. You have to pass either * Enable or disable SSI configuration.
* ssi_private->rxtx_reg_val.rx or tx as vals parameter.
*/ */
static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
struct fsl_ssi_reg_val *vals) struct fsl_ssi_regvals *vals)
{ {
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
struct fsl_ssi_reg_val *avals; struct fsl_ssi_regvals *avals;
int nr_active_streams; int nr_active_streams;
u32 scr_val; u32 scr;
int keep_active; int keep_active;
regmap_read(regs, CCSR_SSI_SCR, &scr_val); regmap_read(regs, REG_SSI_SCR, &scr);
nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) + nr_active_streams = !!(scr & SSI_SCR_TE) + !!(scr & SSI_SCR_RE);
!!(scr_val & CCSR_SSI_SCR_RE);
if (nr_active_streams - 1 > 0) if (nr_active_streams - 1 > 0)
keep_active = 1; keep_active = 1;
else else
keep_active = 0; keep_active = 0;
/* Find the other direction values rx or tx which we do not want to /* Get the opposite direction to keep its values untouched */
* modify */ if (&ssi->regvals[RX] == vals)
if (&ssi_private->rxtx_reg_val.rx == vals) avals = &ssi->regvals[TX];
avals = &ssi_private->rxtx_reg_val.tx;
else else
avals = &ssi_private->rxtx_reg_val.rx; avals = &ssi->regvals[RX];
/* If vals should be disabled, start with disabling the unit */
if (!enable) { if (!enable) {
/*
* To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream
*/
u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr, u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
keep_active); keep_active);
regmap_update_bits(regs, CCSR_SSI_SCR, scr, 0); /* Safely disable SCR register for the stream */
regmap_update_bits(regs, REG_SSI_SCR, scr, 0);
} }
/* /*
* We are running on a SoC which does not support online SSI * For cases where online configuration is not supported,
* reconfiguration, so we have to enable all necessary flags at once * 1) Enable all necessary bits of both streams when 1st stream starts
* even if we do not use them later (capture and playback configuration) * even if the opposite stream will not start
* 2) Disable all remaining bits of both streams when last stream ends
*/ */
if (ssi_private->soc->offline_config) { if (ssi->soc->offline_config) {
if ((enable && !nr_active_streams) || if ((enable && !nr_active_streams) || (!enable && !keep_active))
(!enable && !keep_active)) fsl_ssi_rxtx_config(ssi, enable);
fsl_ssi_rxtx_config(ssi_private, enable);
goto config_done; goto config_done;
} }
/* /* Online configure single direction while SSI is running */
* Configure single direction units while the SSI unit is running
* (online configuration)
*/
if (enable) { if (enable) {
fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE); fsl_ssi_fifo_clear(ssi, vals->scr & SSI_SCR_RE);
regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr);
regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr);
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); regmap_update_bits(regs, REG_SSI_SIER, vals->sier, vals->sier);
} else { } else {
u32 sier; u32 sier;
u32 srcr; u32 srcr;
u32 stcr; u32 stcr;
/* /*
* Disabling the necessary flags for one of rx/tx while the * To keep the other stream safe, exclude shared bits between
* other stream is active is a little bit more difficult. We * both streams, and get safe bits to disable current stream
* have to disable only those flags that differ between both
* streams (rx XOR tx) and that are set in the stream that is
* disabled now. Otherwise we could alter flags of the other
* stream
*/ */
/* These assignments are simply vals without bits set in avals*/
sier = fsl_ssi_disable_val(vals->sier, avals->sier, sier = fsl_ssi_disable_val(vals->sier, avals->sier,
keep_active); keep_active);
srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr, srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
keep_active); keep_active);
stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr, stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
keep_active); keep_active);
regmap_update_bits(regs, CCSR_SSI_SRCR, srcr, 0); /* Safely disable other control registers for the stream */
regmap_update_bits(regs, CCSR_SSI_STCR, stcr, 0); regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0);
regmap_update_bits(regs, CCSR_SSI_SIER, sier, 0); regmap_update_bits(regs, REG_SSI_STCR, stcr, 0);
regmap_update_bits(regs, REG_SSI_SIER, sier, 0);
} }
config_done: config_done:
/* Enabling of subunits is done after configuration */ /* Enabling of subunits is done after configuration */
if (enable) { if (enable) {
if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) { /*
/* * Start DMA before setting TE to avoid FIFO underrun
* Be sure the Tx FIFO is filled when TE is set. * which may cause a channel slip or a channel swap
* Otherwise, there are some chances to start the *
* playback with some void samples inserted first, * TODO: FIQ cases might also need this upon testing
* generating a channel slip. */
* if (ssi->use_dma && (vals->scr & SSI_SCR_TE)) {
* First, SSIEN must be set, to let the FIFO be filled.
*
* Notes:
* - Limit this fix to the DMA case until FIQ cases can
* be tested.
* - Limit the length of the busy loop to not lock the
* system too long, even if 1-2 loops are sufficient
* in general.
*/
int i; int i;
int max_loop = 100; int max_loop = 100;
regmap_update_bits(regs, CCSR_SSI_SCR,
CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN); /* 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 */
for (i = 0; i < max_loop; i++) { for (i = 0; i < max_loop; i++) {
u32 sfcsr; u32 sfcsr;
regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr); regmap_read(regs, REG_SSI_SFCSR, &sfcsr);
if (CCSR_SSI_SFCSR_TFCNT0(sfcsr)) if (SSI_SFCSR_TFCNT0(sfcsr))
break; break;
} }
if (i == max_loop) { if (i == max_loop) {
dev_err(ssi_private->dev, dev_err(ssi->dev,
"Timeout waiting TX FIFO filling\n"); "Timeout waiting TX FIFO filling\n");
} }
} }
regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr); /* 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_rx_config(struct fsl_ssi_private *ssi_private, bool enable) static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi)
{ {
fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx); struct regmap *regs = ssi->regs;
/* no SACC{ST,EN,DIS} regs on imx21-class SSI */
if (!ssi->soc->imx21regs) {
/* Disable all channel slots */
regmap_write(regs, REG_SSI_SACCDIS, 0xff);
/* Enable slots 3 & 4 -- PCM Playback Left & Right channels */
regmap_write(regs, REG_SSI_SACCEN, 0x300);
}
} }
static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable) static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable)
{ {
fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx); /*
* 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]);
} }
/* /**
* Setup rx/tx register values used to enable/disable the streams. These will * Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely
* be used later in fsl_ssi_config to setup the streams without the need to
* check for all different SSI modes.
*/ */
static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private) static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi)
{ {
struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val; struct fsl_ssi_regvals *vals = ssi->regvals;
reg->rx.sier = CCSR_SSI_SIER_RFF0_EN; vals[RX].sier = SSI_SIER_RFF0_EN;
reg->rx.srcr = CCSR_SSI_SRCR_RFEN0; vals[RX].srcr = SSI_SRCR_RFEN0;
reg->rx.scr = 0; vals[RX].scr = 0;
reg->tx.sier = CCSR_SSI_SIER_TFE0_EN; vals[TX].sier = SSI_SIER_TFE0_EN;
reg->tx.stcr = CCSR_SSI_STCR_TFEN0; vals[TX].stcr = SSI_STCR_TFEN0;
reg->tx.scr = 0; vals[TX].scr = 0;
if (!fsl_ssi_is_ac97(ssi_private)) { /* AC97 has already enabled SSIEN, RE and TE, so ignore them */
reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE; if (!fsl_ssi_is_ac97(ssi)) {
reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN; vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE;
reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE; vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN;
} }
if (ssi_private->use_dma) { if (ssi->use_dma) {
reg->rx.sier |= CCSR_SSI_SIER_RDMAE; vals[RX].sier |= SSI_SIER_RDMAE;
reg->tx.sier |= CCSR_SSI_SIER_TDMAE; vals[TX].sier |= SSI_SIER_TDMAE;
} else { } else {
reg->rx.sier |= CCSR_SSI_SIER_RIE; vals[RX].sier |= SSI_SIER_RIE;
reg->tx.sier |= CCSR_SSI_SIER_TIE; vals[TX].sier |= SSI_SIER_TIE;
} }
reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS; vals[RX].sier |= FSLSSI_SIER_DBG_RX_FLAGS;
reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS; vals[TX].sier |= FSLSSI_SIER_DBG_TX_FLAGS;
} }
static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
{ {
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
/*
* Setup the clock control register
*/
regmap_write(regs, CCSR_SSI_STCCR,
CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
regmap_write(regs, CCSR_SSI_SRCCR,
CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
/* /* Setup the clock control register */
* Enable AC97 mode and startup the SSI regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
*/ regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13));
regmap_write(regs, CCSR_SSI_SACNT,
CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
/* no SACC{ST,EN,DIS} regs on imx21-class SSI */ /* Enable AC97 mode and startup the SSI */
if (!ssi_private->soc->imx21regs) { regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV);
regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
}
/* /* AC97 has to communicate with codec before starting a stream */
* Enable SSI, Transmit and Receive. AC97 has to communicate with the regmap_update_bits(regs, REG_SSI_SCR,
* codec before a stream is started. SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE,
*/ SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE);
regmap_update_bits(regs, CCSR_SSI_SCR,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_WAIT(3)); regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3));
} }
/**
* fsl_ssi_startup: create a new substream
*
* This is the first function called when a stream is opened.
*
* If this is the first stream open, then grab the IRQ and program most of
* the SSI registers.
*/
static int fsl_ssi_startup(struct snd_pcm_substream *substream, static int fsl_ssi_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai);
snd_soc_dai_get_drvdata(rtd->cpu_dai);
int ret; int ret;
ret = clk_prepare_enable(ssi_private->clk); ret = clk_prepare_enable(ssi->clk);
if (ret) if (ret)
return ret; return ret;
/* When using dual fifo mode, it is safer to ensure an even period /*
* When using dual fifo mode, it is safer to ensure an even period
* size. If appearing to an odd number while DMA always starts its * size. If appearing to an odd number while DMA always starts its
* task from fifo0, fifo1 would be neglected at the end of each * task from fifo0, fifo1 would be neglected at the end of each
* period. But SSI would still access fifo1 with an invalid data. * period. But SSI would still access fifo1 with an invalid data.
*/ */
if (ssi_private->use_dual_fifo) if (ssi->use_dual_fifo)
snd_pcm_hw_constraint_step(substream->runtime, 0, snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
return 0; return 0;
} }
/**
* fsl_ssi_shutdown: shutdown the SSI
*
*/
static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai);
snd_soc_dai_get_drvdata(rtd->cpu_dai);
clk_disable_unprepare(ssi_private->clk);
clk_disable_unprepare(ssi->clk);
} }
/** /**
* fsl_ssi_set_bclk - configure Digital Audio Interface bit clock * Configure Digital Audio Interface bit clock
* *
* Note: This function can be only called when using SSI as DAI master * Note: This function can be only called when using SSI as DAI master
* *
...@@ -709,12 +675,13 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, ...@@ -709,12 +675,13 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
* (In 2-channel I2S Master mode, slot_width is fixed 32) * (In 2-channel I2S Master mode, slot_width is fixed 32)
*/ */
static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai, struct snd_soc_dai *dai,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct regmap *regs = ssi_private->regs; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; 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);
...@@ -724,29 +691,29 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -724,29 +691,29 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
bool baudclk_is_used; bool baudclk_is_used;
/* Override slots and slot_width if being specifically set... */ /* Override slots and slot_width if being specifically set... */
if (ssi_private->slots) if (ssi->slots)
slots = ssi_private->slots; slots = ssi->slots;
/* ...but keep 32 bits if slots is 2 -- I2S Master mode */ /* ...but keep 32 bits if slots is 2 -- I2S Master mode */
if (ssi_private->slot_width && slots != 2) if (ssi->slot_width && slots != 2)
slot_width = ssi_private->slot_width; slot_width = ssi->slot_width;
/* Generate bit clock based on the slot number and slot width */ /* Generate bit clock based on the slot number and slot width */
freq = slots * slot_width * params_rate(hw_params); freq = slots * slot_width * params_rate(hw_params);
/* Don't apply it to any non-baudclk circumstance */ /* Don't apply it to any non-baudclk circumstance */
if (IS_ERR(ssi_private->baudclk)) if (IS_ERR(ssi->baudclk))
return -EINVAL; return -EINVAL;
/* /*
* Hardware limitation: The bclk rate must be * Hardware limitation: The bclk rate must be
* never greater than 1/5 IPG clock rate * never greater than 1/5 IPG clock rate
*/ */
if (freq * 5 > clk_get_rate(ssi_private->clk)) { if (freq * 5 > clk_get_rate(ssi->clk)) {
dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n"); dev_err(dai->dev, "bitclk > ipgclk / 5\n");
return -EINVAL; return -EINVAL;
} }
baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream)); baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream));
/* It should be already enough to divide clock by setting pm alone */ /* It should be already enough to divide clock by setting pm alone */
psr = 0; psr = 0;
...@@ -758,9 +725,9 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -758,9 +725,9 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
tmprate = freq * factor * (i + 1); tmprate = freq * factor * (i + 1);
if (baudclk_is_used) if (baudclk_is_used)
clkrate = clk_get_rate(ssi_private->baudclk); clkrate = clk_get_rate(ssi->baudclk);
else else
clkrate = clk_round_rate(ssi_private->baudclk, tmprate); clkrate = clk_round_rate(ssi->baudclk, tmprate);
clkrate /= factor; clkrate /= factor;
afreq = clkrate / (i + 1); afreq = clkrate / (i + 1);
...@@ -791,24 +758,22 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -791,24 +758,22 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
/* No proper pm found if it is still remaining the initial value */ /* No proper pm found if it is still remaining the initial value */
if (pm == 999) { if (pm == 999) {
dev_err(cpu_dai->dev, "failed to handle the required sysclk\n"); dev_err(dai->dev, "failed to handle the required sysclk\n");
return -EINVAL; return -EINVAL;
} }
stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) | stccr = SSI_SxCCR_PM(pm + 1) | (div2 ? SSI_SxCCR_DIV2 : 0) |
(psr ? CCSR_SSI_SxCCR_PSR : 0); (psr ? SSI_SxCCR_PSR : 0);
mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
CCSR_SSI_SxCCR_PSR;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || synchronous) /* STCCR is used for RX in synchronous mode */
regmap_update_bits(regs, CCSR_SSI_STCCR, mask, stccr); tx2 = tx || synchronous;
else regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
regmap_update_bits(regs, CCSR_SSI_SRCCR, mask, stccr);
if (!baudclk_is_used) { if (!baudclk_is_used) {
ret = clk_set_rate(ssi_private->baudclk, baudrate); ret = clk_set_rate(ssi->baudclk, baudrate);
if (ret) { if (ret) {
dev_err(cpu_dai->dev, "failed to set baudclk rate\n"); dev_err(dai->dev, "failed to set baudclk rate\n");
return -EINVAL; return -EINVAL;
} }
} }
...@@ -817,185 +782,165 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -817,185 +782,165 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
} }
/** /**
* fsl_ssi_hw_params - program the sample size * Configure SSI based on PCM hardware parameters
*
* Most of the SSI registers have been programmed in the startup function,
* but the word length must be programmed here. Unfortunately, programming
* the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
* cause a problem with supporting simultaneous playback and capture. If
* the SSI is already playing a stream, then that stream may be temporarily
* stopped when you start capture.
* *
* Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the * Notes:
* clock master. * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily
* disabled on offline_config SoCs. Even for online configurable SoCs
* running in synchronous mode (both TX and RX use STCCR), it is not
* safe to re-configure them when both two streams start running.
* 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the
* fsl_ssi_set_bclk() if SSI is the DAI clock master.
*/ */
static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) struct snd_pcm_hw_params *hw_params,
struct snd_soc_dai *dai)
{ {
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct regmap *regs = ssi_private->regs; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
struct regmap *regs = ssi->regs;
unsigned int channels = params_channels(hw_params); unsigned int channels = params_channels(hw_params);
unsigned int sample_size = params_width(hw_params); unsigned int sample_size = params_width(hw_params);
u32 wl = CCSR_SSI_SxCCR_WL(sample_size); u32 wl = SSI_SxCCR_WL(sample_size);
int ret; int ret;
u32 scr_val; u32 scr;
int enabled; int enabled;
regmap_read(regs, CCSR_SSI_SCR, &scr_val); regmap_read(regs, REG_SSI_SCR, &scr);
enabled = scr_val & CCSR_SSI_SCR_SSIEN; enabled = scr & SSI_SCR_SSIEN;
/* /*
* If we're in synchronous mode, and the SSI is already enabled, * SSI is properly configured if it is enabled and running in
* then STCCR is already set properly. * the synchronous mode; Note that AC97 mode is an exception
* that should set separate configurations for STCCR and SRCCR
* despite running in the synchronous mode.
*/ */
if (enabled && ssi_private->cpu_dai_drv.symmetric_rates) if (enabled && ssi->cpu_dai_drv.symmetric_rates)
return 0; return 0;
if (fsl_ssi_is_i2s_master(ssi_private)) { if (fsl_ssi_is_i2s_master(ssi)) {
ret = fsl_ssi_set_bclk(substream, cpu_dai, hw_params); ret = fsl_ssi_set_bclk(substream, dai, hw_params);
if (ret) if (ret)
return ret; return ret;
/* Do not enable the clock if it is already enabled */ /* Do not enable the clock if it is already enabled */
if (!(ssi_private->baudclk_streams & BIT(substream->stream))) { if (!(ssi->baudclk_streams & BIT(substream->stream))) {
ret = clk_prepare_enable(ssi_private->baudclk); ret = clk_prepare_enable(ssi->baudclk);
if (ret) if (ret)
return ret; return ret;
ssi_private->baudclk_streams |= BIT(substream->stream); ssi->baudclk_streams |= BIT(substream->stream);
} }
} }
if (!fsl_ssi_is_ac97(ssi_private)) { if (!fsl_ssi_is_ac97(ssi)) {
u8 i2smode; u8 i2s_net;
/* /* Normal + Network mode to send 16-bit data in 32-bit frames */
* Switch to normal net mode in order to have a frame sync if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16)
* signal every 32 bits instead of 16 bits i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
*/
if (fsl_ssi_is_i2s_cbm_cfs(ssi_private) && sample_size == 16)
i2smode = CCSR_SSI_SCR_I2S_MODE_NORMAL |
CCSR_SSI_SCR_NET;
else else
i2smode = ssi_private->i2s_mode; i2s_net = ssi->i2s_net;
regmap_update_bits(regs, CCSR_SSI_SCR, regmap_update_bits(regs, REG_SSI_SCR,
CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, SSI_SCR_I2S_NET_MASK,
channels == 1 ? 0 : i2smode); channels == 1 ? 0 : i2s_net);
} }
/*
* FIXME: The documentation says that SxCCR[WL] should not be
* modified while the SSI is enabled. The only time this can
* happen is if we're trying to do simultaneous playback and
* capture in asynchronous mode. Unfortunately, I have been enable
* to get that to work at all on the P1022DS. Therefore, we don't
* bother to disable/enable the SSI when setting SxCCR[WL], because
* the SSI will stop anyway. Maybe one day, this will get fixed.
*/
/* In synchronous mode, the SSI uses STCCR for capture */ /* In synchronous mode, the SSI uses STCCR for capture */
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || tx2 = tx || ssi->cpu_dai_drv.symmetric_rates;
ssi_private->cpu_dai_drv.symmetric_rates) regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_WL_MASK,
wl);
else
regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK,
wl);
return 0; return 0;
} }
static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, static int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai);
snd_soc_dai_get_drvdata(rtd->cpu_dai);
if (fsl_ssi_is_i2s_master(ssi_private) && if (fsl_ssi_is_i2s_master(ssi) &&
ssi_private->baudclk_streams & BIT(substream->stream)) { ssi->baudclk_streams & BIT(substream->stream)) {
clk_disable_unprepare(ssi_private->baudclk); clk_disable_unprepare(ssi->baudclk);
ssi_private->baudclk_streams &= ~BIT(substream->stream); ssi->baudclk_streams &= ~BIT(substream->stream);
} }
return 0; return 0;
} }
static int _fsl_ssi_set_dai_fmt(struct device *dev, static int _fsl_ssi_set_dai_fmt(struct device *dev,
struct fsl_ssi_private *ssi_private, struct fsl_ssi *ssi, unsigned int fmt)
unsigned int fmt)
{ {
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
u32 strcr = 0, stcr, srcr, scr, mask; u32 strcr = 0, stcr, srcr, scr, mask;
u8 wm; u8 wm;
ssi_private->dai_fmt = fmt; ssi->dai_fmt = fmt;
if (fsl_ssi_is_i2s_master(ssi_private) && IS_ERR(ssi_private->baudclk)) { if (fsl_ssi_is_i2s_master(ssi) && IS_ERR(ssi->baudclk)) {
dev_err(dev, "baudclk is missing which is necessary for master mode\n"); dev_err(dev, "missing baudclk for master mode\n");
return -EINVAL; return -EINVAL;
} }
fsl_ssi_setup_reg_vals(ssi_private); fsl_ssi_setup_regvals(ssi);
regmap_read(regs, CCSR_SSI_SCR, &scr); regmap_read(regs, REG_SSI_SCR, &scr);
scr &= ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK); scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK);
scr |= CCSR_SSI_SCR_SYNC_TX_FS; /* Synchronize frame sync clock for TE to avoid data slipping */
scr |= SSI_SCR_SYNC_TX_FS;
mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR | mask = SSI_STCR_TXBIT0 | SSI_STCR_TFDIR | SSI_STCR_TXDIR |
CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS;
CCSR_SSI_STCR_TEFS; regmap_read(regs, REG_SSI_STCR, &stcr);
regmap_read(regs, CCSR_SSI_STCR, &stcr); regmap_read(regs, REG_SSI_SRCR, &srcr);
regmap_read(regs, CCSR_SSI_SRCR, &srcr);
stcr &= ~mask; stcr &= ~mask;
srcr &= ~mask; srcr &= ~mask;
ssi_private->i2s_mode = CCSR_SSI_SCR_NET; /* Use Network mode as default */
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, CCSR_SSI_STCCR, regmap_update_bits(regs, REG_SSI_STCCR,
CCSR_SSI_SxCCR_DC_MASK, SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
CCSR_SSI_SxCCR_DC(2)); regmap_update_bits(regs, REG_SSI_SRCCR,
regmap_update_bits(regs, CCSR_SSI_SRCCR, SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
CCSR_SSI_SxCCR_DC_MASK,
CCSR_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_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
ssi_private->i2s_mode |= CCSR_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:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE; ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
/* Data on rising edge of bclk, frame low, 1clk before data */ /* Data on rising edge of bclk, frame low, 1clk before data */
strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP | strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP |
CCSR_SSI_STCR_TXBIT0 | CCSR_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 |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP; strcr |= SSI_STCR_TXBIT0 | 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 |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP |
CCSR_SSI_STCR_TXBIT0 | CCSR_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 |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TXBIT0;
CCSR_SSI_STCR_TXBIT0;
break; break;
case SND_SOC_DAIFMT_AC97: case SND_SOC_DAIFMT_AC97:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL; /* Data on falling edge of bclk, frame high, 1clk before data */
ssi->i2s_net |= SSI_SCR_I2S_MODE_NORMAL;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
scr |= ssi_private->i2s_mode; scr |= ssi->i2s_net;
/* DAI clock inversion */ /* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
...@@ -1004,16 +949,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, ...@@ -1004,16 +949,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
break; break;
case SND_SOC_DAIFMT_IB_NF: case SND_SOC_DAIFMT_IB_NF:
/* Invert bit clock */ /* Invert bit clock */
strcr ^= CCSR_SSI_STCR_TSCKP; strcr ^= SSI_STCR_TSCKP;
break; break;
case SND_SOC_DAIFMT_NB_IF: case SND_SOC_DAIFMT_NB_IF:
/* Invert frame clock */ /* Invert frame clock */
strcr ^= CCSR_SSI_STCR_TFSI; strcr ^= SSI_STCR_TFSI;
break; break;
case SND_SOC_DAIFMT_IB_IF: case SND_SOC_DAIFMT_IB_IF:
/* Invert both clocks */ /* Invert both clocks */
strcr ^= CCSR_SSI_STCR_TSCKP; strcr ^= SSI_STCR_TSCKP;
strcr ^= CCSR_SSI_STCR_TFSI; strcr ^= SSI_STCR_TFSI;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -1022,123 +967,122 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, ...@@ -1022,123 +967,122 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
/* DAI clock master masks */ /* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR; /* Output bit and frame sync clocks */
scr |= CCSR_SSI_SCR_SYS_CLK_EN; strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
scr |= SSI_SCR_SYS_CLK_EN;
break; break;
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; /* 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:
strcr &= ~CCSR_SSI_STCR_TXDIR; /* Input bit clock but output frame sync clock */
strcr |= CCSR_SSI_STCR_TFDIR; strcr &= ~SSI_STCR_TXDIR;
scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; strcr |= SSI_STCR_TFDIR;
scr &= ~SSI_SCR_SYS_CLK_EN;
break; break;
default: default:
if (!fsl_ssi_is_ac97(ssi_private)) if (!fsl_ssi_is_ac97(ssi))
return -EINVAL; return -EINVAL;
} }
stcr |= strcr; stcr |= strcr;
srcr |= strcr; srcr |= strcr;
if (ssi_private->cpu_dai_drv.symmetric_rates /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
|| fsl_ssi_is_ac97(ssi_private)) { if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) {
/* Need to clear RXDIR when using SYNC or AC97 mode */ srcr &= ~SSI_SRCR_RXDIR;
srcr &= ~CCSR_SSI_SRCR_RXDIR; scr |= SSI_SCR_SYN;
scr |= CCSR_SSI_SCR_SYN;
} }
regmap_write(regs, CCSR_SSI_STCR, stcr); regmap_write(regs, REG_SSI_STCR, stcr);
regmap_write(regs, CCSR_SSI_SRCR, srcr); regmap_write(regs, REG_SSI_SRCR, srcr);
regmap_write(regs, CCSR_SSI_SCR, scr); regmap_write(regs, REG_SSI_SCR, scr);
wm = ssi_private->fifo_watermark; wm = ssi->fifo_watermark;
regmap_write(regs, CCSR_SSI_SFCSR, regmap_write(regs, REG_SSI_SFCSR,
CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) |
CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm)); SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm));
if (ssi_private->use_dual_fifo) { if (ssi->use_dual_fifo) {
regmap_update_bits(regs, CCSR_SSI_SRCR, CCSR_SSI_SRCR_RFEN1, regmap_update_bits(regs, REG_SSI_SRCR,
CCSR_SSI_SRCR_RFEN1); SSI_SRCR_RFEN1, SSI_SRCR_RFEN1);
regmap_update_bits(regs, CCSR_SSI_STCR, CCSR_SSI_STCR_TFEN1, regmap_update_bits(regs, REG_SSI_STCR,
CCSR_SSI_STCR_TFEN1); SSI_STCR_TFEN1, SSI_STCR_TFEN1);
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_TCH_EN, regmap_update_bits(regs, REG_SSI_SCR,
CCSR_SSI_SCR_TCH_EN); SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
} }
if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97)
fsl_ssi_setup_ac97(ssi_private); fsl_ssi_setup_ac97(ssi);
return 0; return 0;
} }
/** /**
* fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format. * Configure Digital Audio Interface (DAI) Format
*/ */
static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{ {
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
return _fsl_ssi_set_dai_fmt(cpu_dai->dev, ssi_private, fmt); /* AC97 configured DAIFMT earlier in the probe() */
if (fsl_ssi_is_ac97(ssi))
return 0;
return _fsl_ssi_set_dai_fmt(dai->dev, ssi, fmt);
} }
/** /**
* fsl_ssi_set_dai_tdm_slot - set TDM slot number * Set TDM slot number and slot width
*
* Note: This function can be only called when using SSI as DAI master
*/ */
static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
u32 rx_mask, int slots, int slot_width) u32 rx_mask, int slots, int slot_width)
{ {
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
u32 val; u32 val;
/* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */
if (slot_width & 1 || slot_width < 8 || slot_width > 24) { if (slot_width & 1 || slot_width < 8 || slot_width > 24) {
dev_err(cpu_dai->dev, "invalid slot width: %d\n", slot_width); dev_err(dai->dev, "invalid slot width: %d\n", slot_width);
return -EINVAL; return -EINVAL;
} }
/* 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, CCSR_SSI_SCR, &val); regmap_read(regs, REG_SSI_SCR, &val);
val &= CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET; val &= SSI_SCR_I2S_MODE_MASK | SSI_SCR_NET;
if (val && slots < 2) { if (val && slots < 2) {
dev_err(cpu_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;
} }
regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_DC_MASK, regmap_update_bits(regs, REG_SSI_STCCR,
CCSR_SSI_SxCCR_DC(slots)); SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_DC_MASK, regmap_update_bits(regs, REG_SSI_SRCCR,
CCSR_SSI_SxCCR_DC(slots)); SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
/* The register SxMSKs needs SSI to provide essential clock due to /* Save SSIEN bit of the SCR register */
* hardware design. So we here temporarily enable SSI to set them. regmap_read(regs, REG_SSI_SCR, &val);
*/ val &= SSI_SCR_SSIEN;
regmap_read(regs, CCSR_SSI_SCR, &val); /* Temporarily enable SSI to allow SxMSKs to be configurable */
val &= CCSR_SSI_SCR_SSIEN; regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN,
CCSR_SSI_SCR_SSIEN);
regmap_write(regs, CCSR_SSI_STMSK, ~tx_mask); regmap_write(regs, REG_SSI_STMSK, ~tx_mask);
regmap_write(regs, CCSR_SSI_SRMSK, ~rx_mask); regmap_write(regs, REG_SSI_SRMSK, ~rx_mask);
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val); /* Restore the value of SSIEN bit */
regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val);
ssi_private->slot_width = slot_width; ssi->slot_width = slot_width;
ssi_private->slots = slots; ssi->slots = slots;
return 0; return 0;
} }
/** /**
* fsl_ssi_trigger: start and stop the DMA transfer. * Start or stop SSI and corresponding DMA transaction.
*
* This function is called by ALSA to start, stop, pause, and resume the DMA
* transfer of data.
* *
* The DMA channel is in external master start and pause mode, which * The DMA channel is in external master start and pause mode, which
* means the SSI completely controls the flow of data. * means the SSI completely controls the flow of data.
...@@ -1147,37 +1091,38 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -1147,37 +1091,38 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
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) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
fsl_ssi_tx_config(ssi_private, true); fsl_ssi_tx_config(ssi, true);
else else
fsl_ssi_rx_config(ssi_private, true); fsl_ssi_rx_config(ssi, true);
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) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
fsl_ssi_tx_config(ssi_private, false); fsl_ssi_tx_config(ssi, false);
else else
fsl_ssi_rx_config(ssi_private, false); fsl_ssi_rx_config(ssi, false);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (fsl_ssi_is_ac97(ssi_private)) { /* Clear corresponding FIFO */
if (fsl_ssi_is_ac97(ssi)) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_TX_CLR); regmap_write(regs, REG_SSI_SOR, SSI_SOR_TX_CLR);
else else
regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_RX_CLR); regmap_write(regs, REG_SSI_SOR, SSI_SOR_RX_CLR);
} }
return 0; return 0;
...@@ -1185,27 +1130,26 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -1185,27 +1130,26 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
{ {
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
if (ssi_private->soc->imx && ssi_private->use_dma) { if (ssi->soc->imx && ssi->use_dma) {
dai->playback_dma_data = &ssi_private->dma_params_tx; dai->playback_dma_data = &ssi->dma_params_tx;
dai->capture_dma_data = &ssi_private->dma_params_rx; dai->capture_dma_data = &ssi->dma_params_rx;
} }
return 0; return 0;
} }
static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
.startup = fsl_ssi_startup, .startup = fsl_ssi_startup,
.shutdown = fsl_ssi_shutdown, .shutdown = fsl_ssi_shutdown,
.hw_params = fsl_ssi_hw_params, .hw_params = fsl_ssi_hw_params,
.hw_free = fsl_ssi_hw_free, .hw_free = fsl_ssi_hw_free,
.set_fmt = fsl_ssi_set_dai_fmt, .set_fmt = fsl_ssi_set_dai_fmt,
.set_tdm_slot = fsl_ssi_set_dai_tdm_slot, .set_tdm_slot = fsl_ssi_set_dai_tdm_slot,
.trigger = fsl_ssi_trigger, .trigger = fsl_ssi_trigger,
}; };
/* Template for the CPU dai driver structure */
static struct snd_soc_dai_driver fsl_ssi_dai_template = { static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.probe = fsl_ssi_dai_probe, .probe = fsl_ssi_dai_probe,
.playback = { .playback = {
...@@ -1226,7 +1170,7 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = { ...@@ -1226,7 +1170,7 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
}; };
static const struct snd_soc_component_driver fsl_ssi_component = { static const struct snd_soc_component_driver fsl_ssi_component = {
.name = "fsl-ssi", .name = "fsl-ssi",
}; };
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
...@@ -1237,23 +1181,23 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { ...@@ -1237,23 +1181,23 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000, .rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20,
}, },
.capture = { .capture = {
.stream_name = "AC97 Capture", .stream_name = "AC97 Capture",
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_48000, .rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16-bit capture is broken (errata ERR003778) */
.formats = SNDRV_PCM_FMTBIT_S20,
}, },
.ops = &fsl_ssi_dai_ops, .ops = &fsl_ssi_dai_ops,
}; };
static struct fsl_ssi *fsl_ac97_data;
static struct fsl_ssi_private *fsl_ac97_data;
static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val) unsigned short val)
{ {
struct regmap *regs = fsl_ac97_data->regs; struct regmap *regs = fsl_ac97_data->regs;
unsigned int lreg; unsigned int lreg;
...@@ -1273,13 +1217,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, ...@@ -1273,13 +1217,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
} }
lreg = reg << 12; lreg = reg << 12;
regmap_write(regs, CCSR_SSI_SACADD, lreg); regmap_write(regs, REG_SSI_SACADD, lreg);
lval = val << 4; lval = val << 4;
regmap_write(regs, CCSR_SSI_SACDAT, lval); regmap_write(regs, REG_SSI_SACDAT, lval);
regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK, regmap_update_bits(regs, REG_SSI_SACNT,
CCSR_SSI_SACNT_WR); SSI_SACNT_RDWR_MASK, SSI_SACNT_WR);
udelay(100); udelay(100);
clk_disable_unprepare(fsl_ac97_data->clk); clk_disable_unprepare(fsl_ac97_data->clk);
...@@ -1289,10 +1233,9 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, ...@@ -1289,10 +1233,9 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
} }
static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
unsigned short reg) unsigned short reg)
{ {
struct regmap *regs = fsl_ac97_data->regs; struct regmap *regs = fsl_ac97_data->regs;
unsigned short val = 0; unsigned short val = 0;
u32 reg_val; u32 reg_val;
unsigned int lreg; unsigned int lreg;
...@@ -1302,19 +1245,18 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, ...@@ -1302,19 +1245,18 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
ret = clk_prepare_enable(fsl_ac97_data->clk); ret = clk_prepare_enable(fsl_ac97_data->clk);
if (ret) { if (ret) {
pr_err("ac97 read clk_prepare_enable failed: %d\n", pr_err("ac97 read clk_prepare_enable failed: %d\n", ret);
ret);
goto ret_unlock; goto ret_unlock;
} }
lreg = (reg & 0x7f) << 12; lreg = (reg & 0x7f) << 12;
regmap_write(regs, CCSR_SSI_SACADD, lreg); regmap_write(regs, REG_SSI_SACADD, lreg);
regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK, regmap_update_bits(regs, REG_SSI_SACNT,
CCSR_SSI_SACNT_RD); SSI_SACNT_RDWR_MASK, SSI_SACNT_RD);
udelay(100); udelay(100);
regmap_read(regs, CCSR_SSI_SACDAT, &reg_val); regmap_read(regs, REG_SSI_SACDAT, &reg_val);
val = (reg_val >> 4) & 0xffff; val = (reg_val >> 4) & 0xffff;
clk_disable_unprepare(fsl_ac97_data->clk); clk_disable_unprepare(fsl_ac97_data->clk);
...@@ -1325,8 +1267,8 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, ...@@ -1325,8 +1267,8 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
} }
static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
.read = fsl_ssi_ac97_read, .read = fsl_ssi_ac97_read,
.write = fsl_ssi_ac97_write, .write = fsl_ssi_ac97_write,
}; };
/** /**
...@@ -1341,70 +1283,67 @@ static void make_lowercase(char *s) ...@@ -1341,70 +1283,67 @@ 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_private *ssi_private, void __iomem *iomem) struct fsl_ssi *ssi, void __iomem *iomem)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
u32 dmas[4]; u32 dmas[4];
int ret; int ret;
if (ssi_private->has_ipg_clk_name) /* Backward compatible for a DT without ipg clock name assigned */
ssi_private->clk = devm_clk_get(&pdev->dev, "ipg"); if (ssi->has_ipg_clk_name)
ssi->clk = devm_clk_get(dev, "ipg");
else else
ssi_private->clk = devm_clk_get(&pdev->dev, NULL); ssi->clk = devm_clk_get(dev, NULL);
if (IS_ERR(ssi_private->clk)) { if (IS_ERR(ssi->clk)) {
ret = PTR_ERR(ssi_private->clk); ret = PTR_ERR(ssi->clk);
dev_err(&pdev->dev, "could not get clock: %d\n", ret); dev_err(dev, "failed to get clock: %d\n", ret);
return ret; return ret;
} }
if (!ssi_private->has_ipg_clk_name) { /* Enable the clock since regmap will not handle it in this case */
ret = clk_prepare_enable(ssi_private->clk); if (!ssi->has_ipg_clk_name) {
ret = clk_prepare_enable(ssi->clk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
return ret; return ret;
} }
} }
/* For those SLAVE implementations, we ignore non-baudclk cases /* Do not error out for slave cases that live without a baud clock */
* and, instead, abandon MASTER mode that needs baud clock. ssi->baudclk = devm_clk_get(dev, "baud");
*/ if (IS_ERR(ssi->baudclk))
ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); dev_dbg(dev, "failed to get baud clock: %ld\n",
if (IS_ERR(ssi_private->baudclk)) PTR_ERR(ssi->baudclk));
dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
PTR_ERR(ssi_private->baudclk));
ssi_private->dma_params_tx.maxburst = ssi_private->dma_maxburst; ssi->dma_params_tx.maxburst = ssi->dma_maxburst;
ssi_private->dma_params_rx.maxburst = ssi_private->dma_maxburst; ssi->dma_params_rx.maxburst = ssi->dma_maxburst;
ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0; ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0;
ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0; ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
/* Set to dual FIFO mode according to the SDMA sciprt */
ret = of_property_read_u32_array(np, "dmas", dmas, 4); ret = of_property_read_u32_array(np, "dmas", dmas, 4);
if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) { if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
ssi_private->use_dual_fifo = true; ssi->use_dual_fifo = true;
/* When using dual fifo mode, we need to keep watermark /*
* as even numbers due to dma script limitation. * Use even numbers to avoid channel swap due to SDMA
* script design
*/ */
ssi_private->dma_params_tx.maxburst &= ~0x1; ssi->dma_params_tx.maxburst &= ~0x1;
ssi_private->dma_params_rx.maxburst &= ~0x1; ssi->dma_params_rx.maxburst &= ~0x1;
} }
if (!ssi_private->use_dma) { if (!ssi->use_dma) {
/* /*
* Some boards use an incompatible codec. To get it * Some boards use an incompatible codec. Use imx-fiq-pcm-audio
* working, we are using imx-fiq-pcm-audio, that * to get it working, as DMA is not possible in this situation.
* can handle those codecs. DMA is not possible in this
* situation.
*/ */
ssi->fiq_params.irq = ssi->irq;
ssi->fiq_params.base = iomem;
ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
ssi_private->fiq_params.irq = ssi_private->irq; ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
ssi_private->fiq_params.base = iomem;
ssi_private->fiq_params.dma_params_rx =
&ssi_private->dma_params_rx;
ssi_private->fiq_params.dma_params_tx =
&ssi_private->dma_params_tx;
ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
if (ret) if (ret)
goto error_pcm; goto error_pcm;
} else { } else {
...@@ -1416,26 +1355,26 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, ...@@ -1416,26 +1355,26 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
return 0; return 0;
error_pcm: error_pcm:
if (!ssi->has_ipg_clk_name)
clk_disable_unprepare(ssi->clk);
if (!ssi_private->has_ipg_clk_name)
clk_disable_unprepare(ssi_private->clk);
return ret; return ret;
} }
static void fsl_ssi_imx_clean(struct platform_device *pdev, static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
struct fsl_ssi_private *ssi_private)
{ {
if (!ssi_private->use_dma) if (!ssi->use_dma)
imx_pcm_fiq_exit(pdev); imx_pcm_fiq_exit(pdev);
if (!ssi_private->has_ipg_clk_name) if (!ssi->has_ipg_clk_name)
clk_disable_unprepare(ssi_private->clk); clk_disable_unprepare(ssi->clk);
} }
static int fsl_ssi_probe(struct platform_device *pdev) static int fsl_ssi_probe(struct platform_device *pdev)
{ {
struct fsl_ssi_private *ssi_private; struct fsl_ssi *ssi;
int ret = 0; int ret = 0;
struct device_node *np = pdev->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 uint32_t *iprop;
...@@ -1444,185 +1383,159 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1444,185 +1383,159 @@ static int fsl_ssi_probe(struct platform_device *pdev)
char name[64]; char name[64];
struct regmap_config regconfig = fsl_ssi_regconfig; struct regmap_config regconfig = fsl_ssi_regconfig;
of_id = of_match_device(fsl_ssi_ids, &pdev->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_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private), ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
GFP_KERNEL); if (!ssi)
if (!ssi_private)
return -ENOMEM; return -ENOMEM;
ssi_private->soc = of_id->data; ssi->soc = of_id->data;
ssi_private->dev = &pdev->dev; ssi->dev = dev;
/* 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) {
if (!strcmp(sprop, "ac97-slave")) if (!strcmp(sprop, "ac97-slave"))
ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97; ssi->dai_fmt = SND_SOC_DAIFMT_AC97;
} }
ssi_private->use_dma = !of_property_read_bool(np, /* Select DMA or FIQ */
"fsl,fiq-stream-filter"); ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
if (fsl_ssi_is_ac97(ssi_private)) {
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi_private; if (fsl_ssi_is_ac97(ssi)) {
memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai,
sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi;
} else { } else {
/* Initialize this copy of the CPU DAI driver structure */ memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template,
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
sizeof(fsl_ssi_dai_template)); sizeof(fsl_ssi_dai_template));
} }
ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev); ssi->cpu_dai_drv.name = dev_name(dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iomem = devm_ioremap_resource(&pdev->dev, res); iomem = devm_ioremap_resource(dev, res);
if (IS_ERR(iomem)) if (IS_ERR(iomem))
return PTR_ERR(iomem); return PTR_ERR(iomem);
ssi_private->ssi_phys = res->start; ssi->ssi_phys = res->start;
if (ssi_private->soc->imx21regs) { if (ssi->soc->imx21regs) {
/* /* No SACC{ST,EN,DIS} regs in imx21-class SSI */
* According to datasheet imx21-class SSI regconfig.max_register = REG_SSI_SRMSK;
* don't have SACC{ST,EN,DIS} regs.
*/
regconfig.max_register = CCSR_SSI_SRMSK;
regconfig.num_reg_defaults_raw = regconfig.num_reg_defaults_raw =
CCSR_SSI_SRMSK / sizeof(uint32_t) + 1; REG_SSI_SRMSK / sizeof(uint32_t) + 1;
} }
ret = of_property_match_string(np, "clock-names", "ipg"); ret = of_property_match_string(np, "clock-names", "ipg");
if (ret < 0) { if (ret < 0) {
ssi_private->has_ipg_clk_name = false; ssi->has_ipg_clk_name = false;
ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, ssi->regs = devm_regmap_init_mmio(dev, iomem, &regconfig);
&regconfig);
} else { } else {
ssi_private->has_ipg_clk_name = true; ssi->has_ipg_clk_name = true;
ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev, ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem,
"ipg", iomem, &regconfig); &regconfig);
} }
if (IS_ERR(ssi_private->regs)) { if (IS_ERR(ssi->regs)) {
dev_err(&pdev->dev, "Failed to init register map\n"); dev_err(dev, "failed to init register map\n");
return PTR_ERR(ssi_private->regs); return PTR_ERR(ssi->regs);
} }
ssi_private->irq = platform_get_irq(pdev, 0); ssi->irq = platform_get_irq(pdev, 0);
if (ssi_private->irq < 0) { if (ssi->irq < 0) {
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); dev_err(dev, "no irq for node %s\n", pdev->name);
return ssi_private->irq; return ssi->irq;
} }
/* Are the RX and the TX clocks locked? */ /* Set software limitations for synchronous mode */
if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
if (!fsl_ssi_is_ac97(ssi_private)) if (!fsl_ssi_is_ac97(ssi)) {
ssi_private->cpu_dai_drv.symmetric_rates = 1; ssi->cpu_dai_drv.symmetric_rates = 1;
ssi->cpu_dai_drv.symmetric_samplebits = 1;
}
ssi_private->cpu_dai_drv.symmetric_channels = 1; ssi->cpu_dai_drv.symmetric_channels = 1;
ssi_private->cpu_dai_drv.symmetric_samplebits = 1;
} }
/* Determine the FIFO depth. */ /* Fetch FIFO depth; Set to 8 for older DT without this property */
iprop = of_get_property(np, "fsl,fifo-depth", NULL); iprop = of_get_property(np, "fsl,fifo-depth", NULL);
if (iprop) if (iprop)
ssi_private->fifo_depth = be32_to_cpup(iprop); ssi->fifo_depth = be32_to_cpup(iprop);
else else
/* Older 8610 DTs didn't have the fifo-depth property */ ssi->fifo_depth = 8;
ssi_private->fifo_depth = 8;
/* /*
* Set the watermark for transmit FIFO 0 and receive FIFO 0. We don't * Configure TX and RX DMA watermarks -- when to send a DMA request
* use FIFO 1 but set the watermark appropriately nontheless.
* We program the transmit water to signal a DMA transfer
* if there are N elements left in the FIFO. For chips with 15-deep
* FIFOs, set watermark to 8. This allows the SSI to operate at a
* high data rate without channel slipping. Behavior is unchanged
* for the older chips with a fifo depth of only 8. A value of 4
* might be appropriate for the older chips, but is left at
* fifo_depth-2 until sombody has a chance to test.
* *
* We set the watermark on the same level as the DMA burstsize. For * Values should be tested to avoid FIFO under/over run. Set maxburst
* fiq it is probably better to use the biggest possible watermark * to fifo_watermark to maxiumize DMA transaction to reduce overhead.
* size.
*/ */
switch (ssi_private->fifo_depth) { switch (ssi->fifo_depth) {
case 15: case 15:
/* /*
* 2 samples is not enough when running at high data * Set to 8 as a balanced configuration -- When TX FIFO has 8
* rates (like 48kHz @ 16 bits/channel, 16 channels) * empty slots, send a DMA request to fill these 8 slots. The
* 8 seems to split things evenly and leave enough time * remaining 7 slots should be able to allow DMA to finish the
* for the DMA to fill the FIFO before it's over/under * transaction before TX FIFO underruns; Same applies to RX.
* run. *
* Tested with cases running at 48kHz @ 16 bits x 16 channels
*/ */
ssi_private->fifo_watermark = 8; ssi->fifo_watermark = 8;
ssi_private->dma_maxburst = 8; ssi->dma_maxburst = 8;
break; break;
case 8: case 8:
default: default:
/* /* Safely use old watermark configurations for older chips */
* maintain old behavior for older chips. ssi->fifo_watermark = ssi->fifo_depth - 2;
* Keeping it the same because I don't have an older ssi->dma_maxburst = ssi->fifo_depth - 2;
* board to test with.
* I suspect this could be changed to be something to
* leave some more space in the fifo.
*/
ssi_private->fifo_watermark = ssi_private->fifo_depth - 2;
ssi_private->dma_maxburst = ssi_private->fifo_depth - 2;
break; break;
} }
dev_set_drvdata(&pdev->dev, ssi_private); dev_set_drvdata(dev, ssi);
if (ssi_private->soc->imx) { if (ssi->soc->imx) {
ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem); ret = fsl_ssi_imx_probe(pdev, ssi, iomem);
if (ret) if (ret)
return ret; return ret;
} }
if (fsl_ssi_is_ac97(ssi_private)) { if (fsl_ssi_is_ac97(ssi)) {
mutex_init(&ssi_private->ac97_reg_lock); mutex_init(&ssi->ac97_reg_lock);
ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "could not set AC'97 ops\n"); dev_err(dev, "failed to set AC'97 ops\n");
goto error_ac97_ops; goto error_ac97_ops;
} }
} }
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component, ret = devm_snd_soc_register_component(dev, &fsl_ssi_component,
&ssi_private->cpu_dai_drv, 1); &ssi->cpu_dai_drv, 1);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); dev_err(dev, "failed to register DAI: %d\n", ret);
goto error_asoc_register; goto error_asoc_register;
} }
if (ssi_private->use_dma) { if (ssi->use_dma) {
ret = devm_request_irq(&pdev->dev, ssi_private->irq, ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0,
fsl_ssi_isr, 0, dev_name(&pdev->dev), dev_name(dev), ssi);
ssi_private);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "could not claim irq %u\n", dev_err(dev, "failed to claim irq %u\n", ssi->irq);
ssi_private->irq);
goto error_asoc_register; goto error_asoc_register;
} }
} }
ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev); ret = fsl_ssi_debugfs_create(&ssi->dbg_stats, dev);
if (ret) if (ret)
goto error_asoc_register; goto error_asoc_register;
/* /* Bypass it if using newer DT bindings of ASoC machine drivers */
* If codec-handle property is missing from SSI node, we assume
* that the machine driver uses new binding which does not require
* SSI driver to trigger machine driver's probe.
*/
if (!of_get_property(np, "codec-handle", NULL)) if (!of_get_property(np, "codec-handle", NULL))
goto done; goto done;
/* Trigger the machine driver's probe function. The platform driver /*
* name of the machine driver is taken from /compatible property of the * Backward compatible for older bindings by manually triggering the
* device tree. We also pass the address of the CPU DAI driver * machine driver's probe(). Use /compatible property, including the
* structure. * address of CPU DAI driver structure, as the name of machine driver.
*/ */
sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL); sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
/* Sometimes the compatible name has a "fsl," prefix, so we strip it. */ /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
...@@ -1632,34 +1545,31 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1632,34 +1545,31 @@ static int fsl_ssi_probe(struct platform_device *pdev)
snprintf(name, sizeof(name), "snd-soc-%s", sprop); snprintf(name, sizeof(name), "snd-soc-%s", sprop);
make_lowercase(name); make_lowercase(name);
ssi_private->pdev = ssi->pdev = platform_device_register_data(dev, name, 0, NULL, 0);
platform_device_register_data(&pdev->dev, name, 0, NULL, 0); if (IS_ERR(ssi->pdev)) {
if (IS_ERR(ssi_private->pdev)) { ret = PTR_ERR(ssi->pdev);
ret = PTR_ERR(ssi_private->pdev); dev_err(dev, "failed to register platform: %d\n", ret);
dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
goto error_sound_card; goto error_sound_card;
} }
done: done:
if (ssi_private->dai_fmt) if (ssi->dai_fmt)
_fsl_ssi_set_dai_fmt(&pdev->dev, ssi_private, _fsl_ssi_set_dai_fmt(dev, ssi, ssi->dai_fmt);
ssi_private->dai_fmt);
if (fsl_ssi_is_ac97(ssi_private)) { if (fsl_ssi_is_ac97(ssi)) {
u32 ssi_idx; u32 ssi_idx;
ret = of_property_read_u32(np, "cell-index", &ssi_idx); ret = of_property_read_u32(np, "cell-index", &ssi_idx);
if (ret) { if (ret) {
dev_err(&pdev->dev, "cannot get SSI index property\n"); dev_err(dev, "failed to get SSI index property\n");
goto error_sound_card; goto error_sound_card;
} }
ssi_private->pdev = ssi->pdev = platform_device_register_data(NULL, "ac97-codec",
platform_device_register_data(NULL, ssi_idx, NULL, 0);
"ac97-codec", ssi_idx, NULL, 0); if (IS_ERR(ssi->pdev)) {
if (IS_ERR(ssi_private->pdev)) { ret = PTR_ERR(ssi->pdev);
ret = PTR_ERR(ssi_private->pdev); dev_err(dev,
dev_err(&pdev->dev,
"failed to register AC97 codec platform: %d\n", "failed to register AC97 codec platform: %d\n",
ret); ret);
goto error_sound_card; goto error_sound_card;
...@@ -1669,37 +1579,35 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1669,37 +1579,35 @@ static int fsl_ssi_probe(struct platform_device *pdev)
return 0; return 0;
error_sound_card: error_sound_card:
fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); fsl_ssi_debugfs_remove(&ssi->dbg_stats);
error_asoc_register: error_asoc_register:
if (fsl_ssi_is_ac97(ssi_private)) if (fsl_ssi_is_ac97(ssi))
snd_soc_set_ac97_ops(NULL); snd_soc_set_ac97_ops(NULL);
error_ac97_ops: error_ac97_ops:
if (fsl_ssi_is_ac97(ssi_private)) if (fsl_ssi_is_ac97(ssi))
mutex_destroy(&ssi_private->ac97_reg_lock); mutex_destroy(&ssi->ac97_reg_lock);
if (ssi_private->soc->imx) if (ssi->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private); fsl_ssi_imx_clean(pdev, ssi);
return ret; return ret;
} }
static int fsl_ssi_remove(struct platform_device *pdev) static int fsl_ssi_remove(struct platform_device *pdev)
{ {
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev);
fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); fsl_ssi_debugfs_remove(&ssi->dbg_stats);
if (ssi_private->pdev) if (ssi->pdev)
platform_device_unregister(ssi_private->pdev); platform_device_unregister(ssi->pdev);
if (ssi_private->soc->imx) if (ssi->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private); fsl_ssi_imx_clean(pdev, ssi);
if (fsl_ssi_is_ac97(ssi_private)) { if (fsl_ssi_is_ac97(ssi)) {
snd_soc_set_ac97_ops(NULL); snd_soc_set_ac97_ops(NULL);
mutex_destroy(&ssi_private->ac97_reg_lock); mutex_destroy(&ssi->ac97_reg_lock);
} }
return 0; return 0;
...@@ -1708,13 +1616,11 @@ static int fsl_ssi_remove(struct platform_device *pdev) ...@@ -1708,13 +1616,11 @@ static int fsl_ssi_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int fsl_ssi_suspend(struct device *dev) static int fsl_ssi_suspend(struct device *dev)
{ {
struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); struct fsl_ssi *ssi = dev_get_drvdata(dev);
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
regmap_read(regs, CCSR_SSI_SFCSR, regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr);
&ssi_private->regcache_sfcsr); regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt);
regmap_read(regs, CCSR_SSI_SACNT,
&ssi_private->regcache_sacnt);
regcache_cache_only(regs, true); regcache_cache_only(regs, true);
regcache_mark_dirty(regs); regcache_mark_dirty(regs);
...@@ -1724,17 +1630,16 @@ static int fsl_ssi_suspend(struct device *dev) ...@@ -1724,17 +1630,16 @@ static int fsl_ssi_suspend(struct device *dev)
static int fsl_ssi_resume(struct device *dev) static int fsl_ssi_resume(struct device *dev)
{ {
struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev); struct fsl_ssi *ssi = dev_get_drvdata(dev);
struct regmap *regs = ssi_private->regs; struct regmap *regs = ssi->regs;
regcache_cache_only(regs, false); regcache_cache_only(regs, false);
regmap_update_bits(regs, CCSR_SSI_SFCSR, regmap_update_bits(regs, REG_SSI_SFCSR,
CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK | SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK |
CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK, SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK,
ssi_private->regcache_sfcsr); ssi->regcache_sfcsr);
regmap_write(regs, CCSR_SSI_SACNT, regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt);
ssi_private->regcache_sacnt);
return regcache_sync(regs); return regcache_sync(regs);
} }
......
/* /*
* fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 and i.MX SoC
* *
* Author: Timur Tabi <timur@freescale.com> * Author: Timur Tabi <timur@freescale.com>
* *
...@@ -12,198 +12,261 @@ ...@@ -12,198 +12,261 @@
#ifndef _MPC8610_I2S_H #ifndef _MPC8610_I2S_H
#define _MPC8610_I2S_H #define _MPC8610_I2S_H
/* SSI registers */ #define RX 0
#define CCSR_SSI_STX0 0x00 #define TX 1
#define CCSR_SSI_STX1 0x04
#define CCSR_SSI_SRX0 0x08
#define CCSR_SSI_SRX1 0x0c
#define CCSR_SSI_SCR 0x10
#define CCSR_SSI_SISR 0x14
#define CCSR_SSI_SIER 0x18
#define CCSR_SSI_STCR 0x1c
#define CCSR_SSI_SRCR 0x20
#define CCSR_SSI_STCCR 0x24
#define CCSR_SSI_SRCCR 0x28
#define CCSR_SSI_SFCSR 0x2c
#define CCSR_SSI_STR 0x30
#define CCSR_SSI_SOR 0x34
#define CCSR_SSI_SACNT 0x38
#define CCSR_SSI_SACADD 0x3c
#define CCSR_SSI_SACDAT 0x40
#define CCSR_SSI_SATAG 0x44
#define CCSR_SSI_STMSK 0x48
#define CCSR_SSI_SRMSK 0x4c
#define CCSR_SSI_SACCST 0x50
#define CCSR_SSI_SACCEN 0x54
#define CCSR_SSI_SACCDIS 0x58
#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000 /* -- SSI Register Map -- */
#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800
#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400
#define CCSR_SSI_SCR_TCH_EN 0x00000100
#define CCSR_SSI_SCR_SYS_CLK_EN 0x00000080
#define CCSR_SSI_SCR_I2S_MODE_MASK 0x00000060
#define CCSR_SSI_SCR_I2S_MODE_NORMAL 0x00000000
#define CCSR_SSI_SCR_I2S_MODE_MASTER 0x00000020
#define CCSR_SSI_SCR_I2S_MODE_SLAVE 0x00000040
#define CCSR_SSI_SCR_SYN 0x00000010
#define CCSR_SSI_SCR_NET 0x00000008
#define CCSR_SSI_SCR_RE 0x00000004
#define CCSR_SSI_SCR_TE 0x00000002
#define CCSR_SSI_SCR_SSIEN 0x00000001
#define CCSR_SSI_SISR_RFRC 0x01000000 /* SSI Transmit Data Register 0 */
#define CCSR_SSI_SISR_TFRC 0x00800000 #define REG_SSI_STX0 0x00
#define CCSR_SSI_SISR_CMDAU 0x00040000 /* SSI Transmit Data Register 1 */
#define CCSR_SSI_SISR_CMDDU 0x00020000 #define REG_SSI_STX1 0x04
#define CCSR_SSI_SISR_RXT 0x00010000 /* SSI Receive Data Register 0 */
#define CCSR_SSI_SISR_RDR1 0x00008000 #define REG_SSI_SRX0 0x08
#define CCSR_SSI_SISR_RDR0 0x00004000 /* SSI Receive Data Register 1 */
#define CCSR_SSI_SISR_TDE1 0x00002000 #define REG_SSI_SRX1 0x0c
#define CCSR_SSI_SISR_TDE0 0x00001000 /* SSI Control Register */
#define CCSR_SSI_SISR_ROE1 0x00000800 #define REG_SSI_SCR 0x10
#define CCSR_SSI_SISR_ROE0 0x00000400 /* SSI Interrupt Status Register */
#define CCSR_SSI_SISR_TUE1 0x00000200 #define REG_SSI_SISR 0x14
#define CCSR_SSI_SISR_TUE0 0x00000100 /* SSI Interrupt Enable Register */
#define CCSR_SSI_SISR_TFS 0x00000080 #define REG_SSI_SIER 0x18
#define CCSR_SSI_SISR_RFS 0x00000040 /* SSI Transmit Configuration Register */
#define CCSR_SSI_SISR_TLS 0x00000020 #define REG_SSI_STCR 0x1c
#define CCSR_SSI_SISR_RLS 0x00000010 /* SSI Receive Configuration Register */
#define CCSR_SSI_SISR_RFF1 0x00000008 #define REG_SSI_SRCR 0x20
#define CCSR_SSI_SISR_RFF0 0x00000004 #define REG_SSI_SxCR(tx) ((tx) ? REG_SSI_STCR : REG_SSI_SRCR)
#define CCSR_SSI_SISR_TFE1 0x00000002 /* SSI Transmit Clock Control Register */
#define CCSR_SSI_SISR_TFE0 0x00000001 #define REG_SSI_STCCR 0x24
/* SSI Receive Clock Control Register */
#define REG_SSI_SRCCR 0x28
#define REG_SSI_SxCCR(tx) ((tx) ? REG_SSI_STCCR : REG_SSI_SRCCR)
/* SSI FIFO Control/Status Register */
#define REG_SSI_SFCSR 0x2c
/*
* SSI Test Register (Intended for debugging purposes only)
*
* Note: STR is not documented in recent IMX datasheet, but
* is described in IMX51 reference manual at section 56.3.3.14
*/
#define REG_SSI_STR 0x30
/*
* SSI Option Register (Intended for internal use only)
*
* Note: SOR is not documented in recent IMX datasheet, but
* is described in IMX51 reference manual at section 56.3.3.15
*/
#define REG_SSI_SOR 0x34
/* SSI AC97 Control Register */
#define REG_SSI_SACNT 0x38
/* SSI AC97 Command Address Register */
#define REG_SSI_SACADD 0x3c
/* SSI AC97 Command Data Register */
#define REG_SSI_SACDAT 0x40
/* SSI AC97 Tag Register */
#define REG_SSI_SATAG 0x44
/* SSI Transmit Time Slot Mask Register */
#define REG_SSI_STMSK 0x48
/* SSI Receive Time Slot Mask Register */
#define REG_SSI_SRMSK 0x4c
#define REG_SSI_SxMSK(tx) ((tx) ? REG_SSI_STMSK : REG_SSI_SRMSK)
/*
* SSI AC97 Channel Status Register
*
* The status could be changed by:
* 1) Writing a '1' bit at some position in SACCEN sets relevant bit in SACCST
* 2) Writing a '1' bit at some position in SACCDIS unsets the relevant bit
* 3) Receivng a '1' in SLOTREQ bit from external CODEC via AC Link
*/
#define REG_SSI_SACCST 0x50
/* SSI AC97 Channel Enable Register -- Set bits in SACCST */
#define REG_SSI_SACCEN 0x54
/* SSI AC97 Channel Disable Register -- Clear bits in SACCST */
#define REG_SSI_SACCDIS 0x58
/* -- SSI Register Field Maps -- */
#define CCSR_SSI_SIER_RFRC_EN 0x01000000 /* SSI Control Register -- REG_SSI_SCR 0x10 */
#define CCSR_SSI_SIER_TFRC_EN 0x00800000 #define SSI_SCR_SYNC_TX_FS 0x00001000
#define CCSR_SSI_SIER_RDMAE 0x00400000 #define SSI_SCR_RFR_CLK_DIS 0x00000800
#define CCSR_SSI_SIER_RIE 0x00200000 #define SSI_SCR_TFR_CLK_DIS 0x00000400
#define CCSR_SSI_SIER_TDMAE 0x00100000 #define SSI_SCR_TCH_EN 0x00000100
#define CCSR_SSI_SIER_TIE 0x00080000 #define SSI_SCR_SYS_CLK_EN 0x00000080
#define CCSR_SSI_SIER_CMDAU_EN 0x00040000 #define SSI_SCR_I2S_MODE_MASK 0x00000060
#define CCSR_SSI_SIER_CMDDU_EN 0x00020000 #define SSI_SCR_I2S_MODE_NORMAL 0x00000000
#define CCSR_SSI_SIER_RXT_EN 0x00010000 #define SSI_SCR_I2S_MODE_MASTER 0x00000020
#define CCSR_SSI_SIER_RDR1_EN 0x00008000 #define SSI_SCR_I2S_MODE_SLAVE 0x00000040
#define CCSR_SSI_SIER_RDR0_EN 0x00004000 #define SSI_SCR_SYN 0x00000010
#define CCSR_SSI_SIER_TDE1_EN 0x00002000 #define SSI_SCR_NET 0x00000008
#define CCSR_SSI_SIER_TDE0_EN 0x00001000 #define SSI_SCR_I2S_NET_MASK (SSI_SCR_NET | SSI_SCR_I2S_MODE_MASK)
#define CCSR_SSI_SIER_ROE1_EN 0x00000800 #define SSI_SCR_RE 0x00000004
#define CCSR_SSI_SIER_ROE0_EN 0x00000400 #define SSI_SCR_TE 0x00000002
#define CCSR_SSI_SIER_TUE1_EN 0x00000200 #define SSI_SCR_SSIEN 0x00000001
#define CCSR_SSI_SIER_TUE0_EN 0x00000100
#define CCSR_SSI_SIER_TFS_EN 0x00000080
#define CCSR_SSI_SIER_RFS_EN 0x00000040
#define CCSR_SSI_SIER_TLS_EN 0x00000020
#define CCSR_SSI_SIER_RLS_EN 0x00000010
#define CCSR_SSI_SIER_RFF1_EN 0x00000008
#define CCSR_SSI_SIER_RFF0_EN 0x00000004
#define CCSR_SSI_SIER_TFE1_EN 0x00000002
#define CCSR_SSI_SIER_TFE0_EN 0x00000001
#define CCSR_SSI_STCR_TXBIT0 0x00000200 /* SSI Interrupt Status Register -- REG_SSI_SISR 0x14 */
#define CCSR_SSI_STCR_TFEN1 0x00000100 #define SSI_SISR_RFRC 0x01000000
#define CCSR_SSI_STCR_TFEN0 0x00000080 #define SSI_SISR_TFRC 0x00800000
#define CCSR_SSI_STCR_TFDIR 0x00000040 #define SSI_SISR_CMDAU 0x00040000
#define CCSR_SSI_STCR_TXDIR 0x00000020 #define SSI_SISR_CMDDU 0x00020000
#define CCSR_SSI_STCR_TSHFD 0x00000010 #define SSI_SISR_RXT 0x00010000
#define CCSR_SSI_STCR_TSCKP 0x00000008 #define SSI_SISR_RDR1 0x00008000
#define CCSR_SSI_STCR_TFSI 0x00000004 #define SSI_SISR_RDR0 0x00004000
#define CCSR_SSI_STCR_TFSL 0x00000002 #define SSI_SISR_TDE1 0x00002000
#define CCSR_SSI_STCR_TEFS 0x00000001 #define SSI_SISR_TDE0 0x00001000
#define SSI_SISR_ROE1 0x00000800
#define SSI_SISR_ROE0 0x00000400
#define SSI_SISR_TUE1 0x00000200
#define SSI_SISR_TUE0 0x00000100
#define SSI_SISR_TFS 0x00000080
#define SSI_SISR_RFS 0x00000040
#define SSI_SISR_TLS 0x00000020
#define SSI_SISR_RLS 0x00000010
#define SSI_SISR_RFF1 0x00000008
#define SSI_SISR_RFF0 0x00000004
#define SSI_SISR_TFE1 0x00000002
#define SSI_SISR_TFE0 0x00000001
#define CCSR_SSI_SRCR_RXEXT 0x00000400 /* SSI Interrupt Enable Register -- REG_SSI_SIER 0x18 */
#define CCSR_SSI_SRCR_RXBIT0 0x00000200 #define SSI_SIER_RFRC_EN 0x01000000
#define CCSR_SSI_SRCR_RFEN1 0x00000100 #define SSI_SIER_TFRC_EN 0x00800000
#define CCSR_SSI_SRCR_RFEN0 0x00000080 #define SSI_SIER_RDMAE 0x00400000
#define CCSR_SSI_SRCR_RFDIR 0x00000040 #define SSI_SIER_RIE 0x00200000
#define CCSR_SSI_SRCR_RXDIR 0x00000020 #define SSI_SIER_TDMAE 0x00100000
#define CCSR_SSI_SRCR_RSHFD 0x00000010 #define SSI_SIER_TIE 0x00080000
#define CCSR_SSI_SRCR_RSCKP 0x00000008 #define SSI_SIER_CMDAU_EN 0x00040000
#define CCSR_SSI_SRCR_RFSI 0x00000004 #define SSI_SIER_CMDDU_EN 0x00020000
#define CCSR_SSI_SRCR_RFSL 0x00000002 #define SSI_SIER_RXT_EN 0x00010000
#define CCSR_SSI_SRCR_REFS 0x00000001 #define SSI_SIER_RDR1_EN 0x00008000
#define SSI_SIER_RDR0_EN 0x00004000
#define SSI_SIER_TDE1_EN 0x00002000
#define SSI_SIER_TDE0_EN 0x00001000
#define SSI_SIER_ROE1_EN 0x00000800
#define SSI_SIER_ROE0_EN 0x00000400
#define SSI_SIER_TUE1_EN 0x00000200
#define SSI_SIER_TUE0_EN 0x00000100
#define SSI_SIER_TFS_EN 0x00000080
#define SSI_SIER_RFS_EN 0x00000040
#define SSI_SIER_TLS_EN 0x00000020
#define SSI_SIER_RLS_EN 0x00000010
#define SSI_SIER_RFF1_EN 0x00000008
#define SSI_SIER_RFF0_EN 0x00000004
#define SSI_SIER_TFE1_EN 0x00000002
#define SSI_SIER_TFE0_EN 0x00000001
/* STCCR and SRCCR */ /* SSI Transmit Configuration Register -- REG_SSI_STCR 0x1C */
#define CCSR_SSI_SxCCR_DIV2_SHIFT 18 #define SSI_STCR_TXBIT0 0x00000200
#define CCSR_SSI_SxCCR_DIV2 0x00040000 #define SSI_STCR_TFEN1 0x00000100
#define CCSR_SSI_SxCCR_PSR_SHIFT 17 #define SSI_STCR_TFEN0 0x00000080
#define CCSR_SSI_SxCCR_PSR 0x00020000 #define SSI_STCR_TFDIR 0x00000040
#define CCSR_SSI_SxCCR_WL_SHIFT 13 #define SSI_STCR_TXDIR 0x00000020
#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000 #define SSI_STCR_TSHFD 0x00000010
#define CCSR_SSI_SxCCR_WL(x) \ #define SSI_STCR_TSCKP 0x00000008
(((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK) #define SSI_STCR_TFSI 0x00000004
#define CCSR_SSI_SxCCR_DC_SHIFT 8 #define SSI_STCR_TFSL 0x00000002
#define CCSR_SSI_SxCCR_DC_MASK 0x00001F00 #define SSI_STCR_TEFS 0x00000001
#define CCSR_SSI_SxCCR_DC(x) \
((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK) /* SSI Receive Configuration Register -- REG_SSI_SRCR 0x20 */
#define CCSR_SSI_SxCCR_PM_SHIFT 0 #define SSI_SRCR_RXEXT 0x00000400
#define CCSR_SSI_SxCCR_PM_MASK 0x000000FF #define SSI_SRCR_RXBIT0 0x00000200
#define CCSR_SSI_SxCCR_PM(x) \ #define SSI_SRCR_RFEN1 0x00000100
((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK) #define SSI_SRCR_RFEN0 0x00000080
#define SSI_SRCR_RFDIR 0x00000040
#define SSI_SRCR_RXDIR 0x00000020
#define SSI_SRCR_RSHFD 0x00000010
#define SSI_SRCR_RSCKP 0x00000008
#define SSI_SRCR_RFSI 0x00000004
#define SSI_SRCR_RFSL 0x00000002
#define SSI_SRCR_REFS 0x00000001
/* /*
* The xFCNT bits are read-only, and the xFWM bits are read/write. Use the * SSI Transmit Clock Control Register -- REG_SSI_STCCR 0x24
* CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the * SSI Receive Clock Control Register -- REG_SSI_SRCCR 0x28
* CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks. */
#define SSI_SxCCR_DIV2_SHIFT 18
#define SSI_SxCCR_DIV2 0x00040000
#define SSI_SxCCR_PSR_SHIFT 17
#define SSI_SxCCR_PSR 0x00020000
#define SSI_SxCCR_WL_SHIFT 13
#define SSI_SxCCR_WL_MASK 0x0001E000
#define SSI_SxCCR_WL(x) \
(((((x) / 2) - 1) << SSI_SxCCR_WL_SHIFT) & SSI_SxCCR_WL_MASK)
#define SSI_SxCCR_DC_SHIFT 8
#define SSI_SxCCR_DC_MASK 0x00001F00
#define SSI_SxCCR_DC(x) \
((((x) - 1) << SSI_SxCCR_DC_SHIFT) & SSI_SxCCR_DC_MASK)
#define SSI_SxCCR_PM_SHIFT 0
#define SSI_SxCCR_PM_MASK 0x000000FF
#define SSI_SxCCR_PM(x) \
((((x) - 1) << SSI_SxCCR_PM_SHIFT) & SSI_SxCCR_PM_MASK)
/*
* SSI FIFO Control/Status Register -- REG_SSI_SFCSR 0x2c
*
* Tx or Rx FIFO Counter -- SSI_SFCSR_xFCNTy Read-Only
* Tx or Rx FIFO Watermarks -- SSI_SFCSR_xFWMy Read/Write
*/ */
#define CCSR_SSI_SFCSR_RFCNT1_SHIFT 28 #define SSI_SFCSR_RFCNT1_SHIFT 28
#define CCSR_SSI_SFCSR_RFCNT1_MASK 0xF0000000 #define SSI_SFCSR_RFCNT1_MASK 0xF0000000
#define CCSR_SSI_SFCSR_RFCNT1(x) \ #define SSI_SFCSR_RFCNT1(x) \
(((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT) (((x) & SSI_SFCSR_RFCNT1_MASK) >> SSI_SFCSR_RFCNT1_SHIFT)
#define CCSR_SSI_SFCSR_TFCNT1_SHIFT 24 #define SSI_SFCSR_TFCNT1_SHIFT 24
#define CCSR_SSI_SFCSR_TFCNT1_MASK 0x0F000000 #define SSI_SFCSR_TFCNT1_MASK 0x0F000000
#define CCSR_SSI_SFCSR_TFCNT1(x) \ #define SSI_SFCSR_TFCNT1(x) \
(((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT) (((x) & SSI_SFCSR_TFCNT1_MASK) >> SSI_SFCSR_TFCNT1_SHIFT)
#define CCSR_SSI_SFCSR_RFWM1_SHIFT 20 #define SSI_SFCSR_RFWM1_SHIFT 20
#define CCSR_SSI_SFCSR_RFWM1_MASK 0x00F00000 #define SSI_SFCSR_RFWM1_MASK 0x00F00000
#define CCSR_SSI_SFCSR_RFWM1(x) \ #define SSI_SFCSR_RFWM1(x) \
(((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK) (((x) << SSI_SFCSR_RFWM1_SHIFT) & SSI_SFCSR_RFWM1_MASK)
#define CCSR_SSI_SFCSR_TFWM1_SHIFT 16 #define SSI_SFCSR_TFWM1_SHIFT 16
#define CCSR_SSI_SFCSR_TFWM1_MASK 0x000F0000 #define SSI_SFCSR_TFWM1_MASK 0x000F0000
#define CCSR_SSI_SFCSR_TFWM1(x) \ #define SSI_SFCSR_TFWM1(x) \
(((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK) (((x) << SSI_SFCSR_TFWM1_SHIFT) & SSI_SFCSR_TFWM1_MASK)
#define CCSR_SSI_SFCSR_RFCNT0_SHIFT 12 #define SSI_SFCSR_RFCNT0_SHIFT 12
#define CCSR_SSI_SFCSR_RFCNT0_MASK 0x0000F000 #define SSI_SFCSR_RFCNT0_MASK 0x0000F000
#define CCSR_SSI_SFCSR_RFCNT0(x) \ #define SSI_SFCSR_RFCNT0(x) \
(((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT) (((x) & SSI_SFCSR_RFCNT0_MASK) >> SSI_SFCSR_RFCNT0_SHIFT)
#define CCSR_SSI_SFCSR_TFCNT0_SHIFT 8 #define SSI_SFCSR_TFCNT0_SHIFT 8
#define CCSR_SSI_SFCSR_TFCNT0_MASK 0x00000F00 #define SSI_SFCSR_TFCNT0_MASK 0x00000F00
#define CCSR_SSI_SFCSR_TFCNT0(x) \ #define SSI_SFCSR_TFCNT0(x) \
(((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT) (((x) & SSI_SFCSR_TFCNT0_MASK) >> SSI_SFCSR_TFCNT0_SHIFT)
#define CCSR_SSI_SFCSR_RFWM0_SHIFT 4 #define SSI_SFCSR_RFWM0_SHIFT 4
#define CCSR_SSI_SFCSR_RFWM0_MASK 0x000000F0 #define SSI_SFCSR_RFWM0_MASK 0x000000F0
#define CCSR_SSI_SFCSR_RFWM0(x) \ #define SSI_SFCSR_RFWM0(x) \
(((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK) (((x) << SSI_SFCSR_RFWM0_SHIFT) & SSI_SFCSR_RFWM0_MASK)
#define CCSR_SSI_SFCSR_TFWM0_SHIFT 0 #define SSI_SFCSR_TFWM0_SHIFT 0
#define CCSR_SSI_SFCSR_TFWM0_MASK 0x0000000F #define SSI_SFCSR_TFWM0_MASK 0x0000000F
#define CCSR_SSI_SFCSR_TFWM0(x) \ #define SSI_SFCSR_TFWM0(x) \
(((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK) (((x) << SSI_SFCSR_TFWM0_SHIFT) & SSI_SFCSR_TFWM0_MASK)
#define CCSR_SSI_STR_TEST 0x00008000 /* SSI Test Register -- REG_SSI_STR 0x30 */
#define CCSR_SSI_STR_RCK2TCK 0x00004000 #define SSI_STR_TEST 0x00008000
#define CCSR_SSI_STR_RFS2TFS 0x00002000 #define SSI_STR_RCK2TCK 0x00004000
#define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F) #define SSI_STR_RFS2TFS 0x00002000
#define CCSR_SSI_STR_TXD2RXD 0x00000080 #define SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F)
#define CCSR_SSI_STR_TCK2RCK 0x00000040 #define SSI_STR_TXD2RXD 0x00000080
#define CCSR_SSI_STR_TFS2RFS 0x00000020 #define SSI_STR_TCK2RCK 0x00000040
#define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F) #define SSI_STR_TFS2RFS 0x00000020
#define SSI_STR_TXSTATE(x) ((x) & 0x1F)
#define CCSR_SSI_SOR_CLKOFF 0x00000040 /* SSI Option Register -- REG_SSI_SOR 0x34 */
#define CCSR_SSI_SOR_RX_CLR 0x00000020 #define SSI_SOR_CLKOFF 0x00000040
#define CCSR_SSI_SOR_TX_CLR 0x00000010 #define SSI_SOR_RX_CLR 0x00000020
#define CCSR_SSI_SOR_INIT 0x00000008 #define SSI_SOR_TX_CLR 0x00000010
#define CCSR_SSI_SOR_WAIT_SHIFT 1 #define SSI_SOR_xX_CLR(tx) ((tx) ? SSI_SOR_TX_CLR : SSI_SOR_RX_CLR)
#define CCSR_SSI_SOR_WAIT_MASK 0x00000006 #define SSI_SOR_INIT 0x00000008
#define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) #define SSI_SOR_WAIT_SHIFT 1
#define CCSR_SSI_SOR_SYNRST 0x00000001 #define SSI_SOR_WAIT_MASK 0x00000006
#define SSI_SOR_WAIT(x) (((x) & 3) << SSI_SOR_WAIT_SHIFT)
#define SSI_SOR_SYNRST 0x00000001
#define CCSR_SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) /* SSI AC97 Control Register -- REG_SSI_SACNT 0x38 */
#define CCSR_SSI_SACNT_WR 0x00000010 #define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
#define CCSR_SSI_SACNT_RD 0x00000008 #define SSI_SACNT_WR 0x00000010
#define CCSR_SSI_SACNT_RDWR_MASK 0x00000018 #define SSI_SACNT_RD 0x00000008
#define CCSR_SSI_SACNT_TIF 0x00000004 #define SSI_SACNT_RDWR_MASK 0x00000018
#define CCSR_SSI_SACNT_FV 0x00000002 #define SSI_SACNT_TIF 0x00000004
#define CCSR_SSI_SACNT_AC97EN 0x00000001 #define SSI_SACNT_FV 0x00000002
#define SSI_SACNT_AC97EN 0x00000001
struct device; struct device;
...@@ -255,7 +318,7 @@ static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr) ...@@ -255,7 +318,7 @@ static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr)
} }
static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
struct device *dev) struct device *dev)
{ {
return 0; return 0;
} }
......
...@@ -18,86 +18,86 @@ ...@@ -18,86 +18,86 @@
void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr) void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr)
{ {
if (sisr & CCSR_SSI_SISR_RFRC) if (sisr & SSI_SISR_RFRC)
dbg->stats.rfrc++; dbg->stats.rfrc++;
if (sisr & CCSR_SSI_SISR_TFRC) if (sisr & SSI_SISR_TFRC)
dbg->stats.tfrc++; dbg->stats.tfrc++;
if (sisr & CCSR_SSI_SISR_CMDAU) if (sisr & SSI_SISR_CMDAU)
dbg->stats.cmdau++; dbg->stats.cmdau++;
if (sisr & CCSR_SSI_SISR_CMDDU) if (sisr & SSI_SISR_CMDDU)
dbg->stats.cmddu++; dbg->stats.cmddu++;
if (sisr & CCSR_SSI_SISR_RXT) if (sisr & SSI_SISR_RXT)
dbg->stats.rxt++; dbg->stats.rxt++;
if (sisr & CCSR_SSI_SISR_RDR1) if (sisr & SSI_SISR_RDR1)
dbg->stats.rdr1++; dbg->stats.rdr1++;
if (sisr & CCSR_SSI_SISR_RDR0) if (sisr & SSI_SISR_RDR0)
dbg->stats.rdr0++; dbg->stats.rdr0++;
if (sisr & CCSR_SSI_SISR_TDE1) if (sisr & SSI_SISR_TDE1)
dbg->stats.tde1++; dbg->stats.tde1++;
if (sisr & CCSR_SSI_SISR_TDE0) if (sisr & SSI_SISR_TDE0)
dbg->stats.tde0++; dbg->stats.tde0++;
if (sisr & CCSR_SSI_SISR_ROE1) if (sisr & SSI_SISR_ROE1)
dbg->stats.roe1++; dbg->stats.roe1++;
if (sisr & CCSR_SSI_SISR_ROE0) if (sisr & SSI_SISR_ROE0)
dbg->stats.roe0++; dbg->stats.roe0++;
if (sisr & CCSR_SSI_SISR_TUE1) if (sisr & SSI_SISR_TUE1)
dbg->stats.tue1++; dbg->stats.tue1++;
if (sisr & CCSR_SSI_SISR_TUE0) if (sisr & SSI_SISR_TUE0)
dbg->stats.tue0++; dbg->stats.tue0++;
if (sisr & CCSR_SSI_SISR_TFS) if (sisr & SSI_SISR_TFS)
dbg->stats.tfs++; dbg->stats.tfs++;
if (sisr & CCSR_SSI_SISR_RFS) if (sisr & SSI_SISR_RFS)
dbg->stats.rfs++; dbg->stats.rfs++;
if (sisr & CCSR_SSI_SISR_TLS) if (sisr & SSI_SISR_TLS)
dbg->stats.tls++; dbg->stats.tls++;
if (sisr & CCSR_SSI_SISR_RLS) if (sisr & SSI_SISR_RLS)
dbg->stats.rls++; dbg->stats.rls++;
if (sisr & CCSR_SSI_SISR_RFF1) if (sisr & SSI_SISR_RFF1)
dbg->stats.rff1++; dbg->stats.rff1++;
if (sisr & CCSR_SSI_SISR_RFF0) if (sisr & SSI_SISR_RFF0)
dbg->stats.rff0++; dbg->stats.rff0++;
if (sisr & CCSR_SSI_SISR_TFE1) if (sisr & SSI_SISR_TFE1)
dbg->stats.tfe1++; dbg->stats.tfe1++;
if (sisr & CCSR_SSI_SISR_TFE0) if (sisr & SSI_SISR_TFE0)
dbg->stats.tfe0++; dbg->stats.tfe0++;
} }
/* Show the statistics of a flag only if its interrupt is enabled. The /**
* compiler will optimze this code to a no-op if the interrupt is not * Show the statistics of a flag only if its interrupt is enabled
* enabled. *
* Compilers will optimize it to a no-op if the interrupt is disabled
*/ */
#define SIER_SHOW(flag, name) \ #define SIER_SHOW(flag, name) \
do { \ do { \
if (CCSR_SSI_SIER_##flag) \ if (SSI_SIER_##flag) \
seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \ seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \
} while (0) } while (0)
/** /**
* fsl_sysfs_ssi_show: display SSI statistics * Display the statistics for the current SSI device
* *
* Display the statistics for the current SSI device. To avoid confusion, * To avoid confusion, only show those counts that are enabled
* we only show those counts that are enabled.
*/ */
static int fsl_ssi_stats_show(struct seq_file *s, void *unused) static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
{ {
...@@ -147,7 +147,8 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) ...@@ -147,7 +147,8 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
return -ENOMEM; return -ENOMEM;
ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO, ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops); ssi_dbg->dbg_dir, ssi_dbg,
&fsl_ssi_stats_ops);
if (!ssi_dbg->dbg_stats) { if (!ssi_dbg->dbg_stats) {
debugfs_remove(ssi_dbg->dbg_dir); debugfs_remove(ssi_dbg->dbg_dir);
return -ENOMEM; return -ENOMEM;
......
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