From f86f7bcd2c6b8ac3448483601a721f3b726bcd53 Mon Sep 17 00:00:00 2001 From: Narender Ankam Date: Wed, 25 Jul 2018 15:01:20 +0530 Subject: [PATCH] msm: mdss: rgb: adding rgb interface driver for qcs405 Add mdss_rgb driver to drive RGB parallel interface required on qcs405 target. Also added an SPI controller to transfer panel on/off sequences to RGB panel. Change-Id: I5479a05aa9e0fbc922dabed9a4b85cec1fa167d3 Signed-off-by: Rashi Bindra Signed-off-by: Narender Ankam --- .../devicetree/bindings/fb/mdss-rgb.txt | 153 +++ drivers/video/fbdev/msm/Kconfig | 9 + drivers/video/fbdev/msm/Makefile | 2 + drivers/video/fbdev/msm/mdss_dsi.c | 4 +- drivers/video/fbdev/msm/mdss_dsi.h | 5 + drivers/video/fbdev/msm/mdss_dsi_clk.c | 94 +- drivers/video/fbdev/msm/mdss_dsi_clk.h | 42 + drivers/video/fbdev/msm/mdss_fb.c | 3 + drivers/video/fbdev/msm/mdss_mdp.c | 3 + drivers/video/fbdev/msm/mdss_mdp.h | 3 + drivers/video/fbdev/msm/mdss_mdp_ctl.c | 35 +- drivers/video/fbdev/msm/mdss_mdp_hwio.h | 1 + drivers/video/fbdev/msm/mdss_mdp_intf_video.c | 4 + drivers/video/fbdev/msm/mdss_panel.h | 2 + drivers/video/fbdev/msm/mdss_rgb.c | 1166 +++++++++++++++++ drivers/video/fbdev/msm/mdss_rgb.h | 104 ++ drivers/video/fbdev/msm/mdss_rgb_panel.c | 458 +++++++ 17 files changed, 2033 insertions(+), 55 deletions(-) create mode 100644 Documentation/devicetree/bindings/fb/mdss-rgb.txt create mode 100644 drivers/video/fbdev/msm/mdss_rgb.c create mode 100644 drivers/video/fbdev/msm/mdss_rgb.h create mode 100644 drivers/video/fbdev/msm/mdss_rgb_panel.c diff --git a/Documentation/devicetree/bindings/fb/mdss-rgb.txt b/Documentation/devicetree/bindings/fb/mdss-rgb.txt new file mode 100644 index 000000000000..2384cc6fbe1f --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-rgb.txt @@ -0,0 +1,153 @@ +Qualcomm Technologies, Inc. mdss-rgb + +mdss-rgb is the master RGB device which supports software RGB interface. + +Required properties: +- compatible: Must be "qcom,mdss-rgb" +- ranges: The standard property which specifies the child address +- reg: Base address and length of the different register + regions(s) required for RGB functionality. +- reg-names: A list of strings that map in order to the list of regs. + "dsi_phy" - MDSS DSI PHY register region + "dsi_phy_regulator" - MDSS DSI PHY REGULATOR region + "mmss_misc_phys" - Register region for MMSS DSI clamps +- vdda-supply: Phandle for vdda regulator device node. +- vddio-supply: Phandle for vddio regulator device node. +- qcom,mdss-mdp: pHandle that specifies the mdss-mdp device. +- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the + +- qcom,platform-reset-gpio: Specifies the panel reset gpio. +- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node + Refer to pinctrl-bindings.txt +- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin + controller. These pin configurations are installed in the pinctrl + device node. Refer to pinctrl-bindings.txt + +- qcom,-supply-entries: A node that lists the elements of the supply used by the + a particular "type" of RGB modulee. The module "types" + can be "core", "ctrl", and "phy". Within the same type, + there can be more than one instance of this binding, + in which case the entry would be appended with the + supply entry index. + e.g. qcom,ctrl-supply-entry@0 + -- qcom,supply-name: name of the supply (vdd/vdda/vddio) + -- qcom,supply-min-voltage: minimum voltage level (uV) + -- qcom,supply-max-voltage: maximum voltage level (uV) + -- qcom,supply-enable-load: load drawn (uA) from enabled supply + -- qcom,supply-disable-load: load drawn (uA) from disabled supply + -- qcom,supply-ulp-load: load drawn (uA) from supply in ultra-low power mode + -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on + -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on + -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off + -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off + + +Example: + mdss_rgb: qcom,mdss_rgb { + compatible = "qcom,mdss-rgb"; + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x1a94400 0x1a94400 0x280 + 0x1a94b80 0x1a94b80 0x30 + 0x193e000 0x193e000 0x30>; + + reg = <0x1a94400 0x280>, + <0x1a94b80 0x30>, + <0x193e000 0x30>; + reg-names = "dsi_phy", + "dsi_phy_regulator", "mmss_misc_phys"; + + gdsc-supply = <&gdsc_mdss>; + vdda-1p2-supply = <&pms405_l4>; + vdda-1p8-supply = <&pms405_l5>; + vddio-supply = <&pms405_l6>; + + qcom,mdss-fb-map = <&mdss_fb0>; + qcom,mdss-mdp = <&mdss_mdp>; + + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_rgb_active &mdss_rgb_data0_active + &mdss_rgb_data1_active &mdss_rgb_data2_active + &mdss_rgb_data3_active &mdss_rgb_data4_active + &mdss_rgb_data5_active &mdss_rgb_data_b0_active + &mdss_rgb_data_b1_active &mdss_rgb_data_b2_active + &mdss_rgb_data_b3_active &mdss_rgb_data_b4_active + &mdss_rgb_data_b5_active &mdss_rgb_hsync_active + &mdss_rgb_vsync_active &mdss_rgb_de_active + &mdss_rgb_clk_active>; + pinctrl-1 = <&mdss_rgb_suspend &mdss_rgb_data0_suspend + &mdss_rgb_data1_suspend &mdss_rgb_data2_suspend + &mdss_rgb_data3_suspend &mdss_rgb_data4_suspend + &mdss_rgb_data5_suspend &mdss_rgb_data_b0_suspend + &mdss_rgb_data_b1_suspend &mdss_rgb_data_b2_suspend + &mdss_rgb_data_b3_suspend &mdss_rgb_data_b4_suspend + &mdss_rgb_data_b5_suspend &mdss_rgb_hsync_suspend + &mdss_rgb_vsync_suspend &mdss_rgb_de_suspend + &mdss_rgb_clk_suspend>; + qcom,platform-reset-gpio = <&tlmm 58 0>; + + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-post-on-sleep = <20>; + }; + }; + + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p8"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + }; + }; + +mdss-rgb-spi is the SPI controller for RGB device which supports control +commands communication with RGB panel. + +Required properties: +- compatible: Must be "qcom,mdss-rgb-spi" +- spi-max-frequency : Maximum SPI clocking speed of device in Hz + +Optional properties: +- label: A string used to describe the controller used. +- spi-cpol : Empty property indicating device requires inverse + clock polarity (CPOL) mode +- spi-cpha : Empty property indicating device requires shifted + clock phase (CPHA) mode + +Example: + mdss_rgb_spi: qcom,mdss_rgb_spi { + compatible = "qcom,mdss-rgb-spi"; + label = "MDSS SPI QUP1 CLIENT"; + spi-max-frequency = <5000000>; + } diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index 791182dfc8f2..444a58b7d1b6 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -91,6 +91,15 @@ config FB_MSM_MDSS_SPI_PANEL the current max fps only reach to ~30 fps with 240x240 resolution, and limited by MDP hardware architecture only supply GPU compostition. +config FB_MSM_MDSS_RGB_PANEL + depends on FB_MSM_MDSS + bool "Support RGB panel feature" + ---help--- + The MDSS RGB Panel provides support for transmitting + MDSS frame buffer data over RGB parallel interface + connected to RGB panel. Panel on/off sequence commands + are sent to RGB Panel through an SPI interface. + config FB_MSM_MDSS_MHL3 depends on FB_MSM_MDSS_HDMI_PANEL bool "MHL3 SII8620 Support" diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 35b2a0fbb408..ea9ff0045cc9 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -69,6 +69,8 @@ obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_display.o obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_client.o obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_panel.o +obj-$(CONFIG_FB_MSM_MDSS_RGB_PANEL) += mdss_rgb.o mdss_rgb_panel.o + obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o mdss-qpic-objs := mdss_qpic.o mdss_fb.o mdss_qpic_panel.o mdss_sync.o diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 5bb6575bd053..bdd616aaa6ec 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -477,7 +477,7 @@ end: return ret; } -static void mdss_dsi_put_dt_vreg_data(struct device *dev, +void mdss_dsi_put_dt_vreg_data(struct device *dev, struct dss_module_power *module_power) { if (!module_power) { @@ -492,7 +492,7 @@ static void mdss_dsi_put_dt_vreg_data(struct device *dev, module_power->num_vreg = 0; } -static int mdss_dsi_get_dt_vreg_data(struct device *dev, +int mdss_dsi_get_dt_vreg_data(struct device *dev, struct device_node *of_node, struct dss_module_power *mp, enum dsi_pm_type module) { diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 6fb8fbaf3113..9c1ca61de894 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -681,6 +681,11 @@ void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off, u32 mask, u32 val); int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl); int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, int power_state); +int mdss_dsi_get_dt_vreg_data(struct device *dev, + struct device_node *of_node, struct dss_module_power *mp, + enum dsi_pm_type module); +void mdss_dsi_put_dt_vreg_data(struct device *dev, + struct dss_module_power *module_power); static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_clk.c b/drivers/video/fbdev/msm/mdss_dsi_clk.c index ddbec7775483..6423a5e2e9ab 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_clk.c +++ b/drivers/video/fbdev/msm/mdss_dsi_clk.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-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 @@ -18,50 +18,9 @@ #include "mdss_dsi_clk.h" #include "mdss_dsi.h" #include "mdss_debug.h" +#include "mdss_rgb.h" -#define MAX_CLIENT_NAME_LEN 20 -struct dsi_core_clks { - struct mdss_dsi_core_clk_info clks; - u32 current_clk_state; -}; - -struct dsi_link_clks { - struct mdss_dsi_link_hs_clk_info hs_clks; - struct mdss_dsi_link_lp_clk_info lp_clks; - u32 current_clk_state; -}; - -struct mdss_dsi_clk_mngr { - char name[DSI_CLK_NAME_LEN]; - struct dsi_core_clks core_clks; - struct dsi_link_clks link_clks; - - struct reg_bus_client *reg_bus_clt; - - pre_clockoff_cb pre_clkoff_cb; - post_clockoff_cb post_clkoff_cb; - post_clockon_cb post_clkon_cb; - pre_clockon_cb pre_clkon_cb; - - struct list_head client_list; - struct mutex clk_mutex; - - void *priv_data; -}; - -struct mdss_dsi_clk_client_info { - char name[MAX_CLIENT_NAME_LEN]; - u32 core_refcount; - u32 link_refcount; - u32 core_clk_state; - u32 link_clk_state; - - struct list_head list; - - struct mdss_dsi_clk_mngr *mngr; -}; - -static int dsi_core_clk_start(struct dsi_core_clks *c_clks) +int dsi_core_clk_start(struct dsi_core_clks *c_clks) { int rc = 0; struct mdss_dsi_clk_mngr *mngr; @@ -248,7 +207,7 @@ static int dsi_link_hs_clk_disable( } -static int dsi_link_hs_clk_start( +int dsi_link_hs_clk_start( struct mdss_dsi_link_hs_clk_info *link_hs_clks, enum mdss_dsi_link_clk_op_type op_type) { @@ -291,7 +250,7 @@ error: return rc; } -static int dsi_link_lp_clk_start( +int dsi_link_lp_clk_start( struct mdss_dsi_link_lp_clk_info *link_lp_clks) { int rc = 0; @@ -377,6 +336,10 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, { int rc = 0; struct mdss_dsi_clk_mngr *mngr; + struct mdss_rgb_data *sdata; + struct dss_vreg *vreg; + unsigned int num; + int i = 0; bool l_c_on = false; if (c_clks) { @@ -392,6 +355,8 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, if (!mngr) return -EINVAL; + sdata = mngr->priv_data; + pr_debug("%s: c_state = %d, l_state = %d\n", mngr ? mngr->name : "NA", c_clks ? c_state : -1, l_clks ? l_state : -1); /* @@ -401,12 +366,37 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, */ if (c_clks && (c_state == MDSS_DSI_CLK_ON)) { if (c_clks->current_clk_state == MDSS_DSI_CLK_OFF) { - rc = mngr->pre_clkon_cb(mngr->priv_data, - MDSS_DSI_CORE_CLK, MDSS_DSI_LINK_NONE, - MDSS_DSI_CLK_ON); - if (rc) { - pr_err("failed to turn on MDP FS rc= %d\n", rc); - goto error; + if (mngr->pre_clkon_cb == NULL) { + if (!sdata) { + pr_debug("invalid rgb data\n"); + goto error; + } + + for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) { + vreg = sdata->power_data[i].vreg_config; + num = sdata->power_data[i].num_vreg; + rc = msm_dss_enable_vreg(vreg, num, 1); + if (rc) { + pr_err("%s: failed to enable vregs for %s\n", + __func__, + __mdss_dsi_pm_name(i)); + goto error; + } else { + pr_debug("%s: enabled vregs for %s\n", + __func__, + __mdss_dsi_pm_name(i)); + sdata->core_power = true; + } + } + } else { + rc = mngr->pre_clkon_cb(mngr->priv_data, + MDSS_DSI_CORE_CLK, MDSS_DSI_LINK_NONE, + MDSS_DSI_CLK_ON); + if (rc) { + pr_err("failed to turn on MDP FS rc= %d\n", + rc); + goto error; + } } } rc = dsi_core_clk_start(c_clks); diff --git a/drivers/video/fbdev/msm/mdss_dsi_clk.h b/drivers/video/fbdev/msm/mdss_dsi_clk.h index 5f000f97afc3..fb7e49dce6c5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_clk.h +++ b/drivers/video/fbdev/msm/mdss_dsi_clk.h @@ -18,6 +18,7 @@ #include #define DSI_CLK_NAME_LEN 20 +#define MAX_CLIENT_NAME_LEN 20 #define MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON 0x1 @@ -166,6 +167,47 @@ struct mdss_dsi_clk_client { char *client_name; }; +struct dsi_core_clks { + struct mdss_dsi_core_clk_info clks; + u32 current_clk_state; +}; + +struct dsi_link_clks { + struct mdss_dsi_link_hs_clk_info hs_clks; + struct mdss_dsi_link_lp_clk_info lp_clks; + u32 current_clk_state; +}; + +struct mdss_dsi_clk_mngr { + char name[DSI_CLK_NAME_LEN]; + struct dsi_core_clks core_clks; + struct dsi_link_clks link_clks; + + struct reg_bus_client *reg_bus_clt; + + pre_clockoff_cb pre_clkoff_cb; + post_clockoff_cb post_clkoff_cb; + post_clockon_cb post_clkon_cb; + pre_clockon_cb pre_clkon_cb; + + struct list_head client_list; + struct mutex clk_mutex; + + void *priv_data; +}; + +struct mdss_dsi_clk_client_info { + char name[MAX_CLIENT_NAME_LEN]; + u32 core_refcount; + u32 link_refcount; + u32 core_clk_state; + u32 link_clk_state; + + struct list_head list; + + struct mdss_dsi_clk_mngr *mngr; +}; + /** * mdss_dsi_clk_init() - Initializes clock manager * @info: Clock information to be managed by the clock manager. diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index d46e9bb4298b..f0a868dbc565 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -365,6 +365,9 @@ static ssize_t mdss_fb_get_type(struct device *dev, case SPI_PANEL: ret = snprintf(buf, PAGE_SIZE, "spi panel\n"); break; + case RGB_PANEL: + ret = snprintf(buf, PAGE_SIZE, "rgb panel\n"); + break; default: ret = snprintf(buf, PAGE_SIZE, "unknown panel\n"); break; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 4b24e4db0a3c..813eec0d76ac 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -99,6 +99,7 @@ static struct mdss_panel_intf pan_types[] = { {"dsi", MDSS_PANEL_INTF_DSI}, {"edp", MDSS_PANEL_INTF_EDP}, {"hdmi", MDSS_PANEL_INTF_HDMI}, + {"rgb", MDSS_PANEL_INTF_RGB}, }; static char mdss_mdp_panel[MDSS_MAX_PANEL_LEN]; @@ -2688,6 +2689,8 @@ int mdss_panel_get_intf_status(u32 disp_num, u32 intf_type) rc = (intf_status & MDSS_MDP_INTF_DSI1_SEL); else rc = 0; + } else if (intf_type == MDSS_PANEL_INTF_RGB) { + rc = (intf_status & MDSS_MDP_INTF_DSI0_SEL); } else if (intf_type == MDSS_PANEL_INTF_EDP) { intf_status &= MDSS_MDP_INTF_EDP_SEL; rc = (intf_status == MDSS_MDP_INTF_EDP_SEL); diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 085abc199638..9dae64c6149e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -652,6 +652,9 @@ struct mdss_mdp_ctl { /* vsync handler for FRC */ struct mdss_mdp_vsync_handler frc_vsync_handler; bool need_vsync_on; + + /* pack alignment for DSI or RGB Panels */ + bool pack_align_msb; }; struct mdss_mdp_mixer { diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index c277311de2c9..c06ffd3f7ced 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -2485,6 +2485,7 @@ int mdss_mdp_ctl_free(struct mdss_mdp_ctl *ctl) ctl->ref_cnt--; ctl->intf_num = MDSS_MDP_NO_INTF; ctl->intf_type = MDSS_MDP_NO_INTF; + ctl->pack_align_msb = false; ctl->is_secure = false; ctl->power_state = MDSS_PANEL_POWER_OFF; ctl->mixer_left = NULL; @@ -3711,15 +3712,24 @@ int mdss_mdp_ctl_reconfig(struct mdss_mdp_ctl *ctl, case MIPI_VIDEO_PANEL: ctl->is_video_mode = true; ctl->intf_type = MDSS_INTF_DSI; + ctl->pack_align_msb = true; ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; ctl->ops.start_fnc = mdss_mdp_video_start; break; case MIPI_CMD_PANEL: ctl->is_video_mode = false; ctl->intf_type = MDSS_INTF_DSI; + ctl->pack_align_msb = true; ctl->opmode = MDSS_MDP_CTL_OP_CMD_MODE; ctl->ops.start_fnc = mdss_mdp_cmd_start; break; + case RGB_PANEL: + ctl->is_video_mode = true; + ctl->intf_type = MDSS_INTF_DSI; + ctl->pack_align_msb = false; + ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; + ctl->ops.start_fnc = mdss_mdp_video_start; + break; } ctl->is_secure = false; @@ -3835,6 +3845,7 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, ctl->intf_num = mdp5_data->mixer_swap ? MDSS_MDP_INTF1 : MDSS_MDP_INTF2; ctl->intf_type = MDSS_INTF_DSI; + ctl->pack_align_msb = true; ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; ctl->ops.start_fnc = mdss_mdp_video_start; break; @@ -3846,6 +3857,7 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, ctl->intf_num = mdp5_data->mixer_swap ? MDSS_MDP_INTF1 : MDSS_MDP_INTF2; ctl->intf_type = MDSS_INTF_DSI; + ctl->pack_align_msb = true; ctl->opmode = MDSS_MDP_CTL_OP_CMD_MODE; ctl->ops.start_fnc = mdss_mdp_cmd_start; INIT_WORK(&ctl->cpu_pm_work, __cpu_pm_work_handler); @@ -3861,6 +3873,19 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, ctl->intf_num = MDSS_MDP_NO_INTF; ctl->ops.start_fnc = mdss_mdp_writeback_start; break; + case RGB_PANEL: + ctl->is_video_mode = true; + if (pdata->panel_info.pdest == DISPLAY_1) + ctl->intf_num = mdp5_data->mixer_swap ? MDSS_MDP_INTF2 : + MDSS_MDP_INTF1; + else + ctl->intf_num = mdp5_data->mixer_swap ? MDSS_MDP_INTF1 : + MDSS_MDP_INTF2; + ctl->intf_type = MDSS_INTF_DSI; + ctl->pack_align_msb = false; + ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; + ctl->ops.start_fnc = mdss_mdp_video_start; + break; default: pr_err("unsupported panel type (%d)\n", pdata->panel_info.type); ret = -EINVAL; @@ -3874,7 +3899,15 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, } else { switch (pdata->panel_info.bpp) { case 18: - if (ctl->intf_type == MDSS_INTF_DSI) + /* + * Both DSI and RGB Panels share same MDSS_INTF_DSI + * interface type. In case of 18 bpp, DSI Panels need + * pack alignment and RGB Panels doesn't need pack + * alignment. Enable pack alignment based on ctl's + * pack_align_msb support. + */ + if (ctl->intf_type == MDSS_INTF_DSI && + ctl->pack_align_msb) ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666 | MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB; else diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 5bff904cc728..f0d31bebd8a3 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -683,6 +683,7 @@ enum mdss_mpd_intf_index { #define MDSS_MDP_REG_INTF_INTR_CLEAR 0x1C8 #define MDSS_MDP_REG_INTF_PROG_LINE_INTR_CONF 0x250 #define MDSS_MDP_REG_INTF_VBLANK_END_CONF 0x264 +#define MDSS_MDP_REG_INTF_RGB_INTF_CTRL 0x268 #define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8 #define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 7befce7b7b9c..83b83c034ad0 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -507,6 +507,10 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, mdp_video_write(ctx, MDSS_MDP_REG_INTF_HSYNC_SKEW, p->hsync_skew); mdp_video_write(ctx, MDSS_MDP_REG_INTF_POLARITY_CTL, polarity_ctl); mdp_video_write(ctx, MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN, 0x3); + + if (mdata->pan_cfg.pan_intf == MDSS_PANEL_INTF_RGB) + mdp_video_write(ctx, MDSS_MDP_REG_INTF_RGB_INTF_CTRL, 0x1); + MDSS_XLOG(hsync_period, vsync_period); /* diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 952e8bd2225f..15b8c18f3603 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -61,6 +61,7 @@ enum fps_resolution { #define LVDS_PANEL 11 /* LVDS */ #define EDP_PANEL 12 /* LVDS */ #define SPI_PANEL 13 /* SPI */ +#define RGB_PANEL 14 /* RGB */ #define DSC_PPS_LEN 128 #define INTF_EVENT_STR(x) #x @@ -111,6 +112,7 @@ enum { MDSS_PANEL_INTF_EDP, MDSS_PANEL_INTF_HDMI, MDSS_PANEL_INTF_SPI, + MDSS_PANEL_INTF_RGB, }; enum { diff --git a/drivers/video/fbdev/msm/mdss_rgb.c b/drivers/video/fbdev/msm/mdss_rgb.c new file mode 100644 index 000000000000..4b0c95ea1ffe --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_rgb.c @@ -0,0 +1,1166 @@ +/* Copyright (c) 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 +#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, + FPS_RESOLUTION_HZ); + 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"); diff --git a/drivers/video/fbdev/msm/mdss_rgb.h b/drivers/video/fbdev/msm/mdss_rgb.h new file mode 100644 index 000000000000..c181d0301aa4 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_rgb.h @@ -0,0 +1,104 @@ +/* Copyright (c) 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 "mdss_panel.h" +#include "mdss_dsi_clk.h" +#include "mdss_dsi.h" + +#define NONE_PANEL "none" +#define DSIPHY_CMN_CTRL_0 0x001c +#define DSIPHY_CMN_CTRL_1 0x0020 +#define DSIPHY_CMN_LDO_CNTRL 0x004c +#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018 +#define DSIPHY_PLL_CLKBUFLR_EN 0x041c + +struct mdss_rgb_data { + int ndx; + bool res_init; + struct platform_device *pdev; + struct spi_device *spi; + unsigned char *ctrl_base; + struct dss_io_data ctrl_io; + struct dss_io_data phy_io; + int reg_size; + u32 *dbg_bus; + int dbg_bus_size; + u32 hw_config; /* RGB setup configuration i.e. single*/ + u32 pll_src_config; /* PLL source selection for RGB clocks */ + u32 hw_rev; /* DSI h/w revision */ + struct mdss_panel_data panel_data; + bool refresh_clk_rate; + struct clk *pixel_clk_rgb; + struct clk *byte_clk_rgb; + u32 pclk_rate; + u32 byte_clk_rate; + + /* DSI core regulators */ + struct dss_module_power panel_power_data; + struct dss_module_power power_data[DSI_MAX_PM]; + + /* DSI bus clocks */ + struct clk *mdp_core_clk; + struct clk *mnoc_clk; + struct clk *ahb_clk; + struct clk *axi_clk; + struct clk *mmss_misc_ahb_clk; + + struct clk *ext_pixel0_clk; + struct clk *ext_byte0_clk; + //struct clk *ext_pixel1_clk; + struct clk *byte_clk_rcg; + struct clk *pixel_clk_rcg; + struct clk *byte0_parent; + struct clk *pixel0_parent; + + void *clk_mngr; + void *clk_handle; + void *mdp_clk_handle; + + /* Data bus(AXI) scale settings */ + struct msm_bus_scale_pdata *bus_scale_table; + u32 bus_handle; + u32 bus_refcount; + + int bklt_ctrl; /* backlight ctrl */ + int pwm_enabled; + struct pwm_device *pwm_bl; + struct dsi_pinctrl_res pin_res; + + u8 ctrl_state; + bool core_power; + + /* GPIOs */ + int rst_gpio; + + int (*on)(struct mdss_panel_data *pdata); + int (*off)(struct mdss_panel_data *pdata); + +}; + +enum mdss_rgb_hw_config { + SINGLE_RGB, +}; + +int mdss_rgb_panel_init(struct device_node *node, + struct mdss_rgb_data *rgb_data); +int mdss_rgb_clk_refresh(struct mdss_rgb_data *rgb_data); +int mdss_rgb_clk_div_config(struct mdss_rgb_data *rgb_data, + struct mdss_panel_info *panel_info, int frame_rate); +int rgb_panel_device_register(struct platform_device *ctrl_pdev, + struct device_node *pan_node, struct mdss_rgb_data *rgb_data); +int mdss_rgb_write_command(struct mdss_rgb_data *rgb_data, u8 cmd); +int mdss_rgb_write_data(struct mdss_rgb_data *rgb_data, u8 data); +int mdss_rgb_read_command(struct mdss_rgb_data *rgb_data, + u8 cmd, u8 *data, u8 data_len); diff --git a/drivers/video/fbdev/msm/mdss_rgb_panel.c b/drivers/video/fbdev/msm/mdss_rgb_panel.c new file mode 100644 index 000000000000..e1b081e25d58 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_rgb_panel.c @@ -0,0 +1,458 @@ +/* Copyright (c) 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 +#include +#include