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

#include "hmm.h"

#include "ia_css_types.h"
#define __INLINE_SP__
#include "sp.h"

#include "assert_support.h"
#include "ia_css_spctrl.h"
#include "ia_css_debug.h"

struct spctrl_context_info {
        struct ia_css_sp_init_dmem_cfg dmem_config;
        u32        spctrl_config_dmem_addr; /* location of dmem_cfg  in SP dmem */
        u32        spctrl_state_dmem_addr;
        unsigned int    sp_entry;           /* entry function ptr on SP */
        ia_css_ptr    code_addr;          /* sp firmware location in host mem-DDR*/
        u32        code_size;
        char           *program_name;       /* used in case of PLATFORM_SIM */
};

static struct spctrl_context_info spctrl_cofig_info[N_SP_ID];
static bool spctrl_loaded[N_SP_ID] = {0};

/* Load firmware */
int ia_css_spctrl_load_fw(sp_ID_t sp_id, ia_css_spctrl_cfg *spctrl_cfg)
{
        ia_css_ptr code_addr = mmgr_NULL;
        struct ia_css_sp_init_dmem_cfg *init_dmem_cfg;

        if ((sp_id >= N_SP_ID) || (!spctrl_cfg))
                return -EINVAL;

        spctrl_cofig_info[sp_id].code_addr = mmgr_NULL;

        init_dmem_cfg = &spctrl_cofig_info[sp_id].dmem_config;
        init_dmem_cfg->dmem_data_addr = spctrl_cfg->dmem_data_addr;
        init_dmem_cfg->dmem_bss_addr  = spctrl_cfg->dmem_bss_addr;
        init_dmem_cfg->data_size      = spctrl_cfg->data_size;
        init_dmem_cfg->bss_size       = spctrl_cfg->bss_size;
        init_dmem_cfg->sp_id          = sp_id;

        spctrl_cofig_info[sp_id].spctrl_config_dmem_addr =
            spctrl_cfg->spctrl_config_dmem_addr;
        spctrl_cofig_info[sp_id].spctrl_state_dmem_addr =
            spctrl_cfg->spctrl_state_dmem_addr;

        /* store code (text + icache) and data to DDR
         *
         * Data used to be stored separately, because of access alignment constraints,
         * fix the FW generation instead
         */
        code_addr = hmm_alloc(spctrl_cfg->code_size);
        if (code_addr == mmgr_NULL)
                return -ENOMEM;
        hmm_store(code_addr, spctrl_cfg->code, spctrl_cfg->code_size);

        if (sizeof(ia_css_ptr) > sizeof(hrt_data)) {
                ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
                                    "size of ia_css_ptr can not be greater than hrt_data\n");
                hmm_free(code_addr);
                code_addr = mmgr_NULL;
                return -EINVAL;
        }

        init_dmem_cfg->ddr_data_addr  = code_addr + spctrl_cfg->ddr_data_offset;
        if ((init_dmem_cfg->ddr_data_addr % HIVE_ISP_DDR_WORD_BYTES) != 0) {
                ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
                                    "DDR address pointer is not properly aligned for DMA transfer\n");
                hmm_free(code_addr);
                code_addr = mmgr_NULL;
                return -EINVAL;
        }

        spctrl_cofig_info[sp_id].sp_entry = spctrl_cfg->sp_entry;
        spctrl_cofig_info[sp_id].code_addr = code_addr;
        spctrl_cofig_info[sp_id].program_name = spctrl_cfg->program_name;

        /* now we program the base address into the icache and
         * invalidate the cache.
         */
        sp_ctrl_store(sp_id, SP_ICACHE_ADDR_REG,
                      (hrt_data)spctrl_cofig_info[sp_id].code_addr);
        sp_ctrl_setbit(sp_id, SP_ICACHE_INV_REG, SP_ICACHE_INV_BIT);
        spctrl_loaded[sp_id] = true;
        return 0;
}

/* ISP2401 */
/* reload pre-loaded FW */
void sh_css_spctrl_reload_fw(sp_ID_t sp_id)
{
        /* now we program the base address into the icache and
         * invalidate the cache.
         */
        sp_ctrl_store(sp_id, SP_ICACHE_ADDR_REG,
                      (hrt_data)spctrl_cofig_info[sp_id].code_addr);
        sp_ctrl_setbit(sp_id, SP_ICACHE_INV_REG, SP_ICACHE_INV_BIT);
        spctrl_loaded[sp_id] = true;
}

ia_css_ptr get_sp_code_addr(sp_ID_t  sp_id)
{
        return spctrl_cofig_info[sp_id].code_addr;
}

int ia_css_spctrl_unload_fw(sp_ID_t sp_id)
{
        if ((sp_id >= N_SP_ID) || ((sp_id < N_SP_ID) && (!spctrl_loaded[sp_id])))
                return -EINVAL;

        /*  freeup the resource */
        if (spctrl_cofig_info[sp_id].code_addr) {
                hmm_free(spctrl_cofig_info[sp_id].code_addr);
                spctrl_cofig_info[sp_id].code_addr = mmgr_NULL;
        }
        spctrl_loaded[sp_id] = false;
        return 0;
}

/* Initialize dmem_cfg in SP dmem  and  start SP program*/
int ia_css_spctrl_start(sp_ID_t sp_id)
{
        if ((sp_id >= N_SP_ID) || ((sp_id < N_SP_ID) && (!spctrl_loaded[sp_id])))
                return -EINVAL;

        /* Set descr in the SP to initialize the SP DMEM */
        /*
         * The FW stores user-space pointers to the FW, the ISP pointer
         * is only available here
         *
         */
        assert(sizeof(unsigned int) <= sizeof(hrt_data));

        sp_dmem_store(sp_id,
                      spctrl_cofig_info[sp_id].spctrl_config_dmem_addr,
                      &spctrl_cofig_info[sp_id].dmem_config,
                      sizeof(spctrl_cofig_info[sp_id].dmem_config));
        /* set the start address */
        sp_ctrl_store(sp_id, SP_START_ADDR_REG,
                      (hrt_data)spctrl_cofig_info[sp_id].sp_entry);
        sp_ctrl_setbit(sp_id, SP_SC_REG, SP_RUN_BIT);
        sp_ctrl_setbit(sp_id, SP_SC_REG, SP_START_BIT);
        return 0;
}

/* Query the state of SP1 */
ia_css_spctrl_sp_sw_state ia_css_spctrl_get_state(sp_ID_t sp_id)
{
        ia_css_spctrl_sp_sw_state state = 0;
        unsigned int HIVE_ADDR_sp_sw_state;

        if (sp_id >= N_SP_ID)
                return IA_CSS_SP_SW_TERMINATED;

        HIVE_ADDR_sp_sw_state = spctrl_cofig_info[sp_id].spctrl_state_dmem_addr;
        (void)HIVE_ADDR_sp_sw_state; /* Suppres warnings in CRUN */
        if (sp_id == SP0_ID)
                state = sp_dmem_load_uint32(sp_id, (unsigned int)sp_address_of(sp_sw_state));
        return state;
}

int ia_css_spctrl_is_idle(sp_ID_t sp_id)
{
        int state = 0;

        assert(sp_id < N_SP_ID);

        state = sp_ctrl_getbit(sp_id, SP_SC_REG, SP_IDLE_BIT);
        return state;
}