/* Copyright (c) 2018, 2020, 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 #include #include #include #include #include #include #include #include "mdss.h" #include "mdss_panel.h" #include "mdss_rgb.h" #include "mdss_debug.h" static struct mdss_rgb_data *mdss_rgb_res; static struct spi_device *spi_dev; static int mdss_rgb_spi_write(struct mdss_rgb_data *rgb_data, bool cmd, u8 data) { struct spi_transfer xfer = { 0 }; struct spi_message msg; u16 txbuf; if (cmd) txbuf = (1 << 8) | data; else txbuf = data; spi_message_init(&msg); xfer.tx_buf = &txbuf; xfer.bits_per_word = 9; xfer.len = sizeof(txbuf); spi_message_add_tail(&xfer, &msg); return spi_sync(rgb_data->spi, &msg); } int mdss_rgb_write_command(struct mdss_rgb_data *rgb_data, u8 cmd) { int rc; rc = mdss_rgb_spi_write(rgb_data, false, cmd); if (rc) pr_err("%s: spi write failed. cmd = 0x%x rc = %d\n", __func__, cmd, rc); return rc; } int mdss_rgb_write_data(struct mdss_rgb_data *rgb_data, u8 data) { int rc; rc = mdss_rgb_spi_write(rgb_data, true, data); if (rc) pr_err("%s: spi write failed. data = 0x%x rc = %d\n", __func__, data, rc); return rc; } int mdss_rgb_read_command(struct mdss_rgb_data *rgb_data, u8 cmd, u8 *data, u8 data_len) { int rc; rc = spi_write_then_read(spi_dev, &cmd, 1, data, data_len); if (rc) pr_err("%s: spi read failed. rc = %d\n", __func__, rc); return rc; } static int mdss_rgb_core_clk_init(struct platform_device *pdev, struct mdss_rgb_data *rgb_data) { struct device *dev = NULL; int rc = 0; dev = &pdev->dev; rgb_data->mdp_core_clk = devm_clk_get(dev, "mdp_core_clk"); if (IS_ERR(rgb_data->mdp_core_clk)) { rc = PTR_ERR(rgb_data->mdp_core_clk); pr_err("%s: Unable to get mdp core clk. rc=%d\n", __func__, rc); return rc; } rgb_data->ahb_clk = devm_clk_get(dev, "iface_clk"); if (IS_ERR(rgb_data->ahb_clk)) { rc = PTR_ERR(rgb_data->ahb_clk); pr_err("%s: Unable to get mdss ahb clk. rc=%d\n", __func__, rc); return rc; } rgb_data->axi_clk = devm_clk_get(dev, "bus_clk"); if (IS_ERR(rgb_data->axi_clk)) { rc = PTR_ERR(rgb_data->axi_clk); pr_err("%s: Unable to get axi bus clk. rc=%d\n", __func__, rc); return rc; } rgb_data->ext_byte0_clk = devm_clk_get(dev, "ext_byte0_clk"); if (IS_ERR(rgb_data->ext_byte0_clk)) { pr_debug("%s: unable to get byte0 clk rcg. rc=%d\n", __func__, rc); rgb_data->ext_byte0_clk = NULL; } rgb_data->ext_pixel0_clk = devm_clk_get(dev, "ext_pixel0_clk"); if (IS_ERR(rgb_data->ext_pixel0_clk)) { pr_debug("%s: unable to get pixel0 clk rcg. rc=%d\n", __func__, rc); rgb_data->ext_pixel0_clk = NULL; } rgb_data->mmss_misc_ahb_clk = devm_clk_get(dev, "core_mmss_clk"); if (IS_ERR(rgb_data->mmss_misc_ahb_clk)) { rgb_data->mmss_misc_ahb_clk = NULL; pr_debug("%s: Unable to get mmss misc ahb clk\n", __func__); } rgb_data->mnoc_clk = devm_clk_get(dev, "mnoc_clk"); if (IS_ERR(rgb_data->mnoc_clk)) { pr_debug("%s: Unable to get mnoc clk\n", __func__); rgb_data->mnoc_clk = NULL; } return 0; } static int mdss_rgb_bus_scale_init(struct platform_device *pdev, struct mdss_rgb_data *rgb_data) { int rc = 0; rgb_data->bus_scale_table = msm_bus_cl_get_pdata(pdev); if (IS_ERR_OR_NULL(rgb_data->bus_scale_table)) { rc = PTR_ERR(rgb_data->bus_scale_table); pr_err("%s: msm_bus_cl_get_pdata() failed, rc=%d\n", __func__, rc); rgb_data->bus_scale_table = NULL; return rc; } rgb_data->bus_handle = msm_bus_scale_register_client(rgb_data->bus_scale_table); if (!rgb_data->bus_handle) { rc = -EINVAL; pr_err("%sbus_client register failed\n", __func__); } return rc; } static void mdss_rgb_bus_scale_deinit(struct mdss_rgb_data *sdata) { if (sdata->bus_handle) { if (sdata->bus_refcount) msm_bus_scale_client_update_request(sdata->bus_handle, 0); sdata->bus_refcount = 0; msm_bus_scale_unregister_client(sdata->bus_handle); sdata->bus_handle = 0; } } static int mdss_rgb_regulator_init(struct platform_device *pdev, struct mdss_rgb_data *rgb_data) { int rc = 0, i = 0, j = 0; for (i = DSI_CORE_PM; !rc && (i < DSI_MAX_PM); i++) { rc = msm_dss_config_vreg(&pdev->dev, rgb_data->power_data[i].vreg_config, rgb_data->power_data[i].num_vreg, 1); if (rc) { pr_err("%s: failed to init vregs for %s\n", __func__, __mdss_dsi_pm_name(i)); for (j = i-1; j >= DSI_CORE_PM; j--) { msm_dss_config_vreg(&pdev->dev, rgb_data->power_data[j].vreg_config, rgb_data->power_data[j].num_vreg, 0); } } } return rc; } static void mdss_rgb_res_deinit(struct platform_device *pdev) { int i; struct mdss_rgb_data *sdata = platform_get_drvdata(pdev); for (i = (DSI_MAX_PM - 1); i >= DSI_CORE_PM; i--) { if (msm_dss_config_vreg(&pdev->dev, sdata->power_data[i].vreg_config, sdata->power_data[i].num_vreg, 1) < 0) pr_err("%s: failed to de-init vregs for %s\n", __func__, __mdss_dsi_pm_name(i)); mdss_dsi_put_dt_vreg_data(&pdev->dev, &sdata->power_data[i]); } mdss_rgb_bus_scale_deinit(sdata); } static int mdss_rgb_res_init(struct platform_device *pdev) { int rc = 0, i = 0; mdss_rgb_res = platform_get_drvdata(pdev); if (!mdss_rgb_res) { mdss_rgb_res = devm_kzalloc(&pdev->dev, sizeof(*mdss_rgb_res), GFP_KERNEL); if (!mdss_rgb_res) { rc = -ENOMEM; goto mem_fail; } mdss_rgb_res->spi = spi_dev; mdss_rgb_res->panel_data.panel_info.pdest = DISPLAY_1; mdss_rgb_res->ndx = DSI_CTRL_0; rc = mdss_rgb_core_clk_init(pdev, mdss_rgb_res); if (rc) goto mem_fail; /* Parse the ctrl regulator information */ for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) { rc = mdss_dsi_get_dt_vreg_data(&pdev->dev, pdev->dev.of_node, &mdss_rgb_res->power_data[i], i); if (rc) { pr_err("%s: '%s' get_dt_vreg_data failed.rc=%d\n", __func__, __mdss_dsi_pm_name(i), rc); i--; for (; i >= DSI_CORE_PM; i--) mdss_dsi_put_dt_vreg_data(&pdev->dev, &mdss_rgb_res->power_data[i]); goto mem_fail; } } rc = mdss_rgb_regulator_init(pdev, mdss_rgb_res); if (rc) goto mem_fail; rc = mdss_rgb_bus_scale_init(pdev, mdss_rgb_res); if (rc) goto mem_fail; platform_set_drvdata(pdev, mdss_rgb_res); } mdss_rgb_res->pdev = pdev; pr_debug("%s: Setting up mdss_rgb_res=%pK\n", __func__, mdss_rgb_res); return 0; mem_fail: mdss_rgb_res_deinit(pdev); return rc; } static int mdss_rgb_get_panel_cfg(char *panel_cfg, size_t panel_cfg_len) { int rc; struct mdss_panel_cfg *pan_cfg = NULL; struct mdss_util_intf *util; util = mdss_get_util_intf(); pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_RGB); if (IS_ERR(pan_cfg)) { return PTR_ERR(pan_cfg); } else if (!pan_cfg) { panel_cfg[0] = 0; return 0; } pr_debug("%s:%d: cfg:[%s]\n", __func__, __LINE__, pan_cfg->arg_cfg); rc = strlcpy(panel_cfg, pan_cfg->arg_cfg, min(panel_cfg_len, sizeof(pan_cfg->arg_cfg))); return rc; } static struct device_node *mdss_rgb_pref_prim_panel( struct platform_device *pdev) { struct device_node *dsi_pan_node = NULL; pr_debug("%s:%d: Select primary panel from dt\n", __func__, __LINE__); dsi_pan_node = of_parse_phandle(pdev->dev.of_node, "qcom,rgb-panel", 0); if (!dsi_pan_node) pr_err("%s:can't find panel phandle\n", __func__); return dsi_pan_node; } static struct device_node *mdss_rgb_find_panel_of_node( struct platform_device *pdev, char *panel_cfg) { int len; char panel_name[MDSS_MAX_PANEL_LEN] = ""; struct device_node *dsi_pan_node = NULL; struct mdss_rgb_data *rgb_data = platform_get_drvdata(pdev); len = strlen(panel_cfg); rgb_data->panel_data.dsc_cfg_np_name[0] = '\0'; if (!len) { /* no panel cfg chg, parse dt */ pr_err("%s:%d: no cmd line cfg present\n", __func__, __LINE__); goto end; } end: if (strcmp(panel_name, NONE_PANEL)) dsi_pan_node = mdss_rgb_pref_prim_panel(pdev); return dsi_pan_node; } static struct device_node *mdss_rgb_config_panel(struct platform_device *pdev) { char panel_cfg[MDSS_MAX_PANEL_LEN] = { 0 }; struct device_node *dsi_pan_node = NULL; struct mdss_rgb_data *rgb_data = platform_get_drvdata(pdev); int rc = 0; rc = mdss_rgb_get_panel_cfg(panel_cfg, MDSS_MAX_PANEL_LEN); if (!rc) pr_warn("%s:%d:dsi specific cfg not present\n", __func__, __LINE__); dsi_pan_node = mdss_rgb_find_panel_of_node(pdev, panel_cfg); if (!dsi_pan_node) { pr_err("%s: can't find panel node %s\n", __func__, panel_cfg); return NULL; } rc = mdss_rgb_panel_init(dsi_pan_node, rgb_data); if (rc) { pr_err("%s: dsi panel init failed\n", __func__); of_node_put(dsi_pan_node); return NULL; } return dsi_pan_node; } static int mdss_rgb_set_clk_rates(struct mdss_rgb_data *rgb_data) { int rc; rc = mdss_dsi_clk_set_link_rate(rgb_data->clk_handle, MDSS_DSI_LINK_BYTE_CLK, rgb_data->byte_clk_rate, MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON); if (rc) { pr_err("%s: dsi_byte_clk - clk_set_rate failed\n", __func__); return rc; } rc = mdss_dsi_clk_set_link_rate(rgb_data->clk_handle, MDSS_DSI_LINK_PIX_CLK, rgb_data->pclk_rate, MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON); if (rc) pr_err("%s: dsi_pixel_clk - clk_set_rate failed\n", __func__); return rc; } int mdss_rgb_pix_clk_init(struct platform_device *pdev, struct mdss_rgb_data *rgb_data) { struct device *dev = NULL; int rc = 0; dev = &pdev->dev; rgb_data->byte_clk_rgb = devm_clk_get(dev, "byte_clk"); if (IS_ERR(rgb_data->byte_clk_rgb)) { rc = PTR_ERR(rgb_data->byte_clk_rgb); pr_err("%s: can't find dsi_byte_clk. rc=%d\n", __func__, rc); rgb_data->byte_clk_rgb = NULL; return rc; } rgb_data->pixel_clk_rgb = devm_clk_get(dev, "pixel_clk"); if (IS_ERR(rgb_data->pixel_clk_rgb)) { rc = PTR_ERR(rgb_data->pixel_clk_rgb); pr_err("%s: can't find rgb_pixel_clk. rc=%d\n", __func__, rc); rgb_data->pixel_clk_rgb = NULL; return rc; } rgb_data->byte_clk_rcg = devm_clk_get(dev, "byte_clk_rcg"); if (IS_ERR(rgb_data->byte_clk_rcg)) { pr_debug("%s: can't find byte clk rcg. rc=%d\n", __func__, rc); rgb_data->byte_clk_rcg = NULL; } rgb_data->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg"); if (IS_ERR(rgb_data->pixel_clk_rcg)) { pr_debug("%s: can't find pixel clk rcg. rc=%d\n", __func__, rc); rgb_data->pixel_clk_rcg = NULL; } return 0; } static int mdss_rgb_ctrl_clock_init(struct platform_device *ctrl_pdev, struct mdss_rgb_data *rgb_data) { int rc = 0; struct mdss_dsi_clk_info info; struct mdss_dsi_clk_client client1 = {"dsi_clk_client"}; struct mdss_dsi_clk_client client2 = {"mdp_event_client"}; void *handle; if (mdss_rgb_pix_clk_init(ctrl_pdev, rgb_data)) return -EPERM; memset(&info, 0x0, sizeof(info)); info.core_clks.mdp_core_clk = rgb_data->mdp_core_clk; info.core_clks.mmss_misc_ahb_clk = rgb_data->mmss_misc_ahb_clk; info.link_hs_clks.byte_clk = rgb_data->byte_clk_rgb; info.link_hs_clks.pixel_clk = rgb_data->pixel_clk_rgb; info.priv_data = rgb_data; snprintf(info.name, sizeof(info.name), "DSI0"); rgb_data->clk_mngr = mdss_dsi_clk_init(&info); if (IS_ERR_OR_NULL(rgb_data->clk_mngr)) { rc = PTR_ERR(rgb_data->clk_mngr); rgb_data->clk_mngr = NULL; pr_err("dsi clock registration failed, rc = %d\n", rc); return rc; } handle = mdss_dsi_clk_register(rgb_data->clk_mngr, &client1); if (IS_ERR_OR_NULL(handle)) { rc = PTR_ERR(handle); pr_err("failed to register %s client, rc = %d\n", client1.client_name, rc); return rc; } rgb_data->clk_handle = handle; handle = mdss_dsi_clk_register(rgb_data->clk_mngr, &client2); if (IS_ERR_OR_NULL(handle)) { rc = PTR_ERR(handle); pr_err("failed to register %s client, rc = %d\n", client2.client_name, rc); goto error_clk_client_deregister; } else { rgb_data->mdp_clk_handle = handle; } return 0; error_clk_client_deregister: mdss_dsi_clk_deregister(rgb_data->clk_handle); return rc; } static int mdss_rgb_pinctrl_init(struct platform_device *pdev) { struct mdss_rgb_data *rgb_data; rgb_data = platform_get_drvdata(pdev); rgb_data->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR_OR_NULL(rgb_data->pin_res.pinctrl)) { pr_err("%s: failed to get pinctrl\n", __func__); return PTR_ERR(rgb_data->pin_res.pinctrl); } rgb_data->pin_res.gpio_state_active = pinctrl_lookup_state(rgb_data->pin_res.pinctrl, MDSS_PINCTRL_STATE_DEFAULT); if (IS_ERR_OR_NULL(rgb_data->pin_res.gpio_state_active)) pr_warn("%s: can not get default pinstate\n", __func__); rgb_data->pin_res.gpio_state_suspend = pinctrl_lookup_state(rgb_data->pin_res.pinctrl, MDSS_PINCTRL_STATE_SLEEP); if (IS_ERR_OR_NULL(rgb_data->pin_res.gpio_state_suspend)) pr_warn("%s: can not get sleep pinstate\n", __func__); return 0; } static void mdss_rgb_config_clk_src(struct platform_device *pdev) { struct mdss_rgb_data *rgb_res = platform_get_drvdata(pdev); if (!rgb_res->ext_pixel0_clk) { pr_err("%s: RGB ext. clocks not present\n", __func__); return; } if (rgb_res->pll_src_config == PLL_SRC_DEFAULT) { rgb_res->byte0_parent = rgb_res->ext_byte0_clk; rgb_res->pixel0_parent = rgb_res->ext_pixel0_clk; } pr_debug("%s: default: DSI0 <--> PLL0\n", __func__); } static int mdss_rgb_set_clk_src(struct mdss_rgb_data *ctrl) { int rc; struct clk *byte_parent, *pixel_parent = NULL; if (!ctrl->byte_clk_rcg || !ctrl->pixel_clk_rcg) { pr_debug("%s: set_clk_src not needed\n", __func__); return 0; } byte_parent = ctrl->byte0_parent; pixel_parent = ctrl->pixel0_parent; if (pixel_parent == NULL || byte_parent == NULL) pr_debug("%s : clk_parent is null\n", __func__); rc = clk_set_parent(ctrl->byte_clk_rcg, byte_parent); if (rc) { pr_err("%s: failed to set parent for byte clk for rgb. rc=%d\n", __func__, rc); goto error; } rc = clk_set_parent(ctrl->pixel_clk_rcg, pixel_parent); if (rc) { pr_err("%s: failed to set parent for pixel clk for rgb. rc=%d\n", __func__, rc); goto error; } pr_debug("%s: rgb clock source set to pixel", __func__); error: return rc; } static int mdss_rgb_probe(struct spi_device *spi, struct platform_device *pdev) { struct mdss_panel_cfg *pan_cfg = NULL; struct mdss_util_intf *util; char *panel_cfg; struct device_node *dsi_pan_node = NULL; struct mdss_panel_info *pinfo = NULL; int rc = 0; if (spi) { spi_dev = spi; spi_dev->bits_per_word = 9; return 0; } if (!spi_dev) { pr_err("%s: SPI controller not probed yet\n", __func__); return -EPROBE_DEFER; } if (!pdev || !pdev->dev.of_node) { pr_err("%s: RGB driver only supports device tree probe\n", __func__); return -ENOTSUPP; } util = mdss_get_util_intf(); if (util == NULL) { pr_err("%s: Failed to get mdss utility functions\n", __func__); return -ENODEV; } if (!util->mdp_probe_done) { pr_err("%s: MDP not probed yet\n", __func__); return -EPROBE_DEFER; } pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_RGB); if (IS_ERR_OR_NULL(pan_cfg)) { rc = PTR_ERR(pan_cfg); return rc; } panel_cfg = pan_cfg->arg_cfg; rc = mdss_rgb_res_init(pdev); if (rc) return rc; rc = mdss_rgb_pinctrl_init(pdev); rc = mdss_rgb_ctrl_clock_init(pdev, mdss_rgb_res); if (rc) return rc; dsi_pan_node = mdss_rgb_config_panel(pdev); if (!dsi_pan_node) return -EINVAL; rc = rgb_panel_device_register(pdev, dsi_pan_node, mdss_rgb_res); if (rc) return rc; pinfo = &(mdss_rgb_res->panel_data.panel_info); rc = mdss_rgb_set_clk_rates(mdss_rgb_res); if (rc) return rc; mdss_rgb_config_clk_src(pdev); return 0; } int mdss_rgb_clk_div_config(struct mdss_rgb_data *rgb_data, struct mdss_panel_info *panel_info, int frame_rate) { u64 h_period, v_period, clk_rate; u8 bpp; if (!panel_info) return -EINVAL; pr_debug("mipi.dst_format = %d\n", panel_info->mipi.dst_format); switch (panel_info->mipi.dst_format) { case DSI_CMD_DST_FORMAT_RGB888: case DSI_VIDEO_DST_FORMAT_RGB888: case DSI_VIDEO_DST_FORMAT_RGB666: bpp = 3; break; case DSI_CMD_DST_FORMAT_RGB565: case DSI_VIDEO_DST_FORMAT_RGB565: bpp = 2; break; default: bpp = 3; /* Default format set to RGB888 */ break; } h_period = mdss_panel_get_htotal(panel_info, true); v_period = mdss_panel_get_vtotal(panel_info); panel_info->clk_rate = h_period * v_period * frame_rate * bpp * 8; clk_rate = panel_info->clk_rate; do_div(clk_rate, 8 * bpp); panel_info->mipi.dsi_pclk_rate = (u32) clk_rate; return 0; } static int mdss_rgb_pinctrl_set_state(struct mdss_rgb_data *rgb_data, bool active) { struct pinctrl_state *pin_state; int ret = 0; pin_state = active ? rgb_data->pin_res.gpio_state_active : rgb_data->pin_res.gpio_state_suspend; if (!IS_ERR_OR_NULL(pin_state)) { ret = pinctrl_select_state(rgb_data->pin_res.pinctrl, pin_state); if (ret) pr_err("%s: can not set %s pins\n", __func__, active ? MDSS_PINCTRL_STATE_DEFAULT : MDSS_PINCTRL_STATE_SLEEP); } else { pr_err("%s: invalid '%s' pinstate\n", __func__, active ? MDSS_PINCTRL_STATE_DEFAULT : MDSS_PINCTRL_STATE_SLEEP); } return ret; } static int mdss_rgb_panel_power_off(struct mdss_panel_data *pdata) { int ret = 0; struct mdss_rgb_data *ctrl_pdata = NULL; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); ret = mdss_rgb_pinctrl_set_state(ctrl_pdata, false); ret = msm_dss_enable_vreg( ctrl_pdata->panel_power_data.vreg_config, ctrl_pdata->panel_power_data.num_vreg, 0); if (ret) pr_err("%s: failed to disable vregs for %s\n", __func__, __mdss_dsi_pm_name(DSI_PANEL_PM)); return ret; } static int mdss_rgb_panel_power_on(struct mdss_panel_data *pdata) { int ret = 0; struct mdss_rgb_data *ctrl_pdata = NULL; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); ret = msm_dss_enable_vreg( ctrl_pdata->panel_power_data.vreg_config, ctrl_pdata->panel_power_data.num_vreg, 1); if (ret) { pr_err("%s: failed to enable vregs for %s\n", __func__, __mdss_dsi_pm_name(DSI_PANEL_PM)); return ret; } ret = mdss_rgb_pinctrl_set_state(ctrl_pdata, true); return ret; } int mdss_rgb_panel_power_ctrl(struct mdss_panel_data *pdata, int power_state) { int ret = 0; struct mdss_panel_info *pinfo; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return -EINVAL; } pinfo = &pdata->panel_info; pr_debug("%s: cur_power_state=%d req_power_state=%d\n", __func__, pinfo->panel_power_state, power_state); if (pinfo->panel_power_state == power_state) { pr_debug("%s: no change needed\n", __func__); return 0; } switch (power_state) { case MDSS_PANEL_POWER_ON: ret = mdss_rgb_panel_power_on(pdata); break; case MDSS_PANEL_POWER_OFF: ret = mdss_rgb_panel_power_off(pdata); break; default: pr_err("%s: unknown panel power state requested (%d)\n", __func__, power_state); ret = -EINVAL; } if (!ret) pinfo->panel_power_state = power_state; return ret; } static void mdss_rgb_phy_config(struct mdss_rgb_data *ctrl, bool phy_enable) { if (phy_enable) { /* clk_en */ MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, 0x1); MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_PLL_CLKBUFLR_EN, 0x01); } else { MIPI_OUTP(ctrl->phy_io.base + DSIPHY_PLL_CLKBUFLR_EN, 0); MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_GLBL_TEST_CTRL, 0); } } static int mdss_rgb_unblank(struct mdss_panel_data *pdata) { struct mdss_rgb_data *ctrl_pdata = NULL; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); ctrl_pdata->on(pdata); return 0; } static int mdss_rgb_blank(struct mdss_panel_data *pdata, int power_state) { struct mdss_rgb_data *ctrl_pdata = NULL; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); ctrl_pdata->off(pdata); return 0; } int mdss_rgb_on(struct mdss_panel_data *pdata) { int rc = 0; struct mdss_panel_info *pinfo; struct mipi_panel_info *mipi; struct mdss_rgb_data *ctrl_pdata = NULL; int cur_power_state; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); cur_power_state = pdata->panel_info.panel_power_state; pr_debug("%s+: ctrl=%pK cur_power_state=%d\n", __func__, ctrl_pdata, cur_power_state); pinfo = &pdata->panel_info; mipi = &pdata->panel_info.mipi; rc = mdss_rgb_panel_power_ctrl(pdata, MDSS_PANEL_POWER_ON); if (rc) goto end; if (mdss_panel_is_power_on(cur_power_state)) { pr_debug("%s: rgb_on from panel low power state\n", __func__); goto end; } rc = mdss_rgb_set_clk_src(ctrl_pdata); rc = mdss_dsi_clk_req_state(ctrl_pdata->clk_handle, MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON, 0); if (rc) { pr_err("%s: failed set clk state for Core Clks, rc = %d\n", __func__, rc); goto end; } mdss_rgb_phy_config(ctrl_pdata, 1); rc = mdss_dsi_clk_req_state(ctrl_pdata->clk_handle, MDSS_DSI_LINK_CLK, MDSS_DSI_CLK_ON, 0); if (rc) pr_err("%s: failed set clk state for Link Clks, rc = %d\n", __func__, rc); end: pr_debug("%s-:\n", __func__); return rc; } static int mdss_rgb_off(struct mdss_panel_data *pdata, int power_state) { int ret = 0; struct mdss_rgb_data *ctrl_pdata = NULL; struct mdss_panel_info *panel_info = NULL; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); panel_info = &ctrl_pdata->panel_data.panel_info; pr_debug("%s+: ctrl=%pK power_state=%d\n", __func__, ctrl_pdata, power_state); if (power_state == panel_info->panel_power_state) { pr_debug("%s: No change in power state %d -> %d\n", __func__, panel_info->panel_power_state, power_state); goto end; } if (mdss_panel_is_power_on(power_state)) { pr_debug("%s: dsi_off with panel always on\n", __func__); goto panel_power_ctrl; } /* * Link clocks should be turned off before PHY can be disabled. * For command mode panels, all clocks are turned off prior to reaching * here, so core clocks should be turned on before accessing hardware * registers. For video mode panel, turn off link clocks and then * disable PHY */ if (pdata->panel_info.type == MIPI_CMD_PANEL) ret = mdss_dsi_clk_req_state(ctrl_pdata->clk_handle, MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON, 0); else ret = mdss_dsi_clk_req_state(ctrl_pdata->clk_handle, MDSS_DSI_LINK_CLK, MDSS_DSI_CLK_OFF, 0); /* disable phy */ mdss_rgb_phy_config(ctrl_pdata, 0); ret = mdss_dsi_clk_req_state(ctrl_pdata->clk_handle, MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF, 0); panel_power_ctrl: mdss_rgb_panel_power_ctrl(pdata, power_state); end: pr_debug("%s-:\n", __func__); return ret; } static int mdss_rgb_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { int rc = 0; struct mdss_rgb_data *ctrl_pdata = NULL; struct mdss_panel_info *pinfo; int power_state; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return -EINVAL; } pinfo = &pdata->panel_info; ctrl_pdata = container_of(pdata, struct mdss_rgb_data, panel_data); switch (event) { case MDSS_EVENT_LINK_READY: if (ctrl_pdata->refresh_clk_rate) rc = mdss_rgb_clk_refresh(ctrl_pdata); rc = mdss_rgb_on(pdata); break; case MDSS_EVENT_UNBLANK: rc = mdss_rgb_unblank(pdata); break; case MDSS_EVENT_BLANK: power_state = (int) (unsigned long) arg; rc = mdss_rgb_blank(pdata, power_state); break; case MDSS_EVENT_PANEL_OFF: power_state = (int) (unsigned long) arg; rc = mdss_rgb_blank(pdata, power_state); rc = mdss_rgb_off(pdata, power_state); break; } pr_debug("%s+: event=%d\n", __func__, event); return rc; } static int mdss_rgb_parse_gpio_params(struct platform_device *ctrl_pdev, struct mdss_rgb_data *rgb_data) { int rc = 0; rgb_data->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, "qcom,platform-reset-gpio", 0); if (!gpio_is_valid(rgb_data->rst_gpio)) { pr_err("%s:%d, reset gpio not specified\n", __func__, __LINE__); return -EINVAL; } rc = gpio_request(rgb_data->rst_gpio, "disp_rst_n"); if (rc) { pr_err("request reset gpio failed, rc=%d\n", rc); return rc; } rc = gpio_direction_output(rgb_data->rst_gpio, 1); if (rc) { pr_err("%s: unable to set dir for rst gpio\n", __func__); return rc; } pr_debug("%s: reset gpio set\n", __func__); return 0; } int rgb_panel_device_register(struct platform_device *ctrl_pdev, struct device_node *pan_node, struct mdss_rgb_data *rgb_data) { struct mipi_panel_info *mipi; int rc = 0; struct mdss_panel_info *pinfo = &(rgb_data->panel_data.panel_info); struct mdss_util_intf *util; u64 clk_rate; mipi = &(pinfo->mipi); util = mdss_get_util_intf(); pinfo->type = RGB_PANEL; mdss_rgb_clk_div_config(rgb_data, pinfo, mipi->frame_rate); rgb_data->pclk_rate = mipi->dsi_pclk_rate; clk_rate = pinfo->clk_rate; do_div(clk_rate, 8U); rgb_data->byte_clk_rate = (u32)clk_rate; pr_debug("%s: pclk=%d, bclk=%d\n", __func__, rgb_data->pclk_rate, rgb_data->byte_clk_rate); rc = mdss_dsi_get_dt_vreg_data(&ctrl_pdev->dev, pan_node, &rgb_data->panel_power_data, DSI_PANEL_PM); if (rc) { pr_err("%s: '%s' get_dt_vreg_data failed.rc=%d\n", __func__, __mdss_dsi_pm_name(DSI_PANEL_PM), rc); return rc; } rc = msm_dss_config_vreg(&ctrl_pdev->dev, rgb_data->panel_power_data.vreg_config, rgb_data->panel_power_data.num_vreg, 1); if (rc) { pr_err("%s: failed to init regulator, rc=%d\n", __func__, rc); return rc; } pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo); pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo); rc = mdss_rgb_parse_gpio_params(ctrl_pdev, rgb_data); if (rc) return rc; rc = msm_dss_ioremap_byname(ctrl_pdev, &rgb_data->phy_io, "dsi_phy"); if (rc) { pr_err("%s:%d unable to remap dsi phy resources\n", __func__, __LINE__); return rc; } rgb_data->panel_data.event_handler = mdss_rgb_event_handler; pinfo->is_prim_panel = true; pinfo->cont_splash_enabled = util->panel_intf_status(pinfo->pdest, MDSS_PANEL_INTF_RGB) ? true : false; pr_info("%s: Continuous splash %s\n", __func__, pinfo->cont_splash_enabled ? "enabled" : "disabled"); rc = mdss_register_panel(ctrl_pdev, &(rgb_data->panel_data)); if (rc) { pr_err("%s: unable to register RGB panel\n", __func__); return rc; } panel_debug_register_base("panel", rgb_data->ctrl_base, rgb_data->reg_size); pr_debug("%s: Panel data initialized\n", __func__); return 0; } static int mdss_rgb_remove(struct spi_device *spi, struct platform_device *pdev) { if (pdev) mdss_rgb_res_deinit(pdev); return 0; } static int mdss_rgb_probe_pdev(struct platform_device *pdev) { return mdss_rgb_probe(NULL, pdev); } static int mdss_rgb_remove_pdev(struct platform_device *pdev) { return mdss_rgb_remove(NULL, pdev); } static int mdss_rgb_probe_spi(struct spi_device *spi) { return mdss_rgb_probe(spi, NULL); } static int mdss_rgb_remove_spi(struct spi_device *spi) { return mdss_rgb_remove(spi, NULL); } static const struct of_device_id mdss_rgb_spi_dt_match[] = { {.compatible = "qcom,mdss-rgb-spi"}, {} }; MODULE_DEVICE_TABLE(of, mdss_rgb_spi_dt_match); static const struct of_device_id mdss_rgb_dt_match[] = { {.compatible = "qcom,mdss-rgb"}, {} }; MODULE_DEVICE_TABLE(of, mdss_rgb_dt_match); static struct spi_driver mdss_rgb_spi_driver = { .probe = mdss_rgb_probe_spi, .remove = mdss_rgb_remove_spi, .driver = { .name = "mdss_rgb_spi", .of_match_table = mdss_rgb_spi_dt_match, }, }; static struct platform_driver mdss_rgb_driver = { .probe = mdss_rgb_probe_pdev, .remove = mdss_rgb_remove_pdev, .shutdown = NULL, .driver = { .name = "mdss_rgb", .of_match_table = mdss_rgb_dt_match, }, }; static int __init mdss_rgb_driver_init(void) { int ret; ret = spi_register_driver(&mdss_rgb_spi_driver); if (ret < 0) { pr_err("failed to register mdss_rgb spi driver\n"); return ret; } ret = platform_driver_register(&mdss_rgb_driver); if (ret) pr_err("failed to register mdss_rgb platform driver\n"); return ret; } module_init(mdss_rgb_driver_init); static void __exit mdss_rgb_driver_cleanup(void) { platform_driver_unregister(&mdss_rgb_driver); spi_unregister_driver(&mdss_rgb_spi_driver); } module_exit(mdss_rgb_driver_cleanup); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("RGB driver");