OpenCloudOS-Kernel/drivers/soundwire/cadence_master.h

206 lines
5.3 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
/* Copyright(c) 2015-17 Intel Corporation. */
#include <sound/soc.h>
#ifndef __SDW_CADENCE_H
#define __SDW_CADENCE_H
#define SDW_CADENCE_GSYNC_KHZ 4 /* 4 kHz */
#define SDW_CADENCE_GSYNC_HZ (SDW_CADENCE_GSYNC_KHZ * 1000)
/*
* The Cadence IP supports up to 32 entries in the FIFO, though implementations
* can configure the IP to have a smaller FIFO.
*/
#define CDNS_MCP_IP_MAX_CMD_LEN 32
#define SDW_CADENCE_MCP_IP_OFFSET 0x4000
/**
* struct sdw_cdns_pdi: PDI (Physical Data Interface) instance
*
* @num: pdi number
* @intel_alh_id: link identifier
* @l_ch_num: low channel for PDI
* @h_ch_num: high channel for PDI
* @ch_count: total channel count for PDI
* @dir: data direction
* @type: stream type, (only PCM supported)
*/
struct sdw_cdns_pdi {
int num;
int intel_alh_id;
int l_ch_num;
int h_ch_num;
int ch_count;
enum sdw_data_direction dir;
enum sdw_stream_type type;
};
/**
* struct sdw_cdns_streams: Cadence stream data structure
*
* @num_bd: number of bidirectional streams
* @num_in: number of input streams
* @num_out: number of output streams
* @num_ch_bd: number of bidirectional stream channels
* @num_ch_bd: number of input stream channels
* @num_ch_bd: number of output stream channels
* @num_pdi: total number of PDIs
* @bd: bidirectional streams
* @in: input streams
* @out: output streams
*/
struct sdw_cdns_streams {
unsigned int num_bd;
unsigned int num_in;
unsigned int num_out;
unsigned int num_ch_bd;
unsigned int num_ch_in;
unsigned int num_ch_out;
unsigned int num_pdi;
struct sdw_cdns_pdi *bd;
struct sdw_cdns_pdi *in;
struct sdw_cdns_pdi *out;
};
/**
* struct sdw_cdns_stream_config: stream configuration
*
* @pcm_bd: number of bidirectional PCM streams supported
* @pcm_in: number of input PCM streams supported
* @pcm_out: number of output PCM streams supported
*/
struct sdw_cdns_stream_config {
unsigned int pcm_bd;
unsigned int pcm_in;
unsigned int pcm_out;
};
/**
* struct sdw_cdns_dai_runtime: Cadence DAI runtime data
*
* @name: SoundWire stream name
* @stream: stream runtime
* @pdi: PDI used for this dai
* @bus: Bus handle
* @stream_type: Stream type
* @link_id: Master link id
* @suspended: status set when suspended, to be used in .prepare
soundwire: intel: improve suspend flows This patch provides both a simplification of the suspend flows and a better balanced operation during suspend/resume transition, as part of the transition of Sound Open Firmware (SOF) to dynamic pipelines: the DSP resources are only enabled when required instead of enabled on startup. The exiting code relies on a convoluted way of dealing with suspend signals. Since there is no .suspend DAI callback, we used the component .suspend and marked all the component DAI dmas as 'suspended'. The information was used in the .prepare stage to differentiate resume operations from xrun handling, and only reinitialize SHIM registers and DMA in the former case. While this solution has been working reliably for about 2 years, there is a much better solution consisting in trapping the TRIGGER_SUSPEND in the .trigger DAI ops. The DMA is still marked in the same way for the .prepare op to run, but in addition the callbacks sent to DSP firmware are now balanced. Normal operation: hw_params -> intel_params_stream hw_free -> intel_free_stream suspend -> intel_free_stream prepare -> intel_params_stream This balanced operation was not required with existing SOF firmware relying on static pipelines instantiated at every boot. With the on-going transition to dynamic pipelines, it's however a requirement to keep the use count for the DAI widget balanced across all transitions. The component suspend is not removed but instead modified to deal with a corner case: when a substream is PAUSED, the ALSA core does not throw the TRIGGER_SUSPEND. This is problematic since the refcount for all pipelines and widgets is not balanced, leading to issues on resume. The trigger callback keeps track of the 'paused' state with a new flag, which is tested during the component suspend called later to release the remaining DSP resources. These resources will be re-enabled in the .prepare step. The IPC used in the TRIGGER_SUSPEND to release DSP resources is not a problem since the BE dailink is already marked as non-atomic. Co-developed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Acked-By: Vinod Koul <vkoul@kernel.org> Link: https://lore.kernel.org/r/20211224021034.26635-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2021-12-24 10:10:30 +08:00
* @paused: status set in .trigger, to be used in suspend
* @direction: stream direction
*/
struct sdw_cdns_dai_runtime {
char *name;
struct sdw_stream_runtime *stream;
struct sdw_cdns_pdi *pdi;
struct sdw_bus *bus;
enum sdw_stream_type stream_type;
int link_id;
bool suspended;
soundwire: intel: improve suspend flows This patch provides both a simplification of the suspend flows and a better balanced operation during suspend/resume transition, as part of the transition of Sound Open Firmware (SOF) to dynamic pipelines: the DSP resources are only enabled when required instead of enabled on startup. The exiting code relies on a convoluted way of dealing with suspend signals. Since there is no .suspend DAI callback, we used the component .suspend and marked all the component DAI dmas as 'suspended'. The information was used in the .prepare stage to differentiate resume operations from xrun handling, and only reinitialize SHIM registers and DMA in the former case. While this solution has been working reliably for about 2 years, there is a much better solution consisting in trapping the TRIGGER_SUSPEND in the .trigger DAI ops. The DMA is still marked in the same way for the .prepare op to run, but in addition the callbacks sent to DSP firmware are now balanced. Normal operation: hw_params -> intel_params_stream hw_free -> intel_free_stream suspend -> intel_free_stream prepare -> intel_params_stream This balanced operation was not required with existing SOF firmware relying on static pipelines instantiated at every boot. With the on-going transition to dynamic pipelines, it's however a requirement to keep the use count for the DAI widget balanced across all transitions. The component suspend is not removed but instead modified to deal with a corner case: when a substream is PAUSED, the ALSA core does not throw the TRIGGER_SUSPEND. This is problematic since the refcount for all pipelines and widgets is not balanced, leading to issues on resume. The trigger callback keeps track of the 'paused' state with a new flag, which is tested during the component suspend called later to release the remaining DSP resources. These resources will be re-enabled in the .prepare step. The IPC used in the TRIGGER_SUSPEND to release DSP resources is not a problem since the BE dailink is already marked as non-atomic. Co-developed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Acked-By: Vinod Koul <vkoul@kernel.org> Link: https://lore.kernel.org/r/20211224021034.26635-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
2021-12-24 10:10:30 +08:00
bool paused;
int direction;
};
/**
* struct sdw_cdns - Cadence driver context
* @dev: Linux device
* @bus: Bus handle
* @instance: instance number
* @ip_offset: version-dependent offset to access IP_MCP registers and fields
* @response_buf: SoundWire response buffer
* @tx_complete: Tx completion
* @ports: Data ports
* @num_ports: Total number of data ports
* @pcm: PCM streams
* @registers: Cadence registers
* @link_up: Link status
* @msg_count: Messages sent on bus
* @dai_runtime_array: runtime context for each allocated DAI.
*/
struct sdw_cdns {
struct device *dev;
struct sdw_bus bus;
unsigned int instance;
u32 ip_offset;
/*
* The datasheet says the RX FIFO AVAIL can be 2 entries more
* than the FIFO capacity, so allow for this.
*/
u32 response_buf[CDNS_MCP_IP_MAX_CMD_LEN + 2];
struct completion tx_complete;
struct sdw_cdns_port *ports;
int num_ports;
struct sdw_cdns_streams pcm;
int pdi_loopback_source;
int pdi_loopback_target;
void __iomem *registers;
bool link_up;
unsigned int msg_count;
bool interrupt_enabled;
struct work_struct work;
struct list_head list;
struct sdw_cdns_dai_runtime **dai_runtime_array;
};
#define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus)
/* Exported symbols */
int sdw_cdns_probe(struct sdw_cdns *cdns);
irqreturn_t sdw_cdns_irq(int irq, void *dev_id);
irqreturn_t sdw_cdns_thread(int irq, void *dev_id);
int sdw_cdns_init(struct sdw_cdns *cdns);
int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
struct sdw_cdns_stream_config config);
int sdw_cdns_exit_reset(struct sdw_cdns *cdns);
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state);
bool sdw_cdns_is_clock_stop(struct sdw_cdns *cdns);
int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake);
int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset);
#ifdef CONFIG_DEBUG_FS
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
#endif
struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
struct sdw_cdns_streams *stream,
u32 ch, u32 dir, int dai_id);
void sdw_cdns_config_stream(struct sdw_cdns *cdns,
u32 ch, u32 dir, struct sdw_cdns_pdi *pdi);
enum sdw_command_response
cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg);
enum sdw_command_response
cdns_xfer_msg_defer(struct sdw_bus *bus);
u32 cdns_read_ping_status(struct sdw_bus *bus);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params);
int cdns_set_sdw_stream(struct snd_soc_dai *dai,
void *stream, int direction);
soundwire: cadence: add paranoid check on self-clearing bits The Cadence IP exposes a small number of self-clearing bits in the MCP_CONTROL and MCP_CONFIG_UPDATE registers. We currently do not check that those bits are indeed cleared, e.g. during resume operations. That could lead to resuming peripheral devices too early. In addition, if we happen to read these registers, update one of the fields and write the register back, we may be writing stale data that might have been cleared in hardware. These sort of race conditions could lead to e.g. doing a hw_reset twice or stopping a clock that just restarted. There is no clear way of avoiding these potential race conditions other than making sure that these registers fields are cleared before any read-modify-write sequence. If we detect this sort of errors, we only log them since there is no clear recovery possible. The only way out is likely to restart the IP with a suspend/resume cycle. Note that the checks are performed before updating the registers, as well as after the Intel 'sync go' sequence in multi-link mode. That should cover both the start and end of suspend/resume hardware configurations. The Multi-Master mode gates the configuration updates until the 'sync go' signal is asserted, so we only check on init and after the end of the 'sync go' sequence. The duration of the usleep_range() was defined by the GSYNC frequency used in multi-master mode. With a 4kHz frequency, any configuration change might be deferred by up to 250us. Extending the range to 1000-1500us should guarantee that the configuration change is completed without any significant impact on the overall resume time. Suggested-by: Bard Liao <yung-chuan.liao@linux.intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Link: https://lore.kernel.org/r/20210714051349.13064-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
2021-07-14 13:13:49 +08:00
void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string,
bool initial_delay, int reset_iterations);
void sdw_cdns_config_update(struct sdw_cdns *cdns);
int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns);
#endif /* __SDW_CADENCE_H */