diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index 057d2a2d0bd05f..4f987b1f32f765 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -119,7 +119,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_REG_VS_EM3U 0x103C #define AZX_REG_VS_EM4L 0x1040 #define AZX_REG_VS_EM4U 0x1044 -#define AZX_REG_VS_LTRC 0x1048 +#define AZX_REG_VS_LTRP 0x1048 #define AZX_REG_VS_D0I3C 0x104A #define AZX_REG_VS_PCE 0x104B #define AZX_REG_VS_L2MAGC 0x1050 diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index f7e9358f1f068d..72d85b25df7d4a 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -8,7 +8,7 @@ snd-sof-intel-ipc-objs := intel-ipc.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-bus.o \ - apl.o cnl.o + apl.o cnl.o tgl.o snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 16db0f50d13960..f11a6e4096464e 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -29,7 +29,7 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { static void cnl_ipc_host_done(struct snd_sof_dev *sdev); static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev); -static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) +irqreturn_t cnl_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; u32 hipci; @@ -163,8 +163,7 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, return false; } -static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) +int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc_cmd_hdr *hdr; @@ -211,7 +210,7 @@ static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, return 0; } -static void cnl_ipc_dump(struct snd_sof_dev *sdev) +void cnl_ipc_dump(struct snd_sof_dev *sdev) { u32 hipcctl; u32 hipcida; @@ -369,22 +368,6 @@ const struct sof_intel_dsp_desc icl_chip_info = { }; EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); -const struct sof_intel_dsp_desc tgl_chip_info = { - /* Tigerlake */ - .cores_num = 4, - .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0), - .ipc_req = CNL_DSP_REG_HIPCIDR, - .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, - .ipc_ack = CNL_DSP_REG_HIPCIDA, - .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, - .ipc_ctl = CNL_DSP_REG_HIPCCTL, - .rom_init_timeout = 300, - .ssp_count = ICL_SSP_COUNT, - .ssp_base_offset = CNL_SSP_BASE_OFFSET, -}; -EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); - const struct sof_intel_dsp_desc ehl_chip_info = { /* Elkhartlake */ .cores_num = 4, diff --git a/sound/soc/sof/intel/hda-ipc.h b/sound/soc/sof/intel/hda-ipc.h index ade4c3191a39c9..10fbca5939dba6 100644 --- a/sound/soc/sof/intel/hda-ipc.h +++ b/sound/soc/sof/intel/hda-ipc.h @@ -48,4 +48,8 @@ #define HDA_PM_PG_STREAMING BIT(1) #define HDA_PM_PG_RSVD BIT(0) +irqreturn_t cnl_ipc_irq_thread(int irq, void *context); +int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); +void cnl_ipc_dump(struct snd_sof_dev *sdev); + #endif diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 9cb219b87b3ea4..382782e9f119ca 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -17,11 +17,13 @@ #include #include +#include #include #include "../ops.h" #include "hda.h" #define HDA_FW_BOOT_ATTEMPTS 3 +#define HDA_CL_STREAM_FORMAT 0x40 static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab, @@ -32,11 +34,6 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, struct pci_dev *pci = to_pci_dev(sdev->dev); int ret; - if (direction != SNDRV_PCM_STREAM_PLAYBACK) { - dev_err(sdev->dev, "error: code loading DMA is playback only\n"); - return -EINVAL; - } - dsp_stream = hda_dsp_stream_get(sdev, direction); if (!dsp_stream) { @@ -57,14 +54,21 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, hstream->format_val = format; hstream->bufsize = size; - ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); - if (ret < 0) { - dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); - goto error; + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + ret = hda_dsp_iccmax_stream_hw_params(sdev, dsp_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: iccmax stream prepare failed: %x\n", ret); + goto error; + } + } else { + ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + goto error; + } + hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); } - hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); - return hstream->stream_tag; error: @@ -206,18 +210,15 @@ static int cl_trigger(struct snd_sof_dev *sdev, } static struct hdac_ext_stream *get_stream_with_tag(struct snd_sof_dev *sdev, - int tag) + int tag, int direction) { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *s; /* get stream with tag */ - list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_PLAYBACK && - s->stream_tag == tag) { + list_for_each_entry(s, &bus->stream_list, list) + if (s->direction == direction && s->stream_tag == tag) return stream_to_hdac_ext_stream(s); - } - } return NULL; } @@ -227,12 +228,15 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, { struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret; + int ret = 0; - ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) + ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + else + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, 0); - hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_PLAYBACK, - hstream->stream_tag); + hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); hstream->running = 0; hstream->substream = NULL; @@ -290,6 +294,62 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream) return status; } +int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct hdac_ext_stream *iccmax_stream; + struct hdac_bus *bus = sof_to_bus(sdev); + struct firmware stripped_firmware; + int ret, ret1; + int iccmax_tag; + u8 original_gb; + + /* save the original LTRP guardband value */ + original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK; + + if (plat_data->fw->size <= plat_data->fw_offset) { + dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); + return -EINVAL; + } + + stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; + + /* prepare capture stream for ICCMAX */ + iccmax_tag = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, + &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); + if (iccmax_tag < 0) { + dev_err(sdev->dev, "error: dma prepare for ICCMAX %x\n", iccmax_tag); + return iccmax_tag; + } + + /* get stream with tag */ + iccmax_stream = get_stream_with_tag(sdev, iccmax_tag, SNDRV_PCM_STREAM_CAPTURE); + if (!iccmax_stream) { + dev_err(sdev->dev, "error: could not get stream with stream tag %d\n", iccmax_tag); + ret = -ENODEV; + } else { + ret = hda_dsp_cl_boot_firmware(sdev); + } + + /* + * Perform iccmax stream cleanup. This should be done even if firmware loading fails. + * If the cleanup also fails, we return the initial error + */ + ret1 = cl_cleanup(sdev, &sdev->dmab_bdl, iccmax_stream); + if (ret1 < 0) { + dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); + + /* set return value to indicate cleanup failure */ + if (!ret) + ret = ret1; + } + + /* restore the original guardband value after FW boot */ + snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, original_gb); + + return ret; +} + int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; @@ -313,7 +373,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->boot_wait); /* prepare DMA for code loader stream */ - tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size, + tag = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); if (tag < 0) { @@ -323,7 +383,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) } /* get stream with tag */ - stream = get_stream_with_tag(sdev, tag); + stream = get_stream_with_tag(sdev, tag, SNDRV_PCM_STREAM_PLAYBACK); if (!stream) { dev_err(sdev->dev, "error: could not get stream with stream tag %d\n", diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 1bda14c3590cd1..0e09ede922c7a0 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -23,6 +23,8 @@ #include "../sof-audio.h" #include "hda.h" +#define HDA_LTRP_GB_VALUE_US 95 + /* * set up one of BDL entries for a stream */ @@ -322,6 +324,73 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, return 0; } +/* minimal recommended programming for ICCMAX stream */ +int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *hstream = &stream->hstream; + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int ret; + u32 mask = 0x1 << hstream->index; + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + if (hstream->posbuf) + *hstream->posbuf = 0; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + 0x0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + 0x0); + + hstream->frags = 0; + + ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); + if (ret < 0) { + dev_err(sdev->dev, "error: set up of BDL failed\n"); + return ret; + } + + /* program BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + (u32)hstream->bdl.addr); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + upper_32_bits(hstream->bdl.addr)); + + /* program cyclic buffer length */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, + hstream->bufsize); + + /* program last valid index */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, + 0xffff, (hstream->frags - 1)); + + /* decouple host and link DMA, enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + + /* Follow HW recommendation to set the guardband value to 95us during FW boot */ + snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, HDA_LTRP_GB_VALUE_US); + + /* start DMA */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, SOF_HDA_SD_CTL_DMA_START); + + return 0; +} + /* * prepare for common hdac registers settings, for both code loader * and normal stream. diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index fe452f0d0ec73c..5ee2f835405183 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -246,6 +246,7 @@ /* Intel Vendor Specific Registers */ #define HDA_VS_INTEL_EM2 0x1030 #define HDA_VS_INTEL_EM2_L1SEN BIT(13) +#define HDA_VS_INTEL_LTRP_GB_MASK 0x3F /* HIPCI */ #define HDA_DSP_REG_HIPCI_BUSY BIT(31) @@ -546,6 +547,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params); +int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params); int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); @@ -608,6 +612,7 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); * DSP Code loader. */ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); +int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev); int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev); /* pre and post fw run ops */ @@ -728,6 +733,7 @@ extern struct snd_soc_dai_driver skl_dai[]; */ extern const struct snd_sof_dsp_ops sof_apl_ops; extern const struct snd_sof_dsp_ops sof_cnl_ops; +extern const struct snd_sof_dsp_ops sof_tgl_ops; extern const struct sof_intel_dsp_desc apl_chip_info; extern const struct sof_intel_dsp_desc cnl_chip_info; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c new file mode 100644 index 00000000000000..d0e84b7747a0f9 --- /dev/null +++ b/sound/soc/sof/intel/tgl.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Authors: Ranjani Sridharan +// + +/* + * Hardware interface for audio DSP on Tigerlake. + */ + +#include "../ops.h" +#include "hda.h" +#include "hda-ipc.h" +#include "../sof-audio.h" + +static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, +}; + +/* Tigerlake ops */ +const struct snd_sof_dsp_ops sof_tgl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + .write64 = sof_io_write64, + .read64 = sof_io_read64, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* doorbell */ + .irq_thread = cnl_ipc_irq_thread, + + /* ipc */ + .send_msg = cnl_ipc_send_msg, + .fw_ready = sof_fw_ready, + .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, + .get_window_offset = hda_dsp_ipc_get_window_offset, + + .ipc_msg_data = hda_ipc_msg_data, + .ipc_pcm_params = hda_ipc_pcm_params, + + /* machine driver */ + .machine_select = hda_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = hda_set_mach_params, + + /* debug */ + .debug_map = tgl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + .ipc_dump = cnl_ipc_dump, + + /* stream callbacks */ + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, + .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_pointer = hda_dsp_pcm_pointer, + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) + /* probe callbacks */ + .probe_assign = hda_probe_compr_assign, + .probe_free = hda_probe_compr_free, + .probe_set_params = hda_probe_compr_set_params, + .probe_trigger = hda_probe_compr_trigger, + .probe_pointer = hda_probe_compr_pointer, +#endif + + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_raw, + + /* pre/post fw run */ + .pre_fw_run = hda_dsp_pre_fw_run, + .post_fw_run = hda_dsp_post_fw_run, + + /* dsp core power up/down */ + .core_power_up = hda_dsp_enable_core, + .core_power_down = hda_dsp_core_reset_power_down, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware_iccmax, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, + + /* PM */ + .suspend = hda_dsp_suspend, + .resume = hda_dsp_resume, + .runtime_suspend = hda_dsp_runtime_suspend, + .runtime_resume = hda_dsp_runtime_resume, + .runtime_idle = hda_dsp_runtime_idle, + .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, + .set_power_state = hda_dsp_set_power_state, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + .arch_ops = &sof_xtensa_arch_ops, +}; +EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +const struct sof_intel_dsp_desc tgl_chip_info = { + /* Tigerlake */ + .cores_num = 4, + .init_core_mask = 1, + .cores_mask = HDA_DSP_CORE_MASK(0), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_init_timeout = 300, + .ssp_count = ICL_SSP_COUNT, + .ssp_base_offset = CNL_SSP_BASE_OFFSET, +}; +EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index aa3532ba14349d..d3d3973837de4b 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -201,7 +201,7 @@ static const struct sof_dev_desc tgl_desc = { .default_tplg_path = "intel/sof-tplg", .default_fw_filename = "sof-tgl.ri", .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", - .ops = &sof_cnl_ops, + .ops = &sof_tgl_ops, }; #endif