root/drivers/staging/media/atomisp/pci/runtime/bufq/src/bufq.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Intel Camera Imaging ISP subsystem.
 * Copyright (c) 2015, Intel Corporation.
 */

#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[%d][%d]", 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[%d]", 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);
}