diff options
| author | Jin Li <jinl@codeaurora.org> | 2016-10-06 14:43:08 -0400 |
|---|---|---|
| committer | Yunyun Cao <yunyunc@codeaurora.org> | 2017-03-20 17:10:39 +0800 |
| commit | a12f96b0d2d11a89b68d6581acf759b10c36a68f (patch) | |
| tree | e5e77734f1f78a4cb132f38ff2f6372e14c815ca | |
| parent | d6ca6477e2a6f0458e8e287844b532b7c0385066 (diff) | |
drm/sde: add bridge chip support for drm driver
Display Bridgechip Abstration layer is a common framework to support
different kind of bridge chips with multi client accessing. This
change is to add a DRM bridge driver and hook it up with DBA
framework.
Change-Id: Ie225a7cdb55a4982199c1735c37986950c5fad05
Signed-off-by: Jin Li <jinl@codeaurora.org>
Signed-off-by: Yunyun Cao <yunyunc@codeaurora.org>
| -rw-r--r-- | Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dba_bridge.c | 345 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dba_bridge.h | 65 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c | 29 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_defs.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_display.c | 376 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_display.h | 13 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_drm.c | 119 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_panel.c | 44 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_panel.h | 16 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_backlight.c | 12 |
12 files changed, 857 insertions, 168 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 4fd0c2ecbc6e..302a3c37739d 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -561,6 +561,7 @@ Optional properites: to a non-DSI interface. - qcom,bridge-name: A string to indicate the name of the bridge chip connected to DSI. qcom,bridge-name is required if qcom,dba-panel is defined for the panel. +- qcom,hdmi-mode: Indicates where current panel is HDMI mode, otherwise, it will be DVI mode. - qcom,adjust-timer-wakeup-ms: An integer value to indicate the timer delay(in ms) to accommodate s/w delay while configuring the event timer wakeup logic. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 79ea5a9f90ea..712d8a3c0ede 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_vbif.o \ sde_dbg_evtlog.o \ sde_io_util.o \ + dba_bridge.o \ # use drm gpu driver only if qcom_kgsl driver not available ifneq ($(CONFIG_QCOM_KGSL),y) diff --git a/drivers/gpu/drm/msm/dba_bridge.c b/drivers/gpu/drm/msm/dba_bridge.c new file mode 100644 index 000000000000..0a4bcee42106 --- /dev/null +++ b/drivers/gpu/drm/msm/dba_bridge.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <video/msm_dba.h> +#include "drm_edid.h" +#include "sde_kms.h" +#include "dba_bridge.h" + +#undef pr_fmt +#define pr_fmt(fmt) "dba_bridge:[%s] " fmt, __func__ + +/** + * struct dba_bridge - DBA bridge information + * @base: drm_bridge base + * @client_name: Client's name who calls the init + * @chip_name: Bridge chip name + * @name: Bridge chip name + * @id: Bridge driver index + * @display: Private display handle + * @list: Bridge chip driver list node + * @ops: DBA operation container + * @dba_ctx: DBA context + * @mode: DRM mode info + * @hdmi_mode: HDMI or DVI mode for the sink + * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS + * @pluggable: If it's pluggable + */ +struct dba_bridge { + struct drm_bridge base; + char client_name[MSM_DBA_CLIENT_NAME_LEN]; + char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN]; + u32 id; + void *display; + struct list_head list; + struct msm_dba_ops ops; + void *dba_ctx; + struct drm_display_mode mode; + bool hdmi_mode; + u32 num_of_input_lanes; + bool pluggable; +}; +#define to_dba_bridge(x) container_of((x), struct dba_bridge, base) + +static void _dba_bridge_cb(void *data, enum msm_dba_callback_event event) +{ + struct dba_bridge *d_bridge = data; + + if (!d_bridge) { + SDE_ERROR("Invalid data\n"); + return; + } + + DRM_DEBUG("event: %d\n", event); + + switch (event) { + case MSM_DBA_CB_HPD_CONNECT: + DRM_DEBUG("HPD CONNECT\n"); + break; + case MSM_DBA_CB_HPD_DISCONNECT: + DRM_DEBUG("HPD DISCONNECT\n"); + break; + default: + DRM_DEBUG("event:%d is not supported\n", event); + break; + } +} + +static int _dba_bridge_attach(struct drm_bridge *bridge) +{ + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + struct msm_dba_reg_info info; + int ret = 0; + + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return -EINVAL; + } + + memset(&info, 0, sizeof(info)); + /* initialize DBA registration data */ + strlcpy(info.client_name, d_bridge->client_name, + MSM_DBA_CLIENT_NAME_LEN); + strlcpy(info.chip_name, d_bridge->chip_name, + MSM_DBA_CHIP_NAME_MAX_LEN); + info.instance_id = d_bridge->id; + info.cb = _dba_bridge_cb; + info.cb_data = d_bridge; + + /* register client with DBA and get device's ops*/ + if (IS_ENABLED(CONFIG_MSM_DBA)) { + d_bridge->dba_ctx = msm_dba_register_client(&info, + &d_bridge->ops); + if (IS_ERR_OR_NULL(d_bridge->dba_ctx)) { + SDE_ERROR("dba register failed\n"); + ret = PTR_ERR(d_bridge->dba_ctx); + goto error; + } + } else { + SDE_ERROR("DBA not enabled\n"); + ret = -ENODEV; + goto error; + } + + DRM_INFO("client:%s bridge:[%s:%d] attached\n", + d_bridge->client_name, d_bridge->chip_name, d_bridge->id); + +error: + return ret; +} + +static void _dba_bridge_pre_enable(struct drm_bridge *bridge) +{ + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } +} + +static void _dba_bridge_enable(struct drm_bridge *bridge) +{ + int rc = 0; + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + struct msm_dba_video_cfg video_cfg; + struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi_frame; + + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } + + memset(&video_cfg, 0, sizeof(video_cfg)); + memset(&avi_frame, 0, sizeof(avi_frame)); + mode = &d_bridge->mode; + video_cfg.h_active = mode->hdisplay; + video_cfg.v_active = mode->vdisplay; + video_cfg.h_front_porch = mode->hsync_start - mode->hdisplay; + video_cfg.v_front_porch = mode->vsync_start - mode->vdisplay; + video_cfg.h_back_porch = mode->htotal - mode->hsync_end; + video_cfg.v_back_porch = mode->vtotal - mode->vsync_end; + video_cfg.h_pulse_width = mode->hsync_end - mode->hsync_start; + video_cfg.v_pulse_width = mode->vsync_end - mode->vsync_start; + video_cfg.pclk_khz = mode->clock; + video_cfg.hdmi_mode = d_bridge->hdmi_mode; + video_cfg.num_of_input_lanes = d_bridge->num_of_input_lanes; + + SDE_DEBUG( + "video=h[%d,%d,%d,%d] v[%d,%d,%d,%d] pclk=%d hdmi=%d lane=%d\n", + video_cfg.h_active, video_cfg.h_front_porch, + video_cfg.h_pulse_width, video_cfg.h_back_porch, + video_cfg.v_active, video_cfg.v_front_porch, + video_cfg.v_pulse_width, video_cfg.v_back_porch, + video_cfg.pclk_khz, video_cfg.hdmi_mode, + video_cfg.num_of_input_lanes); + + rc = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, mode); + if (rc) { + SDE_ERROR("get avi frame failed ret=%d\n", rc); + } else { + video_cfg.scaninfo = avi_frame.scan_mode; + switch (avi_frame.picture_aspect) { + case HDMI_PICTURE_ASPECT_4_3: + video_cfg.ar = MSM_DBA_AR_4_3; + break; + case HDMI_PICTURE_ASPECT_16_9: + video_cfg.ar = MSM_DBA_AR_16_9; + break; + default: + break; + } + video_cfg.vic = avi_frame.video_code; + DRM_INFO("scaninfo=%d ar=%d vic=%d\n", + video_cfg.scaninfo, video_cfg.ar, video_cfg.vic); + } + + if (d_bridge->ops.video_on) { + rc = d_bridge->ops.video_on(d_bridge->dba_ctx, true, + &video_cfg, 0); + if (rc) + SDE_ERROR("video on failed ret=%d\n", rc); + } +} + +static void _dba_bridge_disable(struct drm_bridge *bridge) +{ + int rc = 0; + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } + + if (d_bridge->ops.video_on) { + rc = d_bridge->ops.video_on(d_bridge->dba_ctx, false, NULL, 0); + if (rc) + SDE_ERROR("video off failed ret=%d\n", rc); + } +} + +static void _dba_bridge_post_disable(struct drm_bridge *bridge) +{ + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } +} + +static void _dba_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + + if (!bridge || !mode || !adjusted_mode || !d_bridge) { + SDE_ERROR("Invalid params\n"); + return; + } + + d_bridge->mode = *adjusted_mode; +} + +static bool _dba_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + bool ret = true; + + if (!bridge || !mode || !adjusted_mode) { + SDE_ERROR("Invalid params\n"); + return false; + } + + return ret; +} + +static const struct drm_bridge_funcs _dba_bridge_ops = { + .attach = _dba_bridge_attach, + .mode_fixup = _dba_bridge_mode_fixup, + .pre_enable = _dba_bridge_pre_enable, + .enable = _dba_bridge_enable, + .disable = _dba_bridge_disable, + .post_disable = _dba_bridge_post_disable, + .mode_set = _dba_bridge_mode_set, +}; + +struct drm_bridge *dba_bridge_init(struct drm_device *dev, + struct drm_encoder *encoder, + struct dba_bridge_init *data) +{ + int rc = 0; + struct dba_bridge *bridge; + struct msm_drm_private *priv = NULL; + + if (!dev || !encoder || !data) { + SDE_ERROR("dev=%p or encoder=%p or data=%p is NULL\n", + dev, encoder, data); + rc = -EINVAL; + goto error; + } + + priv = dev->dev_private; + if (!priv) { + SDE_ERROR("Private data is not present\n"); + rc = -EINVAL; + goto error; + } + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + SDE_ERROR("out of memory\n"); + rc = -ENOMEM; + goto error; + } + + INIT_LIST_HEAD(&bridge->list); + strlcpy(bridge->client_name, data->client_name, + MSM_DBA_CLIENT_NAME_LEN); + strlcpy(bridge->chip_name, data->chip_name, + MSM_DBA_CHIP_NAME_MAX_LEN); + bridge->id = data->id; + bridge->display = data->display; + bridge->hdmi_mode = data->hdmi_mode; + bridge->num_of_input_lanes = data->num_of_input_lanes; + bridge->pluggable = data->pluggable; + bridge->base.funcs = &_dba_bridge_ops; + bridge->base.encoder = encoder; + + rc = drm_bridge_attach(dev, &bridge->base); + if (rc) { + SDE_ERROR("failed to attach bridge, rc=%d\n", rc); + goto error_free_bridge; + } + + if (data->precede_bridge) { + /* Insert current bridge */ + bridge->base.next = data->precede_bridge->next; + data->precede_bridge->next = &bridge->base; + } else { + encoder->bridge = &bridge->base; + } + + if (!bridge->pluggable) { + if (bridge->ops.power_on) + bridge->ops.power_on(bridge->dba_ctx, true, 0); + if (bridge->ops.check_hpd) + bridge->ops.check_hpd(bridge->dba_ctx, 0); + } + + return &bridge->base; + +error_free_bridge: + kfree(bridge); +error: + return ERR_PTR(rc); +} + +void dba_bridge_cleanup(struct drm_bridge *bridge) +{ + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + + if (!bridge) + return; + + if (IS_ENABLED(CONFIG_MSM_DBA)) { + if (!IS_ERR_OR_NULL(d_bridge->dba_ctx)) + msm_dba_deregister_client(d_bridge->dba_ctx); + } + + if (d_bridge->base.encoder) + d_bridge->base.encoder->bridge = NULL; + + kfree(bridge); +} diff --git a/drivers/gpu/drm/msm/dba_bridge.h b/drivers/gpu/drm/msm/dba_bridge.h new file mode 100644 index 000000000000..42a43846c3c6 --- /dev/null +++ b/drivers/gpu/drm/msm/dba_bridge.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _DBA_BRIDGE_H_ +#define _DBA_BRIDGE_H_ + +#include <linux/types.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +#include "msm_drv.h" + +/** + * struct dba_bridge_init - Init parameters for DBA bridge + * @client_name: Client's name who calls the init + * @chip_name: Bridge chip name + * @id: Bridge driver index + * @display: Private display handle + * @hdmi_mode: HDMI or DVI mode for the sink + * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS + * @precede_bridge: Precede bridge chip + * @pluggable: If it's pluggable + */ +struct dba_bridge_init { + const char *client_name; + const char *chip_name; + u32 id; + void *display; + bool hdmi_mode; + u32 num_of_input_lanes; + struct drm_bridge *precede_bridge; + bool pluggable; +}; + +/** + * dba_bridge_init - Initialize the DBA bridge + * @dev: Pointer to drm device handle + * @encoder: Pointer to drm encoder handle + * @data: Pointer to init data + * Returns: pointer of struct drm_bridge + */ +struct drm_bridge *dba_bridge_init(struct drm_device *dev, + struct drm_encoder *encoder, + struct dba_bridge_init *data); + +/** + * dba_bridge_cleanup - Clean up the DBA bridge + * @bridge: Pointer to DBA bridge handle + * Returns: void + */ +void dba_bridge_cleanup(struct drm_bridge *bridge); + +#endif /* _DBA_BRIDGE_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c index ca04eedd6af1..2f0f6c2f1b01 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -85,6 +85,14 @@ void dsi_ctrl_hw_14_host_setup(struct dsi_ctrl_hw *ctrl, DSI_W32(ctrl, DSI_CTRL, reg_value); + /* Force clock lane in HS */ + reg_value = DSI_R32(ctrl, DSI_LANE_CTRL); + if (cfg->force_clk_lane_hs) + reg_value |= BIT(28); + else + reg_value &= ~BIT(28); + DSI_W32(ctrl, DSI_LANE_CTRL, reg_value); + pr_debug("[DSI_%d]Host configuration complete\n", ctrl->index); } @@ -604,8 +612,9 @@ void dsi_ctrl_hw_14_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes) { u32 reg = 0; + reg = DSI_R32(ctrl, DSI_LANE_CTRL); if (lanes & DSI_CLOCK_LANE) - reg = BIT(4); + reg |= BIT(4); if (lanes & DSI_DATA_LANE_0) reg |= BIT(0); if (lanes & DSI_DATA_LANE_1) @@ -664,7 +673,8 @@ void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes) u32 reg = 0; reg = DSI_R32(ctrl, DSI_LANE_CTRL); - reg &= ~BIT(4); /* clock lane */ + if (lanes & DSI_CLOCK_LANE) + reg &= ~BIT(4); /* clock lane */ if (lanes & DSI_DATA_LANE_0) reg &= ~BIT(0); if (lanes & DSI_DATA_LANE_1) @@ -679,7 +689,18 @@ void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes) * HPG recommends separate writes for clearing ULPS_REQUEST and * ULPS_EXIT. */ - DSI_W32(ctrl, DSI_LANE_CTRL, 0x0); + reg = DSI_R32(ctrl, DSI_LANE_CTRL); + if (lanes & DSI_CLOCK_LANE) + reg &= ~BIT(12); + if (lanes & DSI_DATA_LANE_0) + reg &= ~BIT(8); + if (lanes & DSI_DATA_LANE_1) + reg &= ~BIT(9); + if (lanes & DSI_DATA_LANE_2) + reg &= ~BIT(10); + if (lanes & DSI_DATA_LANE_3) + reg &= ~BIT(11); + DSI_W32(ctrl, DSI_LANE_CTRL, reg); pr_debug("[DSI_%d] ULPS request cleared\n", ctrl->index); } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h index 2caa32ea8f0c..d9fcec60693d 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -259,6 +259,7 @@ struct dsi_lane_mapping { * @ignore_rx_eot: Ignore Rx EOT packets if set to true. * @append_tx_eot: Append EOT packets for forward transmissions if set to * true. + * @force_clk_lane_hs: Force clock lane in high speed mode. */ struct dsi_host_common_cfg { enum dsi_pixel_format dst_format; @@ -277,6 +278,7 @@ struct dsi_host_common_cfg { u32 t_clk_pre; bool ignore_rx_eot; bool append_tx_eot; + bool force_clk_lane_hs; }; /** diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c index 5a166a4bae93..5b63dcdc133a 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,13 +18,16 @@ #include <linux/of.h> #include "msm_drv.h" +#include "sde_kms.h" #include "dsi_display.h" #include "dsi_panel.h" #include "dsi_ctrl.h" #include "dsi_ctrl_hw.h" #include "dsi_drm.h" +#include "dba_bridge.h" #define to_dsi_display(x) container_of(x, struct dsi_display, host) +#define DSI_DBA_CLIENT_NAME "dsi" static DEFINE_MUTEX(dsi_display_list_lock); static LIST_HEAD(dsi_display_list); @@ -45,7 +48,7 @@ int dsi_display_set_backlight(void *display, u32 bl_lvl) if (dsi_display == NULL) return -EINVAL; - panel = dsi_display->panel; + panel = dsi_display->panel[0]; rc = dsi_panel_set_backlight(panel, bl_lvl); if (rc) @@ -87,8 +90,9 @@ static ssize_t debugfs_dump_info_read(struct file *file, display->ctrl[i].phy->name); } - len += snprintf(buf + len, (SZ_4K - len), - "\tPanel = %s\n", display->panel->name); + for (i = 0; i < display->panel_count; i++) + len += snprintf(buf + len, (SZ_4K - len), + "\tPanel_%d = %s\n", i, display->panel[i]->name); len += snprintf(buf + len, (SZ_4K - len), "\tClock master = %s\n", @@ -1108,7 +1112,7 @@ static int dsi_display_parse_lane_map(struct dsi_display *display) static int dsi_display_parse_dt(struct dsi_display *display) { int rc = 0; - int i; + int i, size; u32 phy_count = 0; struct device_node *of_node; @@ -1151,14 +1155,69 @@ static int dsi_display_parse_dt(struct dsi_display *display) goto error; } - of_node = of_parse_phandle(display->pdev->dev.of_node, - "qcom,dsi-panel", 0); - if (!of_node) { - pr_err("No Panel device present\n"); + if (of_get_property(display->pdev->dev.of_node, "qcom,dsi-panel", + &size)) { + display->panel_count = size / sizeof(int); + display->panel_of = devm_kzalloc(&display->pdev->dev, + sizeof(struct device_node *) * display->panel_count, + GFP_KERNEL); + if (!display->panel_of) { + SDE_ERROR("out of memory for panel_of\n"); + rc = -ENOMEM; + goto error; + } + display->panel = devm_kzalloc(&display->pdev->dev, + sizeof(struct dsi_panel *) * display->panel_count, + GFP_KERNEL); + if (!display->panel) { + SDE_ERROR("out of memory for panel\n"); + rc = -ENOMEM; + goto error; + } + for (i = 0; i < display->panel_count; i++) { + display->panel_of[i] = + of_parse_phandle(display->pdev->dev.of_node, + "qcom,dsi-panel", i); + if (!display->panel_of[i]) { + SDE_ERROR("of_parse dsi-panel failed\n"); + rc = -ENODEV; + goto error; + } + } + } else { + SDE_ERROR("No qcom,dsi-panel of node\n"); rc = -ENODEV; goto error; - } else { - display->panel_of = of_node; + } + + if (of_get_property(display->pdev->dev.of_node, "qcom,bridge-index", + &size)) { + if (size / sizeof(int) != display->panel_count) { + SDE_ERROR("size=%lu is different than count=%u\n", + size / sizeof(int), display->panel_count); + rc = -EINVAL; + goto error; + } + display->bridge_idx = devm_kzalloc(&display->pdev->dev, + sizeof(u32) * display->panel_count, GFP_KERNEL); + if (!display->bridge_idx) { + SDE_ERROR("out of memory for bridge_idx\n"); + rc = -ENOMEM; + goto error; + } + for (i = 0; i < display->panel_count; i++) { + rc = of_property_read_u32_index( + display->pdev->dev.of_node, + "qcom,bridge-index", i, + &(display->bridge_idx[i])); + if (rc) { + SDE_ERROR( + "read bridge-index error,i=%d rc=%d\n", + i, rc); + rc = -ENODEV; + goto error; + } + } } rc = dsi_display_parse_lane_map(display); @@ -1167,6 +1226,16 @@ static int dsi_display_parse_dt(struct dsi_display *display) goto error; } error: + if (rc) { + if (display->panel_of) + for (i = 0; i < display->panel_count; i++) + if (display->panel_of[i]) + of_node_put(display->panel_of[i]); + devm_kfree(&display->pdev->dev, display->panel_of); + devm_kfree(&display->pdev->dev, display->panel); + devm_kfree(&display->pdev->dev, display->bridge_idx); + display->panel_count = 0; + } return rc; } @@ -1196,12 +1265,15 @@ static int dsi_display_res_init(struct dsi_display *display) } } - display->panel = dsi_panel_get(&display->pdev->dev, display->panel_of); - if (IS_ERR_OR_NULL(display->panel)) { - rc = PTR_ERR(display->panel); - pr_err("failed to get panel, rc=%d\n", rc); - display->panel = NULL; - goto error_ctrl_put; + for (i = 0; i < display->panel_count; i++) { + display->panel[i] = dsi_panel_get(&display->pdev->dev, + display->panel_of[i]); + if (IS_ERR_OR_NULL(display->panel)) { + rc = PTR_ERR(display->panel); + pr_err("failed to get panel, rc=%d\n", rc); + display->panel[i] = NULL; + goto error_ctrl_put; + } } rc = dsi_display_clocks_init(display); @@ -1230,6 +1302,9 @@ static int dsi_display_res_deinit(struct dsi_display *display) if (rc) pr_err("clocks deinit failed, rc=%d\n", rc); + for (i = 0; i < display->panel_count; i++) + dsi_panel_put(display->panel[i]); + for (i = 0; i < display->ctrl_count; i++) { ctrl = &display->ctrl[i]; dsi_phy_put(ctrl->phy); @@ -1279,7 +1354,7 @@ static bool dsi_display_is_seamless_dfps_possible( return false; } - cur = &display->panel->mode; + cur = &display->panel[0]->mode; if (cur->timing.h_active != tgt->timing.h_active) { pr_debug("timing.h_active differs %d %d\n", @@ -1388,7 +1463,7 @@ static int dsi_display_dfps_update(struct dsi_display *display, } timing = &dsi_mode->timing; - dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps); if (!dfps_caps.dfps_support) { pr_err("dfps not supported\n"); return -ENOTSUPP; @@ -1425,7 +1500,7 @@ static int dsi_display_dfps_update(struct dsi_display *display, } } - panel_mode = &display->panel->mode; + panel_mode = &display->panel[0]->mode; memcpy(panel_mode, dsi_mode, sizeof(*panel_mode)); error: @@ -1493,7 +1568,8 @@ static int dsi_display_get_dfps_timing(struct dsi_display *display, } m_ctrl = display->ctrl[display->clk_master_idx].ctrl; - dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + /* Only check the first panel */ + dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps); if (!dfps_caps.dfps_support) { pr_err("dfps not supported by panel\n"); return -EINVAL; @@ -1574,7 +1650,7 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, int i; struct dsi_display_ctrl *ctrl; - rc = dsi_panel_get_host_cfg_for_mode(display->panel, + rc = dsi_panel_get_host_cfg_for_mode(display->panel[0], mode, &display->config); if (rc) { @@ -1687,7 +1763,7 @@ static int dsi_display_bind(struct device *dev, struct drm_device *drm; struct dsi_display *display; struct platform_device *pdev = to_platform_device(dev); - int i, rc = 0; + int i, j, rc = 0; if (!dev || !pdev || !master) { pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n", @@ -1737,15 +1813,19 @@ static int dsi_display_bind(struct device *dev, goto error_ctrl_deinit; } - rc = dsi_panel_drv_init(display->panel, &display->host); - if (rc) { - if (rc != -EPROBE_DEFER) - pr_err("[%s] failed to initialize panel driver, rc=%d\n", - display->name, rc); - goto error_host_deinit; + for (j = 0; j < display->panel_count; j++) { + rc = dsi_panel_drv_init(display->panel[j], &display->host); + if (rc) { + if (rc != -EPROBE_DEFER) + SDE_ERROR( + "[%s]Failed to init panel driver, rc=%d\n", + display->name, rc); + goto error_panel_deinit; + } } - rc = dsi_panel_get_mode_count(display->panel, &display->num_of_modes); + rc = dsi_panel_get_mode_count(display->panel[0], + &display->num_of_modes); if (rc) { pr_err("[%s] failed to get mode count, rc=%d\n", display->name, rc); @@ -1756,8 +1836,8 @@ static int dsi_display_bind(struct device *dev, goto error; error_panel_deinit: - (void)dsi_panel_drv_deinit(display->panel); -error_host_deinit: + for (j--; j >= 0; j--) + (void)dsi_panel_drv_deinit(display->panel[j]); (void)dsi_display_mipi_host_deinit(display); error_ctrl_deinit: for (i = i - 1; i >= 0; i--) { @@ -1798,10 +1878,12 @@ static void dsi_display_unbind(struct device *dev, mutex_lock(&display->display_lock); - rc = dsi_panel_drv_deinit(display->panel); - if (rc) - pr_err("[%s] failed to deinit panel driver, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_drv_deinit(display->panel[i]); + if (rc) + SDE_ERROR("[%s] failed to deinit panel driver, rc=%d\n", + display->name, rc); + } rc = dsi_display_mipi_host_deinit(display); if (rc) @@ -1890,7 +1972,7 @@ int dsi_display_dev_probe(struct platform_device *pdev) int dsi_display_dev_remove(struct platform_device *pdev) { - int rc = 0; + int rc = 0, i; struct dsi_display *display; struct dsi_display *pos, *tmp; @@ -1913,6 +1995,13 @@ int dsi_display_dev_remove(struct platform_device *pdev) mutex_unlock(&dsi_display_list_lock); platform_set_drvdata(pdev, NULL); + if (display->panel_of) + for (i = 0; i < display->panel_count; i++) + if (display->panel_of[i]) + of_node_put(display->panel_of[i]); + devm_kfree(&pdev->dev, display->panel_of); + devm_kfree(&pdev->dev, display->panel); + devm_kfree(&pdev->dev, display->bridge_idx); devm_kfree(&pdev->dev, display); return rc; } @@ -1984,9 +2073,15 @@ void dsi_display_set_active_state(struct dsi_display *display, bool is_active) int dsi_display_drm_bridge_init(struct dsi_display *display, struct drm_encoder *enc) { - int rc = 0; + int rc = 0, i; struct dsi_bridge *bridge; + struct drm_bridge *dba_bridge; + struct dba_bridge_init init_data; + struct drm_bridge *precede_bridge; struct msm_drm_private *priv = NULL; + struct dsi_panel *panel; + u32 *bridge_idx; + u32 num_of_lanes = 0; if (!display || !display->drm_dev || !enc) { pr_err("invalid param(s)\n"); @@ -1997,44 +2092,111 @@ int dsi_display_drm_bridge_init(struct dsi_display *display, priv = display->drm_dev->dev_private; if (!priv) { - pr_err("Private data is not present\n"); + SDE_ERROR("Private data is not present\n"); rc = -EINVAL; - goto error; + goto out; } if (display->bridge) { - pr_err("display is already initialize\n"); - goto error; + SDE_ERROR("display is already initialize\n"); + goto out; } bridge = dsi_drm_bridge_init(display, display->drm_dev, enc); if (IS_ERR_OR_NULL(bridge)) { rc = PTR_ERR(bridge); - pr_err("[%s] brige init failed, %d\n", display->name, rc); - goto error; + SDE_ERROR("[%s] brige init failed, %d\n", display->name, rc); + goto out; } display->bridge = bridge; priv->bridges[priv->num_bridges++] = &bridge->base; + precede_bridge = &bridge->base; + + if (display->panel_count >= MAX_BRIDGES - 1) { + SDE_ERROR("too many bridge chips=%d\n", display->panel_count); + goto error_bridge; + } + + for (i = 0; i < display->panel_count; i++) { + panel = display->panel[i]; + if (panel && display->bridge_idx && + panel->dba_config.dba_panel) { + bridge_idx = display->bridge_idx + i; + num_of_lanes = 0; + memset(&init_data, 0x00, sizeof(init_data)); + if (panel->host_config.data_lanes & DSI_DATA_LANE_0) + num_of_lanes++; + if (panel->host_config.data_lanes & DSI_DATA_LANE_1) + num_of_lanes++; + if (panel->host_config.data_lanes & DSI_DATA_LANE_2) + num_of_lanes++; + if (panel->host_config.data_lanes & DSI_DATA_LANE_3) + num_of_lanes++; + init_data.client_name = DSI_DBA_CLIENT_NAME; + init_data.chip_name = panel->dba_config.bridge_name; + init_data.id = *bridge_idx; + init_data.display = display; + init_data.hdmi_mode = panel->dba_config.hdmi_mode; + init_data.num_of_input_lanes = num_of_lanes; + init_data.precede_bridge = precede_bridge; + dba_bridge = dba_bridge_init(display->drm_dev, enc, + &init_data); + if (IS_ERR_OR_NULL(dba_bridge)) { + rc = PTR_ERR(dba_bridge); + SDE_ERROR("[%s:%d] dba brige init failed, %d\n", + init_data.chip_name, init_data.id, rc); + goto error_dba_bridge; + } + priv->bridges[priv->num_bridges++] = dba_bridge; + precede_bridge = dba_bridge; + } + } -error: + goto out; + +error_dba_bridge: + for (i = 1; i < MAX_BRIDGES; i++) { + dba_bridge_cleanup(priv->bridges[i]); + priv->bridges[i] = NULL; + } +error_bridge: + dsi_drm_bridge_cleanup(display->bridge); + display->bridge = NULL; + priv->bridges[0] = NULL; + priv->num_bridges = 0; +out: mutex_unlock(&display->display_lock); return rc; } int dsi_display_drm_bridge_deinit(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; + struct msm_drm_private *priv = NULL; if (!display) { - pr_err("Invalid params\n"); + SDE_ERROR("Invalid params\n"); + return -EINVAL; + } + priv = display->drm_dev->dev_private; + + if (!priv) { + SDE_ERROR("Private data is not present\n"); return -EINVAL; } mutex_lock(&display->display_lock); + for (i = 1; i < MAX_BRIDGES; i++) { + dba_bridge_cleanup(priv->bridges[i]); + priv->bridges[i] = NULL; + } + dsi_drm_bridge_cleanup(display->bridge); display->bridge = NULL; + priv->bridges[0] = NULL; + priv->num_bridges = 0; mutex_unlock(&display->display_lock); return rc; @@ -2053,7 +2215,7 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) display = disp; mutex_lock(&display->display_lock); - rc = dsi_panel_get_phy_props(display->panel, &phy_props); + rc = dsi_panel_get_phy_props(display->panel[0], &phy_props); if (rc) { pr_err("[%s] failed to get panel phy props, rc=%d\n", display->name, rc); @@ -2073,7 +2235,7 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) info->max_height = 1080; info->compression = MSM_DISPLAY_COMPRESS_NONE; - switch (display->panel->mode.panel_mode) { + switch (display->panel[0]->mode.panel_mode) { case DSI_OP_VIDEO_MODE: info->capabilities |= MSM_DISPLAY_CAP_VID_MODE; break; @@ -2082,7 +2244,7 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) break; default: pr_err("unknwown dsi panel mode %d\n", - display->panel->mode.panel_mode); + display->panel[0]->mode.panel_mode); break; } error: @@ -2106,7 +2268,7 @@ int dsi_display_get_modes(struct dsi_display *display, mutex_lock(&display->display_lock); - rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + rc = dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps); if (rc) { pr_err("[%s] failed to get dfps caps from panel\n", display->name); @@ -2127,7 +2289,8 @@ int dsi_display_get_modes(struct dsi_display *display, /* Insert the dfps "sub-modes" between main panel modes */ int panel_mode_idx = i / num_dfps_rates; - rc = dsi_panel_get_mode(display->panel, panel_mode_idx, modes); + rc = dsi_panel_get_mode(display->panel[0], panel_mode_idx, + modes); if (rc) { pr_err("[%s] failed to get mode from panel\n", display->name); @@ -2178,7 +2341,7 @@ int dsi_display_validate_mode(struct dsi_display *display, adj_mode = *mode; adjust_timing_by_ctrl_count(display, &adj_mode); - rc = dsi_panel_validate_mode(display->panel, &adj_mode); + rc = dsi_panel_validate_mode(display->panel[0], &adj_mode); if (rc) { pr_err("[%s] panel mode validation failed, rc=%d\n", display->name, rc); @@ -2278,7 +2441,7 @@ error: int dsi_display_prepare(struct dsi_display *display) { - int rc = 0; + int rc = 0, i, j; if (!display) { pr_err("Invalid params\n"); @@ -2287,11 +2450,13 @@ int dsi_display_prepare(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_pre_prepare(display->panel); - if (rc) { - pr_err("[%s] panel pre-prepare failed, rc=%d\n", - display->name, rc); - goto error; + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_pre_prepare(display->panel[i]); + if (rc) { + SDE_ERROR("[%s] panel pre-prepare failed, rc=%d\n", + display->name, rc); + goto error_panel_post_unprep; + } } rc = dsi_display_ctrl_power_on(display); @@ -2349,15 +2514,20 @@ int dsi_display_prepare(struct dsi_display *display) goto error_ctrl_link_off; } - rc = dsi_panel_prepare(display->panel); - if (rc) { - pr_err("[%s] panel prepare failed, rc=%d\n", display->name, rc); - goto error_host_engine_off; + for (j = 0; j < display->panel_count; j++) { + rc = dsi_panel_prepare(display->panel[j]); + if (rc) { + SDE_ERROR("[%s] panel prepare failed, rc=%d\n", + display->name, rc); + goto error_panel_unprep; + } } goto error; -error_host_engine_off: +error_panel_unprep: + for (j--; j >= 0; j--) + (void)dsi_panel_unprepare(display->panel[j]); (void)dsi_display_ctrl_host_disable(display); error_ctrl_link_off: (void)dsi_display_ctrl_link_clk_off(display); @@ -2372,7 +2542,8 @@ error_phy_pwr_off: error_ctrl_pwr_off: (void)dsi_display_ctrl_power_off(display); error_panel_post_unprep: - (void)dsi_panel_post_unprepare(display->panel); + for (i--; i >= 0; i--) + (void)dsi_panel_post_unprepare(display->panel[i]); error: mutex_unlock(&display->display_lock); return rc; @@ -2380,7 +2551,7 @@ error: int dsi_display_enable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2389,11 +2560,13 @@ int dsi_display_enable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_enable(display->panel); - if (rc) { - pr_err("[%s] failed to enable DSI panel, rc=%d\n", - display->name, rc); - goto error; + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_enable(display->panel[i]); + if (rc) { + SDE_ERROR("[%s] failed to enable DSI panel, rc=%d\n", + display->name, rc); + goto error_disable_panel; + } } if (display->config.panel_mode == DSI_OP_VIDEO_MODE) { @@ -2419,7 +2592,8 @@ int dsi_display_enable(struct dsi_display *display) goto error; error_disable_panel: - (void)dsi_panel_disable(display->panel); + for (i--; i >= 0; i--) + (void)dsi_panel_disable(display->panel[i]); error: mutex_unlock(&display->display_lock); return rc; @@ -2427,7 +2601,7 @@ error: int dsi_display_post_enable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2436,10 +2610,12 @@ int dsi_display_post_enable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_post_enable(display->panel); - if (rc) - pr_err("[%s] panel post-enable failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_post_enable(display->panel[i]); + if (rc) + SDE_ERROR("[%s] panel post-enable failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; @@ -2447,7 +2623,7 @@ int dsi_display_post_enable(struct dsi_display *display) int dsi_display_pre_disable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2456,10 +2632,12 @@ int dsi_display_pre_disable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_pre_disable(display->panel); - if (rc) - pr_err("[%s] panel pre-disable failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_pre_disable(display->panel[i]); + if (rc) + SDE_ERROR("[%s] panel pre-disable failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; @@ -2467,7 +2645,7 @@ int dsi_display_pre_disable(struct dsi_display *display) int dsi_display_disable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2481,10 +2659,12 @@ int dsi_display_disable(struct dsi_display *display) pr_err("[%s] display wake up failed, rc=%d\n", display->name, rc); - rc = dsi_panel_disable(display->panel); - if (rc) - pr_err("[%s] failed to disable DSI panel, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_disable(display->panel[i]); + if (rc) + SDE_ERROR("[%s] failed to disable DSI panel, rc=%d\n", + display->name, rc); + } if (display->config.panel_mode == DSI_OP_VIDEO_MODE) { rc = dsi_display_vid_engine_disable(display); @@ -2507,7 +2687,7 @@ int dsi_display_disable(struct dsi_display *display) int dsi_display_unprepare(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2521,10 +2701,12 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] display wake up failed, rc=%d\n", display->name, rc); - rc = dsi_panel_unprepare(display->panel); - if (rc) - pr_err("[%s] panel unprepare failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_unprepare(display->panel[i]); + if (rc) + SDE_ERROR("[%s] panel unprepare failed, rc=%d\n", + display->name, rc); + } rc = dsi_display_ctrl_host_disable(display); if (rc) @@ -2561,10 +2743,12 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] failed to power DSI vregs, rc=%d\n", display->name, rc); - rc = dsi_panel_post_unprepare(display->panel); - if (rc) - pr_err("[%s] panel post-unprepare failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_post_unprepare(display->panel[i]); + if (rc) + pr_err("[%s] panel post-unprepare failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index b77bf268dbd1..210b8d00850b 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -102,8 +102,11 @@ struct dsi_display_clk_info { * @display_lock: Mutex for dsi_display interface. * @ctrl_count: Number of DSI interfaces required by panel. * @ctrl: Controller information for DSI display. + * @panel_count: Number of DSI panel. * @panel: Handle to DSI panel. - * @panel_of: pHandle to DSI panel. + * @panel_of: pHandle to DSI panel, it's an array with panel_count + * of struct device_node pointers. + * @bridge_idx: Bridge chip index for each panel_of. * @type: DSI display type. * @clk_master_idx: The master controller for controlling clocks. This is an * index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array. @@ -133,8 +136,10 @@ struct dsi_display { struct dsi_display_ctrl ctrl[MAX_DSI_CTRLS_PER_DISPLAY]; /* panel info */ - struct dsi_panel *panel; - struct device_node *panel_of; + u32 panel_count; + struct dsi_panel **panel; + struct device_node **panel_of; + u32 *bridge_idx; enum dsi_display_type type; u32 clk_master_idx; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c index a1adecf81cc0..995cda97a2f0 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -213,18 +213,21 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct dsi_bridge *c_bridge = to_dsi_bridge(bridge); + struct dsi_panel *panel; - if (!bridge || !mode || !adjusted_mode) { + if (!bridge || !mode || !adjusted_mode || !c_bridge->display || + !c_bridge->display->panel[0]) { pr_err("Invalid params\n"); return; } + /* dsi drm bridge is always the first panel */ + panel = c_bridge->display->panel[0]; memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode)); convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode)); pr_debug("note: using panel cmd/vid mode instead of user val\n"); - c_bridge->dsi_mode.panel_mode = - c_bridge->display->panel->mode.panel_mode; + c_bridge->dsi_mode.panel_mode = panel->mode.panel_mode; } static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, @@ -271,6 +274,7 @@ int dsi_conn_post_init(struct drm_connector *connector, { struct dsi_display *dsi_display = display; struct dsi_panel *panel; + int i; if (!info || !dsi_display) return -EINVAL; @@ -299,60 +303,65 @@ int dsi_conn_post_init(struct drm_connector *connector, break; } - if (!dsi_display->panel) { - pr_debug("invalid panel data\n"); - goto end; - } - - panel = dsi_display->panel; - sde_kms_info_add_keystr(info, "panel name", panel->name); - - switch (panel->mode.panel_mode) { - case DSI_OP_VIDEO_MODE: - sde_kms_info_add_keystr(info, "panel mode", "video"); - break; - case DSI_OP_CMD_MODE: - sde_kms_info_add_keystr(info, "panel mode", "command"); - sde_kms_info_add_keyint(info, "mdp_transfer_time_us", - panel->cmd_config.mdp_transfer_time_us); - break; - default: - pr_debug("invalid panel type:%d\n", panel->mode.panel_mode); - break; - } - sde_kms_info_add_keystr(info, "dfps support", - panel->dfps_caps.dfps_support ? "true" : "false"); + for (i = 0; i < dsi_display->panel_count; i++) { + if (!dsi_display->panel[i]) { + pr_debug("invalid panel data\n"); + goto end; + } - switch (panel->phy_props.rotation) { - case DSI_PANEL_ROTATE_NONE: - sde_kms_info_add_keystr(info, "panel orientation", "none"); - break; - case DSI_PANEL_ROTATE_H_FLIP: - sde_kms_info_add_keystr(info, "panel orientation", "horz flip"); - break; - case DSI_PANEL_ROTATE_V_FLIP: - sde_kms_info_add_keystr(info, "panel orientation", "vert flip"); - break; - default: - pr_debug("invalid panel rotation:%d\n", + panel = dsi_display->panel[i]; + sde_kms_info_add_keystr(info, "panel name", panel->name); + + switch (panel->mode.panel_mode) { + case DSI_OP_VIDEO_MODE: + sde_kms_info_add_keystr(info, "panel mode", "video"); + break; + case DSI_OP_CMD_MODE: + sde_kms_info_add_keystr(info, "panel mode", "command"); + break; + default: + pr_debug("invalid panel type:%d\n", + panel->mode.panel_mode); + break; + } + sde_kms_info_add_keystr(info, "dfps support", + panel->dfps_caps.dfps_support ? + "true" : "false"); + + switch (panel->phy_props.rotation) { + case DSI_PANEL_ROTATE_NONE: + sde_kms_info_add_keystr(info, "panel orientation", + "none"); + break; + case DSI_PANEL_ROTATE_H_FLIP: + sde_kms_info_add_keystr(info, "panel orientation", + "horz flip"); + break; + case DSI_PANEL_ROTATE_V_FLIP: + sde_kms_info_add_keystr(info, "panel orientation", + "vert flip"); + break; + default: + pr_debug("invalid panel rotation:%d\n", panel->phy_props.rotation); - break; - } + break; + } - switch (panel->bl_config.type) { - case DSI_BACKLIGHT_PWM: - sde_kms_info_add_keystr(info, "backlight type", "pwm"); - break; - case DSI_BACKLIGHT_WLED: - sde_kms_info_add_keystr(info, "backlight type", "wled"); - break; - case DSI_BACKLIGHT_DCS: - sde_kms_info_add_keystr(info, "backlight type", "dcs"); - break; - default: - pr_debug("invalid panel backlight type:%d\n", - panel->bl_config.type); - break; + switch (panel->bl_config.type) { + case DSI_BACKLIGHT_PWM: + sde_kms_info_add_keystr(info, "backlight type", "pwm"); + break; + case DSI_BACKLIGHT_WLED: + sde_kms_info_add_keystr(info, "backlight type", "wled"); + break; + case DSI_BACKLIGHT_DCS: + sde_kms_info_add_keystr(info, "backlight type", "dcs"); + break; + default: + pr_debug("invalid panel backlight type:%d\n", + panel->bl_config.type); + break; + } } end: diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c index a7a39e685d4d..ec19dd3510e5 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> +#include "sde_kms.h" #include "dsi_panel.h" #include "dsi_ctrl_hw.h" @@ -386,6 +387,9 @@ static int dsi_panel_bl_register(struct dsi_panel *panel) case DSI_BACKLIGHT_WLED: rc = dsi_panel_led_bl_register(panel, bl); break; + case DSI_BACKLIGHT_UNKNOWN: + DRM_INFO("backlight type is unknown\n"); + break; default: pr_err("Backlight type(%d) not supported\n", bl->type); rc = -ENOTSUPP; @@ -704,6 +708,8 @@ static int dsi_panel_parse_misc_host_config(struct dsi_host_common_cfg *host, host->append_tx_eot = of_property_read_bool(of_node, "qcom,mdss-dsi-tx-eot-append"); + host->force_clk_lane_hs = of_property_read_bool(of_node, + "qcom,mdss-dsi-force-clock-lane-hs"); return 0; } @@ -1348,6 +1354,8 @@ static int dsi_panel_parse_gpios(struct dsi_panel *panel, { int rc = 0; + /* Need to set GPIO default value to -1, since 0 is a valid value */ + panel->reset_config.disp_en_gpio = -1; panel->reset_config.reset_gpio = of_get_named_gpio(of_node, "qcom,platform-reset-gpio", 0); @@ -1496,6 +1504,33 @@ error: return rc; } +static int dsi_panel_parse_dba_config(struct dsi_panel *panel, + struct device_node *of_node) +{ + int rc = 0, len = 0; + + panel->dba_config.dba_panel = of_property_read_bool(of_node, + "qcom,dba-panel"); + + if (panel->dba_config.dba_panel) { + panel->dba_config.hdmi_mode = of_property_read_bool(of_node, + "qcom,hdmi-mode"); + + panel->dba_config.bridge_name = of_get_property(of_node, + "qcom,bridge-name", &len); + if (!panel->dba_config.bridge_name || len <= 0) { + SDE_ERROR( + "%s:%d Unable to read bridge_name, data=%p,len=%d\n", + __func__, __LINE__, panel->dba_config.bridge_name, len); + rc = -EINVAL; + goto error; + } + } + +error: + return rc; +} + struct dsi_panel *dsi_panel_get(struct device *parent, struct device_node *of_node) { @@ -1560,6 +1595,10 @@ struct dsi_panel *dsi_panel_get(struct device *parent, if (rc) pr_err("failed to parse backlight config, rc=%d\n", rc); + rc = dsi_panel_parse_dba_config(panel, of_node); + if (rc) + pr_err("failed to parse dba config, rc=%d\n", rc); + panel->panel_of_node = of_node; drm_panel_init(&panel->drm_panel); mutex_init(&panel->panel_lock); @@ -1574,6 +1613,9 @@ void dsi_panel_put(struct dsi_panel *panel) { u32 i; + if (!panel) + return; + for (i = 0; i < DSI_CMD_SET_MAX; i++) dsi_panel_destroy_cmd_packets(&panel->cmd_sets[i]); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h index 4d21a4cf6428..8106ed1261b4 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -132,6 +132,18 @@ struct dsi_panel_reset_config { int disp_en_gpio; }; +/** + * struct dsi_panel_dba - DSI DBA panel information + * @dba_panel: Indicate if it's DBA panel + * @bridge_name: Bridge chip name + * @hdmi_mode: If bridge chip is in hdmi mode. + */ +struct dsi_panel_dba { + bool dba_panel; + const char *bridge_name; + bool hdmi_mode; +}; + struct dsi_panel { const char *name; struct device_node *panel_of_node; @@ -158,6 +170,8 @@ struct dsi_panel { struct dsi_panel_reset_config reset_config; struct dsi_pinctrl_info pinctrl; + struct dsi_panel_dba dba_config; + bool lp11_init; }; diff --git a/drivers/gpu/drm/msm/sde/sde_backlight.c b/drivers/gpu/drm/msm/sde/sde_backlight.c index 9034eeb944fe..78df28a0016b 100644 --- a/drivers/gpu/drm/msm/sde/sde_backlight.c +++ b/drivers/gpu/drm/msm/sde/sde_backlight.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,15 +37,15 @@ static int sde_backlight_device_update_status(struct backlight_device *bd) connector = bl_get_data(bd); c_conn = to_sde_connector(connector); display = (struct dsi_display *) c_conn->display; - if (brightness > display->panel->bl_config.bl_max_level) - brightness = display->panel->bl_config.bl_max_level; + if (brightness > display->panel[0]->bl_config.bl_max_level) + brightness = display->panel[0]->bl_config.bl_max_level; /* This maps UI brightness into driver backlight level with * rounding */ SDE_BRIGHT_TO_BL(bl_lvl, brightness, - display->panel->bl_config.bl_max_level, - display->panel->bl_config.brightness_max_level); + display->panel[0]->bl_config.bl_max_level, + display->panel[0]->bl_config.brightness_max_level); if (!bl_lvl && brightness) bl_lvl = 1; @@ -85,7 +85,7 @@ int sde_backlight_setup(struct drm_connector *connector) switch (c_conn->connector_type) { case DRM_MODE_CONNECTOR_DSI: display = (struct dsi_display *) c_conn->display; - bl_config = &display->panel->bl_config; + bl_config = &display->panel[0]->bl_config; props.max_brightness = bl_config->brightness_max_level; props.brightness = bl_config->brightness_max_level; bd = backlight_device_register("sde-backlight", |
