summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
diff options
context:
space:
mode:
authorJeykumar Sankaran <jsanka@codeaurora.org>2018-06-27 15:26:09 -0400
committerSean Paul <seanpaul@chromium.org>2018-07-26 10:45:04 -0400
commit25fdd5933e4c0f5fe2ea5cd59994f8ac5fbe90ef (patch)
tree0da1ad5999deaee810a28b4bfa47d60d4822e00c /drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
parent036bfeb33b89578ab924fa198213260659e08dc8 (diff)
downloadlinux-25fdd5933e4c0f5fe2ea5cd59994f8ac5fbe90ef.tar.gz
linux-25fdd5933e4c0f5fe2ea5cd59994f8ac5fbe90ef.tar.bz2
linux-25fdd5933e4c0f5fe2ea5cd59994f8ac5fbe90ef.zip
drm/msm: Add SDM845 DPU support
SDM845 SoC includes the Mobile Display Sub System (MDSS) which is a top level wrapper consisting of Display Processing Unit (DPU) and display peripheral modules such as Display Serial Interface (DSI) and DisplayPort (DP). MDSS functions essentially as a back-end composition engine. It blends video and graphic images stored in the frame buffers and scans out the composed image to a display sink (over DSI/DP). The following diagram represents hardware blocks for a simple pipeline (two planes are present on a given crtc which is connected to a DSI connector): MDSS +---------------------------------+ | +-----------------------------+ | | | DPU | | | | +--------+ +--------+ | | | | | SSPP | | SSPP | | | | | +----+---+ +----+---+ | | | | | | | | | | +----v-----------v---+ | | | | | Layer Mixer (LM) | | | | | +--------------------+ | | | | +--------------------+ | | | | | PingPong (PP) | | | | | +--------------------+ | | | | +--------------------+ | | | | | INTERFACE (VIDEO) | | | | | +---+----------------+ | | | +------|----------------------+ | | | | | +------|---------------------+ | | | | DISPLAY PERIPHERALS | | | | +---v-+ +-----+ | | | | | DSI | | DP | | | | | +-----+ +-----+ | | | +----------------------------+ | +---------------------------------+ The number of DPU sub-blocks (i.e. SSPPs, LMs, PP blocks and INTFs) depends on SoC capabilities. Overview of DPU sub-blocks: --------------------------- * Source Surface Processor (SSPP): Refers to any of hardware pipes like ViG, DMA etc. Only ViG pipes are capable of performing format conversion, scaling and quality improvement for source surfaces. * Layer Mixer (LM): Blend source surfaces together (in requested zorder) * PingPong (PP): This block controls frame done interrupt output, EOL and EOF generation, overflow/underflow control. * Display interface (INTF): Timing generator and interface connecting the display peripherals. DRM components mapping to DPU architecture: ------------------------------------------ PLANEs maps to SSPPs CRTC maps to LMs Encoder maps to PPs, INTFs Data flow setup: --------------- MDSS hardware can support various data flows (e.g.): - Dual pipe: Output from two LMs combined to single display. - Split display: Output from two LMs connected to two separate interfaces. The hardware capabilities determine the number of concurrent data paths possible. Any control path (i.e. pipeline w/i DPU) can be routed to any of the hardware data paths. A given control path can be triggered, flushed and controlled independently. Changes in v3: - Move msm_media_info.h from uapi to dpu/ subdir - Remove preclose callback dpu (it's handled in core) - Fix kbuild warnings with parent_ops - Remove unused functions from dpu_core_irq - Rename mdss_phys to mdss - Rename mdp_phys address space to mdp - Drop _phys from vbif and regdma binding names Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org> Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Chandan Uddaraju <chandanu@codeaurora.org> Signed-off-by: Jeykumar Sankaran <jsanka@codeaurora.org> Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org> Signed-off-by: Rajesh Yadav <ryadav@codeaurora.org> Signed-off-by: Sravanthi Kollukuduru <skolluku@codeaurora.org> Signed-off-by: Sean Paul <seanpaul@chromium.org> [robclark minor rebase] Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c')
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
new file mode 100644
index 000000000000..8d907faa7496
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
@@ -0,0 +1,204 @@
+/* Copyright (c) 2012-2015, 2017-2018, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk/clk-conf.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include "dpu_io_util.h"
+
+void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk)
+{
+ int i;
+
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_arry[i].clk)
+ clk_put(clk_arry[i].clk);
+ clk_arry[i].clk = NULL;
+ }
+}
+
+int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < num_clk; i++) {
+ clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name);
+ rc = PTR_RET(clk_arry[i].clk);
+ if (rc) {
+ DEV_ERR("%pS->%s: '%s' get failed. rc=%d\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name, rc);
+ goto error;
+ }
+ }
+
+ return rc;
+
+error:
+ for (i--; i >= 0; i--) {
+ if (clk_arry[i].clk)
+ clk_put(clk_arry[i].clk);
+ clk_arry[i].clk = NULL;
+ }
+
+ return rc;
+}
+
+int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < num_clk; i++) {
+ if (clk_arry[i].clk) {
+ if (clk_arry[i].type != DSS_CLK_AHB) {
+ DEV_DBG("%pS->%s: '%s' rate %ld\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name,
+ clk_arry[i].rate);
+ rc = clk_set_rate(clk_arry[i].clk,
+ clk_arry[i].rate);
+ if (rc) {
+ DEV_ERR("%pS->%s: %s failed. rc=%d\n",
+ __builtin_return_address(0),
+ __func__,
+ clk_arry[i].clk_name, rc);
+ break;
+ }
+ }
+ } else {
+ DEV_ERR("%pS->%s: '%s' is not available\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name);
+ rc = -EPERM;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable)
+{
+ int i, rc = 0;
+
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ DEV_DBG("%pS->%s: enable '%s'\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name);
+ if (clk_arry[i].clk) {
+ rc = clk_prepare_enable(clk_arry[i].clk);
+ if (rc)
+ DEV_ERR("%pS->%s: %s en fail. rc=%d\n",
+ __builtin_return_address(0),
+ __func__,
+ clk_arry[i].clk_name, rc);
+ } else {
+ DEV_ERR("%pS->%s: '%s' is not available\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name);
+ rc = -EPERM;
+ }
+
+ if (rc) {
+ msm_dss_enable_clk(&clk_arry[i],
+ i, false);
+ break;
+ }
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ DEV_DBG("%pS->%s: disable '%s'\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name);
+
+ if (clk_arry[i].clk)
+ clk_disable_unprepare(clk_arry[i].clk);
+ else
+ DEV_ERR("%pS->%s: '%s' is not available\n",
+ __builtin_return_address(0), __func__,
+ clk_arry[i].clk_name);
+ }
+ }
+
+ return rc;
+}
+
+int msm_dss_parse_clock(struct platform_device *pdev,
+ struct dss_module_power *mp)
+{
+ u32 i, rc = 0;
+ const char *clock_name;
+ int num_clk = 0;
+
+ if (!pdev || !mp)
+ return -EINVAL;
+
+ mp->num_clk = 0;
+ num_clk = of_property_count_strings(pdev->dev.of_node,
+ "assigned-clock-names");
+ if (num_clk <= 0) {
+ pr_debug("clocks are not defined\n");
+ return 0;
+ }
+
+ mp->clk_config = devm_kzalloc(&pdev->dev,
+ sizeof(struct dss_clk) * num_clk,
+ GFP_KERNEL);
+ if (!mp->clk_config)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clk; i++) {
+ rc = of_property_read_string_index(pdev->dev.of_node,
+ "assigned-clock-names", i,
+ &clock_name);
+ if (rc) {
+ dev_err(&pdev->dev, "Failed to get clock name for %d\n",
+ i);
+ break;
+ }
+ strlcpy(mp->clk_config[i].clk_name, clock_name,
+ sizeof(mp->clk_config[i].clk_name));
+
+ mp->clk_config[i].type = DSS_CLK_AHB;
+ }
+
+ rc = msm_dss_get_clk(&pdev->dev, mp->clk_config, num_clk);
+ if (rc) {
+ dev_err(&pdev->dev, "Failed to get clock refs %d\n", rc);
+ goto err;
+ }
+
+ rc = of_clk_set_defaults(pdev->dev.of_node, false);
+ if (rc) {
+ dev_err(&pdev->dev, "Failed to set clock defaults %d\n", rc);
+ goto err;
+ }
+
+ for (i = 0; i < num_clk; i++) {
+ u32 rate = clk_get_rate(mp->clk_config[i].clk);
+ if (!rate)
+ continue;
+ mp->clk_config[i].rate = rate;
+ mp->clk_config[i].type = DSS_CLK_PCLK;
+ }
+
+ mp->num_clk = num_clk;
+ return 0;
+
+err:
+ msm_dss_put_clk(mp->clk_config, num_clk);
+ return rc;
+}