581 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			581 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
#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/pm_runtime.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_CL_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_CL_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_CL_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_CL_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_CL_SD_BDLPL, HDA_CL_SD_BDLPLBA(0));
 | 
						|
	snd_sof_dsp_write(sdev, HDA_DSP_BAR,
 | 
						|
			  sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0);
 | 
						|
 | 
						|
	/* Set the Cyclic Buffer Length to 0. */
 | 
						|
	snd_sof_dsp_write(sdev, HDA_DSP_BAR,
 | 
						|
			  sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, 0);
 | 
						|
	/* Set the Last Valid Index. */
 | 
						|
	snd_sof_dsp_write(sdev, HDA_DSP_BAR,
 | 
						|
			  sd_offset + SOF_HDA_ADSP_REG_CL_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_CL_SD_BDLPL,
 | 
						|
			  HDA_CL_SD_BDLPLBA(dmab_bdl->addr));
 | 
						|
	snd_sof_dsp_write(sdev, HDA_DSP_BAR,
 | 
						|
			  sd_offset + SOF_HDA_ADSP_REG_CL_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_CL_SD_CBL, max_size);
 | 
						|
	/* Set the Last Valid Index. */
 | 
						|
	snd_sof_dsp_write(sdev, HDA_DSP_BAR,
 | 
						|
			  sd_offset + SOF_HDA_ADSP_REG_CL_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_CL_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_CL_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_CL_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)
 | 
						|
 | 
						|
{
 | 
						|
	struct snd_sof_pdata *plat_data = sdev->pdata;
 | 
						|
	const struct firmware *fw =  plat_data->fw;
 | 
						|
	struct firmware stripped_firmware;
 | 
						|
	unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset;
 | 
						|
	stripped_firmware.size = plat_data->fw->size - plat_data->fw_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;
 | 
						|
}
 |