/* This file is provided under a dual BSD/GPLv2 license. When using or redistributing this file, you may do so under either license. GPL LICENSE SUMMARY Copyright(c) 2010-2011 Texas Instruments Incorporated, All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License 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. The full GNU General Public License is included in this distribution in the file called LICENSE.GPL. BSD LICENSE Copyright(c) 2010-2011 Texas Instruments Incorporated, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Texas Instruments Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "abe_dbg.h" #include "abe.h" #include "abe_aess.h" #include "abe_gain.h" #include "abe_mem.h" #include "abe_port.h" #include "abe_seq.h" #include "abe_taskid.h" #define ABE_TASK_ID(ID) (OMAP_ABE_D_TASKSLIST_ADDR + sizeof(ABE_STask)*(ID)) void omap_abe_build_scheduler_table(struct omap_abe *abe); void omap_abe_reset_all_ports(struct omap_abe *abe); const u32 abe_firmware_array[ABE_FIRMWARE_MAX_SIZE] = { #include "abe_firmware.c" }; /* * initialize the default values for call-backs to subroutines * - FIFO IRQ call-backs for sequenced tasks * - FIFO IRQ call-backs for audio player/recorders (ping-pong protocols) * - Remote debugger interface * - Error monitoring * - Activity Tracing */ /** * 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) { int i; abe = kzalloc(sizeof(struct omap_abe), GFP_KERNEL); if (abe == NULL) printk(KERN_ERR "ABE Allocation ERROR "); for (i = 0; i < 5; i++) abe->io_base[i] = _io_base[i]; mutex_init(&abe->mutex); } EXPORT_SYMBOL(abe_init_mem); /** * 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 * */ int abe_load_fw_param(u32 *ABE_FW) { u32 pmem_size, dmem_size, smem_size, cmem_size; u32 *pmem_ptr, *dmem_ptr, *smem_ptr, *cmem_ptr, *fw_ptr; /* fast counter timer set at 4096 * 250us = 1,024s */ u32 data = 0x10001000; _log(ABE_ID_LOAD_FW_param, 0, 0, 0); #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 (abe->warm_boot) { /* Stop the event Generator */ omap_abe_stop_event_generator(abe); /* Now we are sure the firmware is stalled */ omap_abe_mem_write(abe, OMAP_ABE_CMEM, 0, cmem_ptr, cmem_size); omap_abe_mem_write(abe, OMAP_ABE_SMEM, 0, smem_ptr, smem_size); omap_abe_mem_write(abe, OMAP_ABE_DMEM, 0, dmem_ptr, dmem_size); /* Restore the event Generator status */ omap_abe_start_event_generator(abe); } else { omap_abe_mem_write(abe, OMAP_ABE_PMEM, 0, pmem_ptr, pmem_size); omap_abe_mem_write(abe, OMAP_ABE_CMEM, 0, cmem_ptr, cmem_size); omap_abe_mem_write(abe, OMAP_ABE_SMEM, 0, smem_ptr, smem_size); omap_abe_mem_write(abe, OMAP_ABE_DMEM, 0, dmem_ptr, dmem_size); } omap_abe_mem_write(abe, OMAP_ABE_DMEM, OMAP_ABE_D_FASTCOUNTER_ADDR, &data, OMAP_ABE_D_FASTCOUNTER_SIZE); /* Update Saturation threshold */ data = 0x00700000; omap_abe_mem_write(abe, OMAP_ABE_SMEM, OMAP_ABE_S_SATURATION_EQ_ADDR, &data, 4); data = 0x00900000; omap_abe_mem_write(abe, OMAP_ABE_SMEM, OMAP_ABE_S_SATURATION_EQ_ADDR + 4, &data, 4); abe->warm_boot = 1; return 0; } EXPORT_SYMBOL(abe_load_fw_param); /** * omap_abe_load_fw - Load ABE Firmware and initialize memories * @abe: Pointer on abe handle * */ int omap_abe_load_fw(struct omap_abe *abe, u32 *firmware) { _log(ABE_ID_LOAD_FW, 0, 0, 0); abe_load_fw_param(firmware); omap_abe_reset_all_ports(abe); omap_abe_build_scheduler_table(abe); omap_abe_reset_all_sequence(abe); omap_abe_select_main_port(OMAP_ABE_PDM_DL_PORT); return 0; } EXPORT_SYMBOL(omap_abe_load_fw); /** * abe_reload_fw - Reload ABE Firmware after OFF mode */ int omap_abe_reload_fw(struct omap_abe *abe, u32 *firmware) { abe->warm_boot = 0; abe_load_fw_param(firmware); omap_abe_build_scheduler_table(abe); omap_abe_dbg_reset(&abe->dbg); /* IRQ circular read pointer in DMEM */ abe->irq_dbg_read_ptr = 0; /* Restore Gains not managed by the drivers */ omap_abe_write_gain(abe, GAINS_SPLIT, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_SPLIT, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); return 0; } EXPORT_SYMBOL(omap_abe_reload_fw); /** * omap_abe_get_default_fw * * Get default ABE firmware */ u32 *omap_abe_get_default_fw(struct omap_abe *abe) { return (u32 *)abe_firmware_array; } /** * abe_build_scheduler_table * */ void omap_abe_build_scheduler_table(struct omap_abe *abe) { u16 i, n; u8 *ptr; u16 aUplinkMuxing[NBROUTE_UL]; /* LOAD OF THE TASKS' MULTIFRAME */ /* WARNING ON THE LOCATION OF IO_MM_DL WHICH IS PATCHED IN "abe_init_io_tasks" */ for (ptr = (u8 *) &(abe->MultiFrame[0][0]), i = 0; i < sizeof(abe->MultiFrame); i++) *ptr++ = 0; abe->MultiFrame[0][0] = 0/*ABE_TASK_ID(C_ABE_FW_TASK_IO_PDM_UL)*/; abe->MultiFrame[0][3] = ABE_TASK_ID(C_ABE_FW_TASK_ASRC_VX_DL_8); abe->MultiFrame[1][3] = ABE_TASK_ID(C_ABE_FW_TASK_VX_DL_8_48_FIR); abe->MultiFrame[1][6] = ABE_TASK_ID(C_ABE_FW_TASK_DL2Mixer); abe->MultiFrame[1][7] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_VIB_DL)*/ abe->MultiFrame[2][0] = ABE_TASK_ID(C_ABE_FW_TASK_DL1Mixer); abe->MultiFrame[2][1] = ABE_TASK_ID(C_ABE_FW_TASK_SDTMixer); abe->MultiFrame[2][5] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_DMIC)*/ abe->MultiFrame[3][0] = ABE_TASK_ID(C_ABE_FW_TASK_DL1_GAIN); abe->MultiFrame[3][6] = ABE_TASK_ID(C_ABE_FW_TASK_DL2_GAIN); abe->MultiFrame[3][7] = ABE_TASK_ID(C_ABE_FW_TASK_DL2_EQ); abe->MultiFrame[4][0] = ABE_TASK_ID(C_ABE_FW_TASK_DL1_EQ); abe->MultiFrame[4][2] = ABE_TASK_ID(C_ABE_FW_TASK_VXRECMixer); abe->MultiFrame[4][3] = ABE_TASK_ID(C_ABE_FW_TASK_VXREC_SPLIT); abe->MultiFrame[4][6] = ABE_TASK_ID(C_ABE_FW_TASK_VIBRA1); abe->MultiFrame[4][7] = ABE_TASK_ID(C_ABE_FW_TASK_VIBRA2); abe->MultiFrame[5][0] = 0; abe->MultiFrame[5][1] = ABE_TASK_ID(C_ABE_FW_TASK_EARP_48_96_LP); abe->MultiFrame[5][2] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_PDM_UL)*/ abe->MultiFrame[5][7] = ABE_TASK_ID(C_ABE_FW_TASK_VIBRA_SPLIT); abe->MultiFrame[6][0] = ABE_TASK_ID(C_ABE_FW_TASK_EARP_48_96_LP); abe->MultiFrame[6][4] = ABE_TASK_ID(C_ABE_FW_TASK_EchoMixer); abe->MultiFrame[6][5] = ABE_TASK_ID(C_ABE_FW_TASK_BT_UL_SPLIT); abe->MultiFrame[7][0] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_PDM_DL)*/ abe->MultiFrame[7][3] = ABE_TASK_ID(C_ABE_FW_TASK_DBG_SYNC); abe->MultiFrame[7][5] = ABE_TASK_ID(C_ABE_FW_TASK_ECHO_REF_SPLIT); abe->MultiFrame[8][2] = ABE_TASK_ID(C_ABE_FW_TASK_DMIC1_96_48_LP); abe->MultiFrame[8][4] = ABE_TASK_ID(C_ABE_FW_TASK_DMIC1_SPLIT); abe->MultiFrame[9][2] = ABE_TASK_ID(C_ABE_FW_TASK_DMIC2_96_48_LP); abe->MultiFrame[9][4] = ABE_TASK_ID(C_ABE_FW_TASK_DMIC2_SPLIT); abe->MultiFrame[9][6] = 0; abe->MultiFrame[9][7] = ABE_TASK_ID(C_ABE_FW_TASK_IHF_48_96_LP); abe->MultiFrame[10][2] = ABE_TASK_ID(C_ABE_FW_TASK_DMIC3_96_48_LP); abe->MultiFrame[10][4] = ABE_TASK_ID(C_ABE_FW_TASK_DMIC3_SPLIT); abe->MultiFrame[10][7] = ABE_TASK_ID(C_ABE_FW_TASK_IHF_48_96_LP); abe->MultiFrame[11][2] = ABE_TASK_ID(C_ABE_FW_TASK_AMIC_96_48_LP); abe->MultiFrame[11][4] = ABE_TASK_ID(C_ABE_FW_TASK_AMIC_SPLIT); abe->MultiFrame[11][7] = ABE_TASK_ID(C_ABE_FW_TASK_VIBRA_PACK); abe->MultiFrame[12][3] = ABE_TASK_ID(C_ABE_FW_TASK_VX_UL_ROUTING); abe->MultiFrame[12][4] = ABE_TASK_ID(C_ABE_FW_TASK_ULMixer); abe->MultiFrame[12][5] = ABE_TASK_ID(C_ABE_FW_TASK_VX_UL_48_8); abe->MultiFrame[13][2] = ABE_TASK_ID(C_ABE_FW_TASK_MM_UL2_ROUTING); abe->MultiFrame[13][3] = ABE_TASK_ID(C_ABE_FW_TASK_SideTone); abe->MultiFrame[13][5] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_BT_VX_DL)*/ abe->MultiFrame[14][3] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_DMIC)*/ abe->MultiFrame[14][4] = ABE_TASK_ID(C_ABE_FW_TASK_BT_DL_48_8_FIR_FW_COMPAT); abe->MultiFrame[15][0] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_MM_EXT_OUT)*/ abe->MultiFrame[15][3] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_BT_VX_UL)*/ abe->MultiFrame[15][6] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_ASRC_BT_UL_8)*/ abe->MultiFrame[16][2] = ABE_TASK_ID(C_ABE_FW_TASK_ASRC_VX_UL_8); abe->MultiFrame[16][3] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_VX_UL)*/ abe->MultiFrame[17][2] = ABE_TASK_ID(C_ABE_FW_TASK_BT_UL_8_48); abe->MultiFrame[17][3] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_MM_UL2)*/ abe->MultiFrame[18][0] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_MM_DL)*/ abe->MultiFrame[18][6] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_ASRC_BT_DL_8)*/ abe->MultiFrame[19][0] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_PDM_DL)*/ /* MM_UL is moved to OPP 100% */ abe->MultiFrame[19][6] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_MM_UL)*/ abe->MultiFrame[20][0] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_IO_TONES_DL)*/ abe->MultiFrame[20][6] = 0; /*ABE_TASK_ID(C_ABE_FW_TASK_ASRC_MM_EXT_IN)*/ abe->MultiFrame[21][1] = ABE_TASK_ID(C_ABE_FW_TASK_DEBUGTRACE_VX_ASRCs); abe->MultiFrame[21][2] = ABE_TASK_ID(C_ABE_FW_TASK_CHECK_IIR_RIGHT_8K); abe->MultiFrame[21][3] = 0/*ABE_TASK_ID(C_ABE_FW_TASK_IO_MM_EXT_IN)*/; /* MUST STAY ON SLOT 22 */ abe->MultiFrame[22][0] = ABE_TASK_ID(C_ABE_FW_TASK_DEBUG_IRQFIFO); abe->MultiFrame[22][1] = ABE_TASK_ID(C_ABE_FW_TASK_INIT_FW_MEMORY); abe->MultiFrame[22][2] = ABE_TASK_ID(C_ABE_FW_TASK_IO_VX_DL); /* MM_EXT_IN_SPLIT task must be after IO_MM_EXT_IN and before ASRC_MM_EXT_IN in order to manage OPP50 <-> transitions */ abe->MultiFrame[22][4] = ABE_TASK_ID(C_ABE_FW_TASK_MM_EXT_IN_SPLIT); abe->MultiFrame[23][0] = ABE_TASK_ID(C_ABE_FW_TASK_GAIN_UPDATE); abe->MultiFrame[23][2] = ABE_TASK_ID(C_ABE_FW_TASK_CHECK_IIR_LEFT_8K); omap_abe_mem_write(abe, OMAP_ABE_DMEM, OMAP_ABE_D_MULTIFRAME_ADDR, (u32 *) abe->MultiFrame, sizeof(abe->MultiFrame)); /* reset the uplink router */ n = (OMAP_ABE_D_AUPLINKROUTING_SIZE) >> 1; for (i = 0; i < n; i++) aUplinkMuxing[i] = ZERO_labelID; omap_abe_mem_write(abe, OMAP_ABE_DMEM, OMAP_ABE_D_AUPLINKROUTING_ADDR, (u32 *) aUplinkMuxing, sizeof(aUplinkMuxing)); } /** * omap_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. */ int omap_abe_reset_port(u32 id) { _log(ABE_ID_RESET_PORT, id, 0, 0); abe_port[id] = ((abe_port_t *) abe_port_init)[id]; return 0; } /** * abe_reset_all_ports * * load default configuration for all features */ void omap_abe_reset_all_ports(struct omap_abe *abe) { u16 i; for (i = 0; i < LAST_PORT_ID; i++) omap_abe_reset_port(i); /* mixers' configuration */ omap_abe_write_mixer(abe, MIXDL1, MUTE_GAIN, RAMP_2MS, MIX_DL1_INPUT_MM_DL); omap_abe_write_mixer(abe, MIXDL1, MUTE_GAIN, RAMP_2MS, MIX_DL1_INPUT_MM_UL2); omap_abe_write_mixer(abe, MIXDL1, MUTE_GAIN, RAMP_2MS, MIX_DL1_INPUT_VX_DL); omap_abe_write_mixer(abe, MIXDL1, MUTE_GAIN, RAMP_2MS, MIX_DL1_INPUT_TONES); omap_abe_write_mixer(abe, MIXDL2, MUTE_GAIN, RAMP_2MS, MIX_DL2_INPUT_TONES); omap_abe_write_mixer(abe, MIXDL2, MUTE_GAIN, RAMP_2MS, MIX_DL2_INPUT_VX_DL); omap_abe_write_mixer(abe, MIXDL2, MUTE_GAIN, RAMP_2MS, MIX_DL2_INPUT_MM_DL); omap_abe_write_mixer(abe, MIXDL2, MUTE_GAIN, RAMP_2MS, MIX_DL2_INPUT_MM_UL2); omap_abe_write_mixer(abe, MIXSDT, MUTE_GAIN, RAMP_2MS, MIX_SDT_INPUT_UP_MIXER); omap_abe_write_mixer(abe, MIXSDT, GAIN_0dB, RAMP_2MS, MIX_SDT_INPUT_DL1_MIXER); omap_abe_write_mixer(abe, MIXECHO, MUTE_GAIN, RAMP_2MS, MIX_ECHO_DL1); omap_abe_write_mixer(abe, MIXECHO, MUTE_GAIN, RAMP_2MS, MIX_ECHO_DL2); omap_abe_write_mixer(abe, MIXAUDUL, MUTE_GAIN, RAMP_2MS, MIX_AUDUL_INPUT_MM_DL); omap_abe_write_mixer(abe, MIXAUDUL, MUTE_GAIN, RAMP_2MS, MIX_AUDUL_INPUT_TONES); omap_abe_write_mixer(abe, MIXAUDUL, GAIN_0dB, RAMP_2MS, MIX_AUDUL_INPUT_UPLINK); omap_abe_write_mixer(abe, MIXAUDUL, MUTE_GAIN, RAMP_2MS, MIX_AUDUL_INPUT_VX_DL); omap_abe_write_mixer(abe, MIXVXREC, MUTE_GAIN, RAMP_2MS, MIX_VXREC_INPUT_TONES); omap_abe_write_mixer(abe, MIXVXREC, MUTE_GAIN, RAMP_2MS, MIX_VXREC_INPUT_VX_DL); omap_abe_write_mixer(abe, MIXVXREC, MUTE_GAIN, RAMP_2MS, MIX_VXREC_INPUT_MM_DL); omap_abe_write_mixer(abe, MIXVXREC, MUTE_GAIN, RAMP_2MS, MIX_VXREC_INPUT_VX_UL); omap_abe_write_gain(abe, GAINS_DMIC1, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_DMIC1, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_DMIC2, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_DMIC2, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_DMIC3, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_DMIC3, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_AMIC, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_AMIC, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_SPLIT, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_SPLIT, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_DL1, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_DL1, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_DL2, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_DL2, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); omap_abe_write_gain(abe, GAINS_BTUL, GAIN_0dB, RAMP_2MS, GAIN_LEFT_OFFSET); omap_abe_write_gain(abe, GAINS_BTUL, GAIN_0dB, RAMP_2MS, GAIN_RIGHT_OFFSET); }