| // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) | |
| // | |
| // This file is provided under a dual BSD/GPLv2 license. When using or | |
| // redistributing this file, you may do so under either license. | |
| // | |
| // Copyright(c) 2018-2022 Intel Corporation | |
| // | |
| #include <linux/delay.h> | |
| #include <linux/device.h> | |
| #include <linux/dma-mapping.h> | |
| #include <linux/firmware.h> | |
| #include <linux/fs.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/mm.h> | |
| #include <linux/module.h> | |
| #include <linux/pci.h> | |
| #include <linux/slab.h> | |
| #include <sound/hdaudio_ext.h> | |
| #include <sound/sof.h> | |
| #include <sound/pcm_params.h> | |
| #include "../sof-priv.h" | |
| #include "../ops.h" | |
| #include "hda.h" | |
| #define HDA_SKL_WAIT_TIMEOUT 500 /* 500 msec */ | |
| #define HDA_SKL_CLDMA_MAX_BUFFER_SIZE (32 * PAGE_SIZE) | |
| /* Stream Reset */ | |
| #define HDA_CL_SD_CTL_SRST_SHIFT 0 | |
| #define HDA_CL_SD_CTL_SRST(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_SRST_SHIFT) | |
| /* Stream Run */ | |
| #define HDA_CL_SD_CTL_RUN_SHIFT 1 | |
| #define HDA_CL_SD_CTL_RUN(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_RUN_SHIFT) | |
| /* Interrupt On Completion Enable */ | |
| #define HDA_CL_SD_CTL_IOCE_SHIFT 2 | |
| #define HDA_CL_SD_CTL_IOCE(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_IOCE_SHIFT) | |
| /* FIFO Error Interrupt Enable */ | |
| #define HDA_CL_SD_CTL_FEIE_SHIFT 3 | |
| #define HDA_CL_SD_CTL_FEIE(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_FEIE_SHIFT) | |
| /* Descriptor Error Interrupt Enable */ | |
| #define HDA_CL_SD_CTL_DEIE_SHIFT 4 | |
| #define HDA_CL_SD_CTL_DEIE(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_DEIE_SHIFT) | |
| /* FIFO Limit Change */ | |
| #define HDA_CL_SD_CTL_FIFOLC_SHIFT 5 | |
| #define HDA_CL_SD_CTL_FIFOLC(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_FIFOLC_SHIFT) | |
| /* Stripe Control */ | |
| #define HDA_CL_SD_CTL_STRIPE_SHIFT 16 | |
| #define HDA_CL_SD_CTL_STRIPE(x) (((x) & 0x3) << \ | |
| HDA_CL_SD_CTL_STRIPE_SHIFT) | |
| /* Traffic Priority */ | |
| #define HDA_CL_SD_CTL_TP_SHIFT 18 | |
| #define HDA_CL_SD_CTL_TP(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_TP_SHIFT) | |
| /* Bidirectional Direction Control */ | |
| #define HDA_CL_SD_CTL_DIR_SHIFT 19 | |
| #define HDA_CL_SD_CTL_DIR(x) (((x) & 0x1) << \ | |
| HDA_CL_SD_CTL_DIR_SHIFT) | |
| /* Stream Number */ | |
| #define HDA_CL_SD_CTL_STRM_SHIFT 20 | |
| #define HDA_CL_SD_CTL_STRM(x) (((x) & 0xf) << \ | |
| HDA_CL_SD_CTL_STRM_SHIFT) | |
| #define HDA_CL_SD_CTL_INT(x) \ | |
| (HDA_CL_SD_CTL_IOCE(x) | \ | |
| HDA_CL_SD_CTL_FEIE(x) | \ | |
| HDA_CL_SD_CTL_DEIE(x)) | |
| #define HDA_CL_SD_CTL_INT_MASK \ | |
| (HDA_CL_SD_CTL_IOCE(1) | \ | |
| HDA_CL_SD_CTL_FEIE(1) | \ | |
| HDA_CL_SD_CTL_DEIE(1)) | |
| #define DMA_ADDRESS_128_BITS_ALIGNMENT 7 | |
| #define BDL_ALIGN(x) ((x) >> DMA_ADDRESS_128_BITS_ALIGNMENT) | |
| /* Buffer Descriptor List Lower Base Address */ | |
| #define HDA_CL_SD_BDLPLBA_SHIFT 7 | |
| #define HDA_CL_SD_BDLPLBA_MASK GENMASK(31, 7) | |
| #define HDA_CL_SD_BDLPLBA(x) \ | |
| ((BDL_ALIGN(lower_32_bits(x)) << HDA_CL_SD_BDLPLBA_SHIFT) & \ | |
| HDA_CL_SD_BDLPLBA_MASK) | |
| /* Buffer Descriptor List Upper Base Address */ | |
| #define HDA_CL_SD_BDLPUBA(x) \ | |
| (upper_32_bits(x)) | |
| /* Software Position in Buffer Enable */ | |
| #define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT 0 | |
| #define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK \ | |
| (1 << HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) | |
| #define HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(x) \ | |
| (((x) << HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_SHIFT) & \ | |
| HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK) | |
| #define HDA_CL_DMA_SD_INT_COMPLETE 0x4 | |
| static int cl_skl_cldma_setup_bdle(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab_data, | |
| __le32 **bdlp, int size, int with_ioc) | |
| { | |
| phys_addr_t addr = virt_to_phys(dmab_data->area); | |
| __le32 *bdl = *bdlp; | |
| /* | |
| * This code is simplified by using one fragment of physical memory and assuming | |
| * all the code fits. This could be improved with scatter-gather but the firmware | |
| * size is limited by DSP memory anyways | |
| */ | |
| bdl[0] = cpu_to_le32(lower_32_bits(addr)); | |
| bdl[1] = cpu_to_le32(upper_32_bits(addr)); | |
| bdl[2] = cpu_to_le32(size); | |
| bdl[3] = (!with_ioc) ? 0 : cpu_to_le32(0x01); | |
| return 1; /* one fragment */ | |
| } | |
| static void cl_skl_cldma_stream_run(struct snd_sof_dev *sdev, bool enable) | |
| { | |
| int sd_offset = SOF_HDA_ADSP_LOADER_BASE; | |
| unsigned char val; | |
| int retries; | |
| u32 run = enable ? 0x1 : 0; | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CTL, | |
| HDA_CL_SD_CTL_RUN(1), HDA_CL_SD_CTL_RUN(run)); | |
| retries = 300; | |
| do { | |
| udelay(3); | |
| /* waiting for hardware to report the stream Run bit set */ | |
| val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CTL); | |
| val &= HDA_CL_SD_CTL_RUN(1); | |
| if (enable && val) | |
| break; | |
| else if (!enable && !val) | |
| break; | |
| } while (--retries); | |
| if (retries == 0) | |
| dev_err(sdev->dev, "%s: failed to set Run bit=%d enable=%d\n", | |
| __func__, val, enable); | |
| } | |
| static void cl_skl_cldma_stream_clear(struct snd_sof_dev *sdev) | |
| { | |
| int sd_offset = SOF_HDA_ADSP_LOADER_BASE; | |
| /* make sure Run bit is cleared before setting stream register */ | |
| cl_skl_cldma_stream_run(sdev, 0); | |
| /* Disable the Interrupt On Completion, FIFO Error Interrupt, | |
| * Descriptor Error Interrupt and set the cldma stream number to 0. | |
| */ | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CTL, | |
| HDA_CL_SD_CTL_INT_MASK, HDA_CL_SD_CTL_INT(0)); | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CTL, | |
| HDA_CL_SD_CTL_STRM(0xf), HDA_CL_SD_CTL_STRM(0)); | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, HDA_CL_SD_BDLPLBA(0)); | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); | |
| /* Set the Cyclic Buffer Length to 0. */ | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CBL, 0); | |
| /* Set the Last Valid Index. */ | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_LVI, 0); | |
| } | |
| static void cl_skl_cldma_setup_spb(struct snd_sof_dev *sdev, | |
| unsigned int size, bool enable) | |
| { | |
| int sd_offset = SOF_DSP_REG_CL_SPBFIFO; | |
| if (enable) | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, | |
| HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK, | |
| HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(1)); | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, size); | |
| } | |
| static void cl_skl_cldma_set_intr(struct snd_sof_dev *sdev, bool enable) | |
| { | |
| u32 val = enable ? HDA_DSP_ADSPIC_CL_DMA : 0; | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, | |
| HDA_DSP_ADSPIC_CL_DMA, val); | |
| } | |
| static void cl_skl_cldma_cleanup_spb(struct snd_sof_dev *sdev) | |
| { | |
| int sd_offset = SOF_DSP_REG_CL_SPBFIFO; | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, | |
| HDA_CL_SPBFIFO_SPBFCCTL_SPIBE_MASK, | |
| HDA_CL_SPBFIFO_SPBFCCTL_SPIBE(0)); | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB, 0); | |
| } | |
| static void cl_skl_cldma_setup_controller(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab_bdl, | |
| unsigned int max_size, u32 count) | |
| { | |
| int sd_offset = SOF_HDA_ADSP_LOADER_BASE; | |
| /* Clear the stream first and then set it. */ | |
| cl_skl_cldma_stream_clear(sdev); | |
| /* setting the stream register */ | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, | |
| HDA_CL_SD_BDLPLBA(dmab_bdl->addr)); | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, | |
| HDA_CL_SD_BDLPUBA(dmab_bdl->addr)); | |
| /* Set the Cyclic Buffer Length. */ | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CBL, max_size); | |
| /* Set the Last Valid Index. */ | |
| snd_sof_dsp_write(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_LVI, count - 1); | |
| /* Set the Interrupt On Completion, FIFO Error Interrupt, | |
| * Descriptor Error Interrupt and the cldma stream number. | |
| */ | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CTL, | |
| HDA_CL_SD_CTL_INT_MASK, HDA_CL_SD_CTL_INT(1)); | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_CTL, | |
| HDA_CL_SD_CTL_STRM(0xf), | |
| HDA_CL_SD_CTL_STRM(1)); | |
| } | |
| static int cl_stream_prepare_skl(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab, | |
| struct snd_dma_buffer *dmab_bdl) | |
| { | |
| unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; | |
| __le32 *bdl; | |
| int frags; | |
| int ret; | |
| ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, bufsize, dmab); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: failed to alloc fw buffer: %x\n", __func__, ret); | |
| return ret; | |
| } | |
| ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, bufsize, dmab_bdl); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: failed to alloc blde: %x\n", __func__, ret); | |
| snd_dma_free_pages(dmab); | |
| return ret; | |
| } | |
| bdl = (__le32 *)dmab_bdl->area; | |
| frags = cl_skl_cldma_setup_bdle(sdev, dmab, &bdl, bufsize, 1); | |
| cl_skl_cldma_setup_controller(sdev, dmab_bdl, bufsize, frags); | |
| return ret; | |
| } | |
| static void cl_cleanup_skl(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab, | |
| struct snd_dma_buffer *dmab_bdl) | |
| { | |
| cl_skl_cldma_cleanup_spb(sdev); | |
| cl_skl_cldma_stream_clear(sdev); | |
| snd_dma_free_pages(dmab); | |
| snd_dma_free_pages(dmab_bdl); | |
| } | |
| static int cl_dsp_init_skl(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab, | |
| struct snd_dma_buffer *dmab_bdl) | |
| { | |
| struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
| const struct sof_intel_dsp_desc *chip = hda->desc; | |
| unsigned int status; | |
| u32 flags; | |
| int ret; | |
| /* check if the init_core is already enabled, if yes, reset and make it run, | |
| * if not, powerdown and enable it again. | |
| */ | |
| if (hda_dsp_core_is_enabled(sdev, chip->init_core_mask)) { | |
| /* if enabled, reset it, and run the init_core. */ | |
| ret = hda_dsp_core_stall_reset(sdev, chip->init_core_mask); | |
| if (ret < 0) | |
| goto err; | |
| ret = hda_dsp_core_run(sdev, chip->init_core_mask); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: dsp core start failed %d\n", __func__, ret); | |
| goto err; | |
| } | |
| } else { | |
| /* if not enabled, power down it first and then powerup and run | |
| * the init_core. | |
| */ | |
| ret = hda_dsp_core_reset_power_down(sdev, chip->init_core_mask); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: dsp core0 disable fail: %d\n", __func__, ret); | |
| goto err; | |
| } | |
| ret = hda_dsp_enable_core(sdev, chip->init_core_mask); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: dsp core0 enable fail: %d\n", __func__, ret); | |
| goto err; | |
| } | |
| } | |
| /* prepare DMA for code loader stream */ | |
| ret = cl_stream_prepare_skl(sdev, dmab, dmab_bdl); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: dma prepare fw loading err: %x\n", __func__, ret); | |
| return ret; | |
| } | |
| /* enable the interrupt */ | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, | |
| HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); | |
| /* enable IPC DONE interrupt */ | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, | |
| HDA_DSP_REG_HIPCCTL_DONE, | |
| HDA_DSP_REG_HIPCCTL_DONE); | |
| /* enable IPC BUSY interrupt */ | |
| snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, | |
| HDA_DSP_REG_HIPCCTL_BUSY, | |
| HDA_DSP_REG_HIPCCTL_BUSY); | |
| /* polling the ROM init status information. */ | |
| ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, | |
| chip->rom_status_reg, status, | |
| (FSR_TO_STATE_CODE(status) | |
| == FSR_STATE_INIT_DONE), | |
| HDA_DSP_REG_POLL_INTERVAL_US, | |
| chip->rom_init_timeout * | |
| USEC_PER_MSEC); | |
| if (ret < 0) | |
| goto err; | |
| return ret; | |
| err: | |
| flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX; | |
| snd_sof_dsp_dbg_dump(sdev, "Boot failed\n", flags); | |
| cl_cleanup_skl(sdev, dmab, dmab_bdl); | |
| hda_dsp_core_reset_power_down(sdev, chip->init_core_mask); | |
| return ret; | |
| } | |
| static void cl_skl_cldma_fill_buffer(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab, | |
| unsigned int bufsize, | |
| unsigned int copysize, | |
| const void *curr_pos, | |
| bool intr_enable) | |
| { | |
| struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
| /* copy the image into the buffer with the maximum buffer size. */ | |
| unsigned int size = (bufsize == copysize) ? bufsize : copysize; | |
| memcpy(dmab->area, curr_pos, size); | |
| /* Set the wait condition for every load. */ | |
| hda->code_loading = 1; | |
| /* Set the interrupt. */ | |
| if (intr_enable) | |
| cl_skl_cldma_set_intr(sdev, true); | |
| /* Set the SPB. */ | |
| cl_skl_cldma_setup_spb(sdev, size, true); | |
| /* Trigger the code loading stream. */ | |
| cl_skl_cldma_stream_run(sdev, true); | |
| } | |
| static int cl_skl_cldma_wait_interruptible(struct snd_sof_dev *sdev, | |
| bool intr_wait) | |
| { | |
| struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
| const struct sof_intel_dsp_desc *chip = hda->desc; | |
| int sd_offset = SOF_HDA_ADSP_LOADER_BASE; | |
| u8 cl_dma_intr_status; | |
| /* | |
| * Wait for CLDMA interrupt to inform the binary segment transfer is | |
| * complete. | |
| */ | |
| if (!wait_event_timeout(hda->waitq, !hda->code_loading, | |
| msecs_to_jiffies(HDA_SKL_WAIT_TIMEOUT))) { | |
| dev_err(sdev->dev, "cldma copy timeout\n"); | |
| dev_err(sdev->dev, "ROM code=%#x: FW status=%#x\n", | |
| snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_ERROR), | |
| snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg)); | |
| return -EIO; | |
| } | |
| /* now check DMA interrupt status */ | |
| cl_dma_intr_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, | |
| sd_offset + SOF_HDA_ADSP_REG_SD_STS); | |
| if (!(cl_dma_intr_status & HDA_CL_DMA_SD_INT_COMPLETE)) { | |
| dev_err(sdev->dev, "cldma copy failed\n"); | |
| return -EIO; | |
| } | |
| dev_dbg(sdev->dev, "cldma buffer copy complete\n"); | |
| return 0; | |
| } | |
| static int | |
| cl_skl_cldma_copy_to_buf(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab, | |
| const void *bin, | |
| u32 total_size, u32 bufsize) | |
| { | |
| unsigned int bytes_left = total_size; | |
| const void *curr_pos = bin; | |
| int ret; | |
| if (total_size <= 0) | |
| return -EINVAL; | |
| while (bytes_left > 0) { | |
| if (bytes_left > bufsize) { | |
| dev_dbg(sdev->dev, "cldma copy %#x bytes\n", bufsize); | |
| cl_skl_cldma_fill_buffer(sdev, dmab, bufsize, bufsize, curr_pos, true); | |
| ret = cl_skl_cldma_wait_interruptible(sdev, false); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: fw failed to load. %#x bytes remaining\n", | |
| __func__, bytes_left); | |
| return ret; | |
| } | |
| bytes_left -= bufsize; | |
| curr_pos += bufsize; | |
| } else { | |
| dev_dbg(sdev->dev, "cldma copy %#x bytes\n", bytes_left); | |
| cl_skl_cldma_set_intr(sdev, false); | |
| cl_skl_cldma_fill_buffer(sdev, dmab, bufsize, bytes_left, curr_pos, false); | |
| return 0; | |
| } | |
| } | |
| return bytes_left; | |
| } | |
| static int cl_copy_fw_skl(struct snd_sof_dev *sdev, | |
| struct snd_dma_buffer *dmab) | |
| { | |
| const struct firmware *fw = sdev->basefw.fw; | |
| struct firmware stripped_firmware; | |
| unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; | |
| int ret; | |
| stripped_firmware.data = fw->data + sdev->basefw.payload_offset; | |
| stripped_firmware.size = fw->size - sdev->basefw.payload_offset; | |
| dev_dbg(sdev->dev, "firmware size: %#zx buffer size %#x\n", fw->size, bufsize); | |
| ret = cl_skl_cldma_copy_to_buf(sdev, dmab, stripped_firmware.data, | |
| stripped_firmware.size, bufsize); | |
| if (ret < 0) | |
| dev_err(sdev->dev, "%s: fw copy failed %d\n", __func__, ret); | |
| return ret; | |
| } | |
| int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev) | |
| { | |
| struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
| const struct sof_intel_dsp_desc *chip = hda->desc; | |
| struct snd_dma_buffer dmab_bdl; | |
| struct snd_dma_buffer dmab; | |
| unsigned int reg; | |
| u32 flags; | |
| int ret; | |
| ret = cl_dsp_init_skl(sdev, &dmab, &dmab_bdl); | |
| /* retry enabling core and ROM load. seemed to help */ | |
| if (ret < 0) { | |
| ret = cl_dsp_init_skl(sdev, &dmab, &dmab_bdl); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "Error code=%#x: FW status=%#x\n", | |
| snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_ERROR), | |
| snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg)); | |
| dev_err(sdev->dev, "Core En/ROM load fail:%d\n", ret); | |
| return ret; | |
| } | |
| } | |
| dev_dbg(sdev->dev, "ROM init successful\n"); | |
| /* at this point DSP ROM has been initialized and should be ready for | |
| * code loading and firmware boot | |
| */ | |
| ret = cl_copy_fw_skl(sdev, &dmab); | |
| if (ret < 0) { | |
| dev_err(sdev->dev, "%s: load firmware failed : %d\n", __func__, ret); | |
| goto err; | |
| } | |
| ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, | |
| chip->rom_status_reg, reg, | |
| (FSR_TO_STATE_CODE(reg) | |
| == FSR_STATE_ROM_BASEFW_ENTERED), | |
| HDA_DSP_REG_POLL_INTERVAL_US, | |
| HDA_DSP_BASEFW_TIMEOUT_US); | |
| dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); | |
| cl_skl_cldma_stream_run(sdev, false); | |
| cl_cleanup_skl(sdev, &dmab, &dmab_bdl); | |
| if (!ret) | |
| return chip->init_core_mask; | |
| return ret; | |
| err: | |
| flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX; | |
| snd_sof_dsp_dbg_dump(sdev, "Boot failed\n", flags); | |
| /* power down DSP */ | |
| hda_dsp_core_reset_power_down(sdev, chip->init_core_mask); | |
| cl_skl_cldma_stream_run(sdev, false); | |
| cl_cleanup_skl(sdev, &dmab, &dmab_bdl); | |
| dev_err(sdev->dev, "%s: load fw failed err: %d\n", __func__, ret); | |
| return ret; | |
| } |