18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 108c2ecf20Sopenharmony_ci// Rander Wang <rander.wang@intel.com> 118c2ecf20Sopenharmony_ci// Keyon Jie <yang.jie@linux.intel.com> 128c2ecf20Sopenharmony_ci// 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Hardware interface for HDA DSP code loader 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/firmware.h> 198c2ecf20Sopenharmony_ci#include <sound/hdaudio_ext.h> 208c2ecf20Sopenharmony_ci#include <sound/hda_register.h> 218c2ecf20Sopenharmony_ci#include <sound/sof.h> 228c2ecf20Sopenharmony_ci#include "../ops.h" 238c2ecf20Sopenharmony_ci#include "hda.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define HDA_FW_BOOT_ATTEMPTS 3 268c2ecf20Sopenharmony_ci#define HDA_CL_STREAM_FORMAT 0x40 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, 298c2ecf20Sopenharmony_ci unsigned int size, struct snd_dma_buffer *dmab, 308c2ecf20Sopenharmony_ci int direction) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct hdac_ext_stream *dsp_stream; 338c2ecf20Sopenharmony_ci struct hdac_stream *hstream; 348c2ecf20Sopenharmony_ci struct pci_dev *pci = to_pci_dev(sdev->dev); 358c2ecf20Sopenharmony_ci int ret; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci dsp_stream = hda_dsp_stream_get(sdev, direction); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (!dsp_stream) { 408c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: no stream available\n"); 418c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci hstream = &dsp_stream->hstream; 448c2ecf20Sopenharmony_ci hstream->substream = NULL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* allocate DMA buffer */ 478c2ecf20Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); 488c2ecf20Sopenharmony_ci if (ret < 0) { 498c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: memory alloc failed: %x\n", ret); 508c2ecf20Sopenharmony_ci goto out_put; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci hstream->period_bytes = 0;/* initialize period_bytes */ 548c2ecf20Sopenharmony_ci hstream->format_val = format; 558c2ecf20Sopenharmony_ci hstream->bufsize = size; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_CAPTURE) { 588c2ecf20Sopenharmony_ci ret = hda_dsp_iccmax_stream_hw_params(sdev, dsp_stream, dmab, NULL); 598c2ecf20Sopenharmony_ci if (ret < 0) { 608c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: iccmax stream prepare failed: %x\n", ret); 618c2ecf20Sopenharmony_ci goto out_free; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci } else { 648c2ecf20Sopenharmony_ci ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); 658c2ecf20Sopenharmony_ci if (ret < 0) { 668c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); 678c2ecf20Sopenharmony_ci goto out_free; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return dsp_stream; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciout_free: 758c2ecf20Sopenharmony_ci snd_dma_free_pages(dmab); 768c2ecf20Sopenharmony_ciout_put: 778c2ecf20Sopenharmony_ci hda_dsp_stream_put(sdev, direction, hstream->stream_tag); 788c2ecf20Sopenharmony_ci return ERR_PTR(ret); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * first boot sequence has some extra steps. 838c2ecf20Sopenharmony_ci * power on all host managed cores and only unstall/run the boot core to boot the 848c2ecf20Sopenharmony_ci * DSP then turn off all non boot cores (if any) is powered on. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 898c2ecf20Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 908c2ecf20Sopenharmony_ci unsigned int status; 918c2ecf20Sopenharmony_ci int ret; 928c2ecf20Sopenharmony_ci int i; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* step 1: power up corex */ 958c2ecf20Sopenharmony_ci ret = hda_dsp_core_power_up(sdev, chip->host_managed_cores_mask); 968c2ecf20Sopenharmony_ci if (ret < 0) { 978c2ecf20Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 988c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); 998c2ecf20Sopenharmony_ci goto err; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* DSP is powered up, set all SSPs to slave mode */ 1038c2ecf20Sopenharmony_ci for (i = 0; i < chip->ssp_count; i++) { 1048c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 1058c2ecf20Sopenharmony_ci chip->ssp_base_offset 1068c2ecf20Sopenharmony_ci + i * SSP_DEV_MEM_SIZE 1078c2ecf20Sopenharmony_ci + SSP_SSC1_OFFSET, 1088c2ecf20Sopenharmony_ci SSP_SET_SLAVE, 1098c2ecf20Sopenharmony_ci SSP_SET_SLAVE); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* step 2: purge FW request */ 1138c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, 1148c2ecf20Sopenharmony_ci chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW | 1158c2ecf20Sopenharmony_ci ((stream_tag - 1) << 9))); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* step 3: unset core 0 reset state & unstall/run core 0 */ 1188c2ecf20Sopenharmony_ci ret = hda_dsp_core_run(sdev, chip->init_core_mask); 1198c2ecf20Sopenharmony_ci if (ret < 0) { 1208c2ecf20Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 1218c2ecf20Sopenharmony_ci dev_err(sdev->dev, 1228c2ecf20Sopenharmony_ci "error: dsp core start failed %d\n", ret); 1238c2ecf20Sopenharmony_ci ret = -EIO; 1248c2ecf20Sopenharmony_ci goto err; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* step 4: wait for IPC DONE bit from ROM */ 1288c2ecf20Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 1298c2ecf20Sopenharmony_ci chip->ipc_ack, status, 1308c2ecf20Sopenharmony_ci ((status & chip->ipc_ack_mask) 1318c2ecf20Sopenharmony_ci == chip->ipc_ack_mask), 1328c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 1338c2ecf20Sopenharmony_ci HDA_DSP_INIT_TIMEOUT_US); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (ret < 0) { 1368c2ecf20Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 1378c2ecf20Sopenharmony_ci dev_err(sdev->dev, 1388c2ecf20Sopenharmony_ci "error: %s: timeout for HIPCIE done\n", 1398c2ecf20Sopenharmony_ci __func__); 1408c2ecf20Sopenharmony_ci goto err; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* set DONE bit to clear the reply IPC message */ 1448c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 1458c2ecf20Sopenharmony_ci chip->ipc_ack, 1468c2ecf20Sopenharmony_ci chip->ipc_ack_mask, 1478c2ecf20Sopenharmony_ci chip->ipc_ack_mask); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* step 5: power down corex */ 1508c2ecf20Sopenharmony_ci ret = hda_dsp_core_power_down(sdev, chip->host_managed_cores_mask & ~(BIT(0))); 1518c2ecf20Sopenharmony_ci if (ret < 0) { 1528c2ecf20Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 1538c2ecf20Sopenharmony_ci dev_err(sdev->dev, 1548c2ecf20Sopenharmony_ci "error: dsp core x power down failed\n"); 1558c2ecf20Sopenharmony_ci goto err; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* step 6: enable IPC interrupts */ 1598c2ecf20Sopenharmony_ci hda_dsp_ipc_int_enable(sdev); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* step 7: wait for ROM init */ 1628c2ecf20Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 1638c2ecf20Sopenharmony_ci HDA_DSP_SRAM_REG_ROM_STATUS, status, 1648c2ecf20Sopenharmony_ci ((status & HDA_DSP_ROM_STS_MASK) 1658c2ecf20Sopenharmony_ci == HDA_DSP_ROM_INIT), 1668c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 1678c2ecf20Sopenharmony_ci chip->rom_init_timeout * 1688c2ecf20Sopenharmony_ci USEC_PER_MSEC); 1698c2ecf20Sopenharmony_ci if (!ret) 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) 1738c2ecf20Sopenharmony_ci dev_err(sdev->dev, 1748c2ecf20Sopenharmony_ci "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", 1758c2ecf20Sopenharmony_ci __func__); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cierr: 1788c2ecf20Sopenharmony_ci hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); 1798c2ecf20Sopenharmony_ci hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int cl_trigger(struct snd_sof_dev *sdev, 1858c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, int cmd) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 1888c2ecf20Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* code loader is special case that reuses stream ops */ 1918c2ecf20Sopenharmony_ci switch (cmd) { 1928c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1938c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 1948c2ecf20Sopenharmony_ci 1 << hstream->index, 1958c2ecf20Sopenharmony_ci 1 << hstream->index); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 1988c2ecf20Sopenharmony_ci sd_offset, 1998c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 2008c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK, 2018c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START | 2028c2ecf20Sopenharmony_ci SOF_HDA_CL_DMA_SD_INT_MASK); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci hstream->running = true; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci return hda_dsp_stream_trigger(sdev, stream, cmd); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, 2128c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 2158c2ecf20Sopenharmony_ci int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 2168c2ecf20Sopenharmony_ci int ret = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) 2198c2ecf20Sopenharmony_ci ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 2228c2ecf20Sopenharmony_ci SOF_HDA_SD_CTL_DMA_START, 0); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); 2258c2ecf20Sopenharmony_ci hstream->running = 0; 2268c2ecf20Sopenharmony_ci hstream->substream = NULL; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* reset BDL address */ 2298c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 2308c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0); 2318c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 2328c2ecf20Sopenharmony_ci sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); 2358c2ecf20Sopenharmony_ci snd_dma_free_pages(dmab); 2368c2ecf20Sopenharmony_ci dmab->area = NULL; 2378c2ecf20Sopenharmony_ci hstream->bufsize = 0; 2388c2ecf20Sopenharmony_ci hstream->format_val = 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci unsigned int reg; 2468c2ecf20Sopenharmony_ci int ret, status; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START); 2498c2ecf20Sopenharmony_ci if (ret < 0) { 2508c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: DMA trigger start failed\n"); 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 2558c2ecf20Sopenharmony_ci HDA_DSP_SRAM_REG_ROM_STATUS, reg, 2568c2ecf20Sopenharmony_ci ((reg & HDA_DSP_ROM_STS_MASK) 2578c2ecf20Sopenharmony_ci == HDA_DSP_ROM_FW_ENTERED), 2588c2ecf20Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 2598c2ecf20Sopenharmony_ci HDA_DSP_BASEFW_TIMEOUT_US); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * even in case of errors we still need to stop the DMAs, 2638c2ecf20Sopenharmony_ci * but we return the initial error should the DMA stop also fail 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (status < 0) { 2678c2ecf20Sopenharmony_ci dev_err(sdev->dev, 2688c2ecf20Sopenharmony_ci "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", 2698c2ecf20Sopenharmony_ci __func__); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP); 2738c2ecf20Sopenharmony_ci if (ret < 0) { 2748c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: DMA trigger stop failed\n"); 2758c2ecf20Sopenharmony_ci if (!status) 2768c2ecf20Sopenharmony_ci status = ret; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return status; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciint hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 2858c2ecf20Sopenharmony_ci struct hdac_ext_stream *iccmax_stream; 2868c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 2878c2ecf20Sopenharmony_ci struct firmware stripped_firmware; 2888c2ecf20Sopenharmony_ci int ret, ret1; 2898c2ecf20Sopenharmony_ci u8 original_gb; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* save the original LTRP guardband value */ 2928c2ecf20Sopenharmony_ci original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (plat_data->fw->size <= plat_data->fw_offset) { 2958c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* prepare capture stream for ICCMAX */ 3028c2ecf20Sopenharmony_ci iccmax_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, 3038c2ecf20Sopenharmony_ci &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); 3048c2ecf20Sopenharmony_ci if (IS_ERR(iccmax_stream)) { 3058c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n"); 3068c2ecf20Sopenharmony_ci return PTR_ERR(iccmax_stream); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = hda_dsp_cl_boot_firmware(sdev); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * Perform iccmax stream cleanup. This should be done even if firmware loading fails. 3138c2ecf20Sopenharmony_ci * If the cleanup also fails, we return the initial error 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci ret1 = cl_cleanup(sdev, &sdev->dmab_bdl, iccmax_stream); 3168c2ecf20Sopenharmony_ci if (ret1 < 0) { 3178c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* set return value to indicate cleanup failure */ 3208c2ecf20Sopenharmony_ci if (!ret) 3218c2ecf20Sopenharmony_ci ret = ret1; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* restore the original guardband value after FW boot */ 3258c2ecf20Sopenharmony_ci snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, original_gb); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ciint hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 3338c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 3348c2ecf20Sopenharmony_ci const struct sof_dev_desc *desc = plat_data->desc; 3358c2ecf20Sopenharmony_ci const struct sof_intel_dsp_desc *chip_info; 3368c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream; 3378c2ecf20Sopenharmony_ci struct firmware stripped_firmware; 3388c2ecf20Sopenharmony_ci int ret, ret1, i; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci chip_info = desc->chip_info; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (plat_data->fw->size <= plat_data->fw_offset) { 3438c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset; 3488c2ecf20Sopenharmony_ci stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* init for booting wait */ 3518c2ecf20Sopenharmony_ci init_waitqueue_head(&sdev->boot_wait); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* prepare DMA for code loader stream */ 3548c2ecf20Sopenharmony_ci stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, 3558c2ecf20Sopenharmony_ci &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); 3568c2ecf20Sopenharmony_ci if (IS_ERR(stream)) { 3578c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: dma prepare for fw loading failed\n"); 3588c2ecf20Sopenharmony_ci return PTR_ERR(stream); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci memcpy(sdev->dmab.area, stripped_firmware.data, 3628c2ecf20Sopenharmony_ci stripped_firmware.size); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* try ROM init a few times before giving up */ 3658c2ecf20Sopenharmony_ci for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) { 3668c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, 3678c2ecf20Sopenharmony_ci "Attempting iteration %d of Core En/ROM load...\n", i); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci hda->boot_iteration = i + 1; 3708c2ecf20Sopenharmony_ci ret = cl_dsp_init(sdev, stream->hstream.stream_tag); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* don't retry anymore if successful */ 3738c2ecf20Sopenharmony_ci if (!ret) 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (i == HDA_FW_BOOT_ATTEMPTS) { 3788c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: dsp init failed after %d attempts with err: %d\n", 3798c2ecf20Sopenharmony_ci i, ret); 3808c2ecf20Sopenharmony_ci dev_err(sdev->dev, "ROM error=0x%x: FW status=0x%x\n", 3818c2ecf20Sopenharmony_ci snd_sof_dsp_read(sdev, HDA_DSP_BAR, 3828c2ecf20Sopenharmony_ci HDA_DSP_SRAM_REG_ROM_ERROR), 3838c2ecf20Sopenharmony_ci snd_sof_dsp_read(sdev, HDA_DSP_BAR, 3848c2ecf20Sopenharmony_ci HDA_DSP_SRAM_REG_ROM_STATUS)); 3858c2ecf20Sopenharmony_ci goto cleanup; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * When a SoundWire link is in clock stop state, a Slave 3908c2ecf20Sopenharmony_ci * device may trigger in-band wakes for events such as jack 3918c2ecf20Sopenharmony_ci * insertion or acoustic event detection. This event will lead 3928c2ecf20Sopenharmony_ci * to a WAKEEN interrupt, handled by the PCI device and routed 3938c2ecf20Sopenharmony_ci * to PME if the PCI device is in D3. The resume function in 3948c2ecf20Sopenharmony_ci * audio PCI driver will be invoked by ACPI for PME event and 3958c2ecf20Sopenharmony_ci * initialize the device and process WAKEEN interrupt. 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * The WAKEEN interrupt should be processed ASAP to prevent an 3988c2ecf20Sopenharmony_ci * interrupt flood, otherwise other interrupts, such IPC, 3998c2ecf20Sopenharmony_ci * cannot work normally. The WAKEEN is handled after the ROM 4008c2ecf20Sopenharmony_ci * is initialized successfully, which ensures power rails are 4018c2ecf20Sopenharmony_ci * enabled before accessing the SoundWire SHIM registers 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci if (!sdev->first_boot) 4048c2ecf20Sopenharmony_ci hda_sdw_process_wakeen(sdev); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* 4078c2ecf20Sopenharmony_ci * at this point DSP ROM has been initialized and 4088c2ecf20Sopenharmony_ci * should be ready for code loading and firmware boot 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci ret = cl_copy_fw(sdev, stream); 4118c2ecf20Sopenharmony_ci if (!ret) 4128c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); 4138c2ecf20Sopenharmony_ci else 4148c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: load fw failed ret: %d\n", ret); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cicleanup: 4178c2ecf20Sopenharmony_ci /* 4188c2ecf20Sopenharmony_ci * Perform codeloader stream cleanup. 4198c2ecf20Sopenharmony_ci * This should be done even if firmware loading fails. 4208c2ecf20Sopenharmony_ci * If the cleanup also fails, we return the initial error 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci ret1 = cl_cleanup(sdev, &sdev->dmab, stream); 4238c2ecf20Sopenharmony_ci if (ret1 < 0) { 4248c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* set return value to indicate cleanup failure */ 4278c2ecf20Sopenharmony_ci if (!ret) 4288c2ecf20Sopenharmony_ci ret = ret1; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * return primary core id if both fw copy 4338c2ecf20Sopenharmony_ci * and stream clean up are successful 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci if (!ret) 4368c2ecf20Sopenharmony_ci return chip_info->init_core_mask; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* dump dsp registers and disable DSP upon error */ 4398c2ecf20Sopenharmony_ci hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* disable DSP */ 4428c2ecf20Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, 4438c2ecf20Sopenharmony_ci SOF_HDA_REG_PP_PPCTL, 4448c2ecf20Sopenharmony_ci SOF_HDA_PPCTL_GPROCEN, 0); 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* pre fw run operations */ 4498c2ecf20Sopenharmony_ciint hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci /* disable clock gating and power gating */ 4528c2ecf20Sopenharmony_ci return hda_dsp_ctrl_clock_power_gating(sdev, false); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* post fw run operations */ 4568c2ecf20Sopenharmony_ciint hda_dsp_post_fw_run(struct snd_sof_dev *sdev) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci int ret; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (sdev->first_boot) { 4618c2ecf20Sopenharmony_ci ret = hda_sdw_startup(sdev); 4628c2ecf20Sopenharmony_ci if (ret < 0) { 4638c2ecf20Sopenharmony_ci dev_err(sdev->dev, 4648c2ecf20Sopenharmony_ci "error: could not startup SoundWire links\n"); 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci hda_sdw_int_enable(sdev, true); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* re-enable clock gating and power gating */ 4728c2ecf20Sopenharmony_ci return hda_dsp_ctrl_clock_power_gating(sdev, true); 4738c2ecf20Sopenharmony_ci} 474