| 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 Intel Corporation |
| 7 | // |
| 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> |
| 12 | // |
| 13 | |
| 14 | /* |
| 15 | * Hardware interface for audio DSP on Cannonlake. |
| 16 | */ |
| 17 | |
| 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" |
| 22 | #include "../ops.h" |
| 23 | #include "hda.h" |
| 24 | #include "hda-ipc.h" |
| 25 | #include "../sof-audio.h" |
| 26 | |
| 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}, |
| 31 | }; |
| 32 | |
| 33 | static void cnl_ipc_host_done(struct snd_sof_dev *sdev); |
| 34 | static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev); |
| 35 | |
| 36 | irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) |
| 37 | { |
| 38 | struct sof_ipc4_msg notification_data = {{ 0 }}; |
| 39 | struct snd_sof_dev *sdev = context; |
| 40 | bool ack_received = false; |
| 41 | bool ipc_irq = false; |
| 42 | u32 hipcida, hipctdr; |
| 43 | |
| 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, |
| 49 | CNL_DSP_REG_HIPCCTL, |
| 50 | CNL_DSP_REG_HIPCCTL_DONE, value: 0); |
| 51 | cnl_ipc_dsp_done(sdev); |
| 52 | |
| 53 | ipc_irq = true; |
| 54 | ack_received = true; |
| 55 | } |
| 56 | |
| 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, |
| 60 | CNL_DSP_REG_HIPCTDD); |
| 61 | u32 primary = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; |
| 62 | u32 extension = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; |
| 63 | |
| 64 | if (primary & SOF_IPC4_MSG_DIR_MASK) { |
| 65 | /* Reply received */ |
| 66 | if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { |
| 67 | struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data; |
| 68 | |
| 69 | data->primary = primary; |
| 70 | data->extension = extension; |
| 71 | |
| 72 | spin_lock_irq(lock: &sdev->ipc_lock); |
| 73 | |
| 74 | snd_sof_ipc_get_reply(sdev); |
| 75 | cnl_ipc_host_done(sdev); |
| 76 | snd_sof_ipc_reply(sdev, msg_id: data->primary); |
| 77 | |
| 78 | spin_unlock_irq(lock: &sdev->ipc_lock); |
| 79 | } else { |
| 80 | dev_dbg_ratelimited(sdev->dev, |
| 81 | "IPC reply before FW_READY: %#x|%#x\n" , |
| 82 | primary, extension); |
| 83 | } |
| 84 | } else { |
| 85 | /* Notification received */ |
| 86 | notification_data.primary = primary; |
| 87 | notification_data.extension = extension; |
| 88 | |
| 89 | sdev->ipc->msg.rx_data = ¬ification_data; |
| 90 | snd_sof_ipc_msgs_rx(sdev); |
| 91 | sdev->ipc->msg.rx_data = NULL; |
| 92 | |
| 93 | /* Let DSP know that we have finished processing the message */ |
| 94 | cnl_ipc_host_done(sdev); |
| 95 | } |
| 96 | |
| 97 | ipc_irq = true; |
| 98 | } |
| 99 | |
| 100 | if (!ipc_irq) |
| 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" ); |
| 103 | |
| 104 | if (ack_received) { |
| 105 | struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; |
| 106 | |
| 107 | if (hdev->delayed_ipc_tx_msg) |
| 108 | cnl_ipc4_send_msg(sdev, msg: hdev->delayed_ipc_tx_msg); |
| 109 | } |
| 110 | |
| 111 | return IRQ_HANDLED; |
| 112 | } |
| 113 | EXPORT_SYMBOL_NS(cnl_ipc4_irq_thread, "SND_SOC_SOF_INTEL_CNL" ); |
| 114 | |
| 115 | irqreturn_t cnl_ipc_irq_thread(int irq, void *context) |
| 116 | { |
| 117 | struct snd_sof_dev *sdev = context; |
| 118 | u32 hipci; |
| 119 | u32 hipcida; |
| 120 | u32 hipctdr; |
| 121 | u32 hipctdd; |
| 122 | u32 msg; |
| 123 | u32 msg_ext; |
| 124 | bool ipc_irq = false; |
| 125 | |
| 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); |
| 130 | |
| 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; |
| 135 | |
| 136 | trace_sof_intel_ipc_firmware_response(sdev, msg, msg_ext); |
| 137 | |
| 138 | /* mask Done interrupt */ |
| 139 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, |
| 140 | CNL_DSP_REG_HIPCCTL, |
| 141 | CNL_DSP_REG_HIPCCTL_DONE, value: 0); |
| 142 | |
| 143 | if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { |
| 144 | spin_lock_irq(lock: &sdev->ipc_lock); |
| 145 | |
| 146 | /* handle immediate reply from DSP core */ |
| 147 | hda_dsp_ipc_get_reply(sdev); |
| 148 | snd_sof_ipc_reply(sdev, msg_id: msg); |
| 149 | |
| 150 | cnl_ipc_dsp_done(sdev); |
| 151 | |
| 152 | spin_unlock_irq(lock: &sdev->ipc_lock); |
| 153 | } else { |
| 154 | dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n" , |
| 155 | msg); |
| 156 | } |
| 157 | |
| 158 | ipc_irq = true; |
| 159 | } |
| 160 | |
| 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; |
| 165 | |
| 166 | trace_sof_intel_ipc_firmware_initiated(sdev, msg, msg_ext); |
| 167 | |
| 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; |
| 172 | |
| 173 | /* |
| 174 | * This is a PANIC message! |
| 175 | * |
| 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 |
| 179 | * iteration(s) |
| 180 | */ |
| 181 | if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && |
| 182 | hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) |
| 183 | non_recoverable = false; |
| 184 | |
| 185 | snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), |
| 186 | non_recoverable); |
| 187 | } else { |
| 188 | snd_sof_ipc_msgs_rx(sdev); |
| 189 | } |
| 190 | |
| 191 | cnl_ipc_host_done(sdev); |
| 192 | |
| 193 | ipc_irq = true; |
| 194 | } |
| 195 | |
| 196 | if (!ipc_irq) { |
| 197 | /* |
| 198 | * This interrupt is not shared so no need to return IRQ_NONE. |
| 199 | */ |
| 200 | dev_dbg_ratelimited(sdev->dev, |
| 201 | "nothing to do in IPC IRQ thread\n" ); |
| 202 | } |
| 203 | |
| 204 | return IRQ_HANDLED; |
| 205 | } |
| 206 | EXPORT_SYMBOL_NS(cnl_ipc_irq_thread, "SND_SOC_SOF_INTEL_CNL" ); |
| 207 | |
| 208 | static void cnl_ipc_host_done(struct snd_sof_dev *sdev) |
| 209 | { |
| 210 | /* |
| 211 | * clear busy interrupt to tell dsp controller this |
| 212 | * interrupt has been accepted, not trigger it again |
| 213 | */ |
| 214 | snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, |
| 215 | CNL_DSP_REG_HIPCTDR, |
| 216 | CNL_DSP_REG_HIPCTDR_BUSY, |
| 217 | CNL_DSP_REG_HIPCTDR_BUSY); |
| 218 | /* |
| 219 | * set done bit to ack dsp the msg has been |
| 220 | * processed and send reply msg to dsp |
| 221 | */ |
| 222 | snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, |
| 223 | CNL_DSP_REG_HIPCTDA, |
| 224 | CNL_DSP_REG_HIPCTDA_DONE, |
| 225 | CNL_DSP_REG_HIPCTDA_DONE); |
| 226 | } |
| 227 | |
| 228 | static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev) |
| 229 | { |
| 230 | /* |
| 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 |
| 233 | */ |
| 234 | snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, |
| 235 | CNL_DSP_REG_HIPCIDA, |
| 236 | CNL_DSP_REG_HIPCIDA_DONE, |
| 237 | CNL_DSP_REG_HIPCIDA_DONE); |
| 238 | |
| 239 | /* unmask Done interrupt */ |
| 240 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, |
| 241 | CNL_DSP_REG_HIPCCTL, |
| 242 | CNL_DSP_REG_HIPCCTL_DONE, |
| 243 | CNL_DSP_REG_HIPCCTL_DONE); |
| 244 | } |
| 245 | |
| 246 | static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, |
| 247 | u32 *dr, u32 *dd) |
| 248 | { |
| 249 | struct sof_ipc_pm_gate *pm_gate = msg->msg_data; |
| 250 | |
| 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; |
| 254 | |
| 255 | /* send payload via the extended data register */ |
| 256 | *dd = pm_gate->flags; |
| 257 | |
| 258 | return true; |
| 259 | } |
| 260 | |
| 261 | return false; |
| 262 | } |
| 263 | |
| 264 | int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) |
| 265 | { |
| 266 | struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; |
| 267 | struct sof_ipc4_msg *msg_data = msg->msg_data; |
| 268 | |
| 269 | if (hda_ipc4_tx_is_busy(sdev)) { |
| 270 | hdev->delayed_ipc_tx_msg = msg; |
| 271 | return 0; |
| 272 | } |
| 273 | |
| 274 | hdev->delayed_ipc_tx_msg = NULL; |
| 275 | |
| 276 | /* send the message via mailbox */ |
| 277 | if (msg_data->data_size) |
| 278 | sof_mailbox_write(sdev, offset: sdev->host_box.offset, message: msg_data->data_ptr, |
| 279 | bytes: msg_data->data_size); |
| 280 | |
| 281 | snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, value: msg_data->extension); |
| 282 | snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, |
| 283 | value: msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY); |
| 284 | |
| 285 | hda_dsp_ipc4_schedule_d0i3_work(hdev, msg); |
| 286 | |
| 287 | return 0; |
| 288 | } |
| 289 | EXPORT_SYMBOL_NS(cnl_ipc4_send_msg, "SND_SOC_SOF_INTEL_CNL" ); |
| 290 | |
| 291 | int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) |
| 292 | { |
| 293 | struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; |
| 294 | struct sof_ipc_cmd_hdr *hdr; |
| 295 | u32 dr = 0; |
| 296 | u32 dd = 0; |
| 297 | |
| 298 | /* |
| 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. |
| 305 | */ |
| 306 | if (cnl_compact_ipc_compress(msg, dr: &dr, dd: &dd)) { |
| 307 | /* send the message via IPC registers */ |
| 308 | snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, |
| 309 | value: dd); |
| 310 | snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, |
| 311 | CNL_DSP_REG_HIPCIDR_BUSY | dr); |
| 312 | return 0; |
| 313 | } |
| 314 | |
| 315 | /* send the message via mailbox */ |
| 316 | sof_mailbox_write(sdev, offset: sdev->host_box.offset, message: msg->msg_data, |
| 317 | bytes: msg->msg_size); |
| 318 | snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, |
| 319 | CNL_DSP_REG_HIPCIDR_BUSY); |
| 320 | |
| 321 | hdr = msg->msg_data; |
| 322 | |
| 323 | /* |
| 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. |
| 330 | */ |
| 331 | if (hdr->cmd != (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) |
| 332 | mod_delayed_work(wq: system_dfl_wq, dwork: &hdev->d0i3_work, |
| 333 | delay: msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS)); |
| 334 | |
| 335 | return 0; |
| 336 | } |
| 337 | EXPORT_SYMBOL_NS(cnl_ipc_send_msg, "SND_SOC_SOF_INTEL_CNL" ); |
| 338 | |
| 339 | void cnl_ipc_dump(struct snd_sof_dev *sdev) |
| 340 | { |
| 341 | u32 hipcctl; |
| 342 | u32 hipcida; |
| 343 | u32 hipctdr; |
| 344 | |
| 345 | hda_ipc_irq_dump(sdev); |
| 346 | |
| 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); |
| 351 | |
| 352 | /* dump the IPC regs */ |
| 353 | /* TODO: parse the raw msg */ |
| 354 | dev_err(sdev->dev, |
| 355 | "error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n" , |
| 356 | hipcida, hipctdr, hipcctl); |
| 357 | } |
| 358 | EXPORT_SYMBOL_NS(cnl_ipc_dump, "SND_SOC_SOF_INTEL_CNL" ); |
| 359 | |
| 360 | void cnl_ipc4_dump(struct snd_sof_dev *sdev) |
| 361 | { |
| 362 | u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl; |
| 363 | |
| 364 | hda_ipc_irq_dump(sdev); |
| 365 | |
| 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); |
| 373 | |
| 374 | /* dump the IPC regs */ |
| 375 | /* TODO: parse the raw msg */ |
| 376 | dev_err(sdev->dev, |
| 377 | "Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n" , |
| 378 | hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl); |
| 379 | } |
| 380 | EXPORT_SYMBOL_NS(cnl_ipc4_dump, "SND_SOC_SOF_INTEL_CNL" ); |
| 381 | |
| 382 | /* cannonlake ops */ |
| 383 | struct snd_sof_dsp_ops sof_cnl_ops; |
| 384 | EXPORT_SYMBOL_NS(sof_cnl_ops, "SND_SOC_SOF_INTEL_CNL" ); |
| 385 | |
| 386 | int sof_cnl_ops_init(struct snd_sof_dev *sdev) |
| 387 | { |
| 388 | /* common defaults */ |
| 389 | memcpy(&sof_cnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); |
| 390 | |
| 391 | /* probe/remove/shutdown */ |
| 392 | sof_cnl_ops.shutdown = hda_dsp_shutdown; |
| 393 | |
| 394 | /* ipc */ |
| 395 | if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { |
| 396 | /* doorbell */ |
| 397 | sof_cnl_ops.irq_thread = cnl_ipc_irq_thread; |
| 398 | |
| 399 | /* ipc */ |
| 400 | sof_cnl_ops.send_msg = cnl_ipc_send_msg; |
| 401 | |
| 402 | /* debug */ |
| 403 | sof_cnl_ops.ipc_dump = cnl_ipc_dump; |
| 404 | |
| 405 | sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc3; |
| 406 | } |
| 407 | |
| 408 | if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { |
| 409 | struct sof_ipc4_fw_data *ipc4_data; |
| 410 | |
| 411 | sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL); |
| 412 | if (!sdev->private) |
| 413 | return -ENOMEM; |
| 414 | |
| 415 | ipc4_data = sdev->private; |
| 416 | ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; |
| 417 | |
| 418 | ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8; |
| 419 | |
| 420 | /* External library loading support */ |
| 421 | ipc4_data->load_library = hda_dsp_ipc4_load_library; |
| 422 | |
| 423 | /* doorbell */ |
| 424 | sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread; |
| 425 | |
| 426 | /* ipc */ |
| 427 | sof_cnl_ops.send_msg = cnl_ipc4_send_msg; |
| 428 | |
| 429 | /* debug */ |
| 430 | sof_cnl_ops.ipc_dump = cnl_ipc4_dump; |
| 431 | |
| 432 | sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc4; |
| 433 | } |
| 434 | |
| 435 | /* set DAI driver ops */ |
| 436 | hda_set_dai_drv_ops(sdev, ops: &sof_cnl_ops); |
| 437 | |
| 438 | /* debug */ |
| 439 | sof_cnl_ops.debug_map = cnl_dsp_debugfs; |
| 440 | sof_cnl_ops.debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs); |
| 441 | |
| 442 | /* pre/post fw run */ |
| 443 | sof_cnl_ops.post_fw_run = hda_dsp_post_fw_run; |
| 444 | |
| 445 | /* firmware run */ |
| 446 | sof_cnl_ops.run = hda_dsp_cl_boot_firmware; |
| 447 | |
| 448 | /* dsp core get/put */ |
| 449 | sof_cnl_ops.core_get = hda_dsp_core_get; |
| 450 | |
| 451 | return 0; |
| 452 | }; |
| 453 | EXPORT_SYMBOL_NS(sof_cnl_ops_init, "SND_SOC_SOF_INTEL_CNL" ); |
| 454 | |
| 455 | const struct sof_intel_dsp_desc cnl_chip_info = { |
| 456 | /* Cannonlake */ |
| 457 | .cores_num = 4, |
| 458 | .init_core_mask = 1, |
| 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, |
| 482 | .platform = "cnl" , |
| 483 | }; |
| 484 | |
| 485 | /* |
| 486 | * JasperLake is technically derived from IceLake, and should be in |
| 487 | * described in icl.c. However since JasperLake was designed with |
| 488 | * two cores, it cannot support the IceLake-specific power-up sequences |
| 489 | * which rely on core3. To simplify, JasperLake uses the CannonLake ops and |
| 490 | * is described in cnl.c |
| 491 | */ |
| 492 | const struct sof_intel_dsp_desc jsl_chip_info = { |
| 493 | /* Jasperlake */ |
| 494 | .cores_num = 2, |
| 495 | .init_core_mask = 1, |
| 496 | .host_managed_cores_mask = GENMASK(1, 0), |
| 497 | .ipc_req = CNL_DSP_REG_HIPCIDR, |
| 498 | .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, |
| 499 | .ipc_ack = CNL_DSP_REG_HIPCIDA, |
| 500 | .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, |
| 501 | .ipc_ctl = CNL_DSP_REG_HIPCCTL, |
| 502 | .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, |
| 503 | .rom_init_timeout = 300, |
| 504 | .ssp_count = ICL_SSP_COUNT, |
| 505 | .ssp_base_offset = CNL_SSP_BASE_OFFSET, |
| 506 | .sdw_shim_base = SDW_SHIM_BASE, |
| 507 | .sdw_alh_base = SDW_ALH_BASE, |
| 508 | .d0i3_offset = SOF_HDA_VS_D0I3C, |
| 509 | .read_sdw_lcount = hda_sdw_check_lcount_common, |
| 510 | .enable_sdw_irq = hda_common_enable_sdw_irq, |
| 511 | .check_sdw_irq = hda_common_check_sdw_irq, |
| 512 | .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, |
| 513 | .sdw_process_wakeen = hda_sdw_process_wakeen_common, |
| 514 | .check_ipc_irq = hda_dsp_check_ipc_irq, |
| 515 | .cl_init = cl_dsp_init, |
| 516 | .power_down_dsp = hda_power_down_dsp, |
| 517 | .disable_interrupts = hda_dsp_disable_interrupts, |
| 518 | .hw_ip_version = SOF_INTEL_CAVS_2_0, |
| 519 | .platform = "jsl" , |
| 520 | }; |
| 521 | EXPORT_SYMBOL_NS(jsl_chip_info, "SND_SOC_SOF_INTEL_CNL" ); |
| 522 | |