summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJin Li <jinl@codeaurora.org>2016-10-06 14:43:08 -0400
committerYunyun Cao <yunyunc@codeaurora.org>2017-03-20 17:10:39 +0800
commita12f96b0d2d11a89b68d6581acf759b10c36a68f (patch)
treee5e77734f1f78a4cb132f38ff2f6372e14c815ca
parentd6ca6477e2a6f0458e8e287844b532b7c0385066 (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.txt1
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/dba_bridge.c345
-rw-r--r--drivers/gpu/drm/msm/dba_bridge.h65
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c29
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_defs.h4
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_display.c376
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_display.h13
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_drm.c119
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_panel.c44
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_panel.h16
-rw-r--r--drivers/gpu/drm/msm/sde/sde_backlight.c12
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",