diff --git a/include/sound/tegra_nvfx.h b/include/sound/tegra_nvfx.h new file mode 100644 index 00000000..8a5c8a90 --- /dev/null +++ b/include/sound/tegra_nvfx.h @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * tegra_nvfx.h - Shared NVFX interface between Tegra ADSP ALSA driver and + * ADSP side user space code. + */ + +#ifndef _TEGRA_NVFX_H_ +#define _TEGRA_NVFX_H_ + +/** + * variant_t + * + */ +typedef union { + uint64_t ptr; + + /* Standard C */ + void *pvoid; + char *pchar; + unsigned char *puchar; + short *pshort; + unsigned short *pushort; + long *plong; + unsigned long *pulong; + int *pint; + unsigned int *puint; + float *pfloat; + long long *pllong; + unsigned long long *pullong; + double *pdouble; + + /* stdint.h */ + int8_t *pint8; + uint8_t *puint8; + int16_t *pint16; + uint16_t *puint16; + int32_t *pint32; + uint32_t *puint32; +} variant_t; + +/* Memory can not be used for in-place transform */ +#define NVFX_MEM_READ_ACCESS 0x1 + +/* Memory can only be written to */ +#define NVFX_MEM_WRITE_ACCESS 0x2 + +/* Memory can read from or written to */ +#define NVFX_MEM_ALL_ACCESS \ + (NVFX_MEM_READ_ACCESS | NVFX_MEM_WRITE_ACCESS) + +/** + * nvfx_process_state_t - Required shared process state + * + * @state Current process state (i.e. active, inactive, etc.) + * @count Number invfx_t::process calls + * @ts_last Timestamp of last invfx_t::process call + * @time_total Time spent in all invfx_t::process calls + * @time_high Highest time spent in all invfx_t::process calls + * @time_low Lowest time spent in all invfx_t::process calls + * @time_last Time spent in the last invfx_t::process calls + * @period Actual scheduling period + * + */ +typedef struct { + int32_t state; + uint32_t count; + uint64_t ts_last; + uint64_t time_total; + uint32_t time_high; + uint32_t time_low; + uint32_t time_last; + uint32_t period; +} nvfx_process_state_t; + +/** + * nvfx_pin_state_t - Required shared pin state + * + * @bytes Bytes consumed or produced + * @frames Frames consumed or produced + * + */ +typedef struct { + uint64_t bytes; + uint32_t frames; +} nvfx_pin_state_t; + +/** + * Pin counts + * + * @NVFX_MAX_INPUT_PINS The maximum number of input pins for an effect + * @NVFX_MAX_OUTPUT_PINS The maximum number of output pins for an effect + */ +#define NVFX_MAX_INPUT_PINS 6 +#define NVFX_MAX_OUTPUT_PINS 2 +#define NVFX_MAX_RAW_DATA_WSIZE 1024 + +/** + * nvfx_shared_state_t - Required shared state information + * + * @process invfx_t::process related state + * @input State of input pins + * @output State of output pins + * + */ +typedef struct { + nvfx_process_state_t process; + nvfx_pin_state_t input[NVFX_MAX_INPUT_PINS]; + nvfx_pin_state_t output[NVFX_MAX_OUTPUT_PINS]; + /* custom params */ +} nvfx_shared_state_t; + +/** + * nvfx_call_params_t + * + * @NVFX_MAX_CALL_PARAMS_WSIZE Max size in int32s of unioned call parameters + * @size Size of the call parameters + * @method The index of the function to call + * [custom params] Variable length buffer of parameters e.g.: + * uint32_t custom_params[...] + */ +typedef struct { +#define NVFX_MAX_CALL_PARAMS_WSIZE 128 + uint32_t size; + uint32_t method; + /* custom params */ +} nvfx_call_params_t; + +/** + * Required FX Methods + * + * @nvfx_method_reset Resets FX to default state + * @nvfx_method_set_state Sets the FX state + * @nvfx_method_flush Flushes input and cached data buffers + * to support seek operations + * + * @nvfx_method_external_start: Start of externally defined FX methods + * + */ +enum { + nvfx_method_reset = 0, + nvfx_method_set_state, + nvfx_method_flush, + nvfx_method_read_params, + nvfx_method_error_event, + nvfx_method_set_apr_params, + + /* + * CPU to ADSP : Used to indicate end of stream + * ADSP to CPU : Used to notify that all input data is consumed + */ + nvfx_method_set_eos, + nvfx_method_external_start = 65536, + + nvfx_method_force32 = 0x7fffffff +}; + +/** + * nvfx_reset_params_t - Resets FX to default state + * + * @call nvfx_call_t parameters + * + */ +typedef struct { + nvfx_call_params_t call; +} nvfx_reset_params_t; + +/** + * nvfx_method_set_state - Required FX States + * + * @nvfx_state_inactive No processing except copying cached data + * @nvfx_state_active Process cached and/or new input data + * + * @nvfx_state_external_start Start of externally defined FX States + * + */ +enum { + nvfx_state_inactive = 0, + nvfx_state_active, + + nvfx_state_external_start = 65536, + + nvfx_state_force32 = 0x7fffffff +}; + +/** + * nvfx_set_state_params_t - Sets the FX state + * + * @call nvfx_call_t parameters + * @state State to set the FX + * + */ +typedef struct { + nvfx_call_params_t call; + int32_t state; +} nvfx_set_state_params_t; + +/** + * nvfx_method_error_event - Required FX States + * + * @nvfx_event_no_error + * @nvfx_event_error + * + */ +enum { + nvfx_event_no_error = 0, + nvfx_event_error, +}; + +/** + * nvfx_error_event_params_t - Sets the FX state + * + * @call nvfx_call_t parameters + * @state State to set the FX + * + */ +typedef struct { + nvfx_call_params_t call; + int32_t err; +} nvfx_error_event_params_t; + +/** + * nvfx_flush_params_t - Flushes input and cached data buffers to + * support seek operations + * + * @call nvfx_call_t parameters + * + */ +typedef struct { + nvfx_call_params_t call; +} nvfx_flush_params_t; + +/** + * nvfx_req_call_params_t - Parameters for required call functions + * + */ +typedef union { + nvfx_call_params_t call; + nvfx_reset_params_t reset; + nvfx_set_state_params_t set_state; + nvfx_flush_params_t flush; +} nvfx_req_call_params_t; + +/** + * FX Types + * + * @NVFX_TYPE_HARDWARE No software intervention (e.g. HW Module) + * @NVFX_TYPE_IN_PORT Brings data in (e.g. Stream) + * @NVFX_TYPE_OUT_PORT Returns data out (e.g. Stream, DMA) + * @NVFX_TYPE_N_INPUT Requires n input buffers (e.g. Mix) + * @NVFX_TYPE_N_OUTPUT Requires n output buffers (e.g. Splitter) + * @NVFX_TYPE_IN_PLACE Supports in place processing (e.g. Volume) + * @NVFX_TYPE_REFORMAT Reformats the data (e.g. Deycrypt, Decoder, SRC) + * @NVFX_TYPE_MULTIPASS Multiple processing passes (e.g. AEC) + * @NVFX_TYPE_NONLINEAR Non-linear processing (e.g. Reverb) + * + */ +#define NVFX_TYPE_HARDWARE 0x1 +#define NVFX_TYPE_IN_PORT 0x2 +#define NVFX_TYPE_OUT_PORT 0x4 +#define NVFX_TYPE_N_INPUT 0x10 +#define NVFX_TYPE_N_OUTPUT 0x20 +#define NVFX_TYPE_IN_PLACE 0x40 +#define NVFX_TYPE_REFORMAT 0x80 +#define NVFX_TYPE_MULTIPASS 0x100 +#define NVFX_TYPE_NONLINEAR 0x200 + +#endif /* #ifndef _TEGRA_NVFX_H_ */ diff --git a/include/sound/tegra_nvfx_apm.h b/include/sound/tegra_nvfx_apm.h new file mode 100644 index 00000000..ecdab396 --- /dev/null +++ b/include/sound/tegra_nvfx_apm.h @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * tegra_nvfx_apm.h - Shared APM interface between Tegra ADSP ALSA driver and + * ADSP side user space code. + */ + +#ifndef _TEGRA_NVFX_APM_H_ +#define _TEGRA_NVFX_APM_H_ + +#define NVFX_APM_CMD_QUEUE_WSIZE 2048 + +/** + * Pin types + * + * @NVFX_PIN_TYPE_INPUT Pin type for input pin + * @NVFX_PIN_TYPE_OUTPUT Pin type for output pin + */ +#define NVFX_PIN_TYPE_INPUT 0 +#define NVFX_PIN_TYPE_OUTPUT 1 + +/** + * apm_mbx_cmd: commands exchanged using mailbox. + * + * @apm_cmd_none: no operation. + * @apm_cmd_msg_ready message queue holds a new message. + * @apm_cmd_raw_data_ready message queue holds a raw data message + * @apm_cmd_msg_exit exit command + */ +enum apm_mbx_cmd { + apm_cmd_none = 0, + apm_cmd_msg_ready, + apm_cmd_raw_data_ready, + apm_cmd_msg_exit, +}; + +/** + * APM methods + * + */ +#define NVFX_APM_METHOD_ACK_BIT (1U << 31) /* Flag to request ACK */ + +/* Input modes of APM + * In PUSH mode, APM blocks if there is no input available. Usually + * a continuous mode ADMA sends interrupt when input data is available. + * In PULL mode, APM blocks only if there is no input available and + * a input fetch request is pending. Usually a one shot mode ADMA + * fetches input data when required and signals event to wake up APM. + */ +enum { + NVFX_APM_INPUT_MODE_PUSH = 0, + NVFX_APM_INPUT_MODE_PULL, + NVFX_APM_INPUT_MODE_MAX = NVFX_APM_INPUT_MODE_PULL, +}; + +enum { + /* NVFX APM params */ + nvfx_apm_method_fx_connect = nvfx_method_external_start, + nvfx_apm_method_fx_remove_all, + nvfx_apm_method_fx_set_param, + + nvfx_apm_method_set_io_buffer, + nvfx_apm_method_set_notification_size, + /* + * CPU to ADSP : Used to indicate new write or read position + * ADSP to CPU : Used to notify buffer position as per notification size + */ + nvfx_apm_method_set_position, + /* + * CPU to ADSP : Used to indicate end of stream + * ADSP to CPU : Used to notify that all input data is consumed + */ + nvfx_apm_method_set_eos, + nvfx_apm_method_set_priority, + /* ADSP to CPU : To send acknowledgement */ + nvfx_apm_method_ack, + nvfx_apm_method_raw_ack, + /* CPU to ADSP : To set APM input mode */ + nvfx_apm_method_set_input_mode, + nvfx_apm_method_write_data, + nvfx_apm_method_read_data, + /* ADSP to CPU : To report plugin error */ + nvfx_apm_method_fx_error_event, +}; + +/* For method nvfx_apm_method_set_io_buffer */ +typedef struct { + nvfx_call_params_t call_params; + uint32_t pin_type; /* NVFX_PIN_TYPE_INPUT or NVFX_PIN_TYPE_INPUT */ + uint32_t pin_id; + variant_t addr; + uint32_t size; + uint32_t flags; +} apm_io_buffer_params_t; + +/* For method nvfx_apm_method_set_position */ +typedef struct { + nvfx_call_params_t call_params; + uint32_t pin_type; /* NVFX_PIN_TYPE_INPUT or NVFX_PIN_TYPE_INPUT */ + uint32_t pin_id; + uint32_t offset; +} apm_position_params_t; + +/* For method nvfx_apm_method_set_notification_size */ +typedef struct { + nvfx_call_params_t call_params; + uint32_t pin_type; /* NVFX_PIN_TYPE_INPUT or NVFX_PIN_TYPE_INPUT */ + uint32_t pin_id; + uint32_t size; +} apm_notification_params_t; + +/* For nvfx_method_set_eos */ +typedef struct { + nvfx_call_params_t call_params; +} apm_eos_params_t; + +/* For nvfx_apm_method_set_priority */ +typedef struct { + nvfx_call_params_t call_params; + uint32_t priority; +} apm_set_priority_params_t; + +/* For nvfx_apm_method_set_input_mode */ +typedef struct { + nvfx_call_params_t call_params; + uint32_t mode; +} apm_set_input_mode_params_t; + +/* Module specific structures */ +typedef struct { + nvfx_call_params_t call_params; + variant_t plugin_src; /* source plugin pointer */ + int32_t pin_src; /* input pin id */ + variant_t plugin_dst; /* destination plugin pointer */ + int32_t pin_dst; /* destination pin id */ +} apm_fx_connect_params_t; + +typedef struct { + nvfx_call_params_t call_params; + variant_t plugin; +} apm_fx_remove_params_t; + +typedef struct { + nvfx_call_params_t call_params; + variant_t plugin; /* pointer to plugin_t */ + int32_t params[NVFX_MAX_CALL_PARAMS_WSIZE]; +} apm_fx_set_param_params_t; + +typedef struct { + nvfx_call_params_t call_params; + variant_t plugin; /* pointer to plugin_t */ + int32_t data[NVFX_MAX_RAW_DATA_WSIZE]; + int32_t size; +} apm_fx_raw_data_params_t; + +typedef struct { + nvfx_call_params_t call_params; + variant_t plugin; /* pointer to plugin_t */ + uint32_t req_size; +} apm_fx_read_request_params_t; + +typedef struct { + nvfx_call_params_t call_params; + variant_t plugin; /* pointer to plugin_t */ + uint32_t err; + uint32_t data[NVFX_MAX_CALL_PARAMS_WSIZE]; +} apm_fx_error_event_params_t; + +/* unified app message structure */ +#pragma pack(4) +typedef union { + msgq_message_t msgq_msg; + struct { + int32_t header[MSGQ_MESSAGE_HEADER_WSIZE]; + union { + nvfx_call_params_t call_params; + apm_io_buffer_params_t io_buffer_params; + apm_position_params_t position_params; + apm_notification_params_t notification_params; + nvfx_set_state_params_t state_params; + nvfx_reset_params_t reset_params; + apm_eos_params_t eos_params; + apm_set_priority_params_t priority_params; + apm_set_input_mode_params_t input_mode_params; + apm_fx_connect_params_t fx_connect_params; + apm_fx_remove_params_t fx_remove_params; + apm_fx_set_param_params_t fx_set_param_params; + apm_fx_read_request_params_t fx_read_request_params; + apm_fx_error_event_params_t fx_error_event_params; + }; + } msg; +} apm_msg_t; + +typedef union { + msgq_message_t msgq_msg; + struct { + int32_t header[MSGQ_MESSAGE_HEADER_WSIZE]; + union { + nvfx_call_params_t call_params; + apm_fx_raw_data_params_t fx_raw_data_params; + }; + } msg; +} apm_raw_data_msg_t; + +/* app message queue */ +typedef union { + msgq_t msgq; + struct { + int32_t header[MSGQ_HEADER_WSIZE]; + int32_t queue[NVFX_APM_CMD_QUEUE_WSIZE]; + } app_msgq; +} apm_msgq_t; +#pragma pack() + +/** + * APM state structure shared between ADSP & CPU + */ +typedef struct { + nvfx_shared_state_t nvfx_shared_state; + uint16_t mbox_id; /* mailbox for communication */ + apm_msgq_t msgq_recv; + apm_msgq_t msgq_send; + variant_t input_event; /* event_t pointer to signal input ready */ + variant_t output_event;/* event_t pointer to signal output needed */ +} apm_shared_state_t; + +typedef struct { + variant_t plugin; + /* NVFX specific shared memory follows */ +} plugin_shared_mem_t; + +/** + * Defines for APM priority levels + * 48 corresponds to HIGH_PRIORITY in LK. + */ +#define APM_PRIORITY_MAX (58) +#define APM_PRIORITY_DEFAULT (48) + + +#define PLUGIN_SHARED_MEM(x) ((plugin_shared_mem_t *)x) +#define APM_SHARED_STATE(x) ((apm_shared_state_t *)(PLUGIN_SHARED_MEM(x) + 1)) +#define NVFX_SHARED_STATE(x) ((nvfx_shared_state_t *)(PLUGIN_SHARED_MEM(x) + 1)) + +#endif /* #ifndef _TEGRA_NVFX_APM_H_ */ diff --git a/include/sound/tegra_nvfx_plugin.h b/include/sound/tegra_nvfx_plugin.h new file mode 100644 index 00000000..c9abf63a --- /dev/null +++ b/include/sound/tegra_nvfx_plugin.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * tegra_nvfx_plugin.h - Shared NVFX interface for different plugins between + * Tegra ADSP ALSA driver and ADSP side user space code. + */ + +#ifndef _TEGRA_NVFX_PLUGIN_H_ +#define _TEGRA_NVFX_PLUGIN_H_ + +/* Mode */ +#define ADMA_MODE_ONESHOT 1 +#define ADMA_MODE_CONTINUOUS 2 +#define ADMA_MODE_LINKED_LIST 4 + +/* Direction */ +#define ADMA_MEMORY_TO_MEMORY 1 +#define ADMA_AHUB_TO_MEMORY 2 +#define ADMA_MEMORY_TO_AHUB 4 +#define ADMA_AHUB_TO_AHUB 8 + +#define EAVB_RX_DMA 0 +#define EAVB_TX_DMA 1 + +/* EAVB DMA PLUGIN NAMES */ +#define EAVB_RX_PLUGIN "eavb_dma_rx" +#define EAVB_TX_PLUGIN "eavb_dma_tx" + +/* ADMA plugin related interface */ +enum { + /* NVFX ADMA params */ + nvfx_adma_method_init = nvfx_method_external_start, + nvfx_adma_method_get_position, + nvfx_adma_set_custom_params, + nvfx_adma_set_null_sink_mode, +}; + +typedef struct { + nvfx_call_params_t call_params; + int32_t adma_channel; + int32_t mode; + int32_t direction; + int32_t ahub_channel; + int32_t periods; + uint32_t adma_ch_page; + variant_t event; + uint32_t burst_size; + uint32_t intr_dur; +} nvfx_adma_init_params_t; + +typedef struct { + nvfx_call_params_t nvfx_call_params; + uint32_t is_enabled; + uint32_t buffer; + uint32_t offset; + uint64_t bytes_transferred; +} nvfx_adma_get_position_params_t; + +typedef union { + nvfx_req_call_params_t req_call; + nvfx_adma_init_params_t init; + nvfx_adma_get_position_params_t get_position; +} nvfx_adma_req_call_params_t; + +/* EAVB DMA plugin related interface */ +enum { + /* NVFX EAVB DMA params */ + nvfx_eavbdma_method_init = nvfx_method_external_start, + nvfx_eavbdma_method_get_position, +}; + +typedef struct { + nvfx_call_params_t call_params; + int32_t direction; + variant_t event; +} nvfx_eavbdma_init_params_t; + +typedef union { + nvfx_req_call_params_t req_call; + nvfx_eavbdma_init_params_t init; +} nvfx_eavbdma_req_call_params_t; + +#endif /* #ifndef _TEGRA_NVFX_PLUGIN_H_ */ diff --git a/sound/soc/tegra-virt-alt/Makefile b/sound/soc/tegra-virt-alt/Makefile index 7c80d2f4..5067f64a 100644 --- a/sound/soc/tegra-virt-alt/Makefile +++ b/sound/soc/tegra-virt-alt/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -# Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved. GCOV_PROFILE := y @@ -26,3 +26,6 @@ snd-soc-tegra210-virt-alt-admaif-objs := tegra210_virt_alt_admaif.o \ snd-soc-tegra-virt-t210ref-pcm-objs := tegra_virt_ref_alt.o obj-m += snd-soc-tegra210-virt-alt-admaif.o obj-m += snd-soc-tegra-virt-t210ref-pcm.o + +snd-soc-tegra210-virt-alt-adsp-objs := tegra210_adsp_virt_alt.o +obj-m += snd-soc-tegra210-virt-alt-adsp.o diff --git a/sound/soc/tegra-virt-alt/include/tegra210_adsp_virt_alt.h b/sound/soc/tegra-virt-alt/include/tegra210_adsp_virt_alt.h new file mode 100644 index 00000000..b96f1f11 --- /dev/null +++ b/sound/soc/tegra-virt-alt/include/tegra210_adsp_virt_alt.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * tegra210_adsp_virt_alt.h - Tegra210 ADSP header + * + */ + +#ifndef __TEGRA210_ADSP_ALT_H__ +#define __TEGRA210_ADSP_ALT_H__ + +/* This enum is linked with tegra210_adsp_mux_texts array.*/ +/* Anything changed in enum define should be also reflected in text array */ +enum tegra210_adsp_virt_regs { + TEGRA210_ADSP_NONE, + + /* End-point virtual regs */ + TEGRA210_ADSP_FRONT_END1, + TEGRA210_ADSP_FRONT_END2, + TEGRA210_ADSP_FRONT_END3, + TEGRA210_ADSP_FRONT_END4, + TEGRA210_ADSP_FRONT_END5, + TEGRA210_ADSP_FRONT_END6, + TEGRA210_ADSP_FRONT_END7, + TEGRA210_ADSP_FRONT_END8, + TEGRA210_ADSP_FRONT_END9, + TEGRA210_ADSP_FRONT_END10, + TEGRA210_ADSP_FRONT_END11, + TEGRA210_ADSP_FRONT_END12, + TEGRA210_ADSP_FRONT_END13, + TEGRA210_ADSP_FRONT_END14, + TEGRA210_ADSP_FRONT_END15, + + TEGRA210_ADSP_EAVB, + + TEGRA210_ADSP_ADMAIF1, + TEGRA210_ADSP_ADMAIF2, + TEGRA210_ADSP_ADMAIF3, + TEGRA210_ADSP_ADMAIF4, + TEGRA210_ADSP_ADMAIF5, + TEGRA210_ADSP_ADMAIF6, + TEGRA210_ADSP_ADMAIF7, + TEGRA210_ADSP_ADMAIF8, + TEGRA210_ADSP_ADMAIF9, + TEGRA210_ADSP_ADMAIF10, + TEGRA210_ADSP_ADMAIF11, + TEGRA210_ADSP_ADMAIF12, + TEGRA210_ADSP_ADMAIF13, + TEGRA210_ADSP_ADMAIF14, + TEGRA210_ADSP_ADMAIF15, + TEGRA210_ADSP_ADMAIF16, + TEGRA210_ADSP_ADMAIF17, + TEGRA210_ADSP_ADMAIF18, + TEGRA210_ADSP_ADMAIF19, + TEGRA210_ADSP_ADMAIF20, + + /* Virtual regs for apps */ + TEGRA210_ADSP_APM_IN1, + TEGRA210_ADSP_APM_IN2, + TEGRA210_ADSP_APM_IN3, + TEGRA210_ADSP_APM_IN4, + TEGRA210_ADSP_APM_IN5, + TEGRA210_ADSP_APM_IN6, + TEGRA210_ADSP_APM_IN7, + TEGRA210_ADSP_APM_IN8, + TEGRA210_ADSP_APM_IN9, + TEGRA210_ADSP_APM_IN10, + TEGRA210_ADSP_APM_IN11, + TEGRA210_ADSP_APM_IN12, + TEGRA210_ADSP_APM_IN13, + TEGRA210_ADSP_APM_IN14, + TEGRA210_ADSP_APM_IN15, + + TEGRA210_ADSP_APM_OUT1, + TEGRA210_ADSP_APM_OUT2, + TEGRA210_ADSP_APM_OUT3, + TEGRA210_ADSP_APM_OUT4, + TEGRA210_ADSP_APM_OUT5, + TEGRA210_ADSP_APM_OUT6, + TEGRA210_ADSP_APM_OUT7, + TEGRA210_ADSP_APM_OUT8, + TEGRA210_ADSP_APM_OUT9, + TEGRA210_ADSP_APM_OUT10, + TEGRA210_ADSP_APM_OUT11, + TEGRA210_ADSP_APM_OUT12, + TEGRA210_ADSP_APM_OUT13, + TEGRA210_ADSP_APM_OUT14, + TEGRA210_ADSP_APM_OUT15, + + TEGRA210_ADSP_PLUGIN_ADMA1, + TEGRA210_ADSP_PLUGIN_ADMA2, + TEGRA210_ADSP_PLUGIN_ADMA3, + TEGRA210_ADSP_PLUGIN_ADMA4, + TEGRA210_ADSP_PLUGIN_ADMA5, + TEGRA210_ADSP_PLUGIN_ADMA6, + TEGRA210_ADSP_PLUGIN_ADMA7, + TEGRA210_ADSP_PLUGIN_ADMA8, + TEGRA210_ADSP_PLUGIN_ADMA9, + TEGRA210_ADSP_PLUGIN_ADMA10, + TEGRA210_ADSP_PLUGIN_ADMA11, + TEGRA210_ADSP_PLUGIN_ADMA12, + TEGRA210_ADSP_PLUGIN_ADMA13, + TEGRA210_ADSP_PLUGIN_ADMA14, + TEGRA210_ADSP_PLUGIN_ADMA15, + TEGRA210_ADSP_PLUGIN_ADMA1_TX, + TEGRA210_ADSP_PLUGIN_ADMA2_TX, + TEGRA210_ADSP_PLUGIN_ADMA3_TX, + TEGRA210_ADSP_PLUGIN_ADMA4_TX, + TEGRA210_ADSP_PLUGIN_ADMA5_TX, + TEGRA210_ADSP_PLUGIN_ADMA6_TX, + TEGRA210_ADSP_PLUGIN_ADMA7_TX, + TEGRA210_ADSP_PLUGIN_ADMA8_TX, + TEGRA210_ADSP_PLUGIN_ADMA9_TX, + TEGRA210_ADSP_PLUGIN_ADMA10_TX, + TEGRA210_ADSP_PLUGIN_ADMA11_TX, + TEGRA210_ADSP_PLUGIN_ADMA12_TX, + TEGRA210_ADSP_PLUGIN_ADMA13_TX, + TEGRA210_ADSP_PLUGIN_ADMA14_TX, + TEGRA210_ADSP_PLUGIN_ADMA15_TX, + + TEGRA210_ADSP_PLUGIN1, + TEGRA210_ADSP_PLUGIN2, + TEGRA210_ADSP_PLUGIN3, + TEGRA210_ADSP_PLUGIN4, + TEGRA210_ADSP_PLUGIN5, + TEGRA210_ADSP_PLUGIN6, + TEGRA210_ADSP_PLUGIN7, + TEGRA210_ADSP_PLUGIN8, + TEGRA210_ADSP_PLUGIN9, + TEGRA210_ADSP_PLUGIN10, + TEGRA210_ADSP_PLUGIN11, + TEGRA210_ADSP_PLUGIN12, + TEGRA210_ADSP_PLUGIN13, + TEGRA210_ADSP_PLUGIN14, + TEGRA210_ADSP_PLUGIN15, + TEGRA210_ADSP_PLUGIN16, + TEGRA210_ADSP_PLUGIN17, + TEGRA210_ADSP_PLUGIN18, + TEGRA210_ADSP_PLUGIN19, + TEGRA210_ADSP_PLUGIN20, + + TEGRA210_ADSP_VIRT_REG_MAX, +}; + +/* Supports widget id 0x0 - 0xFF */ +#define TEGRA210_ADSP_WIDGET_SOURCE_SHIFT 0 +#define TEGRA210_ADSP_WIDGET_SOURCE_MASK (0xff << \ + TEGRA210_ADSP_WIDGET_SOURCE_SHIFT) + +#define TEGRA210_ADSP_WIDGET_EN_SHIFT 31 +#define TEGRA210_ADSP_WIDGET_EN_MASK (0x1 << \ + TEGRA210_ADSP_WIDGET_EN_SHIFT) + +/* TODO : Check if we can remove these macros */ +#define ADSP_FE_START TEGRA210_ADSP_FRONT_END1 +#define ADSP_FE_END TEGRA210_ADSP_FRONT_END15 +#define ADSP_ADMAIF_START TEGRA210_ADSP_ADMAIF1 +#define ADSP_ADMAIF_END TEGRA210_ADSP_ADMAIF20 +#define ADSP_EAVB_START TEGRA210_ADSP_EAVB +#define ADSP_FE_COUNT ADSP_EAVB_START +#define APM_IN_START TEGRA210_ADSP_APM_IN1 +#define APM_IN_END TEGRA210_ADSP_APM_IN15 +#define APM_OUT_START TEGRA210_ADSP_APM_OUT1 +#define APM_OUT_END TEGRA210_ADSP_APM_OUT15 +#define ADMA_START TEGRA210_ADSP_PLUGIN_ADMA1 +#define ADMA_END TEGRA210_ADSP_PLUGIN_ADMA15 +#define ADMA_TX_START TEGRA210_ADSP_PLUGIN_ADMA1_TX +#define ADMA_TX_END TEGRA210_ADSP_PLUGIN_ADMA15_TX +#define PLUGIN_START TEGRA210_ADSP_PLUGIN1 +#define PLUGIN_END TEGRA210_ADSP_PLUGIN20 +#define PLUGIN_NUM ((PLUGIN_END - PLUGIN_START) + 1) + +#define IS_APM_IN(reg) ((reg >= APM_IN_START) && (reg <= APM_IN_END)) +#define IS_APM_OUT(reg) ((reg >= APM_OUT_START) && (reg <= APM_OUT_END)) +#define IS_APM(reg) (IS_APM_IN(reg) | IS_APM_OUT(reg)) +#define IS_PLUGIN(reg) ((reg >= PLUGIN_START) && (reg <= PLUGIN_END)) +#define IS_ADMA_RX(reg) ((reg >= ADMA_START) && (reg <= ADMA_END)) +#define IS_ADMA_TX(reg) ((reg >= ADMA_TX_START) && (reg <= ADMA_TX_END)) +#define IS_ADMA(reg) (IS_ADMA_RX(reg) || IS_ADMA_TX(reg)) +#define IS_ADSP_APP(reg) (IS_APM(reg) | IS_PLUGIN(reg) | IS_ADMA(reg)) +#define IS_ADSP_FE(reg) (((reg >= ADSP_FE_START) \ + && (reg <= ADSP_FE_END)) || \ + (reg == ADSP_EAVB_START)) +#define IS_ADSP_ADMAIF(reg) ((reg >= ADSP_ADMAIF_START) \ + && (reg <= ADSP_ADMAIF_END)) + +/* ADSP_MSG_FLAGs */ +#define TEGRA210_ADSP_MSG_FLAG_SEND 0x0 +#define TEGRA210_ADSP_MSG_FLAG_HOLD 0x1 +#define TEGRA210_ADSP_MSG_FLAG_NEED_ACK 0x2 + +/* TODO : Remove hard-coding and get data from DTS */ +#define TEGRA210_ADSP_ADMA_CHANNEL_START 10 +#define TEGRA210_ADSP_ADMA_CHANNEL_COUNT 10 +#define TEGRA210_ADSP_ADMA_BITMAP_COUNT 64 +#define TEGRA210_MAX_ADMA_CHANNEL 22 +#define TEGRA186_MAX_ADMA_CHANNEL 32 + + +#define TEGRA210_ADSP_ADMA_CHANNEL_START_HV 16 + +/* ADSP base index for widget name update */ + +#define TEGRA210_ADSP_ROUTE_BASE ((TEGRA210_ADSP_ADMAIF20 * 18) + \ + (15 * TEGRA210_ADSP_APM_OUT1)) +#define TEGRA210_ADSP_WIDGET_BASE \ + ((TEGRA210_ADSP_ADMAIF20 * 3) + \ + (TEGRA210_ADSP_PLUGIN1 - TEGRA210_ADSP_APM_IN1) * 2) + +#define IS_MMAP_ACCESS(access) \ + ((access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) || \ + (access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) || \ + (access == SNDRV_PCM_ACCESS_MMAP_COMPLEX)) +#endif + +#define ADSP_BACKEND_TO_ADMAIF(be_reg) (be_reg - ADSP_ADMAIF_START) diff --git a/sound/soc/tegra-virt-alt/tegra210_adsp_virt_alt.c b/sound/soc/tegra-virt-alt/tegra210_adsp_virt_alt.c new file mode 100644 index 00000000..bad88c41 --- /dev/null +++ b/sound/soc/tegra-virt-alt/tegra210_adsp_virt_alt.c @@ -0,0 +1,5362 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra_isomgr_bw_alt.h" + +#include "tegra_asoc_utils_alt.h" +#include "tegra210_adsp_virt_alt.h" +#include "tegra210_virt_alt_admaif.h" +#include "tegra_virt_alt_ivc.h" + +#define DRV_NAME_ADSP "tegra210-adsp-virt" + +/* Flag to enable/disable loading of ADSP firmware */ +#define ENABLE_ADSP 1 + +/* Expected to map with SNDRV_CTL_ELEM_TYPE_INTEGER value*/ +#define TEGRA_SNDRV_CTL_ELEM_TYPE_INTEGER 2 + +#define NETLINK_ADSP_EVENT 31 +#define NETLINK_ADSP_EVENT_GROUP 1 + +struct adsp_event_nlmsg { + uint32_t err; + uint32_t data[NVFX_MAX_CALL_PARAMS_WSIZE]; +}; + +struct tegra_soc_bytes { + int base; + int num_regs; + snd_ctl_elem_type_t mask; +}; + +#define ADSP_RESPONSE_TIMEOUT 1000 /* in ms */ +/* ADSP controls plugin index */ +#define PLUGIN_SET_PARAMS_IDX 1 +#define PLUGIN_SEND_BYTES_IDX 21 + +#define DEFAULT_NUM_CHANNELS (2) +#define DEFAULT_RATE (48000) + +static const unsigned int tegra210_adsp_rates[] = { + 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000 +}; + +static const struct snd_pcm_hw_constraint_list + tegra210_adsp_rate_constraints = { + .count = ARRAY_SIZE(tegra210_adsp_rates), + .list = tegra210_adsp_rates, +}; + +static struct tegra210_adsp_app_desc { + const char *name; + const char *fw_name; + const char *wt_name; + snd_ctl_elem_type_t param_type; + uint32_t reg_start; + uint32_t reg_end; + nvadsp_app_handle_t handle; +} adsp_app_minimal[] = { + {"apm", "nvapm.elf", NULL, SNDRV_CTL_ELEM_TYPE_NONE, + APM_IN_START, APM_IN_END}, + {"adma", "nvadma.elf", NULL, SNDRV_CTL_ELEM_TYPE_NONE, + ADMA_START, ADMA_END}, + {"adma_tx", "nvadma_tx.elf", NULL, SNDRV_CTL_ELEM_TYPE_NONE, + ADMA_TX_START, ADMA_TX_END}, +}; + +static struct tegra210_adsp_app_desc *adsp_app_desc; +static unsigned int adsp_app_count; /* total number of apps initialized */ +static int apm_stack_size[APM_IN_END - APM_IN_START + 1]; + +struct tegra210_adsp_app_read_data { + int32_t data[NVFX_MAX_RAW_DATA_WSIZE]; +}; + +/* ADSP APP specific structure */ +struct tegra210_adsp_app { + struct tegra210_adsp *adsp; + const struct tegra210_adsp_app_desc *desc; + nvadsp_app_info_t *info; + plugin_shared_mem_t *plugin; + apm_shared_state_t *apm; /* For a plugin it stores parent apm data */ + struct nvadsp_mbox apm_mbox; + struct completion *msg_complete; /* For ADSP ack wait */ + struct completion *raw_msg_read_complete; + struct completion *raw_msg_write_complete; + uint32_t reg; + uint32_t adma_chan; /* Valid for only ADMA app */ + uint32_t fe:1; /* Whether the app is used as a FE APM */ + /* + * if app is playback FE, indicates whether in triggered state + * or inactive + */ + int32_t fe_playback_triggered; + uint32_t connect:1; /* if app is connected to a source */ + uint32_t priority; /* Valid for only APM app */ + uint32_t min_adsp_clock; /* Min ADSP clock required in MHz */ + uint32_t input_mode; /* APM input mode */ + struct tegra210_adsp_app_read_data read_data; + spinlock_t lock; + void *private_data; + int (*msg_handler)(struct tegra210_adsp_app *app, apm_msg_t *msg); + struct work_struct *override_freq_work; + spinlock_t apm_msg_queue_lock; + spinlock_t fe_playback_lock; +}; + +struct tegra210_adsp_pcm_rtd { + struct device *dev; + struct snd_pcm_substream *substream; + struct tegra210_adsp_app *fe_apm; + snd_pcm_uframes_t prev_appl_ptr; +}; + +struct tegra210_adsp_compr_rtd { + struct device *dev; + struct snd_dma_buffer buf; + struct snd_compr_stream *cstream; + struct snd_codec codec; + struct tegra210_adsp_app *fe_apm; + int is_draining; +}; + +struct adsp_soc_data { + bool is_soc_t210; + uint32_t max_adma_ch; +}; + +struct tegra210_adsp { + struct device *dev; + struct adsp_soc_data *soc_data; + struct tegra210_adsp_app apps[TEGRA210_ADSP_VIRT_REG_MAX]; + atomic_t reg_val[TEGRA210_ADSP_VIRT_REG_MAX]; + DECLARE_BITMAP(adma_usage, TEGRA210_ADSP_ADMA_BITMAP_COUNT); + struct clk *ahub_clk; + struct clk *ape_clk; + struct clk *apb2ape_clk; + struct work_struct override_freq_work; + uint32_t i2s_rate; + struct mutex mutex; + uint32_t init_done; + int adsp_started; + bool is_shutdown; + uint32_t adma_ch_page; + uint32_t adma_ch_start; + uint32_t adma_ch_cnt; + struct tegra210_adsp_path { + uint32_t fe_reg; + uint32_t be_reg; + uint32_t channels; + snd_pcm_format_t format; + uint32_t rate; + } pcm_path[ADSP_FE_COUNT+1][2]; + struct nvaudio_ivc_ctxt *hivc_client; + int32_t fe_to_admaif_map[ADSP_FE_COUNT][2]; + int32_t apm_to_admaif_map[APM_IN_END - APM_IN_START + 1][2]; + bool is_fe_set[ADSP_FE_COUNT]; + struct sock *nl_sk; +}; + +static const struct snd_pcm_hardware adsp_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_DRAIN_TRIGGER, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .period_bytes_min = 128, + .period_bytes_max = PAGE_SIZE * 2, + .periods_min = 1, + .periods_max = 8, + .buffer_bytes_max = PAGE_SIZE * 8, + .fifo_size = 4, +}; + +/* Following structure is ALSA-Compress specific */ +static struct snd_compr_caps + tegra210_adsp_compr_caps[SND_COMPRESS_CAPTURE + 1] = { + [SND_COMPRESS_PLAYBACK] = { + .num_codecs = 2, + .direction = SND_COMPRESS_PLAYBACK, + .min_fragment_size = 1024, + .max_fragment_size = 1024 * 1024, /* 1 MB */ + .min_fragments = 2, + .max_fragments = 1024, + .codecs = { + [0] = SND_AUDIOCODEC_MP3, + [1] = SND_AUDIOCODEC_AAC, + }, + }, + [SND_COMPRESS_CAPTURE] = { + .num_codecs = 0, + .direction = SND_COMPRESS_CAPTURE, + }, +}; + +/* Following structure is ALSA-Compress specific */ +static struct snd_compr_codec_caps adsp_compr_codec_caps[] = { + [SND_AUDIOCODEC_MP3] = { + .codec = SND_AUDIOCODEC_MP3, + .num_descriptors = 1, + .descriptor = { + [0] = { + .max_ch = 2, + .sample_rates = { + [0] = SNDRV_PCM_RATE_8000_48000, + }, + .bit_rate = { + [0] = 32000, + [1] = 64000, + [2] = 128000, + [3] = 256000, + [4] = 320000, + }, + .num_bitrates = 5, + .rate_control = + SND_RATECONTROLMODE_CONSTANTBITRATE | + SND_RATECONTROLMODE_VARIABLEBITRATE, + .profiles = 0, + .modes = SND_AUDIOCHANMODE_MP3_STEREO, + .formats = SND_AUDIOSTREAMFORMAT_UNDEFINED, + .min_buffer = 1024, + }, + }, + }, + [SND_AUDIOCODEC_AAC] = { + .codec = SND_AUDIOCODEC_AAC, + .num_descriptors = 1, + .descriptor = { + [0] = { + .max_ch = 2, + .sample_rates = { + [0] = SNDRV_PCM_RATE_8000_48000, + }, + .bit_rate = { + [0] = 32000, + [1] = 64000, + [2] = 128000, + [3] = 256000, + [4] = 320000, + }, + .num_bitrates = 5, + .rate_control = + SND_RATECONTROLMODE_CONSTANTBITRATE | + SND_RATECONTROLMODE_VARIABLEBITRATE, + .profiles = SND_AUDIOPROFILE_AAC, + .modes = SND_AUDIOMODE_AAC_LC, + .formats = SND_AUDIOSTREAMFORMAT_MP4ADTS, + .min_buffer = 1024, + }, + }, + }, +}; + +static status_t tegra210_adsp_msg_handler(uint32_t msg, void *data); +static int tegra210_adsp_app_default_msg_handler(struct tegra210_adsp_app *app, + apm_msg_t *apm_msg); + +/* + * Utility functions + */ +/* ADSP virtual register read/write functions */ +static uint32_t tegra210_adsp_reg_read(struct tegra210_adsp *adsp, uint32_t reg) +{ + return atomic_read(&adsp->reg_val[reg]); +} + +static void tegra210_adsp_reg_write(struct tegra210_adsp *adsp, + uint32_t reg, uint32_t val) +{ + atomic_set(&adsp->reg_val[reg], val); + dev_vdbg(adsp->dev, "%s : 0x%x -> 0x%x\n", __func__, reg, val); +} + +static void tegra210_adsp_reg_update_bits(struct tegra210_adsp *adsp, + uint32_t reg, uint32_t mask, + uint32_t val) +{ + uint32_t temp; + + temp = tegra210_adsp_reg_read(adsp, reg); + val = (val & mask) | (temp & ~mask); + tegra210_adsp_reg_write(adsp, reg, val); + + dev_vdbg(adsp->dev, "%s : 0x%x -> 0x%x\n", __func__, reg, val); +} + +/* API to find Plugin app from name*/ +static struct tegra210_adsp_app *tegra210_adsp_get_plugin( + struct tegra210_adsp *adsp, + const char *plugin_name) +{ + struct tegra210_adsp_app *app; + int i; + + for (i = PLUGIN_START; i <= PLUGIN_END; i++) { + app = &adsp->apps[i]; + if (!strcmp(app->desc->name, plugin_name)) + return app; + } + return NULL; +} + +/* API to get source widget id connected to a widget */ +static uint32_t tegra210_adsp_get_source(struct tegra210_adsp *adsp, + uint32_t reg) +{ + uint32_t source; + + source = tegra210_adsp_reg_read(adsp, reg); + source &= TEGRA210_ADSP_WIDGET_SOURCE_MASK; + source >>= TEGRA210_ADSP_WIDGET_SOURCE_SHIFT; + + return source; +} +/* ADSP shared memory allocate/free functions */ +static int tegra210_adsp_preallocate_dma_buffer(struct device *dev, size_t size, + struct snd_dma_buffer *buf) +{ + dev_vdbg(dev, "%s : size %d.", __func__, (uint32_t)size); + + buf->area = nvadsp_alloc_coherent(size, &buf->addr, GFP_KERNEL); + if (!buf->area) { + dev_err(dev, "Failed to pre-allocated DMA buffer."); + return -ENOMEM; + } + + buf->bytes = size; + buf->private_data = NULL; + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = dev; + + return 0; +} + +static void tegra210_adsp_deallocate_dma_buffer(struct snd_dma_buffer *buf) +{ + dev_vdbg(buf->dev.dev, "%s : size %d.", __func__, (uint32_t)buf->bytes); + + if (!buf->area) + return; + + nvadsp_free_coherent(buf->bytes, buf->area, buf->addr); + buf->area = NULL; + buf->addr = 0; +} + + +/* ADSP OS boot and init API */ +static int tegra210_adsp_init(struct tegra210_adsp *adsp) +{ + int i, ret = 0; + + mutex_lock(&adsp->mutex); + ret = nvadsp_os_load(); + if (ret < 0) { + dev_err(adsp->dev, "Failed to load OS."); + goto exit; + } + + if (nvadsp_os_start()) { + dev_err(adsp->dev, "Failed to start OS"); + goto exit; + } + + /* Load ADSP audio apps */ + for (i = 0; i < adsp_app_count; i++) { + adsp_app_desc[i].handle = nvadsp_app_load( + adsp_app_desc[i].name, + adsp_app_desc[i].fw_name); + if (!adsp_app_desc[i].handle) { + dev_err(adsp->dev, "Failed to load app %s", + adsp_app_desc[i].name); + } + } + + /* TODO: set callback function for adsp to dump adma registers for debug */ + + /* Suspend OS for now. Resume will happen via runtime pm calls */ + ret = nvadsp_os_suspend(); + if (ret < 0) { + dev_err(adsp->dev, "Failed to suspend OS."); + goto exit; + } + + adsp->init_done = 1; + +exit: + mutex_unlock(&adsp->mutex); + return ret; +} + +static void tegra210_adsp_deinit(struct tegra210_adsp *adsp) +{ + mutex_lock(&adsp->mutex); + if (adsp->init_done) { + nvadsp_os_stop(); + adsp->init_done = 0; + } + mutex_unlock(&adsp->mutex); +} + +/* ADSP-CPU message send-receive utility functions */ +static int tegra210_adsp_get_msg(apm_shared_state_t *apm, apm_msg_t *apm_msg) +{ + apm_msg->msgq_msg.size = MSGQ_MSG_WSIZE(apm_msg_t) - + MSGQ_MESSAGE_HEADER_WSIZE; + return msgq_dequeue_message(&apm->msgq_send.msgq, + &apm_msg->msgq_msg); +} + +static int tegra210_adsp_get_raw_data_msg(apm_shared_state_t *apm, + apm_raw_data_msg_t *apm_msg) +{ + apm_msg->msgq_msg.size = MSGQ_MSG_WSIZE(apm_raw_data_msg_t) - + MSGQ_MESSAGE_HEADER_WSIZE; + + return msgq_dequeue_message(&apm->msgq_send.msgq, + &apm_msg->msgq_msg); +} + +static int tegra210_adsp_send_msg(struct tegra210_adsp_app *app, + apm_msg_t *apm_msg, uint32_t flags) +{ + int ret = 0; + unsigned long flag; + + if (flags & TEGRA210_ADSP_MSG_FLAG_NEED_ACK) { + if (flags & TEGRA210_ADSP_MSG_FLAG_HOLD) { + pr_err("%s: ACK requires FLAG_SEND, ignoring\n", + __func__); + flags &= ~TEGRA210_ADSP_MSG_FLAG_NEED_ACK; + } else { + apm_msg->msg.call_params.method |= + NVFX_APM_METHOD_ACK_BIT; + } + } + + if (!IS_APM_IN(app->reg)) { + uint32_t source; + + while (IS_ADSP_APP(app->reg) && !IS_APM_IN(app->reg)) { + source = tegra210_adsp_get_source(app->adsp, app->reg); + app = &app->adsp->apps[source]; + } + if (!IS_APM_IN(app->reg)) { + pr_err("%s: No APM found, skip msg sending\n", + __func__); + return ret; + } + } + + spin_lock_irqsave(&app->apm_msg_queue_lock, flag); + ret = msgq_queue_message(&app->apm->msgq_recv.msgq, &apm_msg->msgq_msg); + spin_unlock_irqrestore(&app->apm_msg_queue_lock, flag); + if (ret < 0) { + /* Wakeup APM to consume messages and give it some time */ + ret = nvadsp_mbox_send(&app->apm_mbox, apm_cmd_msg_ready, + NVADSP_MBOX_SMSG, false, 0); + if (ret) { + pr_err("%s: Failed to send mailbox message id %d ret %d\n", + __func__, app->apm->mbox_id, ret); + } + mdelay(20); + /* Attempt queueing again */ + spin_lock_irqsave(&app->apm_msg_queue_lock, flag); + ret = msgq_queue_message(&app->apm->msgq_recv.msgq, + &apm_msg->msgq_msg); + spin_unlock_irqrestore(&app->apm_msg_queue_lock, flag); + if (ret < 0) { + pr_err("%s: Failed to queue message ret %d \ + rd %d and wr %d pointer %p mbox_id %d\n", + __func__, ret, + app->apm->msgq_recv.msgq.read_index, + app->apm->msgq_recv.msgq.write_index, + &app->apm->msgq_recv.msgq, app->apm->mbox_id); + return ret; + } + } + + if (flags & TEGRA210_ADSP_MSG_FLAG_HOLD) + return 0; + + ret = nvadsp_mbox_send(&app->apm_mbox, apm_cmd_msg_ready, + NVADSP_MBOX_SMSG, false, 0); + if (ret) { + pr_err("%s: Failed to send mailbox message id %d ret %d\n", + __func__, app->apm->mbox_id, ret); + } + if (!(flags & TEGRA210_ADSP_MSG_FLAG_NEED_ACK)) + return ret; + + ret = wait_for_completion_interruptible_timeout( + app->msg_complete, + msecs_to_jiffies(ADSP_RESPONSE_TIMEOUT)); + if (WARN_ON(ret == 0)) + pr_err("%s: ACK timed out %d rd %d wr %d mbox_id %d \ + msg_q 0x%p\n", __func__, app->reg, + app->apm->msgq_recv.msgq.read_index, + app->apm->msgq_recv.msgq.write_index, + app->apm->mbox_id, &app->apm->msgq_recv.msgq); + return ret; +} + +static int tegra210_adsp_send_raw_data_msg(struct tegra210_adsp_app *app, + apm_raw_data_msg_t *apm_msg) +{ + int ret = 0; + struct tegra210_adsp_app *apm = app; + unsigned long flag; + + /* Find parent APM to wait for ACK*/ + if (!IS_APM_IN(apm->reg)) { + uint32_t source; + + while (IS_ADSP_APP(apm->reg) && !IS_APM_IN(apm->reg)) { + source = tegra210_adsp_get_source(apm->adsp, apm->reg); + apm = &apm->adsp->apps[source]; + } + if (!IS_APM_IN(apm->reg)) { + pr_err("%s: No APM found, skip ACK wait\n", __func__); + return ret; + } + } + apm_msg->msg.call_params.method |= NVFX_APM_METHOD_ACK_BIT; + + spin_lock_irqsave(&apm->apm_msg_queue_lock, flag); + ret = msgq_queue_message(&app->apm->msgq_recv.msgq, &apm_msg->msgq_msg); + spin_unlock_irqrestore(&apm->apm_msg_queue_lock, flag); + if (ret < 0) { + pr_err("%s failed: msgq full\n", __func__); + return ret; + } + + ret = nvadsp_mbox_send(&app->apm_mbox, apm_cmd_raw_data_ready, + NVADSP_MBOX_SMSG, true, 100); + if (ret) { + pr_err("Failed to send mailbox message id %d ret %d\n", + app->apm->mbox_id, ret); + } + + ret = wait_for_completion_interruptible_timeout( + apm->raw_msg_write_complete, + msecs_to_jiffies(ADSP_RESPONSE_TIMEOUT)); + if (WARN_ON(ret == 0)) + pr_err("%s: ACK timed out %d\n", __func__, app->reg); + + return ret; +} + +static int tegra210_adsp_send_remove_msg(struct tegra210_adsp_app *app, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_remove_params_t); + apm_msg.msg.call_params.size = sizeof(apm_fx_remove_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_fx_remove_all; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_connect_msg(struct tegra210_adsp_app *src, + struct tegra210_adsp_app *dst, + uint32_t flags) +{ + apm_msg_t apm_msg; + + if ((src == NULL) || (dst == NULL)) { + pr_err("%s: SRC = %p or DST = %p is NULL\n", + __func__, src, dst); + return -1; + } + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_connect_params_t); + apm_msg.msg.call_params.size = sizeof(apm_fx_connect_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_fx_connect; + apm_msg.msg.fx_connect_params.plugin_src.pvoid = IS_APM_IN(src->reg) ? + NULL : src->plugin->plugin.pvoid; + apm_msg.msg.fx_connect_params.pin_src = 0; + apm_msg.msg.fx_connect_params.plugin_dst.pvoid = IS_APM_OUT(dst->reg) ? + NULL : dst->plugin->plugin.pvoid; + apm_msg.msg.fx_connect_params.pin_dst = 0; + + return tegra210_adsp_send_msg(src, &apm_msg, flags); +} + +static int tegra210_adsp_send_io_buffer_msg(struct tegra210_adsp_app *app, + dma_addr_t addr, size_t size, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_io_buffer_params_t); + apm_msg.msg.call_params.size = sizeof(apm_io_buffer_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_set_io_buffer; + apm_msg.msg.io_buffer_params.pin_type = IS_APM_IN(app->reg) ? + NVFX_PIN_TYPE_INPUT : NVFX_PIN_TYPE_OUTPUT; + apm_msg.msg.io_buffer_params.pin_id = 0; + apm_msg.msg.io_buffer_params.addr.ptr = (uint64_t)addr; + apm_msg.msg.io_buffer_params.size = size; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_period_size_msg(struct tegra210_adsp_app *app, + size_t size, uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = + MSGQ_MSG_WSIZE(apm_notification_params_t); + apm_msg.msg.call_params.size = + sizeof(apm_notification_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_set_notification_size; + apm_msg.msg.notification_params.pin_type = IS_APM_IN(app->reg) ? + NVFX_PIN_TYPE_INPUT : NVFX_PIN_TYPE_OUTPUT; + apm_msg.msg.notification_params.pin_id = 0; + apm_msg.msg.notification_params.size = size; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_adma_params_msg(struct tegra210_adsp_app *app, + nvfx_adma_init_params_t *params, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_set_param_params_t); + apm_msg.msg.call_params.size = sizeof(apm_fx_set_param_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_fx_set_param; + apm_msg.msg.fx_set_param_params.plugin.pvoid = + app->plugin->plugin.pvoid; + + params->call_params.size = sizeof(nvfx_adma_init_params_t); + params->call_params.method = nvfx_adma_method_init; + memcpy(&apm_msg.msg.fx_set_param_params.params, params, + sizeof(*params)); + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_eavbdma_params_msg(struct tegra210_adsp_app *app, + nvfx_eavbdma_init_params_t *params, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_set_param_params_t); + apm_msg.msg.call_params.size = sizeof(apm_fx_set_param_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_fx_set_param; + apm_msg.msg.fx_set_param_params.plugin.pvoid = + app->plugin->plugin.pvoid; + + params->call_params.size = sizeof(nvfx_eavbdma_init_params_t); + params->call_params.method = nvfx_eavbdma_method_init; + memcpy(&apm_msg.msg.fx_set_param_params.params, params, + sizeof(*params)); + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static void tegra_adsp_override_freq_worker(struct work_struct *work) +{ + adsp_override_freq(INT_MAX); +} + +static int tegra210_adsp_send_state_msg(struct tegra210_adsp_app *app, + int32_t state, uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(nvfx_set_state_params_t); + apm_msg.msg.call_params.size = sizeof(nvfx_set_state_params_t); + apm_msg.msg.call_params.method = nvfx_method_set_state; + apm_msg.msg.state_params.state = state; + + /* Spike ADSP freq to max when app transitions to active */ + /* state; DFS will thereafter find appropriate rate */ + if ((state == nvfx_state_active) && (app->override_freq_work != NULL)) + schedule_work(app->override_freq_work); + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_flush_msg(struct tegra210_adsp_app *app, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(nvfx_flush_params_t); + apm_msg.msg.call_params.size = sizeof(nvfx_flush_params_t); + apm_msg.msg.call_params.method = nvfx_method_flush; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_reset_msg(struct tegra210_adsp_app *app, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(nvfx_reset_params_t); + apm_msg.msg.call_params.size = sizeof(nvfx_reset_params_t); + apm_msg.msg.call_params.method = nvfx_method_reset; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_eos_msg(struct tegra210_adsp_app *app, + uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_eos_params_t); + apm_msg.msg.call_params.size = sizeof(apm_eos_params_t); + apm_msg.msg.call_params.method = nvfx_method_set_eos; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_pos_msg(struct tegra210_adsp_app *app, + uint32_t pos, uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_position_params_t); + apm_msg.msg.call_params.size = sizeof(apm_position_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_set_position; + apm_msg.msg.position_params.pin_type = IS_APM_IN(app->reg) ? + NVFX_PIN_TYPE_INPUT : NVFX_PIN_TYPE_OUTPUT; + apm_msg.msg.position_params.pin_id = 0; + apm_msg.msg.position_params.offset = pos; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_data_request_msg(struct tegra210_adsp_app *app, + uint32_t size, uint32_t flags) +{ + apm_msg_t apm_msg; + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_read_request_params_t); + apm_msg.msg.call_params.size = sizeof(apm_fx_read_request_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_read_data; + apm_msg.msg.fx_read_request_params.plugin.pvoid = + app->plugin->plugin.pvoid; + apm_msg.msg.fx_read_request_params.req_size = size; + + return tegra210_adsp_send_msg(app, &apm_msg, flags); +} + +static int tegra210_adsp_send_app_priority(struct tegra210_adsp_app *app) +{ + apm_msg_t msg; + + msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_set_priority_params_t); + msg.msg.call_params.size = sizeof(apm_set_priority_params_t); + msg.msg.call_params.method = nvfx_apm_method_set_priority; + msg.msg.priority_params.priority = app->priority; + + return tegra210_adsp_send_msg(app, &msg, TEGRA210_ADSP_MSG_FLAG_SEND | + TEGRA210_ADSP_MSG_FLAG_NEED_ACK); +} + +static int tegra210_adsp_send_app_inputmode(struct tegra210_adsp_app *app) +{ + apm_msg_t msg; + + msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_set_input_mode_params_t); + msg.msg.call_params.size = sizeof(apm_set_input_mode_params_t); + msg.msg.call_params.method = nvfx_apm_method_set_input_mode; + msg.msg.input_mode_params.mode = app->input_mode; + + return tegra210_adsp_send_msg(app, &msg, TEGRA210_ADSP_MSG_FLAG_SEND | + TEGRA210_ADSP_MSG_FLAG_NEED_ACK); +} + +/* ADSP app init/de-init APIs */ +static int tegra210_adsp_app_init(struct tegra210_adsp *adsp, + struct tegra210_adsp_app *app) +{ + int ret = 0; + + /* If app is already open or it is APM output pin don't open app */ + if (app->info || IS_APM_OUT(app->reg)) + return 0; + + if (!app->desc->handle) + return -ENODEV; + + app->info = nvadsp_app_init(app->desc->handle, NULL); + if (IS_ERR_OR_NULL(app->info)) { + dev_err(adsp->dev, "Failed to init app %s(%s).", + app->desc->name, app->desc->fw_name); + return -ENODEV; + } + + dev_info(adsp->dev, "apm init app %s done\n", app->desc->name); + + spin_lock_init(&app->lock); + spin_lock_init(&app->apm_msg_queue_lock); + spin_lock_init(&app->fe_playback_lock); + + app->adsp = adsp; + app->msg_handler = tegra210_adsp_app_default_msg_handler; + app->override_freq_work = &adsp->override_freq_work; + app->plugin = PLUGIN_SHARED_MEM(app->info->mem.shared); + if (IS_APM_IN(app->reg)) { + uint32_t apm_out_reg = APM_OUT_START + + (app->reg - APM_IN_START); + struct tegra210_adsp_app *apm_out = &adsp->apps[apm_out_reg]; + + app->apm = APM_SHARED_STATE(app->info->mem.shared); + app->info->stack_size = apm_stack_size[app->reg - APM_IN_START]; + + ret = nvadsp_mbox_open(&app->apm_mbox, + &app->apm->mbox_id, + app->desc->name, tegra210_adsp_msg_handler, app); + if (ret < 0) { + dev_err(adsp->dev, "Failed to open mailbox %s(%s).", + app->desc->name, app->desc->fw_name); + goto err_app_exit; + } + + app->msg_complete = devm_kzalloc(adsp->dev, + sizeof(struct completion), GFP_KERNEL); + if (!app->msg_complete) { + dev_err(adsp->dev, "Failed to allocate completion struct."); + return -ENOMEM; + } + + app->raw_msg_read_complete = devm_kzalloc(adsp->dev, + sizeof(struct completion), GFP_KERNEL); + + if (app->raw_msg_read_complete == NULL) { + dev_err(adsp->dev, + "Failed to allocate read completion struct."); + return -ENOMEM; + } + + app->raw_msg_write_complete = devm_kzalloc(adsp->dev, + sizeof(struct completion), GFP_KERNEL); + + if (app->raw_msg_write_complete == NULL) { + dev_err(adsp->dev, + "Failed to allocate read completion struct."); + return -ENOMEM; + } + init_completion(app->msg_complete); + init_completion(app->raw_msg_read_complete); + init_completion(app->raw_msg_write_complete); + + ret = nvadsp_app_start(app->info); + if (ret < 0) { + dev_err(adsp->dev, "Failed to start adsp app"); + goto err_mbox_close; + } + dev_info(adsp->dev, "apm start app %d msg_q 0x%p\n", + app->apm->mbox_id, &app->apm->msgq_recv.msgq); + /* Copy APM IN app data to APM OUT app */ + apm_out->info = app->info; + apm_out->plugin = app->plugin; + apm_out->apm = app->apm; + apm_out->adsp = app->adsp; + apm_out->apm_mbox = app->apm_mbox; + apm_out->msg_complete = app->msg_complete; + apm_out->raw_msg_read_complete = app->raw_msg_read_complete; + apm_out->raw_msg_write_complete = app->raw_msg_write_complete; + + ret = tegra210_adsp_send_app_priority(app); + if (ret < 0) { + dev_err(adsp->dev, "Failed to send app priority!\n"); + goto err_mbox_close; + } + + ret = tegra210_adsp_send_app_inputmode(app); + if (ret < 0) { + dev_err(adsp->dev, "Failed to send app input mode!\n"); + goto err_mbox_close; + } + } else if (IS_ADMA(app->reg)) { + app->adma_chan = find_first_zero_bit(adsp->adma_usage, + adsp->adma_ch_cnt); + if (app->adma_chan >= adsp->adma_ch_cnt) { + dev_err(adsp->dev, "All ADMA channels are busy"); + return -EBUSY; + } + __set_bit(app->adma_chan, adsp->adma_usage); + app->adma_chan += adsp->adma_ch_start; + } + return 0; + +err_mbox_close: + nvadsp_mbox_close(&app->apm_mbox); +err_app_exit: + app->info = NULL; + return 0; +} + +static void tegra210_adsp_app_deinit(struct tegra210_adsp *adsp, + struct tegra210_adsp_app *app) +{ + /* TODO: + * Current usecases use static paths so app_deinit functionality + * is not needed and adds overhead of freeing and allocating apps + * everytime. Add app deinit functionality properly if needed in + * future. + */ +} + +/* API to connect two APMs */ +static int tegra210_adsp_connect_apm(struct tegra210_adsp *adsp, + struct tegra210_adsp_app *app) +{ + uint32_t source = tegra210_adsp_get_source(adsp, app->reg); + struct tegra210_adsp_app *src = &adsp->apps[source]; + int ret = 0; + + // If both APMs are in connected state no need to send connect message + if (app->connect && src->connect) + return 0; + + dev_vdbg(adsp->dev, "Connecting APM 0x%x -> 0x%x", + src->reg, app->reg); + + ret = tegra210_adsp_send_connect_msg(src, app, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(adsp->dev, "Connect msg failed. err %d.", ret); + return ret; + } + return 1; +} + +/* + * Recursive function to connect plugins under a APM. + * Returns BE/FE on the pcm path + */ +static int tegra210_adsp_connect_plugin(struct tegra210_adsp *adsp, + struct tegra210_adsp_app *app, + uint32_t *apm_in_src) +{ + struct tegra210_adsp_app *src; + uint32_t source; + int ret = 0; + + source = tegra210_adsp_get_source(adsp, app->reg); + if (!IS_ADSP_APP(source)) + return -ENODEV; + + src = &adsp->apps[source]; + if (!IS_APM_IN(src->reg)) { + ret = tegra210_adsp_connect_plugin(adsp, src, apm_in_src); + if (ret < 0) + return ret; + } else { + source = tegra210_adsp_get_source(adsp, src->reg); + if (IS_APM_OUT(source)) { + /* connect plugins inside next APM */ + ret = tegra210_adsp_connect_plugin(adsp, + &adsp->apps[source], apm_in_src); + if (ret < 0) + return ret; + /* connect APM_IN to APM_OUT */ + ret = tegra210_adsp_connect_apm(adsp, src); + if (ret < 0) + return ret; + } else { + /* return if APM_IN is not connected to valid inputs */ + if (!IS_ADSP_FE(source) && + !IS_ADSP_ADMAIF(source)) + return -ENODEV; + if (apm_in_src) + *apm_in_src = source; + } + } + app->apm = src->apm; + app->apm_mbox = src->apm_mbox; + + /* + * If App is already connected and source connections have not changed, + * no need to again send connect message + */ + if (!ret && app->connect) + return 0; + + dev_vdbg(adsp->dev, "Connecting plugin 0x%x -> 0x%x", + src->reg, app->reg); + + ret = tegra210_adsp_send_connect_msg(src, app, + TEGRA210_ADSP_MSG_FLAG_SEND | TEGRA210_ADSP_MSG_FLAG_NEED_ACK); + if (ret < 0) { + dev_err(adsp->dev, "Connect msg failed. err %d.", ret); + return ret; + } + app->connect = 1; + + /* return 1 if new connection is established */ + return 1; +} + +/* Manages FE/BE plugins and deletes if fe_apm is specified */ +static void tegra210_adsp_manage_plugin(struct tegra210_adsp *adsp, + uint32_t end_reg, uint32_t apm_out, + struct tegra210_adsp_app *fe_apm) +{ + uint32_t j, fe_reg, be_reg; + + if (IS_ADSP_FE(end_reg)) { + /* manage playback path */ + fe_reg = end_reg; + be_reg = 0; + for (j = ADSP_ADMAIF_START; j <= ADSP_ADMAIF_END; j++) { + if (tegra210_adsp_get_source(adsp, j) == apm_out) { + be_reg = j; + break; + } + } + if (be_reg && fe_reg) { + if (fe_apm) { + dev_vdbg(adsp->dev, "Remove playback FE %d -- BE %d pair", + fe_reg, be_reg); + tegra210_adsp_send_remove_msg(fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].fe_reg = 0; + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].be_reg = 0; + } else { + dev_vdbg(adsp->dev, "Found playback FE %d -- BE %d pair", + fe_reg, be_reg); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].fe_reg = fe_reg; + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].be_reg = be_reg; + } + } + } else if (IS_ADSP_ADMAIF(end_reg)) { + /* manage record path */ + fe_reg = 0; + be_reg = end_reg; + for (j = ADSP_FE_START; j <= ADSP_FE_END; j++) { + if (tegra210_adsp_get_source(adsp, j) == apm_out) { + fe_reg = j; + break; + } + } + if (be_reg && fe_reg) { + if (fe_apm) { + dev_vdbg(adsp->dev, "Remove record FE %d -- BE %d pair", + fe_reg, be_reg); + tegra210_adsp_send_remove_msg(fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].fe_reg = 0; + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].be_reg = 0; + } else { + dev_vdbg(adsp->dev, "Found playback FE %d -- BE %d pair", + fe_reg, be_reg); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].fe_reg = fe_reg; + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].be_reg = be_reg; + } + } + } +} + +/* Iterate over all APMs and establish pending connections */ +static int tegra210_adsp_update_connection(struct tegra210_adsp *adsp) +{ + int i, ret; + uint32_t end_reg; + + for (i = APM_OUT_START; i <= APM_OUT_END; i++) { + ret = tegra210_adsp_connect_plugin(adsp, &adsp->apps[i], + &end_reg); + if (ret >= 0) { + /* Record FE/BE pair for every successful connection */ + tegra210_adsp_manage_plugin(adsp, end_reg, i, NULL); + } + } + + return 0; +} + +/* Remove the plugin connections inside associated APM */ +static int tegra210_adsp_remove_connection(struct tegra210_adsp *adsp, + struct tegra210_adsp_app *plugin) +{ + struct tegra210_adsp_app *app; + uint32_t i, source, apm_out = TEGRA210_ADSP_NONE; + + if (!IS_ADSP_APP(plugin->reg)) + return 0; + + for (i = APM_OUT_START; i <= APM_OUT_END; i++) { + app = &adsp->apps[i]; + /* if the path is already broken, do not continue */ + if (!app->connect) + continue; + while (IS_ADSP_APP(app->reg)) { + if (app->reg == plugin->reg) { + apm_out = i; + break; + } + source = tegra210_adsp_get_source(adsp, app->reg); + app = &adsp->apps[source]; + } + if (apm_out != TEGRA210_ADSP_NONE) + break; + } + /* if plugin is not part of any APM, return here */ + if (apm_out == TEGRA210_ADSP_NONE) + return 0; + + /* disconnect the plugins inside APM */ + app = &adsp->apps[apm_out]; + while (!IS_APM_IN(app->reg)) { + source = tegra210_adsp_get_source(adsp, app->reg); + if (!IS_ADSP_APP(source)) + break; + app->connect = 0; + app = &adsp->apps[source]; + } + + /* delete the plugins inside APM */ + if (IS_APM_IN(app->reg)) { + /* clear the FE/BE list */ + tegra210_adsp_manage_plugin(adsp, + tegra210_adsp_get_source(adsp, app->reg), + apm_out, app); + } + return 0; +} + +/* ADSP socket message handlers */ +static void tegra210_adsp_nl_recv_msg(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr *)skb->data; + pr_info("Established client: %d\n", nlh->nlmsg_pid); +} + +static void tegra210_adsp_nl_send_msg(struct tegra210_adsp *adsp, + apm_fx_error_event_params_t *apm_err_msg) +{ + + struct sk_buff *skb; + struct nlmsghdr *nlh; + int res; + struct adsp_event_nlmsg *msg; + + if (adsp->nl_sk == NULL) { + pr_err("socket not created\n"); + return; + } + + skb = nlmsg_new(sizeof(struct adsp_event_nlmsg), 0); + if (!skb) { + pr_err("Failed to allocate new skb\n"); + return; + } + + nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, sizeof(struct adsp_event_nlmsg), + 0); + + /* to mcast group 1<<0 */ + NETLINK_CB(skb).dst_group = NETLINK_ADSP_EVENT_GROUP; + + msg = (struct adsp_event_nlmsg *)nlmsg_data(nlh); + if (msg != NULL) { + msg->err = apm_err_msg->err; + memcpy(msg->data, apm_err_msg->data, + (sizeof(uint32_t) * NVFX_MAX_CALL_PARAMS_WSIZE)); + } + + res = nlmsg_multicast(adsp->nl_sk, skb, 0, NETLINK_ADSP_EVENT_GROUP, 0); + if (res < 0) + pr_err("Error while sending back to user: %d\n", res); +} + +/* ADSP mailbox message handler */ +static status_t tegra210_adsp_msg_handler(uint32_t msg, void *data) +{ + struct tegra210_adsp_app *app = data; + unsigned long flags; + apm_msg_t apm_msg; + int ret = 0; + + spin_lock_irqsave(&app->lock, flags); + + switch (msg) { + case apm_cmd_msg_ready: { + ret = tegra210_adsp_get_msg(app->apm, &apm_msg); + if (ret < 0) { + pr_err("Dequeue failed %d.", ret); + break; + } + + if (app->msg_handler) + ret = app->msg_handler(app, &apm_msg); + } + break; + case apm_cmd_raw_data_ready: { + apm_raw_data_msg_t *raw_msg = + kzalloc(sizeof(apm_raw_data_msg_t), GFP_ATOMIC); + if (!raw_msg) { + ret = -ENOMEM; + break; + } + + ret = tegra210_adsp_get_raw_data_msg(app->apm, raw_msg); + if (ret < 0) { + pr_err("Dequeue failed raw %d.", ret); + kfree(raw_msg); + break; + } + memcpy(app->read_data.data, + raw_msg->msg.fx_raw_data_params.data, + sizeof(app->read_data.data)); + kfree(raw_msg); + complete(app->raw_msg_read_complete); + } + break; + default: + pr_err("Unsupported mailbox msg %d.", msg); + } + + spin_unlock_irqrestore(&app->lock, flags); + return ret; +} + +static int tegra210_adsp_app_default_msg_handler(struct tegra210_adsp_app *app, + apm_msg_t *apm_msg) +{ + switch (apm_msg->msg.call_params.method) { + case nvfx_apm_method_ack: + complete(app->msg_complete); + break; + case nvfx_apm_method_raw_ack: + complete(app->raw_msg_write_complete); + break; + case nvfx_apm_method_fx_error_event: + tegra210_adsp_nl_send_msg(app->adsp, + &apm_msg->msg.fx_error_event_params); + break; + default: + pr_err("Unsupported cmd %d.", apm_msg->msg.call_params.method); + } + return 0; +} + +static int tegra210_adsp_pcm_ack(struct snd_pcm_substream *substream) +{ + struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + size_t pos; + int ret = 0; + + dev_vdbg(prtd->dev, "%s %d", __func__, (int)runtime->control->appl_ptr); + + pos = frames_to_bytes(runtime, + runtime->control->appl_ptr % runtime->buffer_size); + ret = tegra210_adsp_send_pos_msg(prtd->fe_apm, pos, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to send write position."); + return ret; + } + + return ret; +} + +static int tegra210_adsp_component_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return tegra210_adsp_pcm_ack(substream); +} + +static int tegra210_adsp_pcm_msg_handler(struct tegra210_adsp_app *app, + apm_msg_t *apm_msg) +{ + struct tegra210_adsp_pcm_rtd *prtd = app->private_data; + struct snd_pcm_runtime *runtime; + + switch (apm_msg->msg.call_params.method) { + case nvfx_apm_method_set_position: + if (!prtd || !prtd->substream) + return 0; + runtime = prtd->substream->runtime; + snd_pcm_period_elapsed(prtd->substream); + if ((IS_MMAP_ACCESS(runtime->access))) { + if (prtd->prev_appl_ptr != + runtime->control->appl_ptr) { + prtd->prev_appl_ptr = + runtime->control->appl_ptr; + tegra210_adsp_pcm_ack(prtd->substream); + } + } + break; + case nvfx_method_set_eos: + /* Nothing specific to be done here as DRAIN */ + /* is implemented in native PCM driver */ + break; + case nvfx_apm_method_ack: + complete(app->msg_complete); + break; + case nvfx_apm_method_raw_ack: + complete(app->raw_msg_write_complete); + break; + case nvfx_apm_method_fx_error_event: + tegra210_adsp_nl_send_msg(app->adsp, + &apm_msg->msg.fx_error_event_params); + break; + default: + dev_err(prtd->dev, "Unsupported cmd %d.", + apm_msg->msg.call_params.method); + } + return 0; +} + +static int tegra210_adsp_compr_msg_handler(struct tegra210_adsp_app *app, + apm_msg_t *apm_msg) +{ + struct tegra210_adsp_compr_rtd *prtd = app->private_data; + + if (!prtd) { + return 0; + } + + switch (apm_msg->msg.call_params.method) { + case nvfx_apm_method_set_position: + snd_compr_fragment_elapsed(prtd->cstream); + break; + case nvfx_method_set_eos: + if (!prtd->is_draining) { + dev_warn(prtd->dev, "EOS reached before drain"); + break; + } + snd_compr_drain_notify(prtd->cstream); + prtd->is_draining = 0; + break; + case nvfx_apm_method_ack: + complete(app->msg_complete); + break; + case nvfx_apm_method_fx_error_event: + tegra210_adsp_nl_send_msg(app->adsp, + &apm_msg->msg.fx_error_event_params); + break; + default: + dev_err(prtd->dev, "Unsupported cmd %d.", + apm_msg->msg.call_params.method); + } + return 0; +} + +static int tegra_adsp_get_connected_be(struct tegra210_adsp *adsp, + uint32_t fe_reg, + int stream) +{ + int sink = fe_reg; + int i; + + if (stream == SNDRV_PCM_STREAM_CAPTURE) + while ((sink = tegra210_adsp_get_source(adsp, sink)) != 0 && + !(IS_ADSP_ADMAIF(sink))) + continue; + else { + for (i = ADSP_ADMAIF_START; i <= ADSP_ADMAIF_END; i++) { + sink = i; + while ((sink = tegra210_adsp_get_source(adsp, sink)) + != 0 && sink != fe_reg) + continue; + if (sink == fe_reg) + break; + } + if (i > ADSP_ADMAIF_END) + return 0; + sink = i; + } + + if (IS_ADSP_ADMAIF(sink)) + return sink; + return 0; +} + +static int tegra_adsp_get_connected_fe(struct tegra210_adsp *adsp, + uint32_t be_reg, + int stream) +{ + int src = be_reg; + int i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + while ((src = tegra210_adsp_get_source(adsp, src)) != 0 && + !(IS_ADSP_FE(src) || IS_ADSP_ADMAIF(src))) + continue; + else { + for (i = ADSP_FE_START; i <= ADSP_FE_END; i++) { + src = i; + while ((src = tegra210_adsp_get_source(adsp, src)) != 0 + && src != be_reg) + continue; + if (src == be_reg) + break; + } + if (i > ADSP_FE_END) + return 0; + src = i; + } + + if (IS_ADSP_FE(src)) + return src; + return 0; +} + +/* Compress call-back APIs */ +static int tegra210_adsp_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->device->private_data; + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(component); + struct tegra210_adsp_compr_rtd *prtd; + uint32_t fe_reg = rtd->dais[rtd->dai_link->num_cpus]->id + 1; + int ret; + int i; + + dev_vdbg(adsp->dev, "%s : DAI ID %d", __func__, rtd->dais[rtd->dai_link->num_cpus]->id); + + if (!adsp->init_done) + return -ENODEV; + + if (!adsp->pcm_path[fe_reg][cstream->direction].fe_reg || + !adsp->pcm_path[fe_reg][cstream->direction].be_reg) { + dev_err(adsp->dev, "Broken Path%d - FE not linked to BE", + fe_reg); + return -EPIPE; + } + + prtd = devm_kzalloc(adsp->dev, sizeof(struct tegra210_adsp_compr_rtd), + GFP_KERNEL); + if (!prtd) { + dev_err(adsp->dev, "Failed to allocate adsp rtd."); + return -ENOMEM; + } + + /* Find out the APM connected with ADSP-FE DAI */ + for (i = APM_IN_START; i <= APM_IN_END; i++) { + struct tegra210_adsp_app *app = &adsp->apps[i]; + uint32_t source = tegra210_adsp_get_source(adsp, app->reg); + + if (source == fe_reg) { + app->msg_handler = tegra210_adsp_compr_msg_handler; + app->private_data = prtd; + app->fe = 1; + prtd->fe_apm = app; + break; + } + } + + if (!prtd->fe_apm) { + dev_err(adsp->dev, "No FE APM found\n"); + devm_kfree(adsp->dev, prtd); + return -ENODEV; + } + + prtd->cstream = cstream; + prtd->dev = adsp->dev; + cstream->runtime->private_data = prtd; + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + } + return ret; +} + +static int tegra210_adsp_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + unsigned long flags; + + if (!prtd) + return -ENODEV; + + tegra210_adsp_send_reset_msg(prtd->fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND | TEGRA210_ADSP_MSG_FLAG_NEED_ACK); + + pm_runtime_put(prtd->dev); + + tegra210_adsp_deallocate_dma_buffer(&prtd->buf); + + spin_lock_irqsave(&prtd->fe_apm->lock, flags); + + /* Reset msg handler to disable msg processing */ + prtd->fe_apm->msg_handler = tegra210_adsp_app_default_msg_handler; + + spin_unlock_irqrestore(&prtd->fe_apm->lock, flags); + + cstream->runtime->private_data = NULL; + devm_kfree(prtd->dev, prtd); + + return 0; +} + +static int tegra210_adsp_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + int ret = 0; + + if (!prtd) + return -ENODEV; + + dev_vdbg(prtd->dev, "%s codec %d rate %d chan %d frag size %d frag %d", + __func__, params->codec.id, + snd_pcm_rate_bit_to_rate(params->codec.sample_rate), + params->codec.ch_in, params->buffer.fragment_size, + params->buffer.fragments); + + ret = tegra210_adsp_preallocate_dma_buffer(prtd->dev, + params->buffer.fragment_size * params->buffer.fragments, + &prtd->buf); + if (ret < 0) + return ret; + + ret = tegra210_adsp_send_io_buffer_msg(prtd->fe_apm, prtd->buf.addr, + prtd->buf.bytes, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "IO buffer send msg failed. err %d.", ret); + return ret; + } + + ret = tegra210_adsp_send_period_size_msg(prtd->fe_apm, + params->buffer.fragment_size, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Period size send msg failed. err %d.", ret); + return ret; + } + + memcpy(&prtd->codec, ¶ms->codec, sizeof(struct snd_codec)); + return 0; +} + +static int tegra210_adsp_compr_get_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_codec *codec) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + + memcpy(codec, &prtd->codec, sizeof(struct snd_codec)); + return 0; +} + +static int tegra210_adsp_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + int ret = 0; + + dev_vdbg(prtd->dev, "%s : cmd %d", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_active, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state start."); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_active, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state resume"); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_inactive, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state stop"); + return ret; + } + + ret = tegra210_adsp_send_flush_msg(prtd->fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to reset"); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_inactive, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state pause"); + return ret; + } + break; + case SND_COMPR_TRIGGER_DRAIN: + prtd->is_draining = 1; + ret = tegra210_adsp_send_eos_msg(prtd->fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state drain"); + return ret; + } + break; + default: + dev_err(prtd->dev, "Unsupported state."); + return -EINVAL; + } + return 0; +} + +static int tegra210_adsp_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + struct snd_compr_runtime *runtime = cstream->runtime; + void *dstn; + size_t copy; + u64 app_pointer; + + dev_vdbg(prtd->dev, "%s : size %d", __func__, (uint32_t)count); + + if (!count) + return 0; + + app_pointer = div64_u64(runtime->total_bytes_available, + runtime->buffer_size); + app_pointer = runtime->total_bytes_available - + (app_pointer * runtime->buffer_size); + dstn = prtd->buf.area + app_pointer; + + if (count < runtime->buffer_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = runtime->buffer_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->buf.area, buf + copy, count - copy)) + return -EFAULT; + } + tegra210_adsp_send_pos_msg(prtd->fe_apm, + (runtime->total_bytes_available + count) % runtime->buffer_size, + TEGRA210_ADSP_MSG_FLAG_SEND); + + return count; +} + +static int tegra210_adsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + struct tegra210_adsp_app *app = prtd->fe_apm; + nvfx_shared_state_t *shared = &app->apm->nvfx_shared_state; + uint32_t frames_played = + (uint32_t)((shared->output[0].bytes >> 1) / prtd->codec.ch_in); + + tstamp->byte_offset = shared->input[0].bytes % + cstream->runtime->buffer_size; + tstamp->copied_total = shared->input[0].bytes; + tstamp->pcm_frames = frames_played; + /* TODO : calculate IO frames correctly */ + tstamp->pcm_io_frames = frames_played; + tstamp->sampling_rate = + snd_pcm_rate_bit_to_rate(prtd->codec.sample_rate); + + dev_vdbg(prtd->dev, "%s off %d copied %d pcm %d pcm io %d", + __func__, (int)tstamp->byte_offset, (int)tstamp->copied_total, + (int)tstamp->pcm_frames, (int)tstamp->pcm_io_frames); + return 0; +} + +static int tegra210_adsp_compr_get_caps(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + if (cstream->direction == SND_COMPRESS_PLAYBACK) + memcpy(caps, &tegra210_adsp_compr_caps[SND_COMPRESS_PLAYBACK], + sizeof(struct snd_compr_caps)); + else + memcpy(caps, &tegra210_adsp_compr_caps[SND_COMPRESS_CAPTURE], + sizeof(struct snd_compr_caps)); + + return 0; +} + +static int tegra210_adsp_compr_codec_caps(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec_caps) +{ + struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data; + + dev_vdbg(prtd->dev, "%s : codec %d", __func__, codec_caps->codec); + + if (!codec_caps->codec) + codec_caps->codec = prtd->codec.id; + + switch (codec_caps->codec) { + case SND_AUDIOCODEC_MP3: + memcpy(codec_caps, &adsp_compr_codec_caps[SND_AUDIOCODEC_MP3], + sizeof(struct snd_compr_codec_caps)); + return 0; + case SND_AUDIOCODEC_AAC: + memcpy(codec_caps, &adsp_compr_codec_caps[SND_AUDIOCODEC_AAC], + sizeof(struct snd_compr_codec_caps)); + return 0; + default: + dev_err(prtd->dev, "Unsupported codec %d", codec_caps->codec); + return -EINVAL; + } + return 0; +} + +static struct snd_compress_ops tegra210_adsp_compress_ops = { + .open = tegra210_adsp_compr_open, + .free = tegra210_adsp_compr_free, + .set_params = tegra210_adsp_compr_set_params, + .get_params = tegra210_adsp_compr_get_params, + .trigger = tegra210_adsp_compr_trigger, + .pointer = tegra210_adsp_compr_pointer, + .copy = tegra210_adsp_compr_copy, + .get_caps = tegra210_adsp_compr_get_caps, + .get_codec_caps = tegra210_adsp_compr_codec_caps, +}; + +/* PCM APIs */ +static int tegra210_adsp_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *cmpnt = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_pcm_rtd *prtd; + uint32_t fe_reg = rtd->dais[rtd->dai_link->num_cpus]->id + 1; + uint32_t source; + int i, ret = 0; + + dev_vdbg(adsp->dev, "%s", __func__); + if (substream->stream < 0) + return -EINVAL; + + if (!adsp->pcm_path[fe_reg][substream->stream].fe_reg || + !adsp->pcm_path[fe_reg][substream->stream].be_reg) { + dev_err(adsp->dev, "Broken Path%d - FE not linked to BE", + fe_reg); + return -EPIPE; + } + + prtd = devm_kzalloc(adsp->dev, sizeof(struct tegra210_adsp_pcm_rtd), + GFP_KERNEL); + if (!prtd) { + dev_err(adsp->dev, "Failed to allocate adsp rtd."); + return -ENOMEM; + } + + /* Find out the APM connected with ADSP-FE DAI */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = APM_IN_START; i <= APM_IN_END; i++) { + struct tegra210_adsp_app *app = &adsp->apps[i]; + + source = tegra210_adsp_get_source(adsp, app->reg); + if (source == fe_reg) { + prtd->fe_apm = app; + break; + } + } + } else { + source = tegra210_adsp_get_source(adsp, fe_reg); + if (IS_APM_OUT(source)) { + uint32_t apm_in_reg = + APM_IN_START + (source - APM_OUT_START); + adsp->apps[apm_in_reg].msg_handler = + tegra210_adsp_pcm_msg_handler; + adsp->apps[apm_in_reg].private_data = prtd; + prtd->fe_apm = &adsp->apps[source]; + } + } + + if (!prtd->fe_apm) { + dev_err(adsp->dev, "No FE APM found\n"); + devm_kfree(adsp->dev, prtd); + return -ENODEV; + } + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + tegra_adsp_get_connected_be(adsp, fe_reg, + substream->stream) == 0) { + devm_kfree(adsp->dev, prtd); + return -ENODEV; + } + + prtd->fe_apm->msg_handler = tegra210_adsp_pcm_msg_handler; + prtd->fe_apm->private_data = prtd; + prtd->fe_apm->fe = 1; + + /* Set HW params now that initialization is complete */ + snd_soc_set_runtime_hwparams(substream, &adsp_pcm_hardware); + + /* Ensure period size is multiple of 4 */ + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0x4); + if (ret) { + dev_err(adsp->dev, + "failed to set period_size constraint %d\n", ret); + return ret; + } + + /* Ensure buffer size is multiple of 4 */ + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0x4); + if (ret) { + dev_err(adsp->dev, + "failed to set buffer_size constraint %d\n", ret); + return ret; + } + substream->runtime->private_data = prtd; + prtd->substream = substream; + prtd->dev = adsp->dev; + + return 0; +} + +static int tegra210_adsp_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data; + unsigned long flags; + + dev_vdbg(prtd->dev, "%s", __func__); + + tegra_isomgr_adma_setbw(substream, false); + + if (prtd) { + tegra210_adsp_send_reset_msg(prtd->fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND | + TEGRA210_ADSP_MSG_FLAG_NEED_ACK); + + spin_lock_irqsave(&prtd->fe_apm->lock, flags); + + /* Reset msg handler to disable msg processing */ + prtd->fe_apm->msg_handler = + tegra210_adsp_app_default_msg_handler; + + prtd->fe_apm->private_data = NULL; + + spin_unlock_irqrestore(&prtd->fe_apm->lock, flags); + + prtd->fe_apm->fe = 1; + substream->runtime->private_data = NULL; + devm_kfree(prtd->dev, prtd); + } + + return 0; +} + +static int tegra210_adsp_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data; + struct tegra210_adsp_app *apm = prtd->fe_apm; + unsigned long flags; + + spin_lock_irqsave(&apm->fe_playback_lock, flags); + apm->fe_playback_triggered = 0; + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + + tegra_isomgr_adma_setbw(substream, true); + + return 0; +} + +static int tegra210_adsp_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data; + struct snd_dma_buffer *buf = &substream->dma_buffer; + int ret = 0; + + dev_vdbg(prtd->dev, "%s rate %d chan %d bps %d" + "period size %d buffer size %d", + __func__, params_rate(params), params_channels(params), + snd_pcm_format_width(params_format(params)), + params_period_size(params), + params_buffer_bytes(params)); + + ret = tegra210_adsp_send_io_buffer_msg(prtd->fe_apm, buf->addr, + params_buffer_bytes(params), + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) + return ret; + + ret = tegra210_adsp_send_period_size_msg(prtd->fe_apm, + params_buffer_bytes(params)/params_periods(params), + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static int tegra210_adsp_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +} + +static int tegra_ivc_start_playback( + struct tegra210_adsp *adsp, + uint32_t ivc_msg_admaif_id, + bool ack_required) +{ + int err = 0; + struct nvaudio_ivc_msg msg; + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.cmd = NVAUDIO_START_PLAYBACK; + msg.params.dmaif_info.id = ivc_msg_admaif_id; + msg.ack_required = ack_required; + + + if (ack_required) + err = nvaudio_ivc_send_receive(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + else + err = nvaudio_ivc_send_retry(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + return err; +} + +static int tegra_ivc_start_capture( + struct tegra210_adsp *adsp, + int32_t ivc_msg_admaif_id, + bool ack_required) +{ + int err = 0; + struct nvaudio_ivc_msg msg; + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.cmd = NVAUDIO_START_CAPTURE; + msg.ack_required = ack_required; + msg.params.dmaif_info.id = ivc_msg_admaif_id; + + if (ack_required) + err = nvaudio_ivc_send_receive(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + else + err = nvaudio_ivc_send_retry(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + return err; +} + +static int tegra_ivc_stop_playback( + struct tegra210_adsp *adsp, + int32_t ivc_msg_admaif_id, + bool ack_required) +{ + int err = 0; + struct nvaudio_ivc_msg msg; + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.cmd = NVAUDIO_STOP_PLAYBACK; + msg.params.dmaif_info.id = ivc_msg_admaif_id; + msg.ack_required = ack_required; + + if (ack_required) + err = nvaudio_ivc_send_receive(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + else + err = nvaudio_ivc_send_retry(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + return err; +} + +static int tegra_ivc_stop_capture( + struct tegra210_adsp *adsp, + int32_t ivc_msg_admaif_id, + bool ack_required) +{ + int err = 0; + struct nvaudio_ivc_msg msg; + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.cmd = NVAUDIO_STOP_CAPTURE; + msg.params.dmaif_info.id = ivc_msg_admaif_id; + msg.ack_required = ack_required; + + if (ack_required) + err = nvaudio_ivc_send_receive(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + else + err = nvaudio_ivc_send_retry(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + + return err; +} + +/** + * Function that checks for IO to IO path during widget event + * Used to toggle playback/capture through IVC to Audio Server + */ + +static int tegra210_adsp_send_hv_state_msg( + struct tegra210_adsp *adsp, + struct tegra210_adsp_app *app, + int32_t state, + int32_t is_playback) +{ + uint32_t src, i; + int32_t ret = 0; + uint32_t be_to_be_flag = 0; + int32_t apm_in_id = -1, apm_out_id = -1; + int32_t playback_admaif_id = -1, capture_admaif_id = -1; + + struct device *dev = adsp->dev; + struct device_node *node = dev->of_node; + + /* Only applicable for HV configurations */ + if (!of_device_is_compatible(node, "nvidia,tegra210-adsp-audio-hv")) + return 0; + + apm_in_id = app->reg - APM_IN_START; + if (apm_in_id < 0) + return 0; + + /* + * For IO to IO path, APM-IN input should be ADSP-ADMAIF + * and APM-OUT output should be connected to ADSP-ADMAIF + */ + + if (state == nvfx_state_active) { + + /* Check source of APM-IN. If not ADSP-ADMAIF, return */ + src = tegra210_adsp_get_source(adsp, app->reg); + if (!IS_ADSP_ADMAIF(src)) { + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_PLAYBACK] = -1; + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_CAPTURE] = -1; + return 0; + } + capture_admaif_id = src - ADSP_ADMAIF_START; + + /* Check if any ADSP-ADMAIF is connected to same APM output */ + for (i = ADSP_ADMAIF_START; i <= ADSP_ADMAIF_END; i++) { + app = &adsp->apps[i]; + src = tegra210_adsp_get_source(adsp, app->reg); + + if (!IS_APM_OUT(src)) + continue; + + apm_out_id = src - APM_OUT_START; + if (apm_out_id == apm_in_id) { + be_to_be_flag = 1; + playback_admaif_id = app->reg - ADSP_ADMAIF_START; + break; + } + } + + if (be_to_be_flag) { + /* if IO to IO path exists, map APM to ADMAIFs */ + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_PLAYBACK] = + playback_admaif_id; + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_CAPTURE] = + capture_admaif_id; + + if (is_playback == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra_ivc_start_playback(adsp, playback_admaif_id, false); + if (ret < 0) + dev_err(adsp->dev, "%s: start playback failed\n", __func__); + } else { + ret = tegra_ivc_start_capture(adsp, capture_admaif_id, false); + if (ret < 0) { + dev_err(adsp->dev, "%s: start capture failed\n", __func__); + ret = tegra_ivc_stop_playback(adsp, playback_admaif_id, false); + if (ret < 0) + dev_err(adsp->dev, "%s: stop capture failed\n", __func__); + } + } + } else { + /* No IO to IO path identified. No hv state msg to be sent */ + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_PLAYBACK] = -1; + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_CAPTURE] = -1; + } + + } else if (state == nvfx_state_inactive) { + + playback_admaif_id = + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_PLAYBACK]; + capture_admaif_id = + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_CAPTURE]; + + if ((playback_admaif_id != -1) && (capture_admaif_id != -1)) { + if (is_playback == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra_ivc_stop_playback(adsp, playback_admaif_id, false); + if (ret < 0) + dev_err(adsp->dev, "%s: stop playback failed\n", __func__); + } else { + ret = tegra_ivc_stop_capture(adsp, capture_admaif_id, true); + if (ret < 0) + dev_err(adsp->dev, "%s: stop capture failed\n", __func__); + } + } + + if (is_playback == SNDRV_PCM_STREAM_PLAYBACK) { + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_PLAYBACK] = -1; + adsp->apm_to_admaif_map[apm_in_id][SNDRV_PCM_STREAM_CAPTURE] = -1; + } + } + + return ret; +} + +static int32_t tegra210_adsp_hv_pcm_trigger( + struct tegra210_adsp *adsp, + uint32_t apm_out_in, + int stream, + int cmd) +{ + int ret = 0; + int32_t ivc_msg_admaif_id = 0; + uint32_t source; + + source = tegra210_adsp_get_source(adsp, apm_out_in); + ivc_msg_admaif_id = ADSP_BACKEND_TO_ADMAIF( + tegra_adsp_get_connected_be(adsp, source, stream)); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra_ivc_start_playback(adsp, + ivc_msg_admaif_id, true); + } else { + ret = tegra_ivc_start_capture(adsp, + ivc_msg_admaif_id, true); + } + + if (ret < 0) { + pr_err("%s: error during start_trigger\n", __func__); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = tegra_ivc_stop_playback(adsp, + ivc_msg_admaif_id, true); + } else { + ret = tegra_ivc_stop_capture(adsp, + ivc_msg_admaif_id, true); + } + + if (ret < 0) { + pr_err("%s: error during stop_trigger\n", __func__); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_DRAIN: + /* do nothing */ + break; + default: + pr_err("Unsupported state."); + return -EINVAL; + } + return ret; +} + +static int tegra210_adsp_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data; + struct tegra210_adsp *adsp = prtd->fe_apm->adsp; + struct device *dev = adsp->dev; + struct device_node *node = dev->of_node; + struct tegra210_adsp_app *apm = prtd->fe_apm; +#if defined(NV_SND_SOC_RTD_TO_CODEC_PRESENT) /* Linux 6.7*/ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(soc_runtime, 0); +#else + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(soc_runtime, 0); +#endif + uint32_t codec_id = codec_dai->id + 1; + unsigned long flags; + int ret = 0; + + dev_vdbg(prtd->dev, "%s : state %d", __func__, cmd); + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + tegra_adsp_get_connected_be(adsp, + codec_id, substream->stream) == 0) { + runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; + return -EBADF; + } + spin_lock_irqsave(&apm->fe_playback_lock, flags); + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + && (cmd == SNDRV_PCM_TRIGGER_STOP)) { + if (apm->fe_playback_triggered == 1) + apm->fe_playback_triggered = 0; + else { + /*unlock and return */ + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + return 0; + } + } + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + && (cmd == SNDRV_PCM_TRIGGER_START)) { + if (apm->fe_playback_triggered == 0) + apm->fe_playback_triggered = 1; + else { + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + return 0; + } + } + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_active, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state"); + return ret; + } + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + (IS_MMAP_ACCESS(runtime->access))) { + prtd->prev_appl_ptr = runtime->control->appl_ptr; + tegra210_adsp_pcm_ack(substream); + } + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_inactive, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state"); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = tegra210_adsp_send_state_msg(prtd->fe_apm, + nvfx_state_inactive, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state"); + return ret; + } + + ret = tegra210_adsp_send_flush_msg(prtd->fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to reset"); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_DRAIN: + /* EOS message is sent so that ADSP sends */ + /* notification for last consumed buffer */ + ret = tegra210_adsp_send_eos_msg(prtd->fe_apm, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(prtd->dev, "Failed to set state drain"); + return ret; + } + break; + default: + dev_err(prtd->dev, "Unsupported state."); + return -EINVAL; + } + + if (of_device_is_compatible(node, "nvidia,tegra210-adsp-audio-hv")) { + + ret = tegra210_adsp_hv_pcm_trigger(adsp, + prtd->fe_apm->reg, /* apm_out_in */ + substream->stream, + cmd); + if (ret < 0) { + dev_err(prtd->dev, "error on ivc_send"); + return ret; + } + + } + + return 0; +} + + +static snd_pcm_uframes_t + tegra210_adsp_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data; + struct tegra210_adsp_app *app = prtd->fe_apm; + size_t bytes, pos; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + bytes = app->apm->nvfx_shared_state.output[0].bytes; + else + bytes = app->apm->nvfx_shared_state.input[0].bytes; + + pos = bytes % frames_to_bytes(substream->runtime, + substream->runtime->buffer_size); + + /* TODO : If SRC in path do size conversion */ + + dev_vdbg(prtd->dev, "%s bytes %zu position %zu", __func__, bytes, pos); + return bytes_to_frames(substream->runtime, pos); +} + +static int tegra210_adsp_pcm_construct(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ +#if ENABLE_ADSP + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + struct snd_pcm_substream *substream = + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + + ret = tegra210_adsp_preallocate_dma_buffer(card->dev, + adsp_pcm_hardware.buffer_bytes_max, + &substream->dma_buffer); + + if (ret) + return ret; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + struct snd_pcm_substream *substream = + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + + ret = tegra210_adsp_preallocate_dma_buffer(card->dev, + adsp_pcm_hardware.buffer_bytes_max, + &substream->dma_buffer); + if (ret) + goto err; + } + + return 0; + +err: + tegra210_adsp_deallocate_dma_buffer( + &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); + return ret; +#else + return 0; +#endif +} + +static void tegra210_adsp_pcm_destruct(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + int stream = SNDRV_PCM_STREAM_PLAYBACK; + + tegra210_adsp_deallocate_dma_buffer( + &pcm->streams[stream].substream->dma_buffer); + } + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + int stream = SNDRV_PCM_STREAM_CAPTURE; + + tegra210_adsp_deallocate_dma_buffer( + &pcm->streams[stream].substream->dma_buffer); + } +} + +static void tegra_adsp_set_admaif_id( + struct tegra210_adsp *adsp, + uint32_t admaif_id, + uint32_t be_reg, + int s_stream) +{ + unsigned int i, j; + int stream; + uint32_t src; + struct tegra210_adsp_app *app = NULL; + + /* ADSP Playback (ADSP-ADMAIF Codec Capture) */ + if (s_stream == SNDRV_PCM_STREAM_CAPTURE) { + + app = &adsp->apps[be_reg]; + src = app->reg; + + while (src != TEGRA210_ADSP_NONE) { + + /* Backtrace path till data source is found */ + src = tegra210_adsp_get_source(adsp, src); + + /* Check if data source in path is ADSP-FE (Mem to IO) */ + if (IS_ADSP_FE(src)) { + i = adsp->apps[src].reg; + stream = SNDRV_PCM_STREAM_PLAYBACK; + adsp->fe_to_admaif_map[i-1][stream] = admaif_id; + dev_vdbg(adsp->dev, "%s : capture fe %d admaif %d\n", + __func__, i, admaif_id); + return; + } + + /* Check if data source in path is ADSP-ADMAIF (IO to IO) */ + if (IS_ADSP_ADMAIF(src)) { + dev_vdbg(adsp->dev, "%s : start playback on adsp admaif %d\n", + __func__, admaif_id); + + /* Clear older FE to ADMAIF mappings for given ADMAIF */ + stream = SNDRV_PCM_STREAM_PLAYBACK; + for (j = ADSP_FE_START; j <= ADSP_FE_END; j++) { + if (adsp->fe_to_admaif_map[j-1][stream] == admaif_id) + adsp->fe_to_admaif_map[j-1][stream] = -1; + } + + return; + } + } + + dev_err(adsp->dev, "%s: no data source for ADSP-ADMAIF %d\n", + __func__, admaif_id); + + } else { /* ADSP Capture (ADSP-ADMAIF Codec Playback) */ + + /* Check if ADSP-FE is sink for given ADSP ADMAIF (IO to Mem) */ + for (i = ADSP_FE_START; i <= ADSP_FE_END; i++) { + app = &adsp->apps[i]; + src = app->reg; + while (!IS_ADSP_ADMAIF(src) && src != 0) { + src = tegra210_adsp_get_source( + adsp, src); + } + if (src != be_reg) + continue; + + stream = SNDRV_PCM_STREAM_CAPTURE; + adsp->fe_to_admaif_map[i-1][stream] = admaif_id; + dev_vdbg(adsp->dev, "%s : capture fe %d admaif %d\n", + __func__, i, admaif_id); + return; + } + + /* Check if ADSP-ADMAIF is sink for given ADSP ADMAIF (IO to IO) */ + for (i = ADSP_ADMAIF_START; i <= ADSP_ADMAIF_END; i++) { + app = &adsp->apps[i]; + src = app->reg; + while (!IS_ADSP_ADMAIF(src) && src != 0) { + src = tegra210_adsp_get_source( + adsp, src); + } + if (src != be_reg) + continue; + + + /* Clear older FE to ADMAIF mappings for given ADMAIF */ + stream = SNDRV_PCM_STREAM_CAPTURE; + for (j = ADSP_FE_START; j <= ADSP_FE_END; j++) { + if (adsp->fe_to_admaif_map[j-1][stream] == admaif_id) + adsp->fe_to_admaif_map[j-1][stream] = -1; + } + + dev_vdbg(adsp->dev, "%s : start capture on adsp admaif %d\n", + __func__, admaif_id); + return; + } + + dev_err(adsp->dev, "%s: no data sink for ADSP-ADMAIF %d\n", + __func__, admaif_id); + + } + return; +} + +static int tegra_adsp_admaif_ivc_set_cif(struct tegra210_adsp *adsp, + struct snd_pcm_hw_params *params, + uint32_t admaif_id, + uint32_t be_reg, + nvfx_adma_init_params_t *adma_params, + int stream) +{ + unsigned int ret = 0; + int err = 0; + uint32_t ivc_msg_admaif_id; + struct tegra210_adsp_app *app; + struct tegra210_virt_audio_cif cif_setting; + struct tegra210_virt_audio_cif *cif_conf = NULL; + struct tegra210_adsp_pcm_rtd *prtd = NULL; + struct snd_pcm_runtime *runtime = NULL; + struct nvaudio_ivc_msg msg; + uint32_t value, channels = DEFAULT_NUM_CHANNELS; + snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16_LE; + uint32_t rate = DEFAULT_RATE, apm_in_reg, source; + uint32_t max_bytes = adsp_pcm_hardware.buffer_bytes_max; + + adsp->hivc_client = + nvaudio_ivc_alloc_ctxt(adsp->dev); + + if (!adsp->hivc_client) { + dev_err(adsp->dev, "Failed to allocate IVC context\n"); + err = -ENODEV; + return err; + } + + + if (params) { + channels = params_channels(params); + format = params_format(params); + } + + /* overwrite channel, rate, format if we already have FE info */ + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = tegra_adsp_get_connected_fe(adsp, be_reg, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret != TEGRA210_ADSP_NONE) { + format = adsp->pcm_path + [ret][SNDRV_PCM_STREAM_PLAYBACK].format; + channels = adsp->pcm_path + [ret][SNDRV_PCM_STREAM_PLAYBACK].channels; + rate = adsp->pcm_path + [ret][SNDRV_PCM_STREAM_PLAYBACK].rate; + } + + app = &adsp->apps[be_reg]; + source = tegra210_adsp_get_source(adsp, app->reg); + app = &adsp->apps[source]; + if (!IS_APM_OUT(app->reg)) + return 0; + apm_in_reg = APM_IN_START + (source - APM_OUT_START); + + if (adsp->apps[apm_in_reg].msg_handler + == tegra210_adsp_pcm_msg_handler) { + prtd = adsp->apps[apm_in_reg].private_data; + runtime = prtd->substream->runtime; + max_bytes = frames_to_bytes(runtime, + runtime->buffer_size); + } + } else { + ret = tegra_adsp_get_connected_fe(adsp, be_reg, + SNDRV_PCM_STREAM_CAPTURE); + if (ret != TEGRA210_ADSP_NONE) { + format = adsp->pcm_path + [ret][SNDRV_PCM_STREAM_CAPTURE].format; + channels = adsp->pcm_path + [ret][SNDRV_PCM_STREAM_CAPTURE].channels; + rate = adsp->pcm_path + [ret][SNDRV_PCM_STREAM_CAPTURE].rate; + } + } + + cif_conf = &cif_setting; + + ivc_msg_admaif_id = admaif_id - 1; + + memset(cif_conf, 0, sizeof(struct tegra210_virt_audio_cif)); + cif_conf->audio_channels = channels; + cif_conf->client_channels = channels; + + switch (format) { + case SNDRV_PCM_FORMAT_S8: + cif_conf->client_bits = TEGRA210_AUDIOCIF_BITS_8; + cif_conf->audio_bits = TEGRA210_AUDIOCIF_BITS_8; + adma_params->burst_size = channels/4; + break; + case SNDRV_PCM_FORMAT_S16_LE: + cif_conf->client_bits = TEGRA210_AUDIOCIF_BITS_16; + cif_conf->audio_bits = TEGRA210_AUDIOCIF_BITS_16; + adma_params->burst_size = (channels * 2)/4; + break; + case SNDRV_PCM_FORMAT_S24_LE: + cif_conf->client_bits = TEGRA210_AUDIOCIF_BITS_24; + cif_conf->audio_bits = TEGRA210_AUDIOCIF_BITS_24; + adma_params->burst_size = (channels * 3)/4; + break; + case SNDRV_PCM_FORMAT_S32_LE: + cif_conf->client_bits = TEGRA210_AUDIOCIF_BITS_32; + cif_conf->audio_bits = TEGRA210_AUDIOCIF_BITS_32; + adma_params->burst_size = channels; + break; + default: + dev_err(adsp->dev, "Wrong format!\n"); + return -EINVAL; + } + + cif_conf->direction = stream; + + value = (cif_conf->threshold << + TEGRA210_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) | + ((cif_conf->audio_channels - 1) << + TEGRA210_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) | + ((cif_conf->client_channels - 1) << + TEGRA210_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) | + (cif_conf->audio_bits << + TEGRA210_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) | + (cif_conf->client_bits << + TEGRA210_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) | + (cif_conf->expand << + TEGRA210_AUDIOCIF_CTRL_EXPAND_SHIFT) | + (cif_conf->stereo_conv << + TEGRA210_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) | + (cif_conf->replicate << + TEGRA210_AUDIOCIF_CTRL_REPLICATE_SHIFT) | + (cif_conf->truncate << + TEGRA210_AUDIOCIF_CTRL_TRUNCATE_SHIFT) | + (cif_conf->mono_conv << + TEGRA210_AUDIOCIF_CTRL_MONO_CONV_SHIFT); + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.params.dmaif_info.id = ivc_msg_admaif_id; + msg.params.dmaif_info.value = value; + if (cif_conf->direction) + msg.cmd = NVAUDIO_DMAIF_SET_TXCIF; + else + msg.cmd = NVAUDIO_DMAIF_SET_RXCIF; + + err = nvaudio_ivc_send_retry(adsp->hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + if (err < 0) + pr_err("%s: error during ivc_send\n", __func__); + return err; +} + +static int tegra210_adsp_admaif_hv_hw_params( + struct tegra210_adsp *adsp, + struct snd_pcm_hw_params *params, + uint32_t admaif_id, + uint32_t be_reg, + nvfx_adma_init_params_t *adma_params, + int stream) +{ + int ret = 0; + + ret = tegra_adsp_admaif_ivc_set_cif(adsp, + params, + admaif_id, + be_reg, + adma_params, + stream); + if (ret < 0) { + pr_err("%s: error during adsp_admaif_set_cif\n", __func__); + return ret; + } + tegra_adsp_set_admaif_id(adsp, + admaif_id, + be_reg, + stream); + return ret; +} + +static int tegra210_adsp_fe_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &tegra210_adsp_rate_constraints); +} + +static int tegra210_adsp_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + + struct tegra210_adsp *adsp = snd_soc_dai_get_drvdata(dai); + uint32_t fe_reg = dai->id + 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].rate + = params_rate(params); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].channels + = params_channels(params); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_PLAYBACK].format + = params_format(params); + } else { + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].rate + = params_rate(params); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].channels + = params_channels(params); + adsp->pcm_path[fe_reg][SNDRV_PCM_STREAM_CAPTURE].format + = params_format(params); + } + + return 0; +} + +/* ADSP-ADMAIF codec driver HW-params. Used for configuring ADMA */ +static int tegra210_adsp_admaif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_adsp *adsp = snd_soc_dai_get_drvdata(dai); + struct tegra210_adsp_pcm_rtd *prtd = NULL; + struct snd_pcm_runtime *runtime = NULL; + struct device *dev = adsp->dev; + struct device_node *node = dev->of_node; + struct tegra210_adsp_app *app; + nvfx_adma_init_params_t adma_params; + uint32_t be_reg = dai->id + 1; + uint32_t admaif_id = be_reg - ADSP_ADMAIF_START + 1; + uint32_t source, apm_in_reg; + int i, ret; + + dev_vdbg(adsp->dev, "%s : stream %d admaif %d\n", + __func__, substream->stream, admaif_id); + if (!adsp->adsp_started) + return -EINVAL; + + memset(&adma_params, 0, sizeof(adma_params)); + if (of_device_is_compatible(node, "nvidia,tegra210-adsp-audio-hv")) { + + /*Start of sending IVC command for admaif cif settings*/ + ret = tegra210_adsp_admaif_hv_hw_params(adsp, + params, + admaif_id, + be_reg, + &adma_params, + substream->stream); + if (ret < 0) { + pr_err("%s: error during adsp_admaif_hv_hw_params\n", __func__); + return ret; + } + /*End of sending IVC command for admaif cif setting*/ + + } + + adma_params.mode = ADMA_MODE_CONTINUOUS; + adma_params.ahub_channel = admaif_id; + adma_params.periods = 2; /* We need ping-pong buffers for ADMA */ + adma_params.adma_ch_page = adsp->adma_ch_page; + + /* Set DMA params connected with ADSP-BE */ + /* As a COCEC DAI, CAPTURE is transmit */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + app = &adsp->apps[be_reg]; + source = tegra210_adsp_get_source(adsp, app->reg); + + app = &adsp->apps[source]; + if (!IS_APM_OUT(app->reg)) + return 0; + + apm_in_reg = APM_IN_START + (source - APM_OUT_START); + + /* not needed for IO-IO and compr */ + if (adsp->apps[apm_in_reg].msg_handler + == tegra210_adsp_pcm_msg_handler) { + prtd = adsp->apps[apm_in_reg].private_data; + runtime = prtd->substream->runtime; + if ((IS_MMAP_ACCESS(runtime->access))) + adma_params.periods = 4; + } + + source = tegra210_adsp_get_source(adsp, app->reg); + app = &adsp->apps[source]; + if (!IS_ADMA(app->reg)) + return 0; + + adma_params.adma_channel = app->adma_chan; + adma_params.direction = ADMA_MEMORY_TO_AHUB; + adma_params.event.pvoid = app->apm->output_event.pvoid; + + ret = tegra210_adsp_adma_params_msg(app, &adma_params, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(adsp->dev, "ADMA params msg failed. %d.", ret); + return ret; + } + } else { + for (i = ADMA_START; i <= ADMA_END; i++) { + app = &adsp->apps[i]; + source = tegra210_adsp_get_source(adsp, app->reg); + if (!IS_APM_IN(source)) + continue; + app = &adsp->apps[source]; + + /* not needed for IO-IO and compr */ + if (app->msg_handler == + tegra210_adsp_pcm_msg_handler) { + prtd = adsp->apps[source].private_data; + runtime = prtd->substream->runtime; + } + source = tegra210_adsp_get_source(adsp, app->reg); + if (source != be_reg) + continue; + + if (runtime && (IS_MMAP_ACCESS(runtime->access))) + adma_params.periods = 4; + + app = &adsp->apps[i]; + + adma_params.adma_channel = app->adma_chan; + adma_params.direction = ADMA_AHUB_TO_MEMORY; + adma_params.event.pvoid = app->apm->input_event.pvoid; + + ret = tegra210_adsp_adma_params_msg(app, + &adma_params, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(adsp->dev, "ADMA params msg failed"); + return ret; + } + } + } + return 0; +} + +/* ADSP-EAVB codec driver HW-params. Used for configuring EAVB ADMA */ +static int tegra210_adsp_eavb_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tegra210_adsp *adsp = snd_soc_dai_get_drvdata(dai); + struct tegra210_adsp_app *app; + nvfx_eavbdma_init_params_t eavbdma_params; + int ret; + + if (!adsp->adsp_started) + return -EINVAL; + + /* As a COCEC DAI, CAPTURE is transmit */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + app = tegra210_adsp_get_plugin(adsp, EAVB_TX_PLUGIN); + + if (!app) + return -EINVAL; + + eavbdma_params.direction = EAVB_TX_DMA; + eavbdma_params.event.pvoid = app->apm->output_event.pvoid; + } else { + app = tegra210_adsp_get_plugin(adsp, EAVB_RX_PLUGIN); + + if (!app) + return -EINVAL; + + eavbdma_params.direction = EAVB_RX_DMA; + eavbdma_params.event.pvoid = app->apm->input_event.pvoid; + } + + ret = tegra210_adsp_eavbdma_params_msg(app, &eavbdma_params, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(adsp->dev, "EAVB DMA params msg failed. %d.", ret); + return ret; + } + + return 0; +} + +#ifdef CONFIG_PM +static int tegra210_adsp_runtime_suspend(struct device *dev) +{ + struct tegra210_adsp *adsp = dev_get_drvdata(dev); + int ret = 0, i; + + dev_dbg(adsp->dev, "%s\n", __func__); + + mutex_lock(&adsp->mutex); + if (!adsp->init_done || !adsp->adsp_started) + goto exit; + + /* Check for msgq empty before suspend */ + for (i = 0; i < TEGRA210_ADSP_VIRT_REG_MAX; i++) { + struct tegra210_adsp_app *app = &adsp->apps[i]; + + if (app->plugin && IS_APM_IN(app->reg)) { + msgq_t *msgq = &app->apm->msgq_recv.msgq; + + if (msgq->read_index == msgq->write_index) + continue; + pr_err("%s: app %d, msgq not empty rd %d wr %d\n", + __func__, app->reg, msgq->read_index, + msgq->write_index); + } + } + + ret = nvadsp_os_suspend(); + if (ret) + dev_err(adsp->dev, "Failed to suspend ADSP OS"); + + adsp->adsp_started = 0; + + if (!adsp->soc_data->is_soc_t210) + clk_disable_unprepare(adsp->apb2ape_clk); + clk_disable_unprepare(adsp->ahub_clk); + clk_disable_unprepare(adsp->ape_clk); + +exit: + mutex_unlock(&adsp->mutex); + return ret; +} + +static int tegra210_adsp_runtime_resume(struct device *dev) +{ + struct tegra210_adsp *adsp = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(adsp->dev, "%s\n", __func__); + + mutex_lock(&adsp->mutex); + if (!adsp->init_done || adsp->adsp_started) + goto exit; + + + ret = clk_prepare_enable(adsp->ahub_clk); + if (ret < 0) { + dev_err(dev, "ahub clk_enable failed: %d\n", ret); + goto exit; + } + + ret = clk_prepare_enable(adsp->ape_clk); + if (ret < 0) { + dev_err(dev, "ape clk_enable failed: %d\n", ret); + goto exit; + } + + if (!adsp->soc_data->is_soc_t210) { + ret = clk_prepare_enable(adsp->apb2ape_clk); + if (ret < 0) { + dev_err(dev, "apb2ape clk_enable failed: %d\n" + , ret); + goto exit; + } + } + + ret = nvadsp_os_start(); + if (ret) { + dev_err(adsp->dev, "Failed to start ADSP OS ret 0x%x", ret); + adsp->adsp_started = 0; + goto exit; + } + + adsp->adsp_started = 1; +exit: + mutex_unlock(&adsp->mutex); + return ret; +} +#endif + +/* ADSP platform driver read/write call-back */ +static unsigned int tegra210_adsp_read(struct snd_soc_component *component, + unsigned int reg) +{ + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(component); + + dev_vdbg(adsp->dev, "%s [0x%x] -> 0x%x\n", __func__, reg, + tegra210_adsp_reg_read(adsp, reg)); + + return tegra210_adsp_reg_read(adsp, reg); +} + +static int tegra210_adsp_write(struct snd_soc_component *component, + unsigned int reg, + unsigned int val) +{ + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(component); + + dev_vdbg(adsp->dev, "%s [0x%x] -> 0x%x\n", __func__, reg, val); + + tegra210_adsp_reg_write(adsp, reg, val); + + return 0; +} + +/* DAPM ENUM MUX get/put callbacks */ +static int tegra210_adsp_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(dapm); + struct soc_enum *e = + (struct soc_enum *)kcontrol->private_value; + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + uint32_t val = tegra210_adsp_reg_read(adsp, e->reg); + + ucontrol->value.integer.value[0] = + (val & TEGRA210_ADSP_WIDGET_SOURCE_MASK) >> e->shift_l; + return 0; +} + +static int tegra210_adsp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(dapm); + uint32_t val = ucontrol->value.enumerated.item[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_app *app; + uint32_t cur_val = 0; + int ret = 0; + + if (!adsp->init_done) { + dev_err(adsp->dev, "%s ADSP not initialized\n", __func__); + return -ENODEV; + } + + if (e->reg >= TEGRA210_ADSP_VIRT_REG_MAX) + return -EINVAL; + + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + return ret; + } + + /* Init or de-init app based on connection */ + if (IS_ADSP_APP(e->reg)) { + app = &adsp->apps[e->reg]; + cur_val = tegra210_adsp_get_source(adsp, e->reg); + if (cur_val != val) { + if (app->connect) { + /* remove existing connections if any */ + tegra210_adsp_remove_connection(adsp, app); + } + app->connect = 0; + } + + if (val == TEGRA210_ADSP_NONE) { + tegra210_adsp_app_deinit(adsp, app); + } else { + ret = tegra210_adsp_app_init(adsp, app); + if (ret < 0) { + dev_err(adsp->dev, "Failed to init app %s(%s)", + app->desc->name, app->desc->fw_name); + goto err_put; + } + } + } + tegra210_adsp_reg_update_bits(adsp, e->reg, + TEGRA210_ADSP_WIDGET_SOURCE_MASK, val << e->shift_l); + tegra210_adsp_update_connection(adsp); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, val, e, NULL); + +err_put: + pm_runtime_put(adsp->dev); + return ret ? ret : 1; +} + +/* ALSA control get/put call-back implementation */ +static int tegra210_adsp_init_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + + ucontrol->value.enumerated.item[0] = adsp->init_done; + return 0; +} + +static int tegra210_adsp_init_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + uint32_t init = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (init == adsp->init_done) + return 0; + + if (init) { + ret = tegra210_adsp_init(adsp); + if (ret < 0) { + dev_err(adsp->dev, "Failed to init ADSP."); + return ret; + } + } else { + tegra210_adsp_deinit(adsp); + } + + return 1; +} +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD +static int tegra210_adsp_lpthread_init_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + + if (!adsp->adsp_started) + goto exit; + + ucontrol->value.enumerated.item[0] = adsp_usage_get(); +exit: + return 0; +} + +static int tegra210_adsp_lpthread_init_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + unsigned int init = ucontrol->value.enumerated.item[0]; + + if (!adsp->init_done) + return -ENODEV; + + if (init) { + pm_runtime_get_sync(adsp->dev); + if (!adsp->adsp_started) + goto exit; + adsp_usage_set(init); + + } else { + if (!adsp->adsp_started) + goto exit; + adsp_usage_set(init); + pm_runtime_put_sync(adsp->dev); + } +exit: + return 0; +} +#endif +/* + * ADSP FE DAPM Widget Event + * Widget event allows realizing ADSP FE switch use cases + * + * Start Playback trigger calls handled by pcm_trigger() + * + * Path Connect when ADSP FE is in triggered state + * (Sequence: Start trigger -> Path disconnect -> Path connect) + * handled by fe_widget_event() in SND_SOC_DAPM_PRE_PMU event handling + * + * Stop Playback trigger calls and Path disconnect during playback + * handled by fe_widget_event() in SND_SOC_DAPM_POST_PMD event handling + */ +static int tegra210_adsp_fe_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_app *apm; + struct device *dev = adsp->dev; + struct device_node *node = dev->of_node; + int i; + int ret = 0; + uint32_t source; + struct snd_pcm_runtime *runtime; + struct tegra210_adsp_pcm_rtd *prtd = NULL; + unsigned long flags; + + /* Handle only HV environment ADSP FE events */ + if (!of_device_is_compatible(node, "nvidia,tegra210-adsp-audio-hv")) + return 0; + + if (!IS_ADSP_FE(w->reg)) + return 0; + + /* Find out APM connected to ADSP-FE */ + for (i = APM_IN_START; i <= APM_IN_END; i++) { + apm = &adsp->apps[i]; + source = tegra210_adsp_get_source(adsp, apm->reg); + if (source == w->reg) + break; + } + + if (!IS_APM_IN(i)) { + dev_err(adsp->dev, "%s ADSP-FE %d not connected to any APM-IN\n", + __func__, w->reg); + return -1; + } + + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + goto err_put; + } + + /* + * PRE_PMU: Send Start playback/capture IVC to Audio Server and + * active state message to ADSP + * + * POST_PMD: Send Stop playback/capture IVC to Audio Server and + * inactive state message to ADSP. Flush messages will be sent to + * ADSP during pcm_close(). + */ + if (event == SND_SOC_DAPM_PRE_PMU) { + dev_info(adsp->dev, "connect event on APM %d, ADSP-FE %d\n", + (i + 1) - APM_IN_START, w->reg); + + /* + * For trigger playback, pcm_trigger calls will handle ivc and + * adsp messages. + * + * When ADSP FE is in triggered state, path disconnect and + * connect is done, widget event will send IVC and ADSP messages + */ + spin_lock_irqsave(&apm->fe_playback_lock, flags); + if (!apm->fe_playback_triggered) { + ret = 0; + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + goto err_put; + } + + apm->fe_playback_triggered = 1; + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + + spin_lock_irqsave(&apm->lock, flags); + prtd = apm->private_data; + + /* TODO can prtd be NULL here? */ + + runtime = prtd->substream->runtime; + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + + ret = tegra210_adsp_hv_pcm_trigger(adsp, + apm->reg, /* apm_out_in */ + SNDRV_PCM_STREAM_PLAYBACK, /* playback */ + SNDRV_PCM_TRIGGER_START); /* PCM START */ + if (ret < 0) { + pr_err("%s: error during hv_pcm_trigger\n", __func__); + spin_unlock_irqrestore(&apm->lock, flags); + goto err_put; + } + + ret = tegra210_adsp_send_state_msg(apm, nvfx_state_active, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(adsp->dev, "Failed to set state active"); + spin_unlock_irqrestore(&apm->lock, flags); + goto err_put; + } + spin_unlock_irqrestore(&apm->lock, flags); + + } else if (event == SND_SOC_DAPM_POST_PMD) { + + dev_info(adsp->dev, "disconnect event on APM %d, ADSP-FE %d\n", + (i + 1) - APM_IN_START, w->reg); + + spin_lock_irqsave(&apm->fe_playback_lock, flags); + + if (!apm->fe_playback_triggered) { + ret = 0; + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + goto err_put; + } + + apm->fe_playback_triggered = -1; + spin_unlock_irqrestore(&apm->fe_playback_lock, flags); + + ret = tegra210_adsp_send_state_msg(apm, nvfx_state_inactive, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + dev_err(adsp->dev, "Failed to set state inactive."); + goto err_put; + } + + ret = tegra210_adsp_hv_pcm_trigger(adsp, + apm->reg, /* apm_out_in */ + SNDRV_PCM_STREAM_PLAYBACK, /* playback */ + SNDRV_PCM_TRIGGER_STOP); /* PCM STOP */ + if (ret < 0) { + pr_err("%s: error during hv_pcm_trigger\n", __func__); + goto err_put; + } + + spin_lock_irqsave(&apm->lock, flags); + prtd = apm->private_data; + + if (!prtd) { + dev_dbg(adsp->dev, "PCM close caused this widget event\n"); + spin_unlock_irqrestore(&apm->lock, flags); + goto err_put; + } + + runtime = prtd->substream->runtime; + runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; + + wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); + + spin_unlock_irqrestore(&apm->lock, flags); + + } else { + pr_err("%s: error. unknown event: %d\n", __func__, event); + ret = -1; + goto err_put; + } +err_put: + pm_runtime_put(adsp->dev); + return ret; +} + +/* DAPM widget event */ +static int tegra210_adsp_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_app *app; + int ret = 0; + + if (!IS_ADSP_APP(w->reg)) + return 0; + + app = &adsp->apps[w->reg]; + /* For FE apm state change will be handled from trigger call back */ + if (app->fe) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (IS_APM_IN(w->reg)) { + /* Request higher ADSP clock when starting stream. + * Actmon takes care of adjusting frequency later. */ + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + return ret; + } + if (app->min_adsp_clock) + adsp_update_dfs_min_rate(app->min_adsp_clock * 1000); + + ret = tegra210_adsp_send_hv_state_msg(adsp, + app, nvfx_state_active, SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) + dev_err(adsp->dev, "Failed to send hv state active msg."); + + ret = tegra210_adsp_send_state_msg(app, nvfx_state_active, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) + dev_err(adsp->dev, "Failed to set state active."); + + ret = tegra210_adsp_send_hv_state_msg(adsp, + app, nvfx_state_active, SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) + dev_err(adsp->dev, "Failed to send hv state active msg."); + pm_runtime_put(adsp->dev); + } + } else { + if (IS_APM_IN(w->reg)) { + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + return ret; + } + + ret = tegra210_adsp_send_hv_state_msg(adsp, + app, nvfx_state_inactive, SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) + dev_err(adsp->dev, "Failed to send hv state inactive msg."); + + ret = tegra210_adsp_send_state_msg(app, nvfx_state_inactive, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) + dev_err(adsp->dev, "Failed to set state inactive."); + + ret = tegra210_adsp_send_reset_msg(app, + (TEGRA210_ADSP_MSG_FLAG_SEND | + TEGRA210_ADSP_MSG_FLAG_NEED_ACK)); + if (ret < 0) + dev_err(adsp->dev, "Failed to reset."); + if (app->min_adsp_clock) + adsp_update_dfs_min_rate(0); + + ret = tegra210_adsp_send_hv_state_msg(adsp, + app, nvfx_state_inactive, SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) + dev_err(adsp->dev, "Failed to send hv state inactive msg."); + + pm_runtime_put(adsp->dev); + } + } + + return ret; +} +static struct snd_soc_dai_ops tegra210_adsp_fe_dai_ops = { + .hw_params = tegra210_adsp_fe_hw_params, + .startup = tegra210_adsp_fe_startup, +}; + +static struct snd_soc_dai_ops tegra210_adsp_admaif_dai_ops = { + .hw_params = tegra210_adsp_admaif_hw_params, +}; + +static struct snd_soc_dai_ops tegra210_adsp_eavb_dai_ops = { + .hw_params = tegra210_adsp_eavb_hw_params, +}; + +#define ADSP_PCM_DAI(id) \ + { \ + .name = "ADSP PCM" #id, \ + .playback = { \ + .stream_name = "ADSP PCM" #id " Receive", \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "ADSP PCM" #id " Transmit", \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + } + +#define ADSP_EAVB_DAI() \ + { \ + .name = "ADSP EAVB", \ + .playback = { \ + .stream_name = "ADSP EAVB Transmit", \ + .channels_min = 1, \ + .channels_max = 2, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .stream_name = "ADSP EAVB Receive", \ + .channels_min = 1, \ + .channels_max = 2, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + } + +#define ADSP_FE_CODEC_DAI(idx) \ + { \ + .name = "ADSP-FE" #idx, \ + .playback = { \ + .stream_name = "ADSP-FE" #idx " Receive",\ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "ADSP-FE" #idx " Transmit",\ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra210_adsp_fe_dai_ops, \ + } + +#define ADSP_ADMAIF_CODEC_DAI(idx) \ + { \ + .name = "ADSP-ADMAIF" #idx, \ + .playback = { \ + .stream_name = "ADSP-ADMAIF" #idx " Receive", \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .stream_name = "ADSP-ADMAIF" #idx " Transmit",\ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = &tegra210_adsp_admaif_dai_ops, \ + } + +#define ADSP_EAVB_CODEC_DAI() \ + { \ + .name = "ADSP-EAVB", \ + .playback = { \ + .stream_name = "ADSP-EAVB Receive", \ + .channels_min = 1, \ + .channels_max = 2, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .stream_name = "ADSP-EAVB Transmit", \ + .channels_min = 1, \ + .channels_max = 2, \ + .rates = SNDRV_PCM_RATE_8000_48000, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = &tegra210_adsp_eavb_dai_ops, \ + } + + +static struct snd_soc_dai_driver tegra210_adsp_cmpnt_dai[] = { + ADSP_FE_CODEC_DAI(1), + ADSP_FE_CODEC_DAI(2), + ADSP_FE_CODEC_DAI(3), + ADSP_FE_CODEC_DAI(4), + ADSP_FE_CODEC_DAI(5), + ADSP_FE_CODEC_DAI(6), + ADSP_FE_CODEC_DAI(7), + ADSP_FE_CODEC_DAI(8), + ADSP_FE_CODEC_DAI(9), + ADSP_FE_CODEC_DAI(10), + ADSP_FE_CODEC_DAI(11), + ADSP_FE_CODEC_DAI(12), + ADSP_FE_CODEC_DAI(13), + ADSP_FE_CODEC_DAI(14), + ADSP_FE_CODEC_DAI(15), + ADSP_EAVB_CODEC_DAI(), + ADSP_ADMAIF_CODEC_DAI(1), + ADSP_ADMAIF_CODEC_DAI(2), + ADSP_ADMAIF_CODEC_DAI(3), + ADSP_ADMAIF_CODEC_DAI(4), + ADSP_ADMAIF_CODEC_DAI(5), + ADSP_ADMAIF_CODEC_DAI(6), + ADSP_ADMAIF_CODEC_DAI(7), + ADSP_ADMAIF_CODEC_DAI(8), + ADSP_ADMAIF_CODEC_DAI(9), + ADSP_ADMAIF_CODEC_DAI(10), + ADSP_ADMAIF_CODEC_DAI(11), + ADSP_ADMAIF_CODEC_DAI(12), + ADSP_ADMAIF_CODEC_DAI(13), + ADSP_ADMAIF_CODEC_DAI(14), + ADSP_ADMAIF_CODEC_DAI(15), + ADSP_ADMAIF_CODEC_DAI(16), + ADSP_ADMAIF_CODEC_DAI(17), + ADSP_ADMAIF_CODEC_DAI(18), + ADSP_ADMAIF_CODEC_DAI(19), + ADSP_ADMAIF_CODEC_DAI(20), + ADSP_PCM_DAI(1), + ADSP_PCM_DAI(2), + ADSP_PCM_DAI(3), + ADSP_PCM_DAI(4), + ADSP_PCM_DAI(5), + ADSP_PCM_DAI(6), + ADSP_PCM_DAI(7), + ADSP_PCM_DAI(8), + ADSP_PCM_DAI(9), + ADSP_PCM_DAI(10), + ADSP_PCM_DAI(11), + ADSP_PCM_DAI(12), + ADSP_PCM_DAI(13), + ADSP_PCM_DAI(14), + ADSP_PCM_DAI(15), + ADSP_EAVB_DAI(), +}; + +/* + * This array is linked with tegra210_adsp_virt_regs enum defines. Any thing + * changed in enum define should be also reflected here and vice-versa + */ +static const char *tegra210_adsp_mux_texts[] = { + "None", + "ADSP-FE1", + "ADSP-FE2", + "ADSP-FE3", + "ADSP-FE4", + "ADSP-FE5", + "ADSP-FE6", + "ADSP-FE7", + "ADSP-FE8", + "ADSP-FE9", + "ADSP-FE10", + "ADSP-FE11", + "ADSP-FE12", + "ADSP-FE13", + "ADSP-FE14", + "ADSP-FE15", + "ADSP-EAVB", + "ADSP-ADMAIF1", + "ADSP-ADMAIF2", + "ADSP-ADMAIF3", + "ADSP-ADMAIF4", + "ADSP-ADMAIF5", + "ADSP-ADMAIF6", + "ADSP-ADMAIF7", + "ADSP-ADMAIF8", + "ADSP-ADMAIF9", + "ADSP-ADMAIF10", + "ADSP-ADMAIF11", + "ADSP-ADMAIF12", + "ADSP-ADMAIF13", + "ADSP-ADMAIF14", + "ADSP-ADMAIF15", + "ADSP-ADMAIF16", + "ADSP-ADMAIF17", + "ADSP-ADMAIF18", + "ADSP-ADMAIF19", + "ADSP-ADMAIF20", + "APM-IN1", + "APM-IN2", + "APM-IN3", + "APM-IN4", + "APM-IN5", + "APM-IN6", + "APM-IN7", + "APM-IN8", + "APM-IN9", + "APM-IN10", + "APM-IN11", + "APM-IN12", + "APM-IN13", + "APM-IN14", + "APM-IN15", + "APM-OUT1", + "APM-OUT2", + "APM-OUT3", + "APM-OUT4", + "APM-OUT5", + "APM-OUT6", + "APM-OUT7", + "APM-OUT8", + "APM-OUT9", + "APM-OUT10", + "APM-OUT11", + "APM-OUT12", + "APM-OUT13", + "APM-OUT14", + "APM-OUT15", + "ADMA1", + "ADMA2", + "ADMA3", + "ADMA4", + "ADMA5", + "ADMA6", + "ADMA7", + "ADMA8", + "ADMA9", + "ADMA10", + "ADMA11", + "ADMA12", + "ADMA13", + "ADMA14", + "ADMA15", + "ADMA1-TX", + "ADMA2-TX", + "ADMA3-TX", + "ADMA4-TX", + "ADMA5-TX", + "ADMA6-TX", + "ADMA7-TX", + "ADMA8-TX", + "ADMA9-TX", + "ADMA10-TX", + "ADMA11-TX", + "ADMA12-TX", + "ADMA13-TX", + "ADMA14-TX", + "ADMA15-TX", + "PLUGIN1-PLACE-HOLDER", + "PLUGIN2-PLACE-HOLDER", + "PLUGIN3-PLACE-HOLDER", + "PLUGIN4-PLACE-HOLDER", + "PLUGIN5-PLACE-HOLDER", + "PLUGIN6-PLACE-HOLDER", + "PLUGIN7-PLACE-HOLDER", + "PLUGIN8-PLACE-HOLDER", + "PLUGIN9-PLACE-HOLDER", + "PLUGIN10-PLACE-HOLDER", + "PLUGIN11-PLACE-HOLDER", + "PLUGIN12-PLACE-HOLDER", + "PLUGIN13-PLACE-HOLDER", + "PLUGIN14-PLACE-HOLDER", + "PLUGIN15-PLACE-HOLDER", + "PLUGIN16-PLACE-HOLDER", + "PLUGIN17-PLACE-HOLDER", + "PLUGIN18-PLACE-HOLDER", + "PLUGIN19-PLACE-HOLDER", + "PLUGIN20-PLACE-HOLDER", +}; + +#define ADSP_MUX_ENUM_CTRL_DECL(ename, reg) \ + SOC_ENUM_SINGLE_DECL(ename##_enum, reg, \ + TEGRA210_ADSP_WIDGET_SOURCE_SHIFT, \ + tegra210_adsp_mux_texts); \ + static const struct snd_kcontrol_new ename##_ctrl = \ + SOC_DAPM_ENUM_EXT("ADSP Route", ename##_enum, \ + tegra210_adsp_mux_get, tegra210_adsp_mux_put) + +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe1, TEGRA210_ADSP_FRONT_END1); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe2, TEGRA210_ADSP_FRONT_END2); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe3, TEGRA210_ADSP_FRONT_END3); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe4, TEGRA210_ADSP_FRONT_END4); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe5, TEGRA210_ADSP_FRONT_END5); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe6, TEGRA210_ADSP_FRONT_END6); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe7, TEGRA210_ADSP_FRONT_END7); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe8, TEGRA210_ADSP_FRONT_END8); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe9, TEGRA210_ADSP_FRONT_END9); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe10, TEGRA210_ADSP_FRONT_END10); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe11, TEGRA210_ADSP_FRONT_END11); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe12, TEGRA210_ADSP_FRONT_END12); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe13, TEGRA210_ADSP_FRONT_END13); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe14, TEGRA210_ADSP_FRONT_END14); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe15, TEGRA210_ADSP_FRONT_END15); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_eavb, TEGRA210_ADSP_EAVB); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif1, TEGRA210_ADSP_ADMAIF1); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif2, TEGRA210_ADSP_ADMAIF2); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif3, TEGRA210_ADSP_ADMAIF3); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif4, TEGRA210_ADSP_ADMAIF4); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif5, TEGRA210_ADSP_ADMAIF5); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif6, TEGRA210_ADSP_ADMAIF6); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif7, TEGRA210_ADSP_ADMAIF7); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif8, TEGRA210_ADSP_ADMAIF8); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif9, TEGRA210_ADSP_ADMAIF9); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif10, TEGRA210_ADSP_ADMAIF10); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif11, TEGRA210_ADSP_ADMAIF11); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif12, TEGRA210_ADSP_ADMAIF12); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif13, TEGRA210_ADSP_ADMAIF13); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif14, TEGRA210_ADSP_ADMAIF14); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif15, TEGRA210_ADSP_ADMAIF15); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif16, TEGRA210_ADSP_ADMAIF16); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif17, TEGRA210_ADSP_ADMAIF17); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif18, TEGRA210_ADSP_ADMAIF18); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif19, TEGRA210_ADSP_ADMAIF19); +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif20, TEGRA210_ADSP_ADMAIF20); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in1, TEGRA210_ADSP_APM_IN1); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in2, TEGRA210_ADSP_APM_IN2); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in3, TEGRA210_ADSP_APM_IN3); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in4, TEGRA210_ADSP_APM_IN4); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in5, TEGRA210_ADSP_APM_IN5); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in6, TEGRA210_ADSP_APM_IN6); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in7, TEGRA210_ADSP_APM_IN7); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in8, TEGRA210_ADSP_APM_IN8); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in9, TEGRA210_ADSP_APM_IN9); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in10, TEGRA210_ADSP_APM_IN10); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in11, TEGRA210_ADSP_APM_IN11); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in12, TEGRA210_ADSP_APM_IN12); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in13, TEGRA210_ADSP_APM_IN13); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in14, TEGRA210_ADSP_APM_IN14); +static ADSP_MUX_ENUM_CTRL_DECL(apm_in15, TEGRA210_ADSP_APM_IN15); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out1, TEGRA210_ADSP_APM_OUT1); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out2, TEGRA210_ADSP_APM_OUT2); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out3, TEGRA210_ADSP_APM_OUT3); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out4, TEGRA210_ADSP_APM_OUT4); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out5, TEGRA210_ADSP_APM_OUT5); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out6, TEGRA210_ADSP_APM_OUT6); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out7, TEGRA210_ADSP_APM_OUT7); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out8, TEGRA210_ADSP_APM_OUT8); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out9, TEGRA210_ADSP_APM_OUT9); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out10, TEGRA210_ADSP_APM_OUT10); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out11, TEGRA210_ADSP_APM_OUT11); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out12, TEGRA210_ADSP_APM_OUT12); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out13, TEGRA210_ADSP_APM_OUT13); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out14, TEGRA210_ADSP_APM_OUT14); +static ADSP_MUX_ENUM_CTRL_DECL(apm_out15, TEGRA210_ADSP_APM_OUT15); +static ADSP_MUX_ENUM_CTRL_DECL(adma1, TEGRA210_ADSP_PLUGIN_ADMA1); +static ADSP_MUX_ENUM_CTRL_DECL(adma2, TEGRA210_ADSP_PLUGIN_ADMA2); +static ADSP_MUX_ENUM_CTRL_DECL(adma3, TEGRA210_ADSP_PLUGIN_ADMA3); +static ADSP_MUX_ENUM_CTRL_DECL(adma4, TEGRA210_ADSP_PLUGIN_ADMA4); +static ADSP_MUX_ENUM_CTRL_DECL(adma5, TEGRA210_ADSP_PLUGIN_ADMA5); +static ADSP_MUX_ENUM_CTRL_DECL(adma6, TEGRA210_ADSP_PLUGIN_ADMA6); +static ADSP_MUX_ENUM_CTRL_DECL(adma7, TEGRA210_ADSP_PLUGIN_ADMA7); +static ADSP_MUX_ENUM_CTRL_DECL(adma8, TEGRA210_ADSP_PLUGIN_ADMA8); +static ADSP_MUX_ENUM_CTRL_DECL(adma9, TEGRA210_ADSP_PLUGIN_ADMA9); +static ADSP_MUX_ENUM_CTRL_DECL(adma10, TEGRA210_ADSP_PLUGIN_ADMA10); +static ADSP_MUX_ENUM_CTRL_DECL(adma11, TEGRA210_ADSP_PLUGIN_ADMA11); +static ADSP_MUX_ENUM_CTRL_DECL(adma12, TEGRA210_ADSP_PLUGIN_ADMA12); +static ADSP_MUX_ENUM_CTRL_DECL(adma13, TEGRA210_ADSP_PLUGIN_ADMA13); +static ADSP_MUX_ENUM_CTRL_DECL(adma14, TEGRA210_ADSP_PLUGIN_ADMA14); +static ADSP_MUX_ENUM_CTRL_DECL(adma15, TEGRA210_ADSP_PLUGIN_ADMA15); +static ADSP_MUX_ENUM_CTRL_DECL(adma1_tx, TEGRA210_ADSP_PLUGIN_ADMA1_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma2_tx, TEGRA210_ADSP_PLUGIN_ADMA2_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma3_tx, TEGRA210_ADSP_PLUGIN_ADMA3_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma4_tx, TEGRA210_ADSP_PLUGIN_ADMA4_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma5_tx, TEGRA210_ADSP_PLUGIN_ADMA5_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma6_tx, TEGRA210_ADSP_PLUGIN_ADMA6_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma7_tx, TEGRA210_ADSP_PLUGIN_ADMA7_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma8_tx, TEGRA210_ADSP_PLUGIN_ADMA8_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma9_tx, TEGRA210_ADSP_PLUGIN_ADMA9_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma10_tx, TEGRA210_ADSP_PLUGIN_ADMA10_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma11_tx, TEGRA210_ADSP_PLUGIN_ADMA11_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma12_tx, TEGRA210_ADSP_PLUGIN_ADMA12_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma13_tx, TEGRA210_ADSP_PLUGIN_ADMA13_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma14_tx, TEGRA210_ADSP_PLUGIN_ADMA14_TX); +static ADSP_MUX_ENUM_CTRL_DECL(adma15_tx, TEGRA210_ADSP_PLUGIN_ADMA15_TX); +static ADSP_MUX_ENUM_CTRL_DECL(plugin1, TEGRA210_ADSP_PLUGIN1); +static ADSP_MUX_ENUM_CTRL_DECL(plugin2, TEGRA210_ADSP_PLUGIN2); +static ADSP_MUX_ENUM_CTRL_DECL(plugin3, TEGRA210_ADSP_PLUGIN3); +static ADSP_MUX_ENUM_CTRL_DECL(plugin4, TEGRA210_ADSP_PLUGIN4); +static ADSP_MUX_ENUM_CTRL_DECL(plugin5, TEGRA210_ADSP_PLUGIN5); +static ADSP_MUX_ENUM_CTRL_DECL(plugin6, TEGRA210_ADSP_PLUGIN6); +static ADSP_MUX_ENUM_CTRL_DECL(plugin7, TEGRA210_ADSP_PLUGIN7); +static ADSP_MUX_ENUM_CTRL_DECL(plugin8, TEGRA210_ADSP_PLUGIN8); +static ADSP_MUX_ENUM_CTRL_DECL(plugin9, TEGRA210_ADSP_PLUGIN9); +static ADSP_MUX_ENUM_CTRL_DECL(plugin10, TEGRA210_ADSP_PLUGIN10); +static ADSP_MUX_ENUM_CTRL_DECL(plugin11, TEGRA210_ADSP_PLUGIN11); +static ADSP_MUX_ENUM_CTRL_DECL(plugin12, TEGRA210_ADSP_PLUGIN12); +static ADSP_MUX_ENUM_CTRL_DECL(plugin13, TEGRA210_ADSP_PLUGIN13); +static ADSP_MUX_ENUM_CTRL_DECL(plugin14, TEGRA210_ADSP_PLUGIN14); +static ADSP_MUX_ENUM_CTRL_DECL(plugin15, TEGRA210_ADSP_PLUGIN15); +static ADSP_MUX_ENUM_CTRL_DECL(plugin16, TEGRA210_ADSP_PLUGIN16); +static ADSP_MUX_ENUM_CTRL_DECL(plugin17, TEGRA210_ADSP_PLUGIN17); +static ADSP_MUX_ENUM_CTRL_DECL(plugin18, TEGRA210_ADSP_PLUGIN18); +static ADSP_MUX_ENUM_CTRL_DECL(plugin19, TEGRA210_ADSP_PLUGIN19); +static ADSP_MUX_ENUM_CTRL_DECL(plugin20, TEGRA210_ADSP_PLUGIN20); + +#define ADSP_EP_WIDGETS(sname, ename) \ + SND_SOC_DAPM_AIF_IN(sname " RX", NULL, 0, SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0), \ + SND_SOC_DAPM_MUX(sname " MUX", SND_SOC_NOPM, 0, 0, &ename##_ctrl) + +#define ADSP_FE_WIDGETS(sname, ename, reg) \ + SND_SOC_DAPM_AIF_IN_E(sname " RX", NULL, 0, reg, \ + TEGRA210_ADSP_WIDGET_EN_SHIFT, 0, \ + tegra210_adsp_fe_widget_event, \ + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), \ + SND_SOC_DAPM_AIF_OUT_E(sname " TX", NULL, 0, reg, \ + TEGRA210_ADSP_WIDGET_EN_SHIFT, 0, NULL, \ + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), \ + SND_SOC_DAPM_MUX(sname " MUX", SND_SOC_NOPM, 0, 0, &ename##_ctrl) + +#define ADSP_WIDGETS(sname, ename, reg) \ + SND_SOC_DAPM_AIF_OUT_E(sname " TX", NULL, 0, reg, \ + TEGRA210_ADSP_WIDGET_EN_SHIFT, 0, \ + tegra210_adsp_widget_event, \ + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), \ + SND_SOC_DAPM_MUX(sname " MUX", SND_SOC_NOPM, 0, 0, &ename##_ctrl) + +static struct snd_soc_dapm_widget tegra210_adsp_widgets[] = { + ADSP_FE_WIDGETS("ADSP-FE1", adsp_fe1, TEGRA210_ADSP_FRONT_END1), + ADSP_FE_WIDGETS("ADSP-FE2", adsp_fe2, TEGRA210_ADSP_FRONT_END2), + ADSP_FE_WIDGETS("ADSP-FE3", adsp_fe3, TEGRA210_ADSP_FRONT_END3), + ADSP_FE_WIDGETS("ADSP-FE4", adsp_fe4, TEGRA210_ADSP_FRONT_END4), + ADSP_FE_WIDGETS("ADSP-FE5", adsp_fe5, TEGRA210_ADSP_FRONT_END5), + ADSP_FE_WIDGETS("ADSP-FE6", adsp_fe6, TEGRA210_ADSP_FRONT_END6), + ADSP_FE_WIDGETS("ADSP-FE7", adsp_fe7, TEGRA210_ADSP_FRONT_END7), + ADSP_FE_WIDGETS("ADSP-FE8", adsp_fe8, TEGRA210_ADSP_FRONT_END8), + ADSP_FE_WIDGETS("ADSP-FE9", adsp_fe9, TEGRA210_ADSP_FRONT_END9), + ADSP_FE_WIDGETS("ADSP-FE10", adsp_fe10, TEGRA210_ADSP_FRONT_END10), + ADSP_FE_WIDGETS("ADSP-FE11", adsp_fe11, TEGRA210_ADSP_FRONT_END11), + ADSP_FE_WIDGETS("ADSP-FE12", adsp_fe12, TEGRA210_ADSP_FRONT_END12), + ADSP_FE_WIDGETS("ADSP-FE13", adsp_fe13, TEGRA210_ADSP_FRONT_END13), + ADSP_FE_WIDGETS("ADSP-FE14", adsp_fe14, TEGRA210_ADSP_FRONT_END14), + ADSP_FE_WIDGETS("ADSP-FE15", adsp_fe15, TEGRA210_ADSP_FRONT_END15), + ADSP_EP_WIDGETS("ADSP-EAVB", adsp_eavb), + ADSP_EP_WIDGETS("ADSP-ADMAIF1", adsp_admaif1), + ADSP_EP_WIDGETS("ADSP-ADMAIF2", adsp_admaif2), + ADSP_EP_WIDGETS("ADSP-ADMAIF3", adsp_admaif3), + ADSP_EP_WIDGETS("ADSP-ADMAIF4", adsp_admaif4), + ADSP_EP_WIDGETS("ADSP-ADMAIF5", adsp_admaif5), + ADSP_EP_WIDGETS("ADSP-ADMAIF6", adsp_admaif6), + ADSP_EP_WIDGETS("ADSP-ADMAIF7", adsp_admaif7), + ADSP_EP_WIDGETS("ADSP-ADMAIF8", adsp_admaif8), + ADSP_EP_WIDGETS("ADSP-ADMAIF9", adsp_admaif9), + ADSP_EP_WIDGETS("ADSP-ADMAIF10", adsp_admaif10), + ADSP_EP_WIDGETS("ADSP-ADMAIF11", adsp_admaif11), + ADSP_EP_WIDGETS("ADSP-ADMAIF12", adsp_admaif12), + ADSP_EP_WIDGETS("ADSP-ADMAIF13", adsp_admaif13), + ADSP_EP_WIDGETS("ADSP-ADMAIF14", adsp_admaif14), + ADSP_EP_WIDGETS("ADSP-ADMAIF15", adsp_admaif15), + ADSP_EP_WIDGETS("ADSP-ADMAIF16", adsp_admaif16), + ADSP_EP_WIDGETS("ADSP-ADMAIF17", adsp_admaif17), + ADSP_EP_WIDGETS("ADSP-ADMAIF18", adsp_admaif18), + ADSP_EP_WIDGETS("ADSP-ADMAIF19", adsp_admaif19), + ADSP_EP_WIDGETS("ADSP-ADMAIF20", adsp_admaif20), + ADSP_WIDGETS("APM-IN1", apm_in1, TEGRA210_ADSP_APM_IN1), + ADSP_WIDGETS("APM-IN2", apm_in2, TEGRA210_ADSP_APM_IN2), + ADSP_WIDGETS("APM-IN3", apm_in3, TEGRA210_ADSP_APM_IN3), + ADSP_WIDGETS("APM-IN4", apm_in4, TEGRA210_ADSP_APM_IN4), + ADSP_WIDGETS("APM-IN5", apm_in5, TEGRA210_ADSP_APM_IN5), + ADSP_WIDGETS("APM-IN6", apm_in6, TEGRA210_ADSP_APM_IN6), + ADSP_WIDGETS("APM-IN7", apm_in7, TEGRA210_ADSP_APM_IN7), + ADSP_WIDGETS("APM-IN8", apm_in8, TEGRA210_ADSP_APM_IN8), + ADSP_WIDGETS("APM-IN9", apm_in9, TEGRA210_ADSP_APM_IN9), + ADSP_WIDGETS("APM-IN10", apm_in10, TEGRA210_ADSP_APM_IN10), + ADSP_WIDGETS("APM-IN11", apm_in11, TEGRA210_ADSP_APM_IN11), + ADSP_WIDGETS("APM-IN12", apm_in12, TEGRA210_ADSP_APM_IN12), + ADSP_WIDGETS("APM-IN13", apm_in13, TEGRA210_ADSP_APM_IN13), + ADSP_WIDGETS("APM-IN14", apm_in14, TEGRA210_ADSP_APM_IN14), + ADSP_WIDGETS("APM-IN15", apm_in15, TEGRA210_ADSP_APM_IN15), + ADSP_WIDGETS("APM-OUT1", apm_out1, TEGRA210_ADSP_APM_OUT1), + ADSP_WIDGETS("APM-OUT2", apm_out2, TEGRA210_ADSP_APM_OUT2), + ADSP_WIDGETS("APM-OUT3", apm_out3, TEGRA210_ADSP_APM_OUT3), + ADSP_WIDGETS("APM-OUT4", apm_out4, TEGRA210_ADSP_APM_OUT4), + ADSP_WIDGETS("APM-OUT5", apm_out5, TEGRA210_ADSP_APM_OUT5), + ADSP_WIDGETS("APM-OUT6", apm_out6, TEGRA210_ADSP_APM_OUT6), + ADSP_WIDGETS("APM-OUT7", apm_out7, TEGRA210_ADSP_APM_OUT7), + ADSP_WIDGETS("APM-OUT8", apm_out8, TEGRA210_ADSP_APM_OUT8), + ADSP_WIDGETS("APM-OUT9", apm_out9, TEGRA210_ADSP_APM_OUT9), + ADSP_WIDGETS("APM-OUT10", apm_out10, TEGRA210_ADSP_APM_OUT10), + ADSP_WIDGETS("APM-OUT11", apm_out11, TEGRA210_ADSP_APM_OUT11), + ADSP_WIDGETS("APM-OUT12", apm_out12, TEGRA210_ADSP_APM_OUT12), + ADSP_WIDGETS("APM-OUT13", apm_out13, TEGRA210_ADSP_APM_OUT13), + ADSP_WIDGETS("APM-OUT14", apm_out14, TEGRA210_ADSP_APM_OUT14), + ADSP_WIDGETS("APM-OUT15", apm_out15, TEGRA210_ADSP_APM_OUT15), + + ADSP_WIDGETS("ADMA1", adma1, TEGRA210_ADSP_PLUGIN_ADMA1), + ADSP_WIDGETS("ADMA2", adma2, TEGRA210_ADSP_PLUGIN_ADMA2), + ADSP_WIDGETS("ADMA3", adma3, TEGRA210_ADSP_PLUGIN_ADMA3), + ADSP_WIDGETS("ADMA4", adma4, TEGRA210_ADSP_PLUGIN_ADMA4), + ADSP_WIDGETS("ADMA5", adma5, TEGRA210_ADSP_PLUGIN_ADMA5), + ADSP_WIDGETS("ADMA6", adma6, TEGRA210_ADSP_PLUGIN_ADMA6), + ADSP_WIDGETS("ADMA7", adma7, TEGRA210_ADSP_PLUGIN_ADMA7), + ADSP_WIDGETS("ADMA8", adma8, TEGRA210_ADSP_PLUGIN_ADMA8), + ADSP_WIDGETS("ADMA9", adma9, TEGRA210_ADSP_PLUGIN_ADMA9), + ADSP_WIDGETS("ADMA10", adma10, TEGRA210_ADSP_PLUGIN_ADMA10), + ADSP_WIDGETS("ADMA11", adma11, TEGRA210_ADSP_PLUGIN_ADMA11), + ADSP_WIDGETS("ADMA12", adma12, TEGRA210_ADSP_PLUGIN_ADMA12), + ADSP_WIDGETS("ADMA13", adma13, TEGRA210_ADSP_PLUGIN_ADMA13), + ADSP_WIDGETS("ADMA14", adma14, TEGRA210_ADSP_PLUGIN_ADMA14), + ADSP_WIDGETS("ADMA15", adma15, TEGRA210_ADSP_PLUGIN_ADMA15), + ADSP_WIDGETS("ADMA1-TX", adma1_tx, TEGRA210_ADSP_PLUGIN_ADMA1_TX), + ADSP_WIDGETS("ADMA2-TX", adma2_tx, TEGRA210_ADSP_PLUGIN_ADMA2_TX), + ADSP_WIDGETS("ADMA3-TX", adma3_tx, TEGRA210_ADSP_PLUGIN_ADMA3_TX), + ADSP_WIDGETS("ADMA4-TX", adma4_tx, TEGRA210_ADSP_PLUGIN_ADMA4_TX), + ADSP_WIDGETS("ADMA5-TX", adma5_tx, TEGRA210_ADSP_PLUGIN_ADMA5_TX), + ADSP_WIDGETS("ADMA6-TX", adma6_tx, TEGRA210_ADSP_PLUGIN_ADMA6_TX), + ADSP_WIDGETS("ADMA7-TX", adma7_tx, TEGRA210_ADSP_PLUGIN_ADMA7_TX), + ADSP_WIDGETS("ADMA8-TX", adma8_tx, TEGRA210_ADSP_PLUGIN_ADMA8_TX), + ADSP_WIDGETS("ADMA9-TX", adma9_tx, TEGRA210_ADSP_PLUGIN_ADMA9_TX), + ADSP_WIDGETS("ADMA10-TX", adma10_tx, TEGRA210_ADSP_PLUGIN_ADMA10_TX), + ADSP_WIDGETS("ADMA11-TX", adma11_tx, TEGRA210_ADSP_PLUGIN_ADMA11_TX), + ADSP_WIDGETS("ADMA12-TX", adma12_tx, TEGRA210_ADSP_PLUGIN_ADMA12_TX), + ADSP_WIDGETS("ADMA13-TX", adma13_tx, TEGRA210_ADSP_PLUGIN_ADMA13_TX), + ADSP_WIDGETS("ADMA14-TX", adma14_tx, TEGRA210_ADSP_PLUGIN_ADMA14_TX), + ADSP_WIDGETS("ADMA15-TX", adma15_tx, TEGRA210_ADSP_PLUGIN_ADMA15_TX), + ADSP_WIDGETS("PLUGIN1-PLACE-HOLDER", plugin1, TEGRA210_ADSP_PLUGIN1), + ADSP_WIDGETS("PLUGIN2-PLACE-HOLDER", plugin2, TEGRA210_ADSP_PLUGIN2), + ADSP_WIDGETS("PLUGIN3-PLACE-HOLDER", plugin3, TEGRA210_ADSP_PLUGIN3), + ADSP_WIDGETS("PLUGIN4-PLACE-HOLDER", plugin4, TEGRA210_ADSP_PLUGIN4), + ADSP_WIDGETS("PLUGIN5-PLACE-HOLDER", plugin5, TEGRA210_ADSP_PLUGIN5), + ADSP_WIDGETS("PLUGIN6-PLACE-HOLDER", plugin6, TEGRA210_ADSP_PLUGIN6), + ADSP_WIDGETS("PLUGIN7-PLACE-HOLDER", plugin7, TEGRA210_ADSP_PLUGIN7), + ADSP_WIDGETS("PLUGIN8-PLACE-HOLDER", plugin8, TEGRA210_ADSP_PLUGIN8), + ADSP_WIDGETS("PLUGIN9-PLACE-HOLDER", plugin9, TEGRA210_ADSP_PLUGIN9), + ADSP_WIDGETS("PLUGIN10-PLACE-HOLDER", plugin10, TEGRA210_ADSP_PLUGIN10), + ADSP_WIDGETS("PLUGIN11-PLACE-HOLDER", plugin11, TEGRA210_ADSP_PLUGIN11), + ADSP_WIDGETS("PLUGIN12-PLACE-HOLDER", plugin12, TEGRA210_ADSP_PLUGIN12), + ADSP_WIDGETS("PLUGIN13-PLACE-HOLDER", plugin13, TEGRA210_ADSP_PLUGIN13), + ADSP_WIDGETS("PLUGIN14-PLACE-HOLDER", plugin14, TEGRA210_ADSP_PLUGIN14), + ADSP_WIDGETS("PLUGIN15-PLACE-HOLDER", plugin15, TEGRA210_ADSP_PLUGIN15), + ADSP_WIDGETS("PLUGIN16-PLACE-HOLDER", plugin16, TEGRA210_ADSP_PLUGIN16), + ADSP_WIDGETS("PLUGIN17-PLACE-HOLDER", plugin17, TEGRA210_ADSP_PLUGIN17), + ADSP_WIDGETS("PLUGIN18-PLACE-HOLDER", plugin18, TEGRA210_ADSP_PLUGIN18), + ADSP_WIDGETS("PLUGIN19-PLACE-HOLDER", plugin19, TEGRA210_ADSP_PLUGIN19), + ADSP_WIDGETS("PLUGIN20-PLACE-HOLDER", plugin20, TEGRA210_ADSP_PLUGIN20), +}; + +#define ADSP_EP_ROUTES(name) \ + { name " MUX", "ADSP-FE1", "ADSP-FE1 RX"}, \ + { name " MUX", "ADSP-FE2", "ADSP-FE2 RX"}, \ + { name " MUX", "ADSP-FE3", "ADSP-FE3 RX"}, \ + { name " MUX", "ADSP-FE4", "ADSP-FE4 RX"}, \ + { name " MUX", "ADSP-FE5", "ADSP-FE5 RX"}, \ + { name " MUX", "ADSP-FE6", "ADSP-FE6 RX"}, \ + { name " MUX", "ADSP-FE7", "ADSP-FE7 RX"}, \ + { name " MUX", "ADSP-FE8", "ADSP-FE8 RX"}, \ + { name " MUX", "ADSP-FE9", "ADSP-FE9 RX"}, \ + { name " MUX", "ADSP-FE10", "ADSP-FE10 RX"}, \ + { name " MUX", "ADSP-FE11", "ADSP-FE11 RX"}, \ + { name " MUX", "ADSP-FE12", "ADSP-FE12 RX"}, \ + { name " MUX", "ADSP-FE13", "ADSP-FE13 RX"}, \ + { name " MUX", "ADSP-FE14", "ADSP-FE14 RX"}, \ + { name " MUX", "ADSP-FE15", "ADSP-FE15 RX"}, \ + { name " MUX", "ADSP-ADMAIF1", "ADSP-ADMAIF1 RX"}, \ + { name " MUX", "ADSP-ADMAIF2", "ADSP-ADMAIF2 RX"}, \ + { name " MUX", "ADSP-ADMAIF3", "ADSP-ADMAIF3 RX"}, \ + { name " MUX", "ADSP-ADMAIF4", "ADSP-ADMAIF4 RX"}, \ + { name " MUX", "ADSP-ADMAIF5", "ADSP-ADMAIF5 RX"}, \ + { name " MUX", "ADSP-ADMAIF6", "ADSP-ADMAIF6 RX"}, \ + { name " MUX", "ADSP-ADMAIF7", "ADSP-ADMAIF7 RX"}, \ + { name " MUX", "ADSP-ADMAIF8", "ADSP-ADMAIF8 RX"}, \ + { name " MUX", "ADSP-ADMAIF9", "ADSP-ADMAIF9 RX"}, \ + { name " MUX", "ADSP-ADMAIF10", "ADSP-ADMAIF10 RX"}, \ + { name " MUX", "ADSP-ADMAIF11", "ADSP-ADMAIF11 RX"}, \ + { name " MUX", "ADSP-ADMAIF12", "ADSP-ADMAIF12 RX"}, \ + { name " MUX", "ADSP-ADMAIF13", "ADSP-ADMAIF13 RX"}, \ + { name " MUX", "ADSP-ADMAIF14", "ADSP-ADMAIF14 RX"}, \ + { name " MUX", "ADSP-ADMAIF15", "ADSP-ADMAIF15 RX"}, \ + { name " MUX", "ADSP-ADMAIF16", "ADSP-ADMAIF16 RX"}, \ + { name " MUX", "ADSP-ADMAIF17", "ADSP-ADMAIF17 RX"}, \ + { name " MUX", "ADSP-ADMAIF18", "ADSP-ADMAIF18 RX"}, \ + { name " MUX", "ADSP-ADMAIF19", "ADSP-ADMAIF19 RX"}, \ + { name " MUX", "ADSP-ADMAIF20", "ADSP-ADMAIF20 RX"}, \ + { name " MUX", "ADSP-EAVB", "ADSP-EAVB RX"} + +#define ADSP_APM_IN_ROUTES(name) \ + { name " MUX", "APM-IN1", "APM-IN1 TX"}, \ + { name " MUX", "APM-IN2", "APM-IN2 TX"}, \ + { name " MUX", "APM-IN3", "APM-IN3 TX"}, \ + { name " MUX", "APM-IN4", "APM-IN4 TX"}, \ + { name " MUX", "APM-IN5", "APM-IN5 TX"}, \ + { name " MUX", "APM-IN6", "APM-IN6 TX"}, \ + { name " MUX", "APM-IN7", "APM-IN7 TX"}, \ + { name " MUX", "APM-IN8", "APM-IN8 TX"}, \ + { name " MUX", "APM-IN9", "APM-IN9 TX"}, \ + { name " MUX", "APM-IN10", "APM-IN10 TX"}, \ + { name " MUX", "APM-IN11", "APM-IN11 TX"}, \ + { name " MUX", "APM-IN12", "APM-IN12 TX"}, \ + { name " MUX", "APM-IN13", "APM-IN13 TX"}, \ + { name " MUX", "APM-IN14", "APM-IN14 TX"}, \ + { name " MUX", "APM-IN15", "APM-IN15 TX"} + + +#define ADSP_APM_OUT_ROUTES(name) \ + { name " MUX", "APM-OUT1", "APM-OUT1 TX"}, \ + { name " MUX", "APM-OUT2", "APM-OUT2 TX"}, \ + { name " MUX", "APM-OUT3", "APM-OUT3 TX"}, \ + { name " MUX", "APM-OUT4", "APM-OUT4 TX"}, \ + { name " MUX", "APM-OUT5", "APM-OUT5 TX"}, \ + { name " MUX", "APM-OUT6", "APM-OUT6 TX"}, \ + { name " MUX", "APM-OUT7", "APM-OUT7 TX"}, \ + { name " MUX", "APM-OUT8", "APM-OUT8 TX"}, \ + { name " MUX", "APM-OUT9", "APM-OUT9 TX"}, \ + { name " MUX", "APM-OUT10", "APM-OUT10 TX"}, \ + { name " MUX", "APM-OUT11", "APM-OUT11 TX"}, \ + { name " MUX", "APM-OUT12", "APM-OUT12 TX"}, \ + { name " MUX", "APM-OUT13", "APM-OUT13 TX"}, \ + { name " MUX", "APM-OUT14", "APM-OUT14 TX"}, \ + { name " MUX", "APM-OUT15", "APM-OUT15 TX"} + + +#define ADSP_ADMA_ROUTES(name) \ + { name " MUX", "ADMA1", "ADMA1 TX"}, \ + { name " MUX", "ADMA2", "ADMA2 TX"}, \ + { name " MUX", "ADMA3", "ADMA3 TX"}, \ + { name " MUX", "ADMA4", "ADMA4 TX"}, \ + { name " MUX", "ADMA5", "ADMA5 TX"}, \ + { name " MUX", "ADMA6", "ADMA6 TX"}, \ + { name " MUX", "ADMA7", "ADMA7 TX"}, \ + { name " MUX", "ADMA8", "ADMA8 TX"}, \ + { name " MUX", "ADMA9", "ADMA9 TX"}, \ + { name " MUX", "ADMA10", "ADMA10 TX"}, \ + { name " MUX", "ADMA11", "ADMA11 TX"}, \ + { name " MUX", "ADMA12", "ADMA12 TX"}, \ + { name " MUX", "ADMA13", "ADMA13 TX"}, \ + { name " MUX", "ADMA14", "ADMA14 TX"}, \ + { name " MUX", "ADMA15", "ADMA15 TX"}, \ + { name " MUX", "ADMA1-TX", "ADMA1-TX TX"}, \ + { name " MUX", "ADMA2-TX", "ADMA2-TX TX"}, \ + { name " MUX", "ADMA3-TX", "ADMA3-TX TX"}, \ + { name " MUX", "ADMA4-TX", "ADMA4-TX TX"}, \ + { name " MUX", "ADMA5-TX", "ADMA5-TX TX"}, \ + { name " MUX", "ADMA6-TX", "ADMA6-TX TX"}, \ + { name " MUX", "ADMA7-TX", "ADMA7-TX TX"}, \ + { name " MUX", "ADMA8-TX", "ADMA8-TX TX"}, \ + { name " MUX", "ADMA9-TX", "ADMA9-TX TX"}, \ + { name " MUX", "ADMA10-TX", "ADMA10-TX TX"}, \ + { name " MUX", "ADMA11-TX", "ADMA11-TX TX"}, \ + { name " MUX", "ADMA12-TX", "ADMA12-TX TX"}, \ + { name " MUX", "ADMA13-TX", "ADMA13-TX TX"}, \ + { name " MUX", "ADMA14-TX", "ADMA14-TX TX"}, \ + { name " MUX", "ADMA15-TX", "ADMA15-TX TX"} +#define ADSP_PLUGIN_ROUTES(name) \ + { name " MUX", "PLUGIN1-PLACE-HOLDER", "PLUGIN1-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN2-PLACE-HOLDER", "PLUGIN2-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN3-PLACE-HOLDER", "PLUGIN3-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN4-PLACE-HOLDER", "PLUGIN4-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN5-PLACE-HOLDER", "PLUGIN5-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN6-PLACE-HOLDER", "PLUGIN6-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN7-PLACE-HOLDER", "PLUGIN7-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN8-PLACE-HOLDER", "PLUGIN8-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN9-PLACE-HOLDER", "PLUGIN9-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN10-PLACE-HOLDER", "PLUGIN10-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN11-PLACE-HOLDER", "PLUGIN11-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN12-PLACE-HOLDER", "PLUGIN12-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN13-PLACE-HOLDER", "PLUGIN13-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN14-PLACE-HOLDER", "PLUGIN14-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN15-PLACE-HOLDER", "PLUGIN15-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN16-PLACE-HOLDER", "PLUGIN16-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN17-PLACE-HOLDER", "PLUGIN17-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN18-PLACE-HOLDER", "PLUGIN18-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN19-PLACE-HOLDER", "PLUGIN19-PLACE-HOLDER TX"}, \ + { name " MUX", "PLUGIN20-PLACE-HOLDER", "PLUGIN20-PLACE-HOLDER TX"} + +#define ADSP_EP_MUX_ROUTES(name) \ + { name " RX", NULL, name " Receive"}, \ + { name " Transmit", NULL, name " TX"}, \ + { name " TX", NULL, name " MUX"}, \ + ADSP_APM_OUT_ROUTES(name) + +#define ADSP_APM_IN_MUX_ROUTES(name) \ + { name " TX", NULL, name " MUX"}, \ + ADSP_EP_ROUTES(name), \ + ADSP_APM_OUT_ROUTES(name) + +#define ADSP_APM_OUT_MUX_ROUTES(name) \ + { name " TX", NULL, name " MUX"}, \ + ADSP_ADMA_ROUTES(name), \ + ADSP_PLUGIN_ROUTES(name) + +#define ADSP_PLUGIN_MUX_ROUTES(name) \ + { name " TX", NULL, name " MUX"}, \ + ADSP_APM_IN_ROUTES(name), \ + ADSP_PLUGIN_ROUTES(name), \ + ADSP_ADMA_ROUTES(name) + +#define ADSP_ADMA_MUX_ROUTES(name) \ + { name " TX", NULL, name " MUX"}, \ + ADSP_APM_IN_ROUTES(name), \ + ADSP_PLUGIN_ROUTES(name) + +#define ADSP_ADMA_OUT_MUX_ROUTES(name) \ + { name " TX", NULL, name " MUX"}, \ + ADSP_APM_IN_ROUTES(name), \ + ADSP_PLUGIN_ROUTES(name) + +static struct snd_soc_dapm_route tegra210_adsp_routes[] = { + ADSP_EP_MUX_ROUTES("ADSP-FE1"), + ADSP_EP_MUX_ROUTES("ADSP-FE2"), + ADSP_EP_MUX_ROUTES("ADSP-FE3"), + ADSP_EP_MUX_ROUTES("ADSP-FE4"), + ADSP_EP_MUX_ROUTES("ADSP-FE5"), + ADSP_EP_MUX_ROUTES("ADSP-FE6"), + ADSP_EP_MUX_ROUTES("ADSP-FE7"), + ADSP_EP_MUX_ROUTES("ADSP-FE8"), + ADSP_EP_MUX_ROUTES("ADSP-FE9"), + ADSP_EP_MUX_ROUTES("ADSP-FE10"), + ADSP_EP_MUX_ROUTES("ADSP-FE11"), + ADSP_EP_MUX_ROUTES("ADSP-FE12"), + ADSP_EP_MUX_ROUTES("ADSP-FE13"), + ADSP_EP_MUX_ROUTES("ADSP-FE14"), + ADSP_EP_MUX_ROUTES("ADSP-FE15"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF1"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF2"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF3"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF4"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF5"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF6"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF7"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF8"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF9"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF10"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF11"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF12"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF13"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF14"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF15"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF16"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF17"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF18"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF19"), + ADSP_EP_MUX_ROUTES("ADSP-ADMAIF20"), + + ADSP_EP_MUX_ROUTES("ADSP-EAVB"), + + ADSP_APM_IN_MUX_ROUTES("APM-IN1"), + ADSP_APM_IN_MUX_ROUTES("APM-IN2"), + ADSP_APM_IN_MUX_ROUTES("APM-IN3"), + ADSP_APM_IN_MUX_ROUTES("APM-IN4"), + ADSP_APM_IN_MUX_ROUTES("APM-IN5"), + ADSP_APM_IN_MUX_ROUTES("APM-IN6"), + ADSP_APM_IN_MUX_ROUTES("APM-IN7"), + ADSP_APM_IN_MUX_ROUTES("APM-IN8"), + ADSP_APM_IN_MUX_ROUTES("APM-IN9"), + ADSP_APM_IN_MUX_ROUTES("APM-IN10"), + ADSP_APM_IN_MUX_ROUTES("APM-IN11"), + ADSP_APM_IN_MUX_ROUTES("APM-IN12"), + ADSP_APM_IN_MUX_ROUTES("APM-IN13"), + ADSP_APM_IN_MUX_ROUTES("APM-IN14"), + ADSP_APM_IN_MUX_ROUTES("APM-IN15"), + + ADSP_APM_OUT_MUX_ROUTES("APM-OUT1"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT2"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT3"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT4"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT5"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT6"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT7"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT8"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT9"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT10"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT11"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT12"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT13"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT14"), + ADSP_APM_OUT_MUX_ROUTES("APM-OUT15"), + + ADSP_ADMA_MUX_ROUTES("ADMA1"), + ADSP_ADMA_MUX_ROUTES("ADMA2"), + ADSP_ADMA_MUX_ROUTES("ADMA3"), + ADSP_ADMA_MUX_ROUTES("ADMA4"), + ADSP_ADMA_MUX_ROUTES("ADMA5"), + ADSP_ADMA_MUX_ROUTES("ADMA6"), + ADSP_ADMA_MUX_ROUTES("ADMA7"), + ADSP_ADMA_MUX_ROUTES("ADMA8"), + ADSP_ADMA_MUX_ROUTES("ADMA9"), + ADSP_ADMA_MUX_ROUTES("ADMA10"), + ADSP_ADMA_MUX_ROUTES("ADMA11"), + ADSP_ADMA_MUX_ROUTES("ADMA12"), + ADSP_ADMA_MUX_ROUTES("ADMA13"), + ADSP_ADMA_MUX_ROUTES("ADMA14"), + ADSP_ADMA_MUX_ROUTES("ADMA15"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA1-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA2-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA3-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA4-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA5-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA6-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA7-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA8-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA9-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA10-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA11-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA12-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA13-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA14-TX"), + ADSP_ADMA_OUT_MUX_ROUTES("ADMA15-TX"), + + ADSP_PLUGIN_MUX_ROUTES("PLUGIN1-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN2-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN3-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN4-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN5-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN6-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN7-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN8-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN9-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN10-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN11-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN12-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN13-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN14-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN15-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN16-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN17-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN18-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN19-PLACE-HOLDER"), + ADSP_PLUGIN_MUX_ROUTES("PLUGIN20-PLACE-HOLDER"), +}; + +static void tegra210_adsp_wt_replace(struct device *dev, char **dest1, + const char *src) +{ + char *dest = NULL; + + if (!*dest1 || !src) + return; + + dest = (char *)devm_kzalloc(dev, strlen(src) + 5, GFP_KERNEL); + if (strstr(*dest1, " TX")) { + strcpy(dest, src); + strcat(dest, " TX"); + *dest1 = dest; + } else if (strstr(*dest1, " RX")) { + strcpy(dest, src); + strcat(dest, " RX"); + *dest1 = dest; + } else if (strstr(*dest1, " MUX")) { + strcpy(dest, src); + strcat(dest, " MUX"); + *dest1 = dest; + } else if (strstr(*dest1, " Transmit")) + ;/* do nothing */ + else if (strstr(*dest1, " Receive")) + ;/* do nothing */ + else { + strcpy(dest, src); + *dest1 = dest; + } +} + +static void tegra210_adsp_route_modify(struct device *dev, + const char *wt_default, const char *wt_from_dt) +{ + int i, len, ret; + + if (!wt_default || !wt_from_dt) + return; + + ret = device_property_read_string_array(dev, "fe-info", + NULL, 0); + len = strlen(wt_default); + /* Modify dapm routing table */ + for (i = ((ret > 0) ? 0 : TEGRA210_ADSP_ROUTE_BASE); + i < ARRAY_SIZE(tegra210_adsp_routes); i++) { + /* replace sink name */ + if (tegra210_adsp_routes[i].sink) + if (strstr(tegra210_adsp_routes[i].sink, wt_default) && + (tegra210_adsp_routes[i].sink[len] == ' ' || + tegra210_adsp_routes[i].sink[len] == 0)) + tegra210_adsp_wt_replace(dev, + (char **)&tegra210_adsp_routes[i].sink, + wt_from_dt); + /* replace control name */ + if (tegra210_adsp_routes[i].control) + if (strstr(tegra210_adsp_routes[i].control, wt_default) + && (tegra210_adsp_routes[i].control[len] == ' ' + || tegra210_adsp_routes[i].control[len] == 0)) + tegra210_adsp_wt_replace(dev, + (char **)&tegra210_adsp_routes[i].control, + wt_from_dt); + /* replace source name */ + if (tegra210_adsp_routes[i].source) + if (strstr(tegra210_adsp_routes[i].source, wt_default) + && (tegra210_adsp_routes[i].source[len] == ' ' + || tegra210_adsp_routes[i].source[len] == 0)) + tegra210_adsp_wt_replace(dev, + (char **)&tegra210_adsp_routes[i].source, + wt_from_dt); + } +} + +static void adsp_fe_name_override(struct device *dev, int count) +{ + int ret = 0, i; + const char **fe_names; + char *name; + + fe_names = devm_kcalloc(dev, count, sizeof(*fe_names), + GFP_KERNEL); + if (!fe_names) + return; + + ret = device_property_read_string_array(dev, "fe-info", + fe_names, count); + ret = ret > ADSP_FE_END ? ADSP_FE_END : ret; + + for (i = 0 ; i < ret; i++) { + tegra210_adsp_route_modify(dev, + tegra210_adsp_mux_texts[1+i], + fe_names[i]); + + name = devm_kzalloc(dev, strlen(fe_names[i]) + 3, + GFP_KERNEL); + strcpy((char *)name, fe_names[i]); + strcat((char *)name, " TX"); + tegra210_adsp_widgets[(3*i)+1].name = name; + + name = devm_kzalloc(dev, strlen(fe_names[i]) + 4, + GFP_KERNEL); + strcpy((char *)name, fe_names[i]); + strcat((char *)name, " MUX"); + tegra210_adsp_widgets[(3*i)+2].name = name; + + name = devm_kzalloc(dev, strlen(fe_names[i]) + 3, + GFP_KERNEL); + strcpy((char *)name, fe_names[i]); + strcat((char *)name, " RX"); + tegra210_adsp_widgets[(3*i)].name = name; + tegra210_adsp_mux_texts[1+i] = fe_names[i]; + } +} + +static int tegra210_adsp_param_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + + if (params->mask == SNDRV_CTL_ELEM_TYPE_INTEGER) { + params->num_regs = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffffffff; + } + uinfo->type = params->mask; + uinfo->count = params->num_regs; + + return 0; +} + +static int tegra210_adsp_get_param(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + + if (params->mask == SNDRV_CTL_ELEM_TYPE_INTEGER) + memset(ucontrol->value.integer.value, 0, + params->num_regs * sizeof(long)); + else + memset(ucontrol->value.bytes.data, 0, + params->num_regs); + + return 0; +} + +/* tegra210_adsp_set_param - sets plugin parameters + * @default: byte_format + * @byte_format: nvfx_call_params_t based structure + * @int_format: ,<#params>,,,.... + */ +static int tegra210_adsp_set_param(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_app *app = &adsp->apps[params->base]; + apm_msg_t apm_msg; + int ret; + + if (!adsp->init_done || adsp->is_shutdown) { + dev_warn(adsp->dev, "ADSP is not booted yet\n"); + return -EPERM; + } + + if (!app->plugin) { + dev_warn(adsp->dev, "Unable to set %s, plugin not initialized\n", + kcontrol->id.name); + return -EPERM; + } + + if (!app->connect) { + dev_warn(adsp->dev, "%s, plugin not yet connected\n", + kcontrol->id.name); + return -EPERM; + } + + apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_set_param_params_t); + apm_msg.msg.call_params.size = sizeof(apm_fx_set_param_params_t); + apm_msg.msg.call_params.method = nvfx_apm_method_fx_set_param; + apm_msg.msg.fx_set_param_params.plugin.pvoid = + app->plugin->plugin.pvoid; + + switch (params->mask) { + case SNDRV_CTL_ELEM_TYPE_INTEGER: + { + int32_t num_params, i; + /* check number of params to pass */ + num_params = (int32_t)ucontrol->value.integer.value[1]; + if (num_params < 1) { + dev_warn(adsp->dev, "No params to pass to the plugin\n"); + return 0; + } + + if (num_params + 2 > + sizeof(apm_msg.msg.fx_set_param_params.params)/ + sizeof(apm_msg.msg.fx_set_param_params.params[0])) { + dev_err(adsp->dev, "parameter too large\n"); + return -EINVAL; + } + + apm_msg.msg.fx_set_param_params.params[0] = + (sizeof(nvfx_call_params_t) + + num_params * sizeof(int32_t)); + + /* initialize the method */ + apm_msg.msg.fx_set_param_params.params[1] = + (int32_t)ucontrol->value.integer.value[0]; + + /* copy parameters */ + for (i = 0; i < num_params; i++) + apm_msg.msg.fx_set_param_params.params[i + 2] = + (int32_t)ucontrol->value.integer.value[i + 2]; + } + break; + + case SNDRV_CTL_ELEM_TYPE_BYTES: + { + nvfx_call_params_t *call_params = + (nvfx_call_params_t *)ucontrol->value.bytes.data; + + if (call_params->size > + sizeof(apm_msg.msg.fx_set_param_params.params)) { + dev_err(adsp->dev, "parameter too large\n"); + return -EINVAL; + } + + /* copy parameters */ + memcpy(&apm_msg.msg.fx_set_param_params.params, + call_params, call_params->size); + } + break; + + default: + return -EINVAL; + } + + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + return ret; + } + ret = tegra210_adsp_send_msg(app, &apm_msg, + TEGRA210_ADSP_MSG_FLAG_SEND | TEGRA210_ADSP_MSG_FLAG_NEED_ACK); + pm_runtime_put(adsp->dev); + + return ret; +} + +static int tegra210_adsp_tlv_callback(struct snd_kcontrol *kcontrol, + int op_flag, unsigned int size, unsigned int __user *tlv) +{ + struct tegra_soc_bytes *params = (void *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + unsigned int count = size < params->num_regs ? size : params->num_regs; + struct tegra210_adsp_app *app = &adsp->apps[params->base]; + unsigned int *tlv_data; + int ret = 0; + + if (!adsp->init_done || adsp->is_shutdown) { + dev_warn(adsp->dev, "ADSP is not booted yet\n"); + return 0; + } + + if (!app->plugin) { + dev_warn(adsp->dev, "Plugin not yet initialized\n"); + return 0; + } + + if (!app->connect) { + dev_warn(adsp->dev, "%s, plugin not yet connected\n", + kcontrol->id.name); + return 0; + } + + tlv_data = devm_kzalloc(adsp->dev, count, GFP_KERNEL); + if (!tlv_data) + return -ENOMEM; + + switch (op_flag) { + case SNDRV_CTL_TLV_OP_WRITE: + { + apm_raw_data_msg_t *apm_msg; + nvfx_call_params_t *call_params; + + apm_msg = devm_kzalloc(adsp->dev, sizeof(apm_raw_data_msg_t), + GFP_KERNEL); + if (!apm_msg) { + dev_err(adsp->dev, "Failed to allocate memory for message\n"); + ret = -ENOMEM; + goto end; + } + apm_msg->msgq_msg.size = + MSGQ_MSG_WSIZE(apm_fx_raw_data_params_t); + apm_msg->msg.call_params.size = + sizeof(apm_fx_raw_data_params_t); + apm_msg->msg.call_params.method = nvfx_apm_method_write_data; + apm_msg->msg.fx_raw_data_params.plugin.pvoid = + app->plugin->plugin.pvoid; + + if (copy_from_user(tlv_data, tlv, count)) { + ret = -EFAULT; + devm_kfree(adsp->dev, apm_msg); + goto end; + } + + call_params = (nvfx_call_params_t *)tlv_data; + if (call_params->size <= 0 || + (call_params->size > + sizeof(apm_msg->msg.fx_raw_data_params.data))) { + ret = -EINVAL; + devm_kfree(adsp->dev, apm_msg); + goto end; + } + + memcpy(&apm_msg->msg.fx_raw_data_params.data, + call_params, call_params->size); + + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + devm_kfree(adsp->dev, apm_msg); + goto end; + } + ret = tegra210_adsp_send_raw_data_msg(app, apm_msg); + pm_runtime_put(adsp->dev); + devm_kfree(adsp->dev, apm_msg); + break; + } + case SNDRV_CTL_TLV_OP_READ: + { + int src = app->reg; + struct tegra210_adsp_app *apm; + + while (!IS_APM_IN(src)) + src = tegra210_adsp_get_source(adsp, src); + apm = &adsp->apps[src]; + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + goto end; + } + ret = tegra210_adsp_send_data_request_msg(app, count, + TEGRA210_ADSP_MSG_FLAG_SEND); + if (ret < 0) { + pm_runtime_put(adsp->dev); + dev_err(adsp->dev, "Raw read request dropped\n"); + ret = -ETIMEDOUT; + goto end; + } + ret = wait_for_completion_interruptible_timeout( + apm->raw_msg_read_complete, + msecs_to_jiffies(ADSP_RESPONSE_TIMEOUT)); + pm_runtime_put(adsp->dev); + if (ret <= 0) { + dev_err(adsp->dev, "No data received\n"); + ret = -ETIMEDOUT; + goto end; + } + if (copy_to_user(tlv, apm->read_data.data, count)) { + ret = -EFAULT; + goto end; + } + } + } + +end: + devm_kfree(adsp->dev, tlv_data); + return ret; +} + +static int tegra210_adsp_apm_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_app *app = &adsp->apps[mc->reg]; + + if (strstr(kcontrol->id.name, "Priority")) { + ucontrol->value.integer.value[0] = app->priority; + } else if (strstr(kcontrol->id.name, "Min ADSP Clock")) { + ucontrol->value.integer.value[0] = app->min_adsp_clock; + } else if (strstr(kcontrol->id.name, "Input Mode")) { + ucontrol->value.integer.value[0] = app->input_mode; + } + return 0; +} + +static int tegra210_adsp_apm_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra210_adsp *adsp = snd_soc_component_get_drvdata(cmpnt); + struct tegra210_adsp_app *app = &adsp->apps[mc->reg]; + int ret = 0; + + /* Controls here may execute whether or not APM is initialized */ + if (strstr(kcontrol->id.name, "Min ADSP Clock")) { + app->min_adsp_clock = ucontrol->value.integer.value[0]; + return 0; + } + + if (strstr(kcontrol->id.name, "Priority")) + app->priority = ucontrol->value.integer.value[0]; + else if (strstr(kcontrol->id.name, "Input Mode")) + app->input_mode = ucontrol->value.integer.value[0]; + + if (!adsp->init_done || adsp->is_shutdown || !app->plugin) + return 0; + + ret = pm_runtime_get_sync(adsp->dev); + if (ret < 0) { + dev_err(adsp->dev, "%s pm_runtime_get_sync error 0x%x\n", + __func__, ret); + return ret; + } + + if (strstr(kcontrol->id.name, "Priority")) + ret = tegra210_adsp_send_app_priority(app); + else if (strstr(kcontrol->id.name, "Input Mode")) + ret = tegra210_adsp_send_app_inputmode(app); + + pm_runtime_put(adsp->dev); + + + return ret; +} + +/* Maximum 128 integers or 512 bytes allowed */ +#define SND_SOC_PARAM_EXT(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = tegra210_adsp_param_info, \ + .get = tegra210_adsp_get_param, \ + .put = tegra210_adsp_set_param, \ + .private_value = \ + ((unsigned long)&(struct tegra_soc_bytes) \ + {.base = xbase, .num_regs = 512, \ + .mask = SNDRV_CTL_ELEM_TYPE_BYTES}) } + +#define SND_SOC_PARAM_TLV(xname, xbase, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .tlv.c = tegra210_adsp_tlv_callback, \ + .info = snd_soc_bytes_info_ext, \ + .private_value = \ + ((unsigned long)&(struct tegra_soc_bytes) \ + {.base = xbase, .num_regs = xcount, \ + .mask = SNDRV_CTL_ELEM_TYPE_BYTES}) } + +#define APM_CONTROL(xname, xmax) \ + SOC_SINGLE_EXT("APM1 " xname, TEGRA210_ADSP_APM_IN1, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM2 " xname, TEGRA210_ADSP_APM_IN2, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM3 " xname, TEGRA210_ADSP_APM_IN3, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM4 " xname, TEGRA210_ADSP_APM_IN4, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM5 " xname, TEGRA210_ADSP_APM_IN5, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM6 " xname, TEGRA210_ADSP_APM_IN6, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM7 " xname, TEGRA210_ADSP_APM_IN7, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM8 " xname, TEGRA210_ADSP_APM_IN8, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM9 " xname, TEGRA210_ADSP_APM_IN9, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM10 " xname, TEGRA210_ADSP_APM_IN10, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM11 " xname, TEGRA210_ADSP_APM_IN11, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM12 " xname, TEGRA210_ADSP_APM_IN12, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM13 " xname, TEGRA210_ADSP_APM_IN13, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM14 " xname, TEGRA210_ADSP_APM_IN14, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put), \ + SOC_SINGLE_EXT("APM15 " xname, TEGRA210_ADSP_APM_IN15, 0, xmax, 0,\ + tegra210_adsp_apm_get, tegra210_adsp_apm_put) + + +/* Any new addition of control should be added after PLUGIN controls otherwise +* the index of PLUGIN needs to be changed with define PLUGIN_SET_PARAMS_IDX and +* PLUGIN_SEND_BYTES_IDX */ +static struct snd_kcontrol_new tegra210_adsp_controls[] = { + SOC_SINGLE_BOOL_EXT("ADSP init", 0, + tegra210_adsp_init_get, tegra210_adsp_init_put), + SND_SOC_PARAM_EXT("PLUGIN1-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN1), + SND_SOC_PARAM_EXT("PLUGIN2-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN2), + SND_SOC_PARAM_EXT("PLUGIN3-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN3), + SND_SOC_PARAM_EXT("PLUGIN4-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN4), + SND_SOC_PARAM_EXT("PLUGIN5-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN5), + SND_SOC_PARAM_EXT("PLUGIN6-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN6), + SND_SOC_PARAM_EXT("PLUGIN7-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN7), + SND_SOC_PARAM_EXT("PLUGIN8-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN8), + SND_SOC_PARAM_EXT("PLUGIN9-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN9), + SND_SOC_PARAM_EXT("PLUGIN10-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN10), + SND_SOC_PARAM_EXT("PLUGIN11-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN11), + SND_SOC_PARAM_EXT("PLUGIN12-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN12), + SND_SOC_PARAM_EXT("PLUGIN13-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN13), + SND_SOC_PARAM_EXT("PLUGIN14-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN14), + SND_SOC_PARAM_EXT("PLUGIN15-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN15), + SND_SOC_PARAM_EXT("PLUGIN16-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN16), + SND_SOC_PARAM_EXT("PLUGIN17-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN17), + SND_SOC_PARAM_EXT("PLUGIN18-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN18), + SND_SOC_PARAM_EXT("PLUGIN19-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN19), + SND_SOC_PARAM_EXT("PLUGIN20-PLACE-HOLDER set params", + TEGRA210_ADSP_PLUGIN20), + SND_SOC_PARAM_TLV("PLUGIN1-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN1, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN2-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN2, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN3-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN3, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN4-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN4, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN5-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN5, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN6-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN6, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN7-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN7, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN8-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN8, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN9-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN9, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN10-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN10, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN11-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN11, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN12-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN12, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN13-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN13, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN14-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN14, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN15-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN15, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN16-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN16, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN17-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN17, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN18-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN18, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN19-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN19, 0x1000), + SND_SOC_PARAM_TLV("PLUGIN20-PLACE-HOLDER send bytes", + TEGRA210_ADSP_PLUGIN20, 0x1000), + SND_SOC_PARAM_EXT("ADMA1 set params", + TEGRA210_ADSP_PLUGIN_ADMA1), + SND_SOC_PARAM_EXT("ADMA2 set params", + TEGRA210_ADSP_PLUGIN_ADMA2), + SND_SOC_PARAM_EXT("ADMA3 set params", + TEGRA210_ADSP_PLUGIN_ADMA3), + SND_SOC_PARAM_EXT("ADMA4 set params", + TEGRA210_ADSP_PLUGIN_ADMA4), + SND_SOC_PARAM_EXT("ADMA5 set params", + TEGRA210_ADSP_PLUGIN_ADMA5), + SND_SOC_PARAM_EXT("ADMA6 set params", + TEGRA210_ADSP_PLUGIN_ADMA6), + SND_SOC_PARAM_EXT("ADMA7 set params", + TEGRA210_ADSP_PLUGIN_ADMA7), + SND_SOC_PARAM_EXT("ADMA8 set params", + TEGRA210_ADSP_PLUGIN_ADMA8), + SND_SOC_PARAM_EXT("ADMA9 set params", + TEGRA210_ADSP_PLUGIN_ADMA9), + SND_SOC_PARAM_EXT("ADMA10 set params", + TEGRA210_ADSP_PLUGIN_ADMA10), + SND_SOC_PARAM_EXT("ADMA11 set params", + TEGRA210_ADSP_PLUGIN_ADMA11), + SND_SOC_PARAM_EXT("ADMA12 set params", + TEGRA210_ADSP_PLUGIN_ADMA12), + SND_SOC_PARAM_EXT("ADMA13 set params", + TEGRA210_ADSP_PLUGIN_ADMA13), + SND_SOC_PARAM_EXT("ADMA14 set params", + TEGRA210_ADSP_PLUGIN_ADMA14), + SND_SOC_PARAM_EXT("ADMA15 set params", + TEGRA210_ADSP_PLUGIN_ADMA15), + SND_SOC_PARAM_EXT("ADMA1-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA1_TX), + SND_SOC_PARAM_EXT("ADMA2-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA2_TX), + SND_SOC_PARAM_EXT("ADMA3-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA3_TX), + SND_SOC_PARAM_EXT("ADMA4-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA4_TX), + SND_SOC_PARAM_EXT("ADMA5-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA5_TX), + SND_SOC_PARAM_EXT("ADMA6-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA6_TX), + SND_SOC_PARAM_EXT("ADMA7-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA7_TX), + SND_SOC_PARAM_EXT("ADMA8-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA8_TX), + SND_SOC_PARAM_EXT("ADMA9-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA9_TX), + SND_SOC_PARAM_EXT("ADMA10-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA10_TX), + SND_SOC_PARAM_EXT("ADMA11-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA11_TX), + SND_SOC_PARAM_EXT("ADMA12-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA12_TX), + SND_SOC_PARAM_EXT("ADMA13-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA13_TX), + SND_SOC_PARAM_EXT("ADMA14-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA14_TX), + SND_SOC_PARAM_EXT("ADMA15-TX set params", + TEGRA210_ADSP_PLUGIN_ADMA15_TX), + + APM_CONTROL("Priority", APM_PRIORITY_MAX), + APM_CONTROL("Min ADSP Clock", INT_MAX), + APM_CONTROL("Input Mode", INT_MAX), +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD + SOC_SINGLE_BOOL_EXT("lpthread init", 0, + tegra210_adsp_lpthread_init_get, tegra210_adsp_lpthread_init_put), +#endif +}; + +static int tegra210_adsp_component_probe(struct snd_soc_component *cmpnt) +{ + struct tegra210_adsp *adsp = dev_get_drvdata(cmpnt->dev); + + if (!adsp) + return -ENODEV; + + snd_soc_component_set_drvdata(cmpnt, adsp); + + return 0; +} + +static struct snd_soc_component_driver tegra210_adsp_cmpnt = { + .name = DRV_NAME, + .dapm_widgets = tegra210_adsp_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra210_adsp_widgets), + .dapm_routes = tegra210_adsp_routes, + .num_dapm_routes = ARRAY_SIZE(tegra210_adsp_routes), + .controls = tegra210_adsp_controls, + .num_controls = ARRAY_SIZE(tegra210_adsp_controls), + .probe = tegra210_adsp_component_probe, + .pcm_construct = tegra210_adsp_pcm_construct, + .pcm_destruct = tegra210_adsp_pcm_destruct, + + /* PCM ops */ + .open = tegra210_adsp_pcm_open, + .close = tegra210_adsp_pcm_close, + .hw_params = tegra210_adsp_pcm_hw_params, + .hw_free = tegra210_adsp_pcm_hw_free, + .prepare = tegra210_adsp_pcm_prepare, + .trigger = tegra210_adsp_pcm_trigger, + .pointer = tegra210_adsp_pcm_pointer, + .ack = tegra210_adsp_component_pcm_ack, + + .compress_ops = &tegra210_adsp_compress_ops, + .read = tegra210_adsp_read, + .write = tegra210_adsp_write, +}; + +static u64 tegra_dma_mask = DMA_BIT_MASK(32); + +static struct adsp_soc_data adsp_soc_data_t186 = { + .is_soc_t210 = false, + .max_adma_ch = TEGRA186_MAX_ADMA_CHANNEL, +}; + +static const struct of_device_id tegra210_adsp_audio_of_match[] = { + { .compatible = "nvidia,tegra210-adsp-audio-hv", .data = &adsp_soc_data_t186}, + {}, +}; + +static void adsp_control_name_override(struct device *dev, unsigned int wt_idx, unsigned int i, + unsigned int mux_idx) +{ + char *name = devm_kzalloc(dev, strlen(adsp_app_desc[i].wt_name) + 3, + GFP_KERNEL); + strcpy((char *)name, adsp_app_desc[i].wt_name); + strcat((char *)name, " TX"); + tegra210_adsp_widgets[wt_idx].name = name; + + name = devm_kzalloc(dev, strlen(adsp_app_desc[i].wt_name) + 4, + GFP_KERNEL); + strcpy((char *)name, adsp_app_desc[i].wt_name); + strcat((char *)name, " MUX"); + tegra210_adsp_widgets[wt_idx+PLUGIN_SET_PARAMS_IDX].name = name; + + name = devm_kzalloc(dev, strlen(adsp_app_desc[i].wt_name) + 12, + GFP_KERNEL); + strcpy((char *)name, adsp_app_desc[i].wt_name); + strcat((char *)name, " set params"); + tegra210_adsp_controls[i+PLUGIN_SET_PARAMS_IDX].name = name; + + name = devm_kzalloc(dev, strlen(adsp_app_desc[i].wt_name) + 12, + GFP_KERNEL); + strcpy((char *)name, adsp_app_desc[i].wt_name); + strcat((char *)name, " send bytes"); + tegra210_adsp_controls[i+PLUGIN_SEND_BYTES_IDX].name = name; + + name = devm_kzalloc(dev, strlen(adsp_app_desc[i].wt_name) + 1, + GFP_KERNEL); + strcpy((char *)name, adsp_app_desc[i].wt_name); + tegra210_adsp_mux_texts[mux_idx] = name; +} + +static int tegra210_adsp_audio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node, *subnp; + const struct of_device_id *match; + struct tegra_soc_bytes *controls; + struct tegra210_adsp *adsp; + int32_t j, ret = 0; + uint32_t i, wt_idx, mux_idx, compr_ops = 1; + uint32_t adma_ch_page = 0; + uint32_t adma_ch_start = TEGRA210_ADSP_ADMA_CHANNEL_START_HV; + uint32_t adma_ch_cnt = TEGRA210_ADSP_ADMA_CHANNEL_COUNT; + char plugin_info[20], apm_info[20]; + u32 param_mask; + struct netlink_kernel_cfg cfg = { + .input = tegra210_adsp_nl_recv_msg, + }; + + pr_info("%s: platform probe started\n", __func__); + + if (dev_set_name(&pdev->dev, "%s", DRV_NAME_ADSP) < 0) { + dev_err(&pdev->dev, "error in setting adsp device name\n"); + return -ENODEV; + } + + match = of_match_device(tegra210_adsp_audio_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + + adsp = devm_kzalloc(&pdev->dev, sizeof(*adsp), GFP_KERNEL); + if (!adsp) { + dev_err(&pdev->dev, "Can't allocate tegra210_adsp_ctx\n"); + return -ENOMEM; + } + dev_set_drvdata(&pdev->dev, adsp); + adsp->dev = &pdev->dev; + adsp->is_shutdown = false; + adsp->soc_data = (struct adsp_soc_data *)match->data; + + + + adsp->ahub_clk = devm_clk_get(&pdev->dev, "ahub"); + if (IS_ERR(adsp->ahub_clk)) { + dev_err(&pdev->dev, "Error: Missing AHUB clock\n"); + ret = PTR_ERR(adsp->ahub_clk); + goto err; + } + + adsp->ape_clk = devm_clk_get(&pdev->dev, "ape"); + if (IS_ERR(adsp->ape_clk)) { + dev_err(&pdev->dev, "Error: Missing APE clock\n"); + ret = PTR_ERR(adsp->ape_clk); + goto err; + } + + if (!adsp->soc_data->is_soc_t210) { + adsp->apb2ape_clk = devm_clk_get(&pdev->dev, "apb2ape"); + if (IS_ERR(adsp->apb2ape_clk)) { + dev_err(&pdev->dev, "Error: Missing APB2APE clock\n"); + ret = PTR_ERR(adsp->apb2ape_clk); + goto err; + } + } + + /* TODO: Add mixer control to set I2S playback rate */ + adsp->i2s_rate = 48000; + INIT_WORK(&adsp->override_freq_work, tegra_adsp_override_freq_worker); + mutex_init(&adsp->mutex); + pdev->dev.dma_mask = &tegra_dma_mask; + pdev->dev.coherent_dma_mask = tegra_dma_mask; + + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) + goto err_pm_disable; + + for (i = 0; i < TEGRA210_ADSP_VIRT_REG_MAX; i++) { + adsp->apps[i].reg = i; + adsp->apps[i].priority = APM_PRIORITY_DEFAULT; + adsp->apps[i].min_adsp_clock = 0; + adsp->apps[i].input_mode = NVFX_APM_INPUT_MODE_PUSH; + } + + ret = device_property_read_string_array(&pdev->dev, "fe-info", + NULL, 0); + if (ret > 0) + adsp_fe_name_override(&pdev->dev, ret); + + /* get the plugin count */ + if (of_property_read_u32(pdev->dev.of_node, + "num-plugin", + &adsp_app_count) < 0) { + dev_warn(&pdev->dev, "Missing ADSP plugin count\n"); + adsp_app_count = 0; + } + + /* check for maximum number of plugins */ + if (adsp_app_count > PLUGIN_NUM) { + dev_err(&pdev->dev, "num-plugin: %d\n", adsp_app_count); + dev_err(&pdev->dev, "Max plugins supported: %d\n", + PLUGIN_NUM); + adsp_app_count = PLUGIN_NUM; + } + + /* allocate memory for app descritors */ + adsp_app_desc = devm_kzalloc(&pdev->dev, + (adsp_app_count + ARRAY_SIZE(adsp_app_minimal)) + * sizeof(*adsp_app_desc), GFP_KERNEL); + if (!adsp_app_desc) { + dev_err(&pdev->dev, "Can't allocate tegra210_adsp_app descriptor\n"); + ret = -ENOMEM; + goto err_pm_disable; + } + + /* parse the plugin, firmware, widget names and params */ + for (i = 0; i < adsp_app_count; i++) { + memset((void *)plugin_info, '\0', 20); + if ((sprintf(plugin_info, "plugin-info-%u", i+1)) < 0) + return -EINVAL; + subnp = of_get_child_by_name(np, plugin_info); + if (subnp) { + if (of_property_read_string(subnp, "plugin-name", + &adsp_app_desc[i].name)) { + dev_err(&pdev->dev, + "Missing property plugin-name\n"); + ret = -EINVAL; + goto err_pm_disable; + } + if (of_property_read_string(subnp, "firmware-name", + &adsp_app_desc[i].fw_name)) { + dev_err(&pdev->dev, + "Missing property firmware-name\n"); + ret = -EINVAL; + goto err_pm_disable; + } + if (of_property_read_string(subnp, "widget-name", + &adsp_app_desc[i].wt_name)) { + dev_warn(&pdev->dev, + "Missing property widget-name for %s\n", + adsp_app_desc[i].name); + adsp_app_desc[i].wt_name = NULL; + } else { + if (adsp_app_desc[i].wt_name) { + /* override the widget names from DT if any */ + mux_idx = TEGRA210_ADSP_PLUGIN1 + i; + wt_idx = TEGRA210_ADSP_WIDGET_BASE + (2*i); + if (strlen(tegra210_adsp_mux_texts[mux_idx]) < + strlen(adsp_app_desc[i].wt_name)) { + dev_err(&pdev->dev, + "Widget name too long %s, allowed len %zu\n", + adsp_app_desc[i].wt_name, + strlen(tegra210_adsp_mux_texts[mux_idx])); + continue; + } + tegra210_adsp_route_modify(&pdev->dev, + tegra210_adsp_mux_texts[mux_idx], + adsp_app_desc[i].wt_name); + + adsp_control_name_override(&pdev->dev, + wt_idx, i, mux_idx); + + } + } + if (of_property_read_u32(subnp, "param-type", + ¶m_mask)) { + dev_info(&pdev->dev, + "Default param-type to BYTE for %s\n", + adsp_app_desc[i].name); + adsp_app_desc[i].param_type = + SNDRV_CTL_ELEM_TYPE_BYTES; + } else { + /* override the param-type from DT if any */ + if (param_mask == + TEGRA_SNDRV_CTL_ELEM_TYPE_INTEGER) { + adsp_app_desc[i].param_type = + SNDRV_CTL_ELEM_TYPE_INTEGER; + } else { + adsp_app_desc[i].param_type = + SNDRV_CTL_ELEM_TYPE_BYTES; + } + controls = + (void *)tegra210_adsp_controls[i+1].private_value; + controls->mask = adsp_app_desc[i].param_type; + } + adsp_app_desc[i].reg_start = TEGRA210_ADSP_PLUGIN1 + i; + adsp_app_desc[i].reg_end = TEGRA210_ADSP_PLUGIN1 + i; + } else { + dev_err(&pdev->dev, + "Property '%s' missing or invalid\n", + plugin_info); + ret = -EINVAL; + goto err_pm_disable; + } + } + + + /* copy basic apps needed */ + memcpy(&adsp_app_desc[adsp_app_count], + &adsp_app_minimal[0], sizeof(adsp_app_minimal)); + adsp_app_count += ARRAY_SIZE(adsp_app_minimal); + + for (i = 0; i < adsp_app_count; i++) { + for (j = adsp_app_desc[i].reg_start; + j <= adsp_app_desc[i].reg_end; j++) + adsp->apps[j].desc = &adsp_app_desc[i]; + } + + /* enable/disable compr-ops from DT */ + if (of_property_read_u32(pdev->dev.of_node, "compr-ops", &compr_ops) || + (compr_ops == 0u)) + tegra210_adsp_cmpnt.compress_ops = NULL; + + if (of_property_read_u32_index(pdev->dev.of_node, "nvidia,adma_ch_page", + 0, &adma_ch_page)) { + dev_info(&pdev->dev, "adma channel page address dt entry not found\n"); + dev_info(&pdev->dev, "using adma channel page 0\n"); + } + adsp->adma_ch_page = adma_ch_page; + + adma_ch_start = TEGRA210_ADSP_ADMA_CHANNEL_START_HV; + + if (of_property_read_u32_index(pdev->dev.of_node, + "nvidia,adma_ch_start", 0, &adma_ch_start)) { + dev_info(&pdev->dev, "adma channel start dt entry not found\n"); + dev_info(&pdev->dev, "using adma channels from 16\n"); + } + adsp->adma_ch_start = (adma_ch_start > adsp->soc_data->max_adma_ch) + ? adsp->soc_data->max_adma_ch : adma_ch_start; + + if (of_property_read_u32_index(pdev->dev.of_node, + "nvidia,adma_ch_cnt", 0, &adma_ch_cnt)) { + dev_info(&pdev->dev, "adma channel cnt dt entry not found\n"); + dev_info(&pdev->dev, "using default adma channels count 10\n"); + } + adsp->adma_ch_cnt = adma_ch_cnt; + + if ((adsp->adma_ch_start + adsp->adma_ch_cnt) > + adsp->soc_data->max_adma_ch) { + adsp->adma_ch_cnt = + adsp->soc_data->max_adma_ch - adsp->adma_ch_start; + dev_info(&pdev->dev, "adma channel cnt set to %d\n", + adsp->adma_ch_cnt); + } + + ret = snd_soc_register_component(&pdev->dev, &tegra210_adsp_cmpnt, + tegra210_adsp_cmpnt_dai, + ARRAY_SIZE(tegra210_adsp_cmpnt_dai)); + if (ret != 0) { + dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret); + goto err_unregister_component; + } + + for (i = 0; i < (APM_IN_END - APM_IN_START + 1); i++) { + apm_stack_size[i] = 0; + memset((void *)apm_info, '\0', 20); + if ((sprintf(apm_info, "apm%d-stack-size", i+1)) < 0) + goto err_unregister_component; + if (of_property_read_u32(pdev->dev.of_node, apm_info, + &apm_stack_size[i])) + continue; + } + + adsp->nl_sk = + netlink_kernel_create(&init_net, NETLINK_ADSP_EVENT, &cfg); + if (!adsp->nl_sk) { + dev_err(&pdev->dev, "Error creating socket\n"); + ret = -ENOMEM; + goto err_unregister_component; + } + + dev_info(&pdev->dev, "Tegra210 ADSP driver successfully registered\n"); + + return 0; + +err_unregister_component: + snd_soc_unregister_component(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); +err: + return ret; +} + +static int tegra210_adsp_audio_remove(struct platform_device *pdev) +{ + struct tegra210_adsp *adsp = dev_get_drvdata(&pdev->dev); + + netlink_kernel_release(adsp->nl_sk); + snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static void tegra210_adsp_audio_shutdown( + struct platform_device *pdev) +{ + struct tegra210_adsp *adsp = dev_get_drvdata(&pdev->dev); + + tegra210_adsp_deinit(adsp); + adsp->is_shutdown = true; +} + +static const struct dev_pm_ops tegra210_adsp_pm_ops = { + SET_RUNTIME_PM_OPS(tegra210_adsp_runtime_suspend, + tegra210_adsp_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + +}; + +static struct platform_driver tegra210_adsp_audio_driver = { + .driver = { + .name = DRV_NAME_ADSP, + .owner = THIS_MODULE, + .of_match_table = tegra210_adsp_audio_of_match, + .pm = &tegra210_adsp_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = tegra210_adsp_audio_probe, + .shutdown = tegra210_adsp_audio_shutdown, + .remove = tegra210_adsp_audio_remove, +}; +module_platform_driver(tegra210_adsp_audio_driver); + +MODULE_AUTHOR("Sumit Bhattacharya "); +MODULE_DESCRIPTION("Tegra210 ADSP Audio driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME_ADSP); +MODULE_DEVICE_TABLE(of, tegra210_adsp_audio_of_match); diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 58dcf20f..d187f7b8 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -6,6 +6,7 @@ snd-soc-tegra-utils-objs := tegra_asoc_utils.o tegra_asoc_machine.o \ snd-soc-tegra210-afc-objs := tegra210_afc.o snd-soc-tegra210-iqc-objs := tegra210_iqc.o snd-soc-tegra186-arad-objs := tegra186_arad.o +snd-soc-tegra210-adsp-objs := tegra210_adsp.o snd-soc-tegra-machine-driver-objs := tegra_machine_driver.o snd-soc-tegra-controls-objs := tegra_mixer_control.o @@ -13,5 +14,6 @@ obj-m += snd-soc-tegra-utils.o obj-m += snd-soc-tegra210-afc.o obj-m += snd-soc-tegra210-iqc.o obj-m += snd-soc-tegra186-arad.o +obj-m += snd-soc-tegra210-adsp.o obj-m += snd-soc-tegra-machine-driver.o obj-m += snd-soc-tegra-controls.o diff --git a/sound/soc/tegra/tegra210_adsp.c b/sound/soc/tegra/tegra210_adsp.c index dbac867f..25c9d780 100644 --- a/sound/soc/tegra/tegra210_adsp.c +++ b/sound/soc/tegra/tegra210_adsp.c @@ -3,7 +3,7 @@ * tegra210_adsp.c - Tegra ADSP audio driver * * Author: Sumit Bhattacharya - * Copyright (c) 2014-2022 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2024 NVIDIA CORPORATION. All rights reserved. * */ @@ -13,7 +13,6 @@ #include #include #include -#include <../arch/arm/mach-tegra/iomap.h> #include #include #include @@ -35,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -373,11 +373,6 @@ static void tegra210_adsp_deallocate_dma_buffer(struct snd_dma_buffer *buf) buf->addr = 0; } -#if IS_ENABLED(CONFIG_TEGRA210_ADMA) -/* implemented in adma driver */ -void tegra_adma_dump_ch_reg(void); -#endif - /* ADSP OS boot and init API */ static int tegra210_adsp_init(struct tegra210_adsp *adsp) { @@ -411,10 +406,9 @@ static int tegra210_adsp_init(struct tegra210_adsp *adsp) } } -#if IS_ENABLED(CONFIG_TEGRA210_ADMA) - /* set callback function for adsp to dump adma registers for debug */ - nvadsp_set_adma_dump_reg(&tegra_adma_dump_ch_reg); -#endif + /* TODO: set callback function for adsp to dump adma registers for debug + * purpose, see more details in Bug 3798682 + */ /* Suspend OS for now. Resume will happen via runtime pm calls */ ret = nvadsp_os_suspend(); @@ -2848,19 +2842,6 @@ static struct snd_soc_dai_ops tegra210_adsp_eavb_dai_ops = { }, \ } -#define ADSP_COMPR_DAI(id) \ - { \ - .name = "ADSP COMPR" #id, \ - .compress_new = snd_soc_new_compress, \ - .playback = { \ - .stream_name = "ADSP COMPR" #id " Receive", \ - .channels_min = 1, \ - .channels_max = 2, \ - .rates = SNDRV_PCM_RATE_8000_48000, \ - .formats = SNDRV_PCM_FMTBIT_S16_LE, \ - }, \ - } - #define ADSP_EAVB_DAI() \ { \ .name = "ADSP EAVB", \ @@ -2999,8 +2980,6 @@ static struct snd_soc_dai_driver tegra210_adsp_cmpnt_dai[] = { ADSP_PCM_DAI(13), ADSP_PCM_DAI(14), ADSP_PCM_DAI(15), - ADSP_COMPR_DAI(1), - ADSP_COMPR_DAI(2), ADSP_EAVB_DAI(), }; @@ -4824,5 +4803,4 @@ MODULE_AUTHOR("Sumit Bhattacharya "); MODULE_DESCRIPTION("Tegra210 ADSP Audio driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); -MODULE_DEVICE_TABLE(of, tegra210_adsp_audio_of_match); - +MODULE_DEVICE_TABLE(of, tegra210_adsp_audio_of_match); \ No newline at end of file