Message ID | 20200921115424.4105-3-cezary.rojewski@intel.com |
---|---|
State | Superseded |
Headers | show |
Series | ASoC: Intel: Catpt - Lynx and Wildcat point | expand |
On Mon, Sep 21, 2020 at 01:54:12PM +0200, Cezary Rojewski wrote: > Implement IRQ handlers for immediate and delayed replies and > notifications. Communication is synchronous and allows for serialization > of maximum one message at a time. > > DSP may respond with ADSP_PENDING status for a request - known as > delayed reply - and when situation occurs, framework keeps the lock and > awaits upcoming response through IPCD channel which is handled in > bottom-half. Immediate replies spawn no BH at all as their processing is > very short. > > Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> > --- > sound/soc/intel/catpt/core.h | 43 ++++ > sound/soc/intel/catpt/ipc.c | 246 ++++++++++++++++++ > sound/soc/intel/catpt/messages.h | 401 ++++++++++++++++++++++++++++++ > sound/soc/intel/catpt/registers.h | 1 - > 4 files changed, 690 insertions(+), 1 deletion(-) > create mode 100644 sound/soc/intel/catpt/ipc.c > create mode 100644 sound/soc/intel/catpt/messages.h > > diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h > index 48952e93d86f..0953989e24b3 100644 > --- a/sound/soc/intel/catpt/core.h > +++ b/sound/soc/intel/catpt/core.h > @@ -9,6 +9,7 @@ > #define __SND_SOC_INTEL_CATPT_CORE_H > > #include <linux/dma/dw.h> > +#include "messages.h" > #include "registers.h" > > struct catpt_dev; > @@ -29,6 +30,31 @@ static inline bool catpt_resource_overlapping(struct resource *r1, > return true; > } > > +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; > @@ -59,6 +85,7 @@ struct catpt_spec { > struct catpt_dev { > struct device *dev; > struct dw_dma_chip *dmac; > + struct catpt_ipc ipc; > > void __iomem *pci_ba; > void __iomem *lpe_ba; > @@ -83,4 +110,20 @@ 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); > > +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_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO) > + > +int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, > + struct catpt_ipc_msg request, > + struct catpt_ipc_msg *reply, int timeout); > +int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, > + struct catpt_ipc_msg *reply); > + > #endif > diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c > new file mode 100644 > index 000000000000..500d4845a7cf > --- /dev/null > +++ b/sound/soc/intel/catpt/ipc.c > @@ -0,0 +1,246 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// > +// Copyright(c) 2020 Intel Corporation. All rights reserved. > +// > +// Author: Cezary Rojewski <cezary.rojewski@intel.com> > +// > + > +#include <linux/irqreturn.h> > +#include "core.h" > +#include "messages.h" > +#include "registers.h" > + > +#define CATPT_IPC_TIMEOUT_MSECS 300 A nit-pick: _MS is enough. > +void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev) > +{ > + ipc->dev = dev; > + ipc->ready = false; > + ipc->default_timeout = CATPT_IPC_TIMEOUT_MSECS; > + 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; > + > + if (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) > +{ > + 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 (reply) { > + reply->header = ipc->rx.header; > + if (!ret && reply->data && reply->size) > + memcpy(reply->data, ipc->rx.data, ipc->rx.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) > +{ > + struct catpt_ipc *ipc = &cdev->ipc; > + int ret; > + > + mutex_lock(&ipc->mutex); > + ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout); > + mutex_unlock(&ipc->mutex); > + > + return ret; > +} > + > +int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, > + struct catpt_ipc_msg *reply) > +{ > + return catpt_dsp_send_msg_timeout(cdev, request, reply, > + cdev->ipc.default_timeout); > +} > + > +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.size && ipc->rx.rsp.status == CATPT_REPLY_SUCCESS) { > + memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), > + 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)); > + > + catpt_ipc_arm(ipc, &config); > + complete(&cdev->fw_ready); > + return; > + } > + > + switch (msg.type) { > + case CATPT_GLB_REQUEST_CORE_DUMP: > + break; > + > + case CATPT_GLB_STREAM_MESSAGE: > + switch (msg.subtype) { > + case CATPT_STRM_NOTIFICATION: > + 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.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); > + > + /* 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); > + > + /* 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); > + 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; > +} > diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h > new file mode 100644 > index 000000000000..5ce56e033ad2 > --- /dev/null > +++ b/sound/soc/intel/catpt/messages.h > @@ -0,0 +1,401 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Copyright(c) 2020 Intel Corporation. All rights reserved. > + * > + * 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 { > + enum catpt_reply_status status:5; > + u32 context:19; /* stream or module specific */ > + enum catpt_global_msg_type 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) \ > + { .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); > + > +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, > +}; > + > +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; > + enum catpt_channel_config channel_config __aligned(4); > + enum catpt_interleaving_style interleaving __aligned(4); > + 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 { > + enum catpt_module_id module_id __aligned(4); > + 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_IFACE_LAST = CATPT_SSP_IFACE_1, > +}; > + > +#define CATPT_SSP_COUNT (CATPT_SSP_IFACE_LAST + 1) > + > +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 { > + enum catpt_ssp_iface iface; > + enum catpt_mclk_frequency mclk; > + enum catpt_ssp_mode 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; > + enum catpt_dx_type source __aligned(4); > +} __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 { > + enum catpt_reply_status status:5; > + u32 reserved:7; > + enum catpt_stage_action stage_action:4; > + u32 stream_hw_id:4; > + enum catpt_stream_msg_type subtype:4; > + enum catpt_global_msg_type type:5; > + u32 fw_ready:1; > + u32 done:1; > + u32 busy:1; > + }; > +} __packed; > + > +#define CATPT_STREAM_MSG(msg_type) \ > +{ \ > + .subtype = CATPT_STRM_##msg_type, \ > + .type = CATPT_GLB_STREAM_MESSAGE } > +#define CATPT_STAGE_MSG(msg_type) \ > +{ \ > + .stage_action = CATPT_STG_##msg_type, \ > + .subtype = CATPT_STRM_STAGE_MESSAGE, \ > + .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 { > + enum catpt_reply_status status:5; > + u32 reserved:7; > + enum catpt_notify_reason notify_reason:4; > + u32 stream_hw_id:4; > + enum catpt_stream_msg_type subtype:4; > + enum catpt_global_msg_type 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 { > + enum catpt_glitch_type type __aligned(4); > + u64 presentation_pos; > + u32 write_pos; > +} __packed; > + > +#endif > diff --git a/sound/soc/intel/catpt/registers.h b/sound/soc/intel/catpt/registers.h > index a051d8b36d88..47280d82842e 100644 > --- a/sound/soc/intel/catpt/registers.h > +++ b/sound/soc/intel/catpt/registers.h > @@ -15,7 +15,6 @@ > #define CATPT_SHIM_REGS_SIZE 4096 > #define CATPT_DMA_REGS_SIZE 1024 > #define CATPT_DMA_COUNT 2 > -#define CATPT_SSP_COUNT 2 > #define CATPT_SSP_REGS_SIZE 512 > > /* DSP Shim registers */ > -- > 2.17.1 >
diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 48952e93d86f..0953989e24b3 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -9,6 +9,7 @@ #define __SND_SOC_INTEL_CATPT_CORE_H #include <linux/dma/dw.h> +#include "messages.h" #include "registers.h" struct catpt_dev; @@ -29,6 +30,31 @@ static inline bool catpt_resource_overlapping(struct resource *r1, return true; } +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; @@ -59,6 +85,7 @@ struct catpt_spec { struct catpt_dev { struct device *dev; struct dw_dma_chip *dmac; + struct catpt_ipc ipc; void __iomem *pci_ba; void __iomem *lpe_ba; @@ -83,4 +110,20 @@ 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); +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_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO) + +int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, + struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply, int timeout); +int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply); + #endif diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c new file mode 100644 index 000000000000..500d4845a7cf --- /dev/null +++ b/sound/soc/intel/catpt/ipc.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <linux/irqreturn.h> +#include "core.h" +#include "messages.h" +#include "registers.h" + +#define CATPT_IPC_TIMEOUT_MSECS 300 + +void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev) +{ + ipc->dev = dev; + ipc->ready = false; + ipc->default_timeout = CATPT_IPC_TIMEOUT_MSECS; + 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; + + if (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) +{ + 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 (reply) { + reply->header = ipc->rx.header; + if (!ret && reply->data && reply->size) + memcpy(reply->data, ipc->rx.data, ipc->rx.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) +{ + struct catpt_ipc *ipc = &cdev->ipc; + int ret; + + mutex_lock(&ipc->mutex); + ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout); + mutex_unlock(&ipc->mutex); + + return ret; +} + +int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply) +{ + return catpt_dsp_send_msg_timeout(cdev, request, reply, + cdev->ipc.default_timeout); +} + +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.size && ipc->rx.rsp.status == CATPT_REPLY_SUCCESS) { + memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), + 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)); + + catpt_ipc_arm(ipc, &config); + complete(&cdev->fw_ready); + return; + } + + switch (msg.type) { + case CATPT_GLB_REQUEST_CORE_DUMP: + break; + + case CATPT_GLB_STREAM_MESSAGE: + switch (msg.subtype) { + case CATPT_STRM_NOTIFICATION: + 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.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); + + /* 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); + + /* 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); + 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; +} diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h new file mode 100644 index 000000000000..5ce56e033ad2 --- /dev/null +++ b/sound/soc/intel/catpt/messages.h @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * 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 { + enum catpt_reply_status status:5; + u32 context:19; /* stream or module specific */ + enum catpt_global_msg_type 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) \ + { .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); + +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, +}; + +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; + enum catpt_channel_config channel_config __aligned(4); + enum catpt_interleaving_style interleaving __aligned(4); + 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 { + enum catpt_module_id module_id __aligned(4); + 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_IFACE_LAST = CATPT_SSP_IFACE_1, +}; + +#define CATPT_SSP_COUNT (CATPT_SSP_IFACE_LAST + 1) + +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 { + enum catpt_ssp_iface iface; + enum catpt_mclk_frequency mclk; + enum catpt_ssp_mode 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; + enum catpt_dx_type source __aligned(4); +} __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 { + enum catpt_reply_status status:5; + u32 reserved:7; + enum catpt_stage_action stage_action:4; + u32 stream_hw_id:4; + enum catpt_stream_msg_type subtype:4; + enum catpt_global_msg_type type:5; + u32 fw_ready:1; + u32 done:1; + u32 busy:1; + }; +} __packed; + +#define CATPT_STREAM_MSG(msg_type) \ +{ \ + .subtype = CATPT_STRM_##msg_type, \ + .type = CATPT_GLB_STREAM_MESSAGE } +#define CATPT_STAGE_MSG(msg_type) \ +{ \ + .stage_action = CATPT_STG_##msg_type, \ + .subtype = CATPT_STRM_STAGE_MESSAGE, \ + .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 { + enum catpt_reply_status status:5; + u32 reserved:7; + enum catpt_notify_reason notify_reason:4; + u32 stream_hw_id:4; + enum catpt_stream_msg_type subtype:4; + enum catpt_global_msg_type 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 { + enum catpt_glitch_type type __aligned(4); + u64 presentation_pos; + u32 write_pos; +} __packed; + +#endif diff --git a/sound/soc/intel/catpt/registers.h b/sound/soc/intel/catpt/registers.h index a051d8b36d88..47280d82842e 100644 --- a/sound/soc/intel/catpt/registers.h +++ b/sound/soc/intel/catpt/registers.h @@ -15,7 +15,6 @@ #define CATPT_SHIM_REGS_SIZE 4096 #define CATPT_DMA_REGS_SIZE 1024 #define CATPT_DMA_COUNT 2 -#define CATPT_SSP_COUNT 2 #define CATPT_SSP_REGS_SIZE 512 /* DSP Shim registers */
Implement IRQ handlers for immediate and delayed replies and notifications. Communication is synchronous and allows for serialization of maximum one message at a time. DSP may respond with ADSP_PENDING status for a request - known as delayed reply - and when situation occurs, framework keeps the lock and awaits upcoming response through IPCD channel which is handled in bottom-half. Immediate replies spawn no BH at all as their processing is very short. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> --- sound/soc/intel/catpt/core.h | 43 ++++ sound/soc/intel/catpt/ipc.c | 246 ++++++++++++++++++ sound/soc/intel/catpt/messages.h | 401 ++++++++++++++++++++++++++++++ sound/soc/intel/catpt/registers.h | 1 - 4 files changed, 690 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/catpt/ipc.c create mode 100644 sound/soc/intel/catpt/messages.h