#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)
#define HDA_CL_SD_CTL_SRST_SHIFT 0
#define HDA_CL_SD_CTL_SRST(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_SRST_SHIFT)
#define HDA_CL_SD_CTL_RUN_SHIFT 1
#define HDA_CL_SD_CTL_RUN(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_RUN_SHIFT)
#define HDA_CL_SD_CTL_IOCE_SHIFT 2
#define HDA_CL_SD_CTL_IOCE(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_IOCE_SHIFT)
#define HDA_CL_SD_CTL_FEIE_SHIFT 3
#define HDA_CL_SD_CTL_FEIE(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_FEIE_SHIFT)
#define HDA_CL_SD_CTL_DEIE_SHIFT 4
#define HDA_CL_SD_CTL_DEIE(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_DEIE_SHIFT)
#define HDA_CL_SD_CTL_FIFOLC_SHIFT 5
#define HDA_CL_SD_CTL_FIFOLC(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_FIFOLC_SHIFT)
#define HDA_CL_SD_CTL_STRIPE_SHIFT 16
#define HDA_CL_SD_CTL_STRIPE(x) (((x) & 0x3) << \
HDA_CL_SD_CTL_STRIPE_SHIFT)
#define HDA_CL_SD_CTL_TP_SHIFT 18
#define HDA_CL_SD_CTL_TP(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_TP_SHIFT)
#define HDA_CL_SD_CTL_DIR_SHIFT 19
#define HDA_CL_SD_CTL_DIR(x) (((x) & 0x1) << \
HDA_CL_SD_CTL_DIR_SHIFT)
#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)
#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)
#define HDA_CL_SD_BDLPUBA(x) \
(upper_32_bits(x))
#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;
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;
}
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);
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;
cl_skl_cldma_stream_run(sdev, 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);
snd_sof_dsp_write(sdev, HDA_DSP_BAR,
sd_offset + SOF_HDA_ADSP_REG_SD_CBL, 0);
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;
cl_skl_cldma_stream_clear(sdev);
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));
snd_sof_dsp_write(sdev, HDA_DSP_BAR,
sd_offset + SOF_HDA_ADSP_REG_SD_CBL, max_size);
snd_sof_dsp_write(sdev, HDA_DSP_BAR,
sd_offset + SOF_HDA_ADSP_REG_SD_LVI, count - 1);
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;
if (hda_dsp_core_is_enabled(sdev, chip->init_core_mask)) {
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 {
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;
}
}
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;
}
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC);
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
HDA_DSP_REG_HIPCCTL_DONE,
HDA_DSP_REG_HIPCCTL_DONE);
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
HDA_DSP_REG_HIPCCTL_BUSY,
HDA_DSP_REG_HIPCCTL_BUSY);
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;
unsigned int size = (bufsize == copysize) ? bufsize : copysize;
memcpy(dmab->area, curr_pos, size);
hda->code_loading = 1;
if (intr_enable)
cl_skl_cldma_set_intr(sdev, true);
cl_skl_cldma_setup_spb(sdev, size, true);
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;
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;
}
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);
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");
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);
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;
}