summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/soc/fsl/fsl_ssi.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index d0b9fe31f49a..a85268bb4507 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -131,6 +131,18 @@ enum fsl_ssi_type {
FSL_SSI_MX51,
};
+struct fsl_ssi_reg_val {
+ u32 sier;
+ u32 srcr;
+ u32 stcr;
+ u32 scr;
+};
+
+struct fsl_ssi_rxtx_reg_val {
+ struct fsl_ssi_reg_val rx;
+ struct fsl_ssi_reg_val tx;
+};
+
/**
* fsl_ssi_private: per-SSI private data
*
@@ -169,6 +181,8 @@ struct fsl_ssi_private {
struct imx_dma_data filter_data_tx;
struct imx_dma_data filter_data_rx;
struct imx_pcm_fiq_params fiq_params;
+ /* Register values for rx/tx configuration */
+ struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
struct {
unsigned int rfrc;
@@ -462,6 +476,114 @@ static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
+/*
+ * Enable/Disable all rx/tx config flags at once.
+ */
+static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
+ bool enable)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val;
+
+ if (enable) {
+ write_ssi_mask(&ssi->sier, 0, vals->rx.sier | vals->tx.sier);
+ write_ssi_mask(&ssi->srcr, 0, vals->rx.srcr | vals->tx.srcr);
+ write_ssi_mask(&ssi->stcr, 0, vals->rx.stcr | vals->tx.stcr);
+ } else {
+ write_ssi_mask(&ssi->srcr, vals->rx.srcr | vals->tx.srcr, 0);
+ write_ssi_mask(&ssi->stcr, vals->rx.stcr | vals->tx.stcr, 0);
+ write_ssi_mask(&ssi->sier, vals->rx.sier | vals->tx.sier, 0);
+ }
+}
+
+/*
+ * Enable/Disable a ssi configuration. You have to pass either
+ * ssi_private->rxtx_reg_val.rx or tx as vals parameter.
+ */
+static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
+ struct fsl_ssi_reg_val *vals)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ struct fsl_ssi_reg_val *avals;
+ u32 scr_val = read_ssi(&ssi->scr);
+ int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
+ !!(scr_val & CCSR_SSI_SCR_RE);
+
+ /* Find the other direction values rx or tx which we do not want to
+ * modify */
+ if (&ssi_private->rxtx_reg_val.rx == vals)
+ avals = &ssi_private->rxtx_reg_val.tx;
+ else
+ avals = &ssi_private->rxtx_reg_val.rx;
+
+ /* If vals should be disabled, start with disabling the unit */
+ if (!enable) {
+ u32 scr = vals->scr & (vals->scr ^ avals->scr);
+ write_ssi_mask(&ssi->scr, scr, 0);
+ }
+
+ /*
+ * We are running on a SoC which does not support online SSI
+ * reconfiguration, so we have to enable all necessary flags at once
+ * even if we do not use them later (capture and playback configuration)
+ */
+ if (ssi_private->offline_config) {
+ if ((enable && !nr_active_streams) ||
+ (!enable && nr_active_streams == 1))
+ fsl_ssi_rxtx_config(ssi_private, enable);
+
+ goto config_done;
+ }
+
+ /*
+ * Configure single direction units while the SSI unit is running
+ * (online configuration)
+ */
+ if (enable) {
+ write_ssi_mask(&ssi->sier, 0, vals->sier);
+ write_ssi_mask(&ssi->srcr, 0, vals->srcr);
+ write_ssi_mask(&ssi->stcr, 0, vals->stcr);
+ } else {
+ u32 sier;
+ u32 srcr;
+ u32 stcr;
+
+ /*
+ * Disabling the necessary flags for one of rx/tx while the
+ * other stream is active is a little bit more difficult. We
+ * 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 = vals->sier & (vals->sier ^ avals->sier);
+ srcr = vals->srcr & (vals->srcr ^ avals->srcr);
+ stcr = vals->stcr & (vals->stcr ^ avals->stcr);
+
+ write_ssi_mask(&ssi->srcr, srcr, 0);
+ write_ssi_mask(&ssi->stcr, stcr, 0);
+ write_ssi_mask(&ssi->sier, sier, 0);
+ }
+
+config_done:
+ /* Enabling of subunits is done after configuration */
+ if (enable)
+ write_ssi_mask(&ssi->scr, 0, vals->scr);
+}
+
+
+static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable)
+{
+ fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx);
+}
+
+static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable)
+{
+ fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx);
+}
+
static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
{
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
OpenPOWER on IntegriCloud