void abe_add_sequence(abe_uint32 *id, abe_sequence_t *s) { abe_seq_t *seq_src, *seq_dst; abe_uint32 i, no_end_of_sequence_found; seq_src = &(s->seq1); seq_dst = &((abe_all_sequence[abe_sequence_write_pointer]).seq1); if ((abe_sequence_write_pointer >= MAXNBSEQUENCE) || ((abe_uint32)s == 0)) { abe_dbg_param |= ERR_SEQ; abe_dbg_error_log(ABE_PARAMETER_OVERFLOW); } else { *id = abe_subroutine_write_pointer; (abe_all_sequence[abe_sequence_write_pointer]).mask = s->mask; /* copy the mask */ for (no_end_of_sequence_found = 1, i = 0; i < MAXSEQUENCESTEPS; i++, seq_src++, seq_dst++) { (*seq_dst) = (*seq_src); /* sequence copied line by line */ if ((*(abe_int32 *)seq_src) == -1) { /* stop when the line start with time=(-1) */ no_end_of_sequence_found = 0; break; } } abe_subroutine_write_pointer++; if (no_end_of_sequence_found) abe_dbg_error_log(ABE_SEQTOOLONG); } }
/** * abe_write_fifo * @mem_bank: currently only ABE_DMEM supported * @addr: FIFO descriptor address ( descriptor fields : READ ptr, WRITE ptr, * FIFO START_ADDR, FIFO END_ADDR) * @data: data to write to FIFO * @number: number of 32-bit words to write to DMEM FIFO * * write DMEM FIFO and update FIFO descriptor, * it is assumed that FIFO descriptor is located in DMEM */ void abe_write_fifo(u32 memory_bank, u32 descr_addr, u32 *data, u32 nb_data32) { u32 fifo_addr[4]; u32 i; /* read FIFO descriptor from DMEM */ abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, descr_addr, &fifo_addr[0], 4 * sizeof(u32)); /* WRITE ptr < FIFO start address */ if (fifo_addr[1] < fifo_addr[2]) abe_dbg_error_log(ABE_FW_FIFO_WRITE_PTR_ERR); /* WRITE ptr > FIFO end address */ if (fifo_addr[1] > fifo_addr[3]) abe_dbg_error_log(ABE_FW_FIFO_WRITE_PTR_ERR); switch (memory_bank) { case ABE_DMEM: for (i = 0; i < nb_data32; i++) { abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, (s32) fifo_addr[1], (u32 *) (data + i), 4); /* increment WRITE pointer */ fifo_addr[1] = fifo_addr[1] + 4; if (fifo_addr[1] > fifo_addr[3]) fifo_addr[1] = fifo_addr[2]; if (fifo_addr[1] == fifo_addr[0]) abe_dbg_error_log(ABE_FW_FIFO_WRITE_PTR_ERR); } /* update WRITE pointer in DMEM */ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, descr_addr + sizeof(u32), &fifo_addr[1], 4); break; default: break; } }
/* * ABE_ADD_SUBROUTINE * * Parameter : * port id * pointer to the subroutines * number of parameters to push on the stack before call * * Operations : * add one function pointer more and returns the index to it * * Return value : * */ void abe_add_subroutine (abe_uint32 *id, abe_subroutine2 f, abe_uint32 nparam, abe_uint32* params) { abe_uint32 i, i_found; if ((abe_subroutine_write_pointer >= MAXNBSUBROUTINE) || ((abe_uint32)f == 0)) { abe_dbg_param |= ERR_SEQ; abe_dbg_error_log(ABE_PARAMETER_OVERFLOW); } else { /* search if this subroutine address was not already * declared, then return the previous index */ for (i_found = abe_subroutine_write_pointer, i = 0; i < abe_subroutine_write_pointer; i++) { if (f == abe_all_subsubroutine[i]) i_found = i; } if (i_found == abe_subroutine_write_pointer) { *id = abe_subroutine_write_pointer; abe_all_subsubroutine[abe_subroutine_write_pointer] = (f); abe_all_subroutine_params[abe_subroutine_write_pointer] = params; abe_all_subsubroutine_nparam[abe_subroutine_write_pointer] = nparam; abe_subroutine_write_pointer++; } else { abe_all_subroutine_params[i_found] = params; *id = i_found; } } }
/* * ABE_READ_NEXT_PING_PONG_BUFFER * * Parameter : * Port_ID : * Returned address to the next buffer (byte offset from DMEM start) * * Operations : * Tell the next base address of the next ping_pong Buffer and its size * * */ void abe_read_next_ping_pong_buffer(abe_port_id port, abe_uint32 *p, abe_uint32 *n) { abe_uint32 sio_pp_desc_address; ABE_SPingPongDescriptor desc_pp; _lock_enter _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, (abe_uint32*)&desc_pp, sizeof(ABE_SPingPongDescriptor)); if ((desc_pp.counter & 0x1) == 0) { (*p) = desc_pp.nextbuff0_BaseAddr; } else { (*p) = desc_pp.nextbuff1_BaseAddr; } /* translates the number of samples in bytes */ (*n) = abe_size_pingpong; _lock_exit }
/* * ABE_WRITE_EVENT_GENERATOR * * Parameter : * e: Event Generation Counter, McPDM, DMIC or default. * * Operations : * 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%) * * Return value : * None. */ void abe_write_event_generator(abe_event_id e) { abe_uint32 event, selection, counter, start; _lock_enter _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_MCPDM: selection = EVENT_SOURCE_DMA; event = ABE_ATC_MCPDMDL_DMA_REQ; break; case EVENT_DMIC: selection = EVENT_SOURCE_DMA; event = ABE_ATC_DMIC_DMA_REQ; break; case EVENT_TIMER: selection = EVENT_SOURCE_COUNTER; event = 0; break; case EVENT_McBSP: selection = EVENT_SOURCE_COUNTER; event = 0; break; case EVENT_McASP: selection = EVENT_SOURCE_COUNTER; event = 0; break; case EVENT_SLIMBUS: selection = EVENT_SOURCE_COUNTER; event = 0; break; case EVENT_44100: selection = EVENT_SOURCE_COUNTER; event = 0; counter = EVENT_GENERATOR_COUNTER_44100; break; case EVENT_DEFAULT: selection = EVENT_SOURCE_COUNTER; event = 0; 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); _lock_exit }
/* * ABE_SET_PING_PONG_BUFFER * * Parameter : * Port_ID : * New data * * Operations : * 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. */ void abe_set_ping_pong_buffer(abe_port_id port, abe_uint32 n_bytes) { abe_uint32 sio_pp_desc_address, struct_offset, *src, n_samples, datasize, base_and_size; ABE_SPingPongDescriptor desc_pp; _lock_enter _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, (abe_uint32 *)&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 = (abe_uint32)&(desc_pp.nextbuff0_BaseAddr) - (abe_uint32)&(desc_pp); base_and_size = desc_pp.nextbuff0_BaseAddr; } else { struct_offset = (abe_uint32)&(desc_pp.nextbuff1_BaseAddr) - (abe_uint32)&(desc_pp); base_and_size = desc_pp.nextbuff1_BaseAddr; } base_and_size = (base_and_size & 0xFFFFL) + ((abe_uint32)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, (abe_uint32 *)&base_and_size, sizeof(abe_uint32)); _lock_exit }
/* * ABE_SET_OPP_PROCESSING * * Parameter : * 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% (EANC, multimedia complex use-cases) * * Operations : * 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. * * Return value : * error code when the new OPP do not corresponds the list of activated features */ void abe_set_opp_processing(abe_opp_t opp) { abe_uint32 dOppMode32, sio_desc_address; ABE_SIODescriptor desc; _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(abe_uint32)); sio_desc_address = dmem_port_descriptors + (MM_DL_PORT * sizeof(ABE_SIODescriptor)); abe_block_copy(COPY_FROM_ABE_TO_HOST, ABE_DMEM, sio_desc_address, (abe_uint32*)&desc, sizeof (desc)); if (dOppMode32 == DOPPMODE32_OPP100) desc.smem_addr1 = smem_mm_dl_opp100; /* ASRC input buffer, size 40 */ else desc.smem_addr1 = smem_mm_dl_opp25; /* at OPP 25/50 or without ASRC */ abe_block_copy(COPY_FROM_HOST_TO_ABE, ABE_DMEM, sio_desc_address, (abe_uint32*)&desc, sizeof (desc)); _lock_exit }
/** * @fn abe_connect_dmareq_ping_pong_port() * * Operations : 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. * * Parameters : * 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. * * @see ABE_API.h */ void abe_connect_dmareq_ping_pong_port(abe_port_id id, abe_data_format_t *f, abe_uint32 d, abe_uint32 s, abe_dma_t *returned_dma_t) { abe_dma_t dma1; _lock_enter _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, (abe_uint32 *)&(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 = RUN_P; /* 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 = (abe_uint32 *)(abe_port [id].dma.data + ABE_DMEM_BASE_ADDRESS_L3); dma1.iter = abe_port [id].dma.iter; (*returned_dma_t) = dma1; _lock_exit }
/** * abe_block_copy * @direction: direction of the data move (Read/Write) * @memory_bamk:memory bank among PMEM, DMEM, CMEM, SMEM, ATC/IO * @address: address of the memory copy (byte addressing) * @data: pointer to the data to transfer * @nb_bytes: number of data to move * * Memory transfer to/from ABE to MPU */ void abe_block_copy(u32 direction, u32 memory_bank, u32 address, u32 *data, u32 nb_bytes) { u32 i; u32 base_address = 0, *src_ptr, *dst_ptr, n; switch (memory_bank) { case ABE_PMEM: base_address = (u32) abe->io_base + ABE_PMEM_BASE_OFFSET_MPU; break; case ABE_CMEM: base_address = (u32) abe->io_base + ABE_CMEM_BASE_OFFSET_MPU; break; case ABE_SMEM: base_address = (u32) abe->io_base + ABE_SMEM_BASE_OFFSET_MPU; break; case ABE_DMEM: base_address = (u32) abe->io_base + ABE_DMEM_BASE_OFFSET_MPU; break; case ABE_ATC: base_address = (u32) abe->io_base + ABE_ATC_BASE_OFFSET_MPU; break; default: base_address = (u32) abe->io_base + ABE_SMEM_BASE_OFFSET_MPU; abe->dbg_param |= ERR_LIB; abe_dbg_error_log(ABE_BLOCK_COPY_ERR); break; } if (direction == COPY_FROM_HOST_TO_ABE) { dst_ptr = (u32 *) (base_address + address); src_ptr = (u32 *) data; } else { dst_ptr = (u32 *) data; src_ptr = (u32 *) (base_address + address); } n = (nb_bytes / 4); for (i = 0; i < n; i++) *dst_ptr++ = *src_ptr++; }
/* * ABE_INIT_PING_PONG_BUFFER * * Parameter : * size of the ping pong * number of buffers (2 = ping/pong) * returned address of the ping-pong list of base address (byte offset from DMEM start) * * Operations : * Computes the base address of the ping_pong buffers * */ void abe_init_ping_pong_buffer(abe_port_id id, abe_uint32 size_bytes, abe_uint32 n_buffers, abe_uint32 *p) { abe_uint32 i, dmem_addr; _lock_enter _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); abe_base_address_pingpong [i] = dmem_addr; /* base addresses of the ping pong buffers in U8 unit */ } abe_size_pingpong = size_bytes; /* global data */ *p = (abe_uint32)dmem_ping_pong_buffer; _lock_exit }
/** * abe_read_use_case_opp() description for void abe_read_use_case_opp(). * * Operations : returns the expected min OPP for a given use_case list * * Parameter : No parameter * @param * * @pre no pre-condition * * @post * * @return error code * * @see */ void abe_read_use_case_opp(abe_use_case_id *u, abe_opp_t *o) { abe_uint32 opp, i; abe_use_case_id *ptr = u; #define MAX_READ_USE_CASE_OPP 10 /* there is no reason to have more use_cases */ #define OPP_25 1 #define OPP_50 2 #define OPP_100 4 _lock_enter _log(id_read_use_case_opp,(abe_uint32)(u),(abe_uint32)u>>8,(abe_uint32)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; _lock_exit }