Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-catpt-y := device.o dsp.o loader.o ipc.o messages.o pcm.o sysfs.o
snd-soc-catpt-y += trace.o
# tell define_trace.h where to find the trace header
CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_SND_SOC_INTEL_CATPT) += snd-soc-catpt.o
+175
View File
@@ -0,0 +1,175 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2020 Intel Corporation
*
* Author: Cezary Rojewski <cezary.rojewski@intel.com>
*/
#ifndef __SND_SOC_INTEL_CATPT_CORE_H
#define __SND_SOC_INTEL_CATPT_CORE_H
#include <linux/dma/dw.h>
#include <linux/irqreturn.h>
#include "messages.h"
#include "registers.h"
struct catpt_dev;
extern const struct attribute_group *catpt_attr_groups[];
void catpt_sram_free(struct resource *sram);
struct resource *
catpt_request_region(struct resource *root, resource_size_t size);
struct catpt_ipc_msg {
union {
u32 header;
union catpt_global_msg rsp;
};
void *data;
size_t size;
};
struct catpt_ipc {
struct device *dev;
struct catpt_ipc_msg rx;
struct catpt_fw_ready config;
u32 default_timeout;
bool ready;
spinlock_t lock;
struct mutex mutex;
struct completion done_completion;
struct completion busy_completion;
};
void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev);
struct catpt_module_type {
bool loaded;
u32 entry_point;
u32 persistent_size;
u32 scratch_size;
/* DRAM, initial module state */
u32 state_offset;
u32 state_size;
struct list_head node;
};
struct catpt_spec {
struct snd_soc_acpi_mach *machines;
u8 core_id;
const char *fw_name;
u32 host_dram_offset;
u32 host_iram_offset;
u32 host_shim_offset;
u32 host_dma_offset[CATPT_DMA_COUNT];
u32 host_ssp_offset[CATPT_SSP_COUNT];
u32 dram_mask;
u32 iram_mask;
u32 d3srampgd_bit;
u32 d3pgd_bit;
void (*pll_shutdown)(struct catpt_dev *cdev, bool enable);
};
struct catpt_dev {
struct device *dev;
struct dw_dma_chip *dmac;
struct catpt_ipc ipc;
void __iomem *pci_ba;
void __iomem *lpe_ba;
u32 lpe_base;
int irq;
const struct catpt_spec *spec;
struct completion fw_ready;
struct resource dram;
struct resource iram;
struct resource *scratch;
struct catpt_mixer_stream_info mixer;
struct catpt_module_type modules[CATPT_MODULE_COUNT];
struct catpt_ssp_device_format devfmt[CATPT_SSP_COUNT];
struct list_head stream_list;
struct mutex stream_mutex;
struct mutex clk_mutex;
struct catpt_dx_context dx_ctx;
void *dxbuf_vaddr;
dma_addr_t dxbuf_paddr;
};
int catpt_dmac_probe(struct catpt_dev *cdev);
void catpt_dmac_remove(struct catpt_dev *cdev);
struct dma_chan *catpt_dma_request_config_chan(struct catpt_dev *cdev);
int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan,
dma_addr_t dst_addr, dma_addr_t src_addr,
size_t size);
int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan,
dma_addr_t dst_addr, dma_addr_t src_addr,
size_t size);
void lpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable);
void wpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable);
int catpt_dsp_power_up(struct catpt_dev *cdev);
int catpt_dsp_power_down(struct catpt_dev *cdev);
int catpt_dsp_stall(struct catpt_dev *cdev, bool stall);
void catpt_dsp_update_srampge(struct catpt_dev *cdev, struct resource *sram,
unsigned long mask);
int catpt_dsp_update_lpclock(struct catpt_dev *cdev);
irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id);
irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id);
/*
* IPC handlers may return positive values which denote successful
* HOST <-> DSP communication yet failure to process specific request.
* Use below macro to convert returned non-zero values appropriately
*/
#define CATPT_IPC_RET(ret) (((ret) <= 0) ? (ret) : -EREMOTEIO)
int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
struct catpt_ipc_msg *reply, int timeout, const char *name);
int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
struct catpt_ipc_msg *reply, const char *name);
int catpt_first_boot_firmware(struct catpt_dev *cdev);
int catpt_boot_firmware(struct catpt_dev *cdev, bool restore);
int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan);
int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan);
int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan);
int catpt_coredump(struct catpt_dev *cdev);
#include <sound/memalloc.h>
#include <uapi/sound/asound.h>
struct snd_pcm_substream;
struct catpt_stream_template;
struct catpt_stream_runtime {
struct snd_pcm_substream *substream;
struct catpt_stream_template *template;
struct catpt_stream_info info;
struct resource *persistent;
struct snd_dma_buffer pgtbl;
bool allocated;
bool prepared;
struct list_head node;
};
int catpt_register_plat_component(struct catpt_dev *cdev);
void catpt_stream_update_position(struct catpt_dev *cdev,
struct catpt_stream_runtime *stream,
struct catpt_notify_position *pos);
struct catpt_stream_runtime *
catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id);
int catpt_arm_stream_templates(struct catpt_dev *cdev);
#endif
+403
View File
@@ -0,0 +1,403 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
// Special thanks to:
// Marcin Barlik <marcin.barlik@intel.com>
// Piotr Papierkowski <piotr.papierkowski@intel.com>
//
// for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and
// helping backtrack its historical background
//
#include <linux/acpi.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <sound/intel-dsp-config.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "core.h"
#include "registers.h"
static int catpt_do_suspend(struct device *dev)
{
struct catpt_dev *cdev = dev_get_drvdata(dev);
struct dma_chan *chan;
int ret;
chan = catpt_dma_request_config_chan(cdev);
if (IS_ERR(chan))
return PTR_ERR(chan);
memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
if (ret) {
ret = CATPT_IPC_RET(ret);
goto release_dma_chan;
}
ret = catpt_dsp_stall(cdev, true);
if (ret)
goto release_dma_chan;
ret = catpt_store_memdumps(cdev, chan);
if (ret) {
dev_err(cdev->dev, "store memdumps failed: %d\n", ret);
goto release_dma_chan;
}
ret = catpt_store_module_states(cdev, chan);
if (ret) {
dev_err(cdev->dev, "store module states failed: %d\n", ret);
goto release_dma_chan;
}
ret = catpt_store_streams_context(cdev, chan);
if (ret)
dev_err(cdev->dev, "store streams ctx failed: %d\n", ret);
release_dma_chan:
dma_release_channel(chan);
if (ret)
return ret;
return catpt_dsp_power_down(cdev);
}
/* Do not block the system from suspending, recover on resume() if needed. */
static int catpt_suspend(struct device *dev)
{
catpt_do_suspend(dev);
return 0;
}
static int catpt_resume(struct device *dev)
{
struct catpt_dev *cdev = dev_get_drvdata(dev);
int ret, i;
ret = catpt_dsp_power_up(cdev);
if (ret)
return ret;
if (!try_module_get(dev->driver->owner)) {
dev_info(dev, "module unloading, skipping fw boot\n");
return 0;
}
module_put(dev->driver->owner);
ret = catpt_boot_firmware(cdev, true);
if (ret) {
dev_err(cdev->dev, "boot firmware failed: %d\n", ret);
return ret;
}
/* reconfigure SSP devices after Dx transition */
for (i = 0; i < CATPT_SSP_COUNT; i++) {
if (cdev->devfmt[i].iface == UINT_MAX)
continue;
ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
if (ret)
return CATPT_IPC_RET(ret);
}
return 0;
}
static int catpt_runtime_suspend(struct device *dev)
{
if (!try_module_get(dev->driver->owner)) {
dev_info(dev, "module unloading, skipping suspend\n");
return 0;
}
module_put(dev->driver->owner);
return catpt_do_suspend(dev);
}
static int catpt_runtime_resume(struct device *dev)
{
return catpt_resume(dev);
}
static const struct dev_pm_ops catpt_dev_pm = {
SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume)
RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL)
};
/* machine board owned by CATPT is removed with this hook */
static void board_pdev_unregister(void *data)
{
platform_device_unregister(data);
}
static int catpt_register_board(struct catpt_dev *cdev)
{
const struct catpt_spec *spec = cdev->spec;
struct snd_soc_acpi_mach *mach;
struct platform_device *board;
mach = snd_soc_acpi_find_machine(spec->machines);
if (!mach) {
dev_info(cdev->dev, "no machines present\n");
return 0;
}
mach->mach_params.platform = "catpt-platform";
board = platform_device_register_data(NULL, mach->drv_name,
PLATFORM_DEVID_NONE,
(const void *)mach, sizeof(*mach));
if (IS_ERR(board)) {
dev_err(cdev->dev, "register board failed: %ld\n", PTR_ERR(board));
return PTR_ERR(board);
}
return devm_add_action_or_reset(cdev->dev, board_pdev_unregister,
board);
}
static int catpt_probe_components(struct catpt_dev *cdev)
{
int ret;
ret = catpt_dsp_power_up(cdev);
if (ret)
return ret;
ret = catpt_dmac_probe(cdev);
if (ret) {
dev_err(cdev->dev, "DMAC probe failed: %d\n", ret);
goto err_dmac_probe;
}
ret = catpt_first_boot_firmware(cdev);
if (ret) {
dev_err(cdev->dev, "first fw boot failed: %d\n", ret);
goto err_boot_fw;
}
ret = catpt_register_plat_component(cdev);
if (ret) {
dev_err(cdev->dev, "register plat comp failed: %d\n", ret);
goto err_boot_fw;
}
/* reflect actual ADSP state in pm_runtime */
pm_runtime_set_active(cdev->dev);
pm_runtime_set_autosuspend_delay(cdev->dev, 2000);
pm_runtime_use_autosuspend(cdev->dev);
pm_runtime_mark_last_busy(cdev->dev);
/* Enable PM before spawning child device. See catpt_dai_pcm_new(). */
pm_runtime_enable(cdev->dev);
ret = catpt_register_board(cdev);
if (ret) {
dev_err(cdev->dev, "register board failed: %d\n", ret);
goto err_reg_board;
}
return 0;
err_reg_board:
pm_runtime_disable(cdev->dev);
snd_soc_unregister_component(cdev->dev);
err_boot_fw:
catpt_dmac_remove(cdev);
err_dmac_probe:
catpt_dsp_power_down(cdev);
return ret;
}
static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev,
const struct catpt_spec *spec)
{
cdev->dev = dev;
cdev->spec = spec;
init_completion(&cdev->fw_ready);
INIT_LIST_HEAD(&cdev->stream_list);
mutex_init(&cdev->stream_mutex);
mutex_init(&cdev->clk_mutex);
/*
* Mark both device formats as uninitialized. Once corresponding
* cpu_dai's pcm is created, proper values are assigned.
*/
cdev->devfmt[CATPT_SSP_IFACE_0].iface = UINT_MAX;
cdev->devfmt[CATPT_SSP_IFACE_1].iface = UINT_MAX;
resource_set_range(&cdev->dram, spec->host_dram_offset, catpt_dram_size(cdev));
resource_set_range(&cdev->iram, spec->host_iram_offset, catpt_iram_size(cdev));
catpt_ipc_init(&cdev->ipc, dev);
}
static int catpt_acpi_probe(struct platform_device *pdev)
{
const struct catpt_spec *spec;
struct catpt_dev *cdev;
struct device *dev = &pdev->dev;
const struct acpi_device_id *id;
struct resource *res;
int ret;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return -ENODEV;
ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
dev_dbg(dev, "CATPT ACPI driver not selected, aborting probe\n");
return -ENODEV;
}
cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENOMEM;
spec = (const struct catpt_spec *)id->driver_data;
catpt_dev_init(cdev, dev, spec);
/* map DSP bar address */
cdev->lpe_ba = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(cdev->lpe_ba))
return PTR_ERR(cdev->lpe_ba);
cdev->lpe_base = res->start;
/* map PCI bar address */
cdev->pci_ba = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(cdev->pci_ba))
return PTR_ERR(cdev->pci_ba);
/*
* As per design HOST is responsible for preserving firmware's runtime
* context during D0 -> D3 -> D0 transitions. Addresses used for DMA
* to/from HOST memory shall be outside the reserved range of 0xFFFxxxxx.
*/
ret = dma_coerce_mask_and_coherent(cdev->dev, DMA_BIT_MASK(31));
if (ret)
return ret;
cdev->dxbuf_vaddr = dmam_alloc_coherent(dev, resource_size(&cdev->dram),
&cdev->dxbuf_paddr, GFP_KERNEL);
if (!cdev->dxbuf_vaddr)
return -ENOMEM;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
cdev->irq = ret;
platform_set_drvdata(pdev, cdev);
ret = devm_request_threaded_irq(dev, cdev->irq, catpt_dsp_irq_handler,
catpt_dsp_irq_thread,
IRQF_SHARED, "AudioDSP", cdev);
if (ret)
return ret;
return catpt_probe_components(cdev);
}
static void catpt_acpi_remove(struct platform_device *pdev)
{
struct catpt_dev *cdev = platform_get_drvdata(pdev);
pm_runtime_disable(cdev->dev);
snd_soc_unregister_component(cdev->dev);
catpt_dmac_remove(cdev);
catpt_dsp_power_down(cdev);
catpt_sram_free(&cdev->iram);
catpt_sram_free(&cdev->dram);
}
static struct snd_soc_acpi_mach lpt_machines[] = {
{
.id = "INT33CA",
.drv_name = "hsw_rt5640",
},
{}
};
static struct snd_soc_acpi_mach wpt_machines[] = {
{
.id = "INT33CA",
.drv_name = "hsw_rt5640",
},
{
.id = "INT343A",
.drv_name = "bdw_rt286",
},
{
.id = "10EC5650",
.drv_name = "bdw-rt5650",
},
{
.id = "RT5677CE",
.drv_name = "bdw-rt5677",
},
{}
};
static struct catpt_spec lpt_desc = {
.machines = lpt_machines,
.core_id = 0x01,
.fw_name = "intel/IntcSST1.bin",
.host_dram_offset = 0x000000,
.host_iram_offset = 0x080000,
.host_shim_offset = 0x0E7000,
.host_dma_offset = { 0x0F0000, 0x0F8000 },
.host_ssp_offset = { 0x0E8000, 0x0E9000 },
.dram_mask = LPT_VDRTCTL0_DSRAMPGE_MASK,
.iram_mask = LPT_VDRTCTL0_ISRAMPGE_MASK,
.d3srampgd_bit = LPT_VDRTCTL0_D3SRAMPGD,
.d3pgd_bit = LPT_VDRTCTL0_D3PGD,
.pll_shutdown = lpt_dsp_pll_shutdown,
};
static struct catpt_spec wpt_desc = {
.machines = wpt_machines,
.core_id = 0x02,
.fw_name = "intel/IntcSST2.bin",
.host_dram_offset = 0x000000,
.host_iram_offset = 0x0A0000,
.host_shim_offset = 0x0FB000,
.host_dma_offset = { 0x0FE000, 0x0FF000 },
.host_ssp_offset = { 0x0FC000, 0x0FD000 },
.dram_mask = WPT_VDRTCTL0_DSRAMPGE_MASK,
.iram_mask = WPT_VDRTCTL0_ISRAMPGE_MASK,
.d3srampgd_bit = WPT_VDRTCTL0_D3SRAMPGD,
.d3pgd_bit = WPT_VDRTCTL0_D3PGD,
.pll_shutdown = wpt_dsp_pll_shutdown,
};
static const struct acpi_device_id catpt_ids[] = {
{ "INT33C8", (unsigned long)&lpt_desc },
{ "INT3438", (unsigned long)&wpt_desc },
{ }
};
MODULE_DEVICE_TABLE(acpi, catpt_ids);
static struct platform_driver catpt_acpi_driver = {
.probe = catpt_acpi_probe,
.remove = catpt_acpi_remove,
.driver = {
.name = "intel_catpt",
.acpi_match_table = catpt_ids,
.pm = pm_ptr(&catpt_dev_pm),
.dev_groups = catpt_attr_groups,
},
};
module_platform_driver(catpt_acpi_driver);
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver");
MODULE_LICENSE("GPL v2");
+537
View File
@@ -0,0 +1,537 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/cleanup.h>
#include <linux/devcoredump.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/pxa2xx_ssp.h>
#include "core.h"
#include "messages.h"
#include "registers.h"
static bool catpt_dma_filter(struct dma_chan *chan, void *param)
{
return param == chan->device->dev;
}
/*
* Either engine 0 or 1 can be used for image loading.
* Align with Windows driver equivalent and stick to engine 1.
*/
#define CATPT_DMA_DEVID 1
#define CATPT_DMA_DSP_ADDR_MASK GENMASK(31, 20)
struct dma_chan *catpt_dma_request_config_chan(struct catpt_dev *cdev)
{
struct dma_slave_config config;
struct dma_chan *chan;
dma_cap_mask_t mask;
int ret;
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
chan = dma_request_channel(mask, catpt_dma_filter, cdev->dev);
if (!chan) {
dev_err(cdev->dev, "request channel failed\n");
return ERR_PTR(-ENODEV);
}
memset(&config, 0, sizeof(config));
config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
config.src_maxburst = 16;
config.dst_maxburst = 16;
ret = dmaengine_slave_config(chan, &config);
if (ret) {
dev_err(cdev->dev, "slave config failed: %d\n", ret);
dma_release_channel(chan);
return ERR_PTR(ret);
}
return chan;
}
static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan,
dma_addr_t dst_addr, dma_addr_t src_addr,
size_t size)
{
struct dma_async_tx_descriptor *desc;
enum dma_status status;
int ret;
desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, size,
DMA_CTRL_ACK);
if (!desc) {
dev_err(cdev->dev, "prep dma memcpy failed\n");
return -EIO;
}
/* enable demand mode for dma channel */
catpt_updatel_shim(cdev, HMDC,
CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id),
CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id));
ret = dma_submit_error(dmaengine_submit(desc));
if (ret) {
dev_err(cdev->dev, "submit tx failed: %d\n", ret);
goto clear_hdda;
}
status = dma_wait_for_async_tx(desc);
ret = (status == DMA_COMPLETE) ? 0 : -EPROTO;
clear_hdda:
/* regardless of status, disable access to HOST memory in demand mode */
catpt_updatel_shim(cdev, HMDC,
CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), 0);
return ret;
}
int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan,
dma_addr_t dst_addr, dma_addr_t src_addr,
size_t size)
{
return catpt_dma_memcpy(cdev, chan, dst_addr | CATPT_DMA_DSP_ADDR_MASK,
src_addr, size);
}
int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan,
dma_addr_t dst_addr, dma_addr_t src_addr,
size_t size)
{
return catpt_dma_memcpy(cdev, chan, dst_addr,
src_addr | CATPT_DMA_DSP_ADDR_MASK, size);
}
int catpt_dmac_probe(struct catpt_dev *cdev)
{
struct dw_dma_chip *dmac;
int ret;
dmac = devm_kzalloc(cdev->dev, sizeof(*dmac), GFP_KERNEL);
if (!dmac)
return -ENOMEM;
dmac->regs = catpt_dma_addr(cdev, CATPT_DMA_DEVID);
dmac->dev = cdev->dev;
dmac->irq = cdev->irq;
/*
* Caller is responsible for putting device in D0 to allow
* for I/O and memory access before probing DW.
*/
ret = dw_dma_probe(dmac);
if (ret)
return ret;
cdev->dmac = dmac;
return 0;
}
void catpt_dmac_remove(struct catpt_dev *cdev)
{
/*
* As do_dma_remove() juggles with pm_runtime_get_xxx() and
* pm_runtime_put_xxx() while both ADSP and DW 'devices' are part of
* the same module, caller makes sure pm_runtime_disable() is invoked
* before removing DW to prevent postmortem resume and suspend.
*/
dw_dma_remove(cdev->dmac);
}
static void catpt_dsp_set_srampge(struct catpt_dev *cdev, struct resource *sram,
unsigned long mask, unsigned long new)
{
unsigned long old;
u32 off = sram->start;
unsigned long b = __ffs(mask);
old = catpt_readl_pci(cdev, VDRTCTL0) & mask;
dev_dbg(cdev->dev, "SRAMPGE [0x%08lx] 0x%08lx -> 0x%08lx",
mask, old, new);
if (old == new)
return;
catpt_updatel_pci(cdev, VDRTCTL0, mask, new);
/* wait for SRAM power gating to propagate */
udelay(60);
/*
* Dummy read as the very first access after block enable
* to prevent byte loss in future operations.
*/
for_each_clear_bit_from(b, &new, fls_long(mask)) {
u8 buf[4];
/* newly enabled: new bit=0 while old bit=1 */
if (test_bit(b, &old)) {
dev_dbg(cdev->dev, "sanitize block %ld: off 0x%08x\n",
b - __ffs(mask), off);
memcpy_fromio(buf, cdev->lpe_ba + off, sizeof(buf));
}
off += CATPT_MEMBLOCK_SIZE;
}
}
void catpt_dsp_update_srampge(struct catpt_dev *cdev, struct resource *sram,
unsigned long mask)
{
struct resource *res;
unsigned long new = 0;
/* flag all busy blocks */
for (res = sram->child; res; res = res->sibling) {
u32 h, l;
h = (res->end - sram->start) / CATPT_MEMBLOCK_SIZE;
l = (res->start - sram->start) / CATPT_MEMBLOCK_SIZE;
new |= GENMASK(h, l);
}
/* offset value given mask's start and invert it as ON=b0 */
new = ~(new << __ffs(mask)) & mask;
/* disable core clock gating */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0);
catpt_dsp_set_srampge(cdev, sram, mask, new);
/* enable core clock gating */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE,
CATPT_VDRTCTL2_DCLCGE);
}
int catpt_dsp_stall(struct catpt_dev *cdev, bool stall)
{
u32 reg, val;
val = stall ? CATPT_CS_STALL : 0;
catpt_updatel_shim(cdev, CS1, CATPT_CS_STALL, val);
return catpt_readl_poll_shim(cdev, CS1,
reg, (reg & CATPT_CS_STALL) == val,
500, 10000);
}
static int catpt_dsp_reset(struct catpt_dev *cdev, bool reset)
{
u32 reg, val;
val = reset ? CATPT_CS_RST : 0;
catpt_updatel_shim(cdev, CS1, CATPT_CS_RST, val);
return catpt_readl_poll_shim(cdev, CS1,
reg, (reg & CATPT_CS_RST) == val,
500, 10000);
}
void lpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable)
{
u32 val;
val = enable ? LPT_VDRTCTL0_APLLSE : 0;
catpt_updatel_pci(cdev, VDRTCTL0, LPT_VDRTCTL0_APLLSE, val);
}
void wpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable)
{
u32 val;
val = enable ? WPT_VDRTCTL2_APLLSE : 0;
catpt_updatel_pci(cdev, VDRTCTL2, WPT_VDRTCTL2_APLLSE, val);
}
static int catpt_dsp_select_lpclock(struct catpt_dev *cdev, bool lp, bool waiti)
{
u32 mask, reg, val;
int ret;
guard(mutex)(&cdev->clk_mutex);
val = lp ? CATPT_CS_LPCS : 0;
reg = catpt_readl_shim(cdev, CS1) & CATPT_CS_LPCS;
dev_dbg(cdev->dev, "LPCS [0x%08lx] 0x%08x -> 0x%08x",
CATPT_CS_LPCS, reg, val);
if (reg == val)
return 0;
if (waiti) {
/* wait for DSP to signal WAIT state */
ret = catpt_readl_poll_shim(cdev, ISD,
reg, (reg & CATPT_ISD_DCPWM),
500, 10000);
if (ret) {
dev_warn(cdev->dev, "await WAITI timeout\n");
/* no signal - only high clock selection allowed */
if (lp)
return 0;
}
}
ret = catpt_readl_poll_shim(cdev, CLKCTL,
reg, !(reg & CATPT_CLKCTL_CFCIP),
500, 10000);
if (ret)
dev_warn(cdev->dev, "clock change still in progress\n");
/* default to DSP core & audio fabric high clock */
val |= CATPT_CS_DCS_HIGH;
mask = CATPT_CS_LPCS | CATPT_CS_DCS;
catpt_updatel_shim(cdev, CS1, mask, val);
ret = catpt_readl_poll_shim(cdev, CLKCTL,
reg, !(reg & CATPT_CLKCTL_CFCIP),
500, 10000);
if (ret)
dev_warn(cdev->dev, "clock change still in progress\n");
/* update PLL accordingly */
cdev->spec->pll_shutdown(cdev, lp);
return 0;
}
int catpt_dsp_update_lpclock(struct catpt_dev *cdev)
{
struct catpt_stream_runtime *stream;
list_for_each_entry(stream, &cdev->stream_list, node)
if (stream->prepared)
return catpt_dsp_select_lpclock(cdev, false, true);
return catpt_dsp_select_lpclock(cdev, true, true);
}
/* bring registers to their defaults as HW won't reset itself */
static void catpt_dsp_set_regs_defaults(struct catpt_dev *cdev)
{
int i;
catpt_writel_shim(cdev, CS1, CATPT_CS_DEFAULT);
catpt_writel_shim(cdev, ISC, CATPT_ISC_DEFAULT);
catpt_writel_shim(cdev, ISD, CATPT_ISD_DEFAULT);
catpt_writel_shim(cdev, IMC, CATPT_IMC_DEFAULT);
catpt_writel_shim(cdev, IMD, CATPT_IMD_DEFAULT);
catpt_writel_shim(cdev, IPCC, CATPT_IPCC_DEFAULT);
catpt_writel_shim(cdev, IPCD, CATPT_IPCD_DEFAULT);
catpt_writel_shim(cdev, CLKCTL, CATPT_CLKCTL_DEFAULT);
catpt_writel_shim(cdev, CS2, CATPT_CS2_DEFAULT);
catpt_writel_shim(cdev, LTRC, CATPT_LTRC_DEFAULT);
catpt_writel_shim(cdev, HMDC, CATPT_HMDC_DEFAULT);
for (i = 0; i < CATPT_SSP_COUNT; i++) {
catpt_writel_ssp(cdev, i, SSCR0, CATPT_SSC0_DEFAULT);
catpt_writel_ssp(cdev, i, SSCR1, CATPT_SSC1_DEFAULT);
catpt_writel_ssp(cdev, i, SSSR, CATPT_SSS_DEFAULT);
catpt_writel_ssp(cdev, i, SSITR, CATPT_SSIT_DEFAULT);
catpt_writel_ssp(cdev, i, SSDR, CATPT_SSD_DEFAULT);
catpt_writel_ssp(cdev, i, SSTO, CATPT_SSTO_DEFAULT);
catpt_writel_ssp(cdev, i, SSPSP, CATPT_SSPSP_DEFAULT);
catpt_writel_ssp(cdev, i, SSTSA, CATPT_SSTSA_DEFAULT);
catpt_writel_ssp(cdev, i, SSRSA, CATPT_SSRSA_DEFAULT);
catpt_writel_ssp(cdev, i, SSTSS, CATPT_SSTSS_DEFAULT);
catpt_writel_ssp(cdev, i, SSCR2, CATPT_SSCR2_DEFAULT);
catpt_writel_ssp(cdev, i, SSPSP2, CATPT_SSPSP2_DEFAULT);
}
}
int catpt_dsp_power_down(struct catpt_dev *cdev)
{
u32 mask, val;
/* disable core clock gating */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0);
catpt_dsp_reset(cdev, true);
/* set 24Mhz clock for both SSPs */
catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1),
CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1));
catpt_dsp_select_lpclock(cdev, true, false);
/* disable MCLK */
catpt_updatel_shim(cdev, CLKCTL, CATPT_CLKCTL_SMOS, 0);
catpt_dsp_set_regs_defaults(cdev);
/* switch clock gating */
mask = CATPT_VDRTCTL2_CGEALL & (~CATPT_VDRTCTL2_DCLCGE);
val = mask & (~CATPT_VDRTCTL2_DTCGE);
catpt_updatel_pci(cdev, VDRTCTL2, mask, val);
/* enable DTCGE separatelly */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DTCGE,
CATPT_VDRTCTL2_DTCGE);
/* SRAM power gating all */
catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask,
cdev->spec->dram_mask);
catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask,
cdev->spec->iram_mask);
mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit;
catpt_updatel_pci(cdev, VDRTCTL0, mask, cdev->spec->d3pgd_bit);
catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot);
/* give hw time to drop off */
udelay(50);
/* enable core clock gating */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE,
CATPT_VDRTCTL2_DCLCGE);
udelay(50);
return 0;
}
int catpt_dsp_power_up(struct catpt_dev *cdev)
{
u32 mask, val;
/* disable core clock gating */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0);
/* switch clock gating */
mask = CATPT_VDRTCTL2_CGEALL & (~CATPT_VDRTCTL2_DCLCGE);
val = mask & (~CATPT_VDRTCTL2_DTCGE);
catpt_updatel_pci(cdev, VDRTCTL2, mask, val);
catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0);
/* SRAM power gating none */
mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit;
catpt_updatel_pci(cdev, VDRTCTL0, mask, mask);
catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask, 0);
catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask, 0);
catpt_dsp_set_regs_defaults(cdev);
/* restore MCLK */
catpt_updatel_shim(cdev, CLKCTL, CATPT_CLKCTL_SMOS, CATPT_CLKCTL_SMOS);
catpt_dsp_select_lpclock(cdev, false, false);
/* set 24Mhz clock for both SSPs */
catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1),
CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1));
catpt_dsp_reset(cdev, false);
/* enable core clock gating */
catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE,
CATPT_VDRTCTL2_DCLCGE);
/* generate int deassert msg to fix inversed int logic */
catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB | CATPT_IMC_IPCCD, 0);
return 0;
}
#define CATPT_DUMP_MAGIC 0xcd42
#define CATPT_DUMP_SECTION_ID_FILE 0x00
#define CATPT_DUMP_SECTION_ID_IRAM 0x01
#define CATPT_DUMP_SECTION_ID_DRAM 0x02
#define CATPT_DUMP_SECTION_ID_REGS 0x03
#define CATPT_DUMP_HASH_SIZE 20
struct catpt_dump_section_hdr {
u16 magic;
u8 core_id;
u8 section_id;
u32 size;
};
int catpt_coredump(struct catpt_dev *cdev)
{
struct catpt_dump_section_hdr *hdr;
size_t dump_size, regs_size;
u8 *dump, *pos;
const char *eof;
char *info;
int i;
regs_size = CATPT_SHIM_REGS_SIZE;
regs_size += CATPT_DMA_COUNT * CATPT_DMA_REGS_SIZE;
regs_size += CATPT_SSP_COUNT * CATPT_SSP_REGS_SIZE;
dump_size = resource_size(&cdev->dram);
dump_size += resource_size(&cdev->iram);
dump_size += regs_size;
/* account for header of each section and hash chunk */
dump_size += 4 * sizeof(*hdr) + CATPT_DUMP_HASH_SIZE;
dump = vzalloc(dump_size);
if (!dump)
return -ENOMEM;
pos = dump;
hdr = (struct catpt_dump_section_hdr *)pos;
hdr->magic = CATPT_DUMP_MAGIC;
hdr->core_id = cdev->spec->core_id;
hdr->section_id = CATPT_DUMP_SECTION_ID_FILE;
hdr->size = dump_size - sizeof(*hdr);
pos += sizeof(*hdr);
info = cdev->ipc.config.fw_info;
eof = info + FW_INFO_SIZE_MAX;
/* navigate to fifth info segment (fw hash) */
for (i = 0; i < 4 && info < eof; i++, info++) {
/* info segments are separated by space each */
info = strnchr(info, eof - info, ' ');
if (!info)
break;
}
if (i == 4 && info)
memcpy(pos, info, min_t(u32, eof - info, CATPT_DUMP_HASH_SIZE));
pos += CATPT_DUMP_HASH_SIZE;
hdr = (struct catpt_dump_section_hdr *)pos;
hdr->magic = CATPT_DUMP_MAGIC;
hdr->core_id = cdev->spec->core_id;
hdr->section_id = CATPT_DUMP_SECTION_ID_IRAM;
hdr->size = resource_size(&cdev->iram);
pos += sizeof(*hdr);
memcpy_fromio(pos, catpt_iram_addr(cdev), hdr->size);
pos += hdr->size;
hdr = (struct catpt_dump_section_hdr *)pos;
hdr->magic = CATPT_DUMP_MAGIC;
hdr->core_id = cdev->spec->core_id;
hdr->section_id = CATPT_DUMP_SECTION_ID_DRAM;
hdr->size = resource_size(&cdev->dram);
pos += sizeof(*hdr);
memcpy_fromio(pos, catpt_dram_addr(cdev), hdr->size);
pos += hdr->size;
hdr = (struct catpt_dump_section_hdr *)pos;
hdr->magic = CATPT_DUMP_MAGIC;
hdr->core_id = cdev->spec->core_id;
hdr->section_id = CATPT_DUMP_SECTION_ID_REGS;
hdr->size = regs_size;
pos += sizeof(*hdr);
memcpy_fromio(pos, catpt_shim_addr(cdev), CATPT_SHIM_REGS_SIZE);
pos += CATPT_SHIM_REGS_SIZE;
for (i = 0; i < CATPT_SSP_COUNT; i++) {
memcpy_fromio(pos, catpt_ssp_addr(cdev, i),
CATPT_SSP_REGS_SIZE);
pos += CATPT_SSP_REGS_SIZE;
}
for (i = 0; i < CATPT_DMA_COUNT; i++) {
memcpy_fromio(pos, catpt_dma_addr(cdev, i),
CATPT_DMA_REGS_SIZE);
pos += CATPT_DMA_REGS_SIZE;
}
dev_coredumpv(cdev->dev, dump, dump_size, GFP_KERNEL);
return 0;
}
+306
View File
@@ -0,0 +1,306 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/cleanup.h>
#include <linux/irqreturn.h>
#include "core.h"
#include "messages.h"
#include "registers.h"
#include "trace.h"
#define CATPT_IPC_TIMEOUT_MS 300
void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev)
{
ipc->dev = dev;
ipc->ready = false;
ipc->default_timeout = CATPT_IPC_TIMEOUT_MS;
init_completion(&ipc->done_completion);
init_completion(&ipc->busy_completion);
spin_lock_init(&ipc->lock);
mutex_init(&ipc->mutex);
}
static int catpt_ipc_arm(struct catpt_ipc *ipc, struct catpt_fw_ready *config)
{
/*
* Both tx and rx are put into and received from outbox. Inbox is
* only used for notifications where payload size is known upfront,
* thus no separate buffer is allocated for it.
*/
ipc->rx.data = devm_kzalloc(ipc->dev, config->outbox_size, GFP_KERNEL);
if (!ipc->rx.data)
return -ENOMEM;
memcpy(&ipc->config, config, sizeof(*config));
ipc->ready = true;
return 0;
}
static void catpt_ipc_msg_init(struct catpt_ipc *ipc,
struct catpt_ipc_msg *reply)
{
lockdep_assert_held(&ipc->lock);
ipc->rx.header = 0;
ipc->rx.size = reply ? reply->size : 0;
reinit_completion(&ipc->done_completion);
reinit_completion(&ipc->busy_completion);
}
static void catpt_dsp_send_tx(struct catpt_dev *cdev,
const struct catpt_ipc_msg *tx)
{
u32 header = tx->header | CATPT_IPCC_BUSY;
trace_catpt_ipc_request(header);
trace_catpt_ipc_payload(tx->data, tx->size);
memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size);
catpt_writel_shim(cdev, IPCC, header);
}
static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
{
struct catpt_ipc *ipc = &cdev->ipc;
int ret;
ret = wait_for_completion_timeout(&ipc->done_completion,
msecs_to_jiffies(timeout));
if (!ret)
return -ETIMEDOUT;
if (ipc->rx.rsp.status != CATPT_REPLY_PENDING)
return 0;
/* wait for delayed reply */
ret = wait_for_completion_timeout(&ipc->busy_completion,
msecs_to_jiffies(timeout));
return ret ? 0 : -ETIMEDOUT;
}
static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
struct catpt_ipc_msg *reply, int timeout, const char *name)
{
struct catpt_ipc *ipc = &cdev->ipc;
unsigned long flags;
int ret;
if (!ipc->ready)
return -EPERM;
if (request.size > ipc->config.outbox_size ||
(reply && reply->size > ipc->config.outbox_size))
return -EINVAL;
spin_lock_irqsave(&ipc->lock, flags);
catpt_ipc_msg_init(ipc, reply);
catpt_dsp_send_tx(cdev, &request);
spin_unlock_irqrestore(&ipc->lock, flags);
ret = catpt_wait_msg_completion(cdev, timeout);
if (ret) {
dev_crit(cdev->dev, "communication severed: %d, rebooting dsp..\n",
ret);
ipc->ready = false;
/* TODO: attempt recovery */
return ret;
}
ret = ipc->rx.rsp.status;
if (ret)
dev_err(cdev->dev, "%s (0x%08x) failed: %d\n", name, request.header, ret);
if (reply) {
reply->header = ipc->rx.header;
if (!ret && reply->data)
memcpy(reply->data, ipc->rx.data, reply->size);
}
return ret;
}
int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
struct catpt_ipc_msg request,
struct catpt_ipc_msg *reply, int timeout, const char *name)
{
guard(mutex)(&cdev->ipc.mutex);
return catpt_dsp_do_send_msg(cdev, request, reply, timeout, name);
}
int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
struct catpt_ipc_msg *reply, const char *name)
{
return catpt_dsp_send_msg_timeout(cdev, request, reply,
cdev->ipc.default_timeout, name);
}
static void
catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg)
{
struct catpt_stream_runtime *stream;
struct catpt_notify_position pos;
struct catpt_notify_glitch glitch;
guard(mutex)(&cdev->stream_mutex);
stream = catpt_stream_find(cdev, msg.stream_hw_id);
if (!stream) {
dev_warn(cdev->dev, "notify %d for non-existent stream %d\n",
msg.notify_reason, msg.stream_hw_id);
return;
}
switch (msg.notify_reason) {
case CATPT_NOTIFY_POSITION_CHANGED:
memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos));
trace_catpt_ipc_payload((u8 *)&pos, sizeof(pos));
catpt_stream_update_position(cdev, stream, &pos);
break;
case CATPT_NOTIFY_GLITCH_OCCURRED:
memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch));
trace_catpt_ipc_payload((u8 *)&glitch, sizeof(glitch));
dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n",
glitch.type, glitch.presentation_pos,
glitch.write_pos);
break;
default:
dev_warn(cdev->dev, "unknown notification: %d received\n",
msg.notify_reason);
break;
}
}
static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header)
{
struct catpt_ipc *ipc = &cdev->ipc;
ipc->rx.header = header;
if (ipc->rx.rsp.status != CATPT_REPLY_SUCCESS)
return;
memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size);
trace_catpt_ipc_payload(ipc->rx.data, ipc->rx.size);
}
static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header)
{
union catpt_notify_msg msg = CATPT_MSG(header);
struct catpt_ipc *ipc = &cdev->ipc;
if (msg.fw_ready) {
struct catpt_fw_ready config;
/* to fit 32b header original address is shifted right by 3 */
u32 off = msg.mailbox_address << 3;
memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config));
trace_catpt_ipc_payload((u8 *)&config, sizeof(config));
dev_dbg(cdev->dev, "FW READY 0x%08x\n", header);
catpt_ipc_arm(ipc, &config);
complete(&cdev->fw_ready);
return;
}
switch (msg.global_msg_type) {
case CATPT_GLB_REQUEST_CORE_DUMP:
dev_err(cdev->dev, "ADSP device coredump received\n");
ipc->ready = false;
catpt_coredump(cdev);
if (catpt_readl_dram(cdev, COREDUMP) == CATPT_COREDUMP_REQUEST) {
dev_dbg(cdev->dev, "releasing firmware from the coredump state\n");
catpt_writel_dram(cdev, COREDUMP, CATPT_COREDUMP_RELEASE);
}
complete(&cdev->fw_ready);
/* TODO: attempt recovery */
break;
case CATPT_GLB_STREAM_MESSAGE:
switch (msg.stream_msg_type) {
case CATPT_STRM_NOTIFICATION:
catpt_dsp_notify_stream(cdev, msg);
break;
default:
catpt_dsp_copy_rx(cdev, header);
/* signal completion of delayed reply */
complete(&ipc->busy_completion);
break;
}
break;
default:
dev_warn(cdev->dev, "unknown response: %d received\n",
msg.global_msg_type);
break;
}
}
irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id)
{
struct catpt_dev *cdev = dev_id;
u32 ipcd;
ipcd = catpt_readl_shim(cdev, IPCD);
trace_catpt_ipc_notify(ipcd);
/* ensure there is delayed reply or notification to process */
if (!(ipcd & CATPT_IPCD_BUSY))
return IRQ_NONE;
catpt_dsp_process_response(cdev, ipcd);
/* tell DSP processing is completed */
catpt_updatel_shim(cdev, IPCD, CATPT_IPCD_BUSY | CATPT_IPCD_DONE,
CATPT_IPCD_DONE);
/* unmask dsp BUSY interrupt */
catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, 0);
return IRQ_HANDLED;
}
irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id)
{
struct catpt_dev *cdev = dev_id;
irqreturn_t ret = IRQ_NONE;
u32 isc, ipcc;
isc = catpt_readl_shim(cdev, ISC);
trace_catpt_irq(isc);
/* immediate reply */
if (isc & CATPT_ISC_IPCCD) {
/* mask host DONE interrupt */
catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD);
ipcc = catpt_readl_shim(cdev, IPCC);
trace_catpt_ipc_reply(ipcc);
catpt_dsp_copy_rx(cdev, ipcc);
complete(&cdev->ipc.done_completion);
/* tell DSP processing is completed */
catpt_updatel_shim(cdev, IPCC, CATPT_IPCC_DONE, 0);
/* unmask host DONE interrupt */
catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, 0);
ret = IRQ_HANDLED;
}
/* delayed reply or notification */
if (isc & CATPT_ISC_IPCDB) {
/* mask dsp BUSY interrupt */
catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, CATPT_IMC_IPCDB);
ret = IRQ_WAKE_THREAD;
}
return ret;
}
+667
View File
@@ -0,0 +1,667 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include "core.h"
#include "registers.h"
/* FW load (200ms) plus operational delays */
#define FW_READY_TIMEOUT_MS 250
#define FW_SIGNATURE "$SST"
#define FW_SIGNATURE_SIZE 4
struct catpt_fw_hdr {
char signature[FW_SIGNATURE_SIZE];
u32 file_size;
u32 modules;
u32 file_format;
u32 reserved[4];
} __packed;
struct catpt_fw_mod_hdr {
char signature[FW_SIGNATURE_SIZE];
u32 mod_size;
u32 blocks;
u16 slot;
u16 module_id;
u32 entry_point;
u32 persistent_size;
u32 scratch_size;
} __packed;
enum catpt_ram_type {
CATPT_RAM_TYPE_IRAM = 1,
CATPT_RAM_TYPE_DRAM = 2,
/* DRAM with module's initial state */
CATPT_RAM_TYPE_INSTANCE = 3,
};
struct catpt_fw_block_hdr {
u32 ram_type;
u32 size;
u32 ram_offset;
u32 rsvd;
} __packed;
void catpt_sram_free(struct resource *sram)
{
struct resource *res, *save;
for (res = sram->child; res;) {
save = res->sibling;
release_resource(res);
kfree(res);
res = save;
}
}
struct resource *
catpt_request_region(struct resource *root, resource_size_t size)
{
struct resource *res = root->child;
resource_size_t addr = root->start;
for (;;) {
if (res->start - addr >= size)
break;
addr = res->end + 1;
res = res->sibling;
if (!res)
return NULL;
}
return __request_region(root, addr, size, NULL, 0);
}
int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
{
struct catpt_stream_runtime *stream;
/* Lockless as no streams can be added or removed during D3 -> D0 transition. */
list_for_each_entry(stream, &cdev->stream_list, node) {
u32 off, size;
int ret;
off = stream->persistent->start;
size = resource_size(stream->persistent);
dev_dbg(cdev->dev, "storing stream %d ctx: off 0x%08x size %d\n",
stream->info.stream_hw_id, off, size);
ret = catpt_dma_memcpy_fromdsp(cdev, chan,
cdev->dxbuf_paddr + off,
cdev->lpe_base + off,
ALIGN(size, 4));
if (ret) {
dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
return ret;
}
}
return 0;
}
int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan)
{
int i;
for (i = 0; i < ARRAY_SIZE(cdev->modules); i++) {
struct catpt_module_type *type;
u32 off;
int ret;
type = &cdev->modules[i];
if (!type->loaded || !type->state_size)
continue;
off = type->state_offset;
dev_dbg(cdev->dev, "storing mod %d state: off 0x%08x size %d\n",
i, off, type->state_size);
ret = catpt_dma_memcpy_fromdsp(cdev, chan,
cdev->dxbuf_paddr + off,
cdev->lpe_base + off,
ALIGN(type->state_size, 4));
if (ret) {
dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
return ret;
}
}
return 0;
}
int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
{
int i;
for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
struct catpt_save_meminfo *info;
u32 off;
int ret;
info = &cdev->dx_ctx.meminfo[i];
if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
continue;
off = catpt_to_host_offset(info->offset);
if (off < cdev->dram.start || off > cdev->dram.end)
continue;
dev_dbg(cdev->dev, "storing memdump: off 0x%08x size %d\n",
off, info->size);
ret = catpt_dma_memcpy_fromdsp(cdev, chan,
cdev->dxbuf_paddr + off,
cdev->lpe_base + off,
ALIGN(info->size, 4));
if (ret) {
dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
return ret;
}
}
return 0;
}
static int
catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
{
struct catpt_stream_runtime *stream;
/* Lockless as no streams can be added or removed during D3 -> D0 transition. */
list_for_each_entry(stream, &cdev->stream_list, node) {
u32 off, size;
int ret;
off = stream->persistent->start;
size = resource_size(stream->persistent);
dev_dbg(cdev->dev, "restoring stream %d ctx: off 0x%08x size %d\n",
stream->info.stream_hw_id, off, size);
ret = catpt_dma_memcpy_todsp(cdev, chan,
cdev->lpe_base + off,
cdev->dxbuf_paddr + off,
ALIGN(size, 4));
if (ret) {
dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
return ret;
}
}
return 0;
}
static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
{
int i;
for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
struct catpt_save_meminfo *info;
struct resource r = {};
u32 off;
int ret;
info = &cdev->dx_ctx.meminfo[i];
if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
continue;
off = catpt_to_host_offset(info->offset);
resource_set_range(&r, off, info->size);
if (!resource_contains(&cdev->dram, &r))
continue;
dev_dbg(cdev->dev, "restoring memdump: off 0x%08x size %d\n",
off, info->size);
ret = catpt_dma_memcpy_todsp(cdev, chan,
cdev->lpe_base + off,
cdev->dxbuf_paddr + off,
ALIGN(info->size, 4));
if (ret) {
dev_err(cdev->dev, "restore block failed: %d\n", ret);
return ret;
}
}
return 0;
}
static int catpt_restore_fwimage(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_block_hdr *blk)
{
struct resource r1 = {};
int i;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
blk, sizeof(*blk), false);
resource_set_range(&r1, cdev->dram.start + blk->ram_offset, blk->size);
/* advance to data area */
paddr += sizeof(*blk);
for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
struct catpt_save_meminfo *info;
struct resource common = {};
struct resource r2 = {};
u32 off;
int ret;
info = &cdev->dx_ctx.meminfo[i];
if (info->source != CATPT_DX_TYPE_FW_IMAGE)
continue;
off = catpt_to_host_offset(info->offset);
resource_set_range(&r2, off, info->size);
if (!resource_contains(&cdev->dram, &r2))
continue;
if (!resource_intersection(&r2, &r1, &common))
continue;
/* calculate start offset of common data area */
off = common.start - r1.start;
dev_dbg(cdev->dev, "restoring fwimage: %pr\n", &common);
ret = catpt_dma_memcpy_todsp(cdev, chan, common.start,
paddr + off,
resource_size(&common));
if (ret) {
dev_err(cdev->dev, "memcpy todsp failed: %d\n", ret);
return ret;
}
}
return 0;
}
static int catpt_load_block(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_block_hdr *blk, bool alloc)
{
struct resource *sram, *res;
dma_addr_t dst_addr;
int ret;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
blk, sizeof(*blk), false);
switch (blk->ram_type) {
case CATPT_RAM_TYPE_IRAM:
sram = &cdev->iram;
break;
default:
sram = &cdev->dram;
break;
}
dst_addr = sram->start + blk->ram_offset;
if (alloc) {
res = __request_region(sram, dst_addr, blk->size, NULL, 0);
if (!res)
return -EBUSY;
}
/* advance to data area */
paddr += sizeof(*blk);
ret = catpt_dma_memcpy_todsp(cdev, chan, dst_addr, paddr, blk->size);
if (ret) {
dev_err(cdev->dev, "memcpy error: %d\n", ret);
__release_region(sram, dst_addr, blk->size);
}
return ret;
}
static int catpt_restore_basefw(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_mod_hdr *basefw)
{
u32 offset = sizeof(*basefw);
int ret, i;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
basefw, sizeof(*basefw), false);
/* restore basefw image */
for (i = 0; i < basefw->blocks; i++) {
struct catpt_fw_block_hdr *blk;
blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset);
switch (blk->ram_type) {
case CATPT_RAM_TYPE_IRAM:
ret = catpt_load_block(cdev, chan, paddr + offset,
blk, false);
break;
default:
ret = catpt_restore_fwimage(cdev, chan, paddr + offset,
blk);
break;
}
if (ret) {
dev_err(cdev->dev, "restore block failed: %d\n", ret);
return ret;
}
offset += sizeof(*blk) + blk->size;
}
/* then proceed with memory dumps */
ret = catpt_restore_memdumps(cdev, chan);
if (ret)
dev_err(cdev->dev, "restore memdumps failed: %d\n", ret);
return ret;
}
static int catpt_restore_module(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_mod_hdr *mod)
{
u32 offset = sizeof(*mod);
int i;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
mod, sizeof(*mod), false);
for (i = 0; i < mod->blocks; i++) {
struct catpt_fw_block_hdr *blk;
int ret;
blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
switch (blk->ram_type) {
case CATPT_RAM_TYPE_INSTANCE:
/* restore module state */
ret = catpt_dma_memcpy_todsp(cdev, chan,
cdev->lpe_base + blk->ram_offset,
cdev->dxbuf_paddr + blk->ram_offset,
ALIGN(blk->size, 4));
break;
default:
ret = catpt_load_block(cdev, chan, paddr + offset,
blk, false);
break;
}
if (ret) {
dev_err(cdev->dev, "restore block failed: %d\n", ret);
return ret;
}
offset += sizeof(*blk) + blk->size;
}
return 0;
}
static int catpt_load_module(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_mod_hdr *mod)
{
struct catpt_module_type *type;
u32 offset = sizeof(*mod);
int i;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
mod, sizeof(*mod), false);
type = &cdev->modules[mod->module_id];
for (i = 0; i < mod->blocks; i++) {
struct catpt_fw_block_hdr *blk;
int ret;
blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
ret = catpt_load_block(cdev, chan, paddr + offset, blk, true);
if (ret) {
dev_err(cdev->dev, "load block failed: %d\n", ret);
return ret;
}
/*
* Save state window coordinates - these will be
* used to capture module state on D0 exit.
*/
if (blk->ram_type == CATPT_RAM_TYPE_INSTANCE) {
type->state_offset = blk->ram_offset;
type->state_size = blk->size;
}
offset += sizeof(*blk) + blk->size;
}
/* init module type static info */
type->loaded = true;
/* DSP expects address from module header substracted by 4 */
type->entry_point = mod->entry_point - 4;
type->persistent_size = mod->persistent_size;
type->scratch_size = mod->scratch_size;
return 0;
}
static int catpt_restore_firmware(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_hdr *fw)
{
u32 offset = sizeof(*fw);
int i;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
fw, sizeof(*fw), false);
for (i = 0; i < fw->modules; i++) {
struct catpt_fw_mod_hdr *mod;
int ret;
mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
if (strncmp(fw->signature, mod->signature,
FW_SIGNATURE_SIZE)) {
dev_err(cdev->dev, "module signature mismatch\n");
return -EINVAL;
}
if (mod->module_id > CATPT_MODID_LAST)
return -EINVAL;
switch (mod->module_id) {
case CATPT_MODID_BASE_FW:
ret = catpt_restore_basefw(cdev, chan, paddr + offset,
mod);
break;
default:
ret = catpt_restore_module(cdev, chan, paddr + offset,
mod);
break;
}
if (ret) {
dev_err(cdev->dev, "restore module failed: %d\n", ret);
return ret;
}
offset += sizeof(*mod) + mod->mod_size;
}
return 0;
}
static int catpt_load_firmware(struct catpt_dev *cdev,
struct dma_chan *chan, dma_addr_t paddr,
struct catpt_fw_hdr *fw)
{
u32 offset = sizeof(*fw);
int i;
print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
fw, sizeof(*fw), false);
for (i = 0; i < fw->modules; i++) {
struct catpt_fw_mod_hdr *mod;
int ret;
mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
if (strncmp(fw->signature, mod->signature,
FW_SIGNATURE_SIZE)) {
dev_err(cdev->dev, "module signature mismatch\n");
return -EINVAL;
}
if (mod->module_id > CATPT_MODID_LAST)
return -EINVAL;
ret = catpt_load_module(cdev, chan, paddr + offset, mod);
if (ret) {
dev_err(cdev->dev, "load module failed: %d\n", ret);
return ret;
}
offset += sizeof(*mod) + mod->mod_size;
}
return 0;
}
static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan,
const char *name, const char *signature,
bool restore)
{
struct catpt_fw_hdr *fw;
struct firmware *img;
dma_addr_t paddr;
void *vaddr;
int ret;
ret = request_firmware((const struct firmware **)&img, name, cdev->dev);
if (ret)
return ret;
fw = (struct catpt_fw_hdr *)img->data;
if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) {
dev_err(cdev->dev, "firmware signature mismatch\n");
ret = -EINVAL;
goto release_fw;
}
vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL);
if (!vaddr) {
ret = -ENOMEM;
goto release_fw;
}
memcpy(vaddr, img->data, img->size);
fw = (struct catpt_fw_hdr *)vaddr;
if (restore)
ret = catpt_restore_firmware(cdev, chan, paddr, fw);
else
ret = catpt_load_firmware(cdev, chan, paddr, fw);
dma_free_coherent(cdev->dev, img->size, vaddr, paddr);
release_fw:
release_firmware(img);
return ret;
}
static int catpt_load_images(struct catpt_dev *cdev, bool restore)
{
struct dma_chan *chan;
int ret;
chan = catpt_dma_request_config_chan(cdev);
if (IS_ERR(chan))
return PTR_ERR(chan);
ret = catpt_load_image(cdev, chan, cdev->spec->fw_name,
FW_SIGNATURE, restore);
if (ret)
goto release_dma_chan;
if (!restore)
goto release_dma_chan;
ret = catpt_restore_streams_context(cdev, chan);
if (ret)
dev_err(cdev->dev, "restore streams ctx failed: %d\n", ret);
release_dma_chan:
dma_release_channel(chan);
return ret;
}
int catpt_boot_firmware(struct catpt_dev *cdev, bool restore)
{
int ret;
catpt_dsp_stall(cdev, true);
ret = catpt_load_images(cdev, restore);
if (ret) {
dev_err(cdev->dev, "load binaries failed: %d\n", ret);
return ret;
}
reinit_completion(&cdev->fw_ready);
catpt_dsp_stall(cdev, false);
ret = wait_for_completion_timeout(&cdev->fw_ready,
msecs_to_jiffies(FW_READY_TIMEOUT_MS));
if (!ret) {
dev_err(cdev->dev, "firmware ready timeout\n");
return -ETIMEDOUT;
/* Wake up does not mean FW is ready, an exception could occur. */
} else if (!cdev->ipc.ready) {
return -EREMOTEIO;
}
/* update sram pg & clock once done booting */
catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
catpt_dsp_update_srampge(cdev, &cdev->iram, cdev->spec->iram_mask);
return catpt_dsp_update_lpclock(cdev);
}
int catpt_first_boot_firmware(struct catpt_dev *cdev)
{
struct resource *res;
int ret;
ret = catpt_boot_firmware(cdev, false);
if (ret) {
dev_err(cdev->dev, "basefw boot failed: %d\n", ret);
return ret;
}
/* restrict FW Core dump area */
__request_region(&cdev->dram, 0, 0x200, NULL, 0);
/* restrict entire area following BASE_FW - highest offset in DRAM */
for (res = cdev->dram.child; res->sibling; res = res->sibling)
;
__request_region(&cdev->dram, res->end + 1,
cdev->dram.end - res->end, NULL, 0);
ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
if (ret)
return CATPT_IPC_RET(ret);
ret = catpt_arm_stream_templates(cdev);
if (ret) {
dev_err(cdev->dev, "arm templates failed: %d\n", ret);
return ret;
}
/* update dram pg for scratch and restricted regions */
catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
return 0;
}
@@ -0,0 +1,248 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/slab.h>
#include "core.h"
#include "messages.h"
#include "registers.h"
int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
struct catpt_fw_version *version)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_FW_VERSION);
struct catpt_ipc_msg request = {{0}}, reply;
request.header = msg.val;
reply.size = sizeof(*version);
reply.data = version;
return catpt_dsp_send_msg(cdev, request, &reply, "get fw version");
}
struct catpt_alloc_stream_input {
enum catpt_path_id path_id:8;
enum catpt_stream_type stream_type:8;
enum catpt_format_id format_id:8;
u8 reserved;
struct catpt_audio_format input_format;
struct catpt_ring_info ring_info;
u8 num_entries;
/* flex array with entries here */
struct catpt_memory_info persistent_mem;
struct catpt_memory_info scratch_mem;
u32 num_notifications; /* obsolete */
} __packed;
int catpt_ipc_alloc_stream(struct catpt_dev *cdev,
enum catpt_path_id path_id,
enum catpt_stream_type type,
struct catpt_audio_format *afmt,
struct catpt_ring_info *rinfo,
u8 num_modules,
struct catpt_module_entry *modules,
struct resource *persistent,
struct resource *scratch,
struct catpt_stream_info *sinfo)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(ALLOCATE_STREAM);
struct catpt_alloc_stream_input input;
struct catpt_ipc_msg request, reply;
size_t size, arrsz;
u8 *payload;
off_t off;
int ret;
off = offsetof(struct catpt_alloc_stream_input, persistent_mem);
arrsz = sizeof(*modules) * num_modules;
size = sizeof(input) + arrsz;
payload = kzalloc(size, GFP_KERNEL);
if (!payload)
return -ENOMEM;
memset(&input, 0, sizeof(input));
input.path_id = path_id;
input.stream_type = type;
input.format_id = CATPT_FORMAT_PCM;
input.input_format = *afmt;
input.ring_info = *rinfo;
input.num_entries = num_modules;
input.persistent_mem.offset = catpt_to_dsp_offset(persistent->start);
input.persistent_mem.size = resource_size(persistent);
if (scratch) {
input.scratch_mem.offset = catpt_to_dsp_offset(scratch->start);
input.scratch_mem.size = resource_size(scratch);
}
/* re-arrange the input: account for flex array 'entries' */
memcpy(payload, &input, sizeof(input));
memmove(payload + off + arrsz, payload + off, sizeof(input) - off);
memcpy(payload + off, modules, arrsz);
request.header = msg.val;
request.size = size;
request.data = payload;
reply.size = sizeof(*sinfo);
reply.data = sinfo;
ret = catpt_dsp_send_msg(cdev, request, &reply, "alloc stream");
kfree(payload);
return ret;
}
int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(FREE_STREAM);
struct catpt_ipc_msg request;
request.header = msg.val;
request.size = sizeof(stream_hw_id);
request.data = &stream_hw_id;
return catpt_dsp_send_msg(cdev, request, NULL, "free stream");
}
int catpt_ipc_set_device_format(struct catpt_dev *cdev,
struct catpt_ssp_device_format *devfmt)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(SET_DEVICE_FORMATS);
struct catpt_ipc_msg request;
request.header = msg.val;
request.size = sizeof(*devfmt);
request.data = devfmt;
return catpt_dsp_send_msg(cdev, request, NULL, "set device format");
}
int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
struct catpt_dx_context *context)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(ENTER_DX_STATE);
struct catpt_ipc_msg request, reply;
request.header = msg.val;
request.size = sizeof(state);
request.data = &state;
reply.size = sizeof(*context);
reply.data = context;
return catpt_dsp_send_msg(cdev, request, &reply, "enter dx state");
}
int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
struct catpt_mixer_stream_info *info)
{
union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_MIXER_STREAM_INFO);
struct catpt_ipc_msg request = {{0}}, reply;
request.header = msg.val;
reply.size = sizeof(*info);
reply.data = info;
return catpt_dsp_send_msg(cdev, request, &reply, "get mixer info");
}
int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(RESET_STREAM);
struct catpt_ipc_msg request = {{0}};
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
return catpt_dsp_send_msg(cdev, request, NULL, "reset stream");
}
int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(PAUSE_STREAM);
struct catpt_ipc_msg request = {{0}};
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
return catpt_dsp_send_msg(cdev, request, NULL, "pause stream");
}
int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id)
{
union catpt_stream_msg msg = CATPT_STREAM_MSG(RESUME_STREAM);
struct catpt_ipc_msg request = {{0}};
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
return catpt_dsp_send_msg(cdev, request, NULL, "resume stream");
}
struct catpt_set_volume_input {
u32 channel;
u32 target_volume;
u64 curve_duration;
u32 curve_type;
} __packed;
int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
u32 channel, u32 volume,
u32 curve_duration,
enum catpt_audio_curve_type curve_type)
{
union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_VOLUME);
struct catpt_ipc_msg request;
struct catpt_set_volume_input input;
msg.stream_hw_id = stream_hw_id;
input.channel = channel;
input.target_volume = volume;
input.curve_duration = curve_duration;
input.curve_type = curve_type;
request.header = msg.val;
request.size = sizeof(input);
request.data = &input;
return catpt_dsp_send_msg(cdev, request, NULL, "set stream volume");
}
struct catpt_set_write_pos_input {
u32 new_write_pos;
bool end_of_buffer;
bool low_latency;
} __packed;
int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
u32 pos, bool eob, bool ll)
{
union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_WRITE_POSITION);
struct catpt_ipc_msg request;
struct catpt_set_write_pos_input input;
msg.stream_hw_id = stream_hw_id;
input.new_write_pos = pos;
input.end_of_buffer = eob;
input.low_latency = ll;
request.header = msg.val;
request.size = sizeof(input);
request.data = &input;
return catpt_dsp_send_msg(cdev, request, NULL, "set stream write pos");
}
int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute)
{
union catpt_stream_msg msg = CATPT_STAGE_MSG(MUTE_LOOPBACK);
struct catpt_ipc_msg request;
msg.stream_hw_id = stream_hw_id;
request.header = msg.val;
request.size = sizeof(mute);
request.data = &mute;
return catpt_dsp_send_msg(cdev, request, NULL, "mute loopback");
}
@@ -0,0 +1,402 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2020 Intel Corporation
*
* Author: Cezary Rojewski <cezary.rojewski@intel.com>
*/
#ifndef __SND_SOC_INTEL_CATPT_MSG_H
#define __SND_SOC_INTEL_CATPT_MSG_H
struct catpt_dev;
/* IPC messages base types */
enum catpt_reply_status {
CATPT_REPLY_SUCCESS = 0,
CATPT_REPLY_ERROR_INVALID_PARAM = 1,
CATPT_REPLY_UNKNOWN_MESSAGE_TYPE = 2,
CATPT_REPLY_OUT_OF_RESOURCES = 3,
CATPT_REPLY_BUSY = 4,
CATPT_REPLY_PENDING = 5,
CATPT_REPLY_FAILURE = 6,
CATPT_REPLY_INVALID_REQUEST = 7,
CATPT_REPLY_UNINITIALIZED = 8,
CATPT_REPLY_NOT_FOUND = 9,
CATPT_REPLY_SOURCE_NOT_STARTED = 10,
};
/* GLOBAL messages */
enum catpt_global_msg_type {
CATPT_GLB_GET_FW_VERSION = 0,
CATPT_GLB_ALLOCATE_STREAM = 3,
CATPT_GLB_FREE_STREAM = 4,
CATPT_GLB_STREAM_MESSAGE = 6,
CATPT_GLB_REQUEST_CORE_DUMP = 7,
CATPT_GLB_SET_DEVICE_FORMATS = 10,
CATPT_GLB_ENTER_DX_STATE = 12,
CATPT_GLB_GET_MIXER_STREAM_INFO = 13,
};
union catpt_global_msg {
u32 val;
struct {
u32 status:5;
u32 context:19; /* stream or module specific */
u32 global_msg_type:5;
u32 fw_ready:1;
u32 done:1;
u32 busy:1;
};
} __packed;
#define CATPT_MSG(hdr) { .val = hdr }
#define CATPT_GLOBAL_MSG(msg_type) \
{ .global_msg_type = CATPT_GLB_##msg_type }
#define BUILD_HASH_SIZE 40
struct catpt_fw_version {
u8 build;
u8 minor;
u8 major;
u8 type;
u8 build_hash[BUILD_HASH_SIZE];
u32 log_providers_hash;
} __packed;
int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
struct catpt_fw_version *version);
/* PIN_IDs represent both, individual streams and the general mixer. */
enum catpt_pin_id {
CATPT_PIN_ID_SYSTEM = 0,
CATPT_PIN_ID_REFERENCE = 1,
CATPT_PIN_ID_CAPTURE1 = 2,
CATPT_PIN_ID_CAPTURE2 = 3,
CATPT_PIN_ID_OFFLOAD1 = 4,
CATPT_PIN_ID_OFFLOAD2 = 5,
CATPT_PIN_ID_MIXER = 7,
CATPT_PIN_ID_BLUETOOTH_CAPTURE = 8,
CATPT_PIN_ID_BLUETOOTH_RENDER = 9,
/* 10 is reserved */
CATPT_PIN_ID_INVALID = 11,
};
enum catpt_path_id {
CATPT_PATH_SSP0_OUT = 0,
CATPT_PATH_SSP0_IN = 1,
CATPT_PATH_SSP1_OUT = 2,
CATPT_PATH_SSP1_IN = 3,
/* duplicated audio in capture path */
CATPT_PATH_SSP0_IN_DUP = 4,
};
enum catpt_stream_type {
CATPT_STRM_TYPE_RENDER = 0, /* offload */
CATPT_STRM_TYPE_SYSTEM = 1,
CATPT_STRM_TYPE_CAPTURE = 2,
CATPT_STRM_TYPE_LOOPBACK = 3,
CATPT_STRM_TYPE_BLUETOOTH_RENDER = 4,
CATPT_STRM_TYPE_BLUETOOTH_CAPTURE = 5,
};
enum catpt_format_id {
CATPT_FORMAT_PCM = 0,
CATPT_FORMAT_MP3 = 1,
CATPT_FORMAT_AAC = 2,
CATPT_FORMAT_WMA = 3,
};
enum catpt_channel_index {
CATPT_CHANNEL_LEFT = 0x0,
CATPT_CHANNEL_CENTER = 0x1,
CATPT_CHANNEL_RIGHT = 0x2,
CATPT_CHANNEL_LEFT_SURROUND = 0x3,
CATPT_CHANNEL_CENTER_SURROUND = 0x3,
CATPT_CHANNEL_RIGHT_SURROUND = 0x4,
CATPT_CHANNEL_LFE = 0x7,
CATPT_CHANNEL_INVALID = 0xF,
};
enum catpt_channel_config {
CATPT_CHANNEL_CONFIG_MONO = 0, /* One channel only */
CATPT_CHANNEL_CONFIG_STEREO = 1, /* L & R */
CATPT_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only */
CATPT_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only */
CATPT_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only */
CATPT_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only */
CATPT_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only */
CATPT_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs */
CATPT_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE */
CATPT_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two */
CATPT_CHANNEL_CONFIG_INVALID = 10,
};
enum catpt_interleaving_style {
CATPT_INTERLEAVING_PER_CHANNEL = 0,
CATPT_INTERLEAVING_PER_SAMPLE = 1,
};
struct catpt_audio_format {
u32 sample_rate;
u32 bit_depth;
u32 channel_map;
u32 channel_config;
u32 interleaving;
u8 num_channels;
u8 valid_bit_depth;
u8 reserved[2];
} __packed;
struct catpt_ring_info {
u32 page_table_addr;
u32 num_pages;
u32 size;
u32 offset;
u32 ring_first_page_pfn;
} __packed;
#define CATPT_MODULE_COUNT (CATPT_MODID_LAST + 1)
enum catpt_module_id {
CATPT_MODID_BASE_FW = 0x0,
CATPT_MODID_MP3 = 0x1,
CATPT_MODID_AAC_5_1 = 0x2,
CATPT_MODID_AAC_2_0 = 0x3,
CATPT_MODID_SRC = 0x4,
CATPT_MODID_WAVES = 0x5,
CATPT_MODID_DOLBY = 0x6,
CATPT_MODID_BOOST = 0x7,
CATPT_MODID_LPAL = 0x8,
CATPT_MODID_DTS = 0x9,
CATPT_MODID_PCM_CAPTURE = 0xA,
CATPT_MODID_PCM_SYSTEM = 0xB,
CATPT_MODID_PCM_REFERENCE = 0xC,
CATPT_MODID_PCM = 0xD, /* offload */
CATPT_MODID_BLUETOOTH_RENDER = 0xE,
CATPT_MODID_BLUETOOTH_CAPTURE = 0xF,
CATPT_MODID_LAST = CATPT_MODID_BLUETOOTH_CAPTURE,
};
struct catpt_module_entry {
u32 module_id;
u32 entry_point;
} __packed;
struct catpt_module_map {
u8 num_entries;
struct catpt_module_entry entries[];
} __packed;
struct catpt_memory_info {
u32 offset;
u32 size;
} __packed;
#define CATPT_CHANNELS_MAX 4
#define CATPT_ALL_CHANNELS_MASK UINT_MAX
struct catpt_stream_info {
u32 stream_hw_id;
u32 reserved;
u32 read_pos_regaddr;
u32 pres_pos_regaddr;
u32 peak_meter_regaddr[CATPT_CHANNELS_MAX];
u32 volume_regaddr[CATPT_CHANNELS_MAX];
} __packed;
int catpt_ipc_alloc_stream(struct catpt_dev *cdev,
enum catpt_path_id path_id,
enum catpt_stream_type type,
struct catpt_audio_format *afmt,
struct catpt_ring_info *rinfo,
u8 num_modules,
struct catpt_module_entry *modules,
struct resource *persistent,
struct resource *scratch,
struct catpt_stream_info *sinfo);
int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id);
enum catpt_ssp_iface {
CATPT_SSP_IFACE_0 = 0,
CATPT_SSP_IFACE_1 = 1,
CATPT_SSP_COUNT,
};
enum catpt_mclk_frequency {
CATPT_MCLK_OFF = 0,
CATPT_MCLK_FREQ_6_MHZ = 1,
CATPT_MCLK_FREQ_21_MHZ = 2,
CATPT_MCLK_FREQ_24_MHZ = 3,
};
enum catpt_ssp_mode {
CATPT_SSP_MODE_I2S_CONSUMER = 0,
CATPT_SSP_MODE_I2S_PROVIDER = 1,
CATPT_SSP_MODE_TDM_PROVIDER = 2,
};
struct catpt_ssp_device_format {
u32 iface;
u32 mclk;
u32 mode;
u16 clock_divider;
u8 channels;
} __packed;
int catpt_ipc_set_device_format(struct catpt_dev *cdev,
struct catpt_ssp_device_format *devfmt);
enum catpt_dx_state {
CATPT_DX_STATE_D3 = 3,
};
enum catpt_dx_type {
CATPT_DX_TYPE_FW_IMAGE = 0,
CATPT_DX_TYPE_MEMORY_DUMP = 1,
};
struct catpt_save_meminfo {
u32 offset;
u32 size;
u32 source;
} __packed;
#define SAVE_MEMINFO_MAX 14
struct catpt_dx_context {
u32 num_meminfo;
struct catpt_save_meminfo meminfo[SAVE_MEMINFO_MAX];
} __packed;
int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state,
struct catpt_dx_context *context);
struct catpt_mixer_stream_info {
u32 mixer_hw_id;
u32 peak_meter_regaddr[CATPT_CHANNELS_MAX];
u32 volume_regaddr[CATPT_CHANNELS_MAX];
} __packed;
int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev,
struct catpt_mixer_stream_info *info);
/* STREAM messages */
enum catpt_stream_msg_type {
CATPT_STRM_RESET_STREAM = 0,
CATPT_STRM_PAUSE_STREAM = 1,
CATPT_STRM_RESUME_STREAM = 2,
CATPT_STRM_STAGE_MESSAGE = 3,
CATPT_STRM_NOTIFICATION = 4,
};
enum catpt_stage_action {
CATPT_STG_SET_VOLUME = 1,
CATPT_STG_SET_WRITE_POSITION = 2,
CATPT_STG_MUTE_LOOPBACK = 3,
};
union catpt_stream_msg {
u32 val;
struct {
u32 status:5;
u32 reserved:7;
u32 stage_action:4;
u32 stream_hw_id:4;
u32 stream_msg_type:4;
u32 global_msg_type:5;
u32 fw_ready:1;
u32 done:1;
u32 busy:1;
};
} __packed;
#define CATPT_STREAM_MSG(msg_type) \
{ \
.stream_msg_type = CATPT_STRM_##msg_type, \
.global_msg_type = CATPT_GLB_STREAM_MESSAGE }
#define CATPT_STAGE_MSG(msg_type) \
{ \
.stage_action = CATPT_STG_##msg_type, \
.stream_msg_type = CATPT_STRM_STAGE_MESSAGE, \
.global_msg_type = CATPT_GLB_STREAM_MESSAGE }
int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id);
int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id);
int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id);
/* STREAM messages - STAGE subtype */
enum catpt_audio_curve_type {
CATPT_AUDIO_CURVE_NONE = 0,
CATPT_AUDIO_CURVE_WINDOWS_FADE = 1,
};
int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id,
u32 channel, u32 volume,
u32 curve_duration,
enum catpt_audio_curve_type curve_type);
int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id,
u32 pos, bool eob, bool ll);
int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute);
/* NOTIFICATION messages */
enum catpt_notify_reason {
CATPT_NOTIFY_POSITION_CHANGED = 0,
CATPT_NOTIFY_GLITCH_OCCURRED = 1,
};
union catpt_notify_msg {
u32 val;
struct {
u32 mailbox_address:29;
u32 fw_ready:1;
u32 done:1;
u32 busy:1;
};
struct {
u32 status:5;
u32 reserved:7;
u32 notify_reason:4;
u32 stream_hw_id:4;
u32 stream_msg_type:4;
u32 global_msg_type:5;
u32 hdr:3; /* fw_ready, done, busy */
};
} __packed;
#define FW_INFO_SIZE_MAX 100
struct catpt_fw_ready {
u32 inbox_offset;
u32 outbox_offset;
u32 inbox_size;
u32 outbox_size;
u32 fw_info_size;
char fw_info[FW_INFO_SIZE_MAX];
} __packed;
struct catpt_notify_position {
u32 stream_position;
u32 fw_cycle_count;
} __packed;
enum catpt_glitch_type {
CATPT_GLITCH_UNDERRUN = 1,
CATPT_GLITCH_DECODER_ERROR = 2,
CATPT_GLITCH_DOUBLED_WRITE_POS = 3,
};
struct catpt_notify_glitch {
u32 type;
u64 presentation_pos;
u32 write_pos;
} __packed;
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,192 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2020 Intel Corporation
*
* Author: Cezary Rojewski <cezary.rojewski@intel.com>
*/
#ifndef __SND_SOC_INTEL_CATPT_REGS_H
#define __SND_SOC_INTEL_CATPT_REGS_H
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include <uapi/linux/pci_regs.h>
#define CATPT_SHIM_REGS_SIZE 4096
#define CATPT_DMA_REGS_SIZE 1024
#define CATPT_DMA_COUNT 2
#define CATPT_SSP_REGS_SIZE 512
/* DSP Shim registers */
#define CATPT_SHIM_CS1 0x00
#define CATPT_SHIM_ISC 0x18
#define CATPT_SHIM_ISD 0x20
#define CATPT_SHIM_IMC 0x28
#define CATPT_SHIM_IMD 0x30
#define CATPT_SHIM_IPCC 0x38
#define CATPT_SHIM_IPCD 0x40
#define CATPT_SHIM_CLKCTL 0x78
#define CATPT_SHIM_CS2 0x80
#define CATPT_SHIM_LTRC 0xE0
#define CATPT_SHIM_HMDC 0xE8
#define CATPT_CS_LPCS BIT(31)
#define CATPT_CS_SFCR(ssp) BIT(27 + (ssp))
#define CATPT_CS_S1IOCS BIT(23)
#define CATPT_CS_S0IOCS BIT(21)
#define CATPT_CS_PCE BIT(15)
#define CATPT_CS_SDPM(ssp) BIT(11 + (ssp))
#define CATPT_CS_STALL BIT(10)
#define CATPT_CS_DCS GENMASK(6, 4)
/* b100 DSP core & audio fabric high clock */
#define CATPT_CS_DCS_HIGH (0x4 << 4)
#define CATPT_CS_SBCS(ssp) BIT(2 + (ssp))
#define CATPT_CS_RST BIT(1)
#define CATPT_ISC_IPCDB BIT(1)
#define CATPT_ISC_IPCCD BIT(0)
#define CATPT_ISD_DCPWM BIT(31)
#define CATPT_ISD_IPCCB BIT(1)
#define CATPT_ISD_IPCDD BIT(0)
#define CATPT_IMC_IPCDB BIT(1)
#define CATPT_IMC_IPCCD BIT(0)
#define CATPT_IMD_IPCCB BIT(1)
#define CATPT_IMD_IPCDD BIT(0)
#define CATPT_IPCC_BUSY BIT(31)
#define CATPT_IPCC_DONE BIT(30)
#define CATPT_IPCD_BUSY BIT(31)
#define CATPT_IPCD_DONE BIT(30)
#define CATPT_CLKCTL_CFCIP BIT(31)
#define CATPT_CLKCTL_SMOS GENMASK(25, 24)
#define CATPT_HMDC_HDDA(e, ch) BIT(8 * (e) + (ch))
/* defaults to reset SHIM registers to after each power cycle */
#define CATPT_CS_DEFAULT 0x8480040E
#define CATPT_ISC_DEFAULT 0x0
#define CATPT_ISD_DEFAULT 0x0
#define CATPT_IMC_DEFAULT 0x7FFF0003
#define CATPT_IMD_DEFAULT 0x7FFF0003
#define CATPT_IPCC_DEFAULT 0x0
#define CATPT_IPCD_DEFAULT 0x0
#define CATPT_CLKCTL_DEFAULT 0x7FF
#define CATPT_CS2_DEFAULT 0x0
#define CATPT_LTRC_DEFAULT 0x0
#define CATPT_HMDC_DEFAULT 0x0
/* PCI Configuration registers */
#define CATPT_PCI_PMCAPID 0x80
#define CATPT_PCI_PMCS (CATPT_PCI_PMCAPID + PCI_PM_CTRL)
#define CATPT_PCI_VDRTCTL0 0xA0
#define CATPT_PCI_VDRTCTL2 0xA8
#define CATPT_VDRTCTL2_DTCGE BIT(10)
#define CATPT_VDRTCTL2_DCLCGE BIT(1)
#define CATPT_VDRTCTL2_CGEALL 0xF7F
/* LPT PCI Configuration bits */
#define LPT_VDRTCTL0_DSRAMPGE(b) BIT(16 + (b))
#define LPT_VDRTCTL0_DSRAMPGE_MASK GENMASK(31, 16)
#define LPT_VDRTCTL0_ISRAMPGE(b) BIT(6 + (b))
#define LPT_VDRTCTL0_ISRAMPGE_MASK GENMASK(15, 6)
#define LPT_VDRTCTL0_D3SRAMPGD BIT(2)
#define LPT_VDRTCTL0_D3PGD BIT(1)
#define LPT_VDRTCTL0_APLLSE BIT(0)
/* WPT PCI Configuration bits */
#define WPT_VDRTCTL0_DSRAMPGE(b) BIT(12 + (b))
#define WPT_VDRTCTL0_DSRAMPGE_MASK GENMASK(31, 12)
#define WPT_VDRTCTL0_ISRAMPGE(b) BIT(2 + (b))
#define WPT_VDRTCTL0_ISRAMPGE_MASK GENMASK(11, 2)
#define WPT_VDRTCTL0_D3SRAMPGD BIT(1)
#define WPT_VDRTCTL0_D3PGD BIT(0)
#define WPT_VDRTCTL2_APLLSE BIT(31)
/* defaults to reset SSP registers to after each power cycle */
#define CATPT_SSC0_DEFAULT 0x0
#define CATPT_SSC1_DEFAULT 0x0
#define CATPT_SSS_DEFAULT 0xF004
#define CATPT_SSIT_DEFAULT 0x0
#define CATPT_SSD_DEFAULT 0xC43893A3
#define CATPT_SSTO_DEFAULT 0x0
#define CATPT_SSPSP_DEFAULT 0x0
#define CATPT_SSTSA_DEFAULT 0x0
#define CATPT_SSRSA_DEFAULT 0x0
#define CATPT_SSTSS_DEFAULT 0x0
#define CATPT_SSCR2_DEFAULT 0x0
#define CATPT_SSPSP2_DEFAULT 0x0
/* Coredump register and its states */
#define CATPT_DRAM_COREDUMP 0x1F4
#define CATPT_COREDUMP_REQUEST UINT_MAX
#define CATPT_COREDUMP_RELEASE 0
/* Physically the same block, access address differs between host and dsp */
#define CATPT_DSP_DRAM_OFFSET 0x400000
#define catpt_to_host_offset(offset) ((offset) & ~(CATPT_DSP_DRAM_OFFSET))
#define catpt_to_dsp_offset(offset) ((offset) | CATPT_DSP_DRAM_OFFSET)
#define CATPT_MEMBLOCK_SIZE 0x8000
#define catpt_num_dram(cdev) (hweight_long((cdev)->spec->dram_mask))
#define catpt_num_iram(cdev) (hweight_long((cdev)->spec->iram_mask))
#define catpt_dram_size(cdev) (catpt_num_dram(cdev) * CATPT_MEMBLOCK_SIZE)
#define catpt_iram_size(cdev) (catpt_num_iram(cdev) * CATPT_MEMBLOCK_SIZE)
/* registry I/O helpers */
#define catpt_dram_addr(cdev) \
((cdev)->lpe_ba + (cdev)->spec->host_dram_offset)
#define catpt_iram_addr(cdev) \
((cdev)->lpe_ba + (cdev)->spec->host_iram_offset)
#define catpt_shim_addr(cdev) \
((cdev)->lpe_ba + (cdev)->spec->host_shim_offset)
#define catpt_dma_addr(cdev, dma) \
((cdev)->lpe_ba + (cdev)->spec->host_dma_offset[dma])
#define catpt_ssp_addr(cdev, ssp) \
((cdev)->lpe_ba + (cdev)->spec->host_ssp_offset[ssp])
#define catpt_inbox_addr(cdev) \
((cdev)->lpe_ba + (cdev)->ipc.config.inbox_offset)
#define catpt_outbox_addr(cdev) \
((cdev)->lpe_ba + (cdev)->ipc.config.outbox_offset)
#define catpt_writel_ssp(cdev, ssp, reg, val) \
writel(val, catpt_ssp_addr(cdev, ssp) + (reg))
#define catpt_readl_dram(cdev, reg) \
readl(catpt_dram_addr(cdev) + CATPT_DRAM_##reg)
#define catpt_writel_dram(cdev, reg, val) \
writel(val, catpt_dram_addr(cdev) + CATPT_DRAM_##reg)
#define catpt_readl_shim(cdev, reg) \
readl(catpt_shim_addr(cdev) + CATPT_SHIM_##reg)
#define catpt_writel_shim(cdev, reg, val) \
writel(val, catpt_shim_addr(cdev) + CATPT_SHIM_##reg)
#define catpt_updatel_shim(cdev, reg, mask, val) \
catpt_writel_shim(cdev, reg, \
(catpt_readl_shim(cdev, reg) & ~(mask)) | (val))
#define catpt_readl_poll_shim(cdev, reg, val, cond, delay_us, timeout_us) \
readl_poll_timeout(catpt_shim_addr(cdev) + CATPT_SHIM_##reg, \
val, cond, delay_us, timeout_us)
#define catpt_readl_pci(cdev, reg) \
readl(cdev->pci_ba + CATPT_PCI_##reg)
#define catpt_writel_pci(cdev, reg, val) \
writel(val, cdev->pci_ba + CATPT_PCI_##reg)
#define catpt_updatel_pci(cdev, reg, mask, val) \
catpt_writel_pci(cdev, reg, \
(catpt_readl_pci(cdev, reg) & ~(mask)) | (val))
#define catpt_readl_poll_pci(cdev, reg, val, cond, delay_us, timeout_us) \
readl_poll_timeout((cdev)->pci_ba + CATPT_PCI_##reg, \
val, cond, delay_us, timeout_us)
#endif
+56
View File
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
#include <linux/pm_runtime.h>
#include "core.h"
static ssize_t fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct catpt_dev *cdev = dev_get_drvdata(dev);
struct catpt_fw_version version;
int ret;
ret = pm_runtime_resume_and_get(cdev->dev);
if (ret)
return ret;
ret = catpt_ipc_get_fw_version(cdev, &version);
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
return CATPT_IPC_RET(ret);
return sysfs_emit(buf, "%d.%d.%d.%d\n", version.type, version.major,
version.minor, version.build);
}
static DEVICE_ATTR_RO(fw_version);
static ssize_t fw_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct catpt_dev *cdev = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", cdev->ipc.config.fw_info);
}
static DEVICE_ATTR_RO(fw_info);
static struct attribute *catpt_attrs[] = {
&dev_attr_fw_version.attr,
&dev_attr_fw_info.attr,
NULL
};
static const struct attribute_group catpt_attr_group = {
.attrs = catpt_attrs,
};
const struct attribute_group *catpt_attr_groups[] = {
&catpt_attr_group,
NULL
};
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
#define BYTES_PER_LINE 16
#define MAX_CHUNK_SIZE ((PAGE_SIZE - 150) /* Place for trace header */ \
/ (2 * BYTES_PER_LINE + 4) /* chars per line */ \
* BYTES_PER_LINE)
void trace_catpt_ipc_payload(const void *data, size_t size)
{
size_t remaining = size;
size_t offset = 0;
while (remaining > 0) {
u32 chunk;
chunk = min_t(size_t, remaining, MAX_CHUNK_SIZE);
trace_catpt_ipc_payload_chunk(data, chunk, offset, size);
remaining -= chunk;
offset += chunk;
}
}
+91
View File
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2020 Intel Corporation
*
* Author: Cezary Rojewski <cezary.rojewski@intel.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM intel_catpt
#if !defined(__SND_SOC_INTEL_CATPT_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define __SND_SOC_INTEL_CATPT_TRACE_H
#include <linux/types.h>
#include <linux/tracepoint.h>
DECLARE_EVENT_CLASS(catpt_ipc_msg,
TP_PROTO(u32 header),
TP_ARGS(header),
TP_STRUCT__entry(
__field(u32, header)
),
TP_fast_assign(
__entry->header = header;
),
TP_printk("0x%08x", __entry->header)
);
DEFINE_EVENT(catpt_ipc_msg, catpt_irq,
TP_PROTO(u32 header),
TP_ARGS(header)
);
DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_request,
TP_PROTO(u32 header),
TP_ARGS(header)
);
DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_reply,
TP_PROTO(u32 header),
TP_ARGS(header)
);
DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_notify,
TP_PROTO(u32 header),
TP_ARGS(header)
);
TRACE_EVENT_CONDITION(catpt_ipc_payload_chunk,
TP_PROTO(const u8 *data, size_t size, size_t offset, size_t total),
TP_ARGS(data, size, offset, total),
TP_CONDITION(data && size),
TP_STRUCT__entry(
__dynamic_array(u8, buf, size )
__field(size_t, offset )
__field(size_t, pos )
__field(size_t, total )
),
TP_fast_assign(
memcpy(__get_dynamic_array(buf), data + offset, size);
__entry->offset = offset;
__entry->pos = offset + size;
__entry->total = total;
),
TP_printk("range %zu-%zu out of %zu bytes%s",
__entry->offset, __entry->pos, __entry->total,
__print_hex_dump("", DUMP_PREFIX_NONE, 16, 4,
__get_dynamic_array(buf),
__get_dynamic_array_len(buf), false))
);
void trace_catpt_ipc_payload(const void *data, size_t size);
#endif /* __SND_SOC_INTEL_CATPT_TRACE_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>