| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Support for Intel Camera Imaging ISP subsystem. |
| * Copyright (c) 2015, Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope 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. |
| */ |
| |
| #include "assert_support.h" /* assert */ |
| #include "ia_css_buffer.h" |
| #include "sp.h" |
| #include "ia_css_bufq.h" /* Bufq API's */ |
| #include "ia_css_queue.h" /* ia_css_queue_t */ |
| #include "sw_event_global.h" /* Event IDs.*/ |
| #include "ia_css_eventq.h" /* ia_css_eventq_recv()*/ |
| #include "ia_css_debug.h" /* ia_css_debug_dtrace*/ |
| #include "sh_css_internal.h" /* sh_css_queue_type */ |
| #include "sp_local.h" /* sp_address_of */ |
| #include "sh_css_firmware.h" /* sh_css_sp_fw*/ |
| |
| #define BUFQ_DUMP_FILE_NAME_PREFIX_SIZE 256 |
| |
| static char prefix[BUFQ_DUMP_FILE_NAME_PREFIX_SIZE] = {0}; |
| |
| /*********************************************************/ |
| /* Global Queue objects used by CSS */ |
| /*********************************************************/ |
| |
| struct sh_css_queues { |
| /* Host2SP buffer queue */ |
| ia_css_queue_t host2sp_buffer_queue_handles |
| [SH_CSS_MAX_SP_THREADS][SH_CSS_MAX_NUM_QUEUES]; |
| /* SP2Host buffer queue */ |
| ia_css_queue_t sp2host_buffer_queue_handles |
| [SH_CSS_MAX_NUM_QUEUES]; |
| |
| /* Host2SP event queue */ |
| ia_css_queue_t host2sp_psys_event_queue_handle; |
| |
| /* SP2Host event queue */ |
| ia_css_queue_t sp2host_psys_event_queue_handle; |
| |
| /* Host2SP ISYS event queue */ |
| ia_css_queue_t host2sp_isys_event_queue_handle; |
| |
| /* SP2Host ISYS event queue */ |
| ia_css_queue_t sp2host_isys_event_queue_handle; |
| /* Tagger command queue */ |
| ia_css_queue_t host2sp_tag_cmd_queue_handle; |
| }; |
| |
| /******************************************************* |
| *** Static variables |
| ********************************************************/ |
| static struct sh_css_queues css_queues; |
| |
| static int |
| buffer_type_to_queue_id_map[SH_CSS_MAX_SP_THREADS][IA_CSS_NUM_DYNAMIC_BUFFER_TYPE]; |
| static bool queue_availability[SH_CSS_MAX_SP_THREADS][SH_CSS_MAX_NUM_QUEUES]; |
| |
| /******************************************************* |
| *** Static functions |
| ********************************************************/ |
| static void map_buffer_type_to_queue_id( |
| unsigned int thread_id, |
| enum ia_css_buffer_type buf_type |
| ); |
| static void unmap_buffer_type_to_queue_id( |
| unsigned int thread_id, |
| enum ia_css_buffer_type buf_type |
| ); |
| |
| static ia_css_queue_t *bufq_get_qhandle( |
| enum sh_css_queue_type type, |
| enum sh_css_queue_id id, |
| int thread |
| ); |
| |
| /******************************************************* |
| *** Public functions |
| ********************************************************/ |
| void ia_css_queue_map_init(void) |
| { |
| unsigned int i, j; |
| |
| for (i = 0; i < SH_CSS_MAX_SP_THREADS; i++) { |
| for (j = 0; j < SH_CSS_MAX_NUM_QUEUES; j++) |
| queue_availability[i][j] = true; |
| } |
| |
| for (i = 0; i < SH_CSS_MAX_SP_THREADS; i++) { |
| for (j = 0; j < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE; j++) |
| buffer_type_to_queue_id_map[i][j] = SH_CSS_INVALID_QUEUE_ID; |
| } |
| } |
| |
| void ia_css_queue_map( |
| unsigned int thread_id, |
| enum ia_css_buffer_type buf_type, |
| bool map) |
| { |
| assert(buf_type < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE); |
| assert(thread_id < SH_CSS_MAX_SP_THREADS); |
| |
| ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, |
| "ia_css_queue_map() enter: buf_type=%d, thread_id=%d\n", buf_type, thread_id); |
| |
| if (map) |
| map_buffer_type_to_queue_id(thread_id, buf_type); |
| else |
| unmap_buffer_type_to_queue_id(thread_id, buf_type); |
| } |
| |
| /* |
| * @brief Query the internal queue ID. |
| */ |
| bool ia_css_query_internal_queue_id( |
| enum ia_css_buffer_type buf_type, |
| unsigned int thread_id, |
| enum sh_css_queue_id *val) |
| { |
| IA_CSS_ENTER("buf_type=%d, thread_id=%d, val = %p", buf_type, thread_id, val); |
| |
| if ((!val) || (thread_id >= SH_CSS_MAX_SP_THREADS) || |
| (buf_type >= IA_CSS_NUM_DYNAMIC_BUFFER_TYPE)) { |
| IA_CSS_LEAVE("return_val = false"); |
| return false; |
| } |
| |
| *val = buffer_type_to_queue_id_map[thread_id][buf_type]; |
| if ((*val == SH_CSS_INVALID_QUEUE_ID) || (*val >= SH_CSS_MAX_NUM_QUEUES)) { |
| IA_CSS_LOG("INVALID queue ID MAP = %d\n", *val); |
| IA_CSS_LEAVE("return_val = false"); |
| return false; |
| } |
| IA_CSS_LEAVE("return_val = true"); |
| return true; |
| } |
| |
| /******************************************************* |
| *** Static functions |
| ********************************************************/ |
| static void map_buffer_type_to_queue_id( |
| unsigned int thread_id, |
| enum ia_css_buffer_type buf_type) |
| { |
| unsigned int i; |
| |
| assert(thread_id < SH_CSS_MAX_SP_THREADS); |
| assert(buf_type < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE); |
| assert(buffer_type_to_queue_id_map[thread_id][buf_type] == |
| SH_CSS_INVALID_QUEUE_ID); |
| |
| /* queue 0 is reserved for parameters because it doesn't depend on events */ |
| if (buf_type == IA_CSS_BUFFER_TYPE_PARAMETER_SET) { |
| assert(queue_availability[thread_id][IA_CSS_PARAMETER_SET_QUEUE_ID]); |
| queue_availability[thread_id][IA_CSS_PARAMETER_SET_QUEUE_ID] = false; |
| buffer_type_to_queue_id_map[thread_id][buf_type] = |
| IA_CSS_PARAMETER_SET_QUEUE_ID; |
| return; |
| } |
| |
| /* queue 1 is reserved for per frame parameters because it doesn't depend on events */ |
| if (buf_type == IA_CSS_BUFFER_TYPE_PER_FRAME_PARAMETER_SET) { |
| assert(queue_availability[thread_id][IA_CSS_PER_FRAME_PARAMETER_SET_QUEUE_ID]); |
| queue_availability[thread_id][IA_CSS_PER_FRAME_PARAMETER_SET_QUEUE_ID] = false; |
| buffer_type_to_queue_id_map[thread_id][buf_type] = |
| IA_CSS_PER_FRAME_PARAMETER_SET_QUEUE_ID; |
| return; |
| } |
| |
| for (i = SH_CSS_QUEUE_C_ID; i < SH_CSS_MAX_NUM_QUEUES; i++) { |
| if (queue_availability[thread_id][i]) { |
| queue_availability[thread_id][i] = false; |
| buffer_type_to_queue_id_map[thread_id][buf_type] = i; |
| break; |
| } |
| } |
| |
| assert(i != SH_CSS_MAX_NUM_QUEUES); |
| return; |
| } |
| |
| static void unmap_buffer_type_to_queue_id( |
| unsigned int thread_id, |
| enum ia_css_buffer_type buf_type) |
| { |
| int queue_id; |
| |
| assert(thread_id < SH_CSS_MAX_SP_THREADS); |
| assert(buf_type < IA_CSS_NUM_DYNAMIC_BUFFER_TYPE); |
| assert(buffer_type_to_queue_id_map[thread_id][buf_type] != |
| SH_CSS_INVALID_QUEUE_ID); |
| |
| queue_id = buffer_type_to_queue_id_map[thread_id][buf_type]; |
| buffer_type_to_queue_id_map[thread_id][buf_type] = SH_CSS_INVALID_QUEUE_ID; |
| queue_availability[thread_id][queue_id] = true; |
| } |
| |
| static ia_css_queue_t *bufq_get_qhandle( |
| enum sh_css_queue_type type, |
| enum sh_css_queue_id id, |
| int thread) |
| { |
| ia_css_queue_t *q = NULL; |
| |
| switch (type) { |
| case sh_css_host2sp_buffer_queue: |
| if ((thread >= SH_CSS_MAX_SP_THREADS) || (thread < 0) || |
| (id == SH_CSS_INVALID_QUEUE_ID)) |
| break; |
| q = &css_queues.host2sp_buffer_queue_handles[thread][id]; |
| break; |
| case sh_css_sp2host_buffer_queue: |
| if (id == SH_CSS_INVALID_QUEUE_ID) |
| break; |
| q = &css_queues.sp2host_buffer_queue_handles[id]; |
| break; |
| case sh_css_host2sp_psys_event_queue: |
| q = &css_queues.host2sp_psys_event_queue_handle; |
| break; |
| case sh_css_sp2host_psys_event_queue: |
| q = &css_queues.sp2host_psys_event_queue_handle; |
| break; |
| case sh_css_host2sp_isys_event_queue: |
| q = &css_queues.host2sp_isys_event_queue_handle; |
| break; |
| case sh_css_sp2host_isys_event_queue: |
| q = &css_queues.sp2host_isys_event_queue_handle; |
| break; |
| case sh_css_host2sp_tag_cmd_queue: |
| q = &css_queues.host2sp_tag_cmd_queue_handle; |
| break; |
| default: |
| break; |
| } |
| |
| return q; |
| } |
| |
| /* Local function to initialize a buffer queue. This reduces |
| * the chances of copy-paste errors or typos. |
| */ |
| static inline void |
| init_bufq(unsigned int desc_offset, |
| unsigned int elems_offset, |
| ia_css_queue_t *handle) |
| { |
| const struct ia_css_fw_info *fw; |
| unsigned int q_base_addr; |
| ia_css_queue_remote_t remoteq; |
| |
| fw = &sh_css_sp_fw; |
| q_base_addr = fw->info.sp.host_sp_queue; |
| |
| /* Setup queue location as SP and proc id as SP0_ID */ |
| remoteq.location = IA_CSS_QUEUE_LOC_SP; |
| remoteq.proc_id = SP0_ID; |
| remoteq.cb_desc_addr = q_base_addr + desc_offset; |
| remoteq.cb_elems_addr = q_base_addr + elems_offset; |
| /* Initialize the queue instance and obtain handle */ |
| ia_css_queue_remote_init(handle, &remoteq); |
| } |
| |
| void ia_css_bufq_init(void) |
| { |
| int i, j; |
| |
| IA_CSS_ENTER_PRIVATE(""); |
| |
| /* Setup all the local queue descriptors for Host2SP Buffer Queues */ |
| for (i = 0; i < SH_CSS_MAX_SP_THREADS; i++) |
| for (j = 0; j < SH_CSS_MAX_NUM_QUEUES; j++) { |
| init_bufq((uint32_t)offsetof(struct host_sp_queues, |
| host2sp_buffer_queues_desc[i][j]), |
| (uint32_t)offsetof(struct host_sp_queues, host2sp_buffer_queues_elems[i][j]), |
| &css_queues.host2sp_buffer_queue_handles[i][j]); |
| } |
| |
| /* Setup all the local queue descriptors for SP2Host Buffer Queues */ |
| for (i = 0; i < SH_CSS_MAX_NUM_QUEUES; i++) { |
| init_bufq(offsetof(struct host_sp_queues, sp2host_buffer_queues_desc[i]), |
| offsetof(struct host_sp_queues, sp2host_buffer_queues_elems[i]), |
| &css_queues.sp2host_buffer_queue_handles[i]); |
| } |
| |
| /* Host2SP event queue*/ |
| init_bufq((uint32_t)offsetof(struct host_sp_queues, |
| host2sp_psys_event_queue_desc), |
| (uint32_t)offsetof(struct host_sp_queues, host2sp_psys_event_queue_elems), |
| &css_queues.host2sp_psys_event_queue_handle); |
| |
| /* SP2Host event queue */ |
| init_bufq((uint32_t)offsetof(struct host_sp_queues, |
| sp2host_psys_event_queue_desc), |
| (uint32_t)offsetof(struct host_sp_queues, sp2host_psys_event_queue_elems), |
| &css_queues.sp2host_psys_event_queue_handle); |
| |
| /* Host2SP ISYS event queue */ |
| init_bufq((uint32_t)offsetof(struct host_sp_queues, |
| host2sp_isys_event_queue_desc), |
| (uint32_t)offsetof(struct host_sp_queues, host2sp_isys_event_queue_elems), |
| &css_queues.host2sp_isys_event_queue_handle); |
| |
| /* SP2Host ISYS event queue*/ |
| init_bufq((uint32_t)offsetof(struct host_sp_queues, |
| sp2host_isys_event_queue_desc), |
| (uint32_t)offsetof(struct host_sp_queues, sp2host_isys_event_queue_elems), |
| &css_queues.sp2host_isys_event_queue_handle); |
| |
| /* Host2SP tagger command queue */ |
| init_bufq((uint32_t)offsetof(struct host_sp_queues, host2sp_tag_cmd_queue_desc), |
| (uint32_t)offsetof(struct host_sp_queues, host2sp_tag_cmd_queue_elems), |
| &css_queues.host2sp_tag_cmd_queue_handle); |
| |
| IA_CSS_LEAVE_PRIVATE(""); |
| } |
| |
| int ia_css_bufq_enqueue_buffer( |
| int thread_index, |
| int queue_id, |
| uint32_t item) |
| { |
| ia_css_queue_t *q; |
| int error; |
| |
| IA_CSS_ENTER_PRIVATE("queue_id=%d", queue_id); |
| if ((thread_index >= SH_CSS_MAX_SP_THREADS) || (thread_index < 0) || |
| (queue_id == SH_CSS_INVALID_QUEUE_ID)) |
| return -EINVAL; |
| |
| /* Get the queue for communication */ |
| q = bufq_get_qhandle(sh_css_host2sp_buffer_queue, |
| queue_id, |
| thread_index); |
| if (q) { |
| error = ia_css_queue_enqueue(q, item); |
| } else { |
| IA_CSS_ERROR("queue is not initialized"); |
| error = -EBUSY; |
| } |
| |
| IA_CSS_LEAVE_ERR_PRIVATE(error); |
| return error; |
| } |
| |
| int ia_css_bufq_dequeue_buffer( |
| int queue_id, |
| uint32_t *item) |
| { |
| int error; |
| ia_css_queue_t *q; |
| |
| IA_CSS_ENTER_PRIVATE("queue_id=%d", queue_id); |
| if ((!item) || |
| (queue_id <= SH_CSS_INVALID_QUEUE_ID) || |
| (queue_id >= SH_CSS_MAX_NUM_QUEUES) |
| ) |
| return -EINVAL; |
| |
| q = bufq_get_qhandle(sh_css_sp2host_buffer_queue, |
| queue_id, |
| -1); |
| if (q) { |
| error = ia_css_queue_dequeue(q, item); |
| } else { |
| IA_CSS_ERROR("queue is not initialized"); |
| error = -EBUSY; |
| } |
| |
| IA_CSS_LEAVE_ERR_PRIVATE(error); |
| return error; |
| } |
| |
| int ia_css_bufq_enqueue_psys_event( |
| u8 evt_id, |
| u8 evt_payload_0, |
| u8 evt_payload_1, |
| uint8_t evt_payload_2) |
| { |
| int error = 0; |
| ia_css_queue_t *q; |
| |
| IA_CSS_ENTER_PRIVATE("evt_id=%d", evt_id); |
| q = bufq_get_qhandle(sh_css_host2sp_psys_event_queue, -1, -1); |
| if (!q) { |
| IA_CSS_ERROR("queue is not initialized"); |
| return -EBUSY; |
| } |
| |
| error = ia_css_eventq_send(q, |
| evt_id, evt_payload_0, evt_payload_1, evt_payload_2); |
| |
| IA_CSS_LEAVE_ERR_PRIVATE(error); |
| return error; |
| } |
| |
| int ia_css_bufq_dequeue_psys_event( |
| u8 item[BUFQ_EVENT_SIZE]) |
| { |
| int error = 0; |
| ia_css_queue_t *q; |
| |
| /* No ENTER/LEAVE in this function since this is polled |
| * by some test apps. Enablign logging here floods the log |
| * files which may cause timeouts. */ |
| if (!item) |
| return -EINVAL; |
| |
| q = bufq_get_qhandle(sh_css_sp2host_psys_event_queue, -1, -1); |
| if (!q) { |
| IA_CSS_ERROR("queue is not initialized"); |
| return -EBUSY; |
| } |
| error = ia_css_eventq_recv(q, item); |
| |
| return error; |
| } |
| |
| int ia_css_bufq_dequeue_isys_event( |
| u8 item[BUFQ_EVENT_SIZE]) |
| { |
| int error = 0; |
| ia_css_queue_t *q; |
| |
| /* No ENTER/LEAVE in this function since this is polled |
| * by some test apps. Enablign logging here floods the log |
| * files which may cause timeouts. */ |
| if (!item) |
| return -EINVAL; |
| |
| q = bufq_get_qhandle(sh_css_sp2host_isys_event_queue, -1, -1); |
| if (!q) { |
| IA_CSS_ERROR("queue is not initialized"); |
| return -EBUSY; |
| } |
| error = ia_css_eventq_recv(q, item); |
| return error; |
| } |
| |
| int ia_css_bufq_enqueue_isys_event(uint8_t evt_id) |
| { |
| int error = 0; |
| ia_css_queue_t *q; |
| |
| IA_CSS_ENTER_PRIVATE("event_id=%d", evt_id); |
| q = bufq_get_qhandle(sh_css_host2sp_isys_event_queue, -1, -1); |
| if (!q) { |
| IA_CSS_ERROR("queue is not initialized"); |
| return -EBUSY; |
| } |
| |
| error = ia_css_eventq_send(q, evt_id, 0, 0, 0); |
| |
| IA_CSS_LEAVE_ERR_PRIVATE(error); |
| return error; |
| } |
| |
| int ia_css_bufq_enqueue_tag_cmd( |
| uint32_t item) |
| { |
| int error; |
| ia_css_queue_t *q; |
| |
| IA_CSS_ENTER_PRIVATE("item=%d", item); |
| q = bufq_get_qhandle(sh_css_host2sp_tag_cmd_queue, -1, -1); |
| if (!q) { |
| IA_CSS_ERROR("queue is not initialized"); |
| return -EBUSY; |
| } |
| error = ia_css_queue_enqueue(q, item); |
| |
| IA_CSS_LEAVE_ERR_PRIVATE(error); |
| return error; |
| } |
| |
| int ia_css_bufq_deinit(void) |
| { |
| return 0; |
| } |
| |
| static void bufq_dump_queue_info(const char *prefix, ia_css_queue_t *qhandle) |
| { |
| u32 free = 0, used = 0; |
| |
| assert(prefix && qhandle); |
| ia_css_queue_get_used_space(qhandle, &used); |
| ia_css_queue_get_free_space(qhandle, &free); |
| ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "%s: used=%u free=%u\n", |
| prefix, used, free); |
| } |
| |
| void ia_css_bufq_dump_queue_info(void) |
| { |
| int i, j; |
| |
| ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "Queue Information:\n"); |
| |
| for (i = 0; i < SH_CSS_MAX_SP_THREADS; i++) { |
| for (j = 0; j < SH_CSS_MAX_NUM_QUEUES; j++) { |
| snprintf(prefix, BUFQ_DUMP_FILE_NAME_PREFIX_SIZE, |
| "host2sp_buffer_queue[%u][%u]", i, j); |
| bufq_dump_queue_info(prefix, |
| &css_queues.host2sp_buffer_queue_handles[i][j]); |
| } |
| } |
| |
| for (i = 0; i < SH_CSS_MAX_NUM_QUEUES; i++) { |
| snprintf(prefix, BUFQ_DUMP_FILE_NAME_PREFIX_SIZE, |
| "sp2host_buffer_queue[%u]", i); |
| bufq_dump_queue_info(prefix, |
| &css_queues.sp2host_buffer_queue_handles[i]); |
| } |
| bufq_dump_queue_info("host2sp_psys_event", |
| &css_queues.host2sp_psys_event_queue_handle); |
| bufq_dump_queue_info("sp2host_psys_event", |
| &css_queues.sp2host_psys_event_queue_handle); |
| |
| bufq_dump_queue_info("host2sp_isys_event", |
| &css_queues.host2sp_isys_event_queue_handle); |
| bufq_dump_queue_info("sp2host_isys_event", |
| &css_queues.sp2host_isys_event_queue_handle); |
| bufq_dump_queue_info("host2sp_tag_cmd", |
| &css_queues.host2sp_tag_cmd_queue_handle); |
| } |