/*
 * Copyright (c) 2017-2020, NVIDIA CORPORATION.  All rights reserved. All
 * information contained herein is proprietary and confidential to NVIDIA
 * Corporation.  Any use, reproduction, or disclosure without the written
 * permission of NVIDIA Corporation is prohibited.
 */
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "ref_max96712_96705_ar0231_rccb.h"
#include "isc_max96705.h"
#include "isc_max96712.h"
#include "isc_ar0231_rccb.h"
#include "error_max96712.h"
#include "common_max96712.h"
#include "isc_n24c64.h"
#include "log_utils.h"
#include "dev_property.h"
#include "os_common.h"

#define OSC_MHZ 27
#define MAX_GAIN (3.0 * 8 * (3 + 511 / 512.0))   /* v4 max gain = 8 */
#define DEFAULT_ISC_DEV_IDX           0

static void
Deinit(
    ExtImgDevice *device)
{
    uint32_t i;
    NvMediaStatus status = NVMEDIA_STATUS_OK;

    if (!device)
        return;

    if (device->iscDeserializer) {
        /* Disable replication mode in slave mode */
        LOG_DBG("%s: Replicate output\n", __func__);
        status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                         ISC_CONFIG_MAX96712_DISABLE_REPLICATION);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to replicate output, err = %d(%s)\n", __func__, errno,
                    (const char*)strerror(errno));
        }
    }

    for (i = 0; i < device->sensorsNum; i++) {
        if (device->iscSerializer[i])
            NvMediaISCDeviceDestroy(device->iscSerializer[i]);
        if (device->iscSensor[i])
            NvMediaISCDeviceDestroy(device->iscSensor[i]);
        if(device->iscEeprom[i])
            NvMediaISCDeviceDestroy(device->iscEeprom[i]);
    }

    if (device->iscBroadcastSerializer)
        NvMediaISCDeviceDestroy(device->iscBroadcastSerializer);
    if (device->iscBroadcastSensor)
        NvMediaISCDeviceDestroy(device->iscBroadcastSensor);
    if (device->iscDeserializer)
        NvMediaISCDeviceDestroy(device->iscDeserializer);
    if (device->iscRoot) {
        NvMediaISCRootDeviceDestroy(device->iscRoot);
    }

    free(device);

    return;
}

static
NvMediaStatus
SetupConfigLink(
    ExtImgDevParam *configParam,
    ExtImgDevice *device)
{
    WriteParametersParamMAX96712 paramsMAX96712;
    WriteReadParametersParamMAX96705 paramsMAX96705;
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t i = 0;

    if ((device->iscDeserializer) && (device->iscBroadcastSerializer)) {
        /* Check the deserializer is present */
        LOG_DBG("%s: Check the deserializer is present\n", __func__);
        status = MAX96712CheckPresence(device->iscDeserializer);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: The deserializer is not present, err = %d(%s)\n", __func__,
                    errno, (const char*)strerror(errno));
            return status;
        }

        LOG_DBG("%s: Check deserializer limitations\n", __func__);
        status = ChkMAX96712Limitaions(device, configParam);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Check deserializer limitations failed\n", __func__);
            return status;
        }

        /* Select which I2C port to forward over the link */
        for (i = 0u; i < configParam->sensorsNum; i++) {
            paramsMAX96712.I2CPort.link = i;
            LOG_DBG("%s: Select I2C port over the link\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                         ISC_WRITE_PARAM_CMD_MAX96712_SET_I2C_PORT,
                         sizeof(paramsMAX96712.I2CPort),
                         &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to select I2C port over the link %d, err = %d(%s)\n", __func__,
                        i, errno, (const char*)strerror(errno));
                return status;
            }
        }

        /* Set GMSL mode and link speed */
        LOG_DBG("%s: Set GMSL mode and link speed\n", __func__);
        status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                         ISC_CONFIG_MAX96712_SET_GMSL1_MODE);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set GMSL mode and link speed, err = %d(%s)\n", __func__,
                    errno, (const char*)strerror(errno));
            return status;
        }
    }


    for (i = 0u; i < configParam->sensorsNum; i++) {
        if (device->iscDeserializer) {
            /* Enable specific link */
            paramsMAX96712.SpecificLink = i;
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_ENABLE_SPECIFIC_LINK,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable specific link %d, err = %d(%s)\n",
                        __func__, i, errno, (const char*)strerror(errno));
                return status;
            }
            nvsleep(5000);

            paramsMAX96712.stepHIM.link = i;
            paramsMAX96712.stepHIM.step = 0;

            LOG_DBG("%s: Enable GMSL1 HIM mode - step 0\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_GMSL1_HIM_ENABLED,
                                             sizeof(paramsMAX96712.stepHIM),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable GMSL1 HIM mode - step 1, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscBroadcastSerializer) {
            LOG_DBG("%s: Turn off HIM in MAX96705\n", __func__);
            status = MAX96705SetDeviceConfig(device->iscBroadcastSerializer,
                                             ISC_CONFIG_MAX96705_DISABLE_HIM_MODE);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to turn off HIM mode in MAX96705, err = %d(%s)\n", __func__,
                        errno, (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscDeserializer) {
            paramsMAX96712.stepHIM.link = i;
            paramsMAX96712.stepHIM.step = 1;

            LOG_DBG("%s: Enable GMSL1 HIM mode - step 1\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_GMSL1_HIM_ENABLED,
                                             sizeof(paramsMAX96712.stepHIM),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable GMSL1 HIM mode - step 2, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscBroadcastSerializer) {
            LOG_DBG("%s: Enable config link in MAX96705\n", __func__);
            status = MAX96705SetDeviceConfig(device->iscBroadcastSerializer,
                                             ISC_CONFIG_MAX96705_ENABLE_REVERSE_CHANNEL);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable config link in MAX96705, err = %d(%s)\n", __func__,
                        errno, (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscDeserializer) {
            paramsMAX96712.SpecificLink = i;
            LOG_DBG("%s: Disable forward channel\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_DISABLE_FORWARD_CHANNEL,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to disable forward channel, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscBroadcastSerializer) {
            LOG_DBG("%s: Turn on HIM\n", __func__);
            status = MAX96705SetDeviceConfig(device->iscBroadcastSerializer,
                                             ISC_CONFIG_MAX96705_ENABLE_HIM_MODE);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to turn on HIM mode, err = %d(%s)\n", __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscDeserializer) {
            paramsMAX96712.stepHIM.link = i;
            paramsMAX96712.stepHIM.step = 2;

            LOG_DBG("%s: Enable GMSL1 HIM mode - step 2\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_GMSL1_HIM_ENABLED,
                                             sizeof(paramsMAX96712.stepHIM),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable GMSL1 HIM mode - step 3, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }

            paramsMAX96712.SpecificLink = i;
            LOG_DBG("%s: Enable forward channel\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_ENABLE_FORWARD_CHANNEL,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable forward channel, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }

            paramsMAX96712.SpecificLink = i;
            LOG_DBG("%s: Enable packet based control channel\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_ENABLE_PACKET_BASED_CONTROL_CHANNEL,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable packet based control channel, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }

            paramsMAX96712.SpecificLink = i;
            LOG_DBG("%s: Enable DE\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_DISABLE_DE,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable DE, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscDeserializer) {
            status = MAX96712CheckLink(device->iscDeserializer,
                                         i, ISC_MAX96712_GMSL1_CONFIG_LINK_LOCK, NVMEDIA_TRUE);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s %d: Failed to check config link lock %d, err = %d(%s)\n", __func__, __LINE__, i,
                        errno, (const char*)strerror(errno));
                return status;
            }

            paramsMAX96712.SpecificLink = i;
            LOG_DBG("%s: Enable periodic AEQ\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_ENABLE_PERIODIC_AEQ,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable periodic AEQ, err = %d(%s)\n",
                                                           __func__,
                                                           errno,
                                                           (const char*)strerror(errno));
                return status;
            }
        }

        if (device->iscBroadcastSerializer) {
            /* Set PreEmphasis */
            paramsMAX96705.preemp = ISC_SET_PREEMP_MAX96705_PLU_3_3DB;

            LOG_DBG("%s: Set all serializer Preemphasis setting\n", __func__);
            status = MAX96705WriteParameters(device->iscBroadcastSerializer,
                                             ISC_WRITE_PARAM_CMD_MAX96705_SET_PREEMP,
                                             sizeof(paramsMAX96705.preemp),
                                             &paramsMAX96705);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to set Preemphasis setting\n", __func__);
                return status;
            }
        }

        if (device->iscBroadcastSensor) {
            /* Set address translation for the serializer to control individual serializer */
            if (configParam->serAddr[i] &&
                device->iscBroadcastSerializer &&
                device->iscSerializer[i]) {

                /* Check serializer is present */
                LOG_DBG("%s: Check serializer is present\n", __func__);
                status = MAX96705CheckPresence(device->iscBroadcastSerializer);
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_ERR("%s: Serializer(%d) device is not present, err = %d(%s)\n",
                            __func__, i, errno, (const char*)strerror(errno));
                    return status;
                }

                /* Set unique address with broadcast address */
                paramsMAX96705.DeviceAddress.address = configParam->serAddr[i];
                status = MAX96705WriteParameters(device->iscBroadcastSerializer,
                            ISC_WRITE_PARAM_CMD_MAX96705_SET_DEVICE_ADDRESS,
                            sizeof(paramsMAX96705.DeviceAddress),
                            &paramsMAX96705);
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_ERR("%s: Failed to set serializer device %d address, err = %d(%s)\n",
                            __func__, configParam->serAddr[i], errno,
                            (const char*)strerror(errno));
                    return status;
                }

                /* Set address translation for the serializer */
                paramsMAX96705.Translator.source = configParam->brdcstSerAddr;
                paramsMAX96705.Translator.destination = configParam->serAddr[i];
                status = MAX96705WriteParameters(device->iscSerializer[i],
                                ISC_WRITE_PARAM_CMD_MAX96705_SET_TRANSLATOR_A,
                                sizeof(paramsMAX96705.Translator),
                                &paramsMAX96705);
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_ERR("%s: Address translation setup failed, err = %d(%s)\n",
                            __func__, errno, (const char*)strerror(errno));
                    return status;
                }
            }

            if (device->iscSensor[i]) {
                /* Set address translation for the sensor to control individual sensor */
                if (configParam->sensorAddr[i] && device->iscBroadcastSerializer) {
                    ConfigInfoAR0231 configInfo;
                    WriteReadParametersParamAR0231 paramAR0231;
                    paramAR0231.configInfo = &configInfo;
                    /* Check sensor is present */
                    LOG_DBG("%s: Check sensor is present\n", __func__);
                    status = AR0231RCCBCheckPresence(device->iscBroadcastSensor);
                    if (status != NVMEDIA_STATUS_OK) {
                        LOG_ERR("%s: Image sensor(%d) device is not present, err = %d(%s)\n",
                                __func__, i, errno, (const char*)strerror(errno));
                        return status;
                    }

                    /* Set address translation for the sensor */
                    paramsMAX96705.Translator.source = configParam->sensorAddr[i];
                    paramsMAX96705.Translator.destination = configParam->brdcstSensorAddr;
                    LOG_INFO("%s: Translate image sensor device addr %x to %x\n", __func__,
                        paramsMAX96705.Translator.source, paramsMAX96705.Translator.destination);
                    status = MAX96705WriteParameters(device->iscSerializer[i],
                                    ISC_WRITE_PARAM_CMD_MAX96705_SET_TRANSLATOR_B,
                                    sizeof(paramsMAX96705.Translator),
                                    &paramsMAX96705);
                    if (status != NVMEDIA_STATUS_OK) {
                        LOG_ERR("%s: Address translation setup failed, err = %d(%s)\n",
                                __func__, errno, (const char*)strerror(errno));
                        return status;
                    }
                    status = AR0231RCCBReadParameters(device->iscSensor[i],
                                    ISC_READ_PARAM_CMD_AR0231_FUSE_ID,
                                    sizeof(paramAR0231.configInfo),
                                    &paramAR0231);
                    if (status != NVMEDIA_STATUS_OK) {
                        LOG_ERR("%s: Failed to read Fuse ID\n", __func__);
                        return status;
                    }
                }
            }
        }
    }

    return status;
}

static
NvMediaStatus
SetupVideoLink (
    ExtImgDevParam *configParam,
    ExtImgDevice *device)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint32_t i, pclk = 0;
    WriteParametersParamMAX96712 paramsMAX96712;
    int32_t config;
    uint8_t data[8];

    if (device->iscDeserializer) {
        /* Set MIPI output mode */
        LOG_DBG("%s: Set MIPI output mode\n", __func__);
        status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                           ISC_CONFIG_MAX96712_CONFIG_MIPI_OUT_MODE);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set MIPI output mode, err = %d(%s)\n", __func__, errno,
                    (const char*)strerror(errno));
            return status;
        }

        paramsMAX96712.dataType = ISC_MAX96712_DATA_TYPE_RAW12;
        status = MAX96712WriteParameters(device->iscDeserializer,
                                           ISC_WRITE_PARAM_CMD_MAX96712_SET_DATA_TYPE,
                                           sizeof(paramsMAX96712.dataType),
                                           &paramsMAX96712);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set data type, err = %d(%s)\n", __func__, errno,
                    (const char*)strerror(errno));
            return status;
        }

        /* Update mapping table */
        for (i = 0; i < configParam->sensorsNum; i++) {
            paramsMAX96712.VC.embDataType = device->property.embDataType;
            paramsMAX96712.VC.link = i;
            paramsMAX96712.VC.dataType = ISC_MAX96712_DATA_TYPE_RAW12;
            status = MAX96712WriteParameters(device->iscDeserializer,
                                            ISC_WRITE_PARAM_CMD_MAX96712_PIPELINE_MAPPING,
                                            sizeof(paramsMAX96712.VC),
                                            &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to set Deserializer link %d mapping, err = %d(%s)\n",
                        __func__, i, errno, (const char*)strerror(errno));
                return status;
            }
        }
    }

    switch(configParam->sensorsNum) {
        case 1:
            LOG_DBG("%s: Enabling link: 0\n", __func__);
            status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                    ISC_CONFIG_MAX96712_ENABLE_LINK_0);
            break;
        case 2:
            LOG_DBG("%s: Enabling 2 links: 0 and 1\n", __func__);
            status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                    ISC_CONFIG_MAX96712_ENABLE_LINKS_01);
            break;
        case 3:
            LOG_DBG("%s: Enabling 3 links: 0, 1 and 2\n", __func__);
            status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                    ISC_CONFIG_MAX96712_ENABLE_LINKS_012);
            break;
        case 4:
            LOG_DBG("%s: Enabling 4 links: 0, 1, 2 and 3\n", __func__);
            status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                    ISC_CONFIG_MAX96712_ENABLE_LINKS_0123);
            break;
        default:
            LOG_ERR("%s: Failed to set deserializer configuration\n", __func__);
            return NVMEDIA_STATUS_ERROR;
    }
    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("%s: NvMediaISCSetDeviceConfig failed on enabling links, err = %d(%s)\n",
                __func__, errno, (const char*)strerror(errno));
        return status;
    }


    /* Check config link lock  */
    for (i = 0; i < device->sensorsNum; i++) {
        status = MAX96712CheckLink(device->iscDeserializer,
                                     i, ISC_MAX96712_GMSL1_CONFIG_LINK_LOCK, NVMEDIA_FALSE);
        if (status != NVMEDIA_STATUS_OK) {
            /* Oneshot reset */
            LOG_DBG("%s: Assert one-shot rest\n", __func__);
            status = MAX96712OneShotReset(device->iscDeserializer, (1 << i));
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to set DBL, err = %d(%s)\n",
                        __func__, errno, (const char*)strerror(errno));
                return status;
            }

            status = MAX96712CheckLink(device->iscDeserializer,
                                       i, ISC_MAX96712_GMSL1_CONFIG_LINK_LOCK, NVMEDIA_TRUE);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s:    Failed to check config link lock, err = %d(%s)\n",
                        __func__, errno, (const char*)strerror(errno));
                return status;
            }
        }
    }

    status = GetAR0231RccbConfigSet(configParam->resolution,
                                    configParam->inputFormat,
                                    &config,
                                    device->property.frameRate);
    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("%s: Failed to get config set\n", __func__);
        return status;
    }

    if (device->iscBroadcastSensor) {
        /* Set sensor defaults after software reset */
        LOG_DBG("%s: Set AR0231 rccb defaults\n", __func__);
        status = AR0231RCCBSetDefaults(device->iscBroadcastSensor);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set AR0231 rccb defaults, err = %d(%s)\n",
                    __func__, errno, (const char*)strerror(errno));
            return status;
        }

        /* Enable bottom emb stats */
        LOG_DBG("%s: Enable bottom emb\n", __func__);
        status = AR0231RCCBSetDeviceConfig(device->iscBroadcastSensor,
                           ISC_CONFIG_AR0231_ENABLE_BOTTOM_EMB);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to enable bottom emb, err = %d(%s)\n",
                    __func__, errno, (const char*)strerror(errno));
            return status;
        }

        /* Read sensor AR0231 rccb PLL registers to get pclk */
        status = AR0231RCCBReadRegister(device->iscBroadcastSensor,
                                        DEFAULT_ISC_DEV_IDX,
                                        AR0231_REG_PLL_VT_PIXDIV, 8, data);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to get sensor PCLK, err = %d(%s)\n",
                    __func__, errno, (const char*)strerror(errno));
            return status;
        }

        /* pre_pll_clk_div:data[5]; pll_muliplier:data[7]; */
        /* vt_sys_clk_div_CLK_DIV:data[3]; vt_pix_clk_div:data[1] */
        pclk = OSC_MHZ / data[5] * data[7] / data[3] / data[1]  * 1000000;
        LOG_DBG("%s: Get sensor pclk: %d Hz\n", __func__, pclk);

        /* Set sensor configuration */
        LOG_DBG("%s: Set sensor configuration (%u)\n", __func__, config);
        status = AR0231RCCBSetDeviceConfig(device->iscBroadcastSensor,
                                           config);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set sensor configuration, err = %d(%s)\n",
                    __func__, errno, (const char*)strerror(errno));
            return status;
        }
    }

    if (device->iscDeserializer) {
        /* Enable FRSYNC */
        paramsMAX96712.FRSYNCMode.syncMode = ISC_CONFIG_MAX96712_EXTERNAL_SYNC;
        LOG_DBG("%s: Set FSYNC mode\n", __func__);
        status = MAX96712WriteParameters(device->iscDeserializer,
                                        ISC_WRITE_PARAM_CMD_MAX96712_FSYNC_MODE,
                                        sizeof(paramsMAX96712.FRSYNCMode),
                                        &paramsMAX96712);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set FSYNC mode, err = %d(%s)\n",
                    __func__, errno, (const char*)strerror(errno));
            return status;
        }
    }

    return status;
}

static
void
SetupEEPROM (
    ExtImgDevParam *configParam,
    ExtImgDevice *device)
{
    uint32_t i;
    uint32_t addressList[1] = {0};
    WriteReadParametersParamMAX96705 paramsMAX96705;
    NvMediaStatus status;

    if (device->iscBroadcastSerializer) {
        for (i = 0; i < configParam->sensorsNum; i++) {

            /* Assign a unique address to the EEPROM if it doesn't already have one */
            if ((0 == configParam->e2pAddr[i]) && (0 != configParam->e2pPhysicalAddr)) {
                configParam->e2pAddr[i] = configParam->e2pPhysicalAddr + i + 2;
            }

            /* Create EEPROM ISC devices */
            if(configParam->e2pAddr[i] && configParam->e2pPhysicalAddr) {
                addressList[0] = configParam->slave ? NVMEDIA_ISC_SIMULATOR_ADDRESS :
                                                      configParam->e2pAddr[i];

                /* Create the EEPROM device */
                LOG_INFO("%s: Create EEPROM device %u on address 0x%x\n", __func__, i,
                            configParam->e2pAddr[i]);
                device->iscEeprom[i] = NvMediaISCDeviceCreate(
                            device->iscRoot,
                            addressList,
                            1,
                            GetN24C64Driver(),
                            NULL);
                if(!device->iscEeprom[i]) {
                    LOG_WARN("%s: Failed to create EEPROM device\n", __func__);
                }
            }

            /* Reuse the I2C translator used by the broadcast serializer to access the EEPROM */
            if(configParam->e2pAddr[i] &&
                device->iscSerializer[i] &&
                device->iscEeprom[i]) {

                /* Set address translation for the EEPROM */
                paramsMAX96705.Translator.source = configParam->e2pAddr[i];
                paramsMAX96705.Translator.destination = configParam->e2pPhysicalAddr;
                LOG_INFO("%s: Translate EEPROM device addr %x to %x\n", __func__,
                        paramsMAX96705.Translator.source, paramsMAX96705.Translator.destination);
                status = MAX96705WriteParameters(device->iscSerializer[i],
                                ISC_WRITE_PARAM_CMD_MAX96705_SET_TRANSLATOR_A,
                                sizeof(paramsMAX96705.Translator),
                                &paramsMAX96705);
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_WARN("%s: Address translation setup failed, err = %d(%s)\n", __func__,
                             errno, (const char*)strerror(errno));
                }

                /* Make sure the EEPROM device is actually present */
                if (NVMEDIA_STATUS_OK != N24C64CheckPresence(device->iscEeprom[i])) {
                    LOG_WARN("%s: Could not communicate with EEPROM %d, cleaning up ISC device!,"
                             " err = %d(%s)\n", __func__, i, errno, (const char*)strerror(errno));
                    NvMediaISCDeviceDestroy(device->iscEeprom[i]);
                    device->iscEeprom[i] = NULL;
                }
            }
        }

        /* Destroy the broadcast serializer device handle */
        NvMediaISCDeviceDestroy(device->iscBroadcastSerializer);
        device->iscBroadcastSerializer = NULL;
    }
}

static ExtImgDevice *
Init(
    ExtImgDevParam *configParam)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint32_t i = 0;
    ExtImgDevice *device = NULL;
    NvMediaISCAdvancedConfig advConfig;
    WriteParametersParamMAX96712 paramsMAX96712;
    ReadParametersParamMAX96712 readParamsMAX96712;
    ContextAR0231 ctxAR0231;
    ContextMAX96712 ctxMAX96712;
    uint32_t portCfg = 0;
    uint32_t addressList[1] = {0};
    NvMediaBool enablePG = NVMEDIA_FALSE;

    if (!configParam)
        return NULL;

    if (configParam->camMap != NULL) {
        LOG_WARN("%s: Cam map is not supported for this deserializer. Ignoring\n", __func__);
        configParam->camMap = NULL;
    }

    if (configParam->enableExtSync == NVMEDIA_FALSE && configParam->enableSimulator == NVMEDIA_FALSE) {
        LOG_WARN("%s: External sync must be enabled for deserializer. "
                 "Enabling external sync with duty ratio 0.25\n", __func__);
        configParam->enableExtSync = NVMEDIA_TRUE;
        configParam->dutyRatio = 0.25; /* 0.25 is a dummy value.
                                        * dutyRatio will be configured from the property */
    }

    memset(&ctxAR0231, 0, sizeof(ContextAR0231));
    memset(&ctxMAX96712,0,sizeof(ContextMAX96712));

    device = calloc(1, sizeof(ExtImgDevice));
    if (!device) {
        LOG_ERR("%s: out of memory\n", __func__);
        return NULL;
    }

    // Enable TPG
     if (!strcmp(configParam->moduleName, "ref_max96712_96705_ar0231rccb_tpg")) {
         enablePG = NVMEDIA_TRUE;
     }

    LOG_INFO("%s: Set image device property\n", __func__);
    status = ImgDevSetProperty(GetDriver_ref_max96712_96705_ar0231rccb(),
                                        configParam,
                                        device);
    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("%s: doesn't support the given property, check input param and image property\n",
                 __func__);
        goto failed;
    }

    /* Turning off emb data lines is supported only in PG mode*/
    if (enablePG == NVMEDIA_FALSE && (device->property.embLinesTop + device->property.embLinesBottom == 0)) {
        LOG_ERR("%s: doesn't support the given property, check input param and image property\n",
                __func__);
        goto failed;
    }

    LOG_INFO("%s: Create root device\n", __func__);
    if (configParam->slave == NVMEDIA_FALSE) {
        portCfg = ISC_RDEV_CFG(device->property.interface,
                               configParam->enableSimulator?
                                   NVMEDIA_ISC_I2C_SIMULATOR :
                                   configParam->i2cDevice);
    } else {
        portCfg = ISC_SLV_RDEV_CFG(device->property.interface,
                                   configParam->enableSimulator?
                                       NVMEDIA_ISC_I2C_SIMULATOR :
                                       configParam->i2cDevice);
    }

    device->iscRoot = NvMediaISCRootDeviceCreate(portCfg);
    if (!device->iscRoot) {
        LOG_ERR("%s: Failed to create NvMedia ISC root device\n", __func__);
        goto failed;
    }

    if (configParam->desAddr) {
        addressList[0] = configParam->desAddr;

        /* Configure MAX96712 settings */
        ctxMAX96712.gmslMode = ISC_MAX96712_GMSL1_MODE;
        status = SetMAX96712Ctx(device,
                                configParam,
                                &ctxMAX96712);
        if (status != NVMEDIA_STATUS_OK) {
            goto failed;
        }

        ADV_CONFIG_INIT(advConfig, &ctxMAX96712);
        /* Create the deserializer device */
        LOG_INFO("%s: Create deserializer device on address 0x%x\n", __func__, configParam->desAddr);
        device->iscDeserializer = NvMediaISCDeviceCreate(
                            device->iscRoot,        /* rootDevice */
                            addressList,            /* addressList */
                            1,                      /* size of address list */
                            GetMAX96712Driver(),    /* deviceDriver */
                            &advConfig);            /* advancedConfig */
        if (!device->iscDeserializer) {
            LOG_ERR("%s: Failed to create deserializer device\n", __func__);
            goto failed;
        }
    }

    if (configParam->brdcstSerAddr) {
        addressList[0] = configParam->slave ? NVMEDIA_ISC_SIMULATOR_ADDRESS :
                                               configParam->brdcstSerAddr;

        /* Create broadcast serializer device */
        LOG_INFO("%s: Create broadcast serializer device on address 0x%x\n", __func__,
                          configParam->brdcstSerAddr);
        device->iscBroadcastSerializer = NvMediaISCDeviceCreate(
                          device->iscRoot,
                          addressList,
                          1,
                          GetMAX96705Driver(),
                          NULL);
        if (!device->iscBroadcastSerializer) {
            LOG_ERR("%s: Failed to create broadcast serializer device\n", __func__);
            goto failed;
        }
    }

    ctxAR0231.oscMHz = OSC_MHZ;
    ctxAR0231.maxGain = MAX_GAIN;
    ctxAR0231.embDataType = device->property.embDataType;
    /* set frameRate from device property */
    ctxAR0231.frameRate = device->property.frameRate;
    /* set config set */
    ctxAR0231.configSetIdx = device->property.enableExtSync;

    status = ImgDevGetModuleConfig(&ctxAR0231.moduleConfig, configParam->moduleName);
    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("%s: Failed to get camera module config file name\n", __func__);
    }

    if (configParam->brdcstSensorAddr) {
        addressList[0] = configParam->slave ? NVMEDIA_ISC_SIMULATOR_ADDRESS :
                                               configParam->brdcstSensorAddr;

        ADV_CONFIG_INIT(advConfig, &ctxAR0231);
        /* Create the image sensor device */
        LOG_INFO("%s: Create broadcast sensor device on address 0x%x\n", __func__,
                         configParam->brdcstSensorAddr);
        device->iscBroadcastSensor = NvMediaISCDeviceCreate(
                                        device->iscRoot,
                                        addressList,
                                        1,
                                        GetAR0231RccbDriver(),
                                        &advConfig);
        if (!device->iscBroadcastSensor) {
            LOG_ERR("%s: Failed to create broadcast sensor device\n", __func__);
            goto failed;
        }
    }

    /* Assign unique address to each serializer based on the broadcast address */
    for (i = 0; i < configParam->sensorsNum; i++) {
        if (!configParam->serAddr[i]) {
            configParam->serAddr[i] = configParam->brdcstSerAddr + i + 1;
        }
    }

    /* Assign unique address to each sensor based on the broadcast address */
    for (i = 0; i < configParam->sensorsNum; i++) {
        if (!configParam->sensorAddr[i]) {
            configParam->sensorAddr[i] = configParam->brdcstSensorAddr + i + 1;
        }
    }

    for (i = 0; i < configParam->sensorsNum; i++) {
        if (configParam->serAddr[i]) {
            addressList[0] = configParam->slave ? NVMEDIA_ISC_SIMULATOR_ADDRESS :
                                                  configParam->serAddr[i];

            /* Create the serializer device */
            LOG_INFO("%s: Create serializer device %u on address 0x%x\n", __func__, i,
                        configParam->serAddr[i]);
            device->iscSerializer[i] = NvMediaISCDeviceCreate(
                        device->iscRoot,
                        addressList,
                        1,
                        GetMAX96705Driver(),
                        NULL);
            if (!device->iscSerializer[i]) {
                LOG_ERR("%s: Failed to create serializer device\n", __func__);
                goto failed;
            }
        }

        if (configParam->sensorAddr[i]) {
            addressList[0] = configParam->slave ? NVMEDIA_ISC_SIMULATOR_ADDRESS :
                                                  configParam->sensorAddr[i];

            /* Create the image sensor device */
            LOG_INFO("%s: Create image sensor device %u on address 0x%x\n", __func__, i,
                        configParam->sensorAddr[i]);
            ADV_CONFIG_INIT(advConfig, &ctxAR0231);
            device->iscSensor[i] = NvMediaISCDeviceCreate(
                                        device->iscRoot,
                                        addressList,
                                        1,
                                        GetAR0231RccbDriver(),
                                        &advConfig);
            if (!device->iscSensor[i]) {
                LOG_ERR("%s: Failed to create image sensor device\n", __func__);
                goto failed;
            }
        }
    }

    if (configParam->enableSimulator || configParam->slave) {
        ConfigInfoAR0231 configInfo;
        WriteReadParametersParamAR0231 paramAR0231;
        paramAR0231.configInfo = &configInfo;

        status = AR0231RCCBReadParameters(device->iscBroadcastSensor,
                        ISC_READ_PARAM_CMD_AR0231_CONFIG_INFO,
                        sizeof(paramAR0231.configInfo),
                        &paramAR0231);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to get config\n", __func__);
            goto failed;
        }

        status = GetAR0231RccbConfigSet(configParam->resolution,
                                        configParam->inputFormat,
                                        &configInfo.enumeratedDeviceConfig,
                                        device->property.frameRate);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to get config set\n", __func__);
            goto failed;
        }

        status = AR0231RCCBReadParameters(device->iscBroadcastSensor,
                        ISC_READ_PARAM_CMD_AR0231_EXP_LINE_RATE,
                        sizeof(paramAR0231.configInfo),
                        &paramAR0231);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to get line rate\n", __func__);
            goto failed;
        }

        LOG_INFO("%s: Update config info for boardcastSensorDevice\n", __func__);

        status = AR0231RCCBWriteParameters(device->iscBroadcastSensor,
                        ISC_WRITE_PARAM_CMD_AR0231_CONFIG_INFO,
                        sizeof(paramAR0231.configInfo),
                        &paramAR0231);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set config\n", __func__);
            goto failed;
        }

        goto init_done;
    }

    status = SetupConfigLink(configParam, device);
    if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to setup config link\n", __func__);
            goto failed;
    }

    status = SetupVideoLink(configParam, device);
    if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to setup video link\n", __func__);
            goto failed;
    }

init_done:
    /* update frame id for embedded data */
    if (device->iscBroadcastSensor) {
        /* if individual sensor device handle exists, it needs to copy the config info */
        /* for each sensor from braodcast device to control expose time and to get embedded data. */
        ConfigInfoAR0231 configInfo;
        WriteReadParametersParamAR0231 paramAR0231;
        paramAR0231.configInfo = &configInfo;

        status = AR0231RCCBReadParameters(device->iscBroadcastSensor,
                        ISC_READ_PARAM_CMD_AR0231_CONFIG_INFO,
                        sizeof(paramAR0231.configInfo),
                        &paramAR0231);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to get config\n", __func__);
            goto failed;
        }

        for (i = 0; i < configParam->sensorsNum; i++) {
            if (device->iscSensor[i]) {
                LOG_DBG("%s: Set sensor[%d] config\n", __func__, i);
                status = AR0231RCCBWriteParameters(device->iscSensor[i],
                                ISC_WRITE_PARAM_CMD_AR0231_CONFIG_INFO,
                                sizeof(paramAR0231.configInfo),
                                &paramAR0231);
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_ERR("%s: Failed to set config\n", __func__);
                    goto failed;
                }
            }
        }

        if (configParam->slave == NVMEDIA_TRUE) {
            return device;
        }

        LOG_DBG("%s: Reset sensor frame id\n", __func__);
        status = AR0231RCCBSetDeviceConfig(device->iscBroadcastSensor,
                              ISC_CONFIG_AR0231_RESET_FRAME_ID);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to reset sensor frame id, err = %d(%s)\n",
                    __func__, errno, (const char*)strerror(errno));
            goto failed;
        }

        // Enable TPG
        if (enablePG == NVMEDIA_TRUE) {
            LOG_DBG("%s: Enable PG\n", __func__);
            status = AR0231RCCBSetDeviceConfig(device->iscBroadcastSensor,
                                               ISC_CONFIG_AR0231_ENABLE_PG);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable PG, err = %d(%s)\n",
                        __func__, errno, (const char*)strerror(errno));
                goto failed;
            }
        }
    }

    if ((configParam->enableSimulator == NVMEDIA_TRUE) ||
        (configParam->slave == NVMEDIA_TRUE)) {
        return device;
    }

    if (device->iscBroadcastSensor) {
        LOG_DBG("%s: Enable sensor streaming\n", __func__);
        status = AR0231RCCBSetDeviceConfig(device->iscBroadcastSensor,
                                            ISC_CONFIG_AR0231_ENABLE_STREAMING);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to enable sensor streaming, err = %d(%s)\n", __func__,
                    errno, (const char*)strerror(errno));
            goto failed;
        }
        nvsleep(10000);
    }

    for (i = 0; i < configParam->sensorsNum; i++) {
        if (device->iscBroadcastSerializer) {
            /* Enable PCLK In */
            LOG_DBG("%s: Enable PCLK In\n", __func__);
            status = MAX96705SetDeviceConfig(device->iscSerializer[i],
                                               ISC_CONFIG_MAX96705_PCLKIN);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable PCLK In on link %d, err = %d(%s)\n", __func__, i,
                        errno, (const char*)strerror(errno));
                goto failed;
            }
        }

        if (device->iscDeserializer) {
            /* Set DBL */
            paramsMAX96712.SpecificLink = i;
            LOG_DBG("%s: Set DBL\n", __func__);
            status = MAX96712WriteParameters(device->iscDeserializer,
                                             ISC_WRITE_PARAM_CMD_MAX96712_SET_DBL,
                                             sizeof(paramsMAX96712.SpecificLink),
                                             &paramsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to set DBL on link %d, err = %d(%s)\n",
                        __func__, i, errno, (const char*)strerror(errno));
                goto failed;
            }
        }

        if (device->iscBroadcastSerializer) {
            /* Enable each serial link */
            LOG_DBG("%s: Enable serial link\n", __func__);
            status = MAX96705SetDeviceConfig(device->iscSerializer[i],
                                                ISC_CONFIG_MAX96705_ENABLE_SERIAL_LINK);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to enable serial link on link %d, err = %d(%s)\n", __func__, i,
                        errno, (const char*)strerror(errno));
                goto failed;
            }

            nvsleep(40000);
        }
    }

    /* TODO : Incompleted EMB data received before getting the first frame.
     *        This will cause EMBED_SHORT_FRAME errors and nvmimg_cap can't
     *        recover it. This is a delay temporally and will be removed later.
     */
    nvsleep(100000);  /* wait 100ms */

    if (device->iscDeserializer) {
        /* Check video link lock  */
        for (i = 0; i < device->sensorsNum; i++) {
            LOG_DBG("%s: Check video link lock\n", __func__);
            status = MAX96712CheckLink(device->iscDeserializer,
                                       i, ISC_MAX96712_VIDEO_LINK_LOCK, NVMEDIA_FALSE);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_DBG("%s: Failed to check video link lock %d, err = %d(%s)\n", __func__,
                                                                                  i,
                                                                                  errno,
                                                                                  (const char*)strerror(errno));
                /* The link was up and the sensor was configured properly
                   Assert Onehot reset to make sure the data path cleared
                   and the setting is properly applied */
                    /* Oneshot reset */
                LOG_DBG("%s: Assert one-shot rest\n", __func__);
                status = MAX96712OneShotReset(device->iscDeserializer, GET_MAX96712_GMSL_LINK(i));
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_ERR("%s: Failed to set DBL, err = %d(%s)\n",
                            __func__, errno, (const char*)strerror(errno));
                    goto failed;
                }

                /* Check video link lock again */
                LOG_DBG("%s: Check video link lock\n", __func__);
                status = MAX96712CheckLink(device->iscDeserializer,
                                           i, ISC_MAX96712_VIDEO_LINK_LOCK, NVMEDIA_TRUE);
                if (status != NVMEDIA_STATUS_OK) {
                    LOG_ERR("%s: Failed to enable serial link, err = %d(%s)\n", __func__,
                            errno, (const char*)strerror(errno));
                    goto failed;
                }
            }

            /* Clear the packet based control channel CRC error. GMSL1 only */
            readParamsMAX96712.ErrorStatus.link = i;
            status = MAX96712ReadParameters(device->iscDeserializer,
                                              ISC_READ_PARAM_CMD_MAX96712_CONTROL_CHANNEL_CRC_ERROR,
                                              sizeof(readParamsMAX96712.ErrorStatus),
                                              &readParamsMAX96712);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to clear control channel crc link(%d) error = %d(%s)\n",
                        __func__, i, errno, (const char*)strerror(errno));
                goto failed;
            }
        }

        if (!((configParam->initialized == NVMEDIA_TRUE) ||
             (configParam->enableSimulator == NVMEDIA_TRUE) ||
             (configParam->slave == NVMEDIA_TRUE))) {
            /* Check CSIPLL lock and force CSI PLL Lock if required */
            LOG_DBG("%s: Force CSI PLL Lock\n", __func__);
            status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                             ISC_CONFIG_MAX96712_CHECK_CSIPLL_LOCK);
            if (status != NVMEDIA_STATUS_OK) {
                LOG_ERR("%s: Failed to check CSIPLL lock, err = %d(%s)\n", __func__,
                        errno, (const char*)strerror(errno));
                goto failed;
            }
        }
    }

    if (device->iscBroadcastSerializer) {
        /* Set the remote-i2c-master timeout to never in MAX96705 to prevent timeout in
         * remote-i2c-master while transferring i2c data from the actual i2c master (Bug 1802338, 200419005) */
        status = MAX96705SetDeviceConfig(device->iscBroadcastSerializer,
                                         ISC_CONFIG_MAX96705_SET_MAX_REMOTE_I2C_MASTER_TIMEOUT);
        if(status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to set the max remote-i2c-master timeout, err = %d(%s)\n", __func__,
                    errno, (const char*)strerror(errno));
            goto failed;
        }
    }

    SetupEEPROM(configParam, device);

    device->simulator = configParam->enableSimulator;
    return device;

failed:
    Deinit(device);

    return NULL;
}

static NvMediaStatus
Start(
    ExtImgDevice *device)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;

    if (!device) {
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if (device->iscDeserializer) {
        /* Initialize replication (for slave capture) */
        LOG_DBG("%s: Initialize the replicate mode\n", __func__);
        status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                         ISC_CONFIG_MAX96712_ENABLE_REPLICATION);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to replicate output, err = %d(%s)\n", __func__, errno,
                    (const char*)strerror(errno));
            return status;
        }
    }

    if ((device->iscDeserializer) &&
        (device->property.phyMode == NVMEDIA_ICP_CSI_DPHY_MODE) &&
        (device->property.mipiSpeed[NVMEDIA_ICP_CSI_DPHY_MODE] >= 1500000)) {
        /* Trigger the one time initial deskew */
        LOG_DBG("%s: Trigger the one time initial deskew\n", __func__);
        status = MAX96712SetDeviceConfig(device->iscDeserializer,
                                         ISC_CONFIG_MAX96712_TRIGGER_DESKEW);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("%s: Failed to trigger the one time initial deskew, err = %d(%s)\n", __func__,
                    errno, (const char*)strerror(errno));
            return status;
        }
    }

    return status;
}

static NvMediaStatus
GetError(
   ExtImgDevice *device,
   uint32_t *link,
   ExtImgDevFailureType *errorType)
{
    return _GetError_max96712(device->iscDeserializer, link, errorType);
}

static ImgProperty properties[] = {
                   /* resolution, oscMHz, fps, duty, mipiDphy, mipiCphy, embTop, embBottom, inputFormat, pixelOrder, embDataType */
    IMG_PROPERTY_ENTRY_MIPI(1920x1208, OSC_MHZ, 30, 0.25, 0, 0, 0,       0,       raw12,       grbg,       0),
    IMG_PROPERTY_ENTRY_MIPI(1920x1208, OSC_MHZ, 30, 0.25, 0, 0, 24,      4,       raw12,       grbg,       0), /* default resolution and frame rate */
    IMG_PROPERTY_ENTRY_MIPI(1920x1008, OSC_MHZ, 36, 0.25, 0, 0, 16,      4,       raw12,       grbg,       0),
};

static ImgDevDriver device = {
    .name = "ref_max96712_96705_ar0231rccb",
    .Init = Init,
    .Deinit = Deinit,
    .Start = Start,
    .GetError = GetError,
    .properties = properties,
    .numProperties = sizeof(properties) / sizeof(properties[0]),
};

ImgDevDriver *
GetDriver_ref_max96712_96705_ar0231rccb(void)
{
    return &device;
}
