1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
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.
6 // Copyright(c) 2018 Intel Corporation
8 // Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9 // Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
10 // Rander Wang <rander.wang@intel.com>
11 // Keyon Jie <yang.jie@linux.intel.com>
15 * Hardware interface for audio DSP on Cannonlake.
18 #include <sound/sof/ext_manifest4.h>
19 #include <sound/sof/ipc4/header.h>
20 #include <trace/events/sof_intel.h>
21 #include "../ipc4-priv.h"
25 #include "../sof-audio.h"
27 static const struct snd_sof_debugfs_map cnl_dsp_debugfs
[] = {
28 {"hda", HDA_DSP_HDA_BAR
, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS
},
29 {"pp", HDA_DSP_PP_BAR
, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS
},
30 {"dsp", HDA_DSP_BAR
, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS
},
33 static void cnl_ipc_host_done(struct snd_sof_dev
*sdev
);
34 static void cnl_ipc_dsp_done(struct snd_sof_dev
*sdev
);
36 irqreturn_t
cnl_ipc4_irq_thread(int irq
, void *context
)
38 struct sof_ipc4_msg notification_data
= {{ 0 }};
39 struct snd_sof_dev
*sdev
= context
;
40 bool ack_received
= false;
44 hipcida
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDA
);
45 hipctdr
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDR
);
46 if (hipcida
& CNL_DSP_REG_HIPCIDA_DONE
) {
47 /* DSP received the message */
48 snd_sof_dsp_update_bits(sdev
, HDA_DSP_BAR
,
50 CNL_DSP_REG_HIPCCTL_DONE
, 0);
51 cnl_ipc_dsp_done(sdev
);
57 if (hipctdr
& CNL_DSP_REG_HIPCTDR_BUSY
) {
58 /* Message from DSP (reply or notification) */
59 u32 hipctdd
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
,
61 u32 primary
= hipctdr
& CNL_DSP_REG_HIPCTDR_MSG_MASK
;
62 u32 extension
= hipctdd
& CNL_DSP_REG_HIPCTDD_MSG_MASK
;
64 if (primary
& SOF_IPC4_MSG_DIR_MASK
) {
66 if (likely(sdev
->fw_state
== SOF_FW_BOOT_COMPLETE
)) {
67 struct sof_ipc4_msg
*data
= sdev
->ipc
->msg
.reply_data
;
69 data
->primary
= primary
;
70 data
->extension
= extension
;
72 spin_lock_irq(&sdev
->ipc_lock
);
74 snd_sof_ipc_get_reply(sdev
);
75 cnl_ipc_host_done(sdev
);
76 snd_sof_ipc_reply(sdev
, data
->primary
);
78 spin_unlock_irq(&sdev
->ipc_lock
);
80 dev_dbg_ratelimited(sdev
->dev
,
81 "IPC reply before FW_READY: %#x|%#x\n",
85 /* Notification received */
86 notification_data
.primary
= primary
;
87 notification_data
.extension
= extension
;
89 sdev
->ipc
->msg
.rx_data
= ¬ification_data
;
90 snd_sof_ipc_msgs_rx(sdev
);
91 sdev
->ipc
->msg
.rx_data
= NULL
;
93 /* Let DSP know that we have finished processing the message */
94 cnl_ipc_host_done(sdev
);
101 /* This interrupt is not shared so no need to return IRQ_NONE. */
102 dev_dbg_ratelimited(sdev
->dev
, "nothing to do in IPC IRQ thread\n");
105 struct sof_intel_hda_dev
*hdev
= sdev
->pdata
->hw_pdata
;
107 if (hdev
->delayed_ipc_tx_msg
)
108 cnl_ipc4_send_msg(sdev
, hdev
->delayed_ipc_tx_msg
);
113 EXPORT_SYMBOL_NS(cnl_ipc4_irq_thread
, "SND_SOC_SOF_INTEL_CNL");
115 irqreturn_t
cnl_ipc_irq_thread(int irq
, void *context
)
117 struct snd_sof_dev
*sdev
= context
;
124 bool ipc_irq
= false;
126 hipcida
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDA
);
127 hipctdr
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDR
);
128 hipctdd
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDD
);
129 hipci
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDR
);
131 /* reply message from DSP */
132 if (hipcida
& CNL_DSP_REG_HIPCIDA_DONE
) {
133 msg_ext
= hipci
& CNL_DSP_REG_HIPCIDR_MSG_MASK
;
134 msg
= hipcida
& CNL_DSP_REG_HIPCIDA_MSG_MASK
;
136 trace_sof_intel_ipc_firmware_response(sdev
, msg
, msg_ext
);
138 /* mask Done interrupt */
139 snd_sof_dsp_update_bits(sdev
, HDA_DSP_BAR
,
141 CNL_DSP_REG_HIPCCTL_DONE
, 0);
143 if (likely(sdev
->fw_state
== SOF_FW_BOOT_COMPLETE
)) {
144 spin_lock_irq(&sdev
->ipc_lock
);
146 /* handle immediate reply from DSP core */
147 hda_dsp_ipc_get_reply(sdev
);
148 snd_sof_ipc_reply(sdev
, msg
);
150 cnl_ipc_dsp_done(sdev
);
152 spin_unlock_irq(&sdev
->ipc_lock
);
154 dev_dbg_ratelimited(sdev
->dev
, "IPC reply before FW_READY: %#x\n",
161 /* new message from DSP */
162 if (hipctdr
& CNL_DSP_REG_HIPCTDR_BUSY
) {
163 msg
= hipctdr
& CNL_DSP_REG_HIPCTDR_MSG_MASK
;
164 msg_ext
= hipctdd
& CNL_DSP_REG_HIPCTDD_MSG_MASK
;
166 trace_sof_intel_ipc_firmware_initiated(sdev
, msg
, msg_ext
);
168 /* handle messages from DSP */
169 if ((hipctdr
& SOF_IPC_PANIC_MAGIC_MASK
) == SOF_IPC_PANIC_MAGIC
) {
170 struct sof_intel_hda_dev
*hda
= sdev
->pdata
->hw_pdata
;
171 bool non_recoverable
= true;
174 * This is a PANIC message!
176 * If it is arriving during firmware boot and it is not
177 * the last boot attempt then change the non_recoverable
178 * to false as the DSP might be able to boot in the next
181 if (sdev
->fw_state
== SOF_FW_BOOT_IN_PROGRESS
&&
182 hda
->boot_iteration
< HDA_FW_BOOT_ATTEMPTS
)
183 non_recoverable
= false;
185 snd_sof_dsp_panic(sdev
, HDA_DSP_PANIC_OFFSET(msg_ext
),
188 snd_sof_ipc_msgs_rx(sdev
);
191 cnl_ipc_host_done(sdev
);
198 * This interrupt is not shared so no need to return IRQ_NONE.
200 dev_dbg_ratelimited(sdev
->dev
,
201 "nothing to do in IPC IRQ thread\n");
206 EXPORT_SYMBOL_NS(cnl_ipc_irq_thread
, "SND_SOC_SOF_INTEL_CNL");
208 static void cnl_ipc_host_done(struct snd_sof_dev
*sdev
)
211 * clear busy interrupt to tell dsp controller this
212 * interrupt has been accepted, not trigger it again
214 snd_sof_dsp_update_bits_forced(sdev
, HDA_DSP_BAR
,
216 CNL_DSP_REG_HIPCTDR_BUSY
,
217 CNL_DSP_REG_HIPCTDR_BUSY
);
219 * set done bit to ack dsp the msg has been
220 * processed and send reply msg to dsp
222 snd_sof_dsp_update_bits_forced(sdev
, HDA_DSP_BAR
,
224 CNL_DSP_REG_HIPCTDA_DONE
,
225 CNL_DSP_REG_HIPCTDA_DONE
);
228 static void cnl_ipc_dsp_done(struct snd_sof_dev
*sdev
)
231 * set DONE bit - tell DSP we have received the reply msg
232 * from DSP, and processed it, don't send more reply to host
234 snd_sof_dsp_update_bits_forced(sdev
, HDA_DSP_BAR
,
236 CNL_DSP_REG_HIPCIDA_DONE
,
237 CNL_DSP_REG_HIPCIDA_DONE
);
239 /* unmask Done interrupt */
240 snd_sof_dsp_update_bits(sdev
, HDA_DSP_BAR
,
242 CNL_DSP_REG_HIPCCTL_DONE
,
243 CNL_DSP_REG_HIPCCTL_DONE
);
246 static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg
*msg
,
249 struct sof_ipc_pm_gate
*pm_gate
= msg
->msg_data
;
251 if (pm_gate
->hdr
.cmd
== (SOF_IPC_GLB_PM_MSG
| SOF_IPC_PM_GATE
)) {
252 /* send the compact message via the primary register */
253 *dr
= HDA_IPC_MSG_COMPACT
| HDA_IPC_PM_GATE
;
255 /* send payload via the extended data register */
256 *dd
= pm_gate
->flags
;
264 int cnl_ipc4_send_msg(struct snd_sof_dev
*sdev
, struct snd_sof_ipc_msg
*msg
)
266 struct sof_intel_hda_dev
*hdev
= sdev
->pdata
->hw_pdata
;
267 struct sof_ipc4_msg
*msg_data
= msg
->msg_data
;
269 if (hda_ipc4_tx_is_busy(sdev
)) {
270 hdev
->delayed_ipc_tx_msg
= msg
;
274 hdev
->delayed_ipc_tx_msg
= NULL
;
276 /* send the message via mailbox */
277 if (msg_data
->data_size
)
278 sof_mailbox_write(sdev
, sdev
->host_box
.offset
, msg_data
->data_ptr
,
279 msg_data
->data_size
);
281 snd_sof_dsp_write(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDD
, msg_data
->extension
);
282 snd_sof_dsp_write(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDR
,
283 msg_data
->primary
| CNL_DSP_REG_HIPCIDR_BUSY
);
285 hda_dsp_ipc4_schedule_d0i3_work(hdev
, msg
);
289 EXPORT_SYMBOL_NS(cnl_ipc4_send_msg
, "SND_SOC_SOF_INTEL_CNL");
291 int cnl_ipc_send_msg(struct snd_sof_dev
*sdev
, struct snd_sof_ipc_msg
*msg
)
293 struct sof_intel_hda_dev
*hdev
= sdev
->pdata
->hw_pdata
;
294 struct sof_ipc_cmd_hdr
*hdr
;
299 * Currently the only compact IPC supported is the PM_GATE
300 * IPC which is used for transitioning the DSP between the
301 * D0I0 and D0I3 states. And these are sent only during the
302 * set_power_state() op. Therefore, there will never be a case
303 * that a compact IPC results in the DSP exiting D0I3 without
304 * the host and FW being in sync.
306 if (cnl_compact_ipc_compress(msg
, &dr
, &dd
)) {
307 /* send the message via IPC registers */
308 snd_sof_dsp_write(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDD
,
310 snd_sof_dsp_write(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDR
,
311 CNL_DSP_REG_HIPCIDR_BUSY
| dr
);
315 /* send the message via mailbox */
316 sof_mailbox_write(sdev
, sdev
->host_box
.offset
, msg
->msg_data
,
318 snd_sof_dsp_write(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDR
,
319 CNL_DSP_REG_HIPCIDR_BUSY
);
324 * Use mod_delayed_work() to schedule the delayed work
325 * to avoid scheduling multiple workqueue items when
326 * IPCs are sent at a high-rate. mod_delayed_work()
327 * modifies the timer if the work is pending.
328 * Also, a new delayed work should not be queued after the
329 * CTX_SAVE IPC, which is sent before the DSP enters D3.
331 if (hdr
->cmd
!= (SOF_IPC_GLB_PM_MSG
| SOF_IPC_PM_CTX_SAVE
))
332 mod_delayed_work(system_wq
, &hdev
->d0i3_work
,
333 msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS
));
337 EXPORT_SYMBOL_NS(cnl_ipc_send_msg
, "SND_SOC_SOF_INTEL_CNL");
339 void cnl_ipc_dump(struct snd_sof_dev
*sdev
)
345 hda_ipc_irq_dump(sdev
);
347 /* read IPC status */
348 hipcida
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDA
);
349 hipcctl
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCCTL
);
350 hipctdr
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDR
);
352 /* dump the IPC regs */
353 /* TODO: parse the raw msg */
355 "error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n",
356 hipcida
, hipctdr
, hipcctl
);
358 EXPORT_SYMBOL_NS(cnl_ipc_dump
, "SND_SOC_SOF_INTEL_CNL");
360 void cnl_ipc4_dump(struct snd_sof_dev
*sdev
)
362 u32 hipcidr
, hipcidd
, hipcida
, hipctdr
, hipctdd
, hipctda
, hipcctl
;
364 hda_ipc_irq_dump(sdev
);
366 hipcidr
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDR
);
367 hipcidd
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDD
);
368 hipcida
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCIDA
);
369 hipctdr
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDR
);
370 hipctdd
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDD
);
371 hipctda
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCTDA
);
372 hipcctl
= snd_sof_dsp_read(sdev
, HDA_DSP_BAR
, CNL_DSP_REG_HIPCCTL
);
374 /* dump the IPC regs */
375 /* TODO: parse the raw msg */
377 "Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n",
378 hipcidr
, hipcidd
, hipcida
, hipctdr
, hipctdd
, hipctda
, hipcctl
);
380 EXPORT_SYMBOL_NS(cnl_ipc4_dump
, "SND_SOC_SOF_INTEL_CNL");
383 struct snd_sof_dsp_ops sof_cnl_ops
;
384 EXPORT_SYMBOL_NS(sof_cnl_ops
, "SND_SOC_SOF_INTEL_CNL");
386 int sof_cnl_ops_init(struct snd_sof_dev
*sdev
)
388 /* common defaults */
389 memcpy(&sof_cnl_ops
, &sof_hda_common_ops
, sizeof(struct snd_sof_dsp_ops
));
391 /* probe/remove/shutdown */
392 sof_cnl_ops
.shutdown
= hda_dsp_shutdown
;
395 if (sdev
->pdata
->ipc_type
== SOF_IPC_TYPE_3
) {
397 sof_cnl_ops
.irq_thread
= cnl_ipc_irq_thread
;
400 sof_cnl_ops
.send_msg
= cnl_ipc_send_msg
;
403 sof_cnl_ops
.ipc_dump
= cnl_ipc_dump
;
405 sof_cnl_ops
.set_power_state
= hda_dsp_set_power_state_ipc3
;
408 if (sdev
->pdata
->ipc_type
== SOF_IPC_TYPE_4
) {
409 struct sof_ipc4_fw_data
*ipc4_data
;
411 sdev
->private = kzalloc(sizeof(*ipc4_data
), GFP_KERNEL
);
415 ipc4_data
= sdev
->private;
416 ipc4_data
->manifest_fw_hdr_offset
= SOF_MAN4_FW_HDR_OFFSET
;
418 ipc4_data
->mtrace_type
= SOF_IPC4_MTRACE_INTEL_CAVS_1_8
;
420 /* External library loading support */
421 ipc4_data
->load_library
= hda_dsp_ipc4_load_library
;
424 sof_cnl_ops
.irq_thread
= cnl_ipc4_irq_thread
;
427 sof_cnl_ops
.send_msg
= cnl_ipc4_send_msg
;
430 sof_cnl_ops
.ipc_dump
= cnl_ipc4_dump
;
432 sof_cnl_ops
.set_power_state
= hda_dsp_set_power_state_ipc4
;
435 /* set DAI driver ops */
436 hda_set_dai_drv_ops(sdev
, &sof_cnl_ops
);
439 sof_cnl_ops
.debug_map
= cnl_dsp_debugfs
;
440 sof_cnl_ops
.debug_map_count
= ARRAY_SIZE(cnl_dsp_debugfs
);
442 /* pre/post fw run */
443 sof_cnl_ops
.post_fw_run
= hda_dsp_post_fw_run
;
446 sof_cnl_ops
.run
= hda_dsp_cl_boot_firmware
;
448 /* dsp core get/put */
449 sof_cnl_ops
.core_get
= hda_dsp_core_get
;
453 EXPORT_SYMBOL_NS(sof_cnl_ops_init
, "SND_SOC_SOF_INTEL_CNL");
455 const struct sof_intel_dsp_desc cnl_chip_info
= {
459 .host_managed_cores_mask
= GENMASK(3, 0),
460 .ipc_req
= CNL_DSP_REG_HIPCIDR
,
461 .ipc_req_mask
= CNL_DSP_REG_HIPCIDR_BUSY
,
462 .ipc_ack
= CNL_DSP_REG_HIPCIDA
,
463 .ipc_ack_mask
= CNL_DSP_REG_HIPCIDA_DONE
,
464 .ipc_ctl
= CNL_DSP_REG_HIPCCTL
,
465 .rom_status_reg
= HDA_DSP_SRAM_REG_ROM_STATUS
,
466 .rom_init_timeout
= 300,
467 .ssp_count
= CNL_SSP_COUNT
,
468 .ssp_base_offset
= CNL_SSP_BASE_OFFSET
,
469 .sdw_shim_base
= SDW_SHIM_BASE
,
470 .sdw_alh_base
= SDW_ALH_BASE
,
471 .d0i3_offset
= SOF_HDA_VS_D0I3C
,
472 .read_sdw_lcount
= hda_sdw_check_lcount_common
,
473 .enable_sdw_irq
= hda_common_enable_sdw_irq
,
474 .check_sdw_irq
= hda_common_check_sdw_irq
,
475 .check_sdw_wakeen_irq
= hda_sdw_check_wakeen_irq_common
,
476 .sdw_process_wakeen
= hda_sdw_process_wakeen_common
,
477 .check_ipc_irq
= hda_dsp_check_ipc_irq
,
478 .cl_init
= cl_dsp_init
,
479 .power_down_dsp
= hda_power_down_dsp
,
480 .disable_interrupts
= hda_dsp_disable_interrupts
,
481 .hw_ip_version
= SOF_INTEL_CAVS_1_8
,
485 * JasperLake is technically derived from IceLake, and should be in
486 * described in icl.c. However since JasperLake was designed with
487 * two cores, it cannot support the IceLake-specific power-up sequences
488 * which rely on core3. To simplify, JasperLake uses the CannonLake ops and
489 * is described in cnl.c
491 const struct sof_intel_dsp_desc jsl_chip_info
= {
495 .host_managed_cores_mask
= GENMASK(1, 0),
496 .ipc_req
= CNL_DSP_REG_HIPCIDR
,
497 .ipc_req_mask
= CNL_DSP_REG_HIPCIDR_BUSY
,
498 .ipc_ack
= CNL_DSP_REG_HIPCIDA
,
499 .ipc_ack_mask
= CNL_DSP_REG_HIPCIDA_DONE
,
500 .ipc_ctl
= CNL_DSP_REG_HIPCCTL
,
501 .rom_status_reg
= HDA_DSP_SRAM_REG_ROM_STATUS
,
502 .rom_init_timeout
= 300,
503 .ssp_count
= ICL_SSP_COUNT
,
504 .ssp_base_offset
= CNL_SSP_BASE_OFFSET
,
505 .sdw_shim_base
= SDW_SHIM_BASE
,
506 .sdw_alh_base
= SDW_ALH_BASE
,
507 .d0i3_offset
= SOF_HDA_VS_D0I3C
,
508 .read_sdw_lcount
= hda_sdw_check_lcount_common
,
509 .enable_sdw_irq
= hda_common_enable_sdw_irq
,
510 .check_sdw_irq
= hda_common_check_sdw_irq
,
511 .check_sdw_wakeen_irq
= hda_sdw_check_wakeen_irq_common
,
512 .sdw_process_wakeen
= hda_sdw_process_wakeen_common
,
513 .check_ipc_irq
= hda_dsp_check_ipc_irq
,
514 .cl_init
= cl_dsp_init
,
515 .power_down_dsp
= hda_power_down_dsp
,
516 .disable_interrupts
= hda_dsp_disable_interrupts
,
517 .hw_ip_version
= SOF_INTEL_CAVS_2_0
,
519 EXPORT_SYMBOL_NS(jsl_chip_info
, "SND_SOC_SOF_INTEL_CNL");