mbox series

[v6,00/22] ASoC: qcom: Add AudioReach support

Message ID 20210915131333.19047-1-srinivas.kandagatla@linaro.org
Headers show
Series ASoC: qcom: Add AudioReach support | expand

Message

Srinivas Kandagatla Sept. 15, 2021, 1:13 p.m. UTC
Many thanks for reviewing v5. This version addresses all the comments
raised as part of v5 review.

This patchset adds ASoC driver support to configure signal processing
framework ("AudioReach") which is integral part of Qualcomm next
generation audio SDK and will be deployed on upcoming Qualcomm chipsets.
It makes use of ASoC Topology to load graphs on to the DSP which is then
managed by APM (Audio Processing Manager) service to prepare/start/stop.

Here is simplified high-level block diagram of AudioReach:

 ___________________________________________________________
|                 CPU (Application Processor)               |
|  +---------+          +---------+         +----------+    |
|  |  q6apm  |          |  q6apm  |         |  q6apm   |    |
|  |   dais  | <------> |         | <-----> |lpass-dais|    |
|  +---------+          +---------+         +----------+    |
|                            ^  ^                           |
|                            |  |           +---------+     |
|  +---------+               v  +---------->|topology |     |
|  | q6prm   |          +---------+         |         |     |
|  |         |<-------->|   GPR   |         +---------+     |
|  +---------+          +---------+                         |
|       ^                    ^                              |
|       |                    |                              |
|  +----------+              |                              |
|  |   q6prm  |              |                              |
|  |lpass-clks|              |                              |
|  +----------+              |                              |
|____________________________|______________________________|
                             |  
                             | RPMSG (IPC over GLINK)              
 ____________________________|______________________________
|                            |                              |
|    +-----------------------+                              |
|    |                       |                              |
|    v                       v              q6 (Audio DSP)  |
|+-----+    +----------------------------------+            |
|| PRM |    | APM (Audio Processing Manager)   |            |
|+-----+    |  . Graph Management              |            |  
|           |  . Command Handing               |            |  
|           |  . Event Management              |            |  
|           |  ...                             |            |  
|           +----------------------------------+            |  
|                            ^                              |
|____________________________|______________________________|
                             |  
                             |   LPASS AIF
 ____________________________|______________________________
|                            |            Audio I/O         |
|                            v                              |
|   +--------------------------------------------------+    |
|    |                Audio devices                     |   |
|    | CODEC | HDMI-TX | PCM  | SLIMBUS | I2S |MI2S |...|   |
|    |                                                  |   |
|    +--------------------------------------------------+   |
|___________________________________________________________|

AudioReach has constructs of sub-graph, container and modules.
Each sub-graph can have N containers and each Container can have N Modules
and connections between them can be linear or non-linear.
An audio function can be realized with one or many connected
sub-graphs. There are also control/event paths between modules that can
be wired up while building graph to achieve various control mechanism
between modules. These concepts of Sub-Graph, Containers and Modules
are represented in ASoC topology.

Here is simple I2S graph with a Write Shared Memory and a
Volume control module within a single Subgraph (1) with one Container (1)
and 5 modules.

  ____________________________________________________________
 |                        Sub-Graph [1]                       |
 |  _______________________________________________________   |
 | |                       Container [1]                   |  |
 | | [WR_SH] -> [PCM DEC] -> [PCM CONV] -> [VOL]-> [I2S-EP]|  |
 | |_______________________________________________________|  |
 |____________________________________________________________|

For now this graph is split into two subgraphs to achieve dpcm like below:
 ________________________________________________    _________________
|                Sub-Graph [1]                   |  |  Sub-Graph [2]  |
|  ____________________________________________  |  |  _____________  |
| |              Container [1]                 | |  | |Container [2]| |
| | [WR_SH] -> [PCM DEC] -> [PCM CONV] -> [VOL]| |  | |   [I2S-EP]  | |
| |____________________________________________| |  | |_____________| |
|________________________________________________|  |_________________|

                                                      _________________
                                                    |  Sub-Graph [3]  |
                                                    |  _____________  |
                                                    | |Container [3]| |
                                                    | |  [DMA-EP]   | |
                                                    | |_____________| |
                                                    |_________________|


This patchset adds very minimal support for AudioReach which includes
supporting sub-graphs containing CODEC DMA ports and simple PCM
Decoder/Encoder and Logger Modules. Additional capabilities will
be built over time to expose features offered by AudioReach. 

This patchset is Tested on SM8250 SoC based Qualcomm Robotics Platform RB5
and SM9250 MTP with WSA881X Smart Speaker Amplifiers, DMICs connected via
VA Macro and WCD938x Codec connected via TX and RX Macro and HDMI audio
via I2S.

First 10 Patches are mostly reorganization existing Old QDSP Audio
Framework code and bindings so that we could reuse them on AudioReach.

ASoC topology graphs for DragonBoard RB5 and SM8250 MTP are available at 
https://git.linaro.org/people/srinivas.kandagatla/audioreach-topology.git/
and Qualcomm AudioReach DSP headers are available at:
https://source.codeaurora.org/quic/la/platform/vendor/opensource/arspf-headers

Note: There is one false positive warning in this patchset:
audioreach.c:80:45: warning: array of flexible structures

Thanks,
srini

Changes since v5:
- moved usage zero length arrays to flexible arrays as suggested by Pierre.
- updated dt-bindings as suggested by Rob.
- Fixed various style issues and typos, spotted by Pierre
- Replaced usage of spinlock with mutex, as we are mostly dealing with workqueues.
- Fixed memory leak in two places spotted by Pierre.
- added pga module gain setup into dapm event.
- Updated Kconfig with correct ordering.
- Fixed build warning for non OF cases.
- Added more comments as suggested by Pierre

Srinivas Kandagatla (22):
  soc: dt-bindings: qcom: apr: convert to yaml
  soc: dt-bindings: qcom: apr: deprecate qcom,apr-domain property
  soc: qcom: apr: make code more reuseable
  soc: dt-bindings: qcom: add gpr bindings
  soc: qcom: apr: Add GPR support
  ASoC: dt-bindings: move LPASS dai related bindings out of q6afe
  ASoC: dt-bindings: move LPASS clocks related bindings out of q6afe
  ASoC: dt-bindings: rename q6afe.h to q6dsp-lpass-ports.h
  ASoC: qdsp6: q6afe-dai: move lpass audio ports to common file
  ASoC: qdsp6: q6afe-clocks: move audio-clocks to common file
  ASoC: dt-bindings: q6dsp: add q6apm-lpass-dai compatible
  ASoC: dt-bindings: lpass-clocks: add q6prm clocks compatible
  ASoC: dt-bindings: add q6apm digital audio stream bindings
  ASoC: qdsp6: audioreach: add basic pkt alloc support
  ASoC: qdsp6: audioreach: add q6apm support
  ASoC: qdsp6: audioreach: add module configuration command helpers
  ASoC: qdsp6: audioreach: add Kconfig and Makefile
  ASoC: qdsp6: audioreach: add topology support
  ASoC: qdsp6: audioreach: add q6apm-dai support
  ASoC: qdsp6: audioreach: add q6apm lpass dai support
  ASoC: qdsp6: audioreach: add q6prm support
  ASoC: qdsp6: audioreach: add support for q6prm-clocks

 .../devicetree/bindings/soc/qcom/qcom,apr.txt |  134 --
 .../bindings/soc/qcom/qcom,apr.yaml           |  177 +++
 .../devicetree/bindings/sound/qcom,q6afe.txt  |  181 ---
 .../bindings/sound/qcom,q6apm-dai.yaml        |   53 +
 .../sound/qcom,q6dsp-lpass-clocks.yaml        |   77 ++
 .../sound/qcom,q6dsp-lpass-ports.yaml         |  205 +++
 drivers/soc/qcom/Kconfig                      |    2 +-
 drivers/soc/qcom/apr.c                        |  289 ++++-
 include/dt-bindings/soc/qcom,gpr.h            |   19 +
 include/dt-bindings/sound/qcom,q6afe.h        |  203 +--
 .../sound/qcom,q6dsp-lpass-ports.h            |  208 +++
 include/linux/soc/qcom/apr.h                  |   70 +-
 include/uapi/sound/snd_ar_tokens.h            |  208 +++
 sound/soc/qcom/Kconfig                        |   22 +
 sound/soc/qcom/qdsp6/Makefile                 |   11 +-
 sound/soc/qcom/qdsp6/audioreach.c             | 1140 +++++++++++++++++
 sound/soc/qcom/qdsp6/audioreach.h             |  726 +++++++++++
 sound/soc/qcom/qdsp6/q6afe-clocks.c           |  187 +--
 sound/soc/qcom/qdsp6/q6afe-dai.c              |  687 +---------
 sound/soc/qcom/qdsp6/q6apm-dai.c              |  415 ++++++
 sound/soc/qcom/qdsp6/q6apm-lpass-dais.c       |  260 ++++
 sound/soc/qcom/qdsp6/q6apm.c                  |  836 ++++++++++++
 sound/soc/qcom/qdsp6/q6apm.h                  |  152 +++
 sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c     |  186 +++
 sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h     |   30 +
 sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c      |  627 +++++++++
 sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h      |   22 +
 sound/soc/qcom/qdsp6/q6prm-clocks.c           |   85 ++
 sound/soc/qcom/qdsp6/q6prm.c                  |  202 +++
 sound/soc/qcom/qdsp6/q6prm.h                  |   78 ++
 sound/soc/qcom/qdsp6/topology.c               | 1119 ++++++++++++++++
 31 files changed, 7196 insertions(+), 1415 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-clocks.yaml
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
 create mode 100644 include/dt-bindings/soc/qcom,gpr.h
 create mode 100644 include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
 create mode 100644 include/uapi/sound/snd_ar_tokens.h
 create mode 100644 sound/soc/qcom/qdsp6/audioreach.c
 create mode 100644 sound/soc/qcom/qdsp6/audioreach.h
 create mode 100644 sound/soc/qcom/qdsp6/q6apm-dai.c
 create mode 100644 sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
 create mode 100644 sound/soc/qcom/qdsp6/q6apm.c
 create mode 100644 sound/soc/qcom/qdsp6/q6apm.h
 create mode 100644 sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
 create mode 100644 sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h
 create mode 100644 sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
 create mode 100644 sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
 create mode 100644 sound/soc/qcom/qdsp6/q6prm-clocks.c
 create mode 100644 sound/soc/qcom/qdsp6/q6prm.c
 create mode 100644 sound/soc/qcom/qdsp6/q6prm.h
 create mode 100644 sound/soc/qcom/qdsp6/topology.c

Comments

Pierre-Louis Bossart Sept. 15, 2021, 3:47 p.m. UTC | #1
> +gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev,
> +				gpr_port_cb cb,	void *priv)
> +{
> +	struct packet_router *pr = dev_get_drvdata(gdev->dev.parent);
> +	gpr_port_t *port;
> +	struct pkt_router_svc *svc;
> +	int id;
> +
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return ERR_PTR(-ENOMEM);
> +
> +	svc = port;
> +	svc->callback = cb;
> +	svc->pr = pr;
> +	svc->priv = priv;
> +	svc->dev = dev;
> +	spin_lock_init(&svc->lock);
> +
> +	spin_lock(&pr->svcs_lock);
> +	id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START,
> +			      GPR_DYNAMIC_PORT_END, GFP_ATOMIC);
> +	if (id < 0) {
> +		dev_err(dev, "Unable to allocate dynamic GPR src port\n");
> +		kfree(port);
> +		spin_unlock(&pr->svcs_lock);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	svc->id = id;
> +	spin_unlock(&pr->svcs_lock);
> +
> +	dev_info(dev, "Adding GPR src port (%x)\n", svc->id);

nit-pick: isn't this a bit verbose?

> +
> +	return port;
> +}
> +EXPORT_SYMBOL_GPL(gpr_alloc_port);

> +struct gpr_pkt {
> +	struct gpr_hdr hdr;
> +	uint32_t payload[0];
> +};

looks like a zero-length array?

should this be

struct gpr_pkt {
    struct gpr_hdr hdr;
    uint32_t payload[];
};

?
Pierre-Louis Bossart Sept. 15, 2021, 3:54 p.m. UTC | #2
> +#define APM_SHMEM_FMT_CFG_PSIZE(n) ALIGN( \
> +				sizeof(struct apm_sh_module_media_fmt_cmd) + \
> +				n * sizeof(uint8_t), 8)
> +
> +/* num of channels as argument */
> +#define APM_PCM_MODULE_FMT_CMD_PSIZE(n) ALIGN( \
> +				sizeof(struct apm_pcm_module_media_fmt_cmd) + \
> +				n * sizeof(uint8_t), 8)
> +
> +#define APM_PCM_OUT_FMT_CFG_PSIZE(n) ALIGN((sizeof( \
> +				struct payload_pcm_output_format_cfg) + \
> +				n * sizeof(uint8_t)), 4)

nit-pick: sizeof on the same line for consistency/readability?
Pierre-Louis Bossart Sept. 15, 2021, 4:11 p.m. UTC | #3
> +int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
> +			     size_t period_sz, unsigned int periods)
> +{
> +	struct audioreach_graph_data *data;
> +	struct audio_buffer *buf;
> +	int cnt;
> +	int rc;
> +
> +	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
> +		data = &graph->rx_data;
> +	else
> +		data = &graph->tx_data;
> +
> +	mutex_lock(&graph->lock);
> +
> +	if (data->buf) {
> +		dev_err(graph->dev, "Buffer already allocated\n");
> +		mutex_unlock(&graph->lock);
> +		return 0;

is this an error worth of dev_err() if you return 0?

> +	}
> +
> +	buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL);
> +	if (!buf) {
> +		mutex_unlock(&graph->lock);
> +		return -ENOMEM;
> +	}
> +
> +	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
> +		data = &graph->rx_data;
> +	else
> +		data = &graph->tx_data;
> +
> +	data->buf = buf;
> +
> +	buf[0].phys = phys;
> +	buf[0].size = period_sz;
> +
> +	for (cnt = 1; cnt < periods; cnt++) {
> +		if (period_sz > 0) {
> +			buf[cnt].phys = buf[0].phys + (cnt * period_sz);
> +			buf[cnt].size = period_sz;
> +		}
> +	}
> +	data->num_periods = periods;
> +
> +	mutex_unlock(&graph->lock);
> +
> +	rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
> +	if (rc < 0) {
> +		dev_err(graph->dev, "Memory_map_regions failed\n");
> +		audioreach_graph_free_buf(graph);
> +	}
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);

> +int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
> +		      uint32_t lsw_ts, uint32_t wflags)
> +{
> +	struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write;
> +	int rc, payload_size, iid;
> +	struct audio_buffer *ab;
> +	struct gpr_pkt *pkt;
> +
> +	payload_size = sizeof(*write);

nit-pick on variable-naming: I get confused between actions and objects.

> +
> +	iid = q6apm_graph_get_rx_shmem_module_iid(graph);
> +	pkt = audioreach_alloc_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
> +				 graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
> +				 graph->port->id, iid);
> +	if (IS_ERR(pkt))
> +		return -ENOMEM;
> +
> +	write = (void *)pkt + GPR_HDR_SIZE;
> +
> +	mutex_lock(&graph->lock);
> +	ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
> +
> +	write->buf_addr_lsw = lower_32_bits(ab->phys);
> +	write->buf_addr_msw = upper_32_bits(ab->phys);
> +	write->buf_size = len;
> +	write->timestamp_lsw = lsw_ts;
> +	write->timestamp_msw = msw_ts;
> +	write->mem_map_handle = graph->rx_data.mem_map_handle;
> +	write->flags = wflags;
> +
> +	graph->rx_data.dsp_buf++;
> +
> +	if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
> +		graph->rx_data.dsp_buf = 0;
> +
> +	mutex_unlock(&graph->lock);
> +
> +	rc = gpr_send_port_pkt(graph->port, pkt);
> +
> +	kfree(pkt);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(q6apm_write_async);
> +
> +int q6apm_read(struct q6apm_graph *graph)
> +{
> +	struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read;
> +	struct audioreach_graph_data *port;
> +	struct audio_buffer *ab;
> +	struct gpr_pkt *pkt;
> +	int rc, iid;
> +
> +	iid = q6apm_graph_get_tx_shmem_module_iid(graph);
> +	pkt = audioreach_alloc_pkt(sizeof(*read), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
> +				 graph->tx_data.dsp_buf, graph->port->id, iid);
> +	if (IS_ERR(pkt))
> +		return -ENOMEM;
> +
> +	read = (void *)pkt + GPR_HDR_SIZE;

same nit-pick on variable naming, with the additional present/past
grammar issue that you don't know if it's a read buffer or a pointer to
data read in the past.

> +
> +	mutex_lock(&graph->lock);
> +	port = &graph->tx_data;
> +	ab = &port->buf[port->dsp_buf];
> +
> +	read->buf_addr_lsw = lower_32_bits(ab->phys);
> +	read->buf_addr_msw = upper_32_bits(ab->phys);
> +	read->mem_map_handle = port->mem_map_handle;
> +	read->buf_size = ab->size;
> +
> +	port->dsp_buf++;
> +
> +	if (port->dsp_buf >= port->num_periods)
> +		port->dsp_buf = 0;
> +
> +	mutex_unlock(&graph->lock);
> +
> +	rc = gpr_send_port_pkt(graph->port, pkt);
> +	kfree(pkt);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(q6apm_read);
> +
>  static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
>  {
>  	struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
>
Srinivas Kandagatla Sept. 16, 2021, 3:02 p.m. UTC | #4
Thanks Pierre,

On 15/09/2021 16:54, Pierre-Louis Bossart wrote:
> 
> 
>> +#define APM_SHMEM_FMT_CFG_PSIZE(n) ALIGN( \
>> +				sizeof(struct apm_sh_module_media_fmt_cmd) + \
>> +				n * sizeof(uint8_t), 8)
>> +
>> +/* num of channels as argument */
>> +#define APM_PCM_MODULE_FMT_CMD_PSIZE(n) ALIGN( \
>> +				sizeof(struct apm_pcm_module_media_fmt_cmd) + \
>> +				n * sizeof(uint8_t), 8)
>> +
>> +#define APM_PCM_OUT_FMT_CFG_PSIZE(n) ALIGN((sizeof( \
>> +				struct payload_pcm_output_format_cfg) + \
>> +				n * sizeof(uint8_t)), 4)
> 
> nit-pick: sizeof on the same line for consistency/readability?
> 
Its fixed in next version.

--srini