162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 1062306a36Sopenharmony_ci// Rander Wang <rander.wang@intel.com> 1162306a36Sopenharmony_ci// Keyon Jie <yang.jie@linux.intel.com> 1262306a36Sopenharmony_ci// 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Hardware interface for HDA DSP code loader 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/firmware.h> 1962306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 2062306a36Sopenharmony_ci#include <sound/hda_register.h> 2162306a36Sopenharmony_ci#include <sound/sof.h> 2262306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h> 2362306a36Sopenharmony_ci#include "ext_manifest.h" 2462306a36Sopenharmony_ci#include "../ipc4-priv.h" 2562306a36Sopenharmony_ci#include "../ops.h" 2662306a36Sopenharmony_ci#include "../sof-priv.h" 2762306a36Sopenharmony_ci#include "hda.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 3262306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 3362306a36Sopenharmony_ci int i; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* DSP is powered up, set all SSPs to clock consumer/codec provider mode */ 3662306a36Sopenharmony_ci for (i = 0; i < chip->ssp_count; i++) { 3762306a36Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 3862306a36Sopenharmony_ci chip->ssp_base_offset 3962306a36Sopenharmony_ci + i * SSP_DEV_MEM_SIZE 4062306a36Sopenharmony_ci + SSP_SSC1_OFFSET, 4162306a36Sopenharmony_ci SSP_SET_CBP_CFP, 4262306a36Sopenharmony_ci SSP_SET_CBP_CFP); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, 4762306a36Sopenharmony_ci unsigned int size, struct snd_dma_buffer *dmab, 4862306a36Sopenharmony_ci int direction) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 5162306a36Sopenharmony_ci struct hdac_stream *hstream; 5262306a36Sopenharmony_ci struct pci_dev *pci = to_pci_dev(sdev->dev); 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci hext_stream = hda_dsp_stream_get(sdev, direction, 0); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (!hext_stream) { 5862306a36Sopenharmony_ci dev_err(sdev->dev, "error: no stream available\n"); 5962306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci hstream = &hext_stream->hstream; 6262306a36Sopenharmony_ci hstream->substream = NULL; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* allocate DMA buffer */ 6562306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); 6662306a36Sopenharmony_ci if (ret < 0) { 6762306a36Sopenharmony_ci dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret); 6862306a36Sopenharmony_ci goto out_put; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci hstream->period_bytes = 0;/* initialize period_bytes */ 7262306a36Sopenharmony_ci hstream->format_val = format; 7362306a36Sopenharmony_ci hstream->bufsize = size; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_CAPTURE) { 7662306a36Sopenharmony_ci ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); 7762306a36Sopenharmony_ci if (ret < 0) { 7862306a36Sopenharmony_ci dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret); 7962306a36Sopenharmony_ci goto out_free; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } else { 8262306a36Sopenharmony_ci ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); 8362306a36Sopenharmony_ci if (ret < 0) { 8462306a36Sopenharmony_ci dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); 8562306a36Sopenharmony_ci goto out_free; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return hext_stream; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciout_free: 9362306a36Sopenharmony_ci snd_dma_free_pages(dmab); 9462306a36Sopenharmony_ciout_put: 9562306a36Sopenharmony_ci hda_dsp_stream_put(sdev, direction, hstream->stream_tag); 9662306a36Sopenharmony_ci return ERR_PTR(ret); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * first boot sequence has some extra steps. 10162306a36Sopenharmony_ci * power on all host managed cores and only unstall/run the boot core to boot the 10262306a36Sopenharmony_ci * DSP then turn off all non boot cores (if any) is powered on. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ciint cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 10762306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 10862306a36Sopenharmony_ci unsigned int status, target_status; 10962306a36Sopenharmony_ci u32 flags, ipc_hdr, j; 11062306a36Sopenharmony_ci unsigned long mask; 11162306a36Sopenharmony_ci char *dump_msg; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* step 1: power up corex */ 11562306a36Sopenharmony_ci ret = hda_dsp_core_power_up(sdev, chip->host_managed_cores_mask); 11662306a36Sopenharmony_ci if (ret < 0) { 11762306a36Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 11862306a36Sopenharmony_ci dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); 11962306a36Sopenharmony_ci goto err; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci hda_ssp_set_cbp_cfp(sdev); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* step 2: Send ROM_CONTROL command (stream_tag is ignored for IMR boot) */ 12562306a36Sopenharmony_ci ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL; 12662306a36Sopenharmony_ci if (!imr_boot) 12762306a36Sopenharmony_ci ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* step 3: unset core 0 reset state & unstall/run core 0 */ 13262306a36Sopenharmony_ci ret = hda_dsp_core_run(sdev, chip->init_core_mask); 13362306a36Sopenharmony_ci if (ret < 0) { 13462306a36Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 13562306a36Sopenharmony_ci dev_err(sdev->dev, 13662306a36Sopenharmony_ci "error: dsp core start failed %d\n", ret); 13762306a36Sopenharmony_ci ret = -EIO; 13862306a36Sopenharmony_ci goto err; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* step 4: wait for IPC DONE bit from ROM */ 14262306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 14362306a36Sopenharmony_ci chip->ipc_ack, status, 14462306a36Sopenharmony_ci ((status & chip->ipc_ack_mask) 14562306a36Sopenharmony_ci == chip->ipc_ack_mask), 14662306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 14762306a36Sopenharmony_ci HDA_DSP_INIT_TIMEOUT_US); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (ret < 0) { 15062306a36Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 15162306a36Sopenharmony_ci dev_err(sdev->dev, 15262306a36Sopenharmony_ci "error: %s: timeout for HIPCIE done\n", 15362306a36Sopenharmony_ci __func__); 15462306a36Sopenharmony_ci goto err; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* set DONE bit to clear the reply IPC message */ 15862306a36Sopenharmony_ci snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 15962306a36Sopenharmony_ci chip->ipc_ack, 16062306a36Sopenharmony_ci chip->ipc_ack_mask, 16162306a36Sopenharmony_ci chip->ipc_ack_mask); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* step 5: power down cores that are no longer needed */ 16462306a36Sopenharmony_ci ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask & 16562306a36Sopenharmony_ci ~(chip->init_core_mask)); 16662306a36Sopenharmony_ci if (ret < 0) { 16762306a36Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 16862306a36Sopenharmony_ci dev_err(sdev->dev, 16962306a36Sopenharmony_ci "error: dsp core x power down failed\n"); 17062306a36Sopenharmony_ci goto err; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* step 6: enable IPC interrupts */ 17462306a36Sopenharmony_ci hda_dsp_ipc_int_enable(sdev); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * step 7: 17862306a36Sopenharmony_ci * - Cold/Full boot: wait for ROM init to proceed to download the firmware 17962306a36Sopenharmony_ci * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR) 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci if (imr_boot) 18262306a36Sopenharmony_ci target_status = FSR_STATE_FW_ENTERED; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci target_status = FSR_STATE_INIT_DONE; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 18762306a36Sopenharmony_ci chip->rom_status_reg, status, 18862306a36Sopenharmony_ci (FSR_TO_STATE_CODE(status) == target_status), 18962306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 19062306a36Sopenharmony_ci chip->rom_init_timeout * 19162306a36Sopenharmony_ci USEC_PER_MSEC); 19262306a36Sopenharmony_ci if (!ret) { 19362306a36Sopenharmony_ci /* set enabled cores mask and increment ref count for cores in init_core_mask */ 19462306a36Sopenharmony_ci sdev->enabled_cores_mask |= chip->init_core_mask; 19562306a36Sopenharmony_ci mask = sdev->enabled_cores_mask; 19662306a36Sopenharmony_ci for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES) 19762306a36Sopenharmony_ci sdev->dsp_core_ref_count[j]++; 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 20262306a36Sopenharmony_ci dev_err(sdev->dev, 20362306a36Sopenharmony_ci "%s: timeout with rom_status_reg (%#x) read\n", 20462306a36Sopenharmony_ci __func__, chip->rom_status_reg); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cierr: 20762306a36Sopenharmony_ci flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* after max boot attempts make sure that the dump is printed */ 21062306a36Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 21162306a36Sopenharmony_ci flags &= ~SOF_DBG_DUMP_OPTIONAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d", 21462306a36Sopenharmony_ci hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS); 21562306a36Sopenharmony_ci snd_sof_dsp_dbg_dump(sdev, dump_msg, flags); 21662306a36Sopenharmony_ci hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci kfree(dump_msg); 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int cl_trigger(struct snd_sof_dev *sdev, 22362306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, int cmd) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 22662306a36Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* code loader is special case that reuses stream ops */ 22962306a36Sopenharmony_ci switch (cmd) { 23062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 23162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 23262306a36Sopenharmony_ci 1 << hstream->index, 23362306a36Sopenharmony_ci 1 << hstream->index); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 23662306a36Sopenharmony_ci sd_offset, 23762306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 23862306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 23962306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 24062306a36Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci hstream->running = true; 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci default: 24562306a36Sopenharmony_ci return hda_dsp_stream_trigger(sdev, hext_stream, cmd); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, 25062306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 25362306a36Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 25462306a36Sopenharmony_ci int ret = 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) 25762306a36Sopenharmony_ci ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); 25862306a36Sopenharmony_ci else 25962306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 26062306a36Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, 0); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); 26362306a36Sopenharmony_ci hstream->running = 0; 26462306a36Sopenharmony_ci hstream->substream = NULL; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* reset BDL address */ 26762306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 26862306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); 26962306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 27062306a36Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); 27362306a36Sopenharmony_ci snd_dma_free_pages(dmab); 27462306a36Sopenharmony_ci dmab->area = NULL; 27562306a36Sopenharmony_ci hstream->bufsize = 0; 27662306a36Sopenharmony_ci hstream->format_val = 0; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciint hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 28462306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 28562306a36Sopenharmony_ci unsigned int reg; 28662306a36Sopenharmony_ci int ret, status; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START); 28962306a36Sopenharmony_ci if (ret < 0) { 29062306a36Sopenharmony_ci dev_err(sdev->dev, "error: DMA trigger start failed\n"); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 29562306a36Sopenharmony_ci chip->rom_status_reg, reg, 29662306a36Sopenharmony_ci (FSR_TO_STATE_CODE(reg) == FSR_STATE_FW_ENTERED), 29762306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 29862306a36Sopenharmony_ci HDA_DSP_BASEFW_TIMEOUT_US); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * even in case of errors we still need to stop the DMAs, 30262306a36Sopenharmony_ci * but we return the initial error should the DMA stop also fail 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (status < 0) { 30662306a36Sopenharmony_ci dev_err(sdev->dev, 30762306a36Sopenharmony_ci "%s: timeout with rom_status_reg (%#x) read\n", 30862306a36Sopenharmony_ci __func__, chip->rom_status_reg); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP); 31262306a36Sopenharmony_ci if (ret < 0) { 31362306a36Sopenharmony_ci dev_err(sdev->dev, "error: DMA trigger stop failed\n"); 31462306a36Sopenharmony_ci if (!status) 31562306a36Sopenharmony_ci status = ret; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return status; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciint hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct hdac_ext_stream *iccmax_stream; 32462306a36Sopenharmony_ci struct snd_dma_buffer dmab_bdl; 32562306a36Sopenharmony_ci int ret, ret1; 32662306a36Sopenharmony_ci u8 original_gb; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* save the original LTRP guardband value */ 32962306a36Sopenharmony_ci original_gb = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP) & 33062306a36Sopenharmony_ci HDA_VS_INTEL_LTRP_GB_MASK; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci * Prepare capture stream for ICCMAX. We do not need to store 33462306a36Sopenharmony_ci * the data, so use a buffer of PAGE_SIZE for receiving. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, PAGE_SIZE, 33762306a36Sopenharmony_ci &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); 33862306a36Sopenharmony_ci if (IS_ERR(iccmax_stream)) { 33962306a36Sopenharmony_ci dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n"); 34062306a36Sopenharmony_ci return PTR_ERR(iccmax_stream); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = hda_dsp_cl_boot_firmware(sdev); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * Perform iccmax stream cleanup. This should be done even if firmware loading fails. 34762306a36Sopenharmony_ci * If the cleanup also fails, we return the initial error 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci ret1 = hda_cl_cleanup(sdev, &dmab_bdl, iccmax_stream); 35062306a36Sopenharmony_ci if (ret1 < 0) { 35162306a36Sopenharmony_ci dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* set return value to indicate cleanup failure */ 35462306a36Sopenharmony_ci if (!ret) 35562306a36Sopenharmony_ci ret = ret1; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* restore the original guardband value after FW boot */ 35962306a36Sopenharmony_ci snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP, 36062306a36Sopenharmony_ci HDA_VS_INTEL_LTRP_GB_MASK, original_gb); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return ret; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int hda_dsp_boot_imr(struct snd_sof_dev *sdev) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip_info; 36862306a36Sopenharmony_ci int ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci chip_info = get_chip_info(sdev->pdata); 37162306a36Sopenharmony_ci if (chip_info->cl_init) 37262306a36Sopenharmony_ci ret = chip_info->cl_init(sdev, 0, true); 37362306a36Sopenharmony_ci else 37462306a36Sopenharmony_ci ret = -EINVAL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!ret) 37762306a36Sopenharmony_ci hda_sdw_process_wakeen(sdev); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return ret; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciint hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 38562306a36Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 38662306a36Sopenharmony_ci const struct sof_dev_desc *desc = plat_data->desc; 38762306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip_info; 38862306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 38962306a36Sopenharmony_ci struct firmware stripped_firmware; 39062306a36Sopenharmony_ci struct snd_dma_buffer dmab; 39162306a36Sopenharmony_ci int ret, ret1, i; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (hda->imrboot_supported && !sdev->first_boot && !hda->skip_imr_boot) { 39462306a36Sopenharmony_ci dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n"); 39562306a36Sopenharmony_ci hda->boot_iteration = 0; 39662306a36Sopenharmony_ci ret = hda_dsp_boot_imr(sdev); 39762306a36Sopenharmony_ci if (!ret) { 39862306a36Sopenharmony_ci hda->booted_from_imr = true; 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n"); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci hda->booted_from_imr = false; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci chip_info = desc->chip_info; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) { 41062306a36Sopenharmony_ci dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 41162306a36Sopenharmony_ci return -EINVAL; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset; 41562306a36Sopenharmony_ci stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* init for booting wait */ 41862306a36Sopenharmony_ci init_waitqueue_head(&sdev->boot_wait); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* prepare DMA for code loader stream */ 42162306a36Sopenharmony_ci hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, 42262306a36Sopenharmony_ci stripped_firmware.size, 42362306a36Sopenharmony_ci &dmab, SNDRV_PCM_STREAM_PLAYBACK); 42462306a36Sopenharmony_ci if (IS_ERR(hext_stream)) { 42562306a36Sopenharmony_ci dev_err(sdev->dev, "error: dma prepare for fw loading failed\n"); 42662306a36Sopenharmony_ci return PTR_ERR(hext_stream); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci memcpy(dmab.area, stripped_firmware.data, 43062306a36Sopenharmony_ci stripped_firmware.size); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* try ROM init a few times before giving up */ 43362306a36Sopenharmony_ci for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) { 43462306a36Sopenharmony_ci dev_dbg(sdev->dev, 43562306a36Sopenharmony_ci "Attempting iteration %d of Core En/ROM load...\n", i); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci hda->boot_iteration = i + 1; 43862306a36Sopenharmony_ci if (chip_info->cl_init) 43962306a36Sopenharmony_ci ret = chip_info->cl_init(sdev, hext_stream->hstream.stream_tag, false); 44062306a36Sopenharmony_ci else 44162306a36Sopenharmony_ci ret = -EINVAL; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* don't retry anymore if successful */ 44462306a36Sopenharmony_ci if (!ret) 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (i == HDA_FW_BOOT_ATTEMPTS) { 44962306a36Sopenharmony_ci dev_err(sdev->dev, "error: dsp init failed after %d attempts with err: %d\n", 45062306a36Sopenharmony_ci i, ret); 45162306a36Sopenharmony_ci goto cleanup; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * When a SoundWire link is in clock stop state, a Slave 45662306a36Sopenharmony_ci * device may trigger in-band wakes for events such as jack 45762306a36Sopenharmony_ci * insertion or acoustic event detection. This event will lead 45862306a36Sopenharmony_ci * to a WAKEEN interrupt, handled by the PCI device and routed 45962306a36Sopenharmony_ci * to PME if the PCI device is in D3. The resume function in 46062306a36Sopenharmony_ci * audio PCI driver will be invoked by ACPI for PME event and 46162306a36Sopenharmony_ci * initialize the device and process WAKEEN interrupt. 46262306a36Sopenharmony_ci * 46362306a36Sopenharmony_ci * The WAKEEN interrupt should be processed ASAP to prevent an 46462306a36Sopenharmony_ci * interrupt flood, otherwise other interrupts, such IPC, 46562306a36Sopenharmony_ci * cannot work normally. The WAKEEN is handled after the ROM 46662306a36Sopenharmony_ci * is initialized successfully, which ensures power rails are 46762306a36Sopenharmony_ci * enabled before accessing the SoundWire SHIM registers 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci if (!sdev->first_boot) 47062306a36Sopenharmony_ci hda_sdw_process_wakeen(sdev); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * Set the boot_iteration to the last attempt, indicating that the 47462306a36Sopenharmony_ci * DSP ROM has been initialized and from this point there will be no 47562306a36Sopenharmony_ci * retry done to boot. 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * Continue with code loading and firmware boot 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; 48062306a36Sopenharmony_ci ret = hda_cl_copy_fw(sdev, hext_stream); 48162306a36Sopenharmony_ci if (!ret) { 48262306a36Sopenharmony_ci dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); 48362306a36Sopenharmony_ci hda->skip_imr_boot = false; 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci snd_sof_dsp_dbg_dump(sdev, "Firmware download failed", 48662306a36Sopenharmony_ci SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); 48762306a36Sopenharmony_ci hda->skip_imr_boot = true; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cicleanup: 49162306a36Sopenharmony_ci /* 49262306a36Sopenharmony_ci * Perform codeloader stream cleanup. 49362306a36Sopenharmony_ci * This should be done even if firmware loading fails. 49462306a36Sopenharmony_ci * If the cleanup also fails, we return the initial error 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream); 49762306a36Sopenharmony_ci if (ret1 < 0) { 49862306a36Sopenharmony_ci dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* set return value to indicate cleanup failure */ 50162306a36Sopenharmony_ci if (!ret) 50262306a36Sopenharmony_ci ret = ret1; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * return primary core id if both fw copy 50762306a36Sopenharmony_ci * and stream clean up are successful 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci if (!ret) 51062306a36Sopenharmony_ci return chip_info->init_core_mask; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* disable DSP */ 51362306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, 51462306a36Sopenharmony_ci SOF_HDA_REG_PP_PPCTL, 51562306a36Sopenharmony_ci SOF_HDA_PPCTL_GPROCEN, 0); 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciint hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, 52062306a36Sopenharmony_ci struct sof_ipc4_fw_library *fw_lib, bool reload) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 52362306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 52462306a36Sopenharmony_ci struct firmware stripped_firmware; 52562306a36Sopenharmony_ci struct sof_ipc4_msg msg = {}; 52662306a36Sopenharmony_ci struct snd_dma_buffer dmab; 52762306a36Sopenharmony_ci int ret, ret1; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* IMR booting will restore the libraries as well, skip the loading */ 53062306a36Sopenharmony_ci if (reload && hda->booted_from_imr) 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* the fw_lib has been verified during loading, we can trust the validity here */ 53462306a36Sopenharmony_ci stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset; 53562306a36Sopenharmony_ci stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* prepare DMA for code loader stream */ 53862306a36Sopenharmony_ci hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, 53962306a36Sopenharmony_ci stripped_firmware.size, 54062306a36Sopenharmony_ci &dmab, SNDRV_PCM_STREAM_PLAYBACK); 54162306a36Sopenharmony_ci if (IS_ERR(hext_stream)) { 54262306a36Sopenharmony_ci dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__); 54362306a36Sopenharmony_ci return PTR_ERR(hext_stream); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci msg.primary = hext_stream->hstream.stream_tag - 1; 54962306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY); 55062306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 55162306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); 55262306a36Sopenharmony_ci msg.primary |= SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(fw_lib->id); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START); 55562306a36Sopenharmony_ci if (ret < 0) { 55662306a36Sopenharmony_ci dev_err(sdev->dev, "%s: DMA trigger start failed\n", __func__); 55762306a36Sopenharmony_ci goto cleanup; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP); 56362306a36Sopenharmony_ci if (ret1 < 0) { 56462306a36Sopenharmony_ci dev_err(sdev->dev, "%s: DMA trigger stop failed\n", __func__); 56562306a36Sopenharmony_ci if (!ret) 56662306a36Sopenharmony_ci ret = ret1; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cicleanup: 57062306a36Sopenharmony_ci /* clean up even in case of error and return the first error */ 57162306a36Sopenharmony_ci ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream); 57262306a36Sopenharmony_ci if (ret1 < 0) { 57362306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* set return value to indicate cleanup failure */ 57662306a36Sopenharmony_ci if (!ret) 57762306a36Sopenharmony_ci ret = ret1; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/* pre fw run operations */ 58462306a36Sopenharmony_ciint hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci /* disable clock gating and power gating */ 58762306a36Sopenharmony_ci return hda_dsp_ctrl_clock_power_gating(sdev, false); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* post fw run operations */ 59162306a36Sopenharmony_ciint hda_dsp_post_fw_run(struct snd_sof_dev *sdev) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci int ret; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (sdev->first_boot) { 59662306a36Sopenharmony_ci struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = hda_sdw_startup(sdev); 59962306a36Sopenharmony_ci if (ret < 0) { 60062306a36Sopenharmony_ci dev_err(sdev->dev, 60162306a36Sopenharmony_ci "error: could not startup SoundWire links\n"); 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Check if IMR boot is usable */ 60662306a36Sopenharmony_ci if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) && 60762306a36Sopenharmony_ci (sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT || 60862306a36Sopenharmony_ci sdev->pdata->ipc_type == SOF_INTEL_IPC4)) 60962306a36Sopenharmony_ci hdev->imrboot_supported = true; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci hda_sdw_int_enable(sdev, true); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* re-enable clock gating and power gating */ 61562306a36Sopenharmony_ci return hda_dsp_ctrl_clock_power_gating(sdev, true); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciint hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, 61962306a36Sopenharmony_ci const struct sof_ext_man_elem_header *hdr) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci const struct sof_ext_man_cavs_config_data *config_data = 62262306a36Sopenharmony_ci container_of(hdr, struct sof_ext_man_cavs_config_data, hdr); 62362306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 62462306a36Sopenharmony_ci int i, elem_num; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* calculate total number of config data elements */ 62762306a36Sopenharmony_ci elem_num = (hdr->size - sizeof(struct sof_ext_man_elem_header)) 62862306a36Sopenharmony_ci / sizeof(struct sof_config_elem); 62962306a36Sopenharmony_ci if (elem_num <= 0) { 63062306a36Sopenharmony_ci dev_err(sdev->dev, "cavs config data is inconsistent: %d\n", elem_num); 63162306a36Sopenharmony_ci return -EINVAL; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci for (i = 0; i < elem_num; i++) 63562306a36Sopenharmony_ci switch (config_data->elems[i].token) { 63662306a36Sopenharmony_ci case SOF_EXT_MAN_CAVS_CONFIG_EMPTY: 63762306a36Sopenharmony_ci /* skip empty token */ 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci case SOF_EXT_MAN_CAVS_CONFIG_CAVS_LPRO: 64062306a36Sopenharmony_ci hda->clk_config_lpro = config_data->elems[i].value; 64162306a36Sopenharmony_ci dev_dbg(sdev->dev, "FW clock config: %s\n", 64262306a36Sopenharmony_ci hda->clk_config_lpro ? "LPRO" : "HPRO"); 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case SOF_EXT_MAN_CAVS_CONFIG_OUTBOX_SIZE: 64562306a36Sopenharmony_ci case SOF_EXT_MAN_CAVS_CONFIG_INBOX_SIZE: 64662306a36Sopenharmony_ci /* These elements are defined but not being used yet. No warn is required */ 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci default: 64962306a36Sopenharmony_ci dev_info(sdev->dev, "unsupported token type: %d\n", 65062306a36Sopenharmony_ci config_data->elems[i].token); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 655