/*
 * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#define  __NO_VERSION__

#include "os-interface.h"
#include "nv-linux.h"

#include "os_dsi_panel_props.h"

int bl_name_len;

static u32 *dsi_read_prop_array
(
    const struct device_node *np,
    struct property *prop,
    u32 *array_size
)
{
    u32 *val_array = NULL;
    u32 count = 0;
    u32 i = 0;
    int ret = 0;

    if (!prop)
        return NULL;

#if defined(NV_OF_PROPERTY_COUNT_ELEMS_OF_SIZE_PRESENT)
    count = of_property_count_elems_of_size(np, prop->name, sizeof(u32));
#else
    nv_printf(NV_DBG_ERRORS, "NVRM: dsi_read_prop_array, of_property_count_elems_of_size not present\n");
    return ERR_PTR(-ENOSYS);
#endif

    if (count > 0)
    {
        NV_KMALLOC(val_array, sizeof(u32) * count);
        if (val_array == NULL)
        {
            nv_printf(NV_DBG_ERRORS, "NVRM: dsi_read_prop_array, failed to allocate memory for values of DSI property %s", prop->name);
            return ERR_PTR(-ENOMEM);
        }
    }
    else
    {
        nv_printf(NV_DBG_ERRORS, "NVRM: dsi_read_prop_array, failed to get elements count in property %s\n", prop->name);
        return ERR_PTR(-ENOSYS);
    }

#if defined(NV_OF_PROPERTY_READ_VARIABLE_U32_ARRAY_PRESENT)
    ret = of_property_read_variable_u32_array(np, prop->name,
            val_array, 0, count);
#else
    nv_printf(NV_DBG_ERRORS, "NVRM: dsi_read_prop_array, of_property_read_variable_u32_array not present\n");
    ret = -ENOSYS;
#endif
    if (IS_ERR(&ret))
    {
        nv_printf(NV_DBG_ERRORS, "NVRM: dsi_read_prop_array, failed to read property %s", prop->name);
        NV_KFREE(val_array, sizeof(u32) * count);
        val_array = NULL;
        return ERR_PTR(ret);
    }

    *array_size = count;

    return val_array;
}

static int dsi_get_panel_timings(struct device_node *np_panel, DSI_PANEL_INFO *panelInfo)
{
    struct device_node *np = NULL;
    NvU32 temp;
    DSITIMINGS *modes = &panelInfo->dsiTimings;

    // Get Panel Node from active-panel phandle
    np = of_parse_phandle(np_panel, "nvidia,panel-timings", 0);
    if (!np) {
            nv_printf(NV_DBG_ERRORS, "NVRM: could not find panel timings node for DSI Panel\n");
            return -ENOENT;
    }

    if (!of_property_read_u32(np, "clock-frequency", &temp)) {
            modes->pixelClkRate = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "hsync-len", &temp)) {
            modes->hSyncWidth = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "vsync-len", &temp)) {
            modes->vSyncWidth = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "hback-porch", &temp)) {
            modes->hBackPorch = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "vback-porch", &temp)) {
            modes->vBackPorch = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "hactive", &temp)) {
            modes->hActive = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "vactive", &temp)) {
            modes->vActive = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "hfront-porch", &temp)) {
            modes->hFrontPorch = temp;
        } else {
            goto parse_mode_timings_fail;
        }
        if (!of_property_read_u32(np, "vfront-porch", &temp)) {
            modes->vFrontPorch = temp;
        } else {
            goto parse_mode_timings_fail;
        }

    of_node_put(np);
    return 0U;

parse_mode_timings_fail:
    nv_printf(NV_DBG_ERRORS, "NVRM: One of the mode timings is missing in DSI Panel mode-timings!\n");
    of_node_put(np);
    return -ENOENT;
}

static int dsi_get_panel_gpio(struct device_node *node, DSI_PANEL_INFO *panel)
{
    int count;
    char *label = NULL;

    // If gpios are already populated, just return
    if (panel->panel_gpio_populated)
         return 0;

    if (!node) {
         nv_printf(NV_DBG_ERRORS, "NVRM: DSI Panel node not available\n");
         return -ENOENT;
    }

#if defined(NV_OF_GET_NAME_GPIO_PRESENT)
    panel->panel_gpio[DSI_GPIO_LCD_RESET] =
        of_get_named_gpio(node, "nvidia,panel-rst-gpio", 0);

    panel->panel_gpio[DSI_GPIO_PANEL_EN] =
        of_get_named_gpio(node, "nvidia,panel-en-gpio", 0);

    panel->panel_gpio[DSI_GPIO_PANEL_EN_1] =
        of_get_named_gpio(node, "nvidia,panel-en-1-gpio", 0);

    panel->panel_gpio[DSI_GPIO_BL_ENABLE] =
        of_get_named_gpio(node, "nvidia,panel-bl-en-gpio", 0);

    panel->panel_gpio[DSI_GPIO_BL_PWM] =
        of_get_named_gpio(node, "nvidia,panel-bl-pwm-gpio", 0);

    panel->panel_gpio[DSI_GPIO_TE] =
        of_get_named_gpio(node, "nvidia,te-gpio", 0);

    panel->panel_gpio[DSI_GPIO_AVDD_AVEE_EN] =
        of_get_named_gpio(node, "nvidia,avdd-avee-en-gpio", 0);

    panel->panel_gpio[DSI_GPIO_VDD_1V8_LCD_EN] =
        of_get_named_gpio(node, "nvidia,vdd-1v8-lcd-en-gpio", 0);

    panel->panel_gpio[DSI_GPIO_BRIDGE_EN_0] =
        of_get_named_gpio(node, "nvidia,panel-bridge-en-0-gpio", 0);

    panel->panel_gpio[DSI_GPIO_BRIDGE_EN_1] =
        of_get_named_gpio(node, "nvidia,panel-bridge-en-1-gpio", 0);

    panel->panel_gpio[DSI_GPIO_BRIDGE_REFCLK_EN] =
        of_get_named_gpio(node, "nvidia,panel-bridge-refclk-en-gpio", 0);


    for (count = 0; count < DSI_N_GPIO_PANEL; count++) {
        if (gpio_is_valid(panel->panel_gpio[count])) {
            switch (count) {
            case DSI_GPIO_LCD_RESET:
                label = "dsi-panel-reset";
                break;
            case DSI_GPIO_PANEL_EN:
                label = "dsi-panel-en";
                break;
            case DSI_GPIO_PANEL_EN_1:
                label = "dsi-panel-en-1";
                break;
            case DSI_GPIO_BL_ENABLE:
                label = "dsi-panel-bl-enable";
                break;
            case DSI_GPIO_BL_PWM:
                label = "dsi-panel-pwm";
                break;
            case DSI_GPIO_TE:
                if (panel->dsiEnVRR != NV_TRUE) {
                    panel->panel_gpio[count] = -1;
                } else {
                    label = "dsi-panel-te";
                    panel->dsiVrrPanelSupportsTe = NV_TRUE;
                }
                break;
            case DSI_GPIO_AVDD_AVEE_EN:
                label = "dsi-panel-avdd-avee-en";
                break;
            case DSI_GPIO_VDD_1V8_LCD_EN:
                label = "dsi-panel-vdd-1v8-lcd-en";
                break;
            case DSI_GPIO_BRIDGE_EN_0:
                label = "dsi-panel-bridge-en-0";
                break;
            case DSI_GPIO_BRIDGE_EN_1:
                label = "dsi-panel-bridge-en-1";
                break;
            case DSI_GPIO_BRIDGE_REFCLK_EN:
                label = "dsi-panel-bridge-refclk-en";
                break;
            default:
                nv_printf(NV_DBG_INFO, "NVRM: DSI Panel invalid gpio entry at index %d\n", count);
            }
            if (label) {
                gpio_request(panel->panel_gpio[count], label);
                label = NULL;
            }
        }
    }

    panel->panel_gpio_populated = true;
    return 0U;
#else
    return -EINVAL;
#endif
}

static int dsi_parse_pps_data
(
    const struct device_node *node,
    struct property *prop,
    NvU32 *pps
)
{
    __be32 *prop_val_ptr;
    u32 count = 0;

    if (!prop)
        return -ENOENT;

    prop_val_ptr = prop->value;

#define PPS_COUNT 32
    for (count = 0; count < PPS_COUNT; count++) {
        pps[count] = be32_to_cpu(*prop_val_ptr++);
    }
#undef PPS_COUNT

    return 0U;
}

static int parse_dsi_properties(const struct device_node *np_dsi, DSI_PANEL_INFO *dsi)
{
    u32 temp;
    int ret = 0;
    const __be32 *p;
    struct property *prop;
    struct device_node *np_dsi_panel;

    // Get Panel Node from active-panel phandle
    np_dsi_panel = of_parse_phandle(np_dsi, "nvidia,active-panel", 0);
    if (np_dsi_panel == NULL)
    {
        nv_printf(NV_DBG_ERRORS, "NVRM: None of the dsi panel nodes enabled in DT!\n");
        return -EINVAL;
    }

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,enable-hs-clk-in-lp-mode", &temp))
        dsi->enable_hs_clock_on_lp_cmd_mode = (u8)temp;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,set-max-dsi-timeout"))
        dsi->set_max_timeout = true;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,use-legacy-dphy-core"))
        dsi->use_legacy_dphy_core = true;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-refresh-rate-adj", &temp))
        dsi->refresh_rate_adj = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-n-data-lanes", &temp))
        dsi->n_data_lanes = (u8)temp;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,swap-data-lane-polarity"))
        dsi->swap_data_lane_polarity = true;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,swap-clock-lane-polarity"))
        dsi->swap_clock_lane_polarity = true;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,reverse-clock-polarity"))
        dsi->reverse_clock_polarity = true;

    if (!of_property_read_u32_array(np_dsi_panel,
             "nvidia,lane-xbar-ctrl",
             dsi->lane_xbar_ctrl, dsi->n_data_lanes))
        dsi->lane_xbar_exists = true;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-phy-type", &temp))
    {
        dsi->dsiPhyType = (u8)temp;
        if ((temp != DSI_DPHY) &&
            (temp != DSI_CPHY))
        {
            nv_printf(NV_DBG_ERRORS,"NVRM: invalid dsi phy type 0x%x\n", temp);
            ret = -EINVAL;
            goto parse_dsi_settings_fail;
        }
    }

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,cphy-data-scrambling"))
        dsi->en_data_scrambling = true;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-video-burst-mode", &temp))
    {
        dsi->video_burst_mode = (u8)temp;
        if ((temp != DSI_VIDEO_NON_BURST_MODE) &&
            (temp != DSI_VIDEO_NON_BURST_MODE_WITH_SYNC_END) &&
            (temp != DSI_VIDEO_BURST_MODE_LOWEST_SPEED) &&
            (temp != DSI_VIDEO_BURST_MODE_LOW_SPEED) &&
            (temp != DSI_VIDEO_BURST_MODE_MEDIUM_SPEED) &&
            (temp != DSI_VIDEO_BURST_MODE_FAST_SPEED) &&
            (temp != DSI_VIDEO_BURST_MODE_FASTEST_SPEED))
        {
            nv_printf(NV_DBG_ERRORS,"NVRM: invalid dsi video burst mode\n");
            ret = -EINVAL;
            goto parse_dsi_settings_fail;
        }
    }
    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-pixel-format", &temp))
    {
        dsi->pixel_format = (u8)temp;
        if ((temp != DSI_PIXEL_FORMAT_16BIT_P) &&
            (temp != DSI_PIXEL_FORMAT_18BIT_P) &&
            (temp != DSI_PIXEL_FORMAT_18BIT_NP) &&
            (temp != DSI_PIXEL_FORMAT_24BIT_P) &&
            (temp != DSI_PIXEL_FORMAT_30BIT_P) &&
            (temp != DSI_PIXEL_FORMAT_36BIT_P) &&
            (temp != DSI_PIXEL_FORMAT_8BIT_DSC) &&
            (temp != DSI_PIXEL_FORMAT_10BIT_DSC) &&
            (temp != DSI_PIXEL_FORMAT_12BIT_DSC) &&
            (temp != DSI_PIXEL_FORMAT_16BIT_DSC))
        {
            nv_printf(NV_DBG_ERRORS,"NVRM: invalid dsi pixel format\n");
            ret = -EINVAL;
            goto parse_dsi_settings_fail;
        }
    }
    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-refresh-rate", &temp))
        dsi->refresh_rate = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-rated-refresh-rate", &temp))
        dsi->rated_refresh_rate = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-virtual-channel", &temp))
    {
        dsi->virtual_channel = (u8)temp;
        if ((temp != DSI_VIRTUAL_CHANNEL_0) &&
            (temp != DSI_VIRTUAL_CHANNEL_1) &&
            (temp != DSI_VIRTUAL_CHANNEL_2) &&
            (temp != DSI_VIRTUAL_CHANNEL_3))
        {
            nv_printf(NV_DBG_ERRORS,"NVRM: invalid dsi virtual channel\n");
            ret = -EINVAL;
            goto parse_dsi_settings_fail;
        }
    }

    if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-instance", &temp))
        dsi->dsi_instance = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-panel-reset", &temp))
        dsi->panel_reset = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-te-polarity-low", &temp))
        dsi->te_polarity_low = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-lp00-pre-panel-wakeup", &temp))
        dsi->lp00_pre_panel_wakeup = (u8)temp;

    if (of_find_property(np_dsi_panel,
        "nvidia,dsi-bl-name", &bl_name_len))
    {
        NV_KMALLOC(dsi->bl_name, sizeof(u8) * bl_name_len);
        if (!of_property_read_string(np_dsi_panel,
            "nvidia,dsi-bl-name",
            (const char **)&dsi->bl_name)) {
        } else {
            nv_printf(NV_DBG_ERRORS, "NVRM: dsi error parsing bl name\n");
            NV_KFREE(dsi->bl_name, sizeof(u8) * bl_name_len);
        }
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-ganged-type", &temp)) {
        dsi->ganged_type = (u8)temp;
        /* Set pixel width to 1 by default for even-odd split */
        if (dsi->ganged_type == DSI_GANGED_SYMMETRIC_EVEN_ODD)
            dsi->even_odd_split_width = 1;
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-even-odd-pixel-width", &temp))
        dsi->even_odd_split_width = temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-ganged-overlap", &temp)) {
        dsi->ganged_overlap = (u16)temp;
        if (!dsi->ganged_type)
            nv_printf(NV_DBG_ERRORS, "NVRM: specified ganged overlap, but no ganged type\n");
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-ganged-swap-links", &temp)) {
        dsi->ganged_swap_links = (bool)temp;
        if (!dsi->ganged_type)
            nv_printf(NV_DBG_ERRORS, "NVRM: specified ganged swapped links, but no ganged type\n");
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-ganged-write-to-all-links", &temp)) {
        dsi->ganged_write_to_all_links = (bool)temp;
        if (!dsi->ganged_type)
            nv_printf(NV_DBG_ERRORS, "NVRM: specified ganged write to all links, but no ganged type\n");
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-split-link-type", &temp))
        dsi->split_link_type = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-suspend-aggr", &temp))
        dsi->suspend_aggr = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-edp-bridge", &temp))
        dsi->dsi2edp_bridge_enable = (bool)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-lvds-bridge", &temp))
        dsi->dsi2lvds_bridge_enable = (bool)temp;

    of_property_for_each_u32(np_dsi_panel, "nvidia,dsi-dpd-pads", prop, p, temp)
        dsi->dpd_dsi_pads |= (u32)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-power-saving-suspend", &temp))
        dsi->power_saving_suspend = (bool)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-ulpm-not-support", &temp))
        dsi->ulpm_not_supported = (bool)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-video-data-type", &temp)) {
        dsi->video_data_type = (u8)temp;
        if ((temp != DSI_VIDEO_TYPE_VIDEO_MODE) &&
            (temp != DSI_VIDEO_TYPE_COMMAND_MODE))
        {
            nv_printf(NV_DBG_ERRORS, "NVRM: invalid dsi video data type\n");
            ret = -EINVAL;
            goto parse_dsi_settings_fail;
        }
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-video-clock-mode", &temp)) {
        dsi->video_clock_mode = (u8)temp;
        if ((temp != DSI_VIDEO_CLOCK_CONTINUOUS) &&
           (temp != DSI_VIDEO_CLOCK_TX_ONLY))
        {
            nv_printf(NV_DBG_ERRORS, "NVRM: invalid dsi video clk mode\n");
            ret = -EINVAL;
            goto parse_dsi_settings_fail;
        }
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,enable-vrr", &temp))
        dsi->dsiEnVRR = (u8)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,vrr-force-set-te-pin", &temp))
        dsi->dsiForceSetTePin = (u8)temp;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,send-init-cmds-early"))
        dsi->sendInitCmdsEarly = true;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-n-init-cmd", &temp)) {
        dsi->n_init_cmd = (u16)temp;
    }
    if (dsi->n_init_cmd > 0) {
        dsi->dsi_init_cmd_array = dsi_read_prop_array(np_dsi_panel,
                of_find_property(np_dsi_panel, "nvidia,dsi-init-cmd", NULL),
                &dsi->init_cmd_array_size);
    }
    if (dsi->n_init_cmd &&
        IS_ERR_OR_NULL(dsi->dsi_init_cmd_array)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: DSI init cmd parsing from DT failed\n");
        ret = -EINVAL;
        goto parse_dsi_settings_fail;
    };

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-n-postvideo-cmd", &temp)) {
        dsi->n_postvideo_cmd = (u16)temp;
    }
    if (dsi->n_postvideo_cmd > 0) {
        dsi->dsi_postvideo_cmd_array = dsi_read_prop_array(np_dsi_panel,
                of_find_property(np_dsi_panel, "nvidia,dsi-postvideo-cmd", NULL),
                &dsi->postvideo_cmd_array_size);
    }
    if (dsi->n_postvideo_cmd &&
        IS_ERR_OR_NULL(dsi->dsi_postvideo_cmd_array)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: DSI postvideo cmd parsing from DT failed\n");
        goto parse_dsi_settings_fail;
    };

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-n-suspend-cmd", &temp)) {
        dsi->n_suspend_cmd = (u16)temp;
    }
    if (dsi->n_suspend_cmd > 0) {
    dsi->dsi_suspend_cmd_array = dsi_read_prop_array(np_dsi_panel,
                of_find_property(np_dsi_panel, "nvidia,dsi-suspend-cmd", NULL),
                &dsi->suspend_cmd_array_size);
    }
    if (dsi->n_suspend_cmd &&
        IS_ERR_OR_NULL(dsi->dsi_suspend_cmd_array)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: DSI suspend cmd parsing from DT failed\n");
        ret = -EINVAL;
        goto parse_dsi_settings_fail;
    };

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-n-early-suspend-cmd", &temp)) {
        dsi->n_early_suspend_cmd = (u16)temp;
    }
    if (dsi->n_early_suspend_cmd > 0) {
    dsi->dsi_early_suspend_cmd_array = dsi_read_prop_array(np_dsi_panel,
                of_find_property(np_dsi_panel, "nvidia,dsi-early-suspend-cmd", NULL),
                &dsi->early_suspend_cmd_array_size);
    }
    if (dsi->n_early_suspend_cmd &&
        IS_ERR_OR_NULL(dsi->dsi_early_suspend_cmd_array)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: DSI early suspend cmd parsing from DT failed\n");
        ret = -EINVAL;
        goto parse_dsi_settings_fail;
    };

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-suspend-stop-stream-late", &temp)) {
        dsi->suspend_stop_stream_late = (bool)temp;
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-n-late-resume-cmd", &temp)) {
        dsi->n_late_resume_cmd = (u16)temp;
    }
    if (dsi->n_late_resume_cmd > 0) {
    dsi->dsi_late_resume_cmd_array = dsi_read_prop_array(np_dsi_panel,
                of_find_property(np_dsi_panel, "nvidia,dsi-late-resume-cmd", NULL),
                &dsi->late_resume_cmd_array_size);
    }
    if (dsi->n_late_resume_cmd &&
        IS_ERR_OR_NULL(dsi->dsi_late_resume_cmd_array)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: DSI late resume cmd parsing from DT failed\n");
        ret = -EINVAL;
        goto parse_dsi_settings_fail;
    };

    dsi->pktSeq_array = dsi_read_prop_array(np_dsi_panel,
                of_find_property(np_dsi_panel, "nvidia,dsi-pkt-seq", NULL),
                &dsi->pktSeq_array_size);
    if (IS_ERR(dsi->pktSeq_array)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: DSI packet seq parsing from DT fail\n");
        ret = -EINVAL;
        goto parse_dsi_settings_fail;
    }

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-hsdexit", &temp))
        dsi->phyTimingNs.t_hsdexit_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-hstrail", &temp))
        dsi->phyTimingNs.t_hstrail_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-datzero", &temp))
        dsi->phyTimingNs.t_datzero_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-phy-hsprepare", &temp))
        dsi->phyTimingNs.t_hsprepare_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-phy-hsprebegin", &temp))
        dsi->phyTimingNs.t_hsprebegin_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-phy-hspost", &temp))
        dsi->phyTimingNs.t_hspost_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-phy-clktrail", &temp))
        dsi->phyTimingNs.t_clktrail_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-clkpost", &temp))
        dsi->phyTimingNs.t_clkpost_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-clkzero", &temp))
        dsi->phyTimingNs.t_clkzero_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-tlpx", &temp))
        dsi->phyTimingNs.t_tlpx_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsi-phy-clkprepare", &temp))
        dsi->phyTimingNs.t_clkprepare_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-clkpre", &temp))
        dsi->phyTimingNs.t_clkpre_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-wakeup", &temp))
        dsi->phyTimingNs.t_wakeup_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-taget", &temp))
        dsi->phyTimingNs.t_taget_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-tasure", &temp))
        dsi->phyTimingNs.t_tasure_ns = (u16)temp;

    if (!of_property_read_u32(np_dsi_panel,
        "nvidia,dsi-phy-tago", &temp))
        dsi->phyTimingNs.t_tago_ns = (u16)temp;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,enable-link-compression"))
        dsi->dsiDscEnable = true;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,enable-dual-dsc"))
        dsi->dsiDscEnDualDsc = true;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,enable-block-pred"))
        dsi->dsiDscEnBlockPrediction = true;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,slice-height", &temp))
        dsi->dsiDscSliceHeight = (u32)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,num-of-slices", &temp))
        dsi->dsiDscNumSlices = (u32)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,comp-rate", &temp))
        dsi->dsiDscBpp = (u32)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,version-major", &temp))
        dsi->dsiDscDecoderMajorVersion = (u32)temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,version-minor", &temp))
        dsi->dsiDscDecoderMinorVersion = (u32)temp;

    if (of_property_read_bool(np_dsi_panel,
            "nvidia,use-custom-pps")) {
        dsi->dsiDscUseCustomPPS = true;

        ret = dsi_parse_pps_data(np_dsi_panel,
            of_find_property(np_dsi_panel,
            "nvidia,custom-pps-data", NULL),
            dsi->dsiDscCustomPPSData);

        if (ret != NV_OK) {
            nv_printf(NV_DBG_ERRORS, "NVRM: Parsing DSI Panel Custom PPS data failed\n");
            goto parse_dsi_settings_fail;
        }
    }

    if (of_property_read_bool(np_dsi, "nvidia,dsi-csi-loopback"))
        dsi->dsi_csi_loopback = 1;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,vpll0-rate-hz", &temp))
        dsi->vpll0_rate_hz = temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsipll-vco-rate-hz", &temp))
        dsi->dsipll_vco_rate_hz = temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsipll-clkouta-rate-hz", &temp))
        dsi->dsipll_clkouta_rate_hz = temp;

    if (!of_property_read_u32(np_dsi_panel,
            "nvidia,dsipll-clkoutpn-rate-hz", &temp))
        dsi->dsipll_clkoutpn_rate_hz = temp;

    ret = dsi_get_panel_timings(np_dsi_panel, dsi);
    if (ret != NV_OK) {
        nv_printf(NV_DBG_ERRORS, "NVRM: Parsing DSI Panel Timings failed\n");
        goto parse_dsi_settings_fail;
    }

    ret = dsi_get_panel_gpio(np_dsi_panel, dsi);
    if (ret != NV_OK) {
        nv_printf(NV_DBG_ERRORS, "NVRM: Parsing DSI Panel GPIOs failed\n");
        goto parse_dsi_settings_fail;
    }

parse_dsi_settings_fail:
    return ret;
}

NvBool
nv_dsi_is_panel_connected
(
    nv_state_t      *nv
)
{
    nv_linux_state_t *nvl            = NV_GET_NVL_FROM_NV_STATE(nv);
    struct device_node *np_dsi       = NULL;
    struct device_node *np_dsi_panel = NULL;
    NvBool ret                       = NV_TRUE;

    np_dsi = of_get_child_by_name(nvl->dev->of_node, "dsi");

    if (np_dsi && !of_device_is_available(np_dsi)) {
        ret = NV_FALSE;
        goto fail;
    }

    np_dsi_panel = of_parse_phandle(np_dsi, "nvidia,active-panel", 0);
    if (np_dsi_panel == NULL)
    {
        ret = NV_FALSE;
    }

fail:
    of_node_put(np_dsi_panel);
    of_node_put(np_dsi);
    return ret;
}

NV_STATUS
nv_dsi_parse_panel_props
(
    nv_state_t      *nv,
    void            *dsiPanelInfo
)
{
    int ret                     = NV_OK;
    struct device_node *np_dsi  = NULL;
    nv_linux_state_t *nvl       = NV_GET_NVL_FROM_NV_STATE(nv);

    np_dsi = of_get_child_by_name(nvl->dev->of_node, "dsi");

    if (np_dsi && !of_device_is_available(np_dsi)) {
        nv_printf(NV_DBG_ERRORS, "NVRM: dsi node not enabled in DT\n");
        of_node_put(np_dsi);
        return NV_ERR_NOT_SUPPORTED;
    }

    ret = parse_dsi_properties(np_dsi, (DSI_PANEL_INFO *)dsiPanelInfo);

    return ret;
}

NV_STATUS
nv_dsi_panel_enable
(
    nv_state_t      *nv,
    void            *dsiPanelInfo
)
{
    int ret = NV_OK;
    DSI_PANEL_INFO *panelInfo = dsiPanelInfo;

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_VDD_1V8_LCD_EN])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_VDD_1V8_LCD_EN], 1);
    }

    mdelay(10); //Required 1ms delay

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_AVDD_AVEE_EN])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_AVDD_AVEE_EN], 1);
    }

    mdelay(20); //Required 10ms delay

    // If backlight enable gpio is specified, set it to output direction and pull high
    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_BL_ENABLE])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_BL_ENABLE], 1);
    }

    mdelay(10);

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_PANEL_EN])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_PANEL_EN], 1);
    }

    mdelay(20); // Requied 10ms

    return ret;
}

NV_STATUS
nv_dsi_panel_reset
(
    nv_state_t      *nv,
    void            *dsiPanelInfo
)
{
    int ret = NV_OK;
    int en_panel_rst = -1;
    DSI_PANEL_INFO *panelInfo = dsiPanelInfo;

    // Assert and deassert Panel reset GPIO
    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_LCD_RESET])) {
        en_panel_rst = panelInfo->panel_gpio[DSI_GPIO_LCD_RESET];
    } else {
        nv_printf(NV_DBG_ERRORS, "DSI Panel reset gpio invalid\n");
        goto fail;
    }

    ret = gpio_direction_output(en_panel_rst, 1);
    if (ret < 0) {
        nv_printf(NV_DBG_ERRORS, "Deasserting DSI panel reset gpio failed\n");
        goto fail;
    }

    mdelay(10);

    ret = gpio_direction_output(en_panel_rst, 0);
    if (ret < 0) {
        nv_printf(NV_DBG_ERRORS, "Asserting DSI panel reset gpio failed\n");
        goto fail;
    }

    mdelay(10);

    ret = gpio_direction_output(en_panel_rst, 1);
    if (ret < 0) {
        nv_printf(NV_DBG_ERRORS, "Deasserting Dsi panel reset gpio after asserting failed\n");
        goto fail;
    }

fail:
    return ret;
}

void nv_dsi_panel_disable
(
    nv_state_t      *nv,
    void            *dsiPanelInfo
)
{
    DSI_PANEL_INFO *panelInfo = dsiPanelInfo;

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_BL_ENABLE])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_BL_ENABLE], 0);
    }

    mdelay(10);

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_PANEL_EN])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_PANEL_EN], 0);
    }

    // Assert Panel reset GPIO
    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_LCD_RESET])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_LCD_RESET], 0);
    }

    mdelay(20);

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_AVDD_AVEE_EN])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_AVDD_AVEE_EN], 0);
    }

    mdelay(10);

    if (gpio_is_valid(panelInfo->panel_gpio[DSI_GPIO_VDD_1V8_LCD_EN])) {
        gpio_direction_output(panelInfo->panel_gpio[DSI_GPIO_VDD_1V8_LCD_EN], 0);
    }
}

void nv_dsi_panel_cleanup
(
    nv_state_t      *nv,
    void            *dsiPanelInfo
)
{
    int count;
    DSI_PANEL_INFO *panelInfo = dsiPanelInfo;

    if (!IS_ERR_OR_NULL(panelInfo->dsi_init_cmd_array))
        NV_KFREE(panelInfo->dsi_init_cmd_array, sizeof(u32) * panelInfo->init_cmd_array_size);

    if (!IS_ERR_OR_NULL(panelInfo->dsi_early_suspend_cmd_array))
        NV_KFREE(panelInfo->dsi_early_suspend_cmd_array, sizeof(u32) * panelInfo->early_suspend_cmd_array_size);

    if (!IS_ERR_OR_NULL(panelInfo->dsi_late_resume_cmd_array))
        NV_KFREE(panelInfo->dsi_late_resume_cmd_array, sizeof(u32) * panelInfo->late_resume_cmd_array_size);

     if (!IS_ERR_OR_NULL(panelInfo->dsi_postvideo_cmd_array))
        NV_KFREE(panelInfo->dsi_postvideo_cmd_array, sizeof(u32) * panelInfo->postvideo_cmd_array_size);

    if (!IS_ERR_OR_NULL(panelInfo->dsi_suspend_cmd_array))
        NV_KFREE(panelInfo->dsi_suspend_cmd_array, sizeof(u32) * panelInfo->suspend_cmd_array_size);

    if (!IS_ERR_OR_NULL(panelInfo->pktSeq_array))
        NV_KFREE(panelInfo->pktSeq_array, sizeof(u32) * panelInfo->pktSeq_array_size);

    if (panelInfo->bl_name != NULL) {
        NV_KFREE(panelInfo->bl_name, sizeof(u8) * bl_name_len);
    }

    for (count = 0; count < DSI_N_GPIO_PANEL; count++) {
        if (gpio_is_valid(panelInfo->panel_gpio[count])) {
            gpio_free(panelInfo->panel_gpio[count]);
        }
    }
    panelInfo->panel_gpio_populated = false;
}
