/*
 * 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 <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include "log_utils.h"
#include "devblk_cdi.h"
#include "cdi_max9295.h"
#include "os_common.h"

#define GET_BLOCK_LENGTH(x)            x[0]
#define GET_SIZE(x)                    sizeof(x)
#define GET_BLOCK_DATA(x)              &x[1]
#define SET_NEXT_BLOCK(x)              x += (x[0] + 1)

#define MAX9295_NUM_ADDR_BYTES         2u
#define MAX9295_NUM_DATA_BYTES         1u
#define REG_WRITE_BUFFER_BYTES         MAX9295_NUM_DATA_BYTES
#define MAX9295_CDI_DEVICE_INDEX       0u

#define REG_DEV_ID_ADDR                0x0D
#define MAX9295A_DEV_ID                0x91
#define REG_DEV_REV_ADDR               0x0E
#define MAX9295_REG_MAX_ADDRESS        0x1576

typedef struct {
    RevisionMAX9295 revId;
    uint32_t revVal;
} Revision;

/* These values must include all of values in the RevisionMAX9295 enum */
static Revision supportedRevisions[] = {
    {CDI_MAX9295_REV_5, 5u},
    {CDI_MAX9295_REV_7, 7u},
    {CDI_MAX9295_REV_8, 8u},
};

static NvMediaStatus
WriteArray(
     DevBlkCDIDevice *handle,
     uint32_t arrayByteLength,
     const uint8_t *arrayData)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;

    while (arrayByteLength) {
        status = DevBlkCDIDeviceWrite(handle,
                                      MAX9295_CDI_DEVICE_INDEX,
                                      GET_BLOCK_LENGTH(arrayData),
                                      GET_BLOCK_DATA(arrayData));
        if (status != NVMEDIA_STATUS_OK) {
            if (GET_BLOCK_LENGTH(arrayData) == 3) {
                LOG_ERR("MAX9295: I2C write 0x%02X%02X 0x%02X failed with status %d",
                        arrayData[1],
                        arrayData[2],
                        arrayData[3],
                        status);
            } else {
                /* All calls to WriteArray() have block length 3 so this path is not used */
                LOG_ERR("MAX9295: I2C write failed with status %d", status);
            }
            break;
        }

        arrayByteLength -= GET_BLOCK_LENGTH(arrayData) + 1;
        SET_NEXT_BLOCK(arrayData);
    }

    return status;
}

static NvMediaStatus
SetDeviceAddress(
    DevBlkCDIDevice *handle,
    uint8_t address)
{
    uint8_t setAddr[] = {3, 0x0, 0x0, 0x0};

    /* Check for 7 bit I2C address */
    if (address >= 0x80) {
        LOG_ERR("MAX9295: Bad parameter: Address 0x%02 is greater than 0x80", address);
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    setAddr[3] = address << 1;

    return WriteArray(handle,
                      GET_SIZE(setAddr),
                      setAddr);
}

static NvMediaStatus
SetTranslator(
    DevBlkCDIDevice *handle,
    uint32_t parameterType,
    uint8_t source,
    uint8_t destination)
{
    uint8_t setTranslator[] = {
        3, 0x00, 0x42, 0x00,
        3, 0x00, 0x43, 0x00,
    };

    /* Check for 7 bit I2C address */
    if ((source >= 0x80) || (destination >= 0x80)) {
        LOG_ERR("MAX9295: Bad parameter: Source address = 0x%02x, destination address = 0x%02x\n", source, destination);
        LOG_ERR("must be less than 0x80\n");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if (parameterType == CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_B) {
        setTranslator[2] += 2;
        setTranslator[6] += 2;
    }

    setTranslator[3] = source << 1;
    setTranslator[7] = destination << 1;

    return WriteArray(handle,
                      GET_SIZE(setTranslator),
                      setTranslator);
}

static NvMediaStatus
ConfigPipelines(
    DevBlkCDIDevice *handle,
    DataTypeMAX9295 dataType,
    bool embDataType)
{
    /* Configure pipeline X for pixel data and
     * pipeline Y for emb data if enabled */
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t startPipeFromPort[] = {3, 0x03, 0x11, 0x00};
    uint8_t pipePortMap[] = {3, 0x03, 0x08, 0x40};
    uint8_t videoTxEn[] = {3, 0x0, 0x02, 0x13};
    uint8_t mappingPixel[] = {
        3, 0x03, 0x14, 0x6C, /* Route 12bit RAW to VIDEO_X (MSB enable) */
        3, 0x00, 0x53, 0x10, /* Stream ID for packets for VIDEO_X */
    };
    uint8_t mappingEmb[] = {
        3, 0x03, 0x16, 0x52, /* Route EMBEDDED8 to VIDEO_Y (MSB enable) */
        3, 0x00, 0x57, 0x11, /* Stream ID for packets for VIDEO_Y */
    };
    static uint8_t doubleBpp12Bpp8[] = {
        3, 0x03, 0x12, 0x02,
        3, 0x03, 0x13, 0x10,
        3, 0x03, 0x1C, 0x38,
        3, 0x03, 0x1D, 0x30,
    };
    static const uint8_t disableHeartbeat[] = {
        3, 0x01, 0x02, 0x0E,
        3, 0x01, 0x0A, 0x0E,
    };

    /* Update mapping table with data type */
    switch (dataType) {
        case CDI_MAX9295_DATA_TYPE_RAW10:
            mappingPixel[3] = 0x6B;
            break;
        case CDI_MAX9295_DATA_TYPE_RAW12:
            mappingPixel[3] = 0x6C;
            break;
        case CDI_MAX9295_DATA_TYPE_RAW16:
            mappingPixel[3] = 0x6E;
            break;
        default:
            LOG_ERR("MAX9295: Bad parameter: Invalid data type");
            return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    startPipeFromPort[3] = 0x10;
    pipePortMap[3] |= 0x21;
    if (embDataType) {
        startPipeFromPort[3] |= 0x20;
        pipePortMap[3] |= 0x02;
        videoTxEn[3] |= 0x20;
    }

    status = WriteArray(handle,
                        GET_SIZE(startPipeFromPort),
                        startPipeFromPort);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    status = WriteArray(handle,
                        GET_SIZE(pipePortMap),
                        pipePortMap);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    status = WriteArray(handle,
                        GET_SIZE(mappingPixel),
                        mappingPixel);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    if (embDataType) {
        status = WriteArray(handle,
                            GET_SIZE(mappingEmb),
                            mappingEmb);
        if (status != NVMEDIA_STATUS_OK) {
            return status;
        }
    }

    status = WriteArray(handle,
                        GET_SIZE(videoTxEn),
                        videoTxEn);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    /* Turn on double mode only if emb data type is enabled and pixel data type is RAW12/RAW10 */
    if (dataType == CDI_MAX9295_DATA_TYPE_RAW10) {
        doubleBpp12Bpp8[7] = 0x1;
        doubleBpp12Bpp8[11] = 0x34;
    }
    status = WriteArray(handle,
                        GET_SIZE(doubleBpp12Bpp8),
                        doubleBpp12Bpp8);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    status = WriteArray(handle,
                        GET_SIZE(disableHeartbeat),
                        disableHeartbeat);
    return status;
}

static NvMediaStatus
ConfigPhyMap(
    DevBlkCDIDevice *handle,
    phyMapMAX9295 *mapping,
    uint8_t numDataLanes)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t phyMap[] = {
        3, 0x03, 0x30, 0x00,
        3, 0x03, 0x31, 0x00,
        3, 0x03, 0x32, 0xEE,
        3, 0x03, 0x33, 0xE4
    };

    if (mapping == NULL) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if ((numDataLanes != 2) && (numDataLanes != 4)) {
        LOG_ERR("MAX9295: numDataLanes = %u\n. Valid vals are 2 and 4", numDataLanes);
        return NVMEDIA_STATUS_NOT_SUPPORTED;
    }

    if ((numDataLanes == 2) && (mapping->enableMapping)) {
        LOG_ERR("MAX9295: Lane swapping is supported only in 4 lane mode");
        return NVMEDIA_STATUS_NOT_SUPPORTED;
    }

    status = DevBlkCDIDeviceRead(handle,
                                 MAX9295_CDI_DEVICE_INDEX,
                                 MAX9295_NUM_ADDR_BYTES,
                                 &phyMap[5],
                                 1,
                                 &phyMap[7]);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }
    /* data lanes indexing starts at 0 (0 = 1 lane, 1 = 2 lanes, etc) */
    numDataLanes -= 1;
    /* clear the data lanes settings for Port B */
    phyMap[7] &= ~0x30;
    /* Set num data lanes for Port B */
    phyMap[7] |= (numDataLanes << 4);

    if (mapping->enableMapping) {
        phyMap[11] = (mapping->phy1_d1 << 6) |
                     (mapping->phy1_d0 << 4) |
                     (mapping->phy0_d1 << 2) |
                     (mapping->phy0_d0);

        phyMap[15] = (mapping->phy3_d1 << 6) |
                     (mapping->phy3_d0 << 4) |
                     (mapping->phy2_d1 << 2) |
                     (mapping->phy2_d0);
    }

    return WriteArray(handle,
                      GET_SIZE(phyMap),
                      phyMap);
}

static NvMediaStatus
SetGPIOOutput(
    DevBlkCDIDevice *handle,
    GPIOTypeMAX9295 gpio,
    bool level)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t setGPIOAMode[] = {3, 0x02, 0xBE, 0x80}; /* pull-up 1M ohm, output driver enabled */
    uint8_t setGPIOBMode[] = {3, 0x02, 0xBF, 0x60}; /* pull-up, output push-pull */

    if ((gpio == CDI_MAX9295_GPIO_TYPE_INVALID) || (gpio >= CDI_MAX9295_GPIO_TYPE_NUM)) {
        LOG_ERR("MAX9295: Bad parameter: Invalid GPIO pin");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    setGPIOAMode[2] +=  ((uint8_t) gpio - 1u) * 3u;
    setGPIOBMode[2] +=  ((uint8_t) gpio - 1u) * 3u;

    if (level) {
        setGPIOAMode[3] |= (1 << 4);
    }

    status = WriteArray(handle,
                        GET_SIZE(setGPIOAMode),
                        setGPIOAMode);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    status = WriteArray(handle,
                        GET_SIZE(setGPIOBMode),
                        setGPIOBMode);
    return status;
}

static NvMediaStatus
SetFsyncGPIO(
    DevBlkCDIDevice *handle,
    GPIOTypeMAX9295 gpio,
    uint8_t rxID)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t setGPIOMode[] = {3, 0x02, 0xBE, 0x84}; /* pull-up 1M ohm, GPIO source en for GMSL2 */

    if ((gpio == CDI_MAX9295_GPIO_TYPE_INVALID) || (gpio >= CDI_MAX9295_GPIO_TYPE_NUM)) {
        LOG_ERR("MAX9295: Bad parameter: Invalid GPIO pin");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    setGPIOMode[2] += ((uint8_t) gpio - 1u) * 3u;

    status = WriteArray(handle,
                        GET_SIZE(setGPIOMode),
                        setGPIOMode);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    /* Update the offset from GPIO A to GPIO C*/
    setGPIOMode[2] += 2;
    status = DevBlkCDIDeviceRead(handle,
                                 MAX9295_CDI_DEVICE_INDEX,
                                 MAX9295_NUM_ADDR_BYTES,
                                 &setGPIOMode[1],
                                 1,
                                 &setGPIOMode[3]);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    setGPIOMode[3] &= 0xE0;
    setGPIOMode[3] |= (rxID & 0x1F); /* GPIO receive ID */
    return WriteArray(handle,
                      GET_SIZE(setGPIOMode),
                      setGPIOMode);
}

static NvMediaStatus
EnableRefClock(
    DevBlkCDIDevice *handle,
    GPIOTypeMAX9295 gpio,
    bool enableRClk)
{
    uint8_t enablePCLKOut[] = {3, 0x03, 0xF1, 0x00};

    if ((gpio == CDI_MAX9295_GPIO_TYPE_INVALID) || (gpio >= CDI_MAX9295_GPIO_TYPE_NUM)) {
        LOG_ERR("MAX9295: Bad parameter: Invalid GPIO pin");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if (enableRClk) {
        enablePCLKOut[3] |= 0x80;
    }

    enablePCLKOut[3] |= ((((uint8_t) gpio - 1u) << 1) | 0x1);

    return WriteArray(handle,
                      GET_SIZE(enablePCLKOut),
                      enablePCLKOut);
}

static NvMediaStatus
GetRevId(
    DevBlkCDIDevice *handle,
    RevisionMAX9295 *rev)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint32_t numRev = sizeof(supportedRevisions) / sizeof(supportedRevisions[0]);
    uint8_t revision = 0u;
    uint32_t i = 0u;

    status = MAX9295ReadRegister(handle,
                                 MAX9295_CDI_DEVICE_INDEX,
                                 REG_DEV_REV_ADDR,
                                 1,
                                 &revision);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    revision &= 0x0F;
    for (i = 0u; i < numRev; i++) {
        if (revision == supportedRevisions[i].revVal) {
            *rev = supportedRevisions[i].revId;
            LOG_MSG("MAX9295: Revision %u detected!\n", revision);
            return NVMEDIA_STATUS_OK;
        }
    }

    LOG_ERR("MAX9295: Unsupported MAX9295 revision %u detected!\n"
            "Supported revisions are:", revision);
    for (i = 0u; i < numRev; i++) {
        LOG_MSG("MAX9295: Revision %u\n", supportedRevisions[i].revVal);
    }

    return NVMEDIA_STATUS_NOT_SUPPORTED;
}

static NvMediaStatus
DriverCreate(
    DevBlkCDIDevice *handle,
    void *clientContext)
{
    if (handle == NULL) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if (clientContext != NULL) {
        LOG_ERR("MAX9295: Context must not be supplied");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    handle->deviceDriverHandle = NULL;

    return NVMEDIA_STATUS_OK;

}

static NvMediaStatus
DriverDestroy(
    DevBlkCDIDevice *handle)
{
    if (handle == NULL) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    return NVMEDIA_STATUS_OK;
}

NvMediaStatus
MAX9295SetDefaults(
    DevBlkCDIDevice *handle)
{
    if (handle == NULL) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    return NVMEDIA_STATUS_OK;
}

NvMediaStatus
MAX9295ReadRegister(
    DevBlkCDIDevice *handle,
    uint32_t deviceIndex,
    uint32_t registerNum,
    uint32_t dataLength,
    uint8_t *dataBuff)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t registerData[MAX9295_NUM_ADDR_BYTES] = {} ;

    if (handle == NULL) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    registerData[0] = (registerNum >> 8) & 0xFF;
    registerData[1] = registerNum & 0xFF;

    status = DevBlkCDIDeviceRead(handle,
                                 deviceIndex,
                                 MAX9295_NUM_ADDR_BYTES,
                                 registerData,
                                 dataLength,
                                 dataBuff);
    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("MAX9295: Register 0x%04X I2C read failed with status %d", registerNum, status);
    }

    return status;
}

NvMediaStatus
MAX9295WriteRegister(
    DevBlkCDIDevice *handle,
    uint32_t deviceIndex,
    uint32_t registerNum,
    uint32_t dataLength,
    uint8_t *dataBuff)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t data[MAX9295_NUM_ADDR_BYTES + REG_WRITE_BUFFER_BYTES] = {};

    if ((handle == NULL) || (dataBuff == NULL)) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if (dataLength > REG_WRITE_BUFFER_BYTES) {
        LOG_ERR("MAX9295: Insufficient buffering");
        return NVMEDIA_STATUS_INSUFFICIENT_BUFFERING;
    }

    data[1] = registerNum & 0xFF;
    data[0] = (registerNum >> 8) & 0xFF;
    memcpy(&data[2], dataBuff, dataLength);

    status = DevBlkCDIDeviceWrite(handle,
                                  deviceIndex,
                                  dataLength + MAX9295_NUM_ADDR_BYTES,
                                  data);
    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("MAX9295: Register 0x%04X I2C write failed with status %d", registerNum, status);
    }

    return status;
}

NvMediaStatus
MAX9295WriteParameters(
    DevBlkCDIDevice *handle,
    uint32_t parameterType,
    uint32_t parameterSize,
    void *parameter)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    bool isValidSize = false;
    ReadWriteParamsMAX9295 *param = (ReadWriteParamsMAX9295 *) parameter;
    static const char *cmdString[] = {
        [CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_A] =
            "CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_A",
        [CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_B] =
            "CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_B",
        [CDI_WRITE_PARAM_CMD_MAX9295_SET_DEVICE_ADDRESS] =
            "CDI_WRITE_PARAM_CMD_MAX9295_SET_DEVICE_ADDRESS",
        [CDI_WRITE_PARAM_CMD_MAX9295_SET_FSYNC_GPIO] =
            "CDI_WRITE_PARAM_CMD_MAX9295_SET_FSYNC_GPIO",
        [CDI_WRITE_PARAM_CMD_MAX9295_SET_GPIO_OUTPUT] =
            "CDI_WRITE_PARAM_CMD_MAX9295_SET_GPIO_OUTPUT",
        [CDI_WRITE_PARAM_CMD_MAX9295_ENABLE_REF_CLOCK] =
            "CDI_WRITE_PARAM_CMD_MAX9295_ENABLE_REF_CLOCK",
        [CDI_WRITE_PARAM_CMD_MAX9295_CONFIG_VIDEO_PIPELINES] =
            "CDI_WRITE_PARAM_CMD_MAX9295_CONFIG_VIDEO_PIPELINES",
        [CDI_WRITE_PARAM_CMD_MAX9295_CONFIG_PHY] =
            "CDI_WRITE_PARAM_CMD_MAX9295_CONFIG_PHY",
    };

    if ((handle == NULL) || (parameter == NULL)) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    if ((parameterType == CDI_WRITE_PARAM_CMD_MAX9295_INVALID) ||
        (parameterType >= CDI_WRITE_PARAM_CMD_MAX9295_NUM)) {
        LOG_ERR("MAX9295: Bad parameter: Invalid command");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    LOG_DBG("MAX9295: %s", cmdString[parameterType]);
    switch (parameterType) {
        case CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_A:
        case CDI_WRITE_PARAM_CMD_MAX9295_SET_TRANSLATOR_B:
            if (parameterSize == sizeof(param->Translator)) {
                isValidSize = true;
                status = SetTranslator(handle,
                                       parameterType,
                                       param->Translator.source,
                                       param->Translator.destination);
            }
            break;
        case CDI_WRITE_PARAM_CMD_MAX9295_SET_DEVICE_ADDRESS:
            if (parameterSize == sizeof(param->DeviceAddress)) {
                isValidSize = true;
                status = SetDeviceAddress(handle,
                                          param->DeviceAddress.address);
            };
            break;
        case CDI_WRITE_PARAM_CMD_MAX9295_SET_GPIO_OUTPUT:
            if (parameterSize == sizeof(param->GPIOOutp)) {
                isValidSize = true;
                status = SetGPIOOutput(handle,
                                       param->GPIOOutp.gpioInd,
                                       param->GPIOOutp.level);
            };
            break;
        case CDI_WRITE_PARAM_CMD_MAX9295_SET_FSYNC_GPIO:
            if (parameterSize == sizeof(param->FSyncGPIO)) {
                isValidSize = true;
                status = SetFsyncGPIO(handle,
                                       param->FSyncGPIO.gpioInd,
                                       param->FSyncGPIO.rxID);
            };
            break;
        case CDI_WRITE_PARAM_CMD_MAX9295_ENABLE_REF_CLOCK:
            if (parameterSize == sizeof(param->RefClkGPIO)) {
                isValidSize = true;
                status = EnableRefClock(handle,
                                        param->RefClkGPIO.gpioInd,
                                        param->RefClkGPIO.enableRClk);
            };
            break;
        case CDI_WRITE_PARAM_CMD_MAX9295_CONFIG_VIDEO_PIPELINES:
            if (parameterSize == sizeof(param->ConfigVideoPipeline)) {
                isValidSize = true;
                status = ConfigPipelines(handle,
                                         param->ConfigVideoPipeline.dataType,
                                         param->ConfigVideoPipeline.embDataType);
            };
            break;
        case CDI_WRITE_PARAM_CMD_MAX9295_CONFIG_PHY:
            if (parameterSize == sizeof(param->ConfigPhy)) {
                isValidSize = true;
                status = ConfigPhyMap(handle,
                                      &param->ConfigPhy.mapping,
                                      param->ConfigPhy.numDataLanes);
            };
            break;
        default:
            LOG_ERR("MAX9295: Bad parameter: Invalid command");
            isValidSize = true;
            status = NVMEDIA_STATUS_BAD_PARAMETER;
            break;
    }

    if (status != NVMEDIA_STATUS_OK) {
        LOG_ERR("MAX9295: %s failed", cmdString[parameterType]);
    }

    if (!isValidSize) {
        LOG_ERR("MAX9295: %s: Bad parameter: Invalid param size", cmdString[parameterType]);
        status = NVMEDIA_STATUS_BAD_PARAMETER;
    }

    return status;
}

NvMediaStatus
MAX9295DumpRegisters(
    DevBlkCDIDevice *handle)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    uint8_t data[3] = {0u};
    uint32_t i = 0u;

    if (handle == NULL){
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    for (i = 0u; i <= MAX9295_REG_MAX_ADDRESS; i++) {
        data[0] = i / 256;
        data[1] = i % 256;

        status = DevBlkCDIDeviceRead(handle,
                                     MAX9295_CDI_DEVICE_INDEX,
                                     MAX9295_NUM_ADDR_BYTES,
                                     &data[0],
                                     1,
                                     &data[2]);
        if (status != NVMEDIA_STATUS_OK) {
            LOG_ERR("MAX9295: Register 0x%04X I2C read failed with status %d", i, status);
            return status;
        }

        LOG_MSG("Max9295: 0x%02X%02X - 0x%02X\n", data[0], data[1], data[2]);
    }

    return status;
}

NvMediaStatus
MAX9295CheckPresence(
    DevBlkCDIDevice *handle)
{
    NvMediaStatus status = NVMEDIA_STATUS_OK;
    RevisionMAX9295 rev = CDI_MAX9295_INVALID_REV;
    uint8_t devID = 0u;

    if (handle == NULL) {
        LOG_ERR("MAX9295: Bad parameter: Null ptr");
        return NVMEDIA_STATUS_BAD_PARAMETER;
    }

    status = MAX9295ReadRegister(handle,
                                 MAX9295_CDI_DEVICE_INDEX,
                                 REG_DEV_ID_ADDR,
                                 1,
                                 &devID);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    if (devID != MAX9295A_DEV_ID) {
        LOG_ERR("MAX9295: Device ID mismatch. Expected: %u, Readval: %u", MAX9295A_DEV_ID, devID);
        return NVMEDIA_STATUS_ERROR;
    }

    status = GetRevId(handle,
                      &rev);
    if (status != NVMEDIA_STATUS_OK) {
        return status;
    }

    return NVMEDIA_STATUS_OK;
}

static DevBlkCDIDeviceDriver deviceDriver = {
    .deviceName = "Maxim 9295 Serializer",
    .regLength = MAX9295_NUM_ADDR_BYTES,
    .dataLength = MAX9295_NUM_DATA_BYTES,
    .DriverCreate = DriverCreate,
    .DriverDestroy = DriverDestroy,
};

DevBlkCDIDeviceDriver *
GetMAX9295Driver(
    void)
{
    return &deviceDriver;
}
