blob: 682789bb8ab30bcd40df538817c01f4bae056a56 [file] [log] [blame]
Vinod Koul2f52a512017-12-14 11:19:41 +05301// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2// Copyright(c) 2015-17 Intel Corporation.
3
4/*
5 * Cadence SoundWire Master module
6 * Used by Master driver
7 */
8
9#include <linux/delay.h>
10#include <linux/device.h>
11#include <linux/interrupt.h>
12#include <linux/module.h>
13#include <linux/mod_devicetable.h>
14#include <linux/soundwire/sdw_registers.h>
15#include <linux/soundwire/sdw.h>
Vinod Koul5d6b3c82018-04-26 18:38:53 +053016#include <sound/pcm_params.h>
17#include <sound/soc.h>
Vinod Koul2f52a512017-12-14 11:19:41 +053018#include "bus.h"
19#include "cadence_master.h"
20
21#define CDNS_MCP_CONFIG 0x0
22
23#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24)
24#define CDNS_MCP_CONFIG_MPREQ_DELAY GENMASK(20, 16)
25#define CDNS_MCP_CONFIG_MMASTER BIT(7)
26#define CDNS_MCP_CONFIG_BUS_REL BIT(6)
27#define CDNS_MCP_CONFIG_SNIFFER BIT(5)
28#define CDNS_MCP_CONFIG_SSPMOD BIT(4)
29#define CDNS_MCP_CONFIG_CMD BIT(3)
30#define CDNS_MCP_CONFIG_OP GENMASK(2, 0)
31#define CDNS_MCP_CONFIG_OP_NORMAL 0
32
33#define CDNS_MCP_CONTROL 0x4
34
35#define CDNS_MCP_CONTROL_RST_DELAY GENMASK(10, 8)
36#define CDNS_MCP_CONTROL_CMD_RST BIT(7)
37#define CDNS_MCP_CONTROL_SOFT_RST BIT(6)
38#define CDNS_MCP_CONTROL_SW_RST BIT(5)
39#define CDNS_MCP_CONTROL_HW_RST BIT(4)
40#define CDNS_MCP_CONTROL_CLK_PAUSE BIT(3)
41#define CDNS_MCP_CONTROL_CLK_STOP_CLR BIT(2)
42#define CDNS_MCP_CONTROL_CMD_ACCEPT BIT(1)
43#define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0)
44
Vinod Koul2f52a512017-12-14 11:19:41 +053045#define CDNS_MCP_CMDCTRL 0x8
46#define CDNS_MCP_SSPSTAT 0xC
47#define CDNS_MCP_FRAME_SHAPE 0x10
48#define CDNS_MCP_FRAME_SHAPE_INIT 0x14
49
50#define CDNS_MCP_CONFIG_UPDATE 0x18
51#define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0)
52
53#define CDNS_MCP_PHYCTRL 0x1C
54#define CDNS_MCP_SSP_CTRL0 0x20
55#define CDNS_MCP_SSP_CTRL1 0x28
56#define CDNS_MCP_CLK_CTRL0 0x30
57#define CDNS_MCP_CLK_CTRL1 0x38
58
59#define CDNS_MCP_STAT 0x40
60
61#define CDNS_MCP_STAT_ACTIVE_BANK BIT(20)
62#define CDNS_MCP_STAT_CLK_STOP BIT(16)
63
64#define CDNS_MCP_INTSTAT 0x44
65#define CDNS_MCP_INTMASK 0x48
66
67#define CDNS_MCP_INT_IRQ BIT(31)
68#define CDNS_MCP_INT_WAKEUP BIT(16)
69#define CDNS_MCP_INT_SLAVE_RSVD BIT(15)
70#define CDNS_MCP_INT_SLAVE_ALERT BIT(14)
71#define CDNS_MCP_INT_SLAVE_ATTACH BIT(13)
72#define CDNS_MCP_INT_SLAVE_NATTACH BIT(12)
73#define CDNS_MCP_INT_SLAVE_MASK GENMASK(15, 12)
74#define CDNS_MCP_INT_DPINT BIT(11)
75#define CDNS_MCP_INT_CTRL_CLASH BIT(10)
76#define CDNS_MCP_INT_DATA_CLASH BIT(9)
77#define CDNS_MCP_INT_CMD_ERR BIT(7)
78#define CDNS_MCP_INT_RX_WL BIT(2)
79#define CDNS_MCP_INT_TXE BIT(1)
80
81#define CDNS_MCP_INTSET 0x4C
82
83#define CDNS_SDW_SLAVE_STAT 0x50
84#define CDNS_MCP_SLAVE_STAT_MASK BIT(1, 0)
85
86#define CDNS_MCP_SLAVE_INTSTAT0 0x54
87#define CDNS_MCP_SLAVE_INTSTAT1 0x58
88#define CDNS_MCP_SLAVE_INTSTAT_NPRESENT BIT(0)
89#define CDNS_MCP_SLAVE_INTSTAT_ATTACHED BIT(1)
90#define CDNS_MCP_SLAVE_INTSTAT_ALERT BIT(2)
91#define CDNS_MCP_SLAVE_INTSTAT_RESERVED BIT(3)
92#define CDNS_MCP_SLAVE_STATUS_BITS GENMASK(3, 0)
93#define CDNS_MCP_SLAVE_STATUS_NUM 4
94
95#define CDNS_MCP_SLAVE_INTMASK0 0x5C
96#define CDNS_MCP_SLAVE_INTMASK1 0x60
97
98#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(30, 0)
99#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(16, 0)
100
101#define CDNS_MCP_PORT_INTSTAT 0x64
102#define CDNS_MCP_PDI_STAT 0x6C
103
104#define CDNS_MCP_FIFOLEVEL 0x78
105#define CDNS_MCP_FIFOSTAT 0x7C
106#define CDNS_MCP_RX_FIFO_AVAIL GENMASK(5, 0)
107
108#define CDNS_MCP_CMD_BASE 0x80
109#define CDNS_MCP_RESP_BASE 0x80
110#define CDNS_MCP_CMD_LEN 0x20
111#define CDNS_MCP_CMD_WORD_LEN 0x4
112
113#define CDNS_MCP_CMD_SSP_TAG BIT(31)
114#define CDNS_MCP_CMD_COMMAND GENMASK(30, 28)
115#define CDNS_MCP_CMD_DEV_ADDR GENMASK(27, 24)
116#define CDNS_MCP_CMD_REG_ADDR_H GENMASK(23, 16)
117#define CDNS_MCP_CMD_REG_ADDR_L GENMASK(15, 8)
118#define CDNS_MCP_CMD_REG_DATA GENMASK(7, 0)
119
120#define CDNS_MCP_CMD_READ 2
121#define CDNS_MCP_CMD_WRITE 3
122
123#define CDNS_MCP_RESP_RDATA GENMASK(15, 8)
124#define CDNS_MCP_RESP_ACK BIT(0)
125#define CDNS_MCP_RESP_NACK BIT(1)
126
127#define CDNS_DP_SIZE 128
128
129#define CDNS_DPN_B0_CONFIG(n) (0x100 + CDNS_DP_SIZE * (n))
130#define CDNS_DPN_B0_CH_EN(n) (0x104 + CDNS_DP_SIZE * (n))
131#define CDNS_DPN_B0_SAMPLE_CTRL(n) (0x108 + CDNS_DP_SIZE * (n))
132#define CDNS_DPN_B0_OFFSET_CTRL(n) (0x10C + CDNS_DP_SIZE * (n))
133#define CDNS_DPN_B0_HCTRL(n) (0x110 + CDNS_DP_SIZE * (n))
134#define CDNS_DPN_B0_ASYNC_CTRL(n) (0x114 + CDNS_DP_SIZE * (n))
135
136#define CDNS_DPN_B1_CONFIG(n) (0x118 + CDNS_DP_SIZE * (n))
137#define CDNS_DPN_B1_CH_EN(n) (0x11C + CDNS_DP_SIZE * (n))
138#define CDNS_DPN_B1_SAMPLE_CTRL(n) (0x120 + CDNS_DP_SIZE * (n))
139#define CDNS_DPN_B1_OFFSET_CTRL(n) (0x124 + CDNS_DP_SIZE * (n))
140#define CDNS_DPN_B1_HCTRL(n) (0x128 + CDNS_DP_SIZE * (n))
141#define CDNS_DPN_B1_ASYNC_CTRL(n) (0x12C + CDNS_DP_SIZE * (n))
142
143#define CDNS_DPN_CONFIG_BPM BIT(18)
144#define CDNS_DPN_CONFIG_BGC GENMASK(17, 16)
145#define CDNS_DPN_CONFIG_WL GENMASK(12, 8)
146#define CDNS_DPN_CONFIG_PORT_DAT GENMASK(3, 2)
147#define CDNS_DPN_CONFIG_PORT_FLOW GENMASK(1, 0)
148
149#define CDNS_DPN_SAMPLE_CTRL_SI GENMASK(15, 0)
150
151#define CDNS_DPN_OFFSET_CTRL_1 GENMASK(7, 0)
152#define CDNS_DPN_OFFSET_CTRL_2 GENMASK(15, 8)
153
154#define CDNS_DPN_HCTRL_HSTOP GENMASK(3, 0)
155#define CDNS_DPN_HCTRL_HSTART GENMASK(7, 4)
156#define CDNS_DPN_HCTRL_LCTRL GENMASK(10, 8)
157
158#define CDNS_PORTCTRL 0x130
159#define CDNS_PORTCTRL_DIRN BIT(7)
160#define CDNS_PORTCTRL_BANK_INVERT BIT(8)
161
162#define CDNS_PORT_OFFSET 0x80
163
164#define CDNS_PDI_CONFIG(n) (0x1100 + (n) * 16)
165
166#define CDNS_PDI_CONFIG_SOFT_RESET BIT(24)
167#define CDNS_PDI_CONFIG_CHANNEL GENMASK(15, 8)
168#define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
169
170/* Driver defaults */
171
172#define CDNS_DEFAULT_CLK_DIVIDER 0
173#define CDNS_DEFAULT_FRAME_SHAPE 0x30
174#define CDNS_DEFAULT_SSP_INTERVAL 0x18
175#define CDNS_TX_TIMEOUT 2000
176
177#define CDNS_PCM_PDI_OFFSET 0x2
178#define CDNS_PDM_PDI_OFFSET 0x6
179
180#define CDNS_SCP_RX_FIFOLEVEL 0x2
181
182/*
183 * register accessor helpers
184 */
185static inline u32 cdns_readl(struct sdw_cdns *cdns, int offset)
186{
187 return readl(cdns->registers + offset);
188}
189
190static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value)
191{
192 writel(value, cdns->registers + offset);
193}
194
195static inline void cdns_updatel(struct sdw_cdns *cdns,
196 int offset, u32 mask, u32 val)
197{
198 u32 tmp;
199
200 tmp = cdns_readl(cdns, offset);
201 tmp = (tmp & ~mask) | val;
202 cdns_writel(cdns, offset, tmp);
203}
204
205static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
206{
207 int timeout = 10;
208 u32 reg_read;
209
210 writel(value, cdns->registers + offset);
211
212 /* Wait for bit to be self cleared */
213 do {
214 reg_read = readl(cdns->registers + offset);
215 if ((reg_read & value) == 0)
216 return 0;
217
218 timeout--;
219 udelay(50);
220 } while (timeout != 0);
221
222 return -EAGAIN;
223}
224
225/*
Sanyog Kale956baa12017-12-14 11:19:42 +0530226 * IO Calls
227 */
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500228static enum sdw_command_response
229cdns_fill_msg_resp(struct sdw_cdns *cdns,
230 struct sdw_msg *msg, int count, int offset)
Sanyog Kale956baa12017-12-14 11:19:42 +0530231{
232 int nack = 0, no_ack = 0;
233 int i;
234
235 /* check message response */
236 for (i = 0; i < count; i++) {
237 if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) {
238 no_ack = 1;
239 dev_dbg(cdns->dev, "Msg Ack not received\n");
240 if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) {
241 nack = 1;
242 dev_err(cdns->dev, "Msg NACK received\n");
243 }
244 }
245 }
246
247 if (nack) {
248 dev_err(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num);
249 return SDW_CMD_FAIL;
250 } else if (no_ack) {
251 dev_dbg(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num);
252 return SDW_CMD_IGNORED;
253 }
254
255 /* fill response */
256 for (i = 0; i < count; i++)
257 msg->buf[i + offset] = cdns->response_buf[i] >>
258 SDW_REG_SHIFT(CDNS_MCP_RESP_RDATA);
259
260 return SDW_CMD_OK;
261}
262
263static enum sdw_command_response
264_cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500265 int offset, int count, bool defer)
Sanyog Kale956baa12017-12-14 11:19:42 +0530266{
267 unsigned long time;
268 u32 base, i, data;
269 u16 addr;
270
271 /* Program the watermark level for RX FIFO */
272 if (cdns->msg_count != count) {
273 cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, count);
274 cdns->msg_count = count;
275 }
276
277 base = CDNS_MCP_CMD_BASE;
278 addr = msg->addr;
279
280 for (i = 0; i < count; i++) {
281 data = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR);
282 data |= cmd << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND);
283 data |= addr++ << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L);
284
285 if (msg->flags == SDW_MSG_FLAG_WRITE)
286 data |= msg->buf[i + offset];
287
288 data |= msg->ssp_sync << SDW_REG_SHIFT(CDNS_MCP_CMD_SSP_TAG);
289 cdns_writel(cdns, base, data);
290 base += CDNS_MCP_CMD_WORD_LEN;
291 }
292
293 if (defer)
294 return SDW_CMD_OK;
295
296 /* wait for timeout or response */
297 time = wait_for_completion_timeout(&cdns->tx_complete,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500298 msecs_to_jiffies(CDNS_TX_TIMEOUT));
Sanyog Kale956baa12017-12-14 11:19:42 +0530299 if (!time) {
300 dev_err(cdns->dev, "IO transfer timed out\n");
301 msg->len = 0;
302 return SDW_CMD_TIMEOUT;
303 }
304
305 return cdns_fill_msg_resp(cdns, msg, count, offset);
306}
307
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500308static enum sdw_command_response
309cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg)
Sanyog Kale956baa12017-12-14 11:19:42 +0530310{
311 int nack = 0, no_ack = 0;
312 unsigned long time;
313 u32 data[2], base;
314 int i;
315
316 /* Program the watermark level for RX FIFO */
317 if (cdns->msg_count != CDNS_SCP_RX_FIFOLEVEL) {
318 cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, CDNS_SCP_RX_FIFOLEVEL);
319 cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL;
320 }
321
322 data[0] = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR);
323 data[0] |= 0x3 << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND);
324 data[1] = data[0];
325
326 data[0] |= SDW_SCP_ADDRPAGE1 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L);
327 data[1] |= SDW_SCP_ADDRPAGE2 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L);
328
329 data[0] |= msg->addr_page1;
330 data[1] |= msg->addr_page2;
331
332 base = CDNS_MCP_CMD_BASE;
333 cdns_writel(cdns, base, data[0]);
334 base += CDNS_MCP_CMD_WORD_LEN;
335 cdns_writel(cdns, base, data[1]);
336
337 time = wait_for_completion_timeout(&cdns->tx_complete,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500338 msecs_to_jiffies(CDNS_TX_TIMEOUT));
Sanyog Kale956baa12017-12-14 11:19:42 +0530339 if (!time) {
340 dev_err(cdns->dev, "SCP Msg trf timed out\n");
341 msg->len = 0;
342 return SDW_CMD_TIMEOUT;
343 }
344
345 /* check response the writes */
346 for (i = 0; i < 2; i++) {
347 if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) {
348 no_ack = 1;
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500349 dev_err(cdns->dev, "Program SCP Ack not received\n");
Sanyog Kale956baa12017-12-14 11:19:42 +0530350 if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) {
351 nack = 1;
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500352 dev_err(cdns->dev, "Program SCP NACK received\n");
Sanyog Kale956baa12017-12-14 11:19:42 +0530353 }
354 }
355 }
356
357 /* For NACK, NO ack, don't return err if we are in Broadcast mode */
358 if (nack) {
359 dev_err(cdns->dev,
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500360 "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
Sanyog Kale956baa12017-12-14 11:19:42 +0530361 return SDW_CMD_FAIL;
362 } else if (no_ack) {
363 dev_dbg(cdns->dev,
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500364 "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
Sanyog Kale956baa12017-12-14 11:19:42 +0530365 return SDW_CMD_IGNORED;
366 }
367
368 return SDW_CMD_OK;
369}
370
371static int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd)
372{
373 int ret;
374
375 if (msg->page) {
376 ret = cdns_program_scp_addr(cdns, msg);
377 if (ret) {
378 msg->len = 0;
379 return ret;
380 }
381 }
382
383 switch (msg->flags) {
384 case SDW_MSG_FLAG_READ:
385 *cmd = CDNS_MCP_CMD_READ;
386 break;
387
388 case SDW_MSG_FLAG_WRITE:
389 *cmd = CDNS_MCP_CMD_WRITE;
390 break;
391
392 default:
393 dev_err(cdns->dev, "Invalid msg cmd: %d\n", msg->flags);
394 return -EINVAL;
395 }
396
397 return 0;
398}
399
Shreyas NCc91605f2018-04-26 18:38:43 +0530400enum sdw_command_response
Sanyog Kale956baa12017-12-14 11:19:42 +0530401cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
402{
403 struct sdw_cdns *cdns = bus_to_cdns(bus);
404 int cmd = 0, ret, i;
405
406 ret = cdns_prep_msg(cdns, msg, &cmd);
407 if (ret)
408 return SDW_CMD_FAIL_OTHER;
409
410 for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) {
411 ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500412 CDNS_MCP_CMD_LEN, false);
Sanyog Kale956baa12017-12-14 11:19:42 +0530413 if (ret < 0)
414 goto exit;
415 }
416
417 if (!(msg->len % CDNS_MCP_CMD_LEN))
418 goto exit;
419
420 ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500421 msg->len % CDNS_MCP_CMD_LEN, false);
Sanyog Kale956baa12017-12-14 11:19:42 +0530422
423exit:
424 return ret;
425}
Shreyas NCc91605f2018-04-26 18:38:43 +0530426EXPORT_SYMBOL(cdns_xfer_msg);
Sanyog Kale956baa12017-12-14 11:19:42 +0530427
Shreyas NCc91605f2018-04-26 18:38:43 +0530428enum sdw_command_response
Sanyog Kale956baa12017-12-14 11:19:42 +0530429cdns_xfer_msg_defer(struct sdw_bus *bus,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500430 struct sdw_msg *msg, struct sdw_defer *defer)
Sanyog Kale956baa12017-12-14 11:19:42 +0530431{
432 struct sdw_cdns *cdns = bus_to_cdns(bus);
433 int cmd = 0, ret;
434
435 /* for defer only 1 message is supported */
436 if (msg->len > 1)
437 return -ENOTSUPP;
438
439 ret = cdns_prep_msg(cdns, msg, &cmd);
440 if (ret)
441 return SDW_CMD_FAIL_OTHER;
442
443 cdns->defer = defer;
444 cdns->defer->length = msg->len;
445
446 return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true);
447}
Shreyas NCc91605f2018-04-26 18:38:43 +0530448EXPORT_SYMBOL(cdns_xfer_msg_defer);
Sanyog Kale956baa12017-12-14 11:19:42 +0530449
Shreyas NCc91605f2018-04-26 18:38:43 +0530450enum sdw_command_response
Sanyog Kale956baa12017-12-14 11:19:42 +0530451cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
452{
453 struct sdw_cdns *cdns = bus_to_cdns(bus);
454 struct sdw_msg msg;
455
456 /* Create dummy message with valid device number */
457 memset(&msg, 0, sizeof(msg));
458 msg.dev_num = dev_num;
459
460 return cdns_program_scp_addr(cdns, &msg);
461}
Shreyas NCc91605f2018-04-26 18:38:43 +0530462EXPORT_SYMBOL(cdns_reset_page_addr);
Sanyog Kale956baa12017-12-14 11:19:42 +0530463
464/*
Vinod Koul2f52a512017-12-14 11:19:41 +0530465 * IRQ handling
466 */
467
Sanyog Kale956baa12017-12-14 11:19:42 +0530468static void cdns_read_response(struct sdw_cdns *cdns)
469{
470 u32 num_resp, cmd_base;
471 int i;
472
473 num_resp = cdns_readl(cdns, CDNS_MCP_FIFOSTAT);
474 num_resp &= CDNS_MCP_RX_FIFO_AVAIL;
475
476 cmd_base = CDNS_MCP_CMD_BASE;
477
478 for (i = 0; i < num_resp; i++) {
479 cdns->response_buf[i] = cdns_readl(cdns, cmd_base);
480 cmd_base += CDNS_MCP_CMD_WORD_LEN;
481 }
482}
483
Vinod Koul2f52a512017-12-14 11:19:41 +0530484static int cdns_update_slave_status(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500485 u32 slave0, u32 slave1)
Vinod Koul2f52a512017-12-14 11:19:41 +0530486{
487 enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
488 bool is_slave = false;
489 u64 slave, mask;
490 int i, set_status;
491
492 /* combine the two status */
493 slave = ((u64)slave1 << 32) | slave0;
494 memset(status, 0, sizeof(status));
495
496 for (i = 0; i <= SDW_MAX_DEVICES; i++) {
497 mask = (slave >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) &
498 CDNS_MCP_SLAVE_STATUS_BITS;
499 if (!mask)
500 continue;
501
502 is_slave = true;
503 set_status = 0;
504
505 if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
506 status[i] = SDW_SLAVE_RESERVED;
507 set_status++;
508 }
509
510 if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) {
511 status[i] = SDW_SLAVE_ATTACHED;
512 set_status++;
513 }
514
515 if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) {
516 status[i] = SDW_SLAVE_ALERT;
517 set_status++;
518 }
519
520 if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) {
521 status[i] = SDW_SLAVE_UNATTACHED;
522 set_status++;
523 }
524
525 /* first check if Slave reported multiple status */
526 if (set_status > 1) {
527 dev_warn(cdns->dev,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500528 "Slave reported multiple Status: %d\n",
529 status[i]);
Vinod Koul2f52a512017-12-14 11:19:41 +0530530 /*
531 * TODO: we need to reread the status here by
532 * issuing a PING cmd
533 */
534 }
535 }
536
537 if (is_slave)
538 return sdw_handle_slave_status(&cdns->bus, status);
539
540 return 0;
541}
542
543/**
544 * sdw_cdns_irq() - Cadence interrupt handler
545 * @irq: irq number
546 * @dev_id: irq context
547 */
548irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
549{
550 struct sdw_cdns *cdns = dev_id;
551 u32 int_status;
552 int ret = IRQ_HANDLED;
553
554 /* Check if the link is up */
555 if (!cdns->link_up)
556 return IRQ_NONE;
557
558 int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT);
559
560 if (!(int_status & CDNS_MCP_INT_IRQ))
561 return IRQ_NONE;
562
Sanyog Kale956baa12017-12-14 11:19:42 +0530563 if (int_status & CDNS_MCP_INT_RX_WL) {
564 cdns_read_response(cdns);
565
566 if (cdns->defer) {
567 cdns_fill_msg_resp(cdns, cdns->defer->msg,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500568 cdns->defer->length, 0);
Sanyog Kale956baa12017-12-14 11:19:42 +0530569 complete(&cdns->defer->complete);
570 cdns->defer = NULL;
Pierre-Louis Bossartf6e20962019-05-01 10:57:42 -0500571 } else {
Sanyog Kale956baa12017-12-14 11:19:42 +0530572 complete(&cdns->tx_complete);
Pierre-Louis Bossartf6e20962019-05-01 10:57:42 -0500573 }
Sanyog Kale956baa12017-12-14 11:19:42 +0530574 }
575
Vinod Koul2f52a512017-12-14 11:19:41 +0530576 if (int_status & CDNS_MCP_INT_CTRL_CLASH) {
Vinod Koul2f52a512017-12-14 11:19:41 +0530577 /* Slave is driving bit slot during control word */
578 dev_err_ratelimited(cdns->dev, "Bus clash for control word\n");
579 int_status |= CDNS_MCP_INT_CTRL_CLASH;
580 }
581
582 if (int_status & CDNS_MCP_INT_DATA_CLASH) {
583 /*
584 * Multiple slaves trying to drive bit slot, or issue with
585 * ownership of data bits or Slave gone bonkers
586 */
587 dev_err_ratelimited(cdns->dev, "Bus clash for data word\n");
588 int_status |= CDNS_MCP_INT_DATA_CLASH;
589 }
590
591 if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
592 /* Mask the Slave interrupt and wake thread */
593 cdns_updatel(cdns, CDNS_MCP_INTMASK,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500594 CDNS_MCP_INT_SLAVE_MASK, 0);
Vinod Koul2f52a512017-12-14 11:19:41 +0530595
596 int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
597 ret = IRQ_WAKE_THREAD;
598 }
599
600 cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
601 return ret;
602}
603EXPORT_SYMBOL(sdw_cdns_irq);
604
605/**
606 * sdw_cdns_thread() - Cadence irq thread handler
607 * @irq: irq number
608 * @dev_id: irq context
609 */
610irqreturn_t sdw_cdns_thread(int irq, void *dev_id)
611{
612 struct sdw_cdns *cdns = dev_id;
613 u32 slave0, slave1;
614
615 dev_dbg(cdns->dev, "Slave status change\n");
616
617 slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
618 slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
619
620 cdns_update_slave_status(cdns, slave0, slave1);
621 cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0);
622 cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1);
623
624 /* clear and unmask Slave interrupt now */
625 cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK);
626 cdns_updatel(cdns, CDNS_MCP_INTMASK,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500627 CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);
Vinod Koul2f52a512017-12-14 11:19:41 +0530628
629 return IRQ_HANDLED;
630}
631EXPORT_SYMBOL(sdw_cdns_thread);
632
633/*
634 * init routines
635 */
Sanyog Kale956baa12017-12-14 11:19:42 +0530636static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
637{
638 u32 mask;
639
640 cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500641 CDNS_MCP_SLAVE_INTMASK0_MASK);
Sanyog Kale956baa12017-12-14 11:19:42 +0530642 cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500643 CDNS_MCP_SLAVE_INTMASK1_MASK);
Sanyog Kale956baa12017-12-14 11:19:42 +0530644
645 mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
646 CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH |
647 CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
648 CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT;
649
650 cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
651
652 return 0;
653}
654
655/**
656 * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
657 * @cdns: Cadence instance
658 */
659int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
660{
661 int ret;
662
663 _cdns_enable_interrupt(cdns);
664 ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500665 CDNS_MCP_CONFIG_UPDATE_BIT);
Sanyog Kale956baa12017-12-14 11:19:42 +0530666 if (ret < 0)
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500667 dev_err(cdns->dev, "Config update timedout\n");
Sanyog Kale956baa12017-12-14 11:19:42 +0530668
669 return ret;
670}
671EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
Vinod Koul2f52a512017-12-14 11:19:41 +0530672
Vinod Koul07abeff2018-04-26 18:38:48 +0530673static int cdns_allocate_pdi(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500674 struct sdw_cdns_pdi **stream,
675 u32 num, u32 pdi_offset)
Vinod Koul07abeff2018-04-26 18:38:48 +0530676{
677 struct sdw_cdns_pdi *pdi;
678 int i;
679
680 if (!num)
681 return 0;
682
683 pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL);
684 if (!pdi)
685 return -ENOMEM;
686
687 for (i = 0; i < num; i++) {
688 pdi[i].num = i + pdi_offset;
689 pdi[i].assigned = false;
690 }
691
692 *stream = pdi;
693 return 0;
694}
695
696/**
697 * sdw_cdns_pdi_init() - PDI initialization routine
698 *
699 * @cdns: Cadence instance
700 * @config: Stream configurations
701 */
702int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500703 struct sdw_cdns_stream_config config)
Vinod Koul07abeff2018-04-26 18:38:48 +0530704{
705 struct sdw_cdns_streams *stream;
706 int offset, i, ret;
707
708 cdns->pcm.num_bd = config.pcm_bd;
709 cdns->pcm.num_in = config.pcm_in;
710 cdns->pcm.num_out = config.pcm_out;
711 cdns->pdm.num_bd = config.pdm_bd;
712 cdns->pdm.num_in = config.pdm_in;
713 cdns->pdm.num_out = config.pdm_out;
714
715 /* Allocate PDIs for PCMs */
716 stream = &cdns->pcm;
717
718 /* First two PDIs are reserved for bulk transfers */
719 stream->num_bd -= CDNS_PCM_PDI_OFFSET;
720 offset = CDNS_PCM_PDI_OFFSET;
721
722 ret = cdns_allocate_pdi(cdns, &stream->bd,
723 stream->num_bd, offset);
724 if (ret)
725 return ret;
726
727 offset += stream->num_bd;
728
729 ret = cdns_allocate_pdi(cdns, &stream->in,
730 stream->num_in, offset);
731 if (ret)
732 return ret;
733
734 offset += stream->num_in;
735
736 ret = cdns_allocate_pdi(cdns, &stream->out,
737 stream->num_out, offset);
738 if (ret)
739 return ret;
740
741 /* Update total number of PCM PDIs */
742 stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
743 cdns->num_ports = stream->num_pdi;
744
745 /* Allocate PDIs for PDMs */
746 stream = &cdns->pdm;
747 offset = CDNS_PDM_PDI_OFFSET;
748 ret = cdns_allocate_pdi(cdns, &stream->bd,
749 stream->num_bd, offset);
750 if (ret)
751 return ret;
752
753 offset += stream->num_bd;
754
755 ret = cdns_allocate_pdi(cdns, &stream->in,
756 stream->num_in, offset);
757 if (ret)
758 return ret;
759
760 offset += stream->num_in;
761
762 ret = cdns_allocate_pdi(cdns, &stream->out,
763 stream->num_out, offset);
764 if (ret)
765 return ret;
766
767 /* Update total number of PDM PDIs */
768 stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
769 cdns->num_ports += stream->num_pdi;
770
771 cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500772 sizeof(*cdns->ports), GFP_KERNEL);
Vinod Koul07abeff2018-04-26 18:38:48 +0530773 if (!cdns->ports) {
774 ret = -ENOMEM;
775 return ret;
776 }
777
778 for (i = 0; i < cdns->num_ports; i++) {
779 cdns->ports[i].assigned = false;
780 cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */
781 }
782
783 return 0;
784}
785EXPORT_SYMBOL(sdw_cdns_pdi_init);
786
Vinod Koul2f52a512017-12-14 11:19:41 +0530787/**
788 * sdw_cdns_init() - Cadence initialization
789 * @cdns: Cadence instance
790 */
791int sdw_cdns_init(struct sdw_cdns *cdns)
792{
793 u32 val;
794 int ret;
795
796 /* Exit clock stop */
797 ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500798 CDNS_MCP_CONTROL_CLK_STOP_CLR);
Vinod Koul2f52a512017-12-14 11:19:41 +0530799 if (ret < 0) {
800 dev_err(cdns->dev, "Couldn't exit from clock stop\n");
801 return ret;
802 }
803
804 /* Set clock divider */
805 val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
806 val |= CDNS_DEFAULT_CLK_DIVIDER;
807 cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
808
809 /* Set the default frame shape */
810 cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE);
811
812 /* Set SSP interval to default value */
813 cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
814 cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL);
815
816 /* Set cmd accept mode */
817 cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500818 CDNS_MCP_CONTROL_CMD_ACCEPT);
Vinod Koul2f52a512017-12-14 11:19:41 +0530819
820 /* Configure mcp config */
821 val = cdns_readl(cdns, CDNS_MCP_CONFIG);
822
823 /* Set Max cmd retry to 15 */
824 val |= CDNS_MCP_CONFIG_MCMD_RETRY;
825
826 /* Set frame delay between PREQ and ping frame to 15 frames */
827 val |= 0xF << SDW_REG_SHIFT(CDNS_MCP_CONFIG_MPREQ_DELAY);
828
829 /* Disable auto bus release */
830 val &= ~CDNS_MCP_CONFIG_BUS_REL;
831
832 /* Disable sniffer mode */
833 val &= ~CDNS_MCP_CONFIG_SNIFFER;
834
835 /* Set cmd mode for Tx and Rx cmds */
836 val &= ~CDNS_MCP_CONFIG_CMD;
837
838 /* Set operation to normal */
839 val &= ~CDNS_MCP_CONFIG_OP;
840 val |= CDNS_MCP_CONFIG_OP_NORMAL;
841
842 cdns_writel(cdns, CDNS_MCP_CONFIG, val);
843
844 return 0;
845}
846EXPORT_SYMBOL(sdw_cdns_init);
847
Vinod Koul07abeff2018-04-26 18:38:48 +0530848int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
849{
850 struct sdw_cdns *cdns = bus_to_cdns(bus);
851 int mcp_clkctrl_off, mcp_clkctrl;
852 int divider;
853
854 if (!params->curr_dr_freq) {
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500855 dev_err(cdns->dev, "NULL curr_dr_freq\n");
Vinod Koul07abeff2018-04-26 18:38:48 +0530856 return -EINVAL;
857 }
858
859 divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
860
861 if (params->next_bank)
862 mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
863 else
864 mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
865
866 mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
867 mcp_clkctrl |= divider;
868 cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
869
870 return 0;
871}
872EXPORT_SYMBOL(cdns_bus_conf);
873
874static int cdns_port_params(struct sdw_bus *bus,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500875 struct sdw_port_params *p_params, unsigned int bank)
Vinod Koul07abeff2018-04-26 18:38:48 +0530876{
877 struct sdw_cdns *cdns = bus_to_cdns(bus);
878 int dpn_config = 0, dpn_config_off;
879
880 if (bank)
881 dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num);
882 else
883 dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num);
884
885 dpn_config = cdns_readl(cdns, dpn_config_off);
886
887 dpn_config |= ((p_params->bps - 1) <<
888 SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL));
889 dpn_config |= (p_params->flow_mode <<
890 SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW));
891 dpn_config |= (p_params->data_mode <<
892 SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT));
893
894 cdns_writel(cdns, dpn_config_off, dpn_config);
895
896 return 0;
897}
898
899static int cdns_transport_params(struct sdw_bus *bus,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500900 struct sdw_transport_params *t_params,
901 enum sdw_reg_bank bank)
Vinod Koul07abeff2018-04-26 18:38:48 +0530902{
903 struct sdw_cdns *cdns = bus_to_cdns(bus);
904 int dpn_offsetctrl = 0, dpn_offsetctrl_off;
905 int dpn_config = 0, dpn_config_off;
906 int dpn_hctrl = 0, dpn_hctrl_off;
907 int num = t_params->port_num;
908 int dpn_samplectrl_off;
909
910 /*
911 * Note: Only full data port is supported on the Master side for
912 * both PCM and PDM ports.
913 */
914
915 if (bank) {
916 dpn_config_off = CDNS_DPN_B1_CONFIG(num);
917 dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num);
918 dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num);
919 dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num);
920 } else {
921 dpn_config_off = CDNS_DPN_B0_CONFIG(num);
922 dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num);
923 dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num);
924 dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num);
925 }
926
927 dpn_config = cdns_readl(cdns, dpn_config_off);
928
929 dpn_config |= (t_params->blk_grp_ctrl <<
930 SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC));
931 dpn_config |= (t_params->blk_pkg_mode <<
932 SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM));
933 cdns_writel(cdns, dpn_config_off, dpn_config);
934
935 dpn_offsetctrl |= (t_params->offset1 <<
936 SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1));
937 dpn_offsetctrl |= (t_params->offset2 <<
938 SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2));
939 cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl);
940
941 dpn_hctrl |= (t_params->hstart <<
942 SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART));
943 dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP));
944 dpn_hctrl |= (t_params->lane_ctrl <<
945 SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL));
946
947 cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl);
948 cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1));
949
950 return 0;
951}
952
953static int cdns_port_enable(struct sdw_bus *bus,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500954 struct sdw_enable_ch *enable_ch, unsigned int bank)
Vinod Koul07abeff2018-04-26 18:38:48 +0530955{
956 struct sdw_cdns *cdns = bus_to_cdns(bus);
957 int dpn_chnen_off, ch_mask;
958
959 if (bank)
960 dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->port_num);
961 else
962 dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->port_num);
963
964 ch_mask = enable_ch->ch_mask * enable_ch->enable;
965 cdns_writel(cdns, dpn_chnen_off, ch_mask);
966
967 return 0;
968}
969
970static const struct sdw_master_port_ops cdns_port_ops = {
971 .dpn_set_port_params = cdns_port_params,
972 .dpn_set_port_transport_params = cdns_transport_params,
973 .dpn_port_enable_ch = cdns_port_enable,
974};
975
Sanyog Kale956baa12017-12-14 11:19:42 +0530976/**
977 * sdw_cdns_probe() - Cadence probe routine
978 * @cdns: Cadence instance
979 */
980int sdw_cdns_probe(struct sdw_cdns *cdns)
981{
982 init_completion(&cdns->tx_complete);
Vinod Koul07abeff2018-04-26 18:38:48 +0530983 cdns->bus.port_ops = &cdns_port_ops;
Sanyog Kale956baa12017-12-14 11:19:42 +0530984
985 return 0;
986}
987EXPORT_SYMBOL(sdw_cdns_probe);
988
Vinod Koul5d6b3c82018-04-26 18:38:53 +0530989int cdns_set_sdw_stream(struct snd_soc_dai *dai,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -0500990 void *stream, bool pcm, int direction)
Vinod Koul5d6b3c82018-04-26 18:38:53 +0530991{
992 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
993 struct sdw_cdns_dma_data *dma;
994
995 dma = kzalloc(sizeof(*dma), GFP_KERNEL);
996 if (!dma)
997 return -ENOMEM;
998
999 if (pcm)
1000 dma->stream_type = SDW_STREAM_PCM;
1001 else
1002 dma->stream_type = SDW_STREAM_PDM;
1003
1004 dma->bus = &cdns->bus;
1005 dma->link_id = cdns->instance;
1006
1007 dma->stream = stream;
1008
1009 if (direction == SNDRV_PCM_STREAM_PLAYBACK)
1010 dai->playback_dma_data = dma;
1011 else
1012 dai->capture_dma_data = dma;
1013
1014 return 0;
1015}
1016EXPORT_SYMBOL(cdns_set_sdw_stream);
1017
1018/**
1019 * cdns_find_pdi() - Find a free PDI
1020 *
1021 * @cdns: Cadence instance
1022 * @num: Number of PDIs
1023 * @pdi: PDI instances
1024 *
1025 * Find and return a free PDI for a given PDI array
1026 */
1027static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -05001028 unsigned int num,
1029 struct sdw_cdns_pdi *pdi)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301030{
1031 int i;
1032
1033 for (i = 0; i < num; i++) {
Pierre-Louis Bossart4c568602019-05-01 10:57:43 -05001034 if (pdi[i].assigned)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301035 continue;
1036 pdi[i].assigned = true;
1037 return &pdi[i];
1038 }
1039
1040 return NULL;
1041}
1042
1043/**
1044 * sdw_cdns_config_stream: Configure a stream
1045 *
1046 * @cdns: Cadence instance
1047 * @port: Cadence data port
1048 * @ch: Channel count
1049 * @dir: Data direction
1050 * @pdi: PDI to be used
1051 */
1052void sdw_cdns_config_stream(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -05001053 struct sdw_cdns_port *port,
1054 u32 ch, u32 dir, struct sdw_cdns_pdi *pdi)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301055{
1056 u32 offset, val = 0;
1057
1058 if (dir == SDW_DATA_DIR_RX)
1059 val = CDNS_PORTCTRL_DIRN;
1060
1061 offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET;
1062 cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val);
1063
1064 val = port->num;
1065 val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL);
1066 cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val);
1067}
1068EXPORT_SYMBOL(sdw_cdns_config_stream);
1069
1070/**
1071 * cdns_get_num_pdi() - Get number of PDIs required
1072 *
1073 * @cdns: Cadence instance
1074 * @pdi: PDI to be used
1075 * @num: Number of PDIs
1076 * @ch_count: Channel count
1077 */
1078static int cdns_get_num_pdi(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -05001079 struct sdw_cdns_pdi *pdi,
1080 unsigned int num, u32 ch_count)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301081{
1082 int i, pdis = 0;
1083
1084 for (i = 0; i < num; i++) {
Pierre-Louis Bossart4c568602019-05-01 10:57:43 -05001085 if (pdi[i].assigned)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301086 continue;
1087
1088 if (pdi[i].ch_count < ch_count)
1089 ch_count -= pdi[i].ch_count;
1090 else
1091 ch_count = 0;
1092
1093 pdis++;
1094
1095 if (!ch_count)
1096 break;
1097 }
1098
1099 if (ch_count)
1100 return 0;
1101
1102 return pdis;
1103}
1104
1105/**
1106 * sdw_cdns_get_stream() - Get stream information
1107 *
1108 * @cdns: Cadence instance
1109 * @stream: Stream to be allocated
1110 * @ch: Channel count
1111 * @dir: Data direction
1112 */
1113int sdw_cdns_get_stream(struct sdw_cdns *cdns,
1114 struct sdw_cdns_streams *stream,
1115 u32 ch, u32 dir)
1116{
1117 int pdis = 0;
1118
1119 if (dir == SDW_DATA_DIR_RX)
1120 pdis = cdns_get_num_pdi(cdns, stream->in, stream->num_in, ch);
1121 else
1122 pdis = cdns_get_num_pdi(cdns, stream->out, stream->num_out, ch);
1123
1124 /* check if we found PDI, else find in bi-directional */
1125 if (!pdis)
1126 pdis = cdns_get_num_pdi(cdns, stream->bd, stream->num_bd, ch);
1127
1128 return pdis;
1129}
1130EXPORT_SYMBOL(sdw_cdns_get_stream);
1131
1132/**
1133 * sdw_cdns_alloc_stream() - Allocate a stream
1134 *
1135 * @cdns: Cadence instance
1136 * @stream: Stream to be allocated
1137 * @port: Cadence data port
1138 * @ch: Channel count
1139 * @dir: Data direction
1140 */
1141int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -05001142 struct sdw_cdns_streams *stream,
1143 struct sdw_cdns_port *port, u32 ch, u32 dir)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301144{
1145 struct sdw_cdns_pdi *pdi = NULL;
1146
1147 if (dir == SDW_DATA_DIR_RX)
1148 pdi = cdns_find_pdi(cdns, stream->num_in, stream->in);
1149 else
1150 pdi = cdns_find_pdi(cdns, stream->num_out, stream->out);
1151
1152 /* check if we found a PDI, else find in bi-directional */
1153 if (!pdi)
1154 pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd);
1155
1156 if (!pdi)
1157 return -EIO;
1158
1159 port->pdi = pdi;
1160 pdi->l_ch_num = 0;
1161 pdi->h_ch_num = ch - 1;
1162 pdi->dir = dir;
1163 pdi->ch_count = ch;
1164
1165 return 0;
1166}
1167EXPORT_SYMBOL(sdw_cdns_alloc_stream);
1168
1169void sdw_cdns_shutdown(struct snd_pcm_substream *substream,
Pierre-Louis Bossartbbb63812019-05-01 10:57:41 -05001170 struct snd_soc_dai *dai)
Vinod Koul5d6b3c82018-04-26 18:38:53 +05301171{
1172 struct sdw_cdns_dma_data *dma;
1173
1174 dma = snd_soc_dai_get_dma_data(dai, substream);
1175 if (!dma)
1176 return;
1177
1178 snd_soc_dai_set_dma_data(dai, substream, NULL);
1179 kfree(dma);
1180}
1181EXPORT_SYMBOL(sdw_cdns_shutdown);
1182
Vinod Koul2f52a512017-12-14 11:19:41 +05301183MODULE_LICENSE("Dual BSD/GPL");
1184MODULE_DESCRIPTION("Cadence Soundwire Library");