fbdev: msm: Add snapshot of display framebuffer driver
This is snapshot of the framebuffer driver as of msm-3.18
commit 2166ef69044c ("msm: mdss: Signal outstanding
fences when panel is dead").
Change-Id: I558bb25cbb26ff1ca2b4f79a0e98162bb1992241
Signed-off-by: Sachin Bhayare <sachin.bhayare@codeaurora.org>
diff --git a/drivers/video/fbdev/msm/dsi_v2.c b/drivers/video/fbdev/msm/dsi_v2.c
new file mode 100644
index 0000000..92d512a
--- /dev/null
+++ b/drivers/video/fbdev/msm/dsi_v2.c
@@ -0,0 +1,619 @@
+/* Copyright (c) 2012-2015, 2017-2018, 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) {
+ 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;
+}
+