projects/linux·blob master · refs

416 lines · 10.3 KiB

1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2//
3// This file is provided under a dual BSD/GPLv2 license. When using or
4// redistributing this file, you may do so under either license.
5//
6// Copyright(c) 2018-2021 Intel Corporation
7//
8// Author: Liam Girdwood <[email protected]>
9//
10
11/*
12 * Hardware interface for audio DSP on Atom devices
13 */
14
15#include <linux/module.h>
16#include <sound/sof.h>
17#include <sound/sof/xtensa.h>
18#include <sound/soc-acpi.h>
19#include <sound/soc-acpi-intel-match.h>
20#include <sound/intel-dsp-config.h>
21#include "../ops.h"
22#include "shim.h"
23#include "atom.h"
24#include "../sof-acpi-dev.h"
25#include "../sof-audio.h"
26#include "../../intel/common/soc-intel-quirks.h"
27
28static void atom_host_done(struct snd_sof_dev *sdev);
29static void atom_dsp_done(struct snd_sof_dev *sdev);
30
31/*
32 * Debug
33 */
34
35static void atom_get_registers(struct snd_sof_dev *sdev,
36 struct sof_ipc_dsp_oops_xtensa *xoops,
37 struct sof_ipc_panic_info *panic_info,
38 u32 *stack, size_t stack_words)
39{
40 u32 offset = sdev->dsp_oops_offset;
41
42 /* first read regsisters */
43 sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
44
45 /* note: variable AR register array is not read */
46
47 /* then get panic info */
48 if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
49 dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
50 xoops->arch_hdr.totalsize);
51 return;
52 }
53 offset += xoops->arch_hdr.totalsize;
54 sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
55
56 /* then get the stack */
57 offset += sizeof(*panic_info);
58 sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
59}
60
61void atom_dump(struct snd_sof_dev *sdev, u32 flags)
62{
63 struct sof_ipc_dsp_oops_xtensa xoops;
64 struct sof_ipc_panic_info panic_info;
65 u32 stack[STACK_DUMP_SIZE];
66 u64 status, panic, imrd, imrx;
67
68 /* now try generic SOF status messages */
69 status = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
70 panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
71 atom_get_registers(sdev, &xoops, &panic_info, stack,
72 STACK_DUMP_SIZE);
73 sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
74 &panic_info, stack, STACK_DUMP_SIZE);
75
76 /* provide some context for firmware debug */
77 imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);
78 imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD);
79 dev_err(sdev->dev,
80 "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
81 str_yes_no(panic & SHIM_IPCX_BUSY),
82 str_yes_no(panic & SHIM_IPCX_DONE), panic);
83 dev_err(sdev->dev,
84 "error: mask host: pending %s complete %s raw 0x%llx\n",
85 str_yes_no(imrx & SHIM_IMRX_BUSY),
86 str_yes_no(imrx & SHIM_IMRX_DONE), imrx);
87 dev_err(sdev->dev,
88 "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
89 str_yes_no(status & SHIM_IPCD_BUSY),
90 str_yes_no(status & SHIM_IPCD_DONE), status);
91 dev_err(sdev->dev,
92 "error: mask DSP: pending %s complete %s raw 0x%llx\n",
93 str_yes_no(imrd & SHIM_IMRD_BUSY),
94 str_yes_no(imrd & SHIM_IMRD_DONE), imrd);
95
96}
97EXPORT_SYMBOL_NS(atom_dump, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
98
99/*
100 * IPC Doorbell IRQ handler and thread.
101 */
102
103irqreturn_t atom_irq_handler(int irq, void *context)
104{
105 struct snd_sof_dev *sdev = context;
106 u64 ipcx, ipcd;
107 int ret = IRQ_NONE;
108
109 ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
110 ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
111
112 if (ipcx & SHIM_BYT_IPCX_DONE) {
113
114 /* reply message from DSP, Mask Done interrupt first */
115 snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
116 SHIM_IMRX,
117 SHIM_IMRX_DONE,
118 SHIM_IMRX_DONE);
119 ret = IRQ_WAKE_THREAD;
120 }
121
122 if (ipcd & SHIM_BYT_IPCD_BUSY) {
123
124 /* new message from DSP, Mask Busy interrupt first */
125 snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
126 SHIM_IMRX,
127 SHIM_IMRX_BUSY,
128 SHIM_IMRX_BUSY);
129 ret = IRQ_WAKE_THREAD;
130 }
131
132 return ret;
133}
134EXPORT_SYMBOL_NS(atom_irq_handler, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
135
136irqreturn_t atom_irq_thread(int irq, void *context)
137{
138 struct snd_sof_dev *sdev = context;
139 u64 ipcx, ipcd;
140
141 ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
142 ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
143
144 /* reply message from DSP */
145 if (ipcx & SHIM_BYT_IPCX_DONE) {
146 /*
147 * handle immediate reply from DSP core. If the msg is
148 * found, set done bit in cmd_done which is called at the
149 * end of message processing function, else set it here
150 * because the done bit can't be set in cmd_done function
151 * which is triggered by msg
152 */
153 guard(spinlock_irq)(&sdev->ipc_lock);
154 snd_sof_ipc_process_reply(sdev, ipcx);
155 atom_dsp_done(sdev);
156 }
157
158 /* new message from DSP */
159 if (ipcd & SHIM_BYT_IPCD_BUSY) {
160
161 /* Handle messages from DSP Core */
162 if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
163 snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET,
164 true);
165 } else {
166 snd_sof_ipc_msgs_rx(sdev);
167 }
168
169 atom_host_done(sdev);
170 }
171
172 return IRQ_HANDLED;
173}
174EXPORT_SYMBOL_NS(atom_irq_thread, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
175
176int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
177{
178 /* unmask and prepare to receive Done interrupt */
179 snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
180 SHIM_IMRX_DONE, 0);
181
182 /* send the message */
183 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
184 msg->msg_size);
185 snd_sof_dsp_write64(sdev, DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
186
187 return 0;
188}
189EXPORT_SYMBOL_NS(atom_send_msg, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
190
191int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
192{
193 return MBOX_OFFSET;
194}
195EXPORT_SYMBOL_NS(atom_get_mailbox_offset, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
196
197int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id)
198{
199 return MBOX_OFFSET;
200}
201EXPORT_SYMBOL_NS(atom_get_window_offset, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
202
203static void atom_host_done(struct snd_sof_dev *sdev)
204{
205 /* clear BUSY bit and set DONE bit - accept new messages */
206 snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCD,
207 SHIM_BYT_IPCD_BUSY |
208 SHIM_BYT_IPCD_DONE,
209 SHIM_BYT_IPCD_DONE);
210
211 /* unmask and prepare to receive next new message */
212 snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
213 SHIM_IMRX_BUSY, 0);
214}
215
216static void atom_dsp_done(struct snd_sof_dev *sdev)
217{
218 /* clear DONE bit - tell DSP we have completed */
219 snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCX,
220 SHIM_BYT_IPCX_DONE, 0);
221}
222
223/*
224 * DSP control.
225 */
226
227int atom_run(struct snd_sof_dev *sdev)
228{
229 int tries = 10;
230
231 /* release stall and wait to unstall */
232 snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
233 SHIM_BYT_CSR_STALL, 0x0);
234 while (tries--) {
235 if (!(snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_CSR) &
236 SHIM_BYT_CSR_PWAITMODE))
237 break;
238 msleep(100);
239 }
240 if (tries < 0)
241 return -ENODEV;
242
243 /* return init core mask */
244 return 1;
245}
246EXPORT_SYMBOL_NS(atom_run, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
247
248int atom_reset(struct snd_sof_dev *sdev)
249{
250 /* put DSP into reset, set reset vector and stall */
251 snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
252 SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
253 SHIM_BYT_CSR_STALL,
254 SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
255 SHIM_BYT_CSR_STALL);
256
257 usleep_range(10, 15);
258
259 /* take DSP out of reset and keep stalled for FW loading */
260 snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
261 SHIM_BYT_CSR_RST, 0);
262
263 return 0;
264}
265EXPORT_SYMBOL_NS(atom_reset, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
266
267static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
268 const char *sof_tplg_filename,
269 const char *ssp_str)
270{
271 const char *tplg_filename = NULL;
272 const char *split_ext;
273 char *filename, *tmp;
274
275 filename = kstrdup(sof_tplg_filename, GFP_KERNEL);
276 if (!filename)
277 return NULL;
278
279 /* this assumes a .tplg extension */
280 tmp = filename;
281 split_ext = strsep(&tmp, ".");
282 if (split_ext)
283 tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
284 "%s-%s.tplg",
285 split_ext, ssp_str);
286 kfree(filename);
287
288 return tplg_filename;
289}
290
291struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev)
292{
293 struct snd_sof_pdata *sof_pdata = sdev->pdata;
294 const struct sof_dev_desc *desc = sof_pdata->desc;
295 struct snd_soc_acpi_mach *mach;
296 struct platform_device *pdev;
297 const char *tplg_filename;
298
299 mach = snd_soc_acpi_find_machine(desc->machines);
300 if (!mach) {
301 dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
302 return NULL;
303 }
304
305 pdev = to_platform_device(sdev->dev);
306 if (soc_intel_is_byt_cr(pdev)) {
307 dev_dbg(sdev->dev,
308 "BYT-CR detected, SSP0 used instead of SSP2\n");
309
310 tplg_filename = fixup_tplg_name(sdev,
311 mach->sof_tplg_filename,
312 "ssp0");
313 } else {
314 tplg_filename = mach->sof_tplg_filename;
315 }
316
317 if (!tplg_filename) {
318 dev_dbg(sdev->dev,
319 "error: no topology filename\n");
320 return NULL;
321 }
322
323 sof_pdata->tplg_filename = tplg_filename;
324 mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
325
326 return mach;
327}
328EXPORT_SYMBOL_NS(atom_machine_select, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
329
330/* Atom DAIs */
331struct snd_soc_dai_driver atom_dai[] = {
332{
333 .name = "ssp0-port",
334 .playback = {
335 .channels_min = 1,
336 .channels_max = 8,
337 },
338 .capture = {
339 .channels_min = 1,
340 .channels_max = 8,
341 },
342},
343{
344 .name = "ssp1-port",
345 .playback = {
346 .channels_min = 1,
347 .channels_max = 8,
348 },
349 .capture = {
350 .channels_min = 1,
351 .channels_max = 8,
352 },
353},
354{
355 .name = "ssp2-port",
356 .playback = {
357 .channels_min = 1,
358 .channels_max = 8,
359 },
360 .capture = {
361 .channels_min = 1,
362 .channels_max = 8,
363 }
364},
365{
366 .name = "ssp3-port",
367 .playback = {
368 .channels_min = 1,
369 .channels_max = 8,
370 },
371 .capture = {
372 .channels_min = 1,
373 .channels_max = 8,
374 },
375},
376{
377 .name = "ssp4-port",
378 .playback = {
379 .channels_min = 1,
380 .channels_max = 8,
381 },
382 .capture = {
383 .channels_min = 1,
384 .channels_max = 8,
385 },
386},
387{
388 .name = "ssp5-port",
389 .playback = {
390 .channels_min = 1,
391 .channels_max = 8,
392 },
393 .capture = {
394 .channels_min = 1,
395 .channels_max = 8,
396 },
397},
398};
399EXPORT_SYMBOL_NS(atom_dai, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
400
401void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
402 struct snd_sof_dev *sdev)
403{
404 struct snd_sof_pdata *pdata = sdev->pdata;
405 const struct sof_dev_desc *desc = pdata->desc;
406 struct snd_soc_acpi_mach_params *mach_params;
407
408 mach_params = &mach->mach_params;
409 mach_params->platform = dev_name(sdev->dev);
410 mach_params->num_dai_drivers = desc->ops->num_drv;
411 mach_params->dai_drivers = desc->ops->drv;
412}
413EXPORT_SYMBOL_NS(atom_set_mach_params, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP");
414
415MODULE_LICENSE("Dual BSD/GPL");
416MODULE_DESCRIPTION("SOF support for Atom platforms");