/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>

#include "dsi_v2.h"

static struct dsi_interface dsi_intf;

static int dsi_off(struct mdss_panel_data *pdata)
{
	int rc = 0;

	pr_debug("turn off dsi controller\n");
	if (dsi_intf.off)
		rc = dsi_intf.off(pdata);

	if (rc) {
		pr_err("mdss_dsi_off DSI failed %d\n", rc);
		return rc;
	}
	return rc;
}

static int dsi_on(struct mdss_panel_data *pdata)
{
	int rc = 0;

	pr_debug("dsi_on DSI controller on\n");
	if (dsi_intf.on)
		rc = dsi_intf.on(pdata);

	if (rc) {
		pr_err("mdss_dsi_on DSI failed %d\n", rc);
		return rc;
	}
	return rc;
}

static int dsi_update_pconfig(struct mdss_panel_data *pdata,
				int mode)
{
	int ret = 0;
	struct mdss_panel_info *pinfo = &pdata->panel_info;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
	if (!pdata)
		return -ENODEV;
	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);

	if (mode == DSI_CMD_MODE) {
		pinfo->mipi.mode = DSI_CMD_MODE;
		pinfo->type = MIPI_CMD_PANEL;
		pinfo->mipi.vsync_enable = 1;
		pinfo->mipi.hw_vsync_mode = 1;
	} else {
		pinfo->mipi.mode = DSI_VIDEO_MODE;
		pinfo->type = MIPI_VIDEO_PANEL;
		pinfo->mipi.vsync_enable = 0;
		pinfo->mipi.hw_vsync_mode = 0;
	}

	ctrl_pdata->panel_mode = pinfo->mipi.mode;
	mdss_panel_get_dst_fmt(pinfo->bpp, pinfo->mipi.mode,
			pinfo->mipi.pixel_packing, &(pinfo->mipi.dst_format));
	pinfo->cont_splash_enabled = 0;

	return ret;
}

static int dsi_panel_handler(struct mdss_panel_data *pdata, int enable)
{
	int rc = 0;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;

	pr_debug("dsi_panel_handler enable=%d\n", enable);
	if (!pdata)
		return -ENODEV;
	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);

	if (enable &&
		(pdata->panel_info.panel_power_state == MDSS_PANEL_POWER_OFF)) {
		if (!pdata->panel_info.dynamic_switch_pending) {
			mdss_dsi_panel_reset(pdata, 1);
			rc = ctrl_pdata->on(pdata);
			if (rc)
				pr_err("dsi_panel_handler panel on failed %d\n",
									rc);
		}
		pdata->panel_info.panel_power_state = MDSS_PANEL_POWER_ON;
		if (pdata->panel_info.type == MIPI_CMD_PANEL)
			mdss_dsi_set_tear_on(ctrl_pdata);
	} else if (!enable &&
		(pdata->panel_info.panel_power_state == MDSS_PANEL_POWER_ON)) {
		msm_dsi_sw_reset();
		if (dsi_intf.op_mode_config)
			dsi_intf.op_mode_config(DSI_CMD_MODE, pdata);
		if (pdata->panel_info.dynamic_switch_pending) {
			pr_info("%s: switching to %s mode\n", __func__,
			(pdata->panel_info.mipi.mode ? "video" : "command"));
			if (pdata->panel_info.type == MIPI_CMD_PANEL) {
				ctrl_pdata->switch_mode(pdata, DSI_VIDEO_MODE);
			} else if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
				ctrl_pdata->switch_mode(pdata, DSI_CMD_MODE);
				mdss_dsi_set_tear_off(ctrl_pdata);
			}
		}
		pdata->panel_info.panel_power_state = MDSS_PANEL_POWER_OFF;
		if (!pdata->panel_info.dynamic_switch_pending) {
			rc = ctrl_pdata->off(pdata);
			mdss_dsi_panel_reset(pdata, 0);
		}
	}
	return rc;
}

static int dsi_splash_on(struct mdss_panel_data *pdata)
{
	int rc = 0;

	pr_debug("%s:\n", __func__);

	if (dsi_intf.cont_on)
		rc = dsi_intf.cont_on(pdata);

	if (rc) {
		pr_err("mdss_dsi_on DSI failed %d\n", rc);
		return rc;
	}
	return rc;
}

static int dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable)
{
	int rc = 0;

	pr_debug("%s:\n", __func__);

	if (dsi_intf.clk_ctrl)
		rc = dsi_intf.clk_ctrl(pdata, enable);

	return rc;
}

static int dsi_event_handler(struct mdss_panel_data *pdata,
				int event, void *arg)
{
	int rc = 0;

	if (!pdata) {
		pr_err("%s: Invalid input data\n", __func__);
		return -ENODEV;
	}

	switch (event) {
	case MDSS_EVENT_UNBLANK:
		rc = dsi_on(pdata);
		break;
	case MDSS_EVENT_BLANK:
		rc = dsi_off(pdata);
		break;
	case MDSS_EVENT_PANEL_ON:
		rc = dsi_panel_handler(pdata, 1);
		break;
	case MDSS_EVENT_PANEL_OFF:
		rc = dsi_panel_handler(pdata, 0);
		break;
	case MDSS_EVENT_CONT_SPLASH_BEGIN:
		rc = dsi_splash_on(pdata);
		break;
	case MDSS_EVENT_PANEL_CLK_CTRL:
		rc = dsi_clk_ctrl(pdata,
			(int)(((struct dsi_panel_clk_ctrl *)arg)->state));
		break;
	case MDSS_EVENT_DSI_UPDATE_PANEL_DATA:
		rc = dsi_update_pconfig(pdata, (int)(unsigned long) arg);
		break;
	default:
		pr_debug("%s: unhandled event=%d\n", __func__, event);
		break;
	}
	return rc;
}

static int dsi_parse_gpio(struct platform_device *pdev,
				struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
	struct device_node *np = pdev->dev.of_node;

	ctrl_pdata->disp_en_gpio = of_get_named_gpio(np,
		"qcom,platform-enable-gpio", 0);

	if (!gpio_is_valid(ctrl_pdata->disp_en_gpio))
		pr_err("%s:%d, Disp_en gpio not specified\n",
						__func__, __LINE__);

	ctrl_pdata->rst_gpio = of_get_named_gpio(np,
					"qcom,platform-reset-gpio", 0);
	if (!gpio_is_valid(ctrl_pdata->rst_gpio))
		pr_err("%s:%d, reset gpio not specified\n",
						__func__, __LINE__);

	ctrl_pdata->mode_gpio = -1;
	if (ctrl_pdata->panel_data.panel_info.mode_gpio_state !=
						MODE_GPIO_NOT_VALID) {
		ctrl_pdata->mode_gpio = of_get_named_gpio(np,
						"qcom,platform-mode-gpio", 0);
		if (!gpio_is_valid(ctrl_pdata->mode_gpio))
			pr_info("%s:%d, reset gpio not specified\n",
							__func__, __LINE__);
	}

	ctrl_pdata->bklt_en_gpio = of_get_named_gpio(np,
					"qcom,platform-bklight-en-gpio", 0);
	if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio))
		pr_err("%s:%d, bklt_en gpio not specified\n",
						__func__, __LINE__);

	return 0;
}

static void mdss_dsi_put_dt_vreg_data(struct device *dev,
	struct dss_module_power *module_power)
{
	if (!module_power) {
		pr_err("%s: invalid input\n", __func__);
		return;
	}

	if (module_power->vreg_config) {
		devm_kfree(dev, module_power->vreg_config);
		module_power->vreg_config = NULL;
	}
	module_power->num_vreg = 0;
}

static int mdss_dsi_get_dt_vreg_data(struct device *dev,
	struct dss_module_power *mp, enum dsi_pm_type module)
{
	int i = 0, rc = 0;
	u32 tmp = 0;
	struct device_node *of_node = NULL, *supply_node = NULL;
	const char *pm_supply_name = NULL;
	struct device_node *supply_root_node = NULL;

	if (!dev || !mp) {
		pr_err("%s: invalid input\n", __func__);
		rc = -EINVAL;
		return rc;
	}

	of_node = dev->of_node;

	mp->num_vreg = 0;
	pm_supply_name = __mdss_dsi_pm_supply_node_name(module);
	supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
	if (!supply_root_node) {
		pr_err("no supply entry present\n");
		goto novreg;
	}

	for_each_child_of_node(supply_root_node, supply_node) {
		mp->num_vreg++;
	}

	if (mp->num_vreg == 0) {
		pr_debug("%s: no vreg\n", __func__);
		goto novreg;
	} else {
		pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg);
	}

	mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
		mp->num_vreg, GFP_KERNEL);
	if (!mp->vreg_config) {
		pr_err("%s: can't alloc vreg mem\n", __func__);
		rc = -ENOMEM;
		goto error;
	}

	for_each_child_of_node(supply_root_node, supply_node) {
		const char *st = NULL;
		/* vreg-name */
		rc = of_property_read_string(supply_node,
			"qcom,supply-name", &st);
		if (rc) {
			pr_err("%s: error reading name. rc=%d\n",
				__func__, rc);
			goto error;
		}
		snprintf(mp->vreg_config[i].vreg_name,
			ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
		/* vreg-min-voltage */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-min-voltage", &tmp);
		if (rc) {
			pr_err("%s: error reading min volt. rc=%d\n",
				__func__, rc);
			goto error;
		}
		mp->vreg_config[i].min_voltage = tmp;

		/* vreg-max-voltage */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-max-voltage", &tmp);
		if (rc) {
			pr_err("%s: error reading max volt. rc=%d\n",
				__func__, rc);
			goto error;
		}
		mp->vreg_config[i].max_voltage = tmp;

		/* enable-load */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-enable-load", &tmp);
		if (rc) {
			pr_err("%s: error reading enable load. rc=%d\n",
				__func__, rc);
			goto error;
		}
		mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp;

		/* disable-load */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-disable-load", &tmp);
		if (rc) {
			pr_err("%s: error reading disable load. rc=%d\n",
				__func__, rc);
			goto error;
		}
		mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp;

		/* ulp-load */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-ulp-load", &tmp);
		if (rc)
			pr_warn("%s: error reading ulp load. rc=%d\n",
				__func__, rc);

		mp->vreg_config[i].load[DSS_REG_MODE_ULP] = (!rc ? tmp :
			mp->vreg_config[i].load[DSS_REG_MODE_ENABLE]);

		/* pre-sleep */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-pre-on-sleep", &tmp);
		if (rc) {
			pr_debug("%s: error reading supply pre sleep value. rc=%d\n",
				__func__, rc);
			rc = 0;
		} else {
			mp->vreg_config[i].pre_on_sleep = tmp;
		}

		rc = of_property_read_u32(supply_node,
			"qcom,supply-pre-off-sleep", &tmp);
		if (rc) {
			pr_debug("%s: error reading supply pre sleep value. rc=%d\n",
				__func__, rc);
			rc = 0;
		} else {
			mp->vreg_config[i].pre_off_sleep = tmp;
		}

		/* post-sleep */
		rc = of_property_read_u32(supply_node,
			"qcom,supply-post-on-sleep", &tmp);
		if (rc) {
			pr_debug("%s: error reading supply post sleep value. rc=%d\n",
				__func__, rc);
			rc = 0;
		} else {
			mp->vreg_config[i].post_on_sleep = tmp;
		}

		rc = of_property_read_u32(supply_node,
			"qcom,supply-post-off-sleep", &tmp);
		if (rc) {
			pr_debug("%s: error reading supply post sleep value. rc=%d\n",
				__func__, rc);
			rc = 0;
		} else {
			mp->vreg_config[i].post_off_sleep = tmp;
		}

		pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, ulp=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
			__func__,
			mp->vreg_config[i].vreg_name,
			mp->vreg_config[i].min_voltage,
			mp->vreg_config[i].max_voltage,
			mp->vreg_config[i].load[DSS_REG_MODE_ENABLE]
			mp->vreg_config[i].load[DSS_REG_MODE_DISABLE]
			mp->vreg_config[i].load[DSS_REG_MODE_ULP]
			mp->vreg_config[i].pre_on_sleep,
			mp->vreg_config[i].post_on_sleep,
			mp->vreg_config[i].pre_off_sleep,
			mp->vreg_config[i].post_off_sleep
			);
		++i;
	}

	return rc;

error:
	if (mp->vreg_config) {
		devm_kfree(dev, mp->vreg_config);
		mp->vreg_config = NULL;
	}
novreg:
	mp->num_vreg = 0;

	return rc;
}

static int dsi_parse_phy(struct platform_device *pdev,
				struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
	struct device_node *np = pdev->dev.of_node;
	int i, len;
	const char *data;
	struct mdss_dsi_phy_ctrl *phy_db
		= &(ctrl_pdata->panel_data.panel_info.mipi.dsi_phy_db);

	data = of_get_property(np, "qcom,platform-regulator-settings", &len);
	if ((!data) || (len != 6)) {
		pr_err("%s:%d, Unable to read Phy regulator settings",
			__func__, __LINE__);
		return -EINVAL;
	}
	for (i = 0; i < len; i++)
		phy_db->regulator[i] = data[i];

	data = of_get_property(np, "qcom,platform-strength-ctrl", &len);
	if ((!data) || (len != 2)) {
		pr_err("%s:%d, Unable to read Phy Strength ctrl settings",
			__func__, __LINE__);
		return -EINVAL;
	}
	phy_db->strength[0] = data[0];
	phy_db->strength[1] = data[1];

	data = of_get_property(np, "qcom,platform-bist-ctrl", &len);
	if ((!data) || (len != 6)) {
		pr_err("%s:%d, Unable to read Phy Bist Ctrl settings",
			__func__, __LINE__);
		return -EINVAL;
	}
	for (i = 0; i < len; i++)
		phy_db->bistctrl[i] = data[i];

	data = of_get_property(np, "qcom,platform-lane-config", &len);
	if ((!data) || (len != 30)) {
		pr_err("%s:%d, Unable to read Phy lane configure settings",
			__func__, __LINE__);
		return -EINVAL;
	}
	for (i = 0; i < len; i++)
		phy_db->lanecfg[i] = data[i];

	return 0;
}

void dsi_ctrl_config_deinit(struct platform_device *pdev,
				struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
	int i;

	for (i = DSI_MAX_PM - 1; i >= 0; i--) {
		mdss_dsi_put_dt_vreg_data(&pdev->dev,
			&ctrl_pdata->power_data[i]);
	}
}

int dsi_ctrl_config_init(struct platform_device *pdev,
				struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
	int rc = 0, i;

	for (i = 0; i < DSI_MAX_PM; i++) {
		rc = mdss_dsi_get_dt_vreg_data(&pdev->dev,
			&ctrl_pdata->power_data[i], i);
		if (rc) {
			DEV_ERR("%s: '%s' get_dt_vreg_data failed.rc=%d\n",
				__func__, __mdss_dsi_pm_name(i), rc);
			return rc;
		}
	}

	rc = dsi_parse_gpio(pdev, ctrl_pdata);
	if (rc) {
		pr_err("fail to parse panel GPIOs\n");
		return rc;
	}

	rc = dsi_parse_phy(pdev, ctrl_pdata);
	if (rc) {
		pr_err("fail to parse DSI PHY settings\n");
		return rc;
	}

	return 0;
}
int dsi_panel_device_register_v2(struct platform_device *dev,
				struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
	struct mipi_panel_info *mipi;
	int rc;
	u8 lanes = 0, bpp;
	u32 h_period, v_period;
	struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);

	h_period = ((pinfo->lcdc.h_pulse_width)
			+ (pinfo->lcdc.h_back_porch)
			+ (pinfo->xres)
			+ (pinfo->lcdc.h_front_porch));

	v_period = ((pinfo->lcdc.v_pulse_width)
			+ (pinfo->lcdc.v_back_porch)
			+ (pinfo->yres)
			+ (pinfo->lcdc.v_front_porch));

	mipi  = &pinfo->mipi;

	pinfo->type =
		((mipi->mode == DSI_VIDEO_MODE)
			? MIPI_VIDEO_PANEL : MIPI_CMD_PANEL);

	if (mipi->data_lane3)
		lanes += 1;
	if (mipi->data_lane2)
		lanes += 1;
	if (mipi->data_lane1)
		lanes += 1;
	if (mipi->data_lane0)
		lanes += 1;

	if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888)
		|| (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB888)
		|| (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB666_LOOSE))
		bpp = 3;
	else if ((mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565)
		|| (mipi->dst_format == DSI_VIDEO_DST_FORMAT_RGB565))
		bpp = 2;
	else
		bpp = 3; /* Default format set to RGB888 */

	if (pinfo->type == MIPI_VIDEO_PANEL &&
		!pinfo->clk_rate) {
		h_period += pinfo->lcdc.xres_pad;
		v_period += pinfo->lcdc.yres_pad;

		if (lanes > 0) {
			pinfo->clk_rate =
			((h_period * v_period * (mipi->frame_rate) * bpp * 8)
			   / lanes);
		} else {
			pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__);
			pinfo->clk_rate =
				(h_period * v_period
					 * (mipi->frame_rate) * bpp * 8);
		}
	}

	ctrl_pdata->panel_data.event_handler = dsi_event_handler;

	/*
	 * register in mdp driver
	 */
	rc = mdss_register_panel(dev, &(ctrl_pdata->panel_data));
	if (rc) {
		dev_err(&dev->dev, "unable to register MIPI DSI panel\n");
		return rc;
	}

	pr_debug("%s: Panal data initialized\n", __func__);
	return 0;
}

void dsi_register_interface(struct dsi_interface *intf)
{
	dsi_intf = *intf;
}

int dsi_buf_alloc(struct dsi_buf *dp, int size)
{
	dp->start = kzalloc(size, GFP_KERNEL);
	if (dp->start == NULL) {
		pr_err("%s:%u\n", __func__, __LINE__);
		return -ENOMEM;
	}

	dp->end = dp->start + size;
	dp->size = size;

	if ((int)dp->start & 0x07) {
		pr_err("%s: buf NOT 8 bytes aligned\n", __func__);
		return -EINVAL;
	}

	dp->data = dp->start;
	dp->len = 0;
	return 0;
}

