blob: 53207f2508aa13004b98e6272dce441908dbacc3 [file] [log] [blame]
Rander Wang45027652018-02-08 16:06:33 +08001/*
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 Bossart81708a52018-04-04 18:46:50 -050035#include <sof/stream.h>
36#include <sof/ssp.h>
37#include <sof/alloc.h>
38#include <sof/interrupt.h>
Wu Zhigange3b10cd2018-05-18 14:37:57 +080039#include <sof/math/numbers.h>
Keyon Jie1532dfd2018-02-09 19:39:42 +080040#include <config.h>
Rander Wang45027652018-02-08 16:06:33 +080041
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 Bossart32bcaaf2018-06-18 14:27:31 -050047#define F_19200_kHz 19200000
48#define F_24000_kHz 24000000
49#define F_24576_kHz 24576000
50
Keyon Jiecc54f132018-03-15 20:01:09 +080051/* FIXME: move this to a helper and optimize */
52static 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 Laudadcc61492018-07-16 13:55:35 +020064/* empty SSP receive FIFO */
65static 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 Wang45027652018-02-08 16:06:33 +080090/* save SSP context prior to entering D3 */
91static 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 */
105static 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 */
118static 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 Bossart32bcaaf2018-06-18 14:27:31 -0500136 uint32_t mdivr_val;
Rander Wang45027652018-02-08 16:06:33 +0800137 uint32_t i2s_m;
138 uint32_t i2s_n;
139 uint32_t data_size;
Tomasz Lauda4c1d4492018-03-15 17:05:44 +0100140 uint32_t frame_end_padding;
Pierre-Louis Bossart36f23c62018-04-12 17:27:16 -0500141 uint32_t slot_end_padding;
Rander Wang45027652018-02-08 16:06:33 +0800142 uint32_t frame_len = 0;
Keyon Jiea92debe2018-03-09 20:54:52 +0800143 uint32_t bdiv_min;
Keyon Jiecc54f132018-03-15 20:01:09 +0800144 uint32_t tft;
145 uint32_t rft;
146 uint32_t active_tx_slots = 2;
147 uint32_t active_rx_slots = 2;
Wu Zhigange3b10cd2018-05-18 14:37:57 +0800148 uint32_t sample_width = 2;
Keyon Jiecc54f132018-03-15 20:01:09 +0800149
Rander Wang45027652018-02-08 16:06:33 +0800150 bool inverted_frame = false;
Bartosz Kokoszkoeffa56f2018-08-01 11:53:54 +0200151 bool cfs = false;
Bartosz Kokoszko10fcb202018-08-20 15:28:36 +0200152 bool start_delay = false;
Rander Wang45027652018-02-08 16:06:33 +0800153 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 Ingalsuo48a61422018-05-28 18:21:32 +0300166 trace_value(config->format);
Rander Wang45027652018-02-08 16:06:33 +0800167
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 Lauda4c1d4492018-03-15 17:05:44 +0100177 sscr1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL | SSCR1_RSRE | SSCR1_TSRE;
Rander Wang45027652018-02-08 16:06:33 +0800178
179 /* sscr2 dynamic setting is LJDFD */
180 sscr2 = SSCR2_SDFD | SSCR2_TURM1;
181
182 /* sscr3 dynamic settings are TFT, RFT */
Tomasz Laudaecf43dd2018-03-16 11:49:03 +0100183 sscr3 = 0;
Rander Wang45027652018-02-08 16:06:33 +0800184
185 /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */
186 sspsp = 0;
187
188 ssp->config = *config;
Pierre-Louis Bossartfbcbe582018-04-04 20:43:15 -0500189 ssp->params = config->ssp;
Rander Wang45027652018-02-08 16:06:33 +0800190
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 Bossarte7fd6442018-04-04 20:43:16 -0500207 sstsa = config->ssp.tx_slots;
Rander Wang45027652018-02-08 16:06:33 +0800208
209 /* ssrsa dynamic setting is RTSA, default 2 slots */
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500210 ssrsa = config->ssp.rx_slots;
Rander Wang45027652018-02-08 16:06:33 +0800211
Rander Wang45027652018-02-08 16:06:33 +0800212 trace_value(config->format);
213 switch (config->format & SOF_DAI_FMT_MASTER_MASK) {
214 case SOF_DAI_FMT_CBM_CFM:
Slawomir Blauciak6a6cdf72018-07-25 09:56:58 +0200215 sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
Rander Wang45027652018-02-08 16:06:33 +0800216 break;
217 case SOF_DAI_FMT_CBS_CFS:
218 sscr1 |= SSCR1_SCFR;
Bartosz Kokoszkoeffa56f2018-08-01 11:53:54 +0200219 cfs = true;
Rander Wang45027652018-02-08 16:06:33 +0800220 break;
221 case SOF_DAI_FMT_CBM_CFS:
Rander Wang45027652018-02-08 16:06:33 +0800222 sscr1 |= SSCR1_SCLKDIR;
Rander Wang45027652018-02-08 16:06:33 +0800223 /* FIXME: this mode has not been tested */
Bartosz Kokoszkoeffa56f2018-08-01 11:53:54 +0200224
225 cfs = true;
Rander Wang45027652018-02-08 16:06:33 +0800226 break;
227 case SOF_DAI_FMT_CBS_CFM:
Slawomir Blauciak6a6cdf72018-07-25 09:56:58 +0200228 sscr1 |= SSCR1_SCFR | SSCR1_SFRMDIR;
Rander Wang45027652018-02-08 16:06:33 +0800229 /* 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 Bossart26c291b2018-03-09 09:24:21 -0600242 inverted_frame = true; /* handled later with format */
Rander Wang45027652018-02-08 16:06:33 +0800243 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 Wang45027652018-02-08 16:06:33 +0800250 break;
251 default:
252 trace_ssp_error("ec3");
253 ret = -EINVAL;
254 goto out;
255 }
256
Rander Wang45027652018-02-08 16:06:33 +0800257 sscr0 |= SSCR0_MOD | SSCR0_ACS;
Pierre-Louis Bossart32bcaaf2018-06-18 14:27:31 -0500258
Pierre-Louis Bossart383d89d2018-07-31 19:17:30 -0500259 mdivc = mn_reg_read(0x0);
260 mdivc |= 0x1;
261
Bartosz Kokoszkoeffa56f2018-08-01 11:53:54 +0200262 /* 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 Bossart32bcaaf2018-06-18 14:27:31 -0500296#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 Wang45027652018-02-08 16:06:33 +0800362#endif
363
Pierre-Louis Bossart32bcaaf2018-06-18 14:27:31 -0500364 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 Wang45027652018-02-08 16:06:33 +0800385 ret = -EINVAL;
386 goto out;
387 }
388
389 /* divisor must be within SCR range */
Pierre-Louis Bossart32bcaaf2018-06-18 14:27:31 -0500390 mdiv -= 1;
Rander Wang45027652018-02-08 16:06:33 +0800391 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 Bossarte7fd6442018-04-04 20:43:16 -0500401 if (config->ssp.bclk_rate % config->ssp.fsync_rate) {
Rander Wang45027652018-02-08 16:06:33 +0800402 trace_ssp_error("ec7");
403 ret = -EINVAL;
404 goto out;
405 }
406
Tomasz Lauda4c1d4492018-03-15 17:05:44 +0100407 /* must be enough BCLKs for data */
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500408 bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate;
409 if (bdiv < config->ssp.tdm_slot_width * config->ssp.tdm_slots) {
Rander Wang45027652018-02-08 16:06:33 +0800410 trace_ssp_error("ec8");
411 ret = -EINVAL;
412 goto out;
413 }
414
Pierre-Louis Bossart75794292018-04-04 20:43:14 -0500415 /* tdm_slot_width must be <= 38 for SSP */
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500416 if (config->ssp.tdm_slot_width > 38) {
Rander Wang45027652018-02-08 16:06:33 +0800417 trace_ssp_error("ec9");
418 ret = -EINVAL;
419 goto out;
420 }
421
Pierre-Louis Bossart36f23c62018-04-12 17:27:16 -0500422 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 Wang45027652018-02-08 16:06:33 +0800436 /* format */
437 switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
438 case SOF_DAI_FMT_I2S:
439
Bartosz Kokoszko10fcb202018-08-20 15:28:36 +0200440 start_delay = true;
Rander Wang45027652018-02-08 16:06:33 +0800441
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500442 sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots);
Rander Wang45027652018-02-08 16:06:33 +0800443
Keyon Jiea92debe2018-03-09 20:54:52 +0800444 if (bdiv % 2) {
445 trace_ssp_error("eca");
446 ret = -EINVAL;
447 goto out;
448 }
Rander Wang45027652018-02-08 16:06:33 +0800449
Keyon Jiea92debe2018-03-09 20:54:52 +0800450 /* 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 Wang45027652018-02-08 16:06:33 +0800460
Pierre-Louis Bossart36f23c62018-04-12 17:27:16 -0500461 /*
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 Bossart29d4e8b2018-04-13 12:11:44 -0500480 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 Bossart36f23c62018-04-12 17:27:16 -0500483
Rander Wang45027652018-02-08 16:06:33 +0800484 break;
485
486 case SOF_DAI_FMT_LEFT_J:
487
Bartosz Kokoszko10fcb202018-08-20 15:28:36 +0200488 /* default start_delay value is set to false */
Rander Wang45027652018-02-08 16:06:33 +0800489
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500490 sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots);
Rander Wang45027652018-02-08 16:06:33 +0800491
492 /* LJDFD enable */
493 sscr2 &= ~SSCR2_LJDFD;
494
Keyon Jiea92debe2018-03-09 20:54:52 +0800495 if (bdiv % 2) {
496 trace_ssp_error("ecb");
497 ret = -EINVAL;
498 goto out;
499 }
Rander Wang45027652018-02-08 16:06:33 +0800500
Keyon Jiea92debe2018-03-09 20:54:52 +0800501 /* 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 Wang45027652018-02-08 16:06:33 +0800511
Pierre-Louis Bossart36f23c62018-04-12 17:27:16 -0500512 /*
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 Bossart29d4e8b2018-04-13 12:11:44 -0500531 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 Bossart36f23c62018-04-12 17:27:16 -0500534
Rander Wang45027652018-02-08 16:06:33 +0800535 break;
536 case SOF_DAI_FMT_DSP_A:
537
Bartosz Kokoszko10fcb202018-08-20 15:28:36 +0200538 start_delay = true;
Rander Wang45027652018-02-08 16:06:33 +0800539
Bartosz Kokoszko9fe51a42018-08-09 10:33:22 +0200540 /* fallthrough */
Keyon Jiecc54f132018-03-15 20:01:09 +0800541
Rander Wang45027652018-02-08 16:06:33 +0800542 case SOF_DAI_FMT_DSP_B:
543
Bartosz Kokoszko10fcb202018-08-20 15:28:36 +0200544 /* default start_delay value is set to false */
Rander Wang45027652018-02-08 16:06:33 +0800545
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500546 sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->ssp.tdm_slots);
Rander Wang45027652018-02-08 16:06:33 +0800547
548 /* set asserted frame length */
Bartosz Kokoszkoeffa56f2018-08-01 11:53:54 +0200549 frame_len = 1; /* default */
Rander Wang45027652018-02-08 16:06:33 +0800550
Bartosz Kokoszkoeffa56f2018-08-01 11:53:54 +0200551 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 Jiea92debe2018-03-09 20:54:52 +0800564 /*
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 Wang68079802018-03-07 12:31:02 +0800570 sspsp |= SSPSP_SFRMP(!inverted_frame);
Rander Wang45027652018-02-08 16:06:33 +0800571
Pierre-Louis Bossarte7fd6442018-04-04 20:43:16 -0500572 active_tx_slots = hweight_32(config->ssp.tx_slots);
573 active_rx_slots = hweight_32(config->ssp.rx_slots);
Keyon Jiecc54f132018-03-15 20:01:09 +0800574
Bartosz Kokoszko5562fa32018-08-13 10:59:08 +0200575 /*
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 Bossart36f23c62018-04-12 17:27:16 -0500601 sspsp2 |= (frame_end_padding & SSPSP2_FEP_MASK);
602
Rander Wang45027652018-02-08 16:06:33 +0800603 break;
604 default:
605 trace_ssp_error("eca");
606 ret = -EINVAL;
607 goto out;
608 }
609
Bartosz Kokoszko10fcb202018-08-20 15:28:36 +0200610 if (start_delay)
611 sspsp |= SSPSP_FSRT;
612
Rander Wang45027652018-02-08 16:06:33 +0800613 sspsp |= SSPSP_SFRMWDTH(frame_len);
614
Pierre-Louis Bossart6900f4d2018-04-04 20:43:17 -0500615 data_size = config->ssp.sample_valid_bits;
Rander Wang45027652018-02-08 16:06:33 +0800616
617 if (data_size > 16)
618 sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16));
619 else
620 sscr0 |= SSCR0_DSIZE(data_size);
621
Keyon Jiecc54f132018-03-15 20:01:09 +0800622 /* setting TFT and RFT */
Pierre-Louis Bossart6900f4d2018-04-04 20:43:17 -0500623 switch (config->ssp.sample_valid_bits) {
Keyon Jiecc54f132018-03-15 20:01:09 +0800624 case 16:
Wu Zhigange3b10cd2018-05-18 14:37:57 +0800625 /* use 2 bytes for each slot */
626 sample_width = 2;
627 break;
Keyon Jiecc54f132018-03-15 20:01:09 +0800628 case 24:
629 case 32:
Wu Zhigange3b10cd2018-05-18 14:37:57 +0800630 /* use 4 bytes for each slot */
631 sample_width = 4;
632 break;
Keyon Jiecc54f132018-03-15 20:01:09 +0800633 default:
Wu Zhigange3b10cd2018-05-18 14:37:57 +0800634 trace_ssp_error("ecd");
635 ret = -EINVAL;
636 goto out;
Keyon Jiecc54f132018-03-15 20:01:09 +0800637 }
638
Wu Zhiganga35bcd52018-07-25 13:42:19 +0800639 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 Zhigange3b10cd2018-05-18 14:37:57 +0800643
Keyon Jiecc54f132018-03-15 20:01:09 +0800644 sscr3 |= SSCR3_TX(tft) | SSCR3_RX(rft);
645
Rander Wang45027652018-02-08 16:06:33 +0800646 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 Bossart32bcaaf2018-06-18 14:27:31 -0500671 mn_reg_write(0x80 + config->ssp.mclk_id * 0x4, mdivr);
Pan Xiulie68b10f2018-07-23 16:49:51 +0800672 mn_reg_write(0x100 + config->dai_index * 0x8 + 0x0, i2s_m);
673 mn_reg_write(0x100 + config->dai_index * 0x8 + 0x4, i2s_n);
Rander Wang45027652018-02-08 16:06:33 +0800674
675 ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE;
676 ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE;
677
678out:
679 spin_unlock(&ssp->lock);
680
681 return ret;
682}
683
684/* Digital Audio interface formatting */
685static 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 */
700static 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 Jie72e63a42018-05-08 18:42:09 +0800725static void ssp_stop(struct dai *dai, int direction)
Rander Wang45027652018-02-08 16:06:33 +0800726{
727 struct ssp_pdata *ssp = dai_get_drvdata(dai);
728
729 spin_lock(&ssp->lock);
730
Keyon Jie72e63a42018-05-08 18:42:09 +0800731 /* stop Rx if neeed */
732 if (direction == DAI_DIR_CAPTURE &&
733 ssp->state[SOF_IPC_STREAM_CAPTURE] == COMP_STATE_ACTIVE) {
Rander Wang45027652018-02-08 16:06:33 +0800734 ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0);
Rander Wang7e580382018-03-22 07:30:15 +0800735 ssp_update_bits(dai, SSRSA, 0x1 << 8, 0x0 << 8);
Tomasz Laudadcc61492018-07-16 13:55:35 +0200736 ssp_empty_rx_fifo(dai);
Keyon Jie72e63a42018-05-08 18:42:09 +0800737 ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PAUSED;
Rander Wang45027652018-02-08 16:06:33 +0800738 trace_ssp("Ss0");
739 }
740
Keyon Jie72e63a42018-05-08 18:42:09 +0800741 /* stop Tx if needed */
742 if (direction == DAI_DIR_PLAYBACK &&
743 ssp->state[SOF_IPC_STREAM_PLAYBACK] == COMP_STATE_ACTIVE) {
Rander Wang45027652018-02-08 16:06:33 +0800744 ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0);
Rander Wang7e580382018-03-22 07:30:15 +0800745 ssp_update_bits(dai, SSTSA, 0x1 << 8, 0x0 << 8);
Keyon Jie72e63a42018-05-08 18:42:09 +0800746 ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PAUSED;
Rander Wang45027652018-02-08 16:06:33 +0800747 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
762static 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 Girdwood7ec3a7c2018-03-28 18:05:37 -0700769 case COMP_TRIGGER_START:
Rander Wang45027652018-02-08 16:06:33 +0800770 if (ssp->state[direction] == COMP_STATE_PREPARE ||
771 ssp->state[direction] == COMP_STATE_PAUSED)
772 ssp_start(dai, direction);
773 break;
Liam Girdwood7ec3a7c2018-03-28 18:05:37 -0700774 case COMP_TRIGGER_RELEASE:
Rander Wang45027652018-02-08 16:06:33 +0800775 if (ssp->state[direction] == COMP_STATE_PAUSED ||
776 ssp->state[direction] == COMP_STATE_PREPARE)
777 ssp_start(dai, direction);
778 break;
Liam Girdwood7ec3a7c2018-03-28 18:05:37 -0700779 case COMP_TRIGGER_STOP:
780 case COMP_TRIGGER_PAUSE:
Keyon Jie72e63a42018-05-08 18:42:09 +0800781 ssp_stop(dai, direction);
Rander Wang45027652018-02-08 16:06:33 +0800782 break;
Liam Girdwood7ec3a7c2018-03-28 18:05:37 -0700783 case COMP_TRIGGER_RESUME:
Rander Wang45027652018-02-08 16:06:33 +0800784 ssp_context_restore(dai);
785 break;
Liam Girdwood7ec3a7c2018-03-28 18:05:37 -0700786 case COMP_TRIGGER_SUSPEND:
Rander Wang45027652018-02-08 16:06:33 +0800787 ssp_context_store(dai);
788 break;
789 default:
790 break;
791 }
792
793 return 0;
794}
795
796/* clear IRQ sources atm */
797static 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
809static int ssp_probe(struct dai *dai)
810{
811 struct ssp_pdata *ssp;
812
813 /* allocate private data */
Liam Girdwood1f6aee52018-03-01 16:13:05 +0000814 ssp = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*ssp));
Rander Wang45027652018-02-08 16:06:33 +0800815 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 Laudadcc61492018-07-16 13:55:35 +0200827 ssp_empty_rx_fifo(dai);
828
Rander Wang45027652018-02-08 16:06:33 +0800829 return 0;
830}
831
832const 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};