summaryrefslogtreecommitdiff
path: root/sound/soc/omap/abe/abe_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/omap/abe/abe_api.c')
-rw-r--r--sound/soc/omap/abe/abe_api.c2040
1 files changed, 2040 insertions, 0 deletions
diff --git a/sound/soc/omap/abe/abe_api.c b/sound/soc/omap/abe/abe_api.c
new file mode 100644
index 00000000000..a09ca1e1add
--- /dev/null
+++ b/sound/soc/omap/abe/abe_api.c
@@ -0,0 +1,2040 @@
+/*
+ * ALSA SoC OMAP ABE driver
+ *
+ * Author: Laurent Le Faucheur <l-le-faucheur@ti.com>
+ * Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include "abe_main.h"
+#include "abe_typedef.h"
+#include "abe_initxxx_labels.h"
+#include "abe_dbg.h"
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+u32 warm_boot = 0;
+
+/**
+ * abe_reset_hal - reset the ABE/HAL
+ * @rdev: regulator source
+ * @constraints: constraints to apply
+ *
+ * Operations : reset the HAL by reloading the static variables and
+ * default AESS registers.
+ * Called after a PRCM cold-start reset of ABE
+ */
+abehal_status abe_reset_hal(void)
+{
+ u32 i;
+ _log(id_reset_hal, 0, 0, 0);
+ abe_dbg_output = TERMINAL_OUTPUT;
+ abe_dbg_activity_log_write_pointer = 0;
+ /* IRQ & DBG circular read pointer in DMEM */
+ abe_irq_dbg_read_ptr = 0;
+ /* PDM_DL enable/disable collisions */
+ pdm_dl1_status = 0;
+ pdm_dl2_status = 0;
+ pdm_vib_status = 0;
+ /* default = disable the mixer's adaptive gain control */
+ abe_use_compensated_gain(0);
+ /* reset the default gain values */
+ for (i = 0; i < MAX_NBGAIN_CMEM; i++) {
+ abe_muted_gains_indicator[i] = 0;
+ abe_desired_gains_decibel[i] = (u32) GAIN_MUTE;
+ abe_desired_gains_linear[i] = 0;
+ abe_desired_ramp_delay_ms[i] = 0;
+ abe_muted_gains_decibel[i] = (u32) GAIN_TOOLOW;
+ }
+ /* set debug mask to "enable all traces" */
+ abe_dbg_mask = (abe_dbg_t) (0);
+ abe_hw_configuration();
+ return 0;
+}
+EXPORT_SYMBOL(abe_reset_hal);
+/**
+ * abe_load_fw_param - Load ABE Firmware memories
+ * @PMEM: Pointer of Program memory data
+ * @PMEM_SIZE: Size of PMEM data
+ * @CMEM: Pointer of Coeffients memory data
+ * @CMEM_SIZE: Size of CMEM data
+ * @SMEM: Pointer of Sample memory data
+ * @SMEM_SIZE: Size of SMEM data
+ * @DMEM: Pointer of Data memory data
+ * @DMEM_SIZE: Size of DMEM data
+ *
+ * loads the Audio Engine firmware, generate a single pulse on the Event
+ * generator to let execution start, read the version number returned from
+ * this execution.
+ */
+abehal_status abe_load_fw_param(u32 *ABE_FW)
+{
+ u32 event_gen;
+ u32 pmem_size, dmem_size, smem_size, cmem_size;
+ u32 *pmem_ptr, *dmem_ptr, *smem_ptr, *cmem_ptr, *fw_ptr;
+ _log(id_load_fw_param, 0, 0, 0);
+#if PC_SIMULATION
+ /* the code is loaded from the Checkers */
+#else
+#define ABE_FW_OFFSET 5
+ fw_ptr = ABE_FW;
+ abe_firmware_version_number = *fw_ptr++;
+ pmem_size = *fw_ptr++;
+ cmem_size = *fw_ptr++;
+ dmem_size = *fw_ptr++;
+ smem_size = *fw_ptr++;
+ pmem_ptr = fw_ptr;
+ cmem_ptr = pmem_ptr + (pmem_size >> 2);
+ dmem_ptr = cmem_ptr + (cmem_size >> 2);
+ smem_ptr = dmem_ptr + (dmem_size >> 2);
+ /* do not load PMEM */
+ if (warm_boot) {
+ /* Stop the event Generator */
+ event_gen = 0;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ EVENT_GENERATOR_START, &event_gen, 4);
+ /* Now we are sure the firmware is stalled */
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_CMEM, 0, cmem_ptr,
+ cmem_size);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_SMEM, 0, smem_ptr,
+ smem_size);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, 0, dmem_ptr,
+ dmem_size);
+ /* Restore the event Generator status */
+ event_gen = 1;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ EVENT_GENERATOR_START, &event_gen, 4);
+ } else {
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_PMEM, 0, pmem_ptr,
+ pmem_size);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_CMEM, 0, cmem_ptr,
+ cmem_size);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_SMEM, 0, smem_ptr,
+ smem_size);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, 0, dmem_ptr,
+ dmem_size);
+ }
+ warm_boot = 1;
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(abe_load_fw_param);
+/**
+ * abe_load_fw - Load ABE Firmware and initialize memories
+ *
+ * loads the Audio Engine firmware, generate a single pulse on the Event
+ * generator to let execution start, read the version number returned from
+ * this execution.
+ */
+abehal_status abe_load_fw(void)
+{
+ _log(id_load_fw, 0, 0, 0);
+ abe_load_fw_param((u32 *) abe_firmware_array);
+ abe_reset_all_ports();
+ abe_build_scheduler_table();
+ abe_reset_all_sequence();
+ abe_select_main_port(PDM_DL_PORT);
+ return 0;
+}
+EXPORT_SYMBOL(abe_load_fw);
+/**
+ * abe_reload_fw - Reload ABE Firmware after OFF mode
+ *
+ * loads the Audio Engine firmware, generate a single pulse on the Event
+ * generator to let execution start, read the version number returned from
+ * this execution.
+ */
+abehal_status abe_reload_fw(void)
+{
+ warm_boot = 0;
+ abe_load_fw_param((u32 *) abe_firmware_array);
+ abe_build_scheduler_table();
+
+ /* IRQ & DBG circular read pointer in DMEM */
+ abe_dbg_activity_log_write_pointer = 0;
+ abe_irq_dbg_read_ptr = 0;
+
+ /* Restore Gains not managed by the drivers */
+ abe_write_gain(GAINS_SPLIT, GAIN_0dB, RAMP_100MS, GAIN_LEFT_OFFSET);
+ abe_write_gain(GAINS_SPLIT, GAIN_0dB, RAMP_100MS, GAIN_RIGHT_OFFSET);
+ abe_write_gain(GAINS_DL1, GAIN_0dB, RAMP_100MS, GAIN_LEFT_OFFSET);
+ abe_write_gain(GAINS_DL1, GAIN_0dB, RAMP_100MS, GAIN_RIGHT_OFFSET);
+ abe_write_gain(GAINS_DL2, GAIN_0dB, RAMP_100MS, GAIN_LEFT_OFFSET);
+ abe_write_gain(GAINS_DL2, GAIN_0dB, RAMP_100MS, GAIN_RIGHT_OFFSET);
+
+ return 0;
+}
+EXPORT_SYMBOL(abe_reload_fw);
+/**
+ * abe_read_hardware_configuration - Return default HW periferals configuration
+ * @u: use-case description list (pointer)
+ * @o: opp mode (pointer)
+ * @hw: pointer to the output HW structure
+ *
+ * Parameter :
+ * U : use-case description list (pointer)
+ * H : pointer to the output structure
+ *
+ * Operations :
+ * return a structure with the HW thresholds compatible with the HAL/FW/AESS_ATC
+ * will be upgraded in FW06
+ * return a structure with the HW thresholds compatible with the HAL/FW/AESS_ATC
+ */
+abehal_status abe_read_hardware_configuration(u32 *u, u32 *o,
+ abe_hw_config_init_t *hw)
+{
+ _log(id_read_hardware_configuration, (u32) u,
+ (u32) u >> 8, (u32) u >> 16);
+ abe_read_use_case_opp(u, o);
+ /* 0: 96kHz 1:192kHz */
+ hw->MCPDM_CTRL__DIV_SEL = 0;
+ /* 0: no command in the FIFO, 1: 6 data on each lines (with commands) */
+ hw->MCPDM_CTRL__CMD_INT = 1;
+ /* 0:MSB aligned 1:LSB aligned */
+ hw->MCPDM_CTRL__PDMOUTFORMAT = 0;
+ hw->MCPDM_CTRL__PDM_DN5_EN = 1;
+ hw->MCPDM_CTRL__PDM_DN4_EN = 1;
+ hw->MCPDM_CTRL__PDM_DN3_EN = 1;
+ hw->MCPDM_CTRL__PDM_DN2_EN = 1;
+ hw->MCPDM_CTRL__PDM_DN1_EN = 1;
+ hw->MCPDM_CTRL__PDM_UP3_EN = 0;
+ hw->MCPDM_CTRL__PDM_UP2_EN = 1;
+ hw->MCPDM_CTRL__PDM_UP1_EN = 1;
+ /* All the McPDM_DL FIFOs are enabled simultaneously */
+ hw->MCPDM_FIFO_CTRL_DN__DN_TRESH = MCPDM_DL_ITER / 6;
+ /* number of ATC access upon AMIC DMArequests, 2 the FIFOs channels
+ are enabled */
+ hw->MCPDM_FIFO_CTRL_UP__UP_TRESH = MCPDM_UL_ITER / 2;
+ /* 0:2.4MHz 1:3.84MHz */
+ hw->DMIC_CTRL__DMIC_CLK_DIV = 0;
+ /* 0:MSB aligned 1:LSB aligned */
+ hw->DMIC_CTRL__DMICOUTFORMAT = 0;
+ hw->DMIC_CTRL__DMIC_UP3_EN = 1;
+ hw->DMIC_CTRL__DMIC_UP2_EN = 1;
+ hw->DMIC_CTRL__DMIC_UP1_EN = 1;
+ /* 1*(DMIC_UP1_EN+ 2+ 3)*2 OCP read access every 96/88.1 KHz. */
+ hw->DMIC_FIFO_CTRL__DMIC_TRESH = DMIC_ITER / 6;
+ /* MCBSP SPECIFICATION
+ RJUST = 00 Right justify data and zero fill MSBs in DRR[1,2]
+ RJUST = 01 Right justify data and sign extend it into the MSBs
+ in DRR[1,2]
+ RJUST = 10 Left justify data and zero fill LSBs in DRR[1,2]
+ MCBSPLP_RJUST_MODE_RIGHT_ZERO = 0x0,
+ MCBSPLP_RJUST_MODE_RIGHT_SIGN = 0x1,
+ MCBSPLP_RJUST_MODE_LEFT_ZERO = 0x2,
+ MCBSPLP_RJUST_MODE_MAX = MCBSPLP_RJUST_MODE_LEFT_ZERO
+ */
+ hw->MCBSP_SPCR1_REG__RJUST = 2;
+ /* 1=MONO, 2=STEREO, 3=TDM_3_CHANNELS, 4=TDM_4_CHANNELS, .... */
+ hw->MCBSP_THRSH2_REG_REG__XTHRESHOLD = 1;
+ /* 1=MONO, 2=STEREO, 3=TDM_3_CHANNELS, 4=TDM_4_CHANNELS, .... */
+ hw->MCBSP_THRSH1_REG_REG__RTHRESHOLD = 1;
+ /* Slimbus IP FIFO thresholds */
+ hw->SLIMBUS_DCT_FIFO_SETUP_REG__SB_THRESHOLD = 1;
+ /* 2050 gives about 96kHz */
+ hw->AESS_EVENT_GENERATOR_COUNTER__COUNTER_VALUE =
+ EVENT_GENERATOR_COUNTER_DEFAULT;
+ /* 0: DMAreq, 1:Counter */
+ hw->AESS_EVENT_SOURCE_SELECTION__SELECTION = 1;
+ /* 5bits DMAreq selection */
+ hw->AESS_AUDIO_ENGINE_SCHEDULER__DMA_REQ_SELECTION =
+ ABE_ATC_MCPDMDL_DMA_REQ;
+ /* THE famous EVENT timer ! */
+ hw->HAL_EVENT_SELECTION = EVENT_TIMER;
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_hardware_configuration);
+/**
+ * abe_irq_processing - Process ABE interrupt
+ *
+ * This subroutine is call upon reception of "MA_IRQ_99 ABE_MPU_IRQ" Audio
+ * back-end interrupt. This subroutine will check the ATC Hrdware, the
+ * IRQ_FIFO from the AE and act accordingly. Some IRQ source are originated
+ * for the delivery of "end of time sequenced tasks" notifications, some are
+ * originated from the Ping-Pong protocols, some are generated from
+ * the embedded debugger when the firmware stops on programmable break-points,
+ * etc
+ */
+abehal_status abe_irq_processing(void)
+{
+ u32 abe_irq_dbg_write_ptr, i, cmem_src, sm_cm;
+ abe_irq_data_t IRQ_data;
+#define IrqFiFoMask ((D_McuIrqFifo_sizeof >> 2) -1)
+ _log(id_irq_processing, 0, 0, 0);
+ /* extract the write pointer index from CMEM memory (INITPTR format) */
+ /* CMEM address of the write pointer in bytes */
+ cmem_src = MCU_IRQ_FIFO_ptr_labelID << 2;
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_CMEM, cmem_src,
+ &sm_cm, sizeof(abe_irq_dbg_write_ptr));
+ /* AESS left-pointer index located on MSBs */
+ abe_irq_dbg_write_ptr = sm_cm >> 16;
+ abe_irq_dbg_write_ptr &= 0xFF;
+ /* loop on the IRQ FIFO content */
+ for (i = 0; i < D_McuIrqFifo_sizeof; i++) {
+ /* stop when the FIFO is empty */
+ if (abe_irq_dbg_write_ptr == abe_irq_dbg_read_ptr)
+ break;
+ /* read the IRQ/DBG FIFO */
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM,
+ (D_McuIrqFifo_ADDR +
+ (abe_irq_dbg_read_ptr << 2)),
+ (u32 *) &IRQ_data, sizeof(IRQ_data));
+ abe_irq_dbg_read_ptr = (abe_irq_dbg_read_ptr + 1) &IrqFiFoMask;
+ /* select the source of the interrupt */
+ switch (IRQ_data.tag) {
+ case IRQtag_APS:
+ _log(id_irq_processing, IRQ_data.data, 0, 1);
+ abe_irq_aps(IRQ_data.data);
+ break;
+ case IRQtag_PP:
+ _log(id_irq_processing, 0, 0, 2);
+ abe_irq_ping_pong();
+ break;
+ case IRQtag_COUNT:
+ _log(id_irq_processing, IRQ_data.data, 0, 3);
+ abe_irq_check_for_sequences(IRQ_data.data);
+ break;
+ default:
+ break;
+ }
+ }
+ abe_monitoring();
+ return 0;
+}
+EXPORT_SYMBOL(abe_irq_processing);
+/**
+ * abe_clear_irq - clear ABE interrupt
+ *
+ * This subroutine is call to clear MCU Irq
+ */
+abehal_status abe_clear_irq(void)
+{
+ u32 clear_abe_irq = 1;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC, ABE_MCU_IRQSTATUS,
+ &clear_abe_irq, 4);
+
+ return 0;
+}
+EXPORT_SYMBOL(abe_clear_irq);
+/**
+ * abe_select_main_port - Select stynchronization port for Event generator.
+ * @id: audio port name
+ *
+ * tells the FW which is the reference stream for adjusting
+ * the processing on 23/24/25 slots
+ */
+abehal_status abe_select_main_port(u32 id)
+{
+ u32 selection;
+ _log(id_select_main_port, id, 0, 0);
+ /* flow control */
+ selection = D_IOdescr_ADDR + id * sizeof(ABE_SIODescriptor) +
+ flow_counter_;
+ /* when the main port is a sink port from AESS point of view
+ the sign the firmware task analysis must be changed */
+ selection &= 0xFFFFL;
+ if (abe_port[id].protocol.direction == ABE_ATC_DIRECTION_IN)
+ selection |= 0x80000;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, D_Slot23_ctrl_ADDR,
+ &selection, 4);
+ return 0;
+}
+/**
+ * abe_write_event_generator - Select event generator source
+ * @e: Event Generation Counter, McPDM, DMIC or default.
+ *
+ * load the AESS event generator hardware source. Loads the firmware parameters
+ * accordingly. Indicates to the FW which data stream is the most important to preserve
+ * in case all the streams are asynchronous. If the parameter is "default", let the HAL
+ * decide which Event source is the best appropriate based on the opened ports.
+ *
+ * When neither the DMIC and the McPDM are activated the AE will have its EVENT generator programmed
+ * with the EVENT_COUNTER. The event counter will be tuned in order to deliver a pulse frequency higher
+ * than 96 kHz. The DPLL output at 100% OPP is MCLK = (32768kHz x6000) = 196.608kHz
+ * The ratio is (MCLK/96000)+(1<<1) = 2050
+ * (1<<1) in order to have the same speed at 50% and 100% OPP (only 15 MSB bits are used at OPP50%)
+ */
+abehal_status abe_write_event_generator(u32 e)
+{
+ u32 event, selection, counter, start;
+ _log(id_write_event_generator, e, 0, 0);
+ counter = EVENT_GENERATOR_COUNTER_DEFAULT;
+ start = EVENT_GENERATOR_ON;
+ abe_current_event_id = e;
+ switch (e) {
+ case EVENT_TIMER:
+ selection = EVENT_SOURCE_COUNTER;
+ event = 0;
+ break;
+ case EVENT_44100:
+ selection = EVENT_SOURCE_COUNTER;
+ event = 0;
+ counter = EVENT_GENERATOR_COUNTER_44100;
+ break;
+ default:
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_BLOCK_COPY_ERR);
+ }
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ EVENT_GENERATOR_COUNTER, &counter, 4);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ EVENT_SOURCE_SELECTION, &selection, 4);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ EVENT_GENERATOR_START, &start, 4);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ AUDIO_ENGINE_SCHEDULER, &event, 4);
+ return 0;
+}
+EXPORT_SYMBOL(abe_write_event_generator);
+/**
+ * abe_stop_event_generator - Stop event generator source
+ *
+ * Stop the event genrator of AESS. No more event will be send to AESS engine.
+ * Upper layer needs to wait 1/96kHz to be sure that engine reach IDLE instruction
+ */
+abehal_status abe_stop_event_generator(void)
+{
+ u32 event_gen;
+ /* Stop the event Generator */
+ event_gen = 0;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC,
+ EVENT_GENERATOR_START, &event_gen, 4);
+ return 0;
+}
+EXPORT_SYMBOL(abe_stop_event_generator);
+/**
+ * abe_read_use_case_opp() - description for void abe_read_use_case_opp().
+ *
+ * returns the expected min OPP for a given use_case list
+ */
+abehal_status abe_read_use_case_opp(u32 *u, u32 *o)
+{
+ u32 opp, i;
+ u32 *ptr = u;
+#define MAX_READ_USE_CASE_OPP 10
+#define OPP_25 1
+#define OPP_50 2
+#define OPP_100 4
+ _log(id_read_use_case_opp, (u32) u, (u32) u >> 8, (u32) u >> 16);
+ opp = i = 0;
+ do {
+ /* check for pointer errors */
+ if (i > MAX_READ_USE_CASE_OPP) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_READ_USE_CASE_OPP_ERR);
+ break;
+ }
+ /* check for end_of_list */
+ if (*ptr <= 0)
+ break;
+ /* OPP selection based on current firmware implementation */
+ switch (*ptr) {
+ case ABE_AUDIO_PLAYER_ON_HEADSET_OR_EARPHONE:
+ opp |= OPP_25;
+ break;
+ case ABE_DRIFT_MANAGEMENT_FOR_AUDIO_PLAYER:
+ opp |= OPP_100;
+ break;
+ case ABE_DRIFT_MANAGEMENT_FOR_VOICE_CALL:
+ opp |= OPP_100;
+ break;
+ case ABE_VOICE_CALL_ON_HEADSET_OR_EARPHONE_OR_BT:
+ opp |= OPP_50;
+ break;
+ case ABE_MULTIMEDIA_AUDIO_RECORDER:
+ opp |= OPP_50;
+ break;
+ case ABE_VIBRATOR_OR_HAPTICS:
+ opp |= OPP_100;
+ break;
+ case ABE_VOICE_CALL_ON_HANDS_FREE_SPEAKER:
+ opp |= OPP_100;
+ break;
+ case ABE_RINGER_TONES:
+ opp |= OPP_100;
+ break;
+ case ABE_VOICE_CALL_WITH_EARPHONE_ACTIVE_NOISE_CANCELLER:
+ opp |= OPP_100;
+ break;
+ default:
+ break;
+ }
+ i++;
+ ptr++;
+ } while (*ptr != 0);
+ if (opp & OPP_100)
+ *o = ABE_OPP100;
+ else if (opp & OPP_50)
+ *o = ABE_OPP50;
+ else
+ *o = ABE_OPP25;
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_use_case_opp);
+/**
+ * abe_set_opp_processing - Set OPP mode for ABE Firmware
+ * @opp: OOPP mode
+ *
+ * New processing network and OPP:
+ * 0: Ultra Lowest power consumption audio player (no post-processing, no mixer)
+ * 1: OPP 25% (simple multimedia features, including low-power player)
+ * 2: OPP 50% (multimedia and voice calls)
+ * 3: OPP100% ( multimedia complex use-cases)
+ *
+ * Rearranges the FW task network to the corresponding OPP list of features.
+ * The corresponding AE ports are supposed to be set/reset accordingly before
+ * this switch.
+ *
+ */
+abehal_status abe_set_opp_processing(u32 opp)
+{
+ u32 dOppMode32, sio_desc_address;
+ _lock_enter;
+ _log(id_set_opp_processing, opp, 0, 0);
+ switch (opp) {
+ case ABE_OPP25:
+ /* OPP25% */
+ dOppMode32 = DOPPMODE32_OPP25;
+ break;
+ case ABE_OPP50:
+ /* OPP50% */
+ dOppMode32 = DOPPMODE32_OPP50;
+ break;
+ default:
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_BLOCK_COPY_ERR);
+ case ABE_OPP100:
+ /* OPP100% */
+ dOppMode32 = DOPPMODE32_OPP100;
+ break;
+ }
+ /* Write Multiframe inside DMEM */
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM,
+ D_maxTaskBytesInSlot_ADDR, &dOppMode32, sizeof(u32));
+ sio_desc_address = dmem_port_descriptors + (MM_EXT_IN_PORT *
+ sizeof(ABE_SIODescriptor));
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, sio_desc_address,
+ (u32 *) &sio_desc, sizeof(sio_desc));
+ if (dOppMode32 == DOPPMODE32_OPP100) {
+ /* ASRC input buffer, size 40 */
+ sio_desc.smem_addr1 = smem_mm_ext_in_opp100;
+ /* Init MM_EXT_IN ASRC and enable its adaptation */
+ abe_init_asrc_mm_ext_in(250);
+ } else
+ /* at OPP 50 or without ASRC */
+ sio_desc.smem_addr1 = smem_mm_ext_in_opp50;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, sio_desc_address,
+ (u32 *) &sio_desc, sizeof(sio_desc));
+ sio_desc_address = dmem_port_descriptors + (BT_VX_UL_PORT *
+ sizeof(ABE_SIODescriptor));
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, sio_desc_address,
+ (u32 *) &sio_desc, sizeof(sio_desc));
+ if (dOppMode32 == DOPPMODE32_OPP100) {
+ /* ASRC input buffer, size 40 */
+ sio_desc.smem_addr1 = smem_bt_vx_ul_opp100;
+ /* Init MM_EXT_IN ASRC and enable its adaptation */
+ abe_init_asrc_bt_ul(250);
+ } else
+ /* at OPP 50 or without ASRC */
+ sio_desc.smem_addr1 = smem_bt_vx_ul_opp50;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, sio_desc_address,
+ (u32 *) &sio_desc, sizeof(sio_desc));
+ sio_desc_address = dmem_port_descriptors + (BT_VX_DL_PORT *
+ sizeof(ABE_SIODescriptor));
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, sio_desc_address,
+ (u32 *) &sio_desc, sizeof(sio_desc));
+ if (dOppMode32 == DOPPMODE32_OPP100) {
+ /* ASRC input buffer, size 40 */
+ sio_desc.smem_addr1 = smem_bt_vx_dl_opp100;
+ /* Init MM_EXT_IN ASRC and enable its adaptation */
+ abe_init_asrc_bt_dl(250);
+ } else
+ /* at OPP 50 or without ASRC */
+ sio_desc.smem_addr1 = smem_bt_vx_dl_opp50;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, sio_desc_address,
+ (u32 *) &sio_desc, sizeof(sio_desc));
+ return 0;
+}
+EXPORT_SYMBOL(abe_set_opp_processing);
+/**
+ * abe_set_ping_pong_buffer
+ * @port: ABE port ID
+ * @n_bytes: Size of Ping/Pong buffer
+ *
+ * Updates the next ping-pong buffer with "size" bytes copied from the
+ * host processor. This API notifies the FW that the data transfer is done.
+ */
+abehal_status abe_set_ping_pong_buffer(u32 port, u32 n_bytes)
+{
+ u32 sio_pp_desc_address, struct_offset, n_samples, datasize,
+ base_and_size, *src;
+ _log(id_set_ping_pong_buffer, port, n_bytes, n_bytes >> 8);
+ /* ping_pong is only supported on MM_DL */
+ if (port != MM_DL_PORT) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ }
+ /* translates the number of bytes in samples */
+ /* data size in DMEM words */
+ datasize = abe_dma_port_iter_factor(&((abe_port[port]).format));
+ /* data size in bytes */
+ datasize = datasize << 2;
+ n_samples = n_bytes / datasize;
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, D_PingPongDesc_ADDR,
+ (u32 *) &desc_pp, sizeof(desc_pp));
+ /*
+ * read the port SIO descriptor and extract the current pointer
+ * address after reading the counter
+ */
+ if ((desc_pp.counter & 0x1) == 0) {
+ struct_offset = (u32) &(desc_pp.nextbuff0_BaseAddr) -
+ (u32) &(desc_pp);
+ base_and_size = desc_pp.nextbuff0_BaseAddr;
+ } else {
+ struct_offset = (u32) &(desc_pp.nextbuff1_BaseAddr) -
+ (u32) &(desc_pp);
+ base_and_size = desc_pp.nextbuff1_BaseAddr;
+ }
+ base_and_size = (base_and_size & 0xFFFFL) + (n_samples << 16);
+ sio_pp_desc_address = D_PingPongDesc_ADDR + struct_offset;
+ src = &base_and_size;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, sio_pp_desc_address,
+ (u32 *) &base_and_size, sizeof(u32));
+ return 0;
+}
+EXPORT_SYMBOL(abe_set_ping_pong_buffer);
+/**
+ * abe_read_next_ping_pong_buffer
+ * @port: ABE portID
+ * @p: Next buffer address (pointer)
+ * @n: Next buffer size (pointer)
+ *
+ * Tell the next base address of the next ping_pong Buffer and its size
+ */
+abehal_status abe_read_next_ping_pong_buffer(u32 port, u32 *p, u32 *n)
+{
+ u32 sio_pp_desc_address;
+ _log(id_read_next_ping_pong_buffer, port, 0, 0);
+ /* ping_pong is only supported on MM_DL */
+ if (port != MM_DL_PORT) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ }
+ /* read the port SIO descriptor and extract the current pointer
+ address after reading the counter */
+ sio_pp_desc_address = D_PingPongDesc_ADDR;
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, sio_pp_desc_address,
+ (u32 *) &desc_pp, sizeof(ABE_SPingPongDescriptor));
+ if ((desc_pp.counter & 0x1) == 0) {
+ _log(id_read_next_ping_pong_buffer, port, 0, 0);
+ *p = desc_pp.nextbuff0_BaseAddr;
+ } else {
+ _log(id_read_next_ping_pong_buffer, port, 1, 0);
+ *p = desc_pp.nextbuff1_BaseAddr;
+ }
+ /* translates the number of samples in bytes */
+ *n = abe_size_pingpong;
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_next_ping_pong_buffer);
+/**
+ * abe_init_ping_pong_buffer
+ * @id: ABE port ID
+ * @size_bytes:size of the ping pong
+ * @n_buffers:number of buffers (2 = ping/pong)
+ * @p:returned address of the ping-pong list of base address (byte offset
+ from DMEM start)
+ *
+ * Computes the base address of the ping_pong buffers
+ */
+abehal_status abe_init_ping_pong_buffer(u32 id, u32 size_bytes, u32 n_buffers,
+ u32 *p)
+{
+ u32 i, dmem_addr;
+ _log(id_init_ping_pong_buffer, id, size_bytes, n_buffers);
+ /* ping_pong is supported in 2 buffers configuration right now but FW
+ is ready for ping/pong/pung/pang... */
+ if (id != MM_DL_PORT || n_buffers > MAX_PINGPONG_BUFFERS) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ }
+ for (i = 0; i < n_buffers; i++) {
+ dmem_addr = dmem_ping_pong_buffer + (i * size_bytes);
+ /* base addresses of the ping pong buffers in U8 unit */
+ abe_base_address_pingpong[i] = dmem_addr;
+ }
+ /* global data */
+ abe_size_pingpong = size_bytes;
+ *p = (u32) dmem_ping_pong_buffer;
+ return 0;
+}
+EXPORT_SYMBOL(abe_init_ping_pong_buffer);
+/**
+ * abe_read_offset_from_ping_buffer
+ * @id: ABE port ID
+ * @n: returned address of the offset from the ping buffer start address expressed in samples
+ *
+ * Computes the current firmware ping pong read pointer location, expressed in samples,
+ * as the offset from the start address of ping buffer.
+ */
+abehal_status abe_read_offset_from_ping_buffer(u32 id, u32 *n)
+{
+ u32 sio_pp_desc_address;
+ /* ping_pong is only supported on MM_DL */
+ if (MM_DL_PORT != id) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ } else {
+ /* read the port SIO ping pong descriptor */
+ sio_pp_desc_address = D_PingPongDesc_ADDR;
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM,
+ sio_pp_desc_address, (u32 *) &desc_pp,
+ sizeof(ABE_SPingPongDescriptor));
+ /* extract the current ping pong buffer read pointer based on
+ the value of the counter */
+ if ((desc_pp.counter & 0x1) == 0) {
+ /* the next is buffer0, hence the current is buffer1 */
+ if (abe_port[MM_DL_PORT].format.samp_format &
+ (MONO_MSB | MONO_RSHIFTED_16 | STEREO_16_16)) {
+ *n = abe_size_pingpong / 4 +
+ desc_pp.nextbuff1_Samples -
+ desc_pp.workbuff_Samples;
+ } else if (abe_port[MM_DL_PORT].format.samp_format &
+ (STEREO_MSB | STEREO_RSHIFTED_16)) {
+ *n = abe_size_pingpong / 8 +
+ desc_pp.nextbuff1_Samples -
+ desc_pp.workbuff_Samples;
+ } else {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ }
+ } else {
+ /* the next is buffer1, hence the current is buffer0 */
+ *n = desc_pp.nextbuff0_Samples -
+ desc_pp.workbuff_Samples;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_offset_from_ping_buffer);
+/**
+ * abe_plug_subroutine
+ * @id: returned sequence index after plugging a new subroutine
+ * @f: subroutine address to be inserted
+ * @n: number of parameters of this subroutine
+ * @params: pointer on parameters
+ *
+ * register a list of subroutines for call-back purpose
+ */
+abehal_status abe_plug_subroutine(u32 *id, abe_subroutine2 f, u32 n,
+ u32 *params)
+{
+ _log(id_plug_subroutine, (u32) (*id), (u32) f, n);
+ abe_add_subroutine(id, (abe_subroutine2) f, n, (u32 *) params);
+ return 0;
+}
+EXPORT_SYMBOL(abe_plug_subroutine);
+/**
+ * abe_set_sequence_time_accuracy
+ * @fast: fast counter
+ * @slow: slow counter
+ *
+ */
+abehal_status abe_set_sequence_time_accuracy(u32 fast, u32 slow)
+{
+ u32 data;
+ _log(id_set_sequence_time_accuracy, fast, slow, 0);
+ data = minimum(MAX_UINT16, fast / FW_SCHED_LOOP_FREQ_DIV1000);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, D_fastCounter_ADDR,
+ &data, sizeof(data));
+ data = minimum(MAX_UINT16, slow / FW_SCHED_LOOP_FREQ_DIV1000);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, D_slowCounter_ADDR,
+ &data, sizeof(data));
+ return 0;
+}
+EXPORT_SYMBOL(abe_set_sequence_time_accuracy);
+/**
+ * abe_reset_port
+ * @id: ABE port ID
+ *
+ * stop the port activity and reload default parameters on the associated
+ * processing features.
+ * Clears the internal AE buffers.
+ */
+abehal_status abe_reset_port(u32 id)
+{
+ _log(id_reset_port, id, 0, 0);
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ return 0;
+}
+EXPORT_SYMBOL(abe_reset_port);
+/**
+ * abe_read_remaining_data
+ * @id: ABE port_ID
+ * @n: size pointer to the remaining number of 32bits words
+ *
+ * computes the remaining amount of data in the buffer.
+ */
+abehal_status abe_read_remaining_data(u32 port, u32 *n)
+{
+ u32 sio_pp_desc_address;
+ _log(id_read_remaining_data, port, 0, 0);
+ /*
+ * read the port SIO descriptor and extract the
+ * current pointer address after reading the counter
+ */
+ sio_pp_desc_address = D_PingPongDesc_ADDR;
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, sio_pp_desc_address,
+ (u32 *) &desc_pp, sizeof(ABE_SPingPongDescriptor));
+ *n = desc_pp.workbuff_Samples;
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_remaining_data);
+/**
+ * abe_disable_data_transfer
+ * @id: ABE port id
+ *
+ * disables the ATC descriptor and stop IO/port activities
+ * disable the IO task (@f = 0)
+ * clear ATC DMEM buffer, ATC enabled
+ */
+abehal_status abe_disable_data_transfer(u32 id)
+{
+ abe_port_protocol_t *protocol;
+ _log(id_disable_data_transfer, id, 0, 0);
+ /* there is only one PDM_DL physical port shared
+ with DL1/DL2/VIB. Here is a check for the need to stop
+ PDM_DL if some activity is already on */
+ if (id == PDM_DL1_PORT) {
+ pdm_dl1_status = 0;
+ if (pdm_dl2_status || pdm_vib_status)
+ return 0;
+ else
+ id = PDM_DL_PORT;
+ }
+ if (id == PDM_DL2_PORT) {
+ pdm_dl2_status = 0;
+ if (pdm_dl1_status || pdm_vib_status)
+ return 0;
+ else
+ id = PDM_DL_PORT;
+ }
+ if (id == PDM_VIB_PORT) {
+ pdm_vib_status = 0;
+ if (pdm_dl1_status || pdm_dl2_status)
+ return 0;
+ else
+ id = PDM_DL_PORT;
+ }
+ /* MM_DL managed in ping-pong */
+ if (id == MM_DL_PORT) {
+ protocol = &(abe_port[MM_DL_PORT].protocol);
+ if (protocol->protocol_switch == PINGPONG_PORT_PROT) {
+ abe_disable_pp_io_task(MM_DL_PORT);
+ }
+ }
+ /* local host variable status= "port is running" */
+ abe_port[id].status = OMAP_ABE_PORT_ACTIVITY_IDLE;
+ /* disable DMA requests */
+ abe_disable_dma_request(id);
+ /* disable ATC transfers */
+ abe_init_atc(id);
+ abe_clean_temporary_buffers(id);
+ /* select the main port based on the desactivation of this port */
+ abe_decide_main_port();
+ return 0;
+}
+EXPORT_SYMBOL(abe_disable_data_transfer);
+/**
+ * abe_enable_data_transfer
+ * @ip: ABE port id
+ *
+ * enables the ATC descriptor
+ * reset ATC pointers
+ * enable the IO task (@f <> 0)
+ */
+abehal_status abe_enable_data_transfer(u32 id)
+{
+ abe_port_protocol_t *protocol;
+ abe_data_format_t format;
+ _log(id_enable_data_transfer, id, 0, 0);
+ /* there is only one PDM_DL physical port shared
+ with DL1/DL2/VIB. Here is a check for the need to enable
+ PDM_DL when some activity is already on */
+ if (id == PDM_DL1_PORT) {
+ id = PDM_DL_PORT;
+ if (pdm_dl1_status == 1)
+ return 0;
+ else
+ pdm_dl1_status = 1;
+ }
+ if (id == PDM_DL2_PORT) {
+ id = PDM_DL_PORT;
+ if (pdm_dl2_status == 1)
+ return 0;
+ else
+ pdm_dl2_status = 1;
+ }
+ if (id == PDM_VIB_PORT) {
+ id = PDM_DL_PORT;
+ if (pdm_vib_status == 1)
+ return 0;
+ else
+ pdm_vib_status = 1;
+ }
+ abe_clean_temporary_buffers(id);
+ if (id == PDM_UL_PORT) {
+ /* initializes the ABE ATC descriptors in DMEM - MCPDM_UL */
+ protocol = &(abe_port[PDM_UL_PORT].protocol);
+ format = abe_port[PDM_UL_PORT].format;
+ abe_init_atc(PDM_UL_PORT);
+ abe_init_io_tasks(PDM_UL_PORT, &format, protocol);
+ }
+ if (id == PDM_DL_PORT) {
+ /* initializes the ABE ATC descriptors in DMEM - MCPDM_DL */
+ protocol = &(abe_port[PDM_DL_PORT].protocol);
+ format = abe_port[PDM_DL_PORT].format;
+ abe_init_atc(PDM_DL_PORT);
+ abe_init_io_tasks(PDM_DL_PORT, &format, protocol);
+ }
+ /* MM_DL managed in ping-pong */
+ if (id == MM_DL_PORT) {
+ protocol = &(abe_port[MM_DL_PORT].protocol);
+ if (protocol->protocol_switch == PINGPONG_PORT_PROT) {
+ abe_enable_pp_io_task(MM_DL_PORT);
+ }
+ }
+ if (id == DMIC_PORT) {
+ /* one DMIC port enabled = all DMICs enabled,
+ * since there is a single DMIC path for all DMICs */
+ protocol = &(abe_port[DMIC_PORT].protocol);
+ format = abe_port[DMIC_PORT].format;
+ abe_init_atc(DMIC_PORT);
+ abe_init_io_tasks(DMIC_PORT, &format, protocol);
+ }
+ if (id == VX_UL_PORT) {
+ /* Init VX_UL ASRC and enable its adaptation */
+ abe_init_asrc_vx_ul(250);
+ }
+ if (id == VX_DL_PORT) {
+ /* Init VX_DL ASRC and enable its adaptation */
+ abe_init_asrc_vx_dl(250);
+ }
+ /* local host variable status= "port is running" */
+ abe_port[id].status = OMAP_ABE_PORT_ACTIVITY_RUNNING;
+ /* enable DMA requests */
+ abe_enable_dma_request(id);
+ /* select the main port based on the activation of this new port */
+ abe_decide_main_port();
+ return 0;
+}
+EXPORT_SYMBOL(abe_enable_data_transfer);
+/**
+ * abe_connect_cbpr_dmareq_port
+ * @id: port name
+ * @f: desired data format
+ * @d: desired dma_request line (0..7)
+ * @a: returned pointer to the base address of the CBPr register and number of
+ * samples to exchange during a DMA_request.
+ *
+ * enables the data echange between a DMA and the ABE through the
+ * CBPr registers of AESS.
+ */
+abehal_status abe_connect_cbpr_dmareq_port(u32 id, abe_data_format_t *f, u32 d,
+ abe_dma_t *returned_dma_t)
+{
+ _log(id_connect_cbpr_dmareq_port, id, f->f, f->samp_format);
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ (abe_port[id]).format = (*f);
+ abe_port[id].protocol.protocol_switch = DMAREQ_PORT_PROT;
+ abe_port[id].protocol.p.prot_dmareq.iter = abe_dma_port_iteration(f);
+ abe_port[id].protocol.p.prot_dmareq.dma_addr = ABE_DMASTATUS_RAW;
+ abe_port[id].protocol.p.prot_dmareq.dma_data = (1 << d);
+ abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+ /* load the micro-task parameters */
+ abe_init_io_tasks(id, &((abe_port[id]).format),
+ &((abe_port[id]).protocol));
+ /* load the dma_t with physical information from AE memory mapping */
+ abe_init_dma_t(id, &((abe_port[id]).protocol));
+ /* load the ATC descriptors - disabled */
+ abe_init_atc(id);
+ /* return the dma pointer address */
+ abe_read_port_address(id, returned_dma_t);
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_cbpr_dmareq_port);
+/**
+ * abe_connect_dmareq_ping_pong_port
+ * @id: port name
+ * @f: desired data format
+ * @d: desired dma_request line (0..7)
+ * @s: half-buffer (ping) size
+ * @a: returned pointer to the base address of the ping-pong buffer and number
+ * of samples to exchange during a DMA_request.
+ *
+ * enables the data echanges between a DMA and a direct access to
+ * the DMEM memory of ABE. On each dma_request activation the DMA will exchange
+ * "s" bytes and switch to the "pong" buffer for a new buffer exchange.
+ */
+abehal_status abe_connect_dmareq_ping_pong_port(u32 id, abe_data_format_t *f,
+ u32 d, u32 s,
+ abe_dma_t *returned_dma_t)
+{
+ abe_dma_t dma1;
+ _log(id_connect_dmareq_ping_pong_port, id, f->f, f->samp_format);
+ /* ping_pong is only supported on MM_DL */
+ if (id != MM_DL_PORT) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ }
+ /* declare PP buffer and prepare the returned dma_t */
+ abe_init_ping_pong_buffer(MM_DL_PORT, s, 2,
+ (u32 *) &(returned_dma_t->data));
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ (abe_port[id]).format = (*f);
+ (abe_port[id]).protocol.protocol_switch = PINGPONG_PORT_PROT;
+ (abe_port[id]).protocol.p.prot_pingpong.buf_addr =
+ dmem_ping_pong_buffer;
+ (abe_port[id]).protocol.p.prot_pingpong.buf_size = s;
+ (abe_port[id]).protocol.p.prot_pingpong.irq_addr = ABE_DMASTATUS_RAW;
+ (abe_port[id]).protocol.p.prot_pingpong.irq_data = (1 << d);
+ abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+ /* load the micro-task parameters DESC_IO_PP */
+ abe_init_io_tasks(id, &((abe_port[id]).format),
+ &((abe_port[id]).protocol));
+ /* load the dma_t with physical information from AE memory mapping */
+ abe_init_dma_t(id, &((abe_port[id]).protocol));
+ dma1.data = (u32 *) (abe_port[id].dma.data + ABE_DMEM_BASE_ADDRESS_L3);
+ dma1.iter = abe_port[id].dma.iter;
+ *returned_dma_t = dma1;
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_dmareq_ping_pong_port);
+/**
+ * abe_connect_irq_ping_pong_port
+ * @id: port name
+ * @f: desired data format
+ * @I: index of the call-back subroutine to call
+ * @s: half-buffer (ping) size
+ * @p: returned base address of the first (ping) buffer)
+ *
+ * enables the data echanges between a direct access to the DMEM
+ * memory of ABE using cache flush. On each IRQ activation a subroutine
+ * registered with "abe_plug_subroutine" will be called. This subroutine
+ * will generate an amount of samples, send them to DMEM memory and call
+ * "abe_set_ping_pong_buffer" to notify the new amount of samples in the
+ * pong buffer.
+ */
+abehal_status abe_connect_irq_ping_pong_port(u32 id, abe_data_format_t *f,
+ u32 subroutine_id, u32 size,
+ u32 *sink, u32 dsp_mcu_flag)
+{
+ _log(id_connect_irq_ping_pong_port, id, f->f, f->samp_format);
+ /* ping_pong is only supported on MM_DL */
+ if (id != MM_DL_PORT) {
+ abe_dbg_param |= ERR_API;
+ abe_dbg_error_log(ABE_PARAMETER_ERROR);
+ }
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ (abe_port[id]).format = (*f);
+ (abe_port[id]).protocol.protocol_switch = PINGPONG_PORT_PROT;
+ (abe_port[id]).protocol.p.prot_pingpong.buf_addr =
+ dmem_ping_pong_buffer;
+ (abe_port[id]).protocol.p.prot_pingpong.buf_size = size;
+ (abe_port[id]).protocol.p.prot_pingpong.irq_data = (1);
+ abe_init_ping_pong_buffer(MM_DL_PORT, size, 2, sink);
+ if (dsp_mcu_flag == PING_PONG_WITH_MCU_IRQ)
+ (abe_port[id]).protocol.p.prot_pingpong.irq_addr =
+ ABE_MCU_IRQSTATUS_RAW;
+ if (dsp_mcu_flag == PING_PONG_WITH_DSP_IRQ)
+ (abe_port[id]).protocol.p.prot_pingpong.irq_addr =
+ ABE_DSP_IRQSTATUS_RAW;
+ abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+ /* load the micro-task parameters */
+ abe_init_io_tasks(id, &((abe_port[id]).format),
+ &((abe_port[id]).protocol));
+ /* load the ATC descriptors - disabled */
+ abe_init_atc(id);
+ *sink = (abe_port[id]).protocol.p.prot_pingpong.buf_addr;
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_irq_ping_pong_port);
+/**
+ * abe_connect_serial_port()
+ * @id: port name
+ * @f: data format
+ * @i: peripheral ID (McBSP #1, #2, #3)
+ *
+ * Operations : enables the data echanges between a McBSP and an ATC buffer in
+ * DMEM. This API is used connect 48kHz McBSP streams to MM_DL and 8/16kHz
+ * voice streams to VX_UL, VX_DL, BT_VX_UL, BT_VX_DL. It abstracts the
+ * abe_write_port API.
+ */
+abehal_status abe_connect_serial_port(u32 id, abe_data_format_t *f,
+ u32 mcbsp_id)
+{
+ u32 UC_NULL[] = { 0 };
+ u32 OPP;
+ abe_hw_config_init_t CONFIG;
+ _log(id_connect_serial_port, id, f->samp_format, mcbsp_id);
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ (abe_port[id]).format = (*f);
+ (abe_port[id]).protocol.protocol_switch = SERIAL_PORT_PROT;
+ /* McBSP peripheral connected to ATC */
+ (abe_port[id]).protocol.p.prot_serial.desc_addr = mcbsp_id*ATC_SIZE;
+ /* check the iteration of ATC */
+ abe_read_hardware_configuration(UC_NULL, &OPP, &CONFIG);
+ (abe_port[id]).protocol.p.prot_serial.iter =
+ abe_dma_port_iter_factor(f);
+ abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+ /* load the micro-task parameters */
+ abe_init_io_tasks(id, &((abe_port[id]).format),
+ &((abe_port[id]).protocol));
+ /* load the ATC descriptors - disabled */
+ abe_init_atc(id);
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_serial_port);
+/**
+ * abe_connect_slimbus_port
+ * @id: port name
+ * @f: data format
+ * @i: peripheral ID (McBSP #1, #2, #3)
+ * @j: peripheral ID (McBSP #1, #2, #3)
+ *
+ * enables the data echanges between 1/2 SB and an ATC buffers in
+ * DMEM.
+ */
+abehal_status abe_connect_slimbus_port(u32 id, abe_data_format_t *f,
+ u32 sb_port1, u32 sb_port2)
+{
+ u32 UC_NULL[] = {
+ 0
+ };
+ u32 OPP;
+ abe_hw_config_init_t CONFIG;
+ u32 iter;
+ _log(id_connect_slimbus_port, id, f->samp_format, sb_port2);
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ (abe_port[id]).format = (*f);
+ (abe_port[id]).protocol.protocol_switch = SLIMBUS_PORT_PROT;
+ /* SB1 peripheral connected to ATC */
+ (abe_port[id]).protocol.p.prot_slimbus.desc_addr1 = sb_port1*ATC_SIZE;
+ /* SB2 peripheral connected to ATC */
+ (abe_port[id]).protocol.p.prot_slimbus.desc_addr2 = sb_port2*ATC_SIZE;
+ /* check the iteration of ATC */
+ abe_read_hardware_configuration(UC_NULL, &OPP, &CONFIG);
+ iter = CONFIG.SLIMBUS_DCT_FIFO_SETUP_REG__SB_THRESHOLD;
+ /* SLIMBUS iter should be 1 */
+ (abe_port[id]).protocol.p.prot_serial.iter = iter;
+ abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+ /* load the micro-task parameters */
+ abe_init_io_tasks(id, &((abe_port[id]).format),
+ &((abe_port[id]).protocol));
+ /* load the ATC descriptors - disabled */
+ abe_init_atc(id);
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_slimbus_port);
+/**
+ * abe_connect_tdm_port
+ * @id: port name
+ * @f: data format
+ * @i: peripheral ID (McBSP #1, #2, #3)
+ * @j: peripheral ID (McBSP #1, #2, #3)
+ *
+ * enables the data echanges between TDM McBSP ATC buffers in
+ * DMEM and 1/2 SMEM buffers
+ */
+abehal_status abe_connect_tdm_port(u32 id, abe_data_format_t *f, u32 mcbsp_id)
+{
+ u32 UC_NULL[] = { 0 };
+ u32 OPP;
+ abe_hw_config_init_t CONFIG;
+ u32 iter;
+ _log(id_connect_tdm_port, id, f->samp_format, mcbsp_id);
+ abe_port[id] = ((abe_port_t *) abe_port_init)[id];
+ (abe_port[id]).format = (*f);
+ (abe_port[id]).protocol.protocol_switch = TDM_SERIAL_PORT_PROT;
+ /* McBSP peripheral connected to ATC */
+ (abe_port[id]).protocol.p.prot_serial.desc_addr = mcbsp_id*ATC_SIZE;
+ /* check the iteration of ATC */
+ abe_read_hardware_configuration(UC_NULL, &OPP, &CONFIG);
+ if (abe_port[id].protocol.direction == ABE_ATC_DIRECTION_IN)
+ iter = CONFIG.MCBSP_THRSH1_REG_REG__RTHRESHOLD;
+ else
+ iter = CONFIG.MCBSP_THRSH2_REG_REG__XTHRESHOLD;
+ /* McBSP iter should be 1 */
+ (abe_port[id]).protocol.p.prot_serial.iter = iter;
+ abe_port[id].status = OMAP_ABE_PORT_INITIALIZED;
+ /* load the micro-task parameters */
+ abe_init_io_tasks(id, &((abe_port[id]).format),
+ &((abe_port[id]).protocol));
+ /* load the ATC descriptors - disabled */
+ abe_init_atc(id);
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_tdm_port);
+/**
+ * abe_read_port_address
+ * @dma: output pointer to the DMA iteration and data destination pointer
+ *
+ * This API returns the address of the DMA register used on this audio port.
+ * Depending on the protocol being used, adds the base address offset L3
+ * (DMA) or MPU (ARM)
+ */
+abehal_status abe_read_port_address(u32 port, abe_dma_t *dma2)
+{
+ abe_dma_t_offset dma1;
+ u32 protocol_switch;
+ _log(id_read_port_address, port, 0, 0);
+ dma1 = (abe_port[port]).dma;
+ protocol_switch = abe_port[port].protocol.protocol_switch;
+ switch (protocol_switch) {
+ case PINGPONG_PORT_PROT:
+ /* return the base address of the buffer in L3 and L4 spaces */
+ (*dma2).data = (void *)(dma1.data + ABE_DMEM_BASE_ADDRESS_L3);
+ (*dma2).l3_dmem =
+ (void *)(dma1.data + ABE_DMEM_BASE_ADDRESS_L3);
+ (*dma2).l4_dmem =
+ (void *)(dma1.data + ABE_DMEM_BASE_ADDRESS_L4);
+ break;
+ case DMAREQ_PORT_PROT:
+ /* return the CBPr(L3), DMEM(L3), DMEM(L4) address */
+ (*dma2).data = (void *)(dma1.data + ABE_ATC_BASE_ADDRESS_L3);
+ (*dma2).l3_dmem =
+ (void *)((abe_port[port]).protocol.p.prot_dmareq.
+ buf_addr + ABE_DMEM_BASE_ADDRESS_L3);
+ (*dma2).l4_dmem =
+ (void *)((abe_port[port]).protocol.p.prot_dmareq.
+ buf_addr + ABE_DMEM_BASE_ADDRESS_L4);
+ break;
+ default:
+ break;
+ }
+ (*dma2).iter = (dma1.iter);
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_port_address);
+/*
+ * ABE_SELECT_DATA_SOURCE
+ *
+ * Parameter :
+ * port id where data are exchanged
+ * data_cource_id among:
+ * SRC_DL1_MIXER_OUTPUT (DL1_M_labelID)
+ * SRC_SDT_MIXER_OUTPUT (SDT_M_labelID)
+ * SRC_DL1_GAIN_OUTPUT (DL1_GAIN_out_labelID)
+ * SRC_DL1_EQ_OUTPUT (DL1_EQ_labelID)
+ * SRC_DL2_GAIN_OUTPUT (DL2_GAIN_out_labelID)
+ * SRC_DL2_EQ_OUTPUT (DL2_EQ_labelID)
+ * SRC_MM_DL (MM_DL_labelID)
+ * SRC_TONES_DL (Tones_labelID)
+ * SRC_VX_DL (VX_DL_labelID)
+ * SRC_VX_UL (VX_UL_labelID)
+ * SRC_MM_UL2 (MM_UL2_labelID)
+ * SRC_MM_UL (MM_UL_labelID)
+ *
+ * Operations :
+ *
+ * Return value :
+ * None.
+ */
+abehal_status abe_select_data_source(u32 port_id, u32 smem_source)
+{
+ u32 sio_desc_address;
+ sio_desc_address = dmem_port_descriptors +
+ (port_id * sizeof(ABE_SIODescriptor));
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM,
+ sio_desc_address, (u32 *) &sio_desc, sizeof(sio_desc));
+ sio_desc.smem_addr1 = (u16) smem_source;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM,
+ sio_desc_address, (u32 *) &sio_desc, sizeof(sio_desc));
+ return 0;
+}
+EXPORT_SYMBOL(abe_select_data_source);
+/**
+ * abe_write_equalizer
+ * @id: name of the equalizer
+ * @param : equalizer coefficients
+ *
+ * Load the coefficients in CMEM.
+ */
+abehal_status abe_write_equalizer(u32 id, abe_equ_t *param)
+{
+ u32 eq_offset, length, *src, eq_mem, eq_mem_len;
+ _log(id_write_equalizer, id, 0, 0);
+ switch (id) {
+ default:
+ case EQ1:
+ eq_offset = C_DL1_Coefs_ADDR;
+ eq_mem = S_DL1_M_EQ_data_ADDR;
+ eq_mem_len = S_DL1_M_EQ_data_sizeof;
+ break;
+ case EQ2L:
+ eq_offset = C_DL2_L_Coefs_ADDR;
+ eq_mem = S_DL2_M_LR_EQ_data_ADDR;
+ eq_mem_len = S_DL2_M_LR_EQ_data_sizeof;
+ break;
+ case EQ2R:
+ eq_offset = C_DL2_R_Coefs_ADDR;
+ eq_mem = S_DL2_M_LR_EQ_data_ADDR;
+ eq_mem_len = S_DL2_M_LR_EQ_data_sizeof;
+ break;
+ case EQSDT:
+ eq_offset = C_SDT_Coefs_ADDR;
+ eq_mem = S_SDT_F_data_ADDR;
+ eq_mem_len = S_SDT_F_data_sizeof;
+ break;
+ case EQAMIC:
+ eq_offset = C_96_48_AMIC_Coefs_ADDR;
+ eq_mem = S_AMIC_96_48_data_ADDR;
+ eq_mem_len = S_AMIC_96_48_data_sizeof;
+ break;
+ case EQDMIC:
+ eq_offset = C_96_48_DMIC_Coefs_ADDR;
+ eq_mem = S_DMIC0_96_48_data_ADDR;
+ eq_mem_len = S_DMIC0_96_48_data_sizeof;
+ /* three DMIC are clear at the same time DMIC0 DMIC1 DMIC2 */
+ eq_mem_len *= 3;
+ break;
+ case APS1:
+ eq_offset = C_APS_DL1_coeffs1_ADDR;
+ eq_mem = S_APS_IIRmem1_ADDR;
+ eq_mem_len = S_APS_IIRmem1_sizeof;
+ break;
+ case APS2L:
+ eq_offset = C_APS_DL2_L_coeffs1_ADDR;
+ eq_mem = S_APS_M_IIRmem2_ADDR;
+ eq_mem_len = S_APS_M_IIRmem2_sizeof;
+ break;
+ case APS2R:
+ eq_offset = C_APS_DL2_R_coeffs1_ADDR;
+ eq_mem = S_APS_M_IIRmem2_ADDR;
+ eq_mem_len = S_APS_M_IIRmem2_sizeof;
+ break;
+ }
+ length = param->equ_length;
+ src = (u32 *) ((param->coef).type1);
+ /* translate in bytes */
+ eq_offset <<= 2;
+ /* reset SMEM buffers before the coefficients are loaded */
+ abe_reset_mem(ABE_SMEM, eq_mem << 3, eq_mem_len << 3);
+ /* translate in bytes */
+ length <<= 2;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_CMEM, eq_offset, src, length);
+ /* reset SMEM buffers after the coefficients are loaded */
+ abe_reset_mem(ABE_SMEM, eq_mem << 3, eq_mem_len << 3);
+ return 0;
+}
+EXPORT_SYMBOL(abe_write_equalizer);
+/**
+ * abe_write_asrc
+ * @id: name of the port
+ * @param: drift value to compensate [ppm]
+ *
+ * Load the drift variables to the FW memory. This API can be called only
+ * when the corresponding port has been already opened and the ASRC has
+ * been correctly initialized with API abe_init_asrc_... If this API is
+ * used such that the drift has been changed from positive to negative drift
+ * or vice versa, there will be click in the output signal. Loading the drift
+ * value with zero disables the feature.
+ */
+abehal_status abe_write_asrc(u32 port, s32 dppm)
+{
+ s32 dtempvalue, adppm, drift_sign, drift_sign_addr, alpha_params_addr;
+ s32 alpha_params[3];
+ _log(id_write_asrc, port, dppm, dppm >> 8);
+ /*
+ * x = ppm
+ *
+ * - 1000000/x must be multiple of 16
+ * - deltaalpha = round(2^20*x*16/1000000)=round(2^18/5^6*x) on 22 bits.
+ * then shifted by 2bits
+ * - minusdeltaalpha
+ * - oneminusepsilon = 1-deltaalpha/2.
+ *
+ * ppm = 250
+ * - 1000000/250=4000
+ * - deltaalpha = 4194.3 ~ 4195 => 0x00418c
+ */
+ /* examples for -6250 ppm */
+ /* atempvalue32[1] = -1; d_driftsign */
+ /* atempvalue32[3] = 0x00066668; d_deltaalpha */
+ /* atempvalue32[4] = 0xfff99998; d_minusdeltaalpha */
+ /* atempvalue32[5] = 0x003ccccc; d_oneminusepsilon */
+ /* example for 100 ppm */
+ /* atempvalue32[1] = 1;* d_driftsign */
+ /* atempvalue32[3] = 0x00001a38; d_deltaalpha */
+ /* atempvalue32[4] = 0xffffe5c8; d_minusdeltaalpha */
+ /* atempvalue32[5] = 0x003ccccc; d_oneminusepsilon */
+ /* compute new value for the ppm */
+ if (dppm >= 0) {
+ /* d_driftsign */
+ drift_sign = 1;
+ adppm = dppm;
+ } else {
+ /* d_driftsign */
+ drift_sign = -1;
+ adppm = (-1 * dppm);
+ }
+ if (dppm == 0) {
+ /* delta_alpha */
+ alpha_params[0] = 0;
+ /* minusdelta_alpha */
+ alpha_params[1] = 0;
+ /* one_minusepsilon */
+ alpha_params[2] = 0x003ffff0;
+ } else {
+ dtempvalue = (adppm << 4) + adppm - ((adppm * 3481L) / 15625L);
+ /* delta_alpha */
+ alpha_params[0] = dtempvalue << 2;
+ /* minusdelta_alpha */
+ alpha_params[1] = (-dtempvalue) << 2;
+ /* one_minusepsilon */
+ alpha_params[2] = (0x00100000 - (dtempvalue / 2)) << 2;
+ }
+ switch (port) {
+ /* asynchronous sample-rate-converter for the uplink voice path */
+ case VX_DL_PORT:
+ drift_sign_addr = D_AsrcVars_DL_VX_ADDR + (1 * sizeof(s32));
+ alpha_params_addr = D_AsrcVars_DL_VX_ADDR + (3 * sizeof(s32));
+ break;
+ /* asynchronous sample-rate-converter for the downlink voice path */
+ case VX_UL_PORT:
+ drift_sign_addr = D_AsrcVars_UL_VX_ADDR + (1 * sizeof(s32));
+ alpha_params_addr = D_AsrcVars_UL_VX_ADDR + (3 * sizeof(s32));
+ break;
+ /* asynchronous sample-rate-converter for the BT_UL path */
+ case BT_VX_UL_PORT:
+ drift_sign_addr = D_AsrcVars_BT_UL_ADDR + (1 * sizeof(s32));
+ alpha_params_addr = D_AsrcVars_BT_UL_ADDR + (3 * sizeof(s32));
+ break;
+ /* asynchronous sample-rate-converter for the BT_DL path */
+ case BT_VX_DL_PORT:
+ drift_sign_addr = D_AsrcVars_BT_DL_ADDR + (1 * sizeof(s32));
+ alpha_params_addr = D_AsrcVars_BT_DL_ADDR + (3 * sizeof(s32));
+ break;
+ default:
+ /* asynchronous sample-rate-converter for the MM_EXT_IN path */
+ case MM_EXT_IN_PORT:
+ drift_sign_addr = D_AsrcVars_MM_EXT_IN_ADDR + (1 * sizeof(s32));
+ alpha_params_addr =
+ D_AsrcVars_MM_EXT_IN_ADDR + (3 * sizeof(s32));
+ break;
+ }
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, drift_sign_addr,
+ (u32 *) &drift_sign, 4);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, alpha_params_addr,
+ (u32 *) &alpha_params[0], 12);
+ return 0;
+}
+EXPORT_SYMBOL(abe_write_asrc);
+/**
+ * abe_write_aps
+ * @id: name of the aps filter
+ * @param: table of filter coefficients
+ *
+ * Load the filters and thresholds coefficients in FW memory. This AP
+ * can be called when the corresponding APS is not activated. After
+ * reloading the firmware the default coefficients corresponds to "no APS
+ * activated".
+ * Loading all the coefficients value with zero disables the feature.
+ */
+abehal_status abe_write_aps(u32 id, abe_aps_t *param)
+{
+ _log(id_write_aps, id, 0, 0);
+ return 0;
+}
+EXPORT_SYMBOL(abe_write_aps);
+/**
+ * abe_use_compensated_gain
+ * @on_off:
+ *
+ * Selects the automatic Mixer's gain management
+ * on_off = 1 allows the "abe_write_gain" to adjust the overall
+ * gains of the mixer to be tuned not to create saturation
+ */
+abehal_status abe_use_compensated_gain(u32 on_off)
+{
+ abe_compensated_mixer_gain = on_off;
+ return 0;
+}
+EXPORT_SYMBOL(abe_use_compensated_gain);
+/**
+ * abe_disable_gain
+ * Parameters:
+ * mixer id
+ * sub-port id
+ *
+ */
+abehal_status abe_disable_gain(u32 id, u32 p)
+{
+ u32 mixer_offset, f_g, ramp;
+ abe_gain_offset(id, &mixer_offset);
+ /* save the input parameters for mute/unmute */
+ ramp = abe_desired_ramp_delay_ms[mixer_offset + p];
+ f_g = GAIN_MUTE;
+ if (!(abe_muted_gains_indicator[mixer_offset + p] & 0x02)) {
+ /* Check if we are in mute */
+ if (!(abe_muted_gains_indicator[mixer_offset + p] & 0x01)) {
+ abe_muted_gains_decibel[mixer_offset + p] =
+ abe_desired_gains_decibel[mixer_offset + p];
+ /* mute the gain */
+ abe_write_gain(id, f_g, ramp, p);
+ }
+ abe_muted_gains_indicator[mixer_offset + p] |= 0x02;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(abe_disable_gain);
+/**
+ * abe_enable_gain
+ * Parameters:
+ * mixer id
+ * sub-port id
+ *
+ */
+abehal_status abe_enable_gain(u32 id, u32 p)
+{
+ u32 mixer_offset, f_g, ramp;
+ abe_gain_offset(id, &mixer_offset);
+ if ((abe_muted_gains_indicator[mixer_offset + p] & 0x02)) {
+ /* restore the input parameters for mute/unmute */
+ f_g = abe_muted_gains_decibel[mixer_offset + p];
+ ramp = abe_desired_ramp_delay_ms[mixer_offset + p];
+ abe_muted_gains_indicator[mixer_offset + p] &= ~0x02;
+ /* unmute the gain */
+ abe_write_gain(id, f_g, ramp, p);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(abe_enable_gain);
+/**
+ * abe_mute_gain
+ * Parameters:
+ * mixer id
+ * sub-port id
+ *
+ */
+abehal_status abe_mute_gain(u32 id, u32 p)
+{
+ u32 mixer_offset, f_g, ramp;
+ abe_gain_offset(id, &mixer_offset);
+ /* save the input parameters for mute/unmute */
+ ramp = abe_desired_ramp_delay_ms[mixer_offset + p];
+ f_g = GAIN_MUTE;
+ if (!abe_muted_gains_indicator[mixer_offset + p]) {
+ abe_muted_gains_decibel[mixer_offset + p] =
+ abe_desired_gains_decibel[mixer_offset + p];
+ /* mute the gain */
+ abe_write_gain(id, f_g, ramp, p);
+ }
+ abe_muted_gains_indicator[mixer_offset + p] |= 0x01;
+ return 0;
+}
+EXPORT_SYMBOL(abe_mute_gain);
+/**
+ * abe_unmute_gain
+ * Parameters:
+ * mixer id
+ * sub-port id
+ *
+ */
+abehal_status abe_unmute_gain(u32 id, u32 p)
+{
+ u32 mixer_offset, f_g, ramp;
+ abe_gain_offset(id, &mixer_offset);
+ if ((abe_muted_gains_indicator[mixer_offset + p] & 0x01)) {
+ /* restore the input parameters for mute/unmute */
+ f_g = abe_muted_gains_decibel[mixer_offset + p];
+ ramp = abe_desired_ramp_delay_ms[mixer_offset + p];
+ abe_muted_gains_indicator[mixer_offset + p] &= ~0x01;
+ /* unmute the gain */
+ abe_write_gain(id, f_g, ramp, p);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(abe_unmute_gain);
+/**
+ * abe_write_mixer
+ * @id: name of the mixer
+ * @param: list of input gains of the mixer
+ * @p: list of port corresponding to the above gains
+ *
+ * Load the gain coefficients in FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's gain
+ * in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+abehal_status abe_write_gain(u32 id, s32 f_g, u32 ramp, u32 p)
+{
+ u32 lin_g, sum_g, mixer_target, mixer_offset, i, mean_gain, mean_exp;
+ u32 new_gain_linear[4];
+ s32 gain_index;
+ u32 alpha, beta;
+ u32 ramp_index;
+ _log(id_write_gain, id, f_g, p);
+ gain_index = ((f_g - min_mdb) / 100);
+ gain_index = maximum(gain_index, 0);
+ gain_index = minimum(gain_index, sizeof_db2lin_table);
+ lin_g = abe_db2lin_table[gain_index];
+ abe_gain_offset(id, &mixer_offset);
+ /* save the input parameters for mute/unmute */
+ abe_desired_gains_linear[mixer_offset + p] = lin_g;
+ abe_desired_gains_decibel[mixer_offset + p] = f_g;
+ abe_desired_ramp_delay_ms[mixer_offset + p] = ramp;
+ /* SMEM word32 address */
+ mixer_target = (S_GTarget1_ADDR << 1);
+ mixer_target += mixer_offset;
+ mixer_target += p;
+ /* translate coef address in Bytes */
+ mixer_target <<= 2;
+ if (abe_compensated_mixer_gain) {
+ switch (id) {
+ case MIXDL1:
+ case MIXDL2:
+ case MIXVXREC:
+ case MIXAUDUL:
+ /* compute the sum of the gain of the mixer */
+ for (sum_g = i = 0; i < 4; i++)
+ sum_g += abe_desired_gains_linear[mixer_offset +
+ i];
+ /* lets avoid a division by 0 */
+ if (sum_g == 0)
+ break;
+ /* if the sum is OK with less than 1, then
+ do not weight the gains */
+ if (sum_g < 0x00040000) { /* REMOVE HARD CONST */
+ /* recompute all gains from original
+ desired values */
+ sum_g = 0x00040000;
+ }
+ /* translate it in Q16 format for the later division */
+ abe_int_2_float16(sum_g, &mean_gain, &mean_exp);
+ mean_exp = 10 - mean_exp;
+ for (i = 0; i < 4; i++) {
+ /* new gain = desired gain divided by sum of gains */
+ new_gain_linear[i] =
+ (abe_desired_gains_linear
+ [mixer_offset + i]
+ << 8) / mean_gain;
+ new_gain_linear[i] = (mean_exp > 0) ?
+ new_gain_linear[i] << mean_exp :
+ new_gain_linear[i] >> mean_exp;
+ }
+ /* load the whole adpated S_G_Target SMEM MIXER table */
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_SMEM,
+ mixer_target - (p << 2),
+ new_gain_linear, (4 * sizeof(lin_g)));
+ break;
+ default:
+ /* load the S_G_Target SMEM table */
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_SMEM,
+ mixer_target,
+ (u32 *) &lin_g, sizeof(lin_g));
+ break;
+ }
+ } else {
+ if (!abe_muted_gains_indicator[mixer_offset + p])
+ /* load the S_G_Target SMEM table */
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_SMEM,
+ mixer_target, (u32 *) &lin_g,
+ sizeof(lin_g));
+ else
+ /* update muted gain with new value */
+ abe_muted_gains_decibel[mixer_offset + p] = f_g;
+ }
+ ramp = maximum(minimum(RAMP_MAXLENGTH, ramp), RAMP_MINLENGTH);
+ /* ramp data should be interpolated in the table instead */
+ ramp_index = 8;
+ if ((RAMP_5MS <= ramp) && (ramp < RAMP_50MS))
+ ramp_index = 24;
+ if ((RAMP_50MS <= ramp) && (ramp < RAMP_500MS))
+ ramp_index = 36;
+ if (ramp > RAMP_500MS)
+ ramp_index = 48;
+ beta = abe_alpha_iir[ramp_index];
+ alpha = abe_1_alpha_iir[ramp_index];
+ /* CMEM word32 address */
+ mixer_target = C_1_Alpha_ADDR;
+ /* a pair of gains is updated once in the firmware */
+ mixer_target += (p + mixer_offset) >> 1;
+ /* translate coef address in Bytes */
+ mixer_target <<= 2;
+ /* load the ramp delay data */
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_CMEM, mixer_target,
+ (u32 *) &alpha, sizeof(alpha));
+ /* CMEM word32 address */
+ mixer_target = C_Alpha_ADDR;
+ /* a pair of gains is updated once in the firmware */
+ mixer_target += (p + mixer_offset) >> 1;
+ /* translate coef address in Bytes */
+ mixer_target <<= 2;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_CMEM, mixer_target,
+ (u32 *) &beta, sizeof(beta));
+ return 0;
+}
+EXPORT_SYMBOL(abe_write_gain);
+/**
+ * abe_write_mixer
+ * @id: name of the mixer
+ * @param: input gains and delay ramp of the mixer
+ * @p: port corresponding to the above gains
+ *
+ * Load the gain coefficients in FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's
+ * gain in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+abehal_status abe_write_mixer(u32 id, s32 f_g, u32 f_ramp, u32 p)
+{
+ _log(id_write_mixer, id, f_ramp, p);
+ abe_write_gain(id, f_g, f_ramp, p);
+ return 0;
+}
+EXPORT_SYMBOL(abe_write_mixer);
+/**
+ * abe_read_gain
+ * @id: name of the mixer
+ * @param: list of input gains of the mixer
+ * @p: list of port corresponding to the above gains
+ *
+ */
+abehal_status abe_read_gain(u32 id, u32 *f_g, u32 p)
+{
+ u32 mixer_target, mixer_offset, i;
+ _log(id_read_gain, id, (u32) f_g, p);
+ abe_gain_offset(id, &mixer_offset);
+ /* SMEM word32 address */
+ mixer_target = (S_GTarget1_ADDR << 1);
+ mixer_target += mixer_offset;
+ mixer_target += p;
+ /* translate coef address in Bytes */
+ mixer_target <<= 2;
+
+ if (!abe_muted_gains_indicator[mixer_offset + p]) {
+ /* load the S_G_Target SMEM table */
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_SMEM, mixer_target,
+ (u32 *) f_g, sizeof(*f_g));
+ for (i = 0; i < sizeof_db2lin_table; i++) {
+ if (abe_db2lin_table[i] == *f_g)
+ goto found;
+ }
+ *f_g = 0;
+ return -1;
+ found:
+ *f_g = (i * 100) + min_mdb;
+ } else {
+ /* update muted gain with new value */
+ *f_g = abe_muted_gains_decibel[mixer_offset + p];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_gain);
+/**
+ * abe_read_mixer
+ * @id: name of the mixer
+ * @param: gains of the mixer
+ * @p: port corresponding to the above gains
+ *
+ * Load the gain coefficients in FW memory. This API can be called when
+ * the corresponding MIXER is not activated. After reloading the firmware
+ * the default coefficients corresponds to "all input and output mixer's
+ * gain in mute state". A mixer is disabled with a network reconfiguration
+ * corresponding to an OPP value.
+ */
+abehal_status abe_read_mixer(u32 id, u32 *f_g, u32 p)
+{
+ _log(id_read_mixer, id, 0, p);
+ abe_read_gain(id, f_g, p);
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_mixer);
+/**
+ * abe_set_router_configuration
+ * @Id: name of the router
+ * @Conf: id of the configuration
+ * @param: list of output index of the route
+ *
+ * The uplink router takes its input from DMIC (6 samples), AMIC (2 samples)
+ * and PORT1/2 (2 stereo ports). Each sample will be individually stored in
+ * an intermediate table of 10 elements.
+ *
+ * Example of router table parameter for voice uplink with phoenix microphones
+ *
+ * indexes 0 .. 9 = MM_UL description (digital MICs and MMEXTIN)
+ * DMIC1_L_labelID, DMIC1_R_labelID, DMIC2_L_labelID, DMIC2_R_labelID,
+ * MM_EXT_IN_L_labelID, MM_EXT_IN_R_labelID, ZERO_labelID, ZERO_labelID,
+ * ZERO_labelID, ZERO_labelID,
+ * indexes 10 .. 11 = MM_UL2 description (recording on DMIC3)
+ * DMIC3_L_labelID, DMIC3_R_labelID,
+ * indexes 12 .. 13 = VX_UL description (VXUL based on PDMUL data)
+ * AMIC_L_labelID, AMIC_R_labelID,
+ * indexes 14 .. 15 = RESERVED (NULL)
+ * ZERO_labelID, ZERO_labelID,
+ */
+abehal_status abe_set_router_configuration(u32 id, u32 k, u32 *param)
+{
+ _log(id_set_router_configuration, id, (u32) param, (u32) param >> 8);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM,
+ D_aUplinkRouting_ADDR, param, D_aUplinkRouting_sizeof);
+ return 0;
+}
+EXPORT_SYMBOL(abe_set_router_configuration);
+/**
+ * ABE_READ_DEBUG_TRACE
+ *
+ * Parameter :
+ * data destination pointer
+ * max number of data read
+ *
+ * Operations :
+ * reads the AE circular data pointer holding pairs of debug data+
+ * timestamps, and store the pairs in linear addressing to the parameter
+ * pointer. Stops the copy when the max parameter is reached or when the
+ * FIFO is empty.
+ *
+ * Return value :
+ * None.
+ */
+abehal_status abe_read_debug_trace(u32 *data, u32 *n)
+{
+ _log(id_select_data_source, 0, 0, 0);
+ return 0;
+}
+EXPORT_SYMBOL(abe_read_debug_trace);
+/**
+ * abe_connect_debug_trace
+ * @dma2:pointer to the DMEM trace buffer
+ *
+ * returns the address and size of the real-time debug trace buffer,
+ * the content of which will vary from one firmware release to an other
+ */
+abehal_status abe_connect_debug_trace(abe_dma_t *dma2)
+{
+ _log(id_connect_debug_trace, 0, 0, 0);
+ /* return the base address of the ping buffer in L3 and L4 spaces */
+ (*dma2).data = (void *)(D_DEBUG_FIFO_ADDR + ABE_DMEM_BASE_ADDRESS_L3);
+ (*dma2).l3_dmem =
+ (void *)(D_DEBUG_FIFO_ADDR + ABE_DMEM_BASE_ADDRESS_L3);
+ (*dma2).l4_dmem =
+ (void *)(D_DEBUG_FIFO_ADDR + ABE_DMEM_BASE_ADDRESS_L4);
+ (*dma2).iter = D_DEBUG_FIFO_sizeof;
+ return 0;
+}
+EXPORT_SYMBOL(abe_connect_debug_trace);
+/**
+ * abe_set_debug_trace
+ * @debug: debug ID from a list to be defined
+ *
+ * load a mask which filters the debug trace to dedicated types of data
+ */
+abehal_status abe_set_debug_trace(abe_dbg_t debug)
+{
+ _log(id_set_debug_trace, 0, 0, 0);
+ abe_dbg_mask = debug;
+ return 0;
+}
+EXPORT_SYMBOL(abe_set_debug_trace);
+/**
+ * abe_remote_debugger_interface
+ *
+ * interpretation of the UART stream from the remote debugger commands.
+ * The commands consist in setting break points, loading parameter
+ */
+abehal_status abe_remote_debugger_interface(u32 n, u8 *p)
+{
+ _log(id_remote_debugger_interface, n, 0, 0);
+ return 0;
+}
+EXPORT_SYMBOL(abe_remote_debugger_interface);
+/**
+ * abe_enable_test_pattern
+ *
+ */
+abehal_status abe_enable_test_pattern(u32 smem_id, u32 on_off)
+{
+ u16 dbg_on, dbg_off, idx_patch, task_patch, addr_patch;
+ u32 patch, task32;
+ _log(id_enable_test_pattern, on_off, smem_id, smem_id >> 8);
+ switch (smem_id) {
+ case DBG_PATCH_AMIC:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = AMIC_labelID;
+ task_patch = C_ABE_FW_TASK_AMIC_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_DMIC1:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = DMIC1_labelID;
+ task_patch = C_ABE_FW_TASK_DMIC1_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_DMIC2:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = DMIC2_labelID;
+ task_patch = C_ABE_FW_TASK_DMIC2_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_DMIC3:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = DMIC3_labelID;
+ task_patch = C_ABE_FW_TASK_DMIC3_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_VX_REC:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = VX_REC_labelID;
+ task_patch = C_ABE_FW_TASK_VXREC_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_BT_UL:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = BT_UL_labelID;
+ task_patch = C_ABE_FW_TASK_BT_UL_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_MM_DL:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = MM_DL_labelID;
+ task_patch = C_ABE_FW_TASK_MM_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_DL2_EQ:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = DL2_EQ_labelID;
+ task_patch = C_ABE_FW_TASK_DL2_APS_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_VIBRA:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = VIBRA_labelID;
+ task_patch = C_ABE_FW_TASK_VIBRA_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_MM_EXT_IN:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = MM_EXT_IN_labelID;
+ task_patch = C_ABE_FW_TASK_MM_EXT_IN_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_MIC4:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = MIC4_labelID;
+ task_patch = C_ABE_FW_TASK_MIC4_SPLIT;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_MM_DL_MIXDL1:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = AMIC_labelID;
+ task_patch = C_ABE_FW_TASK_DL1Mixer;
+ idx_patch = 1;
+ break;
+ case DBG_PATCH_MM_DL_MIXDL2:
+ dbg_on = DBG_48K_PATTERN_labelID;
+ dbg_off = AMIC_labelID;
+ task_patch = C_ABE_FW_TASK_DL2Mixer;
+ idx_patch = 1;
+ break;
+ default:
+ return 0;
+ }
+ patch = (on_off != 0) ? dbg_on : dbg_off;
+ /* address is on 16bits boundary */
+ addr_patch = D_tasksList_ADDR + (16 * task_patch) + (2 * idx_patch);
+ /* read on 32bits words' boundary */
+ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, addr_patch & (~0x03),
+ &task32, 4);
+ if (addr_patch & 0x03)
+ task32 = (0x0000FFFFL & task32) | (patch << 16);
+ else
+ task32 = (0xFFFF0000L & task32) | (0x0000FFFF & patch);
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, addr_patch & (~0x03),
+ &task32, 4);
+ return 0;
+}
+EXPORT_SYMBOL(abe_enable_test_pattern);
+/**
+ * abe_wakeup - Wakeup ABE
+ *
+ * Wakeup ABE in case of retention
+ */
+abehal_status abe_wakeup(void)
+{
+ /* Restart event generator */
+ abe_write_event_generator(EVENT_TIMER);
+ /* reconfigure DMA Req and MCU Irq visibility */
+ abe_hw_configuration();
+ return 0;
+}
+EXPORT_SYMBOL(abe_wakeup);
+/**
+ * abe_disable_irq - disable MCU/DSP ABE interrupt
+ *
+ * This subroutine is disabling ABE MCU/DSP Irq
+ */
+abehal_status abe_disable_irq(void)
+{
+ u32 atc_reg;
+ /* disables the DMAreq from AESS AESS_DMAENABLE_CLR = 127
+ * DMA_Req7 will still be enabled as it is used for ABE trace */
+ atc_reg = 0x7F;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC, 0x64, &atc_reg, 4);
+ /* disables the MCU IRQ from AESS to Cortex A9 */
+ atc_reg = 0x01;
+ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_ATC, 0x40, &atc_reg, 4);
+ return 0;
+}
+EXPORT_SYMBOL(abe_disable_irq);
+/**
+ * abe_check_activity - Check if some ABE activity.
+ *
+ * Check if any ABE ports are running.
+ * return 1: still activity on ABE
+ * return 0: no more activity on ABE. Event generator can be stopped
+ *
+ */
+u32 abe_check_activity(void)
+{
+ u32 i;
+ u32 ret;
+ ret = 0;
+ for (i = 0; i < (LAST_PORT_ID - 1); i++) {
+ if (abe_port[abe_port_priority[i]].status ==
+ OMAP_ABE_PORT_ACTIVITY_RUNNING)
+ break;
+ }
+ if (i < (LAST_PORT_ID - 1))
+ ret = 1;
+ return ret;
+}
+EXPORT_SYMBOL(abe_check_activity);
+/**
+ * abe_init_mem - Allocate Kernel space memory map for ABE
+ *
+ * Memory map of ABE memory space for PMEM/DMEM/SMEM/DMEM
+ */
+void abe_init_mem(void __iomem *_io_base)
+{
+ io_base = _io_base;
+}
+EXPORT_SYMBOL(abe_init_mem);