/*
 * Copyright (C) 2018-2023 Intel Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG IntelPGParam

#include "modules/algowrapper/IntelPGParam.h"

#include <algorithm>

#include "iutils/CameraLog.h"
#include "iutils/Utils.h"

namespace icamera {

IntelPGParam::IntelPGParam(int pgId, int cameraId, TuningMode tuningMode)
        : mPgId(pgId),
          mTerminalCount(0),
          mFragmentCount(0),
          mFragmentConfig(nullptr),
          mP2pHandle(nullptr),
          mPgManifest(nullptr),
          mProcessGroup(nullptr),
          mProgramControlInitTerminalIndex(-1),
          mProcessGroupMemory(nullptr) {
    UNUSED(cameraId);
    UNUSED(tuningMode);
    CLEAR(mP2pCacheBuffer);
}

IntelPGParam::~IntelPGParam() {
    if (mFragmentConfig) {
        delete mFragmentConfig;
    }

    for (int i = 0; i < mTerminalCount; i++) {
        if (mPgReqs.terminals[i].kernelOrder) {
            delete[] mPgReqs.terminals[i].kernelOrder;
        }
    }

    destroyPayloads();
    destroyPGBuffer();
}

int IntelPGParam::init(ia_p2p_platform_t platform, const PgConfiguration& pgConfig) {
    mP2pHandle = ia_p2p_init(platform);
    CheckAndLogError(!mP2pHandle, UNKNOWN_ERROR, "ia_p2p_init has failed");

    mP2pCacheBuffer.size = ia_p2p_get_cache_buffer_size(mP2pHandle);
    mP2pCacheBuffer.data = CIPR::callocMemory(1, mP2pCacheBuffer.size);
    LOG1("%s: mP2pCacheBuffer.size=%d", __func__, mP2pCacheBuffer.size);
    CheckAndLogError(!mP2pCacheBuffer.data, UNKNOWN_ERROR, "Failed to allocate P2P cache buffer.");

    mPgManifest = pgConfig.pgManifest;
    mDisableDataTermials = pgConfig.disableDataTermials;
    mTerminalCount = ia_css_program_group_manifest_get_terminal_count(mPgManifest);

    mFragmentCount = pgConfig.fragmentCount;
    mInputMainFrame = pgConfig.inputMainFrame;
    mOutputMainFrame = pgConfig.outputMainFrame;

    return OK;
}

int IntelPGParam::calcFragmentDescriptors(int fragmentCount, const PgFrameDesc& inputMainFrame,
                                          const PgFrameDesc& outputMainFrame,
                                          const ia_css_rbm_t* rbm) {
    if (mFragmentConfig) {
        delete mFragmentConfig;
        mFragmentConfig = nullptr;
    }

    mFragmentConfig = new ia_p2p_fragment_configuration_t;
    CLEAR(*mFragmentConfig);
    int ret = ia_p2p_calculate_fragments_rbm(mP2pHandle, mPgId, (unsigned int)fragmentCount, rbm,
                                             nullptr,  // only for IA_P2P_PLATFORM_IPU6 now
                                             mFragmentConfig);

    dumpFragmentDesc(fragmentCount);
    return ret;
}

static int kernel_id_ffs(ia_css_kernel_bitmap_t bitmap) {
    int n = 0;
    if (ia_css_is_kernel_bitmap_empty(bitmap)) return -1;
    while (!ia_css_is_kernel_bitmap_set(bitmap, (unsigned int)n)) n++;
    return n;
}

int IntelPGParam::getFragmentDescriptors(int descCount, ia_p2p_fragment_desc* descs) {
    CheckAndLogError(descCount < mTerminalCount * mFragmentCount, BAD_VALUE,
                     "descCount %d is small", descCount);

    int descLen = sizeof(ia_p2p_fragment_desc) * mFragmentCount;
    int terminalCount = ia_css_process_group_get_terminal_count(mProcessGroup);
    for (int i = 0; i < terminalCount; i++) {
        ia_css_terminal_t* terminal = ia_css_process_group_get_terminal(mProcessGroup, i);
        CheckAndLogError(!terminal, BAD_VALUE, "terminal is nullptr");

        int termIdx = terminal->tm_index;
        if (!IS_DATA_TERMINAL(mPgReqs.terminals[termIdx].type)) continue;

        if (mFragmentConfig) {
            int kernelId = kernel_id_ffs(mPgReqs.terminals[termIdx].kernelBitmap);
            CheckAndLogError((kernelId < 0 || kernelId >= IA_CSS_KERNEL_BITMAP_BITS), -1,
                             "error terminal %d", termIdx);
            MEMCPY_S(&descs[termIdx * mFragmentCount], descLen,
                     mFragmentConfig->pixel_fragment_descs[kernelId], descLen);
            LOG2("PG %d: Terminal %d: selected fragment desc (<%d,%d> %dx%d) with kernel id %d",
                 mPgId, termIdx, descs[termIdx].fragment_start_x, descs[termIdx].fragment_start_y,
                 descs[termIdx].fragment_width, descs[termIdx].fragment_height, kernelId);
        } else {
            /* PG uses legacy fragment calculation logic */
            MEMCPY_S(&descs[termIdx * mFragmentCount], descLen,
                     mPgReqs.terminals[termIdx].fragment_descs, descLen);
            LOG2("PG %d: Terminal %d: selected legacy fragment descriptor (<%d,%d> %dx%d)", mPgId,
                 termIdx, descs[termIdx].fragment_start_x, descs[termIdx].fragment_start_y,
                 descs[termIdx].fragment_width, descs[termIdx].fragment_height);
        }
    }
    return mFragmentCount;
}

int IntelPGParam::prepare(const ia_binary_data* ipuParameters, const ia_css_rbm_t* rbm,
                          ia_css_kernel_bitmap_t* bitmap, uint32_t* maxStatsSize) {
    CheckAndLogError(ipuParameters == nullptr || bitmap == nullptr, BAD_VALUE,
                     "The input paramter is nullptr.");

    ia_css_terminal_type_t terminalType;
    int8_t termIndex;
    int kernelId = 0;

    ia_err err = ia_p2p_parse(mP2pHandle, ipuParameters, mP2pCacheBuffer.data);
    CheckAndLogError(err != ia_err_none, UNKNOWN_ERROR, "Failed to parse PAL data.");

    int ret = calcFragmentDescriptors(mFragmentCount, mInputMainFrame, mOutputMainFrame, rbm);
    CheckAndLogError(ret != OK, ret, "Failed to calc fragment desc.");

    for (termIndex = 0; termIndex < mTerminalCount; termIndex++) {
        ia_css_terminal_manifest_t* terminalManifest =
            ia_css_program_group_manifest_get_term_mnfst(mPgManifest, (unsigned int)termIndex);
        CheckAndLogError(!terminalManifest, css_err_internal,
                         "No terminal manifest for terminal %d", termIndex);

        terminalType = ia_css_terminal_manifest_get_type(terminalManifest);
        mPgReqs.terminals[termIndex].type = terminalType;
        mPgReqs.terminals[termIndex].kernelOrder = nullptr;
        size_t kernelInfoSize = PSYS_MAX_KERNELS_PER_PG * sizeof(IpuPgTerminalKernelInfo);

        switch (terminalType) {
            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN:
            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: {
                ia_css_param_terminal_manifest_t* paramMani =
                    reinterpret_cast<ia_css_param_terminal_manifest_t*>(terminalManifest);
                mPgReqs.terminals[termIndex].kernelOrder =
                    new IpuPgTerminalKernelInfo[PSYS_MAX_KERNELS_PER_PG];
                memset(reinterpret_cast<void*>(mPgReqs.terminals[termIndex].kernelOrder), UINT8_MAX,
                       kernelInfoSize);
                ret = getKernelOrderForParamCachedInTerm(paramMani,
                                                         mPgReqs.terminals[termIndex].kernelOrder);
                CheckAndLogError(ret != css_err_none, ret,
                                 "getKernelOrderForParamCachedInTerm failed");
                break;
            }
            case IA_CSS_TERMINAL_TYPE_PROGRAM: {
                ia_css_program_terminal_manifest_t* proMani =
                    reinterpret_cast<ia_css_program_terminal_manifest_t*>(terminalManifest);
                mPgReqs.terminals[termIndex].kernelOrder =
                    new IpuPgTerminalKernelInfo[PSYS_MAX_KERNELS_PER_PG];
                memset(reinterpret_cast<void*>(mPgReqs.terminals[termIndex].kernelOrder), UINT8_MAX,
                       kernelInfoSize);

                ret =
                    getKernelOrderForProgramTerm(proMani, mPgReqs.terminals[termIndex].kernelOrder);
                CheckAndLogError(ret != css_err_none, ret, "getKernelOrderForProgramTerm failed");
                break;
            }
            case IA_CSS_TERMINAL_TYPE_DATA_IN: {
                ia_css_data_terminal_manifest_t* dataMani =
                    reinterpret_cast<ia_css_data_terminal_manifest_t*>(terminalManifest);
                /**
                 *Save the kernel bitmaps so that it can later be determined
                 * whether the terminals are disabled or not.
                 */
                mPgReqs.terminals[termIndex].kernelBitmap =
                    ia_css_data_terminal_manifest_get_kernel_bitmap(dataMani);
                break;
            }
            case IA_CSS_TERMINAL_TYPE_DATA_OUT: {
                ia_css_data_terminal_manifest_t* dataMani =
                    reinterpret_cast<ia_css_data_terminal_manifest_t*>(terminalManifest);
                /**
                 * Save the kernel bitmaps so that it can later be determined
                 * whether the terminals are disabled or not.
                 */
                mPgReqs.terminals[termIndex].kernelBitmap =
                    ia_css_data_terminal_manifest_get_kernel_bitmap(dataMani);
                break;
            }
            case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN:
            case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: {
                ia_css_spatial_param_terminal_manifest_t* paramMani =
                    reinterpret_cast<ia_css_spatial_param_terminal_manifest_t*>(terminalManifest);
                kernelId = (int32_t)(paramMani->kernel_id);
                mPgReqs.terminals[termIndex].kernelBitmap =
                    ia_css_kernel_bit_mask((uint32_t)kernelId);
                break;
            }
            case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT:
                /* Calculate the payload later when the final bitmask is known,
                 * in imaging_pipe_ctrl_identify_property, where it is actually
                 * needed. Just save here the index to the terminal. */
                mProgramControlInitTerminalIndex = termIndex;
                break;
            default:
                break;
        }
    }

    mPgReqs.terminalCount = mTerminalCount;

    ia_css_kernel_bitmap_t kernelBitmap = ia_p2p_get_kernel_bitmap(mP2pHandle, mPgId);
    kernelBitmap = ia_css_kernel_bitmap_intersection(
        kernelBitmap, ia_css_program_group_manifest_get_kernel_bitmap(mPgManifest));

    while (!ia_css_is_kernel_bitmap_empty(kernelBitmap)) {
        kernelId = getKernelIdByBitmap(kernelBitmap);
        CheckAndLogError((kernelId < 0 || kernelId >= PSYS_MAX_KERNELS_PER_PG), ia_err_internal,
                         "kernelId is out of range! %d", kernelId);

        /* Get terminal requirements */
        ret = ia_p2p_get_kernel_terminal_requirements(mP2pHandle, mPgId, (uint32_t)kernelId,
                                                      &mKernel.mSections[kernelId]);
        CheckAndLogError(ret != ia_err_none, ret,
                         "%s: failed to get requirements for pg %d kernel %d", __func__, mPgId,
                         kernelId);

        /* Get payload descriptor */
        ret = ia_p2p_get_kernel_payload_desc(mP2pHandle, mPgId, (uint32_t)kernelId,
#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
                                             1,
#else
                                             mFragmentCount,
#endif
                                             mFragmentConfig->pixel_fragment_descs[kernelId],
                                             &mKernel.mPayloads[kernelId]);
        CheckAndLogError(ret != ia_err_none, ret,
                         "%s: failed to get payload for pg %d kernel %d, ret %d", __func__, mPgId,
                         kernelId, ret);

        uint8_t kernelOrder = 0;
        termIndex = -1;
        if (mKernel.mSections[kernelId].param_in_section_count) {
            terminalType = IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN;

            /* P2P assumes single CACHED IN, cumulate to first */
            termIndex = terminalEnumerateByType(&mPgReqs, terminalType, 0);
            CheckAndLogError(termIndex < 0, ia_err_internal,
                             "No PARAM_CACHED_IN according to manifest!");
            if (isKernelIdInKernelOrder(&mPgReqs, termIndex, kernelId, &kernelOrder)) {
                if (mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].sections !=
                    mKernel.mSections[kernelId].param_in_section_count) {
                    LOGW("%s: p2p cached in section count differs (kernel_id:%i p2p:%d vs pg:%d)",
                         __func__, kernelId, mKernel.mSections[kernelId].param_in_section_count,
                         mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].sections);
                    /* Overwrite P2P requirements with manifest */
                    mKernel.mSections[kernelId].param_in_section_count =
                        mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].sections;
                    mKernel.mPayloads[kernelId].param_in_payload_size =
                        std::max(mKernel.mPayloads[kernelId].param_in_payload_size,
                                 mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].size);
                    mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].initialize = true;
                }
                processTerminalKernelRequirements(&mPgReqs, termIndex, terminalType, kernelId);
            }
        }

        if (mKernel.mSections[kernelId].param_out_section_count_per_fragment) {
            terminalType = IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT;
            for (termIndex = 0; termIndex < mTerminalCount; termIndex++) {
                if (mPgReqs.terminals[termIndex].type != terminalType) {
                    continue;
                }

                if (isKernelIdInKernelOrder(&mPgReqs, termIndex, kernelId, nullptr)) {
                    processTerminalKernelRequirements(&mPgReqs, termIndex, terminalType, kernelId);
                }
            }
        }

        if (mKernel.mSections[kernelId].program_section_count_per_fragment) {
            terminalType = IA_CSS_TERMINAL_TYPE_PROGRAM;
            for (termIndex = 0; termIndex < mTerminalCount; termIndex++) {
                if (mPgReqs.terminals[termIndex].type != terminalType) {
                    continue;
                }
                if (isKernelIdInKernelOrder(&mPgReqs, termIndex, kernelId, &kernelOrder)) {
                    if (mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].sections !=
                        mKernel.mSections[kernelId].program_section_count_per_fragment) {
                        LOGW("%s: p2p program section count differs (kernel_id:%i p2p:%d vs pg:%d)",
                             __func__, kernelId,
                             mKernel.mSections[kernelId].program_section_count_per_fragment,
                             mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].sections);
                        /* Overwrite P2P requirements with manifest */
                        mKernel.mSections[kernelId].program_section_count_per_fragment =
                            mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].sections;
                        mKernel.mPayloads[kernelId].program_payload_size =
                            std::max(mKernel.mPayloads[kernelId].program_payload_size,
                                     mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].size);
                        mPgReqs.terminals[termIndex].kernelOrder[kernelOrder].initialize = true;
                    }
                    processTerminalKernelRequirements(&mPgReqs, termIndex, terminalType, kernelId);
                }
            }
        }

        /* P2P assumes each spatial kernel parameter has its own terminal */
        if (mKernel.mSections[kernelId].spatial_param_in_section_count) {
            terminalType = IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN;
            termIndex = terminalEnumerateByBitmap(&mPgReqs, terminalType,
                                                  ia_css_kernel_bit_mask((uint32_t)kernelId));
            if (termIndex < 0) {
                LOG1("%s: No PARAM_SPATIAL_IN for kernel id %d according to manifest!", __func__,
                     kernelId);
            } else if (isKernelIdInKernelOrder(&mPgReqs, termIndex, kernelId, nullptr)) {
                mPgReqs.terminals[termIndex].sectionCount +=
                    mKernel.mSections[kernelId].spatial_param_in_section_count;
                mPgReqs.terminals[termIndex].payloadSize +=
                    mKernel.mPayloads[kernelId].spatial_param_in_payload_size;
                mPgReqs.terminals[termIndex].kernelBitmap =
                    ia_css_kernel_bit_mask((uint32_t)kernelId);
            }
        }

        if (mKernel.mSections[kernelId].spatial_param_out_section_count) {
            terminalType = IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT;
            termIndex = terminalEnumerateByBitmap(&mPgReqs, terminalType,
                                                  ia_css_kernel_bit_mask((uint32_t)kernelId));
            if (termIndex < 0) {
                LOG1("%s: No PARAM_SPATIAL_OUT for kernel id %d according to manifest!", __func__,
                     kernelId);
            } else if (isKernelIdInKernelOrder(&mPgReqs, termIndex, kernelId, nullptr)) {
                mPgReqs.terminals[termIndex].sectionCount +=
                    mKernel.mSections[kernelId].spatial_param_out_section_count;
                mPgReqs.terminals[termIndex].payloadSize +=
                    mKernel.mPayloads[kernelId].spatial_param_out_payload_size;
                mPgReqs.terminals[termIndex].kernelBitmap =
                    ia_css_kernel_bit_mask((uint32_t)kernelId);
            }
        }

        kernelBitmap = ia_css_kernel_bitmap_unset(kernelBitmap, (uint32_t)kernelId);
    }

    /* get all kernel bits back */
    kernelBitmap = ia_css_program_group_manifest_get_kernel_bitmap(mPgManifest);

    /* get disabled kernels from p2p and remove them */
    kernelBitmap = ia_css_kernel_bitmap_intersection(
        kernelBitmap,
        ia_css_kernel_bitmap_complement(ia_p2p_get_kernel_disable_bitmap(mP2pHandle, mPgId)));

    /* get disabled data terminal kernels and remove them */
    for (auto& item : mDisableDataTermials) {
        ia_css_terminal_manifest_t* terminalManifest =
            ia_css_program_group_manifest_get_term_mnfst(mPgManifest, (unsigned int)item);
        ia_css_kernel_bitmap_t dataTerminalKernelBitmap =
            ia_css_data_terminal_manifest_get_kernel_bitmap(
                reinterpret_cast<ia_css_data_terminal_manifest_t*>(terminalManifest));
        kernelBitmap = ia_css_kernel_bitmap_intersection(
            kernelBitmap, ia_css_kernel_bitmap_complement(dataTerminalKernelBitmap));
    }

    /* disable params terminals which payload size are zero */
    ret = disableZeroSizedTerminals(&kernelBitmap);
    CheckAndLogError(ret != OK, ret, "%s: failed to disable zero size terminals", __func__);

    *bitmap = kernelBitmap;

    if (maxStatsSize) *maxStatsSize = ia_p2p_get_statistics_buffer_size(mP2pHandle);
    return ret;
}

void* IntelPGParam::allocatePGBuffer(int pgSize) {
    destroyPGBuffer();
    void* memory = CIPR::mallocAlignedMemory(PAGE_ALIGN(pgSize), CIPR::getPageSize());
    mProcessGroupMemory = reinterpret_cast<ia_css_process_group_t*>(memory);
    return mProcessGroupMemory;
}

void IntelPGParam::destroyPGBuffer() {
    if (mProcessGroupMemory) {
        CIPR::freeMemory(mProcessGroupMemory);
        mProcessGroupMemory = nullptr;
    }
}

int IntelPGParam::setPGAndPrepareProgram(ia_css_process_group_t* pg) {
    CheckAndLogError(!pg, UNKNOWN_ERROR, "input pg nullptr!");
    mProcessGroup = pg;

    int ret = OK;
    int terminalCount = ia_css_process_group_get_terminal_count(mProcessGroup);
    for (int i = 0; i < terminalCount; i++) {
        ia_css_terminal_t* terminal = ia_css_process_group_get_terminal(mProcessGroup, i);
        CheckAndLogError(!terminal, UNKNOWN_ERROR, "failed to get terminal");
        int termIdx = terminal->tm_index;

        if (mPgReqs.terminals[termIdx].type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT) {
            unsigned int payloadSize = 0;
            ret = pg_control_init_get_payload_size(pg, &payloadSize);
            CheckAndLogError(ret != OK, UNKNOWN_ERROR,
                             "call pg_control_init_get_payload_size fail");
            mPgReqs.terminals[termIdx].payloadSize = payloadSize;

            ret = pg_control_init_terminal_init(
                mProcessGroup, reinterpret_cast<ia_css_program_control_init_terminal_t*>(terminal));
            CheckAndLogError(ret != ia_err_none, ret,
                             "Failed to call pg_control_init_terminal_init.");
        }

        if (mPgReqs.terminals[termIdx].type == IA_CSS_TERMINAL_TYPE_PROGRAM) {
            ret = ia_p2p_program_terminal_init_v2(
                mP2pHandle, mPgId, mFragmentConfig,
                reinterpret_cast<ia_css_program_terminal_t*>(terminal));
            CheckAndLogError(ret != ia_err_none, ret, "Failed to init program terminal.");
        }
    }
    return OK;
}

int IntelPGParam::getPayloadSizes(int payloadCount, ia_binary_data* payloads) {
    CheckAndLogError(payloadCount < mTerminalCount || !payloads, BAD_VALUE,
                     "Can't get payload sizes!");
    for (int termIdx = 0; termIdx < mTerminalCount; termIdx++) {
        payloads[termIdx].size = mPgReqs.terminals[termIdx].payloadSize;
    }
    return mTerminalCount;
}

int IntelPGParam::allocatePayloads(int payloadCount, ia_binary_data* payloads) {
    CheckAndLogError(!payloads, BAD_VALUE, "nullptr payloads!");
    for (int idx = 0; idx < payloadCount; idx++) {
        ia_binary_data payload = {nullptr, payloads[idx].size};
        if (payload.size) {
            payload.data = CIPR::mallocAlignedMemory(PAGE_ALIGN(payload.size), CIPR::getPageSize());
            CheckAndLogError(!payload.data, BAD_VALUE, "no memory for payload size %d!",
                             payload.size);
            mAllocatedPayloads.push_back(payload);
        }
        payloads[idx].data = payload.data;
    }
    return OK;
}

void IntelPGParam::destroyPayloads() {
    while (!mAllocatedPayloads.empty()) {
        if (mAllocatedPayloads.back().data) CIPR::freeMemory(mAllocatedPayloads.back().data);
        mAllocatedPayloads.pop_back();
    }
}

int IntelPGParam::updatePALAndEncode(const ia_binary_data* ipuParams, int payloadCount,
                                     ia_binary_data* payloads) {
    ia_err err = ia_p2p_parse(mP2pHandle, ipuParams, mP2pCacheBuffer.data);
    CheckAndLogError(err != ia_err_none, UNKNOWN_ERROR, "Failed to parse PAL data.");

    CheckAndLogError(!payloads, UNKNOWN_ERROR, "no payloads for encode.");
    CheckAndLogError(payloadCount < mTerminalCount, UNKNOWN_ERROR,
                     "small payload count %d, should be %d", payloadCount, mTerminalCount);
    CheckAndLogError(!mProcessGroup, INVALID_OPERATION, "Can't encode due to null pg.");

    int ret = OK;
    int terminalCount = ia_css_process_group_get_terminal_count(mProcessGroup);
    ia_css_terminal_t* programControlInitTerminal = nullptr;
    for (int i = 0; i < terminalCount; i++) {
        ia_css_terminal_t* terminal = ia_css_process_group_get_terminal(mProcessGroup, i);
        CheckAndLogError(!terminal, UNKNOWN_ERROR, "failed to get terminal");
        if (!payloads[terminal->tm_index].data) {
            continue;
        }

        // Encode that terminal at last
        if (terminal->tm_index == mProgramControlInitTerminalIndex) {
            programControlInitTerminal = terminal;
            continue;
        }

        ret = encodeTerminal(terminal, payloads[terminal->tm_index]);
        CheckAndLogError(ret != OK, ret, "Failed to encode for terminal %d.", terminal->tm_index);
    }
    if (programControlInitTerminal) {
        ret = encodeTerminal(programControlInitTerminal,
                             payloads[programControlInitTerminal->tm_index]);
        CheckAndLogError(ret != OK, ret, "Failed to encode for program control init terminal %d.",
                         programControlInitTerminal->tm_index);
    }

    return ret;
}

int IntelPGParam::encodeTerminal(ia_css_terminal_t* terminal, ia_binary_data payload) {
    int ret = OK;

    int terminalIndex = terminal->tm_index;
    if (mPgReqs.terminals[terminalIndex].type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT) {
        unsigned int kupSize = 0;
        ret = ia_p2p_get_kernel_user_parameter_size(mP2pHandle, mPgId, mFragmentCount, &kupSize);
        CheckAndLogError(ret != ia_err_none, ret,
                         "Failed to call ia_p2p_get_kernel_user_parameter_size.");
        if (kupSize != mPgReqs.terminals[terminalIndex].userParamSize) {
            mPgReqs.terminals[terminalIndex].userParamSize = kupSize;
            mPgReqs.terminals[terminalIndex].userParamAddress =
                std::unique_ptr<uint8_t[]>(new uint8_t[kupSize]);
        }

        ret = ia_p2p_get_kernel_user_parameters_v2(
            mP2pHandle, mPgId, mFragmentCount, mFragmentConfig,
            mPgReqs.terminals[terminalIndex].userParamAddress.get());
        CheckAndLogError(ret != ia_err_none, ret,
                         "Failed to call ia_p2p_get_kernel_user_parameters_v2.");

        ia_css_kernel_user_param_t* userParam = reinterpret_cast<ia_css_kernel_user_param_t*>(
            mPgReqs.terminals[terminalIndex].userParamAddress.get());
        ret = pg_control_init_fill_payload(mProcessGroup, userParam, payload.data);
        CheckAndLogError(ret != ia_err_none, ret, "Failed to call pg_control_init_fill_payload.");
        return ret;
    }

    ia_css_kernel_bitmap_t kernelBitmap = mPgReqs.terminals[terminalIndex].kernelBitmap;
    uint16_t kernelId = 0;
    uint8_t kernelIndex = 0;
    unsigned int curSection = 0;
    unsigned int curOffset = 0;
    ia_p2p_payload_desc tmpPayloadDesc;
    while (!ia_css_is_kernel_bitmap_empty(kernelBitmap)) {
        /* Use specific ordering of kernels when available */
        if (mPgReqs.terminals[terminalIndex].kernelOrder) {
            kernelId = mPgReqs.terminals[terminalIndex].kernelOrder[kernelIndex++].id;
            if (kernelId >= PSYS_MAX_KERNELS_PER_PG) {
                /* All the kernels have now been encoded. */
                break;
            }
            /* Initialize parameter payload for current kernel with zeros in
             * case P2P has reported less sections for the kernel */
            if (mPgReqs.terminals[terminalIndex].kernelOrder[kernelIndex - 1].initialize) {
                LOG2("%s: initializing kernel %d payload in terminal %d (offset:%d, size:%d)",
                     __func__, kernelId, terminalIndex, curOffset,
                     mPgReqs.terminals[terminalIndex].kernelOrder[kernelIndex - 1].size);
                unsigned char* start = reinterpret_cast<unsigned char*>(payload.data);
                memset(start + curOffset, 0,
                       mPgReqs.terminals[terminalIndex].kernelOrder[kernelIndex - 1].size);
            }
        } else {
            kernelId = getKernelIdByBitmap(kernelBitmap);
        }

        /* Sanity check sections sizes and return the size to be used */
        css_err_t result = payloadSectionSizeSanityTest(&tmpPayloadDesc, kernelId, terminalIndex,
                                                        curOffset, payload.size);
        CheckAndLogError((result != css_err_none), UNKNOWN_ERROR,
                         "Failed sanity check of terminal payload sizes");

        switch (mPgReqs.terminals[terminalIndex].type) {
            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN:
                ret = ia_p2p_param_in_terminal_encode(
                    mP2pHandle, mPgId, kernelId,
                    reinterpret_cast<ia_css_param_terminal_t*>(terminal), curSection,
                    reinterpret_cast<uint8_t*>(payload.data), payload.size, curOffset);
                CheckAndLogError(ret != ia_err_none, ret, "Failed to encode param in terminal.");

                curSection += mKernel.mSections[kernelId].param_in_section_count;
                curOffset += tmpPayloadDesc.param_in_payload_size;
                break;

            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT:
                ret = ia_p2p_param_out_terminal_prepare(
                    mP2pHandle, mPgId, kernelId, mFragmentCount,
                    mFragmentConfig->pixel_fragment_descs[kernelId],
                    reinterpret_cast<ia_css_param_terminal_t*>(terminal), curSection,
                    mPgReqs.terminals[terminalIndex].sectionCount, payload.size, curOffset);
                CheckAndLogError(ret != ia_err_none, ret, "Failed to prepare param out terminal.");

                curSection += mKernel.mSections[kernelId].param_out_section_count_per_fragment;
                curOffset += tmpPayloadDesc.param_out_payload_size;
                break;

            case IA_CSS_TERMINAL_TYPE_PROGRAM:
#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
                reinterpret_cast<ia_css_program_terminal_t*>(terminal)->payload_fragment_stride =
                    mPgReqs.terminals[terminalIndex].payloadSize / mFragmentCount;
#endif
                ret = ia_p2p_program_terminal_encode(
                    mP2pHandle, mPgId, kernelId, mFragmentCount,
                    mFragmentConfig->pixel_fragment_descs[kernelId],
                    reinterpret_cast<ia_css_program_terminal_t*>(terminal), curSection,
                    mPgReqs.terminals[terminalIndex].sectionCount,
                    reinterpret_cast<uint8_t*>(payload.data), payload.size, curOffset);
                CheckAndLogError(ret != ia_err_none, ret, "Failed to encode program terminal.");

                curSection += mKernel.mSections[kernelId].program_section_count_per_fragment;
                curOffset += tmpPayloadDesc.program_payload_size;
                break;

            case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN:
                ret = ia_p2p_spatial_param_in_terminal_encode(
                    mP2pHandle, mPgId, kernelId, mFragmentCount,
                    mFragmentConfig->pixel_fragment_descs[kernelId],
                    reinterpret_cast<ia_css_spatial_param_terminal_t*>(terminal), curSection,
                    reinterpret_cast<uint8_t*>(payload.data), payload.size, curOffset);
                CheckAndLogError(ret != ia_err_none, ret, "Failed to encode spatial in terminal.");

                curOffset += tmpPayloadDesc.spatial_param_in_payload_size;
                curSection += mKernel.mSections[kernelId].spatial_param_in_section_count;
                break;

            case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT:
                ret = ia_p2p_spatial_param_out_terminal_prepare(
                    mP2pHandle, mPgId, kernelId, mFragmentCount,
                    mFragmentConfig->pixel_fragment_descs[kernelId],
                    reinterpret_cast<ia_css_spatial_param_terminal_t*>(terminal), curSection,
                    payload.size, curOffset);
                CheckAndLogError(ret != ia_err_none, ret,
                                 "Failed to prepare spatial out terminal.");

                curOffset += tmpPayloadDesc.spatial_param_out_payload_size;
                curSection += mKernel.mSections[kernelId].spatial_param_out_section_count;
                break;

            case IA_CSS_TERMINAL_TYPE_DATA_IN:
            case IA_CSS_TERMINAL_TYPE_DATA_OUT:
                /* No encode done for frame terminals */
                break;

            default:
                LOGE("%s: terminal type %d encode not implemented", __func__,
                     mPgReqs.terminals[terminalIndex].type);
                return UNKNOWN_ERROR;
        }

        if (!mPgReqs.terminals[terminalIndex].kernelOrder) {
            kernelBitmap = ia_css_kernel_bitmap_unset(kernelBitmap, kernelId);
        }
    }

    return ret;
}

int IntelPGParam::decode(int payloadCount, ia_binary_data* payload, ia_binary_data* statistics,
                         int64_t sequence) {
    CheckAndLogError(!mProcessGroup, INVALID_OPERATION, "Can't decode due to null pg.");
    CheckAndLogError(!payload, INVALID_OPERATION, "nullptr payload.");
    UNUSED(sequence);

    if (statistics && statistics->data) {
        ia_p2p_set_statistics_buffer(mP2pHandle, statistics->data);
    }
    int ret = OK;
    int terminalCount = ia_css_process_group_get_terminal_count(mProcessGroup);
    for (int i = 0; i < terminalCount; i++) {
        ia_css_terminal_t* terminal = ia_css_process_group_get_terminal(mProcessGroup, i);
        CheckAndLogError(!terminal, UNKNOWN_ERROR, "failed to get terminal");
        if ((terminal->terminal_type != IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) &&
            (terminal->terminal_type != IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT)) {
            continue;
        }
        CheckAndLogError(terminal->tm_index >= payloadCount, UNKNOWN_ERROR,
                         "no payload for term %d decoding", terminal->tm_index);
        ret = decodeTerminal(terminal, payload[terminal->tm_index]);
        CheckAndLogError(ret != OK, ret, "%s, call p2p decode fail", __func__);
    }

    return serializeDecodeCache(statistics);
}

int IntelPGParam::decodeTerminal(ia_css_terminal_t* terminal, ia_binary_data payload) {
    int ret = OK;
    int terminalIndex = terminal->tm_index;
    unsigned int currentSection = 0;
    int kernelIndex = 0;
    uint16_t kernelId;
    ia_css_kernel_bitmap_t kernelBitmap = mPgReqs.terminals[terminal->tm_index].kernelBitmap;
    while (!ia_css_is_kernel_bitmap_empty(kernelBitmap)) {
        /* Use specific ordering of kernels when available */
        if (mPgReqs.terminals[terminalIndex].kernelOrder) {
            kernelId = mPgReqs.terminals[terminalIndex].kernelOrder[kernelIndex++].id;
            CheckAndLogError(
                kernelId >= PSYS_MAX_KERNELS_PER_PG, css_err_internal,
                "%s: Kernel bitmap for terminal %d covers more kernels than in manifest", __func__,
                terminalIndex);
        } else {
            kernelId = getKernelIdByBitmap(kernelBitmap);
        }

        switch (mPgReqs.terminals[terminalIndex].type) {
            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT:
                ret = ia_p2p_param_out_terminal_decode(
                    mP2pHandle, mPgId, kernelId, mFragmentCount,
                    reinterpret_cast<ia_css_param_terminal_t*>(terminal), currentSection,
                    mPgReqs.terminals[terminalIndex].sectionCount,
                    reinterpret_cast<unsigned char*>(payload.data), payload.size);
                currentSection += mKernel.mSections[kernelId].param_out_section_count_per_fragment;
                break;
            case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT:
                ret = ia_p2p_spatial_param_out_terminal_decode_v2(
                    mP2pHandle, mPgId, kernelId, mFragmentCount,
                    mFragmentConfig->pixel_fragment_descs[kernelId],
                    reinterpret_cast<ia_css_spatial_param_terminal_t*>(terminal), currentSection,
                    (unsigned char*)payload.data, payload.size, mP2pCacheBuffer.data);
                currentSection += mKernel.mSections[kernelId].spatial_param_out_section_count;
                break;
            default:
                LOGE("%s: terminal type %d decode not implemented", __func__,
                     mPgReqs.terminals[terminalIndex].type);
                return UNKNOWN_ERROR;
        }

        CheckAndLogError(ret != ia_err_none, ret, "%s: failed to decode terminal %d", __func__,
                         terminalIndex);
        kernelBitmap = ia_css_kernel_bitmap_unset(kernelBitmap, kernelId);
    }

    return ret;
}

int IntelPGParam::serializeDecodeCache(ia_binary_data* result) {
    CheckAndLogError(!result, UNKNOWN_ERROR, "The statistics buffer is nullptr");

    ia_err ia_ret = ia_p2p_serialize_statistics(mP2pHandle, result, nullptr);
    CheckAndLogError(ia_ret != ia_err_none, UNKNOWN_ERROR, "Serializ statistics fail");

    return OK;
}

void IntelPGParam::deinit() {
    ia_p2p_deinit(mP2pHandle);
    if (mP2pCacheBuffer.data) {
        CIPR::freeMemory(mP2pCacheBuffer.data);
    }
}

int IntelPGParam::getKernelIdByBitmap(ia_css_kernel_bitmap_t bitmap) {
    int kernelId = 0;
    CheckAndLogError(ia_css_is_kernel_bitmap_empty(bitmap), BAD_VALUE, "The bitmap is empty");
    while (!ia_css_is_kernel_bitmap_set(bitmap, (unsigned int)kernelId)) {
        kernelId++;
    }

    return kernelId;
}

ia_css_kernel_bitmap_t IntelPGParam::getCachedTerminalKernelBitmap(
    ia_css_param_terminal_manifest_t* manifest) {
    ia_css_kernel_bitmap_t kernelBitmap = ia_css_kernel_bitmap_clear();
    unsigned int section, sectionCount;

    /* Loop through all the sections in manifest and put the kernel ids into the kernel bitmap. */
    sectionCount = manifest->param_manifest_section_desc_count;
    for (section = 0; section < sectionCount; section++) {
        ia_css_param_manifest_section_desc_t* desc =
            ia_css_param_terminal_manifest_get_prm_sct_desc(manifest, section);
        CheckAndLogError(!desc, kernelBitmap, "failed to get desc");
        int index = ia_css_param_manifest_section_desc_get_kernel_id(desc);
        kernelBitmap = ia_css_kernel_bitmap_set(kernelBitmap, index);
    }

    return kernelBitmap;
}

ia_css_kernel_bitmap_t IntelPGParam::getProgramTerminalKernelBitmap(
    ia_css_program_terminal_manifest_t* manifest) {
    ia_css_kernel_bitmap_t kernelBitmap = ia_css_kernel_bitmap_clear();
    unsigned int section, sectionCount;

    /* Loop through all the sections in manifest and put the kernel ids into the kernel bitmap. */
    sectionCount = manifest->fragment_param_manifest_section_desc_count;
    for (section = 0; section < sectionCount; section++) {
        ia_css_fragment_param_manifest_section_desc_t* desc =
            ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc(manifest, section);
        CheckAndLogError(!desc, kernelBitmap, "failed to get desc");
        int index = ia_css_fragment_param_manifest_section_desc_get_kernel_id(desc);
        kernelBitmap = ia_css_kernel_bitmap_set(kernelBitmap, index);
    }

    return kernelBitmap;
}

int IntelPGParam::disableZeroSizedTerminals(ia_css_kernel_bitmap_t* kernelBitmap) {
    int ret = OK;
    ia_css_terminal_type_t terminalType;
    ia_css_kernel_bitmap_t terminalKernelsBitmap = ia_css_kernel_bitmap_clear();
    ia_css_kernel_bitmap_t disabledTerminalKernelsBitmap = ia_css_kernel_bitmap_clear();
    for (int i = 0; i < mTerminalCount; i++) {
        terminalKernelsBitmap = ia_css_kernel_bitmap_clear();
        unsigned int payloadSize = mPgReqs.terminals[i].payloadSize;
        ia_css_terminal_manifest_t* manifest =
            ia_css_program_group_manifest_get_term_mnfst(mPgManifest, i);
        terminalType = ia_css_terminal_manifest_get_type(manifest);

        if (payloadSize == 0) {
            switch (terminalType) {
                case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN:
                case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: {
                    /* Spatial terminals are only associated to a single kernel. */
                    ia_css_spatial_param_terminal_manifest_t* paramMani =
                        reinterpret_cast<ia_css_spatial_param_terminal_manifest_t*>(manifest);
                    terminalKernelsBitmap =
                        ia_css_kernel_bitmap_set(terminalKernelsBitmap, paramMani->kernel_id);
                    break;
                }
                case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN:
                case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: {
                    ia_css_param_terminal_manifest_t* paramMani =
                        reinterpret_cast<ia_css_param_terminal_manifest_t*>(manifest);
                    terminalKernelsBitmap = getCachedTerminalKernelBitmap(paramMani);
                    break;
                }
                case IA_CSS_TERMINAL_TYPE_PROGRAM: {
                    ia_css_program_terminal_manifest_t* proMani =
                        reinterpret_cast<ia_css_program_terminal_manifest_t*>(manifest);
                    terminalKernelsBitmap = getProgramTerminalKernelBitmap(proMani);
                    break;
                }
                case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT:
                    LOG1("%s: program control init terminal is always enabled.", __func__);
                    break;
                default:
                    break;
            }
            disabledTerminalKernelsBitmap =
                ia_css_kernel_bitmap_union(disabledTerminalKernelsBitmap, terminalKernelsBitmap);
        }
    }

    *kernelBitmap = ia_css_kernel_bitmap_intersection(
        *kernelBitmap, ia_css_kernel_bitmap_complement(disabledTerminalKernelsBitmap));
    return ret;
}

css_err_t IntelPGParam::getKernelOrderForParamCachedInTerm(
    ia_css_param_terminal_manifest_t* terminalManifest, IpuPgTerminalKernelInfo* kernelOrder) {
    CheckAndLogError((!terminalManifest || !kernelOrder), ia_err_argument,
                     "No manifest or order info");

    uint16_t sectionCount = terminalManifest->param_manifest_section_desc_count;
    CheckAndLogError(sectionCount == 0, css_err_argument, "No static sections in manifest");
    uint8_t kernelCount = 0;

    for (uint16_t section = 0; section < sectionCount; section++) {
        ia_css_param_manifest_section_desc_t* param =
            ia_css_param_terminal_manifest_get_prm_sct_desc(terminalManifest, section);
        CheckAndLogError(!param, css_err_internal, "Failed to get param from terminal manifest!");

        /* there is agreement that sections of the same kernel are
         * encoded in a row. Here, skipping sections of the same kernel
         * based on this assumption.
         */
        /* info: Indication of the kernel this parameter belongs to,
         * may stand for mem_type, region and kernel_id for ipu6
         */
        int index = ia_css_param_manifest_section_desc_get_kernel_id(param);
        if (kernelCount > 0 && kernelOrder[kernelCount - 1].id == index) {
            ++kernelOrder[kernelCount - 1].sections;
            kernelOrder[kernelCount - 1].size += param->max_mem_size;
            continue;
        }
        kernelOrder[kernelCount].id = (uint8_t)index;
        kernelOrder[kernelCount].sections = 1;
        kernelOrder[kernelCount].size = param->max_mem_size;
        kernelOrder[kernelCount].initialize = false;
        kernelCount++;
    }

    return css_err_none;
}

css_err_t IntelPGParam::getKernelOrderForProgramTerm(
    ia_css_program_terminal_manifest_t* terminalManifest, IpuPgTerminalKernelInfo* kernelOrder) {
    CheckAndLogError((!terminalManifest || !kernelOrder), css_err_argument,
                     "No manifest or order info");
    uint16_t sectionCount = terminalManifest->fragment_param_manifest_section_desc_count;
    CheckAndLogError(sectionCount == 0, ia_err_internal, "No static sections in manifest");
    uint8_t kernelCount = 0;

    for (uint16_t section = 0; section < sectionCount; section++) {
        ia_css_fragment_param_manifest_section_desc_t* param =
            ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc(terminalManifest, section);
        CheckAndLogError(!param, css_err_internal, "Failed to get param from terminal manifest!");

        /* there is agreement that sections of the same kernel are
         * encoded in a row. Here, skipping sections of the same kernel
         * based on this assumption.
         */
        /* info: Indication of the kernel this parameter belongs to,
         * may stand for mem_type, region and kernel_id for ipu6
         */
        int index = ia_css_fragment_param_manifest_section_desc_get_kernel_id(param);
        if (kernelCount > 0 && kernelOrder[kernelCount - 1].id == index) {
            ++kernelOrder[kernelCount - 1].sections;
            kernelOrder[kernelCount - 1].size += param->max_mem_size;
            continue;
        }
        kernelOrder[kernelCount].id = (uint8_t)index;
        kernelOrder[kernelCount].sections = 1;
        kernelOrder[kernelCount].size = param->max_mem_size;
        kernelOrder[kernelCount].initialize = false;
        kernelCount++;
    }

    return css_err_none;
}

int8_t IntelPGParam::terminalEnumerateByType(IpuPgRequirements* reqs,
                                             ia_css_terminal_type_t terminalType, uint8_t num) {
    CheckAndLogError(reqs->terminalCount == 0, -1, "%s: no terminals!", __func__);

    for (uint8_t terminal = 0; terminal < reqs->terminalCount; terminal++) {
        if (reqs->terminals[terminal].type == terminalType) {
            if (num)
                num--;
            else
                return (int8_t)terminal;
        }
    }

    return -1;
}

int8_t IntelPGParam::terminalEnumerateByBitmap(IpuPgRequirements* reqs,
                                               ia_css_terminal_type_t terminal_type,
                                               ia_css_kernel_bitmap_t bitmap) {
    CheckAndLogError(reqs->terminalCount == 0, -1, "%s: no terminals!", __func__);

    for (uint8_t terminal = 0; terminal < reqs->terminalCount; terminal++) {
        if (reqs->terminals[terminal].type == terminal_type &&
            ia_css_is_kernel_bitmap_equal(reqs->terminals[terminal].kernelBitmap, bitmap)) {
            return (int8_t)terminal;
        }
    }

    return -1;
}

bool IntelPGParam::isKernelIdInKernelOrder(IpuPgRequirements* reqs, int8_t termIndex, int kernelId,
                                           uint8_t* orderedIndex) {
    /* No kernel order, return true always */
    if (!reqs->terminals[termIndex].kernelOrder) return true;

    /* Check if the kernel_id can be found from the kernelOrder */
    for (uint8_t i = 0; i < PSYS_MAX_KERNELS_PER_PG; i++) {
        if (reqs->terminals[termIndex].kernelOrder[i].id == kernelId) {
            if (orderedIndex) *orderedIndex = i;
            return true;
        }
    }

    return false;
}

uint32_t IntelPGParam::getKernelCountFromKernelOrder(IpuPgRequirements* reqs, int8_t termIndex,
                                                     int kernelId) {
    if (!reqs->terminals[termIndex].kernelOrder) {
        /* If no kernel order is present, assuming kernel appears once. */
        return 1;
    }

    uint32_t count = 0;
    for (int i = 0; i < PSYS_MAX_KERNELS_PER_PG; i++) {
        if (reqs->terminals[termIndex].kernelOrder[i].id == kernelId) {
            ++count;
        }
    }

    return count;
}

void IntelPGParam::processTerminalKernelRequirements(IpuPgRequirements* reqs, int8_t termIndex,
                                                     ia_css_terminal_type_t terminalType,
                                                     int kernelId) {
    uint32_t kernelCount = getKernelCountFromKernelOrder(reqs, termIndex, kernelId);
    uint32_t sectionCount = 0, payloadSize = 0;
#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
    uint32_t multiplier = 1;
#endif
    for (unsigned int i = 0; i < kernelCount; ++i) {
        switch (terminalType) {
            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN:
                sectionCount = mKernel.mSections[kernelId].param_in_section_count;
                payloadSize = mKernel.mPayloads[kernelId].param_in_payload_size;
                break;
            case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT:
                sectionCount = mKernel.mSections[kernelId].param_out_section_count_per_fragment;
#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
                payloadSize = mKernel.mPayloads[kernelId].param_out_payload_size * mFragmentCount;
#else
                payloadSize = mKernel.mPayloads[kernelId].param_out_payload_size;
#endif
                break;
            case IA_CSS_TERMINAL_TYPE_PROGRAM:
                sectionCount = mKernel.mSections[kernelId].program_section_count_per_fragment;
                payloadSize = mKernel.mPayloads[kernelId].program_payload_size;
#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
                multiplier = mFragmentCount;
#endif
                break;
            default:
                LOG1("%s: terminal type %d encode not implemented", __func__, terminalType);
                break;
        }
        reqs->terminals[termIndex].sectionCount += sectionCount;

#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
        reqs->terminals[termIndex].payloadSize += multiplier * payloadSize;
#else
        reqs->terminals[termIndex].payloadSize += payloadSize;
#endif

        mKernel.mPayloadSize = reqs->terminals[termIndex].payloadSize;
    }

    reqs->terminals[termIndex].kernelBitmap =
        ia_css_kernel_bitmap_set(reqs->terminals[termIndex].kernelBitmap, (unsigned int)kernelId);
}

css_err_t IntelPGParam::payloadSectionSizeSanityTest(ia_p2p_payload_desc* current,
                                                     uint16_t kernelId, uint8_t terminalIndex,
                                                     uint32_t currentOffset, size_t payloadSize) {
    size_t nextPayloadSize = 0;
    ia_p2p_payload_desc init = mKernel.mPayloads[kernelId];
    /* calculate again the memory requirements for each kernel
     * and compare it with what we stored at init time. */
    ia_err ia_ret =
        ia_p2p_get_kernel_payload_desc(mP2pHandle, mPgId, kernelId,
#if defined(IPU_SYSVER_IPU6) && defined(UNIFIED_PROG_TERM_FRAG_DESC)
                                       1,
#else
                                       mFragmentCount,
#endif
                                       mFragmentConfig->pixel_fragment_descs[kernelId], current);
    CheckAndLogError(ia_ret != ia_err_none, css_err_internal,
                     "Failed to get payload description during sanity check (kernel %d)", kernelId);

    switch (mPgReqs.terminals[terminalIndex].type) {
        case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN:
            if (current->param_in_payload_size > init.param_in_payload_size) {
                LOGW("%s: param-in section size mismatch in pg[%d] kernel[%d]"
                     " p2p size %d pg_die size %d",
                     __func__, mPgId, kernelId, current->param_in_payload_size,
                     init.param_in_payload_size);
            } else {
                current->param_in_payload_size = init.param_in_payload_size;
            }
            nextPayloadSize = current->param_in_payload_size;
            break;
        case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT:
            if (current->param_out_payload_size > init.param_out_payload_size) {
                LOGW("%s: param-out section size mismatch in pg[%d] kernel[%d]"
                     " p2p size %d pg_die size %d",
                     __func__, mPgId, kernelId, current->param_out_payload_size,
                     init.param_out_payload_size);
            } else {
                current->param_out_payload_size = init.param_out_payload_size;
            }
            nextPayloadSize = current->param_out_payload_size;
            break;
        case IA_CSS_TERMINAL_TYPE_PROGRAM:
            if (current->program_payload_size > init.program_payload_size) {
                LOG1("%s: program section size mismatch in pg[%d] kernel[%d]"
                     " p2p size %d pg_die size %d",
                     __func__, mPgId, kernelId, current->program_payload_size,
                     init.program_payload_size);
            } else {
                current->program_payload_size = init.program_payload_size;
            }
            nextPayloadSize = current->program_payload_size;
            break;
        case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN:
            if (current->spatial_param_in_payload_size > init.spatial_param_in_payload_size) {
                LOGW("%s: spatial-in section size mismatch in pg[%d] kernel[%d]"
                     " p2p size %d pg_die size %d",
                     __func__, mPgId, kernelId, current->spatial_param_in_payload_size,
                     init.spatial_param_in_payload_size);
            } else {
                current->spatial_param_in_payload_size = init.spatial_param_in_payload_size;
            }
            nextPayloadSize = current->spatial_param_in_payload_size;
            break;
        case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT:
            if (current->spatial_param_out_payload_size > init.spatial_param_out_payload_size) {
                LOGW("%s: spatial-out section size mismatch in pg[%d] kernel[%d]"
                     " p2p size %d pg_die size %d",
                     __func__, mPgId, kernelId, current->spatial_param_out_payload_size,
                     init.spatial_param_out_payload_size);
            } else {
                current->spatial_param_out_payload_size = init.spatial_param_out_payload_size;
            }
            nextPayloadSize = current->spatial_param_out_payload_size;
            break;
        case IA_CSS_TERMINAL_TYPE_DATA_IN:
        case IA_CSS_TERMINAL_TYPE_DATA_OUT:
        case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT:
            /* No check done for frame terminals */
            break;
        default:
            LOGE("%s: terminal type %d payload check not implemented", __func__,
                 mPgReqs.terminals[terminalIndex].type);
            return css_err_argument;
    }

    CheckAndLogError(
        (currentOffset + nextPayloadSize > payloadSize), css_err_nomemory,
        "pg %d terminal %d payload size small, encoding for kernel %d exceed size by %lu bytes",
        mPgId, terminalIndex, kernelId, currentOffset + nextPayloadSize - payloadSize);
    return css_err_none;
}

void IntelPGParam::dumpFragmentDesc(int fragmentCount) {
    if (!Log::isLogTagEnabled(GET_FILE_SHIFT(IntelPGParam), CAMERA_DEBUG_LOG_LEVEL3)) return;

    LOG3("%s: pg %d get frag count %d (new api)", __func__, mPgId, fragmentCount);
    for (int kernel = 0; kernel < IA_P2P_MAX_KERNELS_PER_PG; kernel++) {
        for (int frag = 0; frag < fragmentCount; frag++) {
            LOG3("   kernel %d, frag %d: [%d %d %d %d]", kernel, frag,
                 mFragmentConfig->pixel_fragment_descs[kernel][frag].fragment_width,
                 mFragmentConfig->pixel_fragment_descs[kernel][frag].fragment_height,
                 mFragmentConfig->pixel_fragment_descs[kernel][frag].fragment_start_x,
                 mFragmentConfig->pixel_fragment_descs[kernel][frag].fragment_start_y);
        }
    }
}

}  // namespace icamera
