[Sound-open-firmware] [PATCH] ssp: add ssp support for APL and CNL
Now it supports I2S & TDM4 master mode and no MN devide used. For 19.2M clock, 16bit 48K stream maybe need to pack to 20bit 48K.
Signed-off-by: Rander Wang rander.wang@intel.com --- configure.ac | 1 + src/drivers/Makefile.am | 9 +- src/drivers/apl-ssp.c | 547 ++++++++++++++++++++++++++++++++++++++++++++++++ src/include/reef/ssp.h | 66 +++++- 4 files changed, 612 insertions(+), 11 deletions(-) create mode 100644 src/drivers/apl-ssp.c
diff --git a/configure.ac b/configure.ac index 05a3bee..1c84020 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,7 @@ AM_CONDITIONAL(BUILD_APOLLOLAKE, test "$FW_NAME" = "apl") AM_CONDITIONAL(BUILD_CANNONLAKE, test "$FW_NAME" = "cnl") AM_CONDITIONAL(BUILD_BOOTLOADER, test "$FW_NAME" = "cnl") AM_CONDITIONAL(BUILD_MODULE, test "$FW_NAME" = "apl" -o "$FW_NAME" = "cnl") +AM_CONDITIONAL(BUILD_APL_SSP, test "$FW_NAME" = "apl" -o "$FW_NAME" = "cnl")
# DSP core support (Optional) AC_ARG_WITH([dsp-core], diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am index 8c507fa..53b52af 100644 --- a/src/drivers/Makefile.am +++ b/src/drivers/Makefile.am @@ -1,9 +1,16 @@ noinst_LIBRARIES = libdrivers.a
libdrivers_a_SOURCES = \ - ssp.c \ dw-dma.c
+if BUILD_APL_SSP +libdrivers_a_SOURCES += \ + apl-ssp.c +else + libdrivers_a_SOURCES += \ + ssp.c +endif + libdrivers_a_CFLAGS = \ $(ARCH_CFLAGS) \ $(REEF_INCDIR) \ diff --git a/src/drivers/apl-ssp.c b/src/drivers/apl-ssp.c new file mode 100644 index 0000000..09adee2 --- /dev/null +++ b/src/drivers/apl-ssp.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + * Rander Wang rander.wang@linux.intel.com + */ + +#include <errno.h> +#include <stdbool.h> +#include <reef/stream.h> +#include <reef/ssp.h> +#include <reef/alloc.h> +#include <reef/interrupt.h> + +/* tracing */ +#define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) +#define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) +#define tracev_ssp(__e) tracev_event(TRACE_CLASS_SSP, __e) + +/* save SSP context prior to entering D3 */ +static int ssp_context_store(struct dai *dai) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + ssp->sscr0 = ssp_read(dai, SSCR0); + ssp->sscr1 = ssp_read(dai, SSCR1); + + /* FIXME: need to store sscr2,3,4,5 */ + ssp->psp = ssp_read(dai, SSPSP); + + return 0; +} + +/* restore SSP context after leaving D3 */ +static int ssp_context_restore(struct dai *dai) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + ssp_write(dai, SSCR0, ssp->sscr0); + ssp_write(dai, SSCR1, ssp->sscr1); + /* FIXME: need to restore sscr2,3,4,5 */ + ssp_write(dai, SSPSP, ssp->psp); + + return 0; +} + +/* Digital Audio interface formatting */ +static inline int ssp_set_config(struct dai *dai, + struct sof_ipc_dai_config *config) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + uint32_t sscr0; + uint32_t sscr1; + uint32_t sscr2; + uint32_t sscr3; + uint32_t sspsp; + uint32_t sspsp2; + uint32_t sstsa; + uint32_t ssrsa; + uint32_t ssto; + uint32_t ssioc; + uint32_t mdiv; + uint32_t bdiv; + uint32_t mdivc; + uint32_t mdivr; + uint32_t i2s_m; + uint32_t i2s_n; + uint32_t data_size; + uint32_t start_delay; + uint32_t frame_len = 0; + bool inverted_frame = false; + int ret = 0; + + spin_lock(&ssp->lock); + + /* is playback/capture already running */ + if (ssp->state[DAI_DIR_PLAYBACK] == COMP_STATE_ACTIVE || + ssp->state[DAI_DIR_CAPTURE] == COMP_STATE_ACTIVE) { + trace_ssp_error("ec1"); + ret = -EINVAL; + goto out; + } + + trace_ssp("cos"); + + /* reset SSP settings */ + /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */ + /* + * FIXME: MOD, ACS, NCS are not set, + * no support for network mode for now + */ + sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM; + + /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */ + sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_RWOT | SSCR1_TRAIL; +// sscr1 |= SSCR1_TIE | SSCR1_RIE; + + /* sscr2 dynamic setting is LJDFD */ + sscr2 = SSCR2_SDFD | SSCR2_TURM1; + + /* sscr3 dynamic settings are TFT, RFT */ + sscr3 = 0; + + /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */ +// sspsp = SSPSP_ETDS; /* make sure SDO line is tri-stated when inactive */ + sspsp = 0; + + ssp->config = *config; + ssp->params = config->ssp[0]; + + /* sspsp2 no dynamic setting */ + sspsp2 = 0x0; + + /* ssioc dynamic setting is SFCR */ + ssioc = SSIOC_SCOE; + + /* i2s_m M divider setting, default 1 */ + i2s_m = 0x1; + + /* i2s_n N divider setting, default 1 */ + i2s_n = 0x1; + + /* ssto no dynamic setting */ + ssto = 0x0; + + /* sstsa dynamic setting is TTSA, default 2 slots */ + sstsa = config->tx_slot_mask; + + /* ssrsa dynamic setting is RTSA, default 2 slots */ + ssrsa = config->rx_slot_mask; + + /* clock masters */ + sscr1 &= ~SSCR1_SFRMDIR; + + trace_value(config->format); + switch (config->format & SOF_DAI_FMT_MASTER_MASK) { + case SOF_DAI_FMT_CBM_CFM: + sscr0 |= SSCR0_ECS; /* external clock used */ + sscr1 |= SSCR1_SCLKDIR; + /* + * FIXME: does SSRC1.SCFR need to be set + * when codec is master ? + */ + break; + case SOF_DAI_FMT_CBS_CFS: + sscr1 |= SSCR1_SCFR; + ssioc |= SSIOC_SFCR; + break; + case SOF_DAI_FMT_CBM_CFS: + sscr0 |= SSCR0_ECS; /* external clock used */ + sscr1 |= SSCR1_SCLKDIR; + /* + * FIXME: does SSRC1.SCFR need to be set + * when codec is master ? + */ + /* FIXME: this mode has not been tested */ + break; + case SOF_DAI_FMT_CBS_CFM: + sscr1 |= SSCR1_SCFR; + /* FIXME: this mode has not been tested */ + break; + default: + trace_ssp_error("ec2"); + ret = -EINVAL; + goto out; + } + + /* clock signal polarity */ + switch (config->format & SOF_DAI_FMT_INV_MASK) { + case SOF_DAI_FMT_NB_NF: + break; + case SOF_DAI_FMT_NB_IF: + break; + case SOF_DAI_FMT_IB_IF: + sspsp |= SSPSP_SCMODE(2); + inverted_frame = true; /* handled later with format */ + break; + case SOF_DAI_FMT_IB_NF: + sspsp |= SSPSP_SCMODE(2); + inverted_frame = true; /* handled later with format */ + break; + default: + trace_ssp_error("ec3"); + ret = -EINVAL; + goto out; + } + +#ifdef CLK_TYPE /* not enabled, keep the code for reference */ + /* TODO: allow topology to define SSP clock type */ + config->ssp[0].clk_id = SSP_CLK_EXT; + + /* clock source */ + switch (config->ssp[0].clk_id) { + case SSP_CLK_AUDIO: + sscr0 |= SSCR0_ACS; + break; + case SSP_CLK_NET_PLL: + sscr0 |= SSCR0_MOD; + break; + case SSP_CLK_EXT: + sscr0 |= SSCR0_ECS; + break; + case SSP_CLK_NET: + sscr0 |= SSCR0_NCS | SSCR0_MOD; + break; + default: + trace_ssp_error("ec4"); + ret = -EINVAL; + goto out; + } +#else + sscr0 |= SSCR0_MOD | SSCR0_ACS; +#endif + + /* BCLK is generated from MCLK - must be divisable */ + if (config->mclk % config->bclk) { + trace_ssp_error("ec5"); + ret = -EINVAL; + goto out; + } + + /* divisor must be within SCR range */ + mdiv = (config->mclk / config->bclk) - 1; + if (mdiv > (SSCR0_SCR_MASK >> 8)) { + trace_ssp_error("ec6"); + ret = -EINVAL; + goto out; + } + + /* set the SCR divisor */ + sscr0 |= SSCR0_SCR(mdiv); + + /* calc frame width based on BCLK and rate - must be divisable */ + if (config->bclk % config->fclk) { + trace_ssp_error("ec7"); + ret = -EINVAL; + goto out; + } + + /* must be enouch BCLKs for data */ + bdiv = config->bclk / config->fclk; + if (bdiv < config->sample_container_bits * + ((config->rx_slot_mask + 1) / 2)) { + trace_ssp_error("ec8"); + ret = -EINVAL; + goto out; + } + + /* sample_container_bits must be <= 38 for SSP */ + if (config->sample_container_bits > 38) { + trace_ssp_error("ec9"); + ret = -EINVAL; + goto out; + } + + /* format */ + switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { + case SOF_DAI_FMT_I2S: + + start_delay = config->sample_container_bits > + config->sample_valid_bits ? 1 : 0; + + sscr0 |= SSCR0_FRDC(config->num_slots); + + /* set asserted frame length */ + frame_len = config->sample_container_bits; + + /* handle frame polarity, I2S default is falling/active low */ + sspsp |= SSPSP_SFRMP(!inverted_frame); + + break; + + case SOF_DAI_FMT_LEFT_J: + + start_delay = 0; + + sscr0 |= SSCR0_FRDC(config->num_slots); + + /* LJDFD enable */ + sscr2 &= ~SSCR2_LJDFD; + + /* set asserted frame length */ + frame_len = config->sample_container_bits; + + /* LEFT_J default is rising/active high, opposite of I2S */ + sspsp |= SSPSP_SFRMP(inverted_frame); + + break; + case SOF_DAI_FMT_DSP_A: + + start_delay = config->sample_container_bits > + config->sample_valid_bits ? 1 : 0; + + sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots); + + /* set asserted frame length */ + frame_len = config->sample_container_bits; + + /* handle frame polarity, DSP_A default is rising/active high */ + sspsp |= SSPSP_SFRMP(inverted_frame); + + break; + case SOF_DAI_FMT_DSP_B: + + start_delay = 0; + + sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots); + + /* set asserted frame length */ + frame_len = config->sample_container_bits; + + /* handle frame polarity, DSP_A default is rising/active high */ + sspsp |= SSPSP_SFRMP(inverted_frame); + + break; + default: + trace_ssp_error("eca"); + ret = -EINVAL; + goto out; + } + + sspsp |= SSPSP_DMYSTRT(start_delay); + sspsp |= SSPSP_SFRMWDTH(frame_len); + + data_size = config->sample_valid_bits; + + if (data_size > 16) + sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); + else + sscr0 |= SSCR0_DSIZE(data_size); + + mdivc = 0x00100001; + /* bypass divider for MCLK */ + mdivr = 0x00000fff; + + trace_ssp("coe"); + ssp_write(dai, SSCR0, sscr0); + ssp_write(dai, SSCR1, sscr1); + ssp_write(dai, SSCR2, sscr2); + ssp_write(dai, SSCR3, sscr3); + ssp_write(dai, SSPSP, sspsp); + ssp_write(dai, SSPSP2, sspsp2); + ssp_write(dai, SSIOC, ssioc); + ssp_write(dai, SSTO, ssto); + ssp_write(dai, SSTSA, sstsa); + ssp_write(dai, SSRSA, ssrsa); + + trace_value(sscr0); + trace_value(sscr1); + trace_value(ssto); + trace_value(sspsp); + trace_value(sstsa); + trace_value(ssrsa); + trace_value(sscr2); + trace_value(sspsp2); + trace_value(sscr3); + trace_value(ssioc); + + mn_reg_write(0x0, mdivc); + mn_reg_write(0x80, mdivr); + mn_reg_write(0x100 + config->id * 0x8 + 0x0, i2s_m); + mn_reg_write(0x100 + config->id * 0x8 + 0x4, i2s_n); + + ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; + ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE; + +out: + spin_unlock(&ssp->lock); + + return ret; +} + +/* Digital Audio interface formatting */ +static inline int ssp_set_loopback_mode(struct dai *dai, uint32_t lbm) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + trace_ssp("loo"); + spin_lock(&ssp->lock); + + ssp_update_bits(dai, SSCR1, SSCR1_LBM, lbm ? SSCR1_LBM : 0); + + spin_unlock(&ssp->lock); + + return 0; +} + +/* start the SSP for either playback or capture */ +static void ssp_start(struct dai *dai, int direction) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + spin_lock(&ssp->lock); + + /* enable port */ + ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); + ssp->state[direction] = COMP_STATE_ACTIVE; + + trace_ssp("sta"); + + /* enable DMA */ + if (direction == DAI_DIR_PLAYBACK) { + ssp_update_bits(dai, SSCR1, SSCR1_TSRE, SSCR1_TSRE); + ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x1 << 8); + } else { + ssp_update_bits(dai, SSCR1, SSCR1_RSRE, SSCR1_RSRE); + ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x1 << 8); + } + + spin_unlock(&ssp->lock); +} + +/* stop the SSP for either playback or capture */ +static void ssp_stop(struct dai *dai) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + spin_lock(&ssp->lock); + + /* stop Rx if we are not capturing */ + if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE) { + ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0); + ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x0 << 8); + trace_ssp("Ss0"); + } + + /* stop Tx if we are not playing */ + if (ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { + ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0); + ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x0 << 8); + trace_ssp("Ss1"); + } + + /* disable SSP port if no users */ + if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE && + ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { + ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); + ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; + ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE; + trace_ssp("Ss2"); + } + + spin_unlock(&ssp->lock); +} + +static int ssp_trigger(struct dai *dai, int cmd, int direction) +{ + struct ssp_pdata *ssp = dai_get_drvdata(dai); + + trace_ssp("tri"); + + switch (cmd) { + case COMP_CMD_START: + if (ssp->state[direction] == COMP_STATE_PREPARE || + ssp->state[direction] == COMP_STATE_PAUSED) + ssp_start(dai, direction); + break; + case COMP_CMD_RELEASE: + if (ssp->state[direction] == COMP_STATE_PAUSED || + ssp->state[direction] == COMP_STATE_PREPARE) + ssp_start(dai, direction); + break; + case COMP_CMD_STOP: + case COMP_CMD_PAUSE: + ssp->state[direction] = COMP_STATE_PAUSED; + ssp_stop(dai); + break; + case COMP_CMD_RESUME: + ssp_context_restore(dai); + break; + case COMP_CMD_SUSPEND: + ssp_context_store(dai); + break; + default: + break; + } + + return 0; +} + +/* clear IRQ sources atm */ +static void ssp_irq_handler(void *data) +{ + struct dai *dai = data; + + trace_ssp("irq"); + trace_value(ssp_read(dai, SSSR)); + + /* clear IRQ */ + ssp_write(dai, SSSR, ssp_read(dai, SSSR)); + platform_interrupt_clear(ssp_irq(dai), 1); +} + +static int ssp_probe(struct dai *dai) +{ + struct ssp_pdata *ssp; + + /* allocate private data */ + ssp = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(*ssp)); + dai_set_drvdata(dai, ssp); + + spinlock_init(&ssp->lock); + + ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY; + ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY; + + /* register our IRQ handler */ + interrupt_register(ssp_irq(dai), ssp_irq_handler, dai); + platform_interrupt_unmask(ssp_irq(dai), 1); + interrupt_enable(ssp_irq(dai)); + + return 0; +} + +const struct dai_ops ssp_ops = { + .trigger = ssp_trigger, + .set_config = ssp_set_config, + .pm_context_store = ssp_context_store, + .pm_context_restore = ssp_context_restore, + .probe = ssp_probe, + .set_loopback_mode = ssp_set_loopback_mode, +}; diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h index c00d14c..e041121 100644 --- a/src/include/reef/ssp.h +++ b/src/include/reef/ssp.h @@ -38,6 +38,8 @@ #include <reef/trace.h> #include <reef/wait.h>
+#define BIT(x) (1 << (x)) + #define SSP_CLK_AUDIO 0 #define SSP_CLK_NET_PLL 1 #define SSP_CLK_EXT 2 @@ -55,11 +57,17 @@ #define SSTSA 0x30 #define SSRSA 0x34 #define SSTSS 0x38 + +#if defined CONFIG_BAYTRAIL ||\ + defined CONFIG_CHERRYTRAIL ||\ + defined CONFIG_BROADWELL ||\ + defined CONFIG_HASWELL #define SSCR2 0x40 #define SFIFOTT 0x6C #define SSCR3 0x70 #define SSCR4 0x74 #define SSCR5 0x78 +#endif
extern const struct dai_ops ssp_ops;
@@ -112,17 +120,28 @@ extern const struct dai_ops ssp_ops; #define SSCR1_TTE (1 << 30) #define SSCR1_TTELP (1 << 31)
+#if defined CONFIG_BAYTRAIL ||\ + defined CONFIG_CHERRYTRAIL ||\ + defined CONFIG_BROADWELL ||\ + defined CONFIG_HASWELL /* SSCR2 bits */ -#define SSCR2_URUN_FIX0 (1 << 0) -#define SSCR2_URUN_FIX1 (1 << 1) -#define SSCR2_SLV_EXT_CLK_RUN_EN (1 << 2) -#define SSCR2_CLK_DEL_EN (1 << 3) -#define SSCR2_UNDRN_FIX_EN (1 << 6) -#define SSCR2_FIFO_EMPTY_FIX_EN (1 << 7) -#define SSCR2_ASRC_CNTR_EN (1 << 8) -#define SSCR2_ASRC_CNTR_CLR (1 << 9) -#define SSCR2_ASRC_FRM_CNRT_EN (1 << 10) -#define SSCR2_ASRC_INTR_MASK (1 << 11) +#define SSCR2_URUN_FIX0 BIT(0) +#define SSCR2_URUN_FIX1 BIT(1) +#define SSCR2_SLV_EXT_CLK_RUN_EN BIT(2) +#define SSCR2_CLK_DEL_EN BIT(3) +#define SSCR2_UNDRN_FIX_EN BIT(6) +#define SSCR2_FIFO_EMPTY_FIX_EN BIT(7) +#define SSCR2_ASRC_CNTR_EN BIT(8) +#define SSCR2_ASRC_CNTR_CLR BIT(9) +#define SSCR2_ASRC_FRM_CNRT_EN BIT(10) +#define SSCR2_ASRC_INTR_MASK BIT(11) +#elif defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSCR2_TURM1 BIT(1) +#define SSCR2_SDFD BIT(14) +#define SSCR2_SDPM BIT(16) +#define SSCR2_LJDFD BIT(17) +#endif +
/* SSR bits */ #define SSSR_TNF (1 << 2) @@ -143,6 +162,17 @@ extern const struct dai_ops ssp_ops; #define SSPSP_DMYSTOP(x) ((x) << 23) #define SSPSP_FSRT (1 << 25)
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSPSP_EDMYSTOP(x) ((x) << 26) +#define SSTSS 0x38 +#define SSCR2 0x40 +#define SSPSP2 0x44 +#define SSCR3 0x48 +#define SSIOC 0x4C + +#define SSP_REG_MAX SSIOC +#endif + /* SSCR3 bits */ #define SSCR3_FRM_MST_EN (1 << 0) #define SSCR3_I2S_MODE_EN (1 << 1) @@ -169,6 +199,22 @@ extern const struct dai_ops ssp_ops; #define SFIFOTT_TX(x) ((x) - 1) #define SFIFOTT_RX(x) (((x) - 1) << 16)
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSTSA_TSEN BIT(8) +#define SSRSA_RSEN BIT(8) + +#define SSCR3_TFL_MASK (0x0000003f) +#define SSCR3_RFL_MASK (0x00003f00) +#define SSCR3_TFT_MASK (0x003f0000) +#define SSCR3_TX(x) (((x) - 1) << 16) +#define SSCR3_RFT_MASK (0x3f000000) +#define SSCR3_RX(x) (((x) - 1) << 24) + +#define SSIOC_TXDPDEB BIT(1) +#define SSIOC_SFCR BIT(4) +#define SSIOC_SCOE BIT(5) +#endif + /* tracing */ #define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e)
On 2018年02月08日 15:54, Rander Wang wrote:
Now it supports I2S & TDM4 master mode and no MN devide used. For 19.2M clock, 16bit 48K stream maybe need to pack to 20bit 48K.
Keep my sign-off here also please.
Thanks, ~Keyon
Signed-off-by: Rander Wang rander.wang@intel.com
configure.ac | 1 + src/drivers/Makefile.am | 9 +- src/drivers/apl-ssp.c | 547 ++++++++++++++++++++++++++++++++++++++++++++++++ src/include/reef/ssp.h | 66 +++++- 4 files changed, 612 insertions(+), 11 deletions(-) create mode 100644 src/drivers/apl-ssp.c
diff --git a/configure.ac b/configure.ac index 05a3bee..1c84020 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,7 @@ AM_CONDITIONAL(BUILD_APOLLOLAKE, test "$FW_NAME" = "apl") AM_CONDITIONAL(BUILD_CANNONLAKE, test "$FW_NAME" = "cnl") AM_CONDITIONAL(BUILD_BOOTLOADER, test "$FW_NAME" = "cnl") AM_CONDITIONAL(BUILD_MODULE, test "$FW_NAME" = "apl" -o "$FW_NAME" = "cnl") +AM_CONDITIONAL(BUILD_APL_SSP, test "$FW_NAME" = "apl" -o "$FW_NAME" = "cnl")
# DSP core support (Optional) AC_ARG_WITH([dsp-core], diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am index 8c507fa..53b52af 100644 --- a/src/drivers/Makefile.am +++ b/src/drivers/Makefile.am @@ -1,9 +1,16 @@ noinst_LIBRARIES = libdrivers.a
libdrivers_a_SOURCES = \
- ssp.c \ dw-dma.c
+if BUILD_APL_SSP +libdrivers_a_SOURCES += \
- apl-ssp.c
+else
- libdrivers_a_SOURCES += \
- ssp.c
+endif
- libdrivers_a_CFLAGS = \ $(ARCH_CFLAGS) \ $(REEF_INCDIR) \
diff --git a/src/drivers/apl-ssp.c b/src/drivers/apl-ssp.c new file mode 100644 index 0000000..09adee2 --- /dev/null +++ b/src/drivers/apl-ssp.c @@ -0,0 +1,547 @@ +/*
- Copyright (c) 2016, Intel Corporation
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Intel Corporation nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- Author: Liam Girdwood liam.r.girdwood@linux.intel.com
Keyon Jie <yang.jie@linux.intel.com>
Rander Wang <rander.wang@linux.intel.com>
- */
+#include <errno.h> +#include <stdbool.h> +#include <reef/stream.h> +#include <reef/ssp.h> +#include <reef/alloc.h> +#include <reef/interrupt.h>
+/* tracing */ +#define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) +#define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) +#define tracev_ssp(__e) tracev_event(TRACE_CLASS_SSP, __e)
+/* save SSP context prior to entering D3 */ +static int ssp_context_store(struct dai *dai) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- ssp->sscr0 = ssp_read(dai, SSCR0);
- ssp->sscr1 = ssp_read(dai, SSCR1);
- /* FIXME: need to store sscr2,3,4,5 */
- ssp->psp = ssp_read(dai, SSPSP);
- return 0;
+}
+/* restore SSP context after leaving D3 */ +static int ssp_context_restore(struct dai *dai) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- ssp_write(dai, SSCR0, ssp->sscr0);
- ssp_write(dai, SSCR1, ssp->sscr1);
- /* FIXME: need to restore sscr2,3,4,5 */
- ssp_write(dai, SSPSP, ssp->psp);
- return 0;
+}
+/* Digital Audio interface formatting */ +static inline int ssp_set_config(struct dai *dai,
- struct sof_ipc_dai_config *config)
+{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- uint32_t sscr0;
- uint32_t sscr1;
- uint32_t sscr2;
- uint32_t sscr3;
- uint32_t sspsp;
- uint32_t sspsp2;
- uint32_t sstsa;
- uint32_t ssrsa;
- uint32_t ssto;
- uint32_t ssioc;
- uint32_t mdiv;
- uint32_t bdiv;
- uint32_t mdivc;
- uint32_t mdivr;
- uint32_t i2s_m;
- uint32_t i2s_n;
- uint32_t data_size;
- uint32_t start_delay;
- uint32_t frame_len = 0;
- bool inverted_frame = false;
- int ret = 0;
- spin_lock(&ssp->lock);
- /* is playback/capture already running */
- if (ssp->state[DAI_DIR_PLAYBACK] == COMP_STATE_ACTIVE ||
ssp->state[DAI_DIR_CAPTURE] == COMP_STATE_ACTIVE) {
trace_ssp_error("ec1");
ret = -EINVAL;
goto out;
- }
- trace_ssp("cos");
- /* reset SSP settings */
- /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */
- /*
* FIXME: MOD, ACS, NCS are not set,
* no support for network mode for now
*/
- sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM;
- /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */
- sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_RWOT | SSCR1_TRAIL;
+// sscr1 |= SSCR1_TIE | SSCR1_RIE;
- /* sscr2 dynamic setting is LJDFD */
- sscr2 = SSCR2_SDFD | SSCR2_TURM1;
- /* sscr3 dynamic settings are TFT, RFT */
- sscr3 = 0;
- /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */
+// sspsp = SSPSP_ETDS; /* make sure SDO line is tri-stated when inactive */
- sspsp = 0;
- ssp->config = *config;
- ssp->params = config->ssp[0];
- /* sspsp2 no dynamic setting */
- sspsp2 = 0x0;
- /* ssioc dynamic setting is SFCR */
- ssioc = SSIOC_SCOE;
- /* i2s_m M divider setting, default 1 */
- i2s_m = 0x1;
- /* i2s_n N divider setting, default 1 */
- i2s_n = 0x1;
- /* ssto no dynamic setting */
- ssto = 0x0;
- /* sstsa dynamic setting is TTSA, default 2 slots */
- sstsa = config->tx_slot_mask;
- /* ssrsa dynamic setting is RTSA, default 2 slots */
- ssrsa = config->rx_slot_mask;
- /* clock masters */
- sscr1 &= ~SSCR1_SFRMDIR;
- trace_value(config->format);
- switch (config->format & SOF_DAI_FMT_MASTER_MASK) {
- case SOF_DAI_FMT_CBM_CFM:
sscr0 |= SSCR0_ECS; /* external clock used */
sscr1 |= SSCR1_SCLKDIR;
/*
* FIXME: does SSRC1.SCFR need to be set
* when codec is master ?
*/
break;
- case SOF_DAI_FMT_CBS_CFS:
sscr1 |= SSCR1_SCFR;
ssioc |= SSIOC_SFCR;
break;
- case SOF_DAI_FMT_CBM_CFS:
sscr0 |= SSCR0_ECS; /* external clock used */
sscr1 |= SSCR1_SCLKDIR;
/*
* FIXME: does SSRC1.SCFR need to be set
* when codec is master ?
*/
/* FIXME: this mode has not been tested */
break;
- case SOF_DAI_FMT_CBS_CFM:
sscr1 |= SSCR1_SCFR;
/* FIXME: this mode has not been tested */
break;
- default:
trace_ssp_error("ec2");
ret = -EINVAL;
goto out;
- }
- /* clock signal polarity */
- switch (config->format & SOF_DAI_FMT_INV_MASK) {
- case SOF_DAI_FMT_NB_NF:
break;
- case SOF_DAI_FMT_NB_IF:
break;
- case SOF_DAI_FMT_IB_IF:
sspsp |= SSPSP_SCMODE(2);
inverted_frame = true; /* handled later with format */
break;
- case SOF_DAI_FMT_IB_NF:
sspsp |= SSPSP_SCMODE(2);
inverted_frame = true; /* handled later with format */
break;
- default:
trace_ssp_error("ec3");
ret = -EINVAL;
goto out;
- }
+#ifdef CLK_TYPE /* not enabled, keep the code for reference */
- /* TODO: allow topology to define SSP clock type */
- config->ssp[0].clk_id = SSP_CLK_EXT;
- /* clock source */
- switch (config->ssp[0].clk_id) {
- case SSP_CLK_AUDIO:
sscr0 |= SSCR0_ACS;
break;
- case SSP_CLK_NET_PLL:
sscr0 |= SSCR0_MOD;
break;
- case SSP_CLK_EXT:
sscr0 |= SSCR0_ECS;
break;
- case SSP_CLK_NET:
sscr0 |= SSCR0_NCS | SSCR0_MOD;
break;
- default:
trace_ssp_error("ec4");
ret = -EINVAL;
goto out;
- }
+#else
- sscr0 |= SSCR0_MOD | SSCR0_ACS;
+#endif
- /* BCLK is generated from MCLK - must be divisable */
- if (config->mclk % config->bclk) {
trace_ssp_error("ec5");
ret = -EINVAL;
goto out;
- }
- /* divisor must be within SCR range */
- mdiv = (config->mclk / config->bclk) - 1;
- if (mdiv > (SSCR0_SCR_MASK >> 8)) {
trace_ssp_error("ec6");
ret = -EINVAL;
goto out;
- }
- /* set the SCR divisor */
- sscr0 |= SSCR0_SCR(mdiv);
- /* calc frame width based on BCLK and rate - must be divisable */
- if (config->bclk % config->fclk) {
trace_ssp_error("ec7");
ret = -EINVAL;
goto out;
- }
- /* must be enouch BCLKs for data */
- bdiv = config->bclk / config->fclk;
- if (bdiv < config->sample_container_bits *
((config->rx_slot_mask + 1) / 2)) {
trace_ssp_error("ec8");
ret = -EINVAL;
goto out;
- }
- /* sample_container_bits must be <= 38 for SSP */
- if (config->sample_container_bits > 38) {
trace_ssp_error("ec9");
ret = -EINVAL;
goto out;
- }
- /* format */
- switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
- case SOF_DAI_FMT_I2S:
start_delay = config->sample_container_bits >
config->sample_valid_bits ? 1 : 0;
sscr0 |= SSCR0_FRDC(config->num_slots);
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* handle frame polarity, I2S default is falling/active low */
sspsp |= SSPSP_SFRMP(!inverted_frame);
break;
- case SOF_DAI_FMT_LEFT_J:
start_delay = 0;
sscr0 |= SSCR0_FRDC(config->num_slots);
/* LJDFD enable */
sscr2 &= ~SSCR2_LJDFD;
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* LEFT_J default is rising/active high, opposite of I2S */
sspsp |= SSPSP_SFRMP(inverted_frame);
break;
- case SOF_DAI_FMT_DSP_A:
start_delay = config->sample_container_bits >
config->sample_valid_bits ? 1 : 0;
sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots);
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* handle frame polarity, DSP_A default is rising/active high */
sspsp |= SSPSP_SFRMP(inverted_frame);
break;
- case SOF_DAI_FMT_DSP_B:
start_delay = 0;
sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots);
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* handle frame polarity, DSP_A default is rising/active high */
sspsp |= SSPSP_SFRMP(inverted_frame);
break;
- default:
trace_ssp_error("eca");
ret = -EINVAL;
goto out;
- }
- sspsp |= SSPSP_DMYSTRT(start_delay);
- sspsp |= SSPSP_SFRMWDTH(frame_len);
- data_size = config->sample_valid_bits;
- if (data_size > 16)
sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16));
- else
sscr0 |= SSCR0_DSIZE(data_size);
- mdivc = 0x00100001;
- /* bypass divider for MCLK */
- mdivr = 0x00000fff;
- trace_ssp("coe");
- ssp_write(dai, SSCR0, sscr0);
- ssp_write(dai, SSCR1, sscr1);
- ssp_write(dai, SSCR2, sscr2);
- ssp_write(dai, SSCR3, sscr3);
- ssp_write(dai, SSPSP, sspsp);
- ssp_write(dai, SSPSP2, sspsp2);
- ssp_write(dai, SSIOC, ssioc);
- ssp_write(dai, SSTO, ssto);
- ssp_write(dai, SSTSA, sstsa);
- ssp_write(dai, SSRSA, ssrsa);
- trace_value(sscr0);
- trace_value(sscr1);
- trace_value(ssto);
- trace_value(sspsp);
- trace_value(sstsa);
- trace_value(ssrsa);
- trace_value(sscr2);
- trace_value(sspsp2);
- trace_value(sscr3);
- trace_value(ssioc);
- mn_reg_write(0x0, mdivc);
- mn_reg_write(0x80, mdivr);
- mn_reg_write(0x100 + config->id * 0x8 + 0x0, i2s_m);
- mn_reg_write(0x100 + config->id * 0x8 + 0x4, i2s_n);
- ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE;
- ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE;
+out:
- spin_unlock(&ssp->lock);
- return ret;
+}
+/* Digital Audio interface formatting */ +static inline int ssp_set_loopback_mode(struct dai *dai, uint32_t lbm) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- trace_ssp("loo");
- spin_lock(&ssp->lock);
- ssp_update_bits(dai, SSCR1, SSCR1_LBM, lbm ? SSCR1_LBM : 0);
- spin_unlock(&ssp->lock);
- return 0;
+}
+/* start the SSP for either playback or capture */ +static void ssp_start(struct dai *dai, int direction) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- spin_lock(&ssp->lock);
- /* enable port */
- ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE);
- ssp->state[direction] = COMP_STATE_ACTIVE;
- trace_ssp("sta");
- /* enable DMA */
- if (direction == DAI_DIR_PLAYBACK) {
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, SSCR1_TSRE);
ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x1 << 8);
- } else {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, SSCR1_RSRE);
ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x1 << 8);
- }
- spin_unlock(&ssp->lock);
+}
+/* stop the SSP for either playback or capture */ +static void ssp_stop(struct dai *dai) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- spin_lock(&ssp->lock);
- /* stop Rx if we are not capturing */
- if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE) {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x0 << 8);
trace_ssp("Ss0");
- }
- /* stop Tx if we are not playing */
- if (ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x0 << 8);
trace_ssp("Ss1");
- }
- /* disable SSP port if no users */
- if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
trace_ssp("Ss2");
- }
- spin_unlock(&ssp->lock);
+}
+static int ssp_trigger(struct dai *dai, int cmd, int direction) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- trace_ssp("tri");
- switch (cmd) {
- case COMP_CMD_START:
if (ssp->state[direction] == COMP_STATE_PREPARE ||
ssp->state[direction] == COMP_STATE_PAUSED)
ssp_start(dai, direction);
break;
- case COMP_CMD_RELEASE:
if (ssp->state[direction] == COMP_STATE_PAUSED ||
ssp->state[direction] == COMP_STATE_PREPARE)
ssp_start(dai, direction);
break;
- case COMP_CMD_STOP:
- case COMP_CMD_PAUSE:
ssp->state[direction] = COMP_STATE_PAUSED;
ssp_stop(dai);
break;
- case COMP_CMD_RESUME:
ssp_context_restore(dai);
break;
- case COMP_CMD_SUSPEND:
ssp_context_store(dai);
break;
- default:
break;
- }
- return 0;
+}
+/* clear IRQ sources atm */ +static void ssp_irq_handler(void *data) +{
- struct dai *dai = data;
- trace_ssp("irq");
- trace_value(ssp_read(dai, SSSR));
- /* clear IRQ */
- ssp_write(dai, SSSR, ssp_read(dai, SSSR));
- platform_interrupt_clear(ssp_irq(dai), 1);
+}
+static int ssp_probe(struct dai *dai) +{
- struct ssp_pdata *ssp;
- /* allocate private data */
- ssp = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(*ssp));
- dai_set_drvdata(dai, ssp);
- spinlock_init(&ssp->lock);
- ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY;
- ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY;
- /* register our IRQ handler */
- interrupt_register(ssp_irq(dai), ssp_irq_handler, dai);
- platform_interrupt_unmask(ssp_irq(dai), 1);
- interrupt_enable(ssp_irq(dai));
- return 0;
+}
+const struct dai_ops ssp_ops = {
- .trigger = ssp_trigger,
- .set_config = ssp_set_config,
- .pm_context_store = ssp_context_store,
- .pm_context_restore = ssp_context_restore,
- .probe = ssp_probe,
- .set_loopback_mode = ssp_set_loopback_mode,
+}; diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h index c00d14c..e041121 100644 --- a/src/include/reef/ssp.h +++ b/src/include/reef/ssp.h @@ -38,6 +38,8 @@ #include <reef/trace.h> #include <reef/wait.h>
+#define BIT(x) (1 << (x))
- #define SSP_CLK_AUDIO 0 #define SSP_CLK_NET_PLL 1 #define SSP_CLK_EXT 2
@@ -55,11 +57,17 @@ #define SSTSA 0x30 #define SSRSA 0x34 #define SSTSS 0x38
+#if defined CONFIG_BAYTRAIL ||\
- defined CONFIG_CHERRYTRAIL ||\
- defined CONFIG_BROADWELL ||\
- defined CONFIG_HASWELL #define SSCR2 0x40 #define SFIFOTT 0x6C #define SSCR3 0x70 #define SSCR4 0x74 #define SSCR5 0x78
+#endif
extern const struct dai_ops ssp_ops;
@@ -112,17 +120,28 @@ extern const struct dai_ops ssp_ops; #define SSCR1_TTE (1 << 30) #define SSCR1_TTELP (1 << 31)
+#if defined CONFIG_BAYTRAIL ||\
- defined CONFIG_CHERRYTRAIL ||\
- defined CONFIG_BROADWELL ||\
- defined CONFIG_HASWELL /* SSCR2 bits */
-#define SSCR2_URUN_FIX0 (1 << 0) -#define SSCR2_URUN_FIX1 (1 << 1) -#define SSCR2_SLV_EXT_CLK_RUN_EN (1 << 2) -#define SSCR2_CLK_DEL_EN (1 << 3) -#define SSCR2_UNDRN_FIX_EN (1 << 6) -#define SSCR2_FIFO_EMPTY_FIX_EN (1 << 7) -#define SSCR2_ASRC_CNTR_EN (1 << 8) -#define SSCR2_ASRC_CNTR_CLR (1 << 9) -#define SSCR2_ASRC_FRM_CNRT_EN (1 << 10) -#define SSCR2_ASRC_INTR_MASK (1 << 11) +#define SSCR2_URUN_FIX0 BIT(0) +#define SSCR2_URUN_FIX1 BIT(1) +#define SSCR2_SLV_EXT_CLK_RUN_EN BIT(2) +#define SSCR2_CLK_DEL_EN BIT(3) +#define SSCR2_UNDRN_FIX_EN BIT(6) +#define SSCR2_FIFO_EMPTY_FIX_EN BIT(7) +#define SSCR2_ASRC_CNTR_EN BIT(8) +#define SSCR2_ASRC_CNTR_CLR BIT(9) +#define SSCR2_ASRC_FRM_CNRT_EN BIT(10) +#define SSCR2_ASRC_INTR_MASK BIT(11) +#elif defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSCR2_TURM1 BIT(1) +#define SSCR2_SDFD BIT(14) +#define SSCR2_SDPM BIT(16) +#define SSCR2_LJDFD BIT(17) +#endif
/* SSR bits */ #define SSSR_TNF (1 << 2)
@@ -143,6 +162,17 @@ extern const struct dai_ops ssp_ops; #define SSPSP_DMYSTOP(x) ((x) << 23) #define SSPSP_FSRT (1 << 25)
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSPSP_EDMYSTOP(x) ((x) << 26) +#define SSTSS 0x38 +#define SSCR2 0x40 +#define SSPSP2 0x44 +#define SSCR3 0x48 +#define SSIOC 0x4C
+#define SSP_REG_MAX SSIOC +#endif
- /* SSCR3 bits */ #define SSCR3_FRM_MST_EN (1 << 0) #define SSCR3_I2S_MODE_EN (1 << 1)
@@ -169,6 +199,22 @@ extern const struct dai_ops ssp_ops; #define SFIFOTT_TX(x) ((x) - 1) #define SFIFOTT_RX(x) (((x) - 1) << 16)
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSTSA_TSEN BIT(8) +#define SSRSA_RSEN BIT(8)
+#define SSCR3_TFL_MASK (0x0000003f) +#define SSCR3_RFL_MASK (0x00003f00) +#define SSCR3_TFT_MASK (0x003f0000) +#define SSCR3_TX(x) (((x) - 1) << 16) +#define SSCR3_RFT_MASK (0x3f000000) +#define SSCR3_RX(x) (((x) - 1) << 24)
+#define SSIOC_TXDPDEB BIT(1) +#define SSIOC_SFCR BIT(4) +#define SSIOC_SCOE BIT(5) +#endif
- /* tracing */ #define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e)
On Thu, 2018-02-08 at 15:54 +0800, Rander Wang wrote:
Now it supports I2S & TDM4 master mode and no MN devide used. For 19.2M clock, 16bit 48K stream maybe need to pack to 20bit 48K.
Signed-off-by: Rander Wang rander.wang@intel.com
Just some minor stuff that can be fixed in V2 alongside adding Keyon's sign-off.
configure.ac | 1 + src/drivers/Makefile.am | 9 +- src/drivers/apl-ssp.c | 547 ++++++++++++++++++++++++++++++++++++++++++++++++ src/include/reef/ssp.h | 66 +++++- 4 files changed, 612 insertions(+), 11 deletions(-) create mode 100644 src/drivers/apl-ssp.c
diff --git a/configure.ac b/configure.ac index 05a3bee..1c84020 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,7 @@ AM_CONDITIONAL(BUILD_APOLLOLAKE, test "$FW_NAME" = "apl") AM_CONDITIONAL(BUILD_CANNONLAKE, test "$FW_NAME" = "cnl") AM_CONDITIONAL(BUILD_BOOTLOADER, test "$FW_NAME" = "cnl") AM_CONDITIONAL(BUILD_MODULE, test "$FW_NAME" = "apl" -o "$FW_NAME" = "cnl") +AM_CONDITIONAL(BUILD_APL_SSP, test "$FW_NAME" = "apl" -o "$FW_NAME" = "cnl")
# DSP core support (Optional) AC_ARG_WITH([dsp-core], diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am index 8c507fa..53b52af 100644 --- a/src/drivers/Makefile.am +++ b/src/drivers/Makefile.am @@ -1,9 +1,16 @@ noinst_LIBRARIES = libdrivers.a
libdrivers_a_SOURCES = \
- ssp.c \ dw-dma.c
+if BUILD_APL_SSP +libdrivers_a_SOURCES += \
- apl-ssp.c
+else
- libdrivers_a_SOURCES += \
- ssp.c
+endif
libdrivers_a_CFLAGS = \ $(ARCH_CFLAGS) \ $(REEF_INCDIR) \ diff --git a/src/drivers/apl-ssp.c b/src/drivers/apl-ssp.c new file mode 100644 index 0000000..09adee2 --- /dev/null +++ b/src/drivers/apl-ssp.c @@ -0,0 +1,547 @@ +/*
- Copyright (c) 2016, Intel Corporation
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Intel Corporation nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- Author: Liam Girdwood liam.r.girdwood@linux.intel.com
Keyon Jie <yang.jie@linux.intel.com>
Rander Wang <rander.wang@linux.intel.com>
- */
+#include <errno.h> +#include <stdbool.h> +#include <reef/stream.h> +#include <reef/ssp.h> +#include <reef/alloc.h> +#include <reef/interrupt.h>
+/* tracing */ +#define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) +#define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) +#define tracev_ssp(__e) tracev_event(TRACE_CLASS_SSP, __e)
+/* save SSP context prior to entering D3 */ +static int ssp_context_store(struct dai *dai) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- ssp->sscr0 = ssp_read(dai, SSCR0);
- ssp->sscr1 = ssp_read(dai, SSCR1);
- /* FIXME: need to store sscr2,3,4,5 */
- ssp->psp = ssp_read(dai, SSPSP);
- return 0;
+}
+/* restore SSP context after leaving D3 */ +static int ssp_context_restore(struct dai *dai) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- ssp_write(dai, SSCR0, ssp->sscr0);
- ssp_write(dai, SSCR1, ssp->sscr1);
- /* FIXME: need to restore sscr2,3,4,5 */
- ssp_write(dai, SSPSP, ssp->psp);
- return 0;
+}
+/* Digital Audio interface formatting */ +static inline int ssp_set_config(struct dai *dai,
- struct sof_ipc_dai_config *config)
+{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- uint32_t sscr0;
- uint32_t sscr1;
- uint32_t sscr2;
- uint32_t sscr3;
- uint32_t sspsp;
- uint32_t sspsp2;
- uint32_t sstsa;
- uint32_t ssrsa;
- uint32_t ssto;
- uint32_t ssioc;
- uint32_t mdiv;
- uint32_t bdiv;
- uint32_t mdivc;
- uint32_t mdivr;
- uint32_t i2s_m;
- uint32_t i2s_n;
- uint32_t data_size;
- uint32_t start_delay;
- uint32_t frame_len = 0;
- bool inverted_frame = false;
- int ret = 0;
- spin_lock(&ssp->lock);
- /* is playback/capture already running */
- if (ssp->state[DAI_DIR_PLAYBACK] == COMP_STATE_ACTIVE ||
ssp->state[DAI_DIR_CAPTURE] == COMP_STATE_ACTIVE) {
trace_ssp_error("ec1");
ret = -EINVAL;
goto out;
- }
- trace_ssp("cos");
- /* reset SSP settings */
- /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */
- /*
* FIXME: MOD, ACS, NCS are not set,
* no support for network mode for now
*/
- sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM;
- /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */
- sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_RWOT | SSCR1_TRAIL;
+// sscr1 |= SSCR1_TIE | SSCR1_RIE;
Can you remove the code commented out.
Thanks
Liam
- /* sscr2 dynamic setting is LJDFD */
- sscr2 = SSCR2_SDFD | SSCR2_TURM1;
- /* sscr3 dynamic settings are TFT, RFT */
- sscr3 = 0;
- /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */
+// sspsp = SSPSP_ETDS; /* make sure SDO line is tri-stated when inactive */
- sspsp = 0;
- ssp->config = *config;
- ssp->params = config->ssp[0];
- /* sspsp2 no dynamic setting */
- sspsp2 = 0x0;
- /* ssioc dynamic setting is SFCR */
- ssioc = SSIOC_SCOE;
- /* i2s_m M divider setting, default 1 */
- i2s_m = 0x1;
- /* i2s_n N divider setting, default 1 */
- i2s_n = 0x1;
- /* ssto no dynamic setting */
- ssto = 0x0;
- /* sstsa dynamic setting is TTSA, default 2 slots */
- sstsa = config->tx_slot_mask;
- /* ssrsa dynamic setting is RTSA, default 2 slots */
- ssrsa = config->rx_slot_mask;
- /* clock masters */
- sscr1 &= ~SSCR1_SFRMDIR;
- trace_value(config->format);
- switch (config->format & SOF_DAI_FMT_MASTER_MASK) {
- case SOF_DAI_FMT_CBM_CFM:
sscr0 |= SSCR0_ECS; /* external clock used */
sscr1 |= SSCR1_SCLKDIR;
/*
* FIXME: does SSRC1.SCFR need to be set
* when codec is master ?
*/
break;
- case SOF_DAI_FMT_CBS_CFS:
sscr1 |= SSCR1_SCFR;
ssioc |= SSIOC_SFCR;
break;
- case SOF_DAI_FMT_CBM_CFS:
sscr0 |= SSCR0_ECS; /* external clock used */
sscr1 |= SSCR1_SCLKDIR;
/*
* FIXME: does SSRC1.SCFR need to be set
* when codec is master ?
*/
/* FIXME: this mode has not been tested */
break;
- case SOF_DAI_FMT_CBS_CFM:
sscr1 |= SSCR1_SCFR;
/* FIXME: this mode has not been tested */
break;
- default:
trace_ssp_error("ec2");
ret = -EINVAL;
goto out;
- }
- /* clock signal polarity */
- switch (config->format & SOF_DAI_FMT_INV_MASK) {
- case SOF_DAI_FMT_NB_NF:
break;
- case SOF_DAI_FMT_NB_IF:
break;
- case SOF_DAI_FMT_IB_IF:
sspsp |= SSPSP_SCMODE(2);
inverted_frame = true; /* handled later with format */
break;
- case SOF_DAI_FMT_IB_NF:
sspsp |= SSPSP_SCMODE(2);
inverted_frame = true; /* handled later with format */
break;
- default:
trace_ssp_error("ec3");
ret = -EINVAL;
goto out;
- }
+#ifdef CLK_TYPE /* not enabled, keep the code for reference */
- /* TODO: allow topology to define SSP clock type */
- config->ssp[0].clk_id = SSP_CLK_EXT;
- /* clock source */
- switch (config->ssp[0].clk_id) {
- case SSP_CLK_AUDIO:
sscr0 |= SSCR0_ACS;
break;
- case SSP_CLK_NET_PLL:
sscr0 |= SSCR0_MOD;
break;
- case SSP_CLK_EXT:
sscr0 |= SSCR0_ECS;
break;
- case SSP_CLK_NET:
sscr0 |= SSCR0_NCS | SSCR0_MOD;
break;
- default:
trace_ssp_error("ec4");
ret = -EINVAL;
goto out;
- }
+#else
- sscr0 |= SSCR0_MOD | SSCR0_ACS;
+#endif
- /* BCLK is generated from MCLK - must be divisable */
- if (config->mclk % config->bclk) {
trace_ssp_error("ec5");
ret = -EINVAL;
goto out;
- }
- /* divisor must be within SCR range */
- mdiv = (config->mclk / config->bclk) - 1;
- if (mdiv > (SSCR0_SCR_MASK >> 8)) {
trace_ssp_error("ec6");
ret = -EINVAL;
goto out;
- }
- /* set the SCR divisor */
- sscr0 |= SSCR0_SCR(mdiv);
- /* calc frame width based on BCLK and rate - must be divisable */
- if (config->bclk % config->fclk) {
trace_ssp_error("ec7");
ret = -EINVAL;
goto out;
- }
- /* must be enouch BCLKs for data */
- bdiv = config->bclk / config->fclk;
- if (bdiv < config->sample_container_bits *
((config->rx_slot_mask + 1) / 2)) {
trace_ssp_error("ec8");
ret = -EINVAL;
goto out;
- }
- /* sample_container_bits must be <= 38 for SSP */
- if (config->sample_container_bits > 38) {
trace_ssp_error("ec9");
ret = -EINVAL;
goto out;
- }
- /* format */
- switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
- case SOF_DAI_FMT_I2S:
start_delay = config->sample_container_bits >
config->sample_valid_bits ? 1 : 0;
sscr0 |= SSCR0_FRDC(config->num_slots);
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* handle frame polarity, I2S default is falling/active low */
sspsp |= SSPSP_SFRMP(!inverted_frame);
break;
- case SOF_DAI_FMT_LEFT_J:
start_delay = 0;
sscr0 |= SSCR0_FRDC(config->num_slots);
/* LJDFD enable */
sscr2 &= ~SSCR2_LJDFD;
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* LEFT_J default is rising/active high, opposite of I2S */
sspsp |= SSPSP_SFRMP(inverted_frame);
break;
- case SOF_DAI_FMT_DSP_A:
start_delay = config->sample_container_bits >
config->sample_valid_bits ? 1 : 0;
sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots);
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* handle frame polarity, DSP_A default is rising/active high */
sspsp |= SSPSP_SFRMP(inverted_frame);
break;
- case SOF_DAI_FMT_DSP_B:
start_delay = 0;
sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->num_slots);
/* set asserted frame length */
frame_len = config->sample_container_bits;
/* handle frame polarity, DSP_A default is rising/active high */
sspsp |= SSPSP_SFRMP(inverted_frame);
break;
- default:
trace_ssp_error("eca");
ret = -EINVAL;
goto out;
- }
- sspsp |= SSPSP_DMYSTRT(start_delay);
- sspsp |= SSPSP_SFRMWDTH(frame_len);
- data_size = config->sample_valid_bits;
- if (data_size > 16)
sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16));
- else
sscr0 |= SSCR0_DSIZE(data_size);
- mdivc = 0x00100001;
- /* bypass divider for MCLK */
- mdivr = 0x00000fff;
- trace_ssp("coe");
- ssp_write(dai, SSCR0, sscr0);
- ssp_write(dai, SSCR1, sscr1);
- ssp_write(dai, SSCR2, sscr2);
- ssp_write(dai, SSCR3, sscr3);
- ssp_write(dai, SSPSP, sspsp);
- ssp_write(dai, SSPSP2, sspsp2);
- ssp_write(dai, SSIOC, ssioc);
- ssp_write(dai, SSTO, ssto);
- ssp_write(dai, SSTSA, sstsa);
- ssp_write(dai, SSRSA, ssrsa);
- trace_value(sscr0);
- trace_value(sscr1);
- trace_value(ssto);
- trace_value(sspsp);
- trace_value(sstsa);
- trace_value(ssrsa);
- trace_value(sscr2);
- trace_value(sspsp2);
- trace_value(sscr3);
- trace_value(ssioc);
- mn_reg_write(0x0, mdivc);
- mn_reg_write(0x80, mdivr);
- mn_reg_write(0x100 + config->id * 0x8 + 0x0, i2s_m);
- mn_reg_write(0x100 + config->id * 0x8 + 0x4, i2s_n);
- ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE;
- ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE;
+out:
- spin_unlock(&ssp->lock);
- return ret;
+}
+/* Digital Audio interface formatting */ +static inline int ssp_set_loopback_mode(struct dai *dai, uint32_t lbm) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- trace_ssp("loo");
- spin_lock(&ssp->lock);
- ssp_update_bits(dai, SSCR1, SSCR1_LBM, lbm ? SSCR1_LBM : 0);
- spin_unlock(&ssp->lock);
- return 0;
+}
+/* start the SSP for either playback or capture */ +static void ssp_start(struct dai *dai, int direction) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- spin_lock(&ssp->lock);
- /* enable port */
- ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE);
- ssp->state[direction] = COMP_STATE_ACTIVE;
- trace_ssp("sta");
- /* enable DMA */
- if (direction == DAI_DIR_PLAYBACK) {
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, SSCR1_TSRE);
ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x1 << 8);
- } else {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, SSCR1_RSRE);
ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x1 << 8);
- }
- spin_unlock(&ssp->lock);
+}
+/* stop the SSP for either playback or capture */ +static void ssp_stop(struct dai *dai) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- spin_lock(&ssp->lock);
- /* stop Rx if we are not capturing */
- if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE) {
ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x0 << 8);
trace_ssp("Ss0");
- }
- /* stop Tx if we are not playing */
- if (ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x0 << 8);
trace_ssp("Ss1");
- }
- /* disable SSP port if no users */
- if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE &&
ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) {
ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0);
ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE;
ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE;
trace_ssp("Ss2");
- }
- spin_unlock(&ssp->lock);
+}
+static int ssp_trigger(struct dai *dai, int cmd, int direction) +{
- struct ssp_pdata *ssp = dai_get_drvdata(dai);
- trace_ssp("tri");
- switch (cmd) {
- case COMP_CMD_START:
if (ssp->state[direction] == COMP_STATE_PREPARE ||
ssp->state[direction] == COMP_STATE_PAUSED)
ssp_start(dai, direction);
break;
- case COMP_CMD_RELEASE:
if (ssp->state[direction] == COMP_STATE_PAUSED ||
ssp->state[direction] == COMP_STATE_PREPARE)
ssp_start(dai, direction);
break;
- case COMP_CMD_STOP:
- case COMP_CMD_PAUSE:
ssp->state[direction] = COMP_STATE_PAUSED;
ssp_stop(dai);
break;
- case COMP_CMD_RESUME:
ssp_context_restore(dai);
break;
- case COMP_CMD_SUSPEND:
ssp_context_store(dai);
break;
- default:
break;
- }
- return 0;
+}
+/* clear IRQ sources atm */ +static void ssp_irq_handler(void *data) +{
- struct dai *dai = data;
- trace_ssp("irq");
- trace_value(ssp_read(dai, SSSR));
- /* clear IRQ */
- ssp_write(dai, SSSR, ssp_read(dai, SSSR));
- platform_interrupt_clear(ssp_irq(dai), 1);
+}
+static int ssp_probe(struct dai *dai) +{
- struct ssp_pdata *ssp;
- /* allocate private data */
- ssp = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(*ssp));
- dai_set_drvdata(dai, ssp);
- spinlock_init(&ssp->lock);
- ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY;
- ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY;
- /* register our IRQ handler */
- interrupt_register(ssp_irq(dai), ssp_irq_handler, dai);
- platform_interrupt_unmask(ssp_irq(dai), 1);
- interrupt_enable(ssp_irq(dai));
- return 0;
+}
+const struct dai_ops ssp_ops = {
- .trigger = ssp_trigger,
- .set_config = ssp_set_config,
- .pm_context_store = ssp_context_store,
- .pm_context_restore = ssp_context_restore,
- .probe = ssp_probe,
- .set_loopback_mode = ssp_set_loopback_mode,
+}; diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h index c00d14c..e041121 100644 --- a/src/include/reef/ssp.h +++ b/src/include/reef/ssp.h @@ -38,6 +38,8 @@ #include <reef/trace.h> #include <reef/wait.h>
+#define BIT(x) (1 << (x))
#define SSP_CLK_AUDIO 0 #define SSP_CLK_NET_PLL 1 #define SSP_CLK_EXT 2 @@ -55,11 +57,17 @@ #define SSTSA 0x30 #define SSRSA 0x34 #define SSTSS 0x38
+#if defined CONFIG_BAYTRAIL ||\
- defined CONFIG_CHERRYTRAIL ||\
- defined CONFIG_BROADWELL ||\
- defined CONFIG_HASWELL
#define SSCR2 0x40 #define SFIFOTT 0x6C #define SSCR3 0x70 #define SSCR4 0x74 #define SSCR5 0x78 +#endif
extern const struct dai_ops ssp_ops;
@@ -112,17 +120,28 @@ extern const struct dai_ops ssp_ops; #define SSCR1_TTE (1 << 30) #define SSCR1_TTELP (1 << 31)
+#if defined CONFIG_BAYTRAIL ||\
- defined CONFIG_CHERRYTRAIL ||\
- defined CONFIG_BROADWELL ||\
- defined CONFIG_HASWELL
/* SSCR2 bits */ -#define SSCR2_URUN_FIX0 (1 << 0) -#define SSCR2_URUN_FIX1 (1 << 1) -#define SSCR2_SLV_EXT_CLK_RUN_EN (1 << 2) -#define SSCR2_CLK_DEL_EN (1 << 3) -#define SSCR2_UNDRN_FIX_EN (1 << 6) -#define SSCR2_FIFO_EMPTY_FIX_EN (1 << 7) -#define SSCR2_ASRC_CNTR_EN (1 << 8) -#define SSCR2_ASRC_CNTR_CLR (1 << 9) -#define SSCR2_ASRC_FRM_CNRT_EN (1 << 10) -#define SSCR2_ASRC_INTR_MASK (1 << 11) +#define SSCR2_URUN_FIX0 BIT(0) +#define SSCR2_URUN_FIX1 BIT(1) +#define SSCR2_SLV_EXT_CLK_RUN_EN BIT(2) +#define SSCR2_CLK_DEL_EN BIT(3) +#define SSCR2_UNDRN_FIX_EN BIT(6) +#define SSCR2_FIFO_EMPTY_FIX_EN BIT(7) +#define SSCR2_ASRC_CNTR_EN BIT(8) +#define SSCR2_ASRC_CNTR_CLR BIT(9) +#define SSCR2_ASRC_FRM_CNRT_EN BIT(10) +#define SSCR2_ASRC_INTR_MASK BIT(11) +#elif defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSCR2_TURM1 BIT(1) +#define SSCR2_SDFD BIT(14) +#define SSCR2_SDPM BIT(16) +#define SSCR2_LJDFD BIT(17) +#endif
/* SSR bits */ #define SSSR_TNF (1 << 2) @@ -143,6 +162,17 @@ extern const struct dai_ops ssp_ops; #define SSPSP_DMYSTOP(x) ((x) << 23) #define SSPSP_FSRT (1 << 25)
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSPSP_EDMYSTOP(x) ((x) << 26) +#define SSTSS 0x38 +#define SSCR2 0x40 +#define SSPSP2 0x44 +#define SSCR3 0x48 +#define SSIOC 0x4C
+#define SSP_REG_MAX SSIOC +#endif
/* SSCR3 bits */ #define SSCR3_FRM_MST_EN (1 << 0) #define SSCR3_I2S_MODE_EN (1 << 1) @@ -169,6 +199,22 @@ extern const struct dai_ops ssp_ops; #define SFIFOTT_TX(x) ((x) - 1) #define SFIFOTT_RX(x) (((x) - 1) << 16)
+#if defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE +#define SSTSA_TSEN BIT(8) +#define SSRSA_RSEN BIT(8)
+#define SSCR3_TFL_MASK (0x0000003f) +#define SSCR3_RFL_MASK (0x00003f00) +#define SSCR3_TFT_MASK (0x003f0000) +#define SSCR3_TX(x) (((x) - 1) << 16) +#define SSCR3_RFT_MASK (0x3f000000) +#define SSCR3_RX(x) (((x) - 1) << 24)
+#define SSIOC_TXDPDEB BIT(1) +#define SSIOC_SFCR BIT(4) +#define SSIOC_SCOE BIT(5) +#endif
/* tracing */ #define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e)
participants (3)
-
Keyon Jie
-
Liam Girdwood
-
Rander Wang