blob: 05de8c44449f5836ec5bdba50faecb2075630a09 [file] [log] [blame]
Vinod Koul71bb8a12017-12-14 11:19:43 +05301// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2// Copyright(c) 2015-17 Intel Corporation.
3
4/*
5 * Soundwire Intel Master Driver
6 */
7
8#include <linux/acpi.h>
Pierre-Louis Bossart79ee6632019-08-21 13:58:20 -05009#include <linux/debugfs.h>
Vinod Koul71bb8a12017-12-14 11:19:43 +053010#include <linux/delay.h>
Paul Gortmaker4abbd782019-04-13 11:12:52 -040011#include <linux/module.h>
Vinod Koul71bb8a12017-12-14 11:19:43 +053012#include <linux/interrupt.h>
Pierre-Louis Bossartdf72b712019-09-16 13:57:38 -050013#include <linux/io.h>
Vinod Koul71bb8a12017-12-14 11:19:43 +053014#include <linux/platform_device.h>
Vinod Koul37a2d222018-04-26 18:38:58 +053015#include <sound/pcm_params.h>
Rander Wangab2c9132020-07-16 23:09:46 +080016#include <linux/pm_runtime.h>
Vinod Koul37a2d222018-04-26 18:38:58 +053017#include <sound/soc.h>
Vinod Koul71bb8a12017-12-14 11:19:43 +053018#include <linux/soundwire/sdw_registers.h>
19#include <linux/soundwire/sdw.h>
20#include <linux/soundwire/sdw_intel.h>
21#include "cadence_master.h"
Pierre-Louis Bossart79ee6632019-08-21 13:58:20 -050022#include "bus.h"
Vinod Koul71bb8a12017-12-14 11:19:43 +053023#include "intel.h"
24
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +080025#define INTEL_MASTER_SUSPEND_DELAY_MS 3000
26
27/*
28 * debug/config flags for the Intel SoundWire Master.
29 *
30 * Since we may have multiple masters active, we can have up to 8
31 * flags reused in each byte, with master0 using the ls-byte, etc.
32 */
33
Pierre-Louis Bossarta2d9c162020-08-17 23:29:17 +080034#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0)
35#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1)
36#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE BIT(2)
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +080037#define SDW_INTEL_MASTER_DISABLE_MULTI_LINK BIT(3)
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +080038
39static int md_flags;
40module_param_named(sdw_md_flags, md_flags, int, 0444);
41MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");
42
Vinod Koul71bb8a12017-12-14 11:19:43 +053043/* Intel SHIM Registers Definition */
44#define SDW_SHIM_LCAP 0x0
45#define SDW_SHIM_LCTL 0x4
46#define SDW_SHIM_IPPTR 0x8
47#define SDW_SHIM_SYNC 0xC
48
Pierre-Louis Bossart7cc6e312019-05-01 10:57:39 -050049#define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * (x))
50#define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * (x))
51#define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * (x))
52#define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * (x))
53#define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * (x))
54#define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * (x))
Vinod Koul71bb8a12017-12-14 11:19:43 +053055
Pierre-Louis Bossart7cc6e312019-05-01 10:57:39 -050056#define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * (x)) + (0x2 * (y)))
57#define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * (x)) + (0x2 * (y)))
58#define SDW_SHIM_PDMSCAP(x) (0x062 + 0x60 * (x))
59#define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * (x))
60#define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * (x))
Vinod Koul71bb8a12017-12-14 11:19:43 +053061
62#define SDW_SHIM_WAKEEN 0x190
63#define SDW_SHIM_WAKESTS 0x192
64
65#define SDW_SHIM_LCTL_SPA BIT(0)
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +080066#define SDW_SHIM_LCTL_SPA_MASK GENMASK(3, 0)
Vinod Koul71bb8a12017-12-14 11:19:43 +053067#define SDW_SHIM_LCTL_CPA BIT(8)
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +080068#define SDW_SHIM_LCTL_CPA_MASK GENMASK(11, 8)
Vinod Koul71bb8a12017-12-14 11:19:43 +053069
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +080070#define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1)
71#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1)
Vinod Koul71bb8a12017-12-14 11:19:43 +053072#define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0)
73#define SDW_SHIM_SYNC_SYNCCPU BIT(15)
74#define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16)
75#define SDW_SHIM_SYNC_CMDSYNC BIT(16)
76#define SDW_SHIM_SYNC_SYNCGO BIT(24)
77
78#define SDW_SHIM_PCMSCAP_ISS GENMASK(3, 0)
79#define SDW_SHIM_PCMSCAP_OSS GENMASK(7, 4)
80#define SDW_SHIM_PCMSCAP_BSS GENMASK(12, 8)
81
82#define SDW_SHIM_PCMSYCM_LCHN GENMASK(3, 0)
83#define SDW_SHIM_PCMSYCM_HCHN GENMASK(7, 4)
84#define SDW_SHIM_PCMSYCM_STREAM GENMASK(13, 8)
85#define SDW_SHIM_PCMSYCM_DIR BIT(15)
86
87#define SDW_SHIM_PDMSCAP_ISS GENMASK(3, 0)
88#define SDW_SHIM_PDMSCAP_OSS GENMASK(7, 4)
89#define SDW_SHIM_PDMSCAP_BSS GENMASK(12, 8)
90#define SDW_SHIM_PDMSCAP_CPSS GENMASK(15, 13)
91
92#define SDW_SHIM_IOCTL_MIF BIT(0)
93#define SDW_SHIM_IOCTL_CO BIT(1)
94#define SDW_SHIM_IOCTL_COE BIT(2)
95#define SDW_SHIM_IOCTL_DO BIT(3)
96#define SDW_SHIM_IOCTL_DOE BIT(4)
97#define SDW_SHIM_IOCTL_BKE BIT(5)
98#define SDW_SHIM_IOCTL_WPDD BIT(6)
99#define SDW_SHIM_IOCTL_CIBD BIT(8)
100#define SDW_SHIM_IOCTL_DIBD BIT(9)
101
102#define SDW_SHIM_CTMCTL_DACTQE BIT(0)
103#define SDW_SHIM_CTMCTL_DODS BIT(1)
104#define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3)
105
106#define SDW_SHIM_WAKEEN_ENABLE BIT(0)
107#define SDW_SHIM_WAKESTS_STATUS BIT(0)
108
109/* Intel ALH Register definitions */
Pierre-Louis Bossart7cc6e312019-05-01 10:57:39 -0500110#define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x)))
Pierre-Louis Bossart79ee6632019-08-21 13:58:20 -0500111#define SDW_ALH_NUM_STREAMS 64
Vinod Koul71bb8a12017-12-14 11:19:43 +0530112
113#define SDW_ALH_STRMZCFG_DMAT_VAL 0x3
114#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
115#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
116
Vinod Koulc46302e2018-04-26 18:39:05 +0530117enum intel_pdi_type {
118 INTEL_PDI_IN = 0,
119 INTEL_PDI_OUT = 1,
120 INTEL_PDI_BD = 2,
121};
122
Vinod Koul71bb8a12017-12-14 11:19:43 +0530123#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
124
125/*
126 * Read, write helpers for HW registers
127 */
128static inline int intel_readl(void __iomem *base, int offset)
129{
130 return readl(base + offset);
131}
132
133static inline void intel_writel(void __iomem *base, int offset, int value)
134{
135 writel(value, base + offset);
136}
137
138static inline u16 intel_readw(void __iomem *base, int offset)
139{
140 return readw(base + offset);
141}
142
143static inline void intel_writew(void __iomem *base, int offset, u16 value)
144{
145 writew(value, base + offset);
146}
147
Pierre-Louis Bossart7d2845d2020-07-16 23:09:39 +0800148static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
Vinod Koul71bb8a12017-12-14 11:19:43 +0530149{
150 int timeout = 10;
151 u32 reg_read;
152
Vinod Koul71bb8a12017-12-14 11:19:43 +0530153 do {
154 reg_read = readl(base + offset);
Pierre-Louis Bossart7d2845d2020-07-16 23:09:39 +0800155 if ((reg_read & mask) == target)
Vinod Koul71bb8a12017-12-14 11:19:43 +0530156 return 0;
157
158 timeout--;
Pierre-Louis Bossart7d2845d2020-07-16 23:09:39 +0800159 usleep_range(50, 100);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530160 } while (timeout != 0);
161
162 return -EAGAIN;
163}
164
Pierre-Louis Bossart7d2845d2020-07-16 23:09:39 +0800165static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask)
166{
167 writel(value, base + offset);
168 return intel_wait_bit(base, offset, mask, 0);
169}
170
Vinod Koul71bb8a12017-12-14 11:19:43 +0530171static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
172{
Vinod Koul71bb8a12017-12-14 11:19:43 +0530173 writel(value, base + offset);
Pierre-Louis Bossart7d2845d2020-07-16 23:09:39 +0800174 return intel_wait_bit(base, offset, mask, mask);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530175}
176
177/*
Pierre-Louis Bossart79ee6632019-08-21 13:58:20 -0500178 * debugfs
179 */
180#ifdef CONFIG_DEBUG_FS
181
182#define RD_BUF (2 * PAGE_SIZE)
183
184static ssize_t intel_sprintf(void __iomem *mem, bool l,
185 char *buf, size_t pos, unsigned int reg)
186{
187 int value;
188
189 if (l)
190 value = intel_readl(mem, reg);
191 else
192 value = intel_readw(mem, reg);
193
194 return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
195}
196
197static int intel_reg_show(struct seq_file *s_file, void *data)
198{
199 struct sdw_intel *sdw = s_file->private;
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600200 void __iomem *s = sdw->link_res->shim;
201 void __iomem *a = sdw->link_res->alh;
Pierre-Louis Bossart79ee6632019-08-21 13:58:20 -0500202 char *buf;
203 ssize_t ret;
204 int i, j;
205 unsigned int links, reg;
206
207 buf = kzalloc(RD_BUF, GFP_KERNEL);
208 if (!buf)
209 return -ENOMEM;
210
211 links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
212
213 ret = scnprintf(buf, RD_BUF, "Register Value\n");
214 ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
215
216 for (i = 0; i < links; i++) {
217 reg = SDW_SHIM_LCAP + i * 4;
218 ret += intel_sprintf(s, true, buf, ret, reg);
219 }
220
221 for (i = 0; i < links; i++) {
222 ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
223 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
224 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
225 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
226 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
227 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
228 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
229
230 ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n");
231
232 /*
233 * the value 10 is the number of PDIs. We will need a
234 * cleanup to remove hard-coded Intel configurations
235 * from cadence_master.c
236 */
237 for (j = 0; j < 10; j++) {
238 ret += intel_sprintf(s, false, buf, ret,
239 SDW_SHIM_PCMSYCHM(i, j));
240 ret += intel_sprintf(s, false, buf, ret,
241 SDW_SHIM_PCMSYCHC(i, j));
242 }
243 ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
244
245 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
246 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
247 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
248 }
249
250 ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");
251 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
252 ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
253
254 ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");
255 for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)
256 ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
257
258 seq_printf(s_file, "%s", buf);
259 kfree(buf);
260
261 return 0;
262}
263DEFINE_SHOW_ATTRIBUTE(intel_reg);
264
265static void intel_debugfs_init(struct sdw_intel *sdw)
266{
267 struct dentry *root = sdw->cdns.bus.debugfs;
268
269 if (!root)
270 return;
271
272 sdw->debugfs = debugfs_create_dir("intel-sdw", root);
273
274 debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,
275 &intel_reg_fops);
276
277 sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);
278}
279
280static void intel_debugfs_exit(struct sdw_intel *sdw)
281{
282 debugfs_remove_recursive(sdw->debugfs);
283}
284#else
285static void intel_debugfs_init(struct sdw_intel *sdw) {}
286static void intel_debugfs_exit(struct sdw_intel *sdw) {}
287#endif /* CONFIG_DEBUG_FS */
288
289/*
Vinod Koul71bb8a12017-12-14 11:19:43 +0530290 * shim ops
291 */
292
293static int intel_link_power_up(struct sdw_intel *sdw)
294{
295 unsigned int link_id = sdw->instance;
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600296 void __iomem *shim = sdw->link_res->shim;
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800297 u32 *shim_mask = sdw->link_res->shim_mask;
298 struct sdw_bus *bus = &sdw->cdns.bus;
299 struct sdw_master_prop *prop = &bus->prop;
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800300 u32 spa_mask, cpa_mask;
301 u32 link_control;
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800302 int ret = 0;
303 u32 syncprd;
304 u32 sync_reg;
305
306 mutex_lock(sdw->link_res->shim_lock);
307
308 /*
309 * The hardware relies on an internal counter, typically 4kHz,
310 * to generate the SoundWire SSP - which defines a 'safe'
311 * synchronization point between commands and audio transport
312 * and allows for multi link synchronization. The SYNCPRD value
313 * is only dependent on the oscillator clock provided to
314 * the IP, so adjust based on _DSD properties reported in DSDT
315 * tables. The values reported are based on either 24MHz
316 * (CNL/CML) or 38.4 MHz (ICL/TGL+).
317 */
318 if (prop->mclk_freq % 6000000)
319 syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
320 else
321 syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
322
323 if (!*shim_mask) {
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800324 dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__);
325
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800326 /* we first need to program the SyncPRD/CPU registers */
327 dev_dbg(sdw->cdns.dev,
328 "%s: first link up, programming SYNCPRD\n", __func__);
329
330 /* set SyncPRD period */
331 sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
Vinod Koul3b4979c2020-09-03 17:15:02 +0530332 sync_reg |= FIELD_PREP(SDW_SHIM_SYNC_SYNCPRD, syncprd);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800333
334 /* Set SyncCPU bit */
335 sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
336 intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530337
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800338 /* Link power up sequence */
339 link_control = intel_readl(shim, SDW_SHIM_LCTL);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530340
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800341 /* only power-up enabled links */
Vinod Koul3b4979c2020-09-03 17:15:02 +0530342 spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
343 cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800344
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800345 link_control |= spa_mask;
346
347 ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
348 if (ret < 0) {
349 dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
350 goto out;
351 }
352
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800353 /* SyncCPU will change once link is active */
354 ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
355 SDW_SHIM_SYNC_SYNCCPU, 0);
356 if (ret < 0) {
357 dev_err(sdw->cdns.dev,
358 "Failed to set SHIM_SYNC: %d\n", ret);
359 goto out;
360 }
361 }
362
363 *shim_mask |= BIT(link_id);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530364
365 sdw->cdns.link_up = true;
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800366out:
367 mutex_unlock(sdw->link_res->shim_lock);
368
369 return ret;
Vinod Koul71bb8a12017-12-14 11:19:43 +0530370}
371
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800372/* this needs to be called with shim_lock */
373static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
Vinod Koul71bb8a12017-12-14 11:19:43 +0530374{
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600375 void __iomem *shim = sdw->link_res->shim;
Vinod Koul71bb8a12017-12-14 11:19:43 +0530376 unsigned int link_id = sdw->instance;
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800377 u16 ioctl;
Vinod Koul71bb8a12017-12-14 11:19:43 +0530378
379 /* Switch to MIP from Glue logic */
380 ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
381
382 ioctl &= ~(SDW_SHIM_IOCTL_DOE);
383 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800384 usleep_range(10, 15);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530385
386 ioctl &= ~(SDW_SHIM_IOCTL_DO);
387 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800388 usleep_range(10, 15);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530389
390 ioctl |= (SDW_SHIM_IOCTL_MIF);
391 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800392 usleep_range(10, 15);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530393
394 ioctl &= ~(SDW_SHIM_IOCTL_BKE);
395 ioctl &= ~(SDW_SHIM_IOCTL_COE);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530396 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800397 usleep_range(10, 15);
398
399 /* at this point Master IP has full control of the I/Os */
400}
401
402/* this needs to be called with shim_lock */
403static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
404{
405 unsigned int link_id = sdw->instance;
406 void __iomem *shim = sdw->link_res->shim;
407 u16 ioctl;
408
409 /* Glue logic */
410 ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));
411 ioctl |= SDW_SHIM_IOCTL_BKE;
412 ioctl |= SDW_SHIM_IOCTL_COE;
413 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
414 usleep_range(10, 15);
415
416 ioctl &= ~(SDW_SHIM_IOCTL_MIF);
417 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
418 usleep_range(10, 15);
419
420 /* at this point Integration Glue has full control of the I/Os */
421}
422
423static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
424{
425 void __iomem *shim = sdw->link_res->shim;
426 unsigned int link_id = sdw->instance;
427 int ret = 0;
428 u16 ioctl = 0, act = 0;
429
430 mutex_lock(sdw->link_res->shim_lock);
431
432 /* Initialize Shim */
433 ioctl |= SDW_SHIM_IOCTL_BKE;
434 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
435 usleep_range(10, 15);
436
437 ioctl |= SDW_SHIM_IOCTL_WPDD;
438 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
439 usleep_range(10, 15);
440
441 ioctl |= SDW_SHIM_IOCTL_DO;
442 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
443 usleep_range(10, 15);
444
445 ioctl |= SDW_SHIM_IOCTL_DOE;
446 intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
447 usleep_range(10, 15);
448
449 intel_shim_glue_to_master_ip(sdw);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530450
Vinod Koul3b4979c2020-09-03 17:15:02 +0530451 act |= FIELD_PREP(SDW_SHIM_CTMCTL_DOAIS, 0x1);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530452 act |= SDW_SHIM_CTMCTL_DACTQE;
453 act |= SDW_SHIM_CTMCTL_DODS;
454 intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800455 usleep_range(10, 15);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530456
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800457 mutex_unlock(sdw->link_res->shim_lock);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530458
Vinod Koul71bb8a12017-12-14 11:19:43 +0530459 return ret;
460}
461
Rander Wangab2c9132020-07-16 23:09:46 +0800462static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800463{
464 void __iomem *shim = sdw->link_res->shim;
465 unsigned int link_id = sdw->instance;
466 u16 wake_en, wake_sts;
467
468 mutex_lock(sdw->link_res->shim_lock);
469 wake_en = intel_readw(shim, SDW_SHIM_WAKEEN);
470
471 if (wake_enable) {
472 /* Enable the wakeup */
473 wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
474 intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
475 } else {
476 /* Disable the wake up interrupt */
477 wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);
478 intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);
479
480 /* Clear wake status */
481 wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
482 wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id);
483 intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts);
484 }
485 mutex_unlock(sdw->link_res->shim_lock);
486}
487
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +0800488static int intel_link_power_down(struct sdw_intel *sdw)
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800489{
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800490 u32 link_control, spa_mask, cpa_mask;
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800491 unsigned int link_id = sdw->instance;
492 void __iomem *shim = sdw->link_res->shim;
493 u32 *shim_mask = sdw->link_res->shim_mask;
494 int ret = 0;
495
496 mutex_lock(sdw->link_res->shim_lock);
497
498 intel_shim_master_ip_to_glue(sdw);
499
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800500 if (!(*shim_mask & BIT(link_id)))
501 dev_err(sdw->cdns.dev,
502 "%s: Unbalanced power-up/down calls\n", __func__);
503
504 *shim_mask &= ~BIT(link_id);
505
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800506 if (!*shim_mask) {
507
508 dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__);
509
510 /* Link power down sequence */
511 link_control = intel_readl(shim, SDW_SHIM_LCTL);
512
513 /* only power-down enabled links */
Vinod Koul3b4979c2020-09-03 17:15:02 +0530514 spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask);
515 cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800516
517 link_control &= spa_mask;
518
519 ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
520 }
521
522 link_control = intel_readl(shim, SDW_SHIM_LCTL);
523
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800524 mutex_unlock(sdw->link_res->shim_lock);
525
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800526 if (ret < 0) {
527 dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__);
528
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800529 return ret;
Pierre-Louis Bossart5ee74eb2020-09-01 23:05:56 +0800530 }
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800531
532 sdw->cdns.link_up = false;
533 return 0;
534}
535
Pierre-Louis Bossart02629e452020-07-16 23:09:41 +0800536static void intel_shim_sync_arm(struct sdw_intel *sdw)
537{
538 void __iomem *shim = sdw->link_res->shim;
539 u32 sync_reg;
540
541 mutex_lock(sdw->link_res->shim_lock);
542
543 /* update SYNC register */
544 sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
545 sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance);
546 intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
547
548 mutex_unlock(sdw->link_res->shim_lock);
549}
550
Pierre-Louis Bossart437e3282020-07-16 23:09:42 +0800551static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
552{
553 void __iomem *shim = sdw->link_res->shim;
554 u32 sync_reg;
555 int ret;
556
557 /* Read SYNC register */
558 sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
559
560 /*
561 * Set SyncGO bit to synchronously trigger a bank switch for
562 * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all
563 * the Masters.
564 */
565 sync_reg |= SDW_SHIM_SYNC_SYNCGO;
566
567 ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
568 SDW_SHIM_SYNC_SYNCGO);
569
570 if (ret < 0)
571 dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
Vinod Koul71bb8a12017-12-14 11:19:43 +0530572
573 return ret;
574}
575
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +0800576static int intel_shim_sync_go(struct sdw_intel *sdw)
577{
578 int ret;
579
580 mutex_lock(sdw->link_res->shim_lock);
581
582 ret = intel_shim_sync_go_unlocked(sdw);
583
584 mutex_unlock(sdw->link_res->shim_lock);
585
586 return ret;
587}
588
Vinod Koul37a2d222018-04-26 18:38:58 +0530589/*
590 * PDI routines
591 */
592static void intel_pdi_init(struct sdw_intel *sdw,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500593 struct sdw_cdns_stream_config *config)
Vinod Koul37a2d222018-04-26 18:38:58 +0530594{
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600595 void __iomem *shim = sdw->link_res->shim;
Vinod Koul37a2d222018-04-26 18:38:58 +0530596 unsigned int link_id = sdw->instance;
597 int pcm_cap, pdm_cap;
598
599 /* PCM Stream Capability */
600 pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
601
Vinod Koul3b4979c2020-09-03 17:15:02 +0530602 config->pcm_bd = FIELD_GET(SDW_SHIM_PCMSCAP_BSS, pcm_cap);
603 config->pcm_in = FIELD_GET(SDW_SHIM_PCMSCAP_ISS, pcm_cap);
604 config->pcm_out = FIELD_GET(SDW_SHIM_PCMSCAP_OSS, pcm_cap);
Vinod Koul37a2d222018-04-26 18:38:58 +0530605
Pierre-Louis Bossart121f4362019-05-22 14:47:29 -0500606 dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n",
607 config->pcm_bd, config->pcm_in, config->pcm_out);
608
Vinod Koul37a2d222018-04-26 18:38:58 +0530609 /* PDM Stream Capability */
610 pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
611
Vinod Koul3b4979c2020-09-03 17:15:02 +0530612 config->pdm_bd = FIELD_GET(SDW_SHIM_PDMSCAP_BSS, pdm_cap);
613 config->pdm_in = FIELD_GET(SDW_SHIM_PDMSCAP_ISS, pdm_cap);
614 config->pdm_out = FIELD_GET(SDW_SHIM_PDMSCAP_OSS, pdm_cap);
Pierre-Louis Bossart121f4362019-05-22 14:47:29 -0500615
616 dev_dbg(sdw->cdns.dev, "PDM cap bd:%d in:%d out:%d\n",
617 config->pdm_bd, config->pdm_in, config->pdm_out);
Vinod Koul37a2d222018-04-26 18:38:58 +0530618}
619
620static int
621intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
622{
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600623 void __iomem *shim = sdw->link_res->shim;
Vinod Koul37a2d222018-04-26 18:38:58 +0530624 unsigned int link_id = sdw->instance;
625 int count;
626
627 if (pcm) {
628 count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
Pierre-Louis Bossart18046332019-08-05 19:55:07 -0500629
630 /*
631 * WORKAROUND: on all existing Intel controllers, pdi
632 * number 2 reports channel count as 1 even though it
633 * supports 8 channels. Performing hardcoding for pdi
634 * number 2.
635 */
636 if (pdi_num == 2)
637 count = 7;
638
Vinod Koul37a2d222018-04-26 18:38:58 +0530639 } else {
640 count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
Vinod Koul3b4979c2020-09-03 17:15:02 +0530641 count = FIELD_GET(SDW_SHIM_PDMSCAP_CPSS, count);
Vinod Koul37a2d222018-04-26 18:38:58 +0530642 }
643
644 /* zero based values for channel count in register */
645 count++;
646
647 return count;
648}
649
650static int intel_pdi_get_ch_update(struct sdw_intel *sdw,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500651 struct sdw_cdns_pdi *pdi,
652 unsigned int num_pdi,
653 unsigned int *num_ch, bool pcm)
Vinod Koul37a2d222018-04-26 18:38:58 +0530654{
655 int i, ch_count = 0;
656
657 for (i = 0; i < num_pdi; i++) {
658 pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm);
659 ch_count += pdi->ch_count;
660 pdi++;
661 }
662
663 *num_ch = ch_count;
664 return 0;
665}
666
667static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500668 struct sdw_cdns_streams *stream, bool pcm)
Vinod Koul37a2d222018-04-26 18:38:58 +0530669{
670 intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500671 &stream->num_ch_bd, pcm);
Vinod Koul37a2d222018-04-26 18:38:58 +0530672
673 intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500674 &stream->num_ch_in, pcm);
Vinod Koul37a2d222018-04-26 18:38:58 +0530675
676 intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500677 &stream->num_ch_out, pcm);
Vinod Koul37a2d222018-04-26 18:38:58 +0530678
679 return 0;
680}
681
682static int intel_pdi_ch_update(struct sdw_intel *sdw)
683{
684 /* First update PCM streams followed by PDM streams */
685 intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true);
686 intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false);
687
688 return 0;
689}
690
691static void
692intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
693{
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600694 void __iomem *shim = sdw->link_res->shim;
Vinod Koul37a2d222018-04-26 18:38:58 +0530695 unsigned int link_id = sdw->instance;
696 int pdi_conf = 0;
697
Pierre-Louis Bossartc134f912019-10-22 18:29:48 -0500698 /* the Bulk and PCM streams are not contiguous */
699 pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
700 if (pdi->num >= 2)
701 pdi->intel_alh_id += 2;
Vinod Koul37a2d222018-04-26 18:38:58 +0530702
703 /*
704 * Program stream parameters to stream SHIM register
705 * This is applicable for PCM stream only.
706 */
707 if (pdi->type != SDW_STREAM_PCM)
708 return;
709
710 if (pdi->dir == SDW_DATA_DIR_RX)
711 pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
712 else
713 pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
714
Vinod Koul3b4979c2020-09-03 17:15:02 +0530715 pdi_conf |= FIELD_PREP(SDW_SHIM_PCMSYCM_STREAM, pdi->intel_alh_id);
716 pdi_conf |= FIELD_PREP(SDW_SHIM_PCMSYCM_LCHN, pdi->l_ch_num);
717 pdi_conf |= FIELD_PREP(SDW_SHIM_PCMSYCM_HCHN, pdi->h_ch_num);
Vinod Koul37a2d222018-04-26 18:38:58 +0530718
719 intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
720}
721
722static void
723intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
724{
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600725 void __iomem *alh = sdw->link_res->alh;
Vinod Koul37a2d222018-04-26 18:38:58 +0530726 unsigned int link_id = sdw->instance;
727 unsigned int conf;
728
Pierre-Louis Bossartc134f912019-10-22 18:29:48 -0500729 /* the Bulk and PCM streams are not contiguous */
730 pdi->intel_alh_id = (link_id * 16) + pdi->num + 3;
731 if (pdi->num >= 2)
732 pdi->intel_alh_id += 2;
Vinod Koul37a2d222018-04-26 18:38:58 +0530733
734 /* Program Stream config ALH register */
735 conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
736
Vinod Koul3b4979c2020-09-03 17:15:02 +0530737 conf |= FIELD_PREP(SDW_ALH_STRMZCFG_DMAT, SDW_ALH_STRMZCFG_DMAT_VAL);
738 conf |= FIELD_PREP(SDW_ALH_STRMZCFG_CHN, pdi->ch_count - 1);
Vinod Koul37a2d222018-04-26 18:38:58 +0530739
740 intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
741}
742
Rander Wang4b206d32019-12-11 19:45:02 -0600743static int intel_params_stream(struct sdw_intel *sdw,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500744 struct snd_pcm_substream *substream,
745 struct snd_soc_dai *dai,
Rander Wang4b206d32019-12-11 19:45:02 -0600746 struct snd_pcm_hw_params *hw_params,
747 int link_id, int alh_stream_id)
Vinod Koulc46302e2018-04-26 18:39:05 +0530748{
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600749 struct sdw_intel_link_res *res = sdw->link_res;
Rander Wang4b206d32019-12-11 19:45:02 -0600750 struct sdw_intel_stream_params_data params_data;
Pierre-Louis Bossart05c8afe42019-08-05 19:55:06 -0500751
Rander Wang4b206d32019-12-11 19:45:02 -0600752 params_data.substream = substream;
753 params_data.dai = dai;
754 params_data.hw_params = hw_params;
755 params_data.link_id = link_id;
756 params_data.alh_stream_id = alh_stream_id;
Vinod Koulc46302e2018-04-26 18:39:05 +0530757
Rander Wang4b206d32019-12-11 19:45:02 -0600758 if (res->ops && res->ops->params_stream && res->dev)
759 return res->ops->params_stream(res->dev,
760 &params_data);
Vinod Koulc46302e2018-04-26 18:39:05 +0530761 return -EIO;
762}
763
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -0600764static int intel_free_stream(struct sdw_intel *sdw,
765 struct snd_pcm_substream *substream,
766 struct snd_soc_dai *dai,
767 int link_id)
768{
769 struct sdw_intel_link_res *res = sdw->link_res;
770 struct sdw_intel_stream_free_data free_data;
771
772 free_data.substream = substream;
773 free_data.dai = dai;
774 free_data.link_id = link_id;
775
776 if (res->ops && res->ops->free_stream && res->dev)
777 return res->ops->free_stream(res->dev,
778 &free_data);
779
780 return 0;
781}
782
Vinod Koulc46302e2018-04-26 18:39:05 +0530783/*
Shreyas NC30246e22018-07-27 14:44:17 +0530784 * bank switch routines
785 */
786
787static int intel_pre_bank_switch(struct sdw_bus *bus)
788{
789 struct sdw_cdns *cdns = bus_to_cdns(bus);
790 struct sdw_intel *sdw = cdns_to_intel(cdns);
Shreyas NC30246e22018-07-27 14:44:17 +0530791
792 /* Write to register only for multi-link */
793 if (!bus->multi_link)
794 return 0;
795
Pierre-Louis Bossart02629e452020-07-16 23:09:41 +0800796 intel_shim_sync_arm(sdw);
Shreyas NC30246e22018-07-27 14:44:17 +0530797
798 return 0;
799}
800
801static int intel_post_bank_switch(struct sdw_bus *bus)
802{
803 struct sdw_cdns *cdns = bus_to_cdns(bus);
804 struct sdw_intel *sdw = cdns_to_intel(cdns);
Pierre-Louis Bossart25234862020-02-14 19:47:36 -0600805 void __iomem *shim = sdw->link_res->shim;
Shreyas NC30246e22018-07-27 14:44:17 +0530806 int sync_reg, ret;
807
808 /* Write to register only for multi-link */
809 if (!bus->multi_link)
810 return 0;
811
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800812 mutex_lock(sdw->link_res->shim_lock);
813
Shreyas NC30246e22018-07-27 14:44:17 +0530814 /* Read SYNC register */
815 sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
816
817 /*
818 * post_bank_switch() ops is called from the bus in loop for
819 * all the Masters in the steam with the expectation that
820 * we trigger the bankswitch for the only first Master in the list
821 * and do nothing for the other Masters
822 *
823 * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
824 */
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800825 if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
826 ret = 0;
827 goto unlock;
828 }
Shreyas NC30246e22018-07-27 14:44:17 +0530829
Pierre-Louis Bossart437e3282020-07-16 23:09:42 +0800830 ret = intel_shim_sync_go_unlocked(sdw);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +0800831unlock:
832 mutex_unlock(sdw->link_res->shim_lock);
Shreyas NC30246e22018-07-27 14:44:17 +0530833
Shreyas NC30246e22018-07-27 14:44:17 +0530834 if (ret < 0)
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500835 dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
Shreyas NC30246e22018-07-27 14:44:17 +0530836
837 return ret;
838}
839
840/*
Vinod Koulc46302e2018-04-26 18:39:05 +0530841 * DAI routines
842 */
843
Rander Wang5e7484d2020-02-14 19:47:39 -0600844static int intel_startup(struct snd_pcm_substream *substream,
845 struct snd_soc_dai *dai)
846{
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +0800847 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
848 int ret;
849
850 ret = pm_runtime_get_sync(cdns->dev);
851 if (ret < 0 && ret != -EACCES) {
852 dev_err_ratelimited(cdns->dev,
853 "pm_runtime_get_sync failed in %s, ret %d\n",
854 __func__, ret);
855 pm_runtime_put_noidle(cdns->dev);
856 return ret;
857 }
Pierre-Louis Bossartff16d1e2020-07-01 02:43:54 +0800858 return 0;
Rander Wang5e7484d2020-02-14 19:47:39 -0600859}
860
Vinod Koulc46302e2018-04-26 18:39:05 +0530861static int intel_hw_params(struct snd_pcm_substream *substream,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -0500862 struct snd_pcm_hw_params *params,
863 struct snd_soc_dai *dai)
Vinod Koulc46302e2018-04-26 18:39:05 +0530864{
865 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
866 struct sdw_intel *sdw = cdns_to_intel(cdns);
867 struct sdw_cdns_dma_data *dma;
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500868 struct sdw_cdns_pdi *pdi;
Vinod Koulc46302e2018-04-26 18:39:05 +0530869 struct sdw_stream_config sconfig;
870 struct sdw_port_config *pconfig;
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500871 int ch, dir;
872 int ret;
Vinod Koulc46302e2018-04-26 18:39:05 +0530873 bool pcm = true;
874
875 dma = snd_soc_dai_get_dma_data(dai, substream);
876 if (!dma)
877 return -EIO;
878
879 ch = params_channels(params);
880 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
881 dir = SDW_DATA_DIR_RX;
882 else
883 dir = SDW_DATA_DIR_TX;
884
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500885 if (dma->stream_type == SDW_STREAM_PDM)
Vinod Koulc46302e2018-04-26 18:39:05 +0530886 pcm = false;
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500887
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500888 if (pcm)
Bard Liao1b53385e2019-09-16 14:23:48 -0500889 pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500890 else
Bard Liao1b53385e2019-09-16 14:23:48 -0500891 pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id);
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500892
893 if (!pdi) {
894 ret = -EINVAL;
895 goto error;
Vinod Koulc46302e2018-04-26 18:39:05 +0530896 }
897
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500898 /* do run-time configurations for SHIM, ALH and PDI/PORT */
899 intel_pdi_shim_configure(sdw, pdi);
900 intel_pdi_alh_configure(sdw, pdi);
901 sdw_cdns_config_stream(cdns, ch, dir, pdi);
Vinod Koulc46302e2018-04-26 18:39:05 +0530902
Bard Liaoa5a02392020-08-17 23:29:16 +0800903 /* store pdi and hw_params, may be needed in prepare step */
904 dma->suspended = false;
905 dma->pdi = pdi;
906 dma->hw_params = params;
Vinod Koulc46302e2018-04-26 18:39:05 +0530907
908 /* Inform DSP about PDI stream number */
Rander Wang4b206d32019-12-11 19:45:02 -0600909 ret = intel_params_stream(sdw, substream, dai, params,
910 sdw->instance,
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500911 pdi->intel_alh_id);
912 if (ret)
913 goto error;
Vinod Koulc46302e2018-04-26 18:39:05 +0530914
915 sconfig.direction = dir;
916 sconfig.ch_count = ch;
917 sconfig.frame_rate = params_rate(params);
918 sconfig.type = dma->stream_type;
919
920 if (dma->stream_type == SDW_STREAM_PDM) {
921 sconfig.frame_rate *= 50;
922 sconfig.bps = 1;
923 } else {
924 sconfig.bps = snd_pcm_format_width(params_format(params));
925 }
926
927 /* Port configuration */
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500928 pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL);
Vinod Koulc46302e2018-04-26 18:39:05 +0530929 if (!pconfig) {
930 ret = -ENOMEM;
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500931 goto error;
Vinod Koulc46302e2018-04-26 18:39:05 +0530932 }
933
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500934 pconfig->num = pdi->num;
935 pconfig->ch_mask = (1 << ch) - 1;
Vinod Koulc46302e2018-04-26 18:39:05 +0530936
937 ret = sdw_stream_add_master(&cdns->bus, &sconfig,
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500938 pconfig, 1, dma->stream);
939 if (ret)
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -0500940 dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
Vinod Koulc46302e2018-04-26 18:39:05 +0530941
942 kfree(pconfig);
Pierre-Louis Bossart57a34792019-09-16 14:23:46 -0500943error:
Vinod Koulc46302e2018-04-26 18:39:05 +0530944 return ret;
945}
946
Rander Wang27b198f2020-02-14 19:47:37 -0600947static int intel_prepare(struct snd_pcm_substream *substream,
948 struct snd_soc_dai *dai)
949{
Bard Liaoa5a02392020-08-17 23:29:16 +0800950 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
951 struct sdw_intel *sdw = cdns_to_intel(cdns);
Rander Wang27b198f2020-02-14 19:47:37 -0600952 struct sdw_cdns_dma_data *dma;
Bard Liaoa5a02392020-08-17 23:29:16 +0800953 int ch, dir;
954 int ret;
Rander Wang27b198f2020-02-14 19:47:37 -0600955
956 dma = snd_soc_dai_get_dma_data(dai, substream);
957 if (!dma) {
958 dev_err(dai->dev, "failed to get dma data in %s",
959 __func__);
960 return -EIO;
961 }
962
Bard Liaoa5a02392020-08-17 23:29:16 +0800963 if (dma->suspended) {
964 dma->suspended = false;
965
966 /*
967 * .prepare() is called after system resume, where we
968 * need to reinitialize the SHIM/ALH/Cadence IP.
969 * .prepare() is also called to deal with underflows,
970 * but in those cases we cannot touch ALH/SHIM
971 * registers
972 */
973
974 /* configure stream */
975 ch = params_channels(dma->hw_params);
976 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
977 dir = SDW_DATA_DIR_RX;
978 else
979 dir = SDW_DATA_DIR_TX;
980
981 intel_pdi_shim_configure(sdw, dma->pdi);
982 intel_pdi_alh_configure(sdw, dma->pdi);
983 sdw_cdns_config_stream(cdns, ch, dir, dma->pdi);
984
985 /* Inform DSP about PDI stream number */
986 ret = intel_params_stream(sdw, substream, dai,
987 dma->hw_params,
988 sdw->instance,
989 dma->pdi->intel_alh_id);
990 if (ret)
991 goto err;
992 }
993
994 ret = sdw_prepare_stream(dma->stream);
995
996err:
997 return ret;
Rander Wang27b198f2020-02-14 19:47:37 -0600998}
999
Vinod Koulc46302e2018-04-26 18:39:05 +05301000static int
1001intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
1002{
1003 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -06001004 struct sdw_intel *sdw = cdns_to_intel(cdns);
Vinod Koulc46302e2018-04-26 18:39:05 +05301005 struct sdw_cdns_dma_data *dma;
1006 int ret;
1007
1008 dma = snd_soc_dai_get_dma_data(dai, substream);
1009 if (!dma)
1010 return -EIO;
1011
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -06001012 ret = sdw_deprepare_stream(dma->stream);
1013 if (ret) {
1014 dev_err(dai->dev, "sdw_deprepare_stream: failed %d", ret);
1015 return ret;
1016 }
1017
Vinod Koulc46302e2018-04-26 18:39:05 +05301018 ret = sdw_stream_remove_master(&cdns->bus, dma->stream);
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -06001019 if (ret < 0) {
Pierre-Louis Bossart17ed5be2019-05-01 10:57:45 -05001020 dev_err(dai->dev, "remove master from stream %s failed: %d\n",
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -05001021 dma->stream->name, ret);
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -06001022 return ret;
1023 }
Vinod Koulc46302e2018-04-26 18:39:05 +05301024
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -06001025 ret = intel_free_stream(sdw, substream, dai, sdw->instance);
1026 if (ret < 0) {
1027 dev_err(dai->dev, "intel_free_stream: failed %d", ret);
1028 return ret;
1029 }
1030
Bard Liaoa5a02392020-08-17 23:29:16 +08001031 dma->hw_params = NULL;
1032 dma->pdi = NULL;
1033
Pierre-Louis Bossarteff346f2020-02-14 19:47:40 -06001034 return 0;
Vinod Koulc46302e2018-04-26 18:39:05 +05301035}
1036
Pierre-Louis Bossart183c7682019-08-05 19:55:22 -05001037static void intel_shutdown(struct snd_pcm_substream *substream,
1038 struct snd_soc_dai *dai)
1039{
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001040 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
Pierre-Louis Bossart183c7682019-08-05 19:55:22 -05001041
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001042 pm_runtime_mark_last_busy(cdns->dev);
1043 pm_runtime_put_autosuspend(cdns->dev);
Pierre-Louis Bossart183c7682019-08-05 19:55:22 -05001044}
1045
Bard Liaoa5a02392020-08-17 23:29:16 +08001046static int intel_component_dais_suspend(struct snd_soc_component *component)
1047{
1048 struct sdw_cdns_dma_data *dma;
1049 struct snd_soc_dai *dai;
1050
1051 for_each_component_dais(component, dai) {
1052 /*
1053 * we don't have a .suspend dai_ops, and we don't have access
1054 * to the substream, so let's mark both capture and playback
1055 * DMA contexts as suspended
1056 */
1057 dma = dai->playback_dma_data;
1058 if (dma)
1059 dma->suspended = true;
1060
1061 dma = dai->capture_dma_data;
1062 if (dma)
1063 dma->suspended = true;
1064 }
1065
1066 return 0;
1067}
1068
Vinod Koulc46302e2018-04-26 18:39:05 +05301069static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -05001070 void *stream, int direction)
Vinod Koulc46302e2018-04-26 18:39:05 +05301071{
1072 return cdns_set_sdw_stream(dai, stream, true, direction);
1073}
1074
1075static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -05001076 void *stream, int direction)
Vinod Koulc46302e2018-04-26 18:39:05 +05301077{
1078 return cdns_set_sdw_stream(dai, stream, false, direction);
1079}
1080
Pierre-Louis Bossart09553142020-07-01 02:43:52 +08001081static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
1082 int direction)
1083{
1084 struct sdw_cdns_dma_data *dma;
1085
1086 if (direction == SNDRV_PCM_STREAM_PLAYBACK)
1087 dma = dai->playback_dma_data;
1088 else
1089 dma = dai->capture_dma_data;
1090
1091 if (!dma)
Pierre-Louis Bossart06dcb4e2020-09-04 04:47:37 +08001092 return ERR_PTR(-EINVAL);
Pierre-Louis Bossart09553142020-07-01 02:43:52 +08001093
1094 return dma->stream;
1095}
1096
Julia Lawallb1635592018-10-27 15:34:42 +02001097static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
Rander Wang5e7484d2020-02-14 19:47:39 -06001098 .startup = intel_startup,
Vinod Koulc46302e2018-04-26 18:39:05 +05301099 .hw_params = intel_hw_params,
Rander Wang27b198f2020-02-14 19:47:37 -06001100 .prepare = intel_prepare,
Vinod Koulc46302e2018-04-26 18:39:05 +05301101 .hw_free = intel_hw_free,
Pierre-Louis Bossart183c7682019-08-05 19:55:22 -05001102 .shutdown = intel_shutdown,
Vinod Koulc46302e2018-04-26 18:39:05 +05301103 .set_sdw_stream = intel_pcm_set_sdw_stream,
Pierre-Louis Bossart09553142020-07-01 02:43:52 +08001104 .get_sdw_stream = intel_get_sdw_stream,
Vinod Koulc46302e2018-04-26 18:39:05 +05301105};
1106
Julia Lawallb1635592018-10-27 15:34:42 +02001107static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
Rander Wang5e7484d2020-02-14 19:47:39 -06001108 .startup = intel_startup,
Vinod Koulc46302e2018-04-26 18:39:05 +05301109 .hw_params = intel_hw_params,
Rander Wang27b198f2020-02-14 19:47:37 -06001110 .prepare = intel_prepare,
Vinod Koulc46302e2018-04-26 18:39:05 +05301111 .hw_free = intel_hw_free,
Pierre-Louis Bossart183c7682019-08-05 19:55:22 -05001112 .shutdown = intel_shutdown,
Vinod Koulc46302e2018-04-26 18:39:05 +05301113 .set_sdw_stream = intel_pdm_set_sdw_stream,
Pierre-Louis Bossart09553142020-07-01 02:43:52 +08001114 .get_sdw_stream = intel_get_sdw_stream,
Vinod Koulc46302e2018-04-26 18:39:05 +05301115};
1116
1117static const struct snd_soc_component_driver dai_component = {
1118 .name = "soundwire",
Bard Liaoa5a02392020-08-17 23:29:16 +08001119 .suspend = intel_component_dais_suspend
Vinod Koulc46302e2018-04-26 18:39:05 +05301120};
1121
1122static int intel_create_dai(struct sdw_cdns *cdns,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -05001123 struct snd_soc_dai_driver *dais,
1124 enum intel_pdi_type type,
1125 u32 num, u32 off, u32 max_ch, bool pcm)
Vinod Koulc46302e2018-04-26 18:39:05 +05301126{
1127 int i;
1128
1129 if (num == 0)
1130 return 0;
1131
1132 /* TODO: Read supported rates/formats from hardware */
1133 for (i = off; i < (off + num); i++) {
Pierre-Louis Bossartbf6d6e62020-06-17 11:35:36 -05001134 dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
1135 "SDW%d Pin%d",
1136 cdns->instance, i);
Vinod Koulc46302e2018-04-26 18:39:05 +05301137 if (!dais[i].name)
1138 return -ENOMEM;
1139
1140 if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
Vinod Koulc46302e2018-04-26 18:39:05 +05301141 dais[i].playback.channels_min = 1;
1142 dais[i].playback.channels_max = max_ch;
1143 dais[i].playback.rates = SNDRV_PCM_RATE_48000;
1144 dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
1145 }
1146
1147 if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
Srinivas Kandagatla39194122019-06-06 12:23:04 +01001148 dais[i].capture.channels_min = 1;
1149 dais[i].capture.channels_max = max_ch;
Vinod Koulc46302e2018-04-26 18:39:05 +05301150 dais[i].capture.rates = SNDRV_PCM_RATE_48000;
1151 dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
1152 }
1153
Vinod Koulc46302e2018-04-26 18:39:05 +05301154 if (pcm)
1155 dais[i].ops = &intel_pcm_dai_ops;
1156 else
1157 dais[i].ops = &intel_pdm_dai_ops;
1158 }
1159
1160 return 0;
1161}
1162
1163static int intel_register_dai(struct sdw_intel *sdw)
1164{
1165 struct sdw_cdns *cdns = &sdw->cdns;
1166 struct sdw_cdns_streams *stream;
1167 struct snd_soc_dai_driver *dais;
1168 int num_dai, ret, off = 0;
1169
1170 /* DAIs are created based on total number of PDIs supported */
1171 num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi;
1172
1173 dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
1174 if (!dais)
1175 return -ENOMEM;
1176
1177 /* Create PCM DAIs */
1178 stream = &cdns->pcm;
1179
Bard Liaocf924962019-09-16 14:23:43 -05001180 ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in,
Vinod Koul1215dae2019-05-02 16:29:25 +05301181 off, stream->num_ch_in, true);
Vinod Koulc46302e2018-04-26 18:39:05 +05301182 if (ret)
1183 return ret;
1184
1185 off += cdns->pcm.num_in;
Vinod Koul1215dae2019-05-02 16:29:25 +05301186 ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out,
1187 off, stream->num_ch_out, true);
Vinod Koulc46302e2018-04-26 18:39:05 +05301188 if (ret)
1189 return ret;
1190
1191 off += cdns->pcm.num_out;
Vinod Koul1215dae2019-05-02 16:29:25 +05301192 ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd,
1193 off, stream->num_ch_bd, true);
Vinod Koulc46302e2018-04-26 18:39:05 +05301194 if (ret)
1195 return ret;
1196
1197 /* Create PDM DAIs */
1198 stream = &cdns->pdm;
1199 off += cdns->pcm.num_bd;
Vinod Koul1215dae2019-05-02 16:29:25 +05301200 ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pdm.num_in,
1201 off, stream->num_ch_in, false);
Vinod Koulc46302e2018-04-26 18:39:05 +05301202 if (ret)
1203 return ret;
1204
1205 off += cdns->pdm.num_in;
Vinod Koul1215dae2019-05-02 16:29:25 +05301206 ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pdm.num_out,
1207 off, stream->num_ch_out, false);
Vinod Koulc46302e2018-04-26 18:39:05 +05301208 if (ret)
1209 return ret;
1210
Bard Liaocf924962019-09-16 14:23:43 -05001211 off += cdns->pdm.num_out;
Vinod Koul1215dae2019-05-02 16:29:25 +05301212 ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pdm.num_bd,
1213 off, stream->num_ch_bd, false);
Vinod Koulc46302e2018-04-26 18:39:05 +05301214 if (ret)
1215 return ret;
1216
1217 return snd_soc_register_component(cdns->dev, &dai_component,
Pierre-Louis Bossartd542bc92019-05-01 10:57:38 -05001218 dais, num_dai);
Vinod Koulc46302e2018-04-26 18:39:05 +05301219}
1220
Pierre-Louis Bossart085f4ac2019-08-05 19:55:16 -05001221static int sdw_master_read_intel_prop(struct sdw_bus *bus)
1222{
1223 struct sdw_master_prop *prop = &bus->prop;
1224 struct fwnode_handle *link;
1225 char name[32];
Pierre-Louis Bossart395713d2019-08-21 13:58:21 -05001226 u32 quirk_mask;
Pierre-Louis Bossart085f4ac2019-08-05 19:55:16 -05001227
1228 /* Find master handle */
1229 snprintf(name, sizeof(name),
1230 "mipi-sdw-link-%d-subproperties", bus->link_id);
1231
1232 link = device_get_named_child_node(bus->dev, name);
1233 if (!link) {
1234 dev_err(bus->dev, "Master node %s not found\n", name);
1235 return -EIO;
1236 }
1237
1238 fwnode_property_read_u32(link,
1239 "intel-sdw-ip-clock",
1240 &prop->mclk_freq);
Pierre-Louis Bossart395713d2019-08-21 13:58:21 -05001241
Bard Liaoa19efb52020-01-13 17:11:29 -06001242 /* the values reported by BIOS are the 2x clock, not the bus clock */
1243 prop->mclk_freq /= 2;
1244
Pierre-Louis Bossart395713d2019-08-21 13:58:21 -05001245 fwnode_property_read_u32(link,
1246 "intel-quirk-mask",
1247 &quirk_mask);
1248
1249 if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
1250 prop->hw_disabled = true;
1251
Pierre-Louis Bossart085f4ac2019-08-05 19:55:16 -05001252 return 0;
1253}
1254
Vinod Koul71bb8a12017-12-14 11:19:43 +05301255static int intel_prop_read(struct sdw_bus *bus)
1256{
1257 /* Initialize with default handler to read all DisCo properties */
1258 sdw_master_read_prop(bus);
1259
Pierre-Louis Bossart085f4ac2019-08-05 19:55:16 -05001260 /* read Intel-specific properties */
1261 sdw_master_read_intel_prop(bus);
1262
Vinod Koul71bb8a12017-12-14 11:19:43 +05301263 return 0;
1264}
1265
Shreyas NCc91605f2018-04-26 18:38:43 +05301266static struct sdw_master_ops sdw_intel_ops = {
1267 .read_prop = sdw_master_read_prop,
1268 .xfer_msg = cdns_xfer_msg,
1269 .xfer_msg_defer = cdns_xfer_msg_defer,
1270 .reset_page_addr = cdns_reset_page_addr,
Vinod Koul07abeff2018-04-26 18:38:48 +05301271 .set_bus_conf = cdns_bus_conf,
Shreyas NC30246e22018-07-27 14:44:17 +05301272 .pre_bank_switch = intel_pre_bank_switch,
1273 .post_bank_switch = intel_post_bank_switch,
Shreyas NCc91605f2018-04-26 18:38:43 +05301274};
1275
Pierre-Louis Bossartdfbe6422019-10-22 18:54:46 -05001276static int intel_init(struct sdw_intel *sdw)
1277{
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +08001278 bool clock_stop;
1279
Pierre-Louis Bossartdfbe6422019-10-22 18:54:46 -05001280 /* Initialize shim and controller */
1281 intel_link_power_up(sdw);
Pierre-Louis Bossart4a17c442020-07-16 23:09:40 +08001282
1283 clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
1284
1285 intel_shim_init(sdw, clock_stop);
1286
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001287 return 0;
Pierre-Louis Bossartdfbe6422019-10-22 18:54:46 -05001288}
1289
Vinod Koul71bb8a12017-12-14 11:19:43 +05301290/*
1291 * probe and init
1292 */
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001293static int intel_master_probe(struct platform_device *pdev)
Vinod Koul71bb8a12017-12-14 11:19:43 +05301294{
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001295 struct device *dev = &pdev->dev;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301296 struct sdw_intel *sdw;
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001297 struct sdw_cdns *cdns;
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001298 struct sdw_bus *bus;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301299 int ret;
1300
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001301 sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301302 if (!sdw)
1303 return -ENOMEM;
1304
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001305 cdns = &sdw->cdns;
1306 bus = &cdns->bus;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301307
Vinod Koul71bb8a12017-12-14 11:19:43 +05301308 sdw->instance = pdev->id;
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001309 sdw->link_res = dev_get_platdata(dev);
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001310 cdns->dev = dev;
1311 cdns->registers = sdw->link_res->registers;
1312 cdns->instance = sdw->instance;
1313 cdns->msg_count = 0;
1314
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001315 bus->link_id = pdev->id;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301316
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001317 sdw_cdns_probe(cdns);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301318
1319 /* Set property read ops */
Shreyas NCc91605f2018-04-26 18:38:43 +05301320 sdw_intel_ops.read_prop = intel_prop_read;
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001321 bus->ops = &sdw_intel_ops;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301322
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001323 /* set driver data, accessed by snd_soc_dai_get_drvdata() */
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001324 dev_set_drvdata(dev, cdns);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301325
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001326 ret = sdw_bus_master_add(bus, dev, dev->fwnode);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301327 if (ret) {
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001328 dev_err(dev, "sdw_bus_master_add fail: %d\n", ret);
Pierre-Louis Bossart9e3d47f2019-10-22 18:54:47 -05001329 return ret;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301330 }
1331
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001332 if (bus->prop.hw_disabled)
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001333 dev_info(dev,
1334 "SoundWire master %d is disabled, will be ignored\n",
1335 bus->link_id);
Pierre-Louis Bossart0ef29862020-09-01 23:05:48 +08001336 /*
1337 * Ignore BIOS err_threshold, it's a really bad idea when dealing
1338 * with multiple hardware synchronized links
1339 */
1340 bus->prop.err_threshold = 0;
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001341
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001342 return 0;
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001343}
1344
1345int intel_master_startup(struct platform_device *pdev)
1346{
1347 struct sdw_cdns_stream_config config;
1348 struct device *dev = &pdev->dev;
1349 struct sdw_cdns *cdns = dev_get_drvdata(dev);
1350 struct sdw_intel *sdw = cdns_to_intel(cdns);
1351 struct sdw_bus *bus = &cdns->bus;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001352 int link_flags;
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001353 bool multi_link;
Pierre-Louis Bossartcaf68812020-08-17 23:29:20 +08001354 u32 clock_stop_quirks;
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001355 int ret;
1356
1357 if (bus->prop.hw_disabled) {
1358 dev_info(dev,
1359 "SoundWire master %d is disabled, ignoring\n",
1360 sdw->instance);
Pierre-Louis Bossart395713d2019-08-21 13:58:21 -05001361 return 0;
1362 }
1363
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001364 link_flags = md_flags >> (bus->link_id * 8);
1365 multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1366 if (!multi_link) {
1367 dev_dbg(dev, "Multi-link is disabled\n");
1368 bus->multi_link = false;
1369 } else {
Pierre-Louis Bossart94eed662020-09-01 23:05:52 +08001370 /*
1371 * hardware-based synchronization is required regardless
1372 * of the number of segments used by a stream: SSP-based
1373 * synchronization is gated by gsync when the multi-master
1374 * mode is set.
1375 */
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001376 bus->multi_link = true;
Pierre-Louis Bossart94eed662020-09-01 23:05:52 +08001377 bus->hw_sync_min_links = 1;
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001378 }
1379
1380 /* Initialize shim, controller */
Pierre-Louis Bossartdfbe6422019-10-22 18:54:46 -05001381 ret = intel_init(sdw);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301382 if (ret)
1383 goto err_init;
1384
Vinod Koul37a2d222018-04-26 18:38:58 +05301385 /* Read the PDI config and initialize cadence PDI */
1386 intel_pdi_init(sdw, &config);
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001387 ret = sdw_cdns_pdi_init(cdns, config);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301388 if (ret)
1389 goto err_init;
1390
Vinod Koul37a2d222018-04-26 18:38:58 +05301391 intel_pdi_ch_update(sdw);
1392
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001393 ret = sdw_cdns_enable_interrupt(cdns, true);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301394 if (ret < 0) {
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001395 dev_err(dev, "cannot enable interrupts\n");
Vinod Koul71bb8a12017-12-14 11:19:43 +05301396 goto err_init;
1397 }
1398
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001399 /*
1400 * follow recommended programming flows to avoid timeouts when
1401 * gsync is enabled
1402 */
1403 if (multi_link)
1404 intel_shim_sync_arm(sdw);
1405
1406 ret = sdw_cdns_init(cdns);
1407 if (ret < 0) {
1408 dev_err(dev, "unable to initialize Cadence IP\n");
1409 goto err_interrupt;
1410 }
1411
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001412 ret = sdw_cdns_exit_reset(cdns);
Pierre-Louis Bossart49ea07d2019-10-22 18:54:44 -05001413 if (ret < 0) {
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001414 dev_err(dev, "unable to exit bus reset sequence\n");
Pierre-Louis Bossart9e3d47f2019-10-22 18:54:47 -05001415 goto err_interrupt;
Pierre-Louis Bossart49ea07d2019-10-22 18:54:44 -05001416 }
1417
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001418 if (multi_link) {
1419 ret = intel_shim_sync_go(sdw);
1420 if (ret < 0) {
1421 dev_err(dev, "sync go failed: %d\n", ret);
1422 goto err_interrupt;
1423 }
1424 }
1425
Vinod Koulc46302e2018-04-26 18:39:05 +05301426 /* Register DAIs */
1427 ret = intel_register_dai(sdw);
1428 if (ret) {
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001429 dev_err(dev, "DAI registration failed: %d\n", ret);
1430 snd_soc_unregister_component(dev);
Pierre-Louis Bossart9e3d47f2019-10-22 18:54:47 -05001431 goto err_interrupt;
Vinod Koulc46302e2018-04-26 18:39:05 +05301432 }
1433
Pierre-Louis Bossart79ee6632019-08-21 13:58:20 -05001434 intel_debugfs_init(sdw);
1435
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001436 /* Enable runtime PM */
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001437 if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
1438 pm_runtime_set_autosuspend_delay(dev,
1439 INTEL_MASTER_SUSPEND_DELAY_MS);
1440 pm_runtime_use_autosuspend(dev);
1441 pm_runtime_mark_last_busy(dev);
1442
1443 pm_runtime_set_active(dev);
1444 pm_runtime_enable(dev);
1445 }
1446
Pierre-Louis Bossartcaf68812020-08-17 23:29:20 +08001447 clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1448 if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) {
1449 /*
1450 * To keep the clock running we need to prevent
1451 * pm_runtime suspend from happening by increasing the
1452 * reference count.
1453 * This quirk is specified by the parent PCI device in
1454 * case of specific latency requirements. It will have
1455 * no effect if pm_runtime is disabled by the user via
1456 * a module parameter for testing purposes.
1457 */
1458 pm_runtime_get_noresume(dev);
1459 }
1460
Pierre-Louis Bossarta2d9c162020-08-17 23:29:17 +08001461 /*
1462 * The runtime PM status of Slave devices is "Unsupported"
1463 * until they report as ATTACHED. If they don't, e.g. because
1464 * there are no Slave devices populated or if the power-on is
1465 * delayed or dependent on a power switch, the Master will
1466 * remain active and prevent its parent from suspending.
1467 *
1468 * Conditionally force the pm_runtime core to re-evaluate the
1469 * Master status in the absence of any Slave activity. A quirk
1470 * is provided to e.g. deal with Slaves that may be powered on
1471 * with a delay. A more complete solution would require the
1472 * definition of Master properties.
1473 */
1474 if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1475 pm_runtime_idle(dev);
1476
Vinod Koul71bb8a12017-12-14 11:19:43 +05301477 return 0;
1478
Pierre-Louis Bossart9e3d47f2019-10-22 18:54:47 -05001479err_interrupt:
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001480 sdw_cdns_enable_interrupt(cdns, false);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301481err_init:
Vinod Koul71bb8a12017-12-14 11:19:43 +05301482 return ret;
1483}
1484
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001485static int intel_master_remove(struct platform_device *pdev)
Vinod Koul71bb8a12017-12-14 11:19:43 +05301486{
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001487 struct device *dev = &pdev->dev;
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001488 struct sdw_cdns *cdns = dev_get_drvdata(dev);
1489 struct sdw_intel *sdw = cdns_to_intel(cdns);
1490 struct sdw_bus *bus = &cdns->bus;
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001491
Pierre-Louis Bossartcaf68812020-08-17 23:29:20 +08001492 /*
1493 * Since pm_runtime is already disabled, we don't decrease
1494 * the refcount when the clock_stop_quirk is
1495 * SDW_INTEL_CLK_STOP_NOT_ALLOWED
1496 */
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001497 if (!bus->prop.hw_disabled) {
Pierre-Louis Bossart395713d2019-08-21 13:58:21 -05001498 intel_debugfs_exit(sdw);
Pierre-Louis Bossart83e129af2020-06-01 02:20:58 +08001499 sdw_cdns_enable_interrupt(cdns, false);
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001500 snd_soc_unregister_component(dev);
Pierre-Louis Bossart395713d2019-08-21 13:58:21 -05001501 }
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001502 sdw_bus_master_delete(bus);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301503
1504 return 0;
1505}
1506
Rander Wangab2c9132020-07-16 23:09:46 +08001507int intel_master_process_wakeen_event(struct platform_device *pdev)
1508{
1509 struct device *dev = &pdev->dev;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301510 struct sdw_intel *sdw;
Rander Wangab2c9132020-07-16 23:09:46 +08001511 struct sdw_bus *bus;
1512 void __iomem *shim;
1513 u16 wake_sts;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301514
1515 sdw = platform_get_drvdata(pdev);
Rander Wangab2c9132020-07-16 23:09:46 +08001516 bus = &sdw->cdns.bus;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301517
Rander Wangab2c9132020-07-16 23:09:46 +08001518 if (bus->prop.hw_disabled) {
1519 dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id);
1520 return 0;
Vinod Koul71bb8a12017-12-14 11:19:43 +05301521 }
Rander Wangab2c9132020-07-16 23:09:46 +08001522
1523 shim = sdw->link_res->shim;
1524 wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
1525
1526 if (!(wake_sts & BIT(sdw->instance)))
1527 return 0;
1528
1529 /* disable WAKEEN interrupt ASAP to prevent interrupt flood */
1530 intel_shim_wake(sdw, false);
1531
1532 /*
1533 * resume the Master, which will generate a bus reset and result in
1534 * Slaves re-attaching and be re-enumerated. The SoundWire physical
1535 * device which generated the wake will trigger an interrupt, which
1536 * will in turn cause the corresponding Linux Slave device to be
1537 * resumed and the Slave codec driver to check the status.
1538 */
1539 pm_request_resume(dev);
Vinod Koul71bb8a12017-12-14 11:19:43 +05301540
1541 return 0;
1542}
1543
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001544/*
1545 * PM calls
1546 */
1547
1548#ifdef CONFIG_PM
1549
Bard Liaof046b232020-08-24 21:32:34 +08001550static int __maybe_unused intel_suspend(struct device *dev)
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001551{
1552 struct sdw_cdns *cdns = dev_get_drvdata(dev);
1553 struct sdw_intel *sdw = cdns_to_intel(cdns);
1554 struct sdw_bus *bus = &cdns->bus;
Pierre-Louis Bossarte4be9fa2020-09-01 23:05:47 +08001555 u32 clock_stop_quirks;
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001556 int ret;
1557
1558 if (bus->prop.hw_disabled) {
1559 dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1560 bus->link_id);
1561 return 0;
1562 }
1563
Pierre-Louis Bossartb61b8b32020-08-17 23:29:13 +08001564 if (pm_runtime_suspended(dev)) {
1565 dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__);
1566
Pierre-Louis Bossarte4be9fa2020-09-01 23:05:47 +08001567 clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1568
1569 if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
1570 !clock_stop_quirks) &&
1571 !pm_runtime_suspended(dev->parent)) {
1572
1573 /*
1574 * if we've enabled clock stop, and the parent
1575 * is still active, disable shim wake. The
1576 * SHIM registers are not accessible if the
1577 * parent is already pm_runtime suspended so
1578 * it's too late to change that configuration
1579 */
1580
1581 intel_shim_wake(sdw, false);
1582 }
1583
Pierre-Louis Bossartb61b8b32020-08-17 23:29:13 +08001584 return 0;
1585 }
1586
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001587 ret = sdw_cdns_enable_interrupt(cdns, false);
1588 if (ret < 0) {
1589 dev_err(dev, "cannot disable interrupts on suspend\n");
1590 return ret;
1591 }
1592
1593 ret = intel_link_power_down(sdw);
1594 if (ret) {
1595 dev_err(dev, "Link power down failed: %d", ret);
1596 return ret;
1597 }
1598
1599 intel_shim_wake(sdw, false);
1600
1601 return 0;
1602}
1603
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001604static int intel_suspend_runtime(struct device *dev)
1605{
1606 struct sdw_cdns *cdns = dev_get_drvdata(dev);
1607 struct sdw_intel *sdw = cdns_to_intel(cdns);
1608 struct sdw_bus *bus = &cdns->bus;
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001609 u32 clock_stop_quirks;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001610 int ret;
1611
1612 if (bus->prop.hw_disabled) {
1613 dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1614 bus->link_id);
1615 return 0;
1616 }
1617
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001618 clock_stop_quirks = sdw->link_res->clock_stop_quirks;
1619
1620 if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1621
1622 ret = sdw_cdns_enable_interrupt(cdns, false);
1623 if (ret < 0) {
1624 dev_err(dev, "cannot disable interrupts on suspend\n");
1625 return ret;
1626 }
1627
1628 ret = intel_link_power_down(sdw);
1629 if (ret) {
1630 dev_err(dev, "Link power down failed: %d", ret);
1631 return ret;
1632 }
1633
1634 intel_shim_wake(sdw, false);
1635
Pierre-Louis Bossart61fb8302020-08-17 23:29:22 +08001636 } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET ||
1637 !clock_stop_quirks) {
Rander Wang6626a612020-08-17 23:29:19 +08001638 ret = sdw_cdns_clock_stop(cdns, true);
1639 if (ret < 0) {
1640 dev_err(dev, "cannot enable clock stop on suspend\n");
1641 return ret;
1642 }
1643
1644 ret = sdw_cdns_enable_interrupt(cdns, false);
1645 if (ret < 0) {
1646 dev_err(dev, "cannot disable interrupts on suspend\n");
1647 return ret;
1648 }
1649
1650 ret = intel_link_power_down(sdw);
1651 if (ret) {
1652 dev_err(dev, "Link power down failed: %d", ret);
1653 return ret;
1654 }
1655
1656 intel_shim_wake(sdw, true);
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001657 } else {
1658 dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1659 __func__, clock_stop_quirks);
1660 ret = -EINVAL;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001661 }
1662
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001663 return ret;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001664}
1665
Bard Liaof046b232020-08-24 21:32:34 +08001666static int __maybe_unused intel_resume(struct device *dev)
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001667{
1668 struct sdw_cdns *cdns = dev_get_drvdata(dev);
1669 struct sdw_intel *sdw = cdns_to_intel(cdns);
1670 struct sdw_bus *bus = &cdns->bus;
Pierre-Louis Bossarta2d9c162020-08-17 23:29:17 +08001671 int link_flags;
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001672 bool multi_link;
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001673 int ret;
1674
1675 if (bus->prop.hw_disabled) {
1676 dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1677 bus->link_id);
1678 return 0;
1679 }
1680
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001681 link_flags = md_flags >> (bus->link_id * 8);
1682 multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1683
Pierre-Louis Bossartb61b8b32020-08-17 23:29:13 +08001684 if (pm_runtime_suspended(dev)) {
1685 dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__);
1686
1687 /* follow required sequence from runtime_pm.rst */
1688 pm_runtime_disable(dev);
1689 pm_runtime_set_active(dev);
1690 pm_runtime_mark_last_busy(dev);
1691 pm_runtime_enable(dev);
Pierre-Louis Bossarta2d9c162020-08-17 23:29:17 +08001692
1693 link_flags = md_flags >> (bus->link_id * 8);
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001694
Pierre-Louis Bossarta2d9c162020-08-17 23:29:17 +08001695 if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
1696 pm_runtime_idle(dev);
Pierre-Louis Bossartb61b8b32020-08-17 23:29:13 +08001697 }
1698
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001699 ret = intel_init(sdw);
1700 if (ret) {
1701 dev_err(dev, "%s failed: %d", __func__, ret);
1702 return ret;
1703 }
1704
Pierre-Louis Bossart99b6a302020-08-17 23:29:15 +08001705 /*
1706 * make sure all Slaves are tagged as UNATTACHED and provide
1707 * reason for reinitialization
1708 */
1709 sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
1710
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001711 ret = sdw_cdns_enable_interrupt(cdns, true);
1712 if (ret < 0) {
1713 dev_err(dev, "cannot enable interrupts during resume\n");
1714 return ret;
1715 }
1716
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001717 /*
1718 * follow recommended programming flows to avoid timeouts when
1719 * gsync is enabled
1720 */
1721 if (multi_link)
1722 intel_shim_sync_arm(sdw);
1723
1724 ret = sdw_cdns_init(&sdw->cdns);
1725 if (ret < 0) {
1726 dev_err(dev, "unable to initialize Cadence IP during resume\n");
1727 return ret;
1728 }
1729
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001730 ret = sdw_cdns_exit_reset(cdns);
1731 if (ret < 0) {
1732 dev_err(dev, "unable to exit bus reset sequence during resume\n");
1733 return ret;
1734 }
1735
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001736 if (multi_link) {
1737 ret = intel_shim_sync_go(sdw);
1738 if (ret < 0) {
1739 dev_err(dev, "sync go failed during resume\n");
1740 return ret;
1741 }
1742 }
1743
Pierre-Louis Bossartcb1e6d52020-08-17 23:29:14 +08001744 /*
1745 * after system resume, the pm_runtime suspend() may kick in
1746 * during the enumeration, before any children device force the
1747 * master device to remain active. Using pm_runtime_get()
1748 * routines is not really possible, since it'd prevent the
1749 * master from suspending.
1750 * A reasonable compromise is to update the pm_runtime
1751 * counters and delay the pm_runtime suspend by several
1752 * seconds, by when all enumeration should be complete.
1753 */
1754 pm_runtime_mark_last_busy(dev);
1755
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001756 return ret;
1757}
1758
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001759static int intel_resume_runtime(struct device *dev)
1760{
1761 struct sdw_cdns *cdns = dev_get_drvdata(dev);
1762 struct sdw_intel *sdw = cdns_to_intel(cdns);
1763 struct sdw_bus *bus = &cdns->bus;
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001764 u32 clock_stop_quirks;
Rander Wang08abad92020-08-17 23:29:23 +08001765 bool clock_stop0;
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001766 int link_flags;
1767 bool multi_link;
Rander Wang08abad92020-08-17 23:29:23 +08001768 int status;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001769 int ret;
1770
1771 if (bus->prop.hw_disabled) {
1772 dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
1773 bus->link_id);
1774 return 0;
1775 }
1776
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001777 link_flags = md_flags >> (bus->link_id * 8);
1778 multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
1779
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001780 clock_stop_quirks = sdw->link_res->clock_stop_quirks;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001781
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001782 if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
1783 ret = intel_init(sdw);
1784 if (ret) {
1785 dev_err(dev, "%s failed: %d", __func__, ret);
1786 return ret;
1787 }
Pierre-Louis Bossart99b6a302020-08-17 23:29:15 +08001788
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001789 /*
1790 * make sure all Slaves are tagged as UNATTACHED and provide
1791 * reason for reinitialization
1792 */
1793 sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001794
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001795 ret = sdw_cdns_enable_interrupt(cdns, true);
1796 if (ret < 0) {
1797 dev_err(dev, "cannot enable interrupts during resume\n");
1798 return ret;
1799 }
1800
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001801 /*
1802 * follow recommended programming flows to avoid
1803 * timeouts when gsync is enabled
1804 */
1805 if (multi_link)
1806 intel_shim_sync_arm(sdw);
1807
1808 ret = sdw_cdns_init(&sdw->cdns);
1809 if (ret < 0) {
1810 dev_err(dev, "unable to initialize Cadence IP during resume\n");
1811 return ret;
1812 }
1813
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001814 ret = sdw_cdns_exit_reset(cdns);
1815 if (ret < 0) {
1816 dev_err(dev, "unable to exit bus reset sequence during resume\n");
1817 return ret;
1818 }
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001819
1820 if (multi_link) {
1821 ret = intel_shim_sync_go(sdw);
1822 if (ret < 0) {
1823 dev_err(dev, "sync go failed during resume\n");
1824 return ret;
1825 }
1826 }
Rander Wang6626a612020-08-17 23:29:19 +08001827 } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
1828 ret = intel_init(sdw);
1829 if (ret) {
1830 dev_err(dev, "%s failed: %d", __func__, ret);
1831 return ret;
1832 }
1833
1834 /*
Rander Wang08abad92020-08-17 23:29:23 +08001835 * An exception condition occurs for the CLK_STOP_BUS_RESET
1836 * case if one or more masters remain active. In this condition,
1837 * all the masters are powered on for they are in the same power
1838 * domain. Master can preserve its context for clock stop0, so
1839 * there is no need to clear slave status and reset bus.
1840 */
1841 clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
1842
Rander Wang08abad92020-08-17 23:29:23 +08001843 if (!clock_stop0) {
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001844
1845 /*
Pierre-Louis Bossart857a7c42020-09-01 23:05:49 +08001846 * make sure all Slaves are tagged as UNATTACHED and
1847 * provide reason for reinitialization
1848 */
1849
Rander Wang08abad92020-08-17 23:29:23 +08001850 status = SDW_UNATTACH_REQUEST_MASTER_RESET;
1851 sdw_clear_slave_status(bus, status);
Rander Wang08abad92020-08-17 23:29:23 +08001852
Pierre-Louis Bossartd78071b2020-09-01 23:05:50 +08001853 ret = sdw_cdns_enable_interrupt(cdns, true);
1854 if (ret < 0) {
1855 dev_err(dev, "cannot enable interrupts during resume\n");
1856 return ret;
1857 }
Rander Wang6626a612020-08-17 23:29:19 +08001858
Pierre-Louis Bossartd78071b2020-09-01 23:05:50 +08001859 /*
1860 * follow recommended programming flows to avoid
1861 * timeouts when gsync is enabled
1862 */
1863 if (multi_link)
1864 intel_shim_sync_arm(sdw);
1865
1866 /*
1867 * Re-initialize the IP since it was powered-off
1868 */
1869 sdw_cdns_init(&sdw->cdns);
1870
1871 } else {
1872 ret = sdw_cdns_enable_interrupt(cdns, true);
1873 if (ret < 0) {
1874 dev_err(dev, "cannot enable interrupts during resume\n");
1875 return ret;
1876 }
Rander Wang6626a612020-08-17 23:29:19 +08001877 }
1878
Rander Wang08abad92020-08-17 23:29:23 +08001879 ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
Rander Wang6626a612020-08-17 23:29:19 +08001880 if (ret < 0) {
1881 dev_err(dev, "unable to restart clock during resume\n");
1882 return ret;
1883 }
Pierre-Louis Bossartd78071b2020-09-01 23:05:50 +08001884
1885 if (!clock_stop0) {
1886 ret = sdw_cdns_exit_reset(cdns);
1887 if (ret < 0) {
1888 dev_err(dev, "unable to exit bus reset sequence during resume\n");
1889 return ret;
1890 }
1891
1892 if (multi_link) {
1893 ret = intel_shim_sync_go(sdw);
1894 if (ret < 0) {
1895 dev_err(sdw->cdns.dev, "sync go failed during resume\n");
1896 return ret;
1897 }
1898 }
1899 }
Pierre-Louis Bossart61fb8302020-08-17 23:29:22 +08001900 } else if (!clock_stop_quirks) {
Pierre-Louis Bossartf748f34e2020-09-01 23:05:54 +08001901
1902 clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
1903 if (!clock_stop0)
1904 dev_err(dev, "%s invalid configuration, clock was not stopped", __func__);
1905
Pierre-Louis Bossart61fb8302020-08-17 23:29:22 +08001906 ret = intel_init(sdw);
1907 if (ret) {
1908 dev_err(dev, "%s failed: %d", __func__, ret);
1909 return ret;
1910 }
1911
1912 ret = sdw_cdns_enable_interrupt(cdns, true);
1913 if (ret < 0) {
1914 dev_err(dev, "cannot enable interrupts during resume\n");
1915 return ret;
1916 }
1917
1918 ret = sdw_cdns_clock_restart(cdns, false);
1919 if (ret < 0) {
1920 dev_err(dev, "unable to resume master during resume\n");
1921 return ret;
1922 }
Pierre-Louis Bossarta320f412020-08-17 23:29:18 +08001923 } else {
1924 dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
1925 __func__, clock_stop_quirks);
1926 ret = -EINVAL;
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001927 }
1928
1929 return ret;
1930}
1931
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001932#endif
1933
1934static const struct dev_pm_ops intel_pm = {
1935 SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume)
Pierre-Louis Bossartebf878e2020-08-17 23:29:12 +08001936 SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL)
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001937};
1938
Vinod Koul71bb8a12017-12-14 11:19:43 +05301939static struct platform_driver sdw_intel_drv = {
Pierre-Louis Bossartb6109dd2020-06-01 02:20:57 +08001940 .probe = intel_master_probe,
1941 .remove = intel_master_remove,
Vinod Koul71bb8a12017-12-14 11:19:43 +05301942 .driver = {
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001943 .name = "intel-sdw",
Pierre-Louis Bossart9b3b4b32020-07-22 04:37:11 +08001944 .pm = &intel_pm,
1945 }
Vinod Koul71bb8a12017-12-14 11:19:43 +05301946};
1947
1948module_platform_driver(sdw_intel_drv);
1949
1950MODULE_LICENSE("Dual BSD/GPL");
Pierre-Louis Bossart6d2c6662020-06-01 02:21:02 +08001951MODULE_ALIAS("platform:intel-sdw");
Vinod Koul71bb8a12017-12-14 11:19:43 +05301952MODULE_DESCRIPTION("Intel Soundwire Master Driver");