Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2016, Intel Corporation |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions are met: |
| 7 | * * Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * * Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * * Neither the name of the Intel Corporation nor the |
| 13 | * names of its contributors may be used to endorse or promote products |
| 14 | * derived from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 26 | * POSSIBILITY OF SUCH DAMAGE. |
| 27 | * |
| 28 | * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> |
| 29 | * Keyon Jie <yang.jie@linux.intel.com> |
| 30 | * Rander Wang <rander.wang@linux.intel.com> |
| 31 | */ |
| 32 | |
| 33 | #include <errno.h> |
| 34 | #include <stdbool.h> |
Pierre-Louis Bossart | 81708a5 | 2018-04-04 18:46:50 -0500 | [diff] [blame] | 35 | #include <sof/stream.h> |
| 36 | #include <sof/ssp.h> |
| 37 | #include <sof/alloc.h> |
| 38 | #include <sof/interrupt.h> |
Wu Zhigang | e3b10cd | 2018-05-18 14:37:57 +0800 | [diff] [blame] | 39 | #include <sof/math/numbers.h> |
Keyon Jie | 1532dfd | 2018-02-09 19:39:42 +0800 | [diff] [blame] | 40 | #include <config.h> |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 41 | |
| 42 | /* tracing */ |
| 43 | #define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) |
| 44 | #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) |
| 45 | #define tracev_ssp(__e) tracev_event(TRACE_CLASS_SSP, __e) |
| 46 | |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 47 | #define F_19200_kHz 19200000 |
| 48 | #define F_24000_kHz 24000000 |
| 49 | #define F_24576_kHz 24576000 |
| 50 | |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 51 | /* FIXME: move this to a helper and optimize */ |
| 52 | static int hweight_32(uint32_t mask) |
| 53 | { |
| 54 | int i; |
| 55 | int count = 0; |
| 56 | |
| 57 | for (i = 0; i < 32; i++) { |
| 58 | count += mask & 1; |
| 59 | mask >>= 1; |
| 60 | } |
| 61 | return count; |
| 62 | } |
| 63 | |
Tomasz Lauda | dcc6149 | 2018-07-16 13:55:35 +0200 | [diff] [blame] | 64 | /* empty SSP receive FIFO */ |
| 65 | static void ssp_empty_rx_fifo(struct dai *dai) |
| 66 | { |
| 67 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 68 | uint32_t sssr; |
| 69 | uint32_t entries; |
| 70 | uint32_t i; |
| 71 | |
| 72 | spin_lock(&ssp->lock); |
| 73 | |
| 74 | sssr = ssp_read(dai, SSSR); |
| 75 | |
| 76 | /* clear interrupt */ |
| 77 | if (sssr & SSSR_ROR) |
| 78 | ssp_write(dai, SSSR, sssr); |
| 79 | |
| 80 | /* empty fifo */ |
| 81 | if (sssr & SSSR_RNE) { |
| 82 | entries = (ssp_read(dai, SSCR3) & SSCR3_RFL_MASK) >> 8; |
| 83 | for (i = 0; i < entries + 1; i++) |
| 84 | ssp_read(dai, SSDR); |
| 85 | } |
| 86 | |
| 87 | spin_unlock(&ssp->lock); |
| 88 | } |
| 89 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 90 | /* save SSP context prior to entering D3 */ |
| 91 | static int ssp_context_store(struct dai *dai) |
| 92 | { |
| 93 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 94 | |
| 95 | ssp->sscr0 = ssp_read(dai, SSCR0); |
| 96 | ssp->sscr1 = ssp_read(dai, SSCR1); |
| 97 | |
| 98 | /* FIXME: need to store sscr2,3,4,5 */ |
| 99 | ssp->psp = ssp_read(dai, SSPSP); |
| 100 | |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | /* restore SSP context after leaving D3 */ |
| 105 | static int ssp_context_restore(struct dai *dai) |
| 106 | { |
| 107 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 108 | |
| 109 | ssp_write(dai, SSCR0, ssp->sscr0); |
| 110 | ssp_write(dai, SSCR1, ssp->sscr1); |
| 111 | /* FIXME: need to restore sscr2,3,4,5 */ |
| 112 | ssp_write(dai, SSPSP, ssp->psp); |
| 113 | |
| 114 | return 0; |
| 115 | } |
| 116 | |
| 117 | /* Digital Audio interface formatting */ |
| 118 | static inline int ssp_set_config(struct dai *dai, |
| 119 | struct sof_ipc_dai_config *config) |
| 120 | { |
| 121 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 122 | uint32_t sscr0; |
| 123 | uint32_t sscr1; |
| 124 | uint32_t sscr2; |
| 125 | uint32_t sscr3; |
| 126 | uint32_t sspsp; |
| 127 | uint32_t sspsp2; |
| 128 | uint32_t sstsa; |
| 129 | uint32_t ssrsa; |
| 130 | uint32_t ssto; |
| 131 | uint32_t ssioc; |
| 132 | uint32_t mdiv; |
| 133 | uint32_t bdiv; |
| 134 | uint32_t mdivc; |
| 135 | uint32_t mdivr; |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 136 | uint32_t mdivr_val; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 137 | uint32_t i2s_m; |
| 138 | uint32_t i2s_n; |
| 139 | uint32_t data_size; |
Tomasz Lauda | 4c1d449 | 2018-03-15 17:05:44 +0100 | [diff] [blame] | 140 | uint32_t frame_end_padding; |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 141 | uint32_t slot_end_padding; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 142 | uint32_t frame_len = 0; |
Keyon Jie | a92debe | 2018-03-09 20:54:52 +0800 | [diff] [blame] | 143 | uint32_t bdiv_min; |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 144 | uint32_t tft; |
| 145 | uint32_t rft; |
| 146 | uint32_t active_tx_slots = 2; |
| 147 | uint32_t active_rx_slots = 2; |
Wu Zhigang | e3b10cd | 2018-05-18 14:37:57 +0800 | [diff] [blame] | 148 | uint32_t sample_width = 2; |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 149 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 150 | bool inverted_frame = false; |
Bartosz Kokoszko | effa56f | 2018-08-01 11:53:54 +0200 | [diff] [blame] | 151 | bool cfs = false; |
Bartosz Kokoszko | 10fcb20 | 2018-08-20 15:28:36 +0200 | [diff] [blame] | 152 | bool start_delay = false; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 153 | int ret = 0; |
| 154 | |
| 155 | spin_lock(&ssp->lock); |
| 156 | |
| 157 | /* is playback/capture already running */ |
| 158 | if (ssp->state[DAI_DIR_PLAYBACK] == COMP_STATE_ACTIVE || |
| 159 | ssp->state[DAI_DIR_CAPTURE] == COMP_STATE_ACTIVE) { |
| 160 | trace_ssp_error("ec1"); |
| 161 | ret = -EINVAL; |
| 162 | goto out; |
| 163 | } |
| 164 | |
| 165 | trace_ssp("cos"); |
Seppo Ingalsuo | 48a6142 | 2018-05-28 18:21:32 +0300 | [diff] [blame] | 166 | trace_value(config->format); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 167 | |
| 168 | /* reset SSP settings */ |
| 169 | /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */ |
| 170 | /* |
| 171 | * FIXME: MOD, ACS, NCS are not set, |
| 172 | * no support for network mode for now |
| 173 | */ |
| 174 | sscr0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM; |
| 175 | |
| 176 | /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */ |
Tomasz Lauda | 4c1d449 | 2018-03-15 17:05:44 +0100 | [diff] [blame] | 177 | sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL | SSCR1_RSRE | SSCR1_TSRE; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 178 | |
| 179 | /* sscr2 dynamic setting is LJDFD */ |
| 180 | sscr2 = SSCR2_SDFD | SSCR2_TURM1; |
| 181 | |
| 182 | /* sscr3 dynamic settings are TFT, RFT */ |
Tomasz Lauda | ecf43dd | 2018-03-16 11:49:03 +0100 | [diff] [blame] | 183 | sscr3 = 0; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 184 | |
| 185 | /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */ |
| 186 | sspsp = 0; |
| 187 | |
| 188 | ssp->config = *config; |
Pierre-Louis Bossart | fbcbe58 | 2018-04-04 20:43:15 -0500 | [diff] [blame] | 189 | ssp->params = config->ssp; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 190 | |
| 191 | /* sspsp2 no dynamic setting */ |
| 192 | sspsp2 = 0x0; |
| 193 | |
| 194 | /* ssioc dynamic setting is SFCR */ |
| 195 | ssioc = SSIOC_SCOE; |
| 196 | |
| 197 | /* i2s_m M divider setting, default 1 */ |
| 198 | i2s_m = 0x1; |
| 199 | |
| 200 | /* i2s_n N divider setting, default 1 */ |
| 201 | i2s_n = 0x1; |
| 202 | |
| 203 | /* ssto no dynamic setting */ |
| 204 | ssto = 0x0; |
| 205 | |
| 206 | /* sstsa dynamic setting is TTSA, default 2 slots */ |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 207 | sstsa = config->ssp.tx_slots; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 208 | |
| 209 | /* ssrsa dynamic setting is RTSA, default 2 slots */ |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 210 | ssrsa = config->ssp.rx_slots; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 211 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 212 | trace_value(config->format); |
| 213 | switch (config->format & SOF_DAI_FMT_MASTER_MASK) { |
| 214 | case SOF_DAI_FMT_CBM_CFM: |
Slawomir Blauciak | 6a6cdf7 | 2018-07-25 09:56:58 +0200 | [diff] [blame] | 215 | sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 216 | break; |
| 217 | case SOF_DAI_FMT_CBS_CFS: |
| 218 | sscr1 |= SSCR1_SCFR; |
Bartosz Kokoszko | effa56f | 2018-08-01 11:53:54 +0200 | [diff] [blame] | 219 | cfs = true; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 220 | break; |
| 221 | case SOF_DAI_FMT_CBM_CFS: |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 222 | sscr1 |= SSCR1_SCLKDIR; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 223 | /* FIXME: this mode has not been tested */ |
Bartosz Kokoszko | effa56f | 2018-08-01 11:53:54 +0200 | [diff] [blame] | 224 | |
| 225 | cfs = true; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 226 | break; |
| 227 | case SOF_DAI_FMT_CBS_CFM: |
Slawomir Blauciak | 6a6cdf7 | 2018-07-25 09:56:58 +0200 | [diff] [blame] | 228 | sscr1 |= SSCR1_SCFR | SSCR1_SFRMDIR; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 229 | /* FIXME: this mode has not been tested */ |
| 230 | break; |
| 231 | default: |
| 232 | trace_ssp_error("ec2"); |
| 233 | ret = -EINVAL; |
| 234 | goto out; |
| 235 | } |
| 236 | |
| 237 | /* clock signal polarity */ |
| 238 | switch (config->format & SOF_DAI_FMT_INV_MASK) { |
| 239 | case SOF_DAI_FMT_NB_NF: |
| 240 | break; |
| 241 | case SOF_DAI_FMT_NB_IF: |
Pierre-Louis Bossart | 26c291b | 2018-03-09 09:24:21 -0600 | [diff] [blame] | 242 | inverted_frame = true; /* handled later with format */ |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 243 | break; |
| 244 | case SOF_DAI_FMT_IB_IF: |
| 245 | sspsp |= SSPSP_SCMODE(2); |
| 246 | inverted_frame = true; /* handled later with format */ |
| 247 | break; |
| 248 | case SOF_DAI_FMT_IB_NF: |
| 249 | sspsp |= SSPSP_SCMODE(2); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 250 | break; |
| 251 | default: |
| 252 | trace_ssp_error("ec3"); |
| 253 | ret = -EINVAL; |
| 254 | goto out; |
| 255 | } |
| 256 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 257 | sscr0 |= SSCR0_MOD | SSCR0_ACS; |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 258 | |
Pierre-Louis Bossart | 383d89d | 2018-07-31 19:17:30 -0500 | [diff] [blame] | 259 | mdivc = mn_reg_read(0x0); |
| 260 | mdivc |= 0x1; |
| 261 | |
Bartosz Kokoszko | effa56f | 2018-08-01 11:53:54 +0200 | [diff] [blame] | 262 | /* Additional hardware settings */ |
| 263 | |
| 264 | /* Receiver Time-out Interrupt Disabled/Enabled */ |
| 265 | sscr1 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_TINTE) ? |
| 266 | SSCR1_TINTE : 0; |
| 267 | |
| 268 | /* Peripheral Trailing Byte Interrupts Disable/Enable */ |
| 269 | sscr1 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PINTE) ? |
| 270 | SSCR1_PINTE : 0; |
| 271 | |
| 272 | /* Transmit data are driven at the same/opposite clock edge specified |
| 273 | * in SSPSP.SCMODE[1:0] |
| 274 | */ |
| 275 | sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_SMTATF) ? |
| 276 | SSCR2_SMTATF : 0; |
| 277 | |
| 278 | /* Receive data are sampled at the same/opposite clock edge specified |
| 279 | * in SSPSP.SCMODE[1:0] |
| 280 | */ |
| 281 | sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_MMRATF) ? |
| 282 | SSCR2_MMRATF : 0; |
| 283 | |
| 284 | /* Enable/disable the fix for PSP slave mode TXD wait for frame |
| 285 | * de-assertion before starting the second channel |
| 286 | */ |
| 287 | sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD) ? |
| 288 | SSCR2_PSPSTWFDFD : 0; |
| 289 | |
| 290 | /* Enable/disable the fix for PSP master mode FSRT with dummy stop & |
| 291 | * frame end padding capability |
| 292 | */ |
| 293 | sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD) ? |
| 294 | SSCR2_PSPSRWFDFD : 0; |
| 295 | |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 296 | #ifdef CONFIG_CANNONLAKE |
| 297 | if (!config->ssp.mclk_rate || config->ssp.mclk_rate > F_24000_kHz) { |
| 298 | trace_ssp_error("eci"); |
| 299 | ret = -EINVAL; |
| 300 | goto out; |
| 301 | } |
| 302 | if (!config->ssp.bclk_rate || |
| 303 | config->ssp.bclk_rate > config->ssp.mclk_rate) { |
| 304 | trace_ssp_error("ecj"); |
| 305 | ret = -EINVAL; |
| 306 | goto out; |
| 307 | } |
| 308 | |
| 309 | if (F_24000_kHz % config->ssp.mclk_rate == 0) { |
| 310 | mdivr_val = F_24000_kHz / config->ssp.mclk_rate; |
| 311 | } else { |
| 312 | trace_ssp_error("eck"); |
| 313 | ret = -EINVAL; |
| 314 | goto out; |
| 315 | } |
| 316 | |
| 317 | if (F_24000_kHz % config->ssp.bclk_rate == 0) { |
| 318 | mdiv = F_24000_kHz / config->ssp.bclk_rate; |
| 319 | } else { |
| 320 | trace_ssp_error("ecl"); |
| 321 | ret = -EINVAL; |
| 322 | goto out; |
| 323 | } |
| 324 | #else |
| 325 | if (!config->ssp.mclk_rate || config->ssp.mclk_rate > F_24576_kHz) { |
| 326 | trace_ssp_error("eci"); |
| 327 | ret = -EINVAL; |
| 328 | goto out; |
| 329 | } |
| 330 | if (!config->ssp.bclk_rate || |
| 331 | config->ssp.bclk_rate > config->ssp.mclk_rate) { |
| 332 | trace_ssp_error("ecj"); |
| 333 | ret = -EINVAL; |
| 334 | goto out; |
| 335 | } |
| 336 | if (F_24576_kHz % config->ssp.mclk_rate == 0) { |
| 337 | /* select Audio Cardinal clock for MCLK */ |
| 338 | mdivc |= MCDSS(1); |
| 339 | mdivr_val = F_24576_kHz / config->ssp.mclk_rate; |
| 340 | } else if (config->ssp.mclk_rate <= F_19200_kHz && |
| 341 | F_19200_kHz % config->ssp.mclk_rate == 0) { |
| 342 | mdivr_val = F_19200_kHz / config->ssp.mclk_rate; |
| 343 | } else { |
| 344 | trace_ssp_error("eck"); |
| 345 | ret = -EINVAL; |
| 346 | goto out; |
| 347 | } |
| 348 | |
| 349 | if (F_24576_kHz % config->ssp.bclk_rate == 0) { |
| 350 | /* select Audio Cardinal clock for M/N dividers */ |
| 351 | mdivc |= MNDSS(1); |
| 352 | mdiv = F_24576_kHz / config->ssp.bclk_rate; |
| 353 | /* select M/N output for bclk */ |
| 354 | sscr0 |= SSCR0_ECS; |
| 355 | } else if (F_19200_kHz % config->ssp.bclk_rate == 0) { |
| 356 | mdiv = F_19200_kHz / config->ssp.bclk_rate; |
| 357 | } else { |
| 358 | trace_ssp_error("ecl"); |
| 359 | ret = -EINVAL; |
| 360 | goto out; |
| 361 | } |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 362 | #endif |
| 363 | |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 364 | switch (mdivr_val) { |
| 365 | case 1: |
| 366 | mdivr = 0x00000fff; /* bypass divider for MCLK */ |
| 367 | break; |
| 368 | case 2: |
| 369 | mdivr = 0x0; /* 1/2 */ |
| 370 | break; |
| 371 | case 4: |
| 372 | mdivr = 0x2; /* 1/4 */ |
| 373 | break; |
| 374 | case 8: |
| 375 | mdivr = 0x6; /* 1/8 */ |
| 376 | break; |
| 377 | default: |
| 378 | trace_ssp_error("ecm"); |
| 379 | ret = -EINVAL; |
| 380 | goto out; |
| 381 | } |
| 382 | |
| 383 | if (config->ssp.mclk_id > 1) { |
| 384 | trace_ssp_error("ecn"); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 385 | ret = -EINVAL; |
| 386 | goto out; |
| 387 | } |
| 388 | |
| 389 | /* divisor must be within SCR range */ |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 390 | mdiv -= 1; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 391 | if (mdiv > (SSCR0_SCR_MASK >> 8)) { |
| 392 | trace_ssp_error("ec6"); |
| 393 | ret = -EINVAL; |
| 394 | goto out; |
| 395 | } |
| 396 | |
| 397 | /* set the SCR divisor */ |
| 398 | sscr0 |= SSCR0_SCR(mdiv); |
| 399 | |
| 400 | /* calc frame width based on BCLK and rate - must be divisable */ |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 401 | if (config->ssp.bclk_rate % config->ssp.fsync_rate) { |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 402 | trace_ssp_error("ec7"); |
| 403 | ret = -EINVAL; |
| 404 | goto out; |
| 405 | } |
| 406 | |
Tomasz Lauda | 4c1d449 | 2018-03-15 17:05:44 +0100 | [diff] [blame] | 407 | /* must be enough BCLKs for data */ |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 408 | bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate; |
| 409 | if (bdiv < config->ssp.tdm_slot_width * config->ssp.tdm_slots) { |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 410 | trace_ssp_error("ec8"); |
| 411 | ret = -EINVAL; |
| 412 | goto out; |
| 413 | } |
| 414 | |
Pierre-Louis Bossart | 7579429 | 2018-04-04 20:43:14 -0500 | [diff] [blame] | 415 | /* tdm_slot_width must be <= 38 for SSP */ |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 416 | if (config->ssp.tdm_slot_width > 38) { |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 417 | trace_ssp_error("ec9"); |
| 418 | ret = -EINVAL; |
| 419 | goto out; |
| 420 | } |
| 421 | |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 422 | bdiv_min = config->ssp.tdm_slots * config->ssp.sample_valid_bits; |
| 423 | if (bdiv < bdiv_min) { |
| 424 | trace_ssp_error("ecc"); |
| 425 | ret = -EINVAL; |
| 426 | goto out; |
| 427 | } |
| 428 | |
| 429 | frame_end_padding = bdiv - bdiv_min; |
| 430 | if (frame_end_padding > SSPSP2_FEP_MASK) { |
| 431 | trace_ssp_error("ecd"); |
| 432 | ret = -EINVAL; |
| 433 | goto out; |
| 434 | } |
| 435 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 436 | /* format */ |
| 437 | switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { |
| 438 | case SOF_DAI_FMT_I2S: |
| 439 | |
Bartosz Kokoszko | 10fcb20 | 2018-08-20 15:28:36 +0200 | [diff] [blame] | 440 | start_delay = true; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 441 | |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 442 | sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 443 | |
Keyon Jie | a92debe | 2018-03-09 20:54:52 +0800 | [diff] [blame] | 444 | if (bdiv % 2) { |
| 445 | trace_ssp_error("eca"); |
| 446 | ret = -EINVAL; |
| 447 | goto out; |
| 448 | } |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 449 | |
Keyon Jie | a92debe | 2018-03-09 20:54:52 +0800 | [diff] [blame] | 450 | /* set asserted frame length to half frame length */ |
| 451 | frame_len = bdiv / 2; |
| 452 | |
| 453 | /* |
| 454 | * handle frame polarity, I2S default is falling/active low, |
| 455 | * non-inverted(inverted_frame=0) -- active low(SFRMP=0), |
| 456 | * inverted(inverted_frame=1) -- rising/active high(SFRMP=1), |
| 457 | * so, we should set SFRMP to inverted_frame. |
| 458 | */ |
| 459 | sspsp |= SSPSP_SFRMP(inverted_frame); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 460 | |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 461 | /* |
| 462 | * for I2S/LEFT_J, the padding has to happen at the end |
| 463 | * of each slot |
| 464 | */ |
| 465 | if (frame_end_padding % 2) { |
| 466 | trace_ssp_error("ece"); |
| 467 | ret = -EINVAL; |
| 468 | goto out; |
| 469 | } |
| 470 | |
| 471 | slot_end_padding = frame_end_padding / 2; |
| 472 | |
| 473 | if (slot_end_padding > 15) { |
| 474 | /* can't handle padding over 15 bits */ |
| 475 | trace_ssp_error("ecf"); |
| 476 | ret = -EINVAL; |
| 477 | goto out; |
| 478 | } |
| 479 | |
Pierre-Louis Bossart | 29d4e8b | 2018-04-13 12:11:44 -0500 | [diff] [blame] | 480 | sspsp |= SSPSP_DMYSTOP(slot_end_padding & SSPSP_DMYSTOP_MASK); |
| 481 | slot_end_padding >>= SSPSP_DMYSTOP_BITS; |
| 482 | sspsp |= SSPSP_EDMYSTOP(slot_end_padding & SSPSP_EDMYSTOP_MASK); |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 483 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 484 | break; |
| 485 | |
| 486 | case SOF_DAI_FMT_LEFT_J: |
| 487 | |
Bartosz Kokoszko | 10fcb20 | 2018-08-20 15:28:36 +0200 | [diff] [blame] | 488 | /* default start_delay value is set to false */ |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 489 | |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 490 | sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 491 | |
| 492 | /* LJDFD enable */ |
| 493 | sscr2 &= ~SSCR2_LJDFD; |
| 494 | |
Keyon Jie | a92debe | 2018-03-09 20:54:52 +0800 | [diff] [blame] | 495 | if (bdiv % 2) { |
| 496 | trace_ssp_error("ecb"); |
| 497 | ret = -EINVAL; |
| 498 | goto out; |
| 499 | } |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 500 | |
Keyon Jie | a92debe | 2018-03-09 20:54:52 +0800 | [diff] [blame] | 501 | /* set asserted frame length to half frame length */ |
| 502 | frame_len = bdiv / 2; |
| 503 | |
| 504 | /* |
| 505 | * handle frame polarity, LEFT_J default is rising/active high, |
| 506 | * non-inverted(inverted_frame=0) -- active high(SFRMP=1), |
| 507 | * inverted(inverted_frame=1) -- falling/active low(SFRMP=0), |
| 508 | * so, we should set SFRMP to !inverted_frame. |
| 509 | */ |
| 510 | sspsp |= SSPSP_SFRMP(!inverted_frame); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 511 | |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 512 | /* |
| 513 | * for I2S/LEFT_J, the padding has to happen at the end |
| 514 | * of each slot |
| 515 | */ |
| 516 | if (frame_end_padding % 2) { |
| 517 | trace_ssp_error("ecg"); |
| 518 | ret = -EINVAL; |
| 519 | goto out; |
| 520 | } |
| 521 | |
| 522 | slot_end_padding = frame_end_padding / 2; |
| 523 | |
| 524 | if (slot_end_padding > 15) { |
| 525 | /* can't handle padding over 15 bits */ |
| 526 | trace_ssp_error("ech"); |
| 527 | ret = -EINVAL; |
| 528 | goto out; |
| 529 | } |
| 530 | |
Pierre-Louis Bossart | 29d4e8b | 2018-04-13 12:11:44 -0500 | [diff] [blame] | 531 | sspsp |= SSPSP_DMYSTOP(slot_end_padding & SSPSP_DMYSTOP_MASK); |
| 532 | slot_end_padding >>= SSPSP_DMYSTOP_BITS; |
| 533 | sspsp |= SSPSP_EDMYSTOP(slot_end_padding & SSPSP_EDMYSTOP_MASK); |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 534 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 535 | break; |
| 536 | case SOF_DAI_FMT_DSP_A: |
| 537 | |
Bartosz Kokoszko | 10fcb20 | 2018-08-20 15:28:36 +0200 | [diff] [blame] | 538 | start_delay = true; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 539 | |
Bartosz Kokoszko | 9fe51a4 | 2018-08-09 10:33:22 +0200 | [diff] [blame] | 540 | /* fallthrough */ |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 541 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 542 | case SOF_DAI_FMT_DSP_B: |
| 543 | |
Bartosz Kokoszko | 10fcb20 | 2018-08-20 15:28:36 +0200 | [diff] [blame] | 544 | /* default start_delay value is set to false */ |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 545 | |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 546 | sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->ssp.tdm_slots); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 547 | |
| 548 | /* set asserted frame length */ |
Bartosz Kokoszko | effa56f | 2018-08-01 11:53:54 +0200 | [diff] [blame] | 549 | frame_len = 1; /* default */ |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 550 | |
Bartosz Kokoszko | effa56f | 2018-08-01 11:53:54 +0200 | [diff] [blame] | 551 | if (cfs && ssp->params.frame_pulse_width > 0 && |
| 552 | ssp->params.frame_pulse_width <= |
| 553 | SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX) { |
| 554 | frame_len = ssp->params.frame_pulse_width; |
| 555 | } |
| 556 | |
| 557 | /* frame_pulse_width must less or equal 38 */ |
| 558 | if (ssp->params.frame_pulse_width > |
| 559 | SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX) { |
| 560 | trace_ssp_error("efb"); |
| 561 | ret = -EINVAL; |
| 562 | goto out; |
| 563 | } |
Keyon Jie | a92debe | 2018-03-09 20:54:52 +0800 | [diff] [blame] | 564 | /* |
| 565 | * handle frame polarity, DSP_B default is rising/active high, |
| 566 | * non-inverted(inverted_frame=0) -- active high(SFRMP=1), |
| 567 | * inverted(inverted_frame=1) -- falling/active low(SFRMP=0), |
| 568 | * so, we should set SFRMP to !inverted_frame. |
| 569 | */ |
Rander Wang | 6807980 | 2018-03-07 12:31:02 +0800 | [diff] [blame] | 570 | sspsp |= SSPSP_SFRMP(!inverted_frame); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 571 | |
Pierre-Louis Bossart | e7fd644 | 2018-04-04 20:43:16 -0500 | [diff] [blame] | 572 | active_tx_slots = hweight_32(config->ssp.tx_slots); |
| 573 | active_rx_slots = hweight_32(config->ssp.rx_slots); |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 574 | |
Bartosz Kokoszko | 5562fa3 | 2018-08-13 10:59:08 +0200 | [diff] [blame] | 575 | /* |
| 576 | * handle TDM mode, TDM mode has padding at the end of |
| 577 | * each slot. The amount of padding is equal to result of |
| 578 | * subtracting slot width and valid bits per slot. |
| 579 | */ |
| 580 | if (ssp->params.tdm_per_slot_padding_flag) { |
| 581 | frame_end_padding = bdiv - config->ssp.tdm_slots * |
| 582 | config->ssp.tdm_slot_width; |
| 583 | |
| 584 | slot_end_padding = config->ssp.tdm_slot_width - |
| 585 | config->ssp.sample_valid_bits; |
| 586 | |
| 587 | if (slot_end_padding > |
| 588 | SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX) { |
| 589 | trace_ssp_error("esb"); |
| 590 | ret = -EINVAL; |
| 591 | goto out; |
| 592 | } |
| 593 | |
| 594 | sspsp |= SSPSP_DMYSTOP(slot_end_padding & |
| 595 | SSPSP_DMYSTOP_MASK); |
| 596 | slot_end_padding >>= SSPSP_DMYSTOP_BITS; |
| 597 | sspsp |= SSPSP_EDMYSTOP(slot_end_padding & |
| 598 | SSPSP_EDMYSTOP_MASK); |
| 599 | } |
| 600 | |
Pierre-Louis Bossart | 36f23c6 | 2018-04-12 17:27:16 -0500 | [diff] [blame] | 601 | sspsp2 |= (frame_end_padding & SSPSP2_FEP_MASK); |
| 602 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 603 | break; |
| 604 | default: |
| 605 | trace_ssp_error("eca"); |
| 606 | ret = -EINVAL; |
| 607 | goto out; |
| 608 | } |
| 609 | |
Bartosz Kokoszko | 10fcb20 | 2018-08-20 15:28:36 +0200 | [diff] [blame] | 610 | if (start_delay) |
| 611 | sspsp |= SSPSP_FSRT; |
| 612 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 613 | sspsp |= SSPSP_SFRMWDTH(frame_len); |
| 614 | |
Pierre-Louis Bossart | 6900f4d | 2018-04-04 20:43:17 -0500 | [diff] [blame] | 615 | data_size = config->ssp.sample_valid_bits; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 616 | |
| 617 | if (data_size > 16) |
| 618 | sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); |
| 619 | else |
| 620 | sscr0 |= SSCR0_DSIZE(data_size); |
| 621 | |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 622 | /* setting TFT and RFT */ |
Pierre-Louis Bossart | 6900f4d | 2018-04-04 20:43:17 -0500 | [diff] [blame] | 623 | switch (config->ssp.sample_valid_bits) { |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 624 | case 16: |
Wu Zhigang | e3b10cd | 2018-05-18 14:37:57 +0800 | [diff] [blame] | 625 | /* use 2 bytes for each slot */ |
| 626 | sample_width = 2; |
| 627 | break; |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 628 | case 24: |
| 629 | case 32: |
Wu Zhigang | e3b10cd | 2018-05-18 14:37:57 +0800 | [diff] [blame] | 630 | /* use 4 bytes for each slot */ |
| 631 | sample_width = 4; |
| 632 | break; |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 633 | default: |
Wu Zhigang | e3b10cd | 2018-05-18 14:37:57 +0800 | [diff] [blame] | 634 | trace_ssp_error("ecd"); |
| 635 | ret = -EINVAL; |
| 636 | goto out; |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 637 | } |
| 638 | |
Wu Zhigang | a35bcd5 | 2018-07-25 13:42:19 +0800 | [diff] [blame] | 639 | tft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, |
| 640 | sample_width * active_tx_slots); |
| 641 | rft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, |
| 642 | sample_width * active_rx_slots); |
Wu Zhigang | e3b10cd | 2018-05-18 14:37:57 +0800 | [diff] [blame] | 643 | |
Keyon Jie | cc54f13 | 2018-03-15 20:01:09 +0800 | [diff] [blame] | 644 | sscr3 |= SSCR3_TX(tft) | SSCR3_RX(rft); |
| 645 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 646 | trace_ssp("coe"); |
| 647 | ssp_write(dai, SSCR0, sscr0); |
| 648 | ssp_write(dai, SSCR1, sscr1); |
| 649 | ssp_write(dai, SSCR2, sscr2); |
| 650 | ssp_write(dai, SSCR3, sscr3); |
| 651 | ssp_write(dai, SSPSP, sspsp); |
| 652 | ssp_write(dai, SSPSP2, sspsp2); |
| 653 | ssp_write(dai, SSIOC, ssioc); |
| 654 | ssp_write(dai, SSTO, ssto); |
| 655 | ssp_write(dai, SSTSA, sstsa); |
| 656 | ssp_write(dai, SSRSA, ssrsa); |
| 657 | |
| 658 | trace_value(sscr0); |
| 659 | trace_value(sscr1); |
| 660 | trace_value(ssto); |
| 661 | trace_value(sspsp); |
| 662 | trace_value(sstsa); |
| 663 | trace_value(ssrsa); |
| 664 | trace_value(sscr2); |
| 665 | trace_value(sspsp2); |
| 666 | trace_value(sscr3); |
| 667 | trace_value(ssioc); |
| 668 | |
| 669 | /* TODO: move this into M/N driver */ |
| 670 | mn_reg_write(0x0, mdivc); |
Pierre-Louis Bossart | 32bcaaf | 2018-06-18 14:27:31 -0500 | [diff] [blame] | 671 | mn_reg_write(0x80 + config->ssp.mclk_id * 0x4, mdivr); |
Pan Xiuli | e68b10f | 2018-07-23 16:49:51 +0800 | [diff] [blame] | 672 | mn_reg_write(0x100 + config->dai_index * 0x8 + 0x0, i2s_m); |
| 673 | mn_reg_write(0x100 + config->dai_index * 0x8 + 0x4, i2s_n); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 674 | |
| 675 | ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; |
| 676 | ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE; |
| 677 | |
| 678 | out: |
| 679 | spin_unlock(&ssp->lock); |
| 680 | |
| 681 | return ret; |
| 682 | } |
| 683 | |
| 684 | /* Digital Audio interface formatting */ |
| 685 | static inline int ssp_set_loopback_mode(struct dai *dai, uint32_t lbm) |
| 686 | { |
| 687 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 688 | |
| 689 | trace_ssp("loo"); |
| 690 | spin_lock(&ssp->lock); |
| 691 | |
| 692 | ssp_update_bits(dai, SSCR1, SSCR1_LBM, lbm ? SSCR1_LBM : 0); |
| 693 | |
| 694 | spin_unlock(&ssp->lock); |
| 695 | |
| 696 | return 0; |
| 697 | } |
| 698 | |
| 699 | /* start the SSP for either playback or capture */ |
| 700 | static void ssp_start(struct dai *dai, int direction) |
| 701 | { |
| 702 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 703 | |
| 704 | spin_lock(&ssp->lock); |
| 705 | |
| 706 | /* enable port */ |
| 707 | ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); |
| 708 | ssp->state[direction] = COMP_STATE_ACTIVE; |
| 709 | |
| 710 | trace_ssp("sta"); |
| 711 | |
| 712 | /* enable DMA */ |
| 713 | if (direction == DAI_DIR_PLAYBACK) { |
| 714 | ssp_update_bits(dai, SSCR1, SSCR1_TSRE, SSCR1_TSRE); |
| 715 | ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x1 << 8); |
| 716 | } else { |
| 717 | ssp_update_bits(dai, SSCR1, SSCR1_RSRE, SSCR1_RSRE); |
| 718 | ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x1 << 8); |
| 719 | } |
| 720 | |
| 721 | spin_unlock(&ssp->lock); |
| 722 | } |
| 723 | |
| 724 | /* stop the SSP for either playback or capture */ |
Keyon Jie | 72e63a4 | 2018-05-08 18:42:09 +0800 | [diff] [blame] | 725 | static void ssp_stop(struct dai *dai, int direction) |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 726 | { |
| 727 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 728 | |
| 729 | spin_lock(&ssp->lock); |
| 730 | |
Keyon Jie | 72e63a4 | 2018-05-08 18:42:09 +0800 | [diff] [blame] | 731 | /* stop Rx if neeed */ |
| 732 | if (direction == DAI_DIR_CAPTURE && |
| 733 | ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_ACTIVE) { |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 734 | ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0); |
Rander Wang | 7e58038 | 2018-03-22 07:30:15 +0800 | [diff] [blame] | 735 | ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x0 << 8); |
Tomasz Lauda | dcc6149 | 2018-07-16 13:55:35 +0200 | [diff] [blame] | 736 | ssp_empty_rx_fifo(dai); |
Keyon Jie | 72e63a4 | 2018-05-08 18:42:09 +0800 | [diff] [blame] | 737 | ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PAUSED; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 738 | trace_ssp("Ss0"); |
| 739 | } |
| 740 | |
Keyon Jie | 72e63a4 | 2018-05-08 18:42:09 +0800 | [diff] [blame] | 741 | /* stop Tx if needed */ |
| 742 | if (direction == DAI_DIR_PLAYBACK && |
| 743 | ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_ACTIVE) { |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 744 | ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0); |
Rander Wang | 7e58038 | 2018-03-22 07:30:15 +0800 | [diff] [blame] | 745 | ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x0 << 8); |
Keyon Jie | 72e63a4 | 2018-05-08 18:42:09 +0800 | [diff] [blame] | 746 | ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PAUSED; |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 747 | trace_ssp("Ss1"); |
| 748 | } |
| 749 | |
| 750 | /* disable SSP port if no users */ |
| 751 | if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE && |
| 752 | ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { |
| 753 | ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); |
| 754 | ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; |
| 755 | ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE; |
| 756 | trace_ssp("Ss2"); |
| 757 | } |
| 758 | |
| 759 | spin_unlock(&ssp->lock); |
| 760 | } |
| 761 | |
| 762 | static int ssp_trigger(struct dai *dai, int cmd, int direction) |
| 763 | { |
| 764 | struct ssp_pdata *ssp = dai_get_drvdata(dai); |
| 765 | |
| 766 | trace_ssp("tri"); |
| 767 | |
| 768 | switch (cmd) { |
Liam Girdwood | 7ec3a7c | 2018-03-28 18:05:37 -0700 | [diff] [blame] | 769 | case COMP_TRIGGER_START: |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 770 | if (ssp->state[direction] == COMP_STATE_PREPARE || |
| 771 | ssp->state[direction] == COMP_STATE_PAUSED) |
| 772 | ssp_start(dai, direction); |
| 773 | break; |
Liam Girdwood | 7ec3a7c | 2018-03-28 18:05:37 -0700 | [diff] [blame] | 774 | case COMP_TRIGGER_RELEASE: |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 775 | if (ssp->state[direction] == COMP_STATE_PAUSED || |
| 776 | ssp->state[direction] == COMP_STATE_PREPARE) |
| 777 | ssp_start(dai, direction); |
| 778 | break; |
Liam Girdwood | 7ec3a7c | 2018-03-28 18:05:37 -0700 | [diff] [blame] | 779 | case COMP_TRIGGER_STOP: |
| 780 | case COMP_TRIGGER_PAUSE: |
Keyon Jie | 72e63a4 | 2018-05-08 18:42:09 +0800 | [diff] [blame] | 781 | ssp_stop(dai, direction); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 782 | break; |
Liam Girdwood | 7ec3a7c | 2018-03-28 18:05:37 -0700 | [diff] [blame] | 783 | case COMP_TRIGGER_RESUME: |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 784 | ssp_context_restore(dai); |
| 785 | break; |
Liam Girdwood | 7ec3a7c | 2018-03-28 18:05:37 -0700 | [diff] [blame] | 786 | case COMP_TRIGGER_SUSPEND: |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 787 | ssp_context_store(dai); |
| 788 | break; |
| 789 | default: |
| 790 | break; |
| 791 | } |
| 792 | |
| 793 | return 0; |
| 794 | } |
| 795 | |
| 796 | /* clear IRQ sources atm */ |
| 797 | static void ssp_irq_handler(void *data) |
| 798 | { |
| 799 | struct dai *dai = data; |
| 800 | |
| 801 | trace_ssp("irq"); |
| 802 | trace_value(ssp_read(dai, SSSR)); |
| 803 | |
| 804 | /* clear IRQ */ |
| 805 | ssp_write(dai, SSSR, ssp_read(dai, SSSR)); |
| 806 | platform_interrupt_clear(ssp_irq(dai), 1); |
| 807 | } |
| 808 | |
| 809 | static int ssp_probe(struct dai *dai) |
| 810 | { |
| 811 | struct ssp_pdata *ssp; |
| 812 | |
| 813 | /* allocate private data */ |
Liam Girdwood | 1f6aee5 | 2018-03-01 16:13:05 +0000 | [diff] [blame] | 814 | ssp = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*ssp)); |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 815 | dai_set_drvdata(dai, ssp); |
| 816 | |
| 817 | spinlock_init(&ssp->lock); |
| 818 | |
| 819 | ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY; |
| 820 | ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY; |
| 821 | |
| 822 | /* register our IRQ handler */ |
| 823 | interrupt_register(ssp_irq(dai), ssp_irq_handler, dai); |
| 824 | platform_interrupt_unmask(ssp_irq(dai), 1); |
| 825 | interrupt_enable(ssp_irq(dai)); |
| 826 | |
Tomasz Lauda | dcc6149 | 2018-07-16 13:55:35 +0200 | [diff] [blame] | 827 | ssp_empty_rx_fifo(dai); |
| 828 | |
Rander Wang | 4502765 | 2018-02-08 16:06:33 +0800 | [diff] [blame] | 829 | return 0; |
| 830 | } |
| 831 | |
| 832 | const struct dai_ops ssp_ops = { |
| 833 | .trigger = ssp_trigger, |
| 834 | .set_config = ssp_set_config, |
| 835 | .pm_context_store = ssp_context_store, |
| 836 | .pm_context_restore = ssp_context_restore, |
| 837 | .probe = ssp_probe, |
| 838 | .set_loopback_mode = ssp_set_loopback_mode, |
| 839 | }; |