Merge "ARM: dts: msm: Add support for AOP clock for SDM845" into msm-4.9
diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt
index d1f8ce1..eaa7146b 100644
--- a/Documentation/devicetree/bindings/arm/msm/imem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/imem.txt
@@ -57,6 +57,11 @@
-compatible: "qcom,msm-imem-emergency_download_mode"
-reg: start address and size of emergency_download_mode region in imem
+Kaslr Offset:
+------------------------
+-compatible: "qcom,msm-imem-kaslr_offset"
+-reg: start address and size of kaslr_offset region in imem
+
USB Diag Cookies:
-----------------
Memory region used to store USB PID and serial numbers to be used by
@@ -95,6 +100,12 @@
reg = <0x6b0 32>;
};
+ kaslr_offset@6d0 {
+ compatible = "qcom,msm-imem-kaslr_offset";
+ reg = <0x6d0 12>;
+ };
+
+
pil@94c {
compatible = "qcom,msm-imem-pil";
reg = <0x94c 200>;
diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
index c6626d1..3ad0986 100644
--- a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
+++ b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
@@ -6,8 +6,9 @@
DSI Controller:
Required properties:
- compatible: Should be "qcom,dsi-ctrl-hw-v<version>". Supported
- versions include 1.4 and 2.0.
- eg: qcom,dsi-ctrl-hw-v1.4, qcom,dsi-ctrl-hw-v2.0
+ versions include 1.4, 2.0 and 2.2.
+ eg: qcom,dsi-ctrl-hw-v1.4, qcom,dsi-ctrl-hw-v2.0,
+ qcom,dsi-ctrl-hw-v2.2
And for dsi phy driver:
qcom,dsi-phy-v0.0-hpm, qcom,dsi-phy-v0.0-lpm,
qcom,dsi-phy-v1.0, qcom,dsi-phy-v2.0,
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 95e6f6c..da9a632 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -72,6 +72,11 @@
during clock scaling. If this property is not
defined, then it falls back to the default HS
bus speed mode to maintain backward compatibility.
+ - qcom,sdr104-wa: On Certain chipsets, SDR104 mode might be unstable causing CRC errors
+ on the interface. So there is a workaround implemented to skip printing
+ register dumps on CRC errors and also downgrade bus speed mode to
+ SDR50/DDR50 in case of continuous CRC errors. Set this flag to enable
+ this workaround.
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
diff --git a/Documentation/devicetree/bindings/prng/msm-rng.txt b/Documentation/devicetree/bindings/prng/msm-rng.txt
new file mode 100644
index 0000000..917c2fb
--- /dev/null
+++ b/Documentation/devicetree/bindings/prng/msm-rng.txt
@@ -0,0 +1,18 @@
+* RNG (Random Number Generator)
+
+Required properties:
+- compatible : Should be "qcom,msm-rng"
+- reg : Offset and length of the register set for the device
+
+Optional property:
+- qcom,msm-rng-iface-clk : If the device uses iface-clk.
+- qcom,no-qrng-config : Flag to decide whether the driver do the hardware configuration or not.
+
+Example:
+
+ qcom,msm-rng@f9bff000 {
+ compatible = "qcom,msm-rng";
+ reg = <0xf9bff000 0x200>;
+ qcom,msm-rng-iface-clk;
+ qcom,no-qrng-config;
+ };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index cb7ab27..0afa5a8 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -12,6 +12,7 @@
#include "skeleton.dtsi"
+#include <dt-bindings/clock/qcom,gcc-sdxpoorwills.h>
/ {
model = "Qualcomm Technologies, Inc. SDX POORWILLS";
@@ -135,6 +136,18 @@
};
};
+ clock_gcc: qcom,gcc@100000 {
+ compatible = "qcom,dummycc";
+ clock-output-names = "gcc_clocks";
+ #clock-cells = <1>;
+ };
+
+ clock_cpu: qcom,clock-a7@17810008 {
+ compatible = "qcom,dummycc";
+ clock-output-names = "cpu_clocks";
+ #clock-cells = <1>;
+ };
+
blsp1_uart2: serial@831000 {
compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
reg = <0x831000 0x200>;
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index ef75d2f..f4d7965 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -49,5 +49,7 @@
select MSM_JTAG_MM if CORESIGHT_ETM
select PM_DEVFREQ
select COMMON_CLK
+ select COMMON_CLK_QCOM
+ select QCOM_GDSC
endmenu
endif
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 2f45c41..522ad95 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -273,12 +273,13 @@
};
mdss_dsi0: qcom,mdss_dsi_ctrl0@ae94000 {
- compatible = "qcom,dsi-ctrl-hw-v2.0";
+ compatible = "qcom,dsi-ctrl-hw-v2.2";
label = "dsi-ctrl-0";
status = "disabled";
cell-index = <0>;
- reg = <0xae94000 0x400>;
- reg-names = "dsi_ctrl";
+ reg = <0xae94000 0x400>,
+ <0xaf08000 0x4>;
+ reg-names = "dsi_ctrl", "disp_cc_base";
interrupt-parent = <&mdss_mdp>;
interrupts = <4 0>;
vdda-1p2-supply = <&pm8998_l26>;
@@ -315,12 +316,13 @@
};
mdss_dsi1: qcom,mdss_dsi_ctrl1@ae96000 {
- compatible = "qcom,dsi-ctrl-hw-v2.0";
+ compatible = "qcom,dsi-ctrl-hw-v2.2";
label = "dsi-ctrl-1";
status = "disabled";
cell-index = <1>;
- reg = <0xae96000 0x400>;
- reg-names = "dsi_ctrl";
+ reg = <0xae96000 0x400>,
+ <0xaf08000 0x4>;
+ reg-names = "dsi_ctrl", "disp_cc_base";
interrupt-parent = <&mdss_mdp>;
interrupts = <5 0>;
vdda-1p2-supply = <&pm8998_l26>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 092acb0..2c48371 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1271,6 +1271,8 @@
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 4294967295>;
+ qcom,sdr104-wa;
+
qcom,devfreq,freq-table = <50000000 200000000>;
clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>,
<&clock_gcc GCC_SDCC2_APPS_CLK>;
@@ -1580,6 +1582,11 @@
compatible = "qcom,msm-imem-pil";
reg = <0x94c 200>;
};
+
+ kaslr_offset@6d0 {
+ compatible = "qcom,msm-imem-kaslr_offset";
+ reg = <0x6d0 12>;
+ };
};
qcom,venus@aae0000 {
@@ -2064,6 +2071,29 @@
qcom,qsee-reentrancy-support = <2>;
};
+ qcom_rng: qrng@793000 {
+ compatible = "qcom,msm-rng";
+ reg = <0x793000 0x1000>;
+ qcom,msm-rng-iface-clk;
+ qcom,no-qrng-config;
+ qcom,msm-bus,name = "msm-rng-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 618 0 0>, /* No vote */
+ <1 618 0 800>; /* 100 KHz */
+ clocks = <&clock_gcc GCC_PRNG_AHB_CLK>;
+ clock-names = "iface_clk";
+ };
+
+ qcom_tzlog: tz-log@146bf720 {
+ compatible = "qcom,tz-log";
+ reg = <0x146bf720 0x3000>;
+ qcom,hyplog-enabled;
+ hyplog-address-offset = <0x410>;
+ hyplog-size-offset = <0x414>;
+ };
+
qcom,msm_gsi {
compatible = "qcom,msm_gsi";
};
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 658c871..a2d659e 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -275,6 +275,7 @@
CONFIG_SERIAL_MSM_GENI=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_MSM_ADSPRPC=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QCOM_GENI=y
@@ -490,6 +491,7 @@
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 7507c3b..54f9d41 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -282,9 +282,8 @@
CONFIG_SERIAL_MSM_GENI=y
CONFIG_SERIAL_MSM_GENI_CONSOLE=y
CONFIG_DIAG_CHAR=y
-CONFIG_HVC_DCC=y
-CONFIG_HVC_DCC_SERIALIZE_SMP=y
CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_MSM_ADSPRPC=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QCOM_GENI=y
@@ -513,6 +512,7 @@
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 200dab5..18849f4 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -337,7 +337,6 @@
config HW_RANDOM_MSM
tristate "Qualcomm SoCs Random Number Generator support"
depends on HW_RANDOM && ARCH_QCOM
- default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Qualcomm SoCs.
@@ -347,6 +346,20 @@
If unsure, say Y.
+config HW_RANDOM_MSM_LEGACY
+ tristate "QTI MSM Random Number Generator support (LEGACY)"
+ depends on HW_RANDOM && ARCH_QCOM
+ select CRYPTO_AES
+ select CRYPTO_ECB
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on QTI MSM SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called msm_rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_ST
tristate "ST Microelectronics HW Random Number Generator support"
depends on HW_RANDOM && ARCH_STI
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 5f52b1e..637adb5 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -30,6 +30,7 @@
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o
+obj-$(CONFIG_HW_RANDOM_MSM_LEGACY) += msm_rng.o
obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o
obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
new file mode 100644
index 0000000..7641a6a
--- /dev/null
+++ b/drivers/char/hw_random/msm_rng.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 2011-2013, 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
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <soc/qcom/socinfo.h>
+#include <linux/msm-bus.h>
+#include <linux/qrng.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/crypto.h>
+#include <crypto/internal/rng.h>
+
+#include <linux/platform_data/qcom_crypto_device.h>
+
+
+
+#define DRIVER_NAME "msm_rng"
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT_OFFSET 0x0000
+#define PRNG_STATUS_OFFSET 0x0004
+#define PRNG_LFSR_CFG_OFFSET 0x0100
+#define PRNG_CONFIG_OFFSET 0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK 0xFFFF0000
+#define PRNG_LFSR_CFG_CLOCKS 0x0000DDDD
+#define PRNG_CONFIG_MASK 0xFFFFFFFD
+#define PRNG_HW_ENABLE 0x00000002
+
+#define MAX_HW_FIFO_DEPTH 16 /* FIFO is 16 words deep */
+#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide */
+
+struct msm_rng_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+ struct clk *prng_clk;
+ uint32_t qrng_perf_client;
+ struct mutex rng_lock;
+};
+
+struct msm_rng_device msm_rng_device_info;
+static struct msm_rng_device *msm_rng_dev_cached;
+struct mutex cached_rng_lock;
+static long msm_rng_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+
+ switch (cmd) {
+ case QRNG_IOCTL_RESET_BUS_BANDWIDTH:
+ pr_info("calling msm_rng_bus_scale(LOW)\n");
+ ret = msm_bus_scale_client_update_request(
+ msm_rng_device_info.qrng_perf_client, 0);
+ if (ret)
+ pr_err("failed qrng_reset_bus_bw, ret = %ld\n", ret);
+ break;
+ default:
+ pr_err("Unsupported IOCTL call");
+ break;
+ }
+ return ret;
+}
+
+/*
+ *
+ * This function calls hardware random bit generator directory and retuns it
+ * back to caller
+ *
+ */
+static int msm_rng_direct_read(struct msm_rng_device *msm_rng_dev,
+ void *data, size_t max)
+{
+ struct platform_device *pdev;
+ void __iomem *base;
+ size_t currsize = 0;
+ u32 val;
+ u32 *retdata = data;
+ int ret;
+ int failed = 0;
+
+ pdev = msm_rng_dev->pdev;
+ base = msm_rng_dev->base;
+
+ /* no room for word data */
+ if (max < 4)
+ return 0;
+
+ mutex_lock(&msm_rng_dev->rng_lock);
+
+ if (msm_rng_dev->qrng_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ msm_rng_dev->qrng_perf_client, 1);
+ if (ret)
+ pr_err("bus_scale_client_update_req failed!\n");
+ }
+ /* enable PRNG clock */
+ ret = clk_prepare_enable(msm_rng_dev->prng_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock in callback\n");
+ goto err;
+ }
+ /* read random data from h/w */
+ do {
+ /* check status bit if data is available */
+ while (!(readl_relaxed(base + PRNG_STATUS_OFFSET)
+ & 0x00000001)) {
+ if (failed == 10) {
+ pr_err("Data not available after retry\n");
+ break;
+ }
+ pr_err("msm_rng:Data not available!\n");
+ msleep_interruptible(10);
+ failed++;
+ }
+
+ /* read FIFO */
+ val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
+ if (!val)
+ break; /* no data to read so just bail */
+
+ /* write data back to callers pointer */
+ *(retdata++) = val;
+ currsize += 4;
+ /* make sure we stay on 32bit boundary */
+ if ((max - currsize) < 4)
+ break;
+
+ } while (currsize < max);
+
+ /* vote to turn off clock */
+ clk_disable_unprepare(msm_rng_dev->prng_clk);
+err:
+ if (msm_rng_dev->qrng_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ msm_rng_dev->qrng_perf_client, 0);
+ if (ret)
+ pr_err("bus_scale_client_update_req failed!\n");
+ }
+ mutex_unlock(&msm_rng_dev->rng_lock);
+
+ val = 0L;
+ return currsize;
+}
+static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ struct msm_rng_device *msm_rng_dev;
+ int rv = 0;
+
+ msm_rng_dev = (struct msm_rng_device *)rng->priv;
+ rv = msm_rng_direct_read(msm_rng_dev, data, max);
+
+ return rv;
+}
+
+
+static struct hwrng msm_rng = {
+ .name = DRIVER_NAME,
+ .read = msm_rng_read,
+ .quality = 700,
+};
+
+static int msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
+{
+ unsigned long val = 0;
+ unsigned long reg_val = 0;
+ int ret = 0;
+
+ if (msm_rng_dev->qrng_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ msm_rng_dev->qrng_perf_client, 1);
+ if (ret)
+ pr_err("bus_scale_client_update_req failed!\n");
+ }
+ /* Enable the PRNG CLK */
+ ret = clk_prepare_enable(msm_rng_dev->prng_clk);
+ if (ret) {
+ dev_err(&(msm_rng_dev->pdev)->dev,
+ "failed to enable clock in probe\n");
+ return -EPERM;
+ }
+
+ /* Enable PRNG h/w only if it is NOT ON */
+ val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET) &
+ PRNG_HW_ENABLE;
+ /* PRNG H/W is not ON */
+ if (val != PRNG_HW_ENABLE) {
+ val = readl_relaxed(msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
+ val &= PRNG_LFSR_CFG_MASK;
+ val |= PRNG_LFSR_CFG_CLOCKS;
+ writel_relaxed(val, msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
+
+ /* The PRNG CONFIG register should be first written */
+ mb();
+
+ reg_val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET)
+ & PRNG_CONFIG_MASK;
+ reg_val |= PRNG_HW_ENABLE;
+ writel_relaxed(reg_val, msm_rng_dev->base + PRNG_CONFIG_OFFSET);
+
+ /* The PRNG clk should be disabled only after we enable the
+ * PRNG h/w by writing to the PRNG CONFIG register.
+ */
+ mb();
+ }
+ clk_disable_unprepare(msm_rng_dev->prng_clk);
+
+ if (msm_rng_dev->qrng_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ msm_rng_dev->qrng_perf_client, 0);
+ if (ret)
+ pr_err("bus_scale_client_update_req failed!\n");
+ }
+
+ return 0;
+}
+
+static const struct file_operations msm_rng_fops = {
+ .unlocked_ioctl = msm_rng_ioctl,
+};
+static struct class *msm_rng_class;
+static struct cdev msm_rng_cdev;
+
+static int msm_rng_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct msm_rng_device *msm_rng_dev = NULL;
+ void __iomem *base = NULL;
+ bool configure_qrng = true;
+ int error = 0;
+ int ret = 0;
+ struct device *dev;
+
+ struct msm_bus_scale_pdata *qrng_platform_support = NULL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "invalid address\n");
+ error = -EFAULT;
+ goto err_exit;
+ }
+
+ msm_rng_dev = kzalloc(sizeof(struct msm_rng_device), GFP_KERNEL);
+ if (!msm_rng_dev) {
+ error = -ENOMEM;
+ goto err_exit;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ error = -ENOMEM;
+ goto err_iomap;
+ }
+ msm_rng_dev->base = base;
+
+ /* create a handle for clock control */
+ if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
+ "qcom,msm-rng-iface-clk")))
+ msm_rng_dev->prng_clk = clk_get(&pdev->dev,
+ "iface_clk");
+ else
+ msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(msm_rng_dev->prng_clk)) {
+ dev_err(&pdev->dev, "failed to register clock source\n");
+ error = -EPERM;
+ goto err_clk_get;
+ }
+
+ /* save away pdev and register driver data */
+ msm_rng_dev->pdev = pdev;
+ platform_set_drvdata(pdev, msm_rng_dev);
+
+ if (pdev->dev.of_node) {
+ /* Register bus client */
+ qrng_platform_support = msm_bus_cl_get_pdata(pdev);
+ msm_rng_dev->qrng_perf_client = msm_bus_scale_register_client(
+ qrng_platform_support);
+ msm_rng_device_info.qrng_perf_client =
+ msm_rng_dev->qrng_perf_client;
+ if (!msm_rng_dev->qrng_perf_client)
+ pr_err("Unable to register bus client\n");
+ }
+
+ /* Enable rng h/w for the targets which can access the entire
+ * address space of PRNG.
+ */
+ if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
+ "qcom,no-qrng-config")))
+ configure_qrng = false;
+ if (configure_qrng) {
+ error = msm_rng_enable_hw(msm_rng_dev);
+ if (error)
+ goto rollback_clk;
+ }
+
+ mutex_init(&msm_rng_dev->rng_lock);
+ mutex_init(&cached_rng_lock);
+
+ /* register with hwrng framework */
+ msm_rng.priv = (unsigned long) msm_rng_dev;
+ error = hwrng_register(&msm_rng);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register hwrng\n");
+ error = -EPERM;
+ goto rollback_clk;
+ }
+ ret = register_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME, &msm_rng_fops);
+
+ msm_rng_class = class_create(THIS_MODULE, "msm-rng");
+ if (IS_ERR(msm_rng_class)) {
+ pr_err("class_create failed\n");
+ return PTR_ERR(msm_rng_class);
+ }
+
+ dev = device_create(msm_rng_class, NULL, MKDEV(QRNG_IOC_MAGIC, 0),
+ NULL, "msm-rng");
+ if (IS_ERR(dev)) {
+ pr_err("Device create failed\n");
+ error = PTR_ERR(dev);
+ goto unregister_chrdev;
+ }
+ cdev_init(&msm_rng_cdev, &msm_rng_fops);
+ msm_rng_dev_cached = msm_rng_dev;
+ return error;
+
+unregister_chrdev:
+ unregister_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME);
+rollback_clk:
+ clk_put(msm_rng_dev->prng_clk);
+err_clk_get:
+ iounmap(msm_rng_dev->base);
+err_iomap:
+ kzfree(msm_rng_dev);
+err_exit:
+ return error;
+}
+
+static int msm_rng_remove(struct platform_device *pdev)
+{
+ struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
+
+ unregister_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME);
+ hwrng_unregister(&msm_rng);
+ clk_put(msm_rng_dev->prng_clk);
+ iounmap(msm_rng_dev->base);
+ platform_set_drvdata(pdev, NULL);
+ if (msm_rng_dev->qrng_perf_client)
+ msm_bus_scale_unregister_client(msm_rng_dev->qrng_perf_client);
+
+ kzfree(msm_rng_dev);
+ msm_rng_dev_cached = NULL;
+ return 0;
+}
+
+static int qrng_get_random(struct crypto_rng *tfm, const u8 *src,
+ unsigned int slen, u8 *rdata,
+ unsigned int dlen)
+{
+ int sizeread = 0;
+ int rv = -EFAULT;
+
+ if (!msm_rng_dev_cached) {
+ pr_err("%s: msm_rng_dev is not initialized.\n", __func__);
+ rv = -ENODEV;
+ goto err_exit;
+ }
+
+ if (!rdata) {
+ pr_err("%s: data buffer is null!\n", __func__);
+ rv = -EINVAL;
+ goto err_exit;
+ }
+
+ if (signal_pending(current) ||
+ mutex_lock_interruptible(&cached_rng_lock)) {
+ pr_err("%s: mutex lock interrupted!\n", __func__);
+ rv = -ERESTARTSYS;
+ goto err_exit;
+ }
+ sizeread = msm_rng_direct_read(msm_rng_dev_cached, rdata, dlen);
+
+ if (sizeread == dlen)
+ rv = 0;
+
+ mutex_unlock(&cached_rng_lock);
+err_exit:
+ return rv;
+
+}
+
+static int qrng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen)
+{
+ return 0;
+}
+
+static struct rng_alg rng_algs[] = { {
+ .generate = qrng_get_random,
+ .seed = qrng_reset,
+ .seedsize = 0,
+ .base = {
+ .cra_name = "qrng",
+ .cra_driver_name = "fips_hw_qrng",
+ .cra_priority = 300,
+ .cra_ctxsize = 0,
+ .cra_module = THIS_MODULE,
+ }
+} };
+
+static const struct of_device_id qrng_match[] = {
+ { .compatible = "qcom,msm-rng",
+ },
+ {}
+};
+
+static struct platform_driver rng_driver = {
+ .probe = msm_rng_probe,
+ .remove = msm_rng_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qrng_match,
+ }
+};
+
+static int __init msm_rng_init(void)
+{
+ int ret;
+
+ msm_rng_dev_cached = NULL;
+ ret = platform_driver_register(&rng_driver);
+ if (ret) {
+ pr_err("%s: platform_driver_register error:%d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+ ret = crypto_register_rngs(rng_algs, ARRAY_SIZE(rng_algs));
+ if (ret) {
+ pr_err("%s: crypto_register_algs error:%d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+err_exit:
+ return ret;
+}
+
+module_init(msm_rng_init);
+
+static void __exit msm_rng_exit(void)
+{
+ crypto_unregister_rngs(rng_algs, ARRAY_SIZE(rng_algs));
+ platform_driver_unregister(&rng_driver);
+}
+
+module_exit(msm_rng_exit);
+
+MODULE_DESCRIPTION("QTI MSM Random Number Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
index 6ce0d76..3daefbc 100644
--- a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
@@ -40,6 +40,8 @@
#define PLL_CALIBRATION_SETTINGS 0x030
#define PLL_BAND_SEL_CAL_SETTINGS_THREE 0x054
#define PLL_FREQ_DETECT_SETTINGS_ONE 0x064
+#define PLL_PFILT 0x07c
+#define PLL_IFILT 0x080
#define PLL_OUTDIV 0x094
#define PLL_CORE_OVERRIDE 0x0a4
#define PLL_CORE_INPUT_OVERRIDE 0x0a8
@@ -63,6 +65,7 @@
#define PLL_PLL_FL_INT_GAIN_PFILT_BAND_1 0x164
#define PLL_PLL_LOCK_OVERRIDE 0x180
#define PLL_PLL_LOCK_DELAY 0x184
+#define PLL_CLOCK_INVERTERS 0x18c
#define PLL_COMMON_STATUS_ONE 0x1a0
/* Register Offsets from PHY base address */
@@ -338,7 +341,6 @@
MDSS_PLL_REG_W(pll_base, PLL_ANALOG_CONTROLS_THREE, 0x00);
MDSS_PLL_REG_W(pll_base, PLL_DSM_DIVIDER, 0x00);
MDSS_PLL_REG_W(pll_base, PLL_FEEDBACK_DIVIDER, 0x4e);
- MDSS_PLL_REG_W(pll_base, PLL_CMODE, 0x00);
MDSS_PLL_REG_W(pll_base, PLL_CALIBRATION_SETTINGS, 0x40);
MDSS_PLL_REG_W(pll_base, PLL_BAND_SEL_CAL_SETTINGS_THREE, 0xba);
MDSS_PLL_REG_W(pll_base, PLL_FREQ_DETECT_SETTINGS_ONE, 0x0c);
@@ -347,9 +349,11 @@
MDSS_PLL_REG_W(pll_base, PLL_PLL_DIGITAL_TIMERS_TWO, 0x08);
MDSS_PLL_REG_W(pll_base, PLL_PLL_PROP_GAIN_RATE_1, 0x08);
MDSS_PLL_REG_W(pll_base, PLL_PLL_BAND_SET_RATE_1, 0xc0);
- MDSS_PLL_REG_W(pll_base, PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x82);
+ MDSS_PLL_REG_W(pll_base, PLL_PLL_INT_GAIN_IFILT_BAND_1, 0xfa);
MDSS_PLL_REG_W(pll_base, PLL_PLL_FL_INT_GAIN_PFILT_BAND_1, 0x4c);
MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCK_OVERRIDE, 0x80);
+ MDSS_PLL_REG_W(pll_base, PLL_PFILT, 0x29);
+ MDSS_PLL_REG_W(pll_base, PLL_IFILT, 0x3f);
}
static void dsi_pll_commit(struct dsi_pll_10nm *pll,
@@ -367,9 +371,11 @@
reg->frac_div_start_mid);
MDSS_PLL_REG_W(pll_base, PLL_FRAC_DIV_START_HIGH_1,
reg->frac_div_start_high);
- MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCKDET_RATE_1, 0xc8);
+ MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCKDET_RATE_1, 0x40);
MDSS_PLL_REG_W(pll_base, PLL_PLL_OUTDIV_RATE, reg->pll_outdiv_rate);
- MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCK_DELAY, 0x0a);
+ MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCK_DELAY, 0x06);
+ MDSS_PLL_REG_W(pll_base, PLL_CMODE, 0x10);
+ MDSS_PLL_REG_W(pll_base, PLL_CLOCK_INVERTERS, 0x0);
}
@@ -450,8 +456,8 @@
{
u32 data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CTRL_0);
- MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data & ~BIT(5));
MDSS_PLL_REG_W(rsc->pll_base, PLL_SYSTEM_MUXES, 0);
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data & ~BIT(5));
ndelay(250);
}
@@ -464,6 +470,22 @@
ndelay(250);
}
+static void dsi_pll_disable_global_clk(struct mdss_pll_resources *rsc)
+{
+ u32 data;
+
+ data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CLK_CFG1);
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CLK_CFG1, (data & ~BIT(5)));
+}
+
+static void dsi_pll_enable_global_clk(struct mdss_pll_resources *rsc)
+{
+ u32 data;
+
+ data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CLK_CFG1);
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CLK_CFG1, (data | BIT(5)));
+}
+
static int dsi_pll_enable(struct dsi_pll_vco_clk *vco)
{
int rc;
@@ -490,6 +512,11 @@
}
rsc->pll_on = true;
+
+ dsi_pll_enable_global_clk(rsc);
+ if (rsc->slave)
+ dsi_pll_enable_global_clk(rsc->slave);
+
MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_RBUF_CTRL, 0x01);
if (rsc->slave)
MDSS_PLL_REG_W(rsc->slave->phy_base, PHY_CMN_RBUF_CTRL, 0x01);
@@ -500,8 +527,9 @@
static void dsi_pll_disable_sub(struct mdss_pll_resources *rsc)
{
- dsi_pll_disable_pll_bias(rsc);
+ dsi_pll_disable_global_clk(rsc);
MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_RBUF_CTRL, 0);
+ dsi_pll_disable_pll_bias(rsc);
}
static void dsi_pll_disable(struct dsi_pll_vco_clk *vco)
@@ -613,6 +641,9 @@
u32 outdiv;
u64 pll_freq, tmp64;
+ if (!vco->priv)
+ pr_err("vco priv is null\n");
+
rc = mdss_pll_resource_enable(pll, true);
if (rc) {
pr_err("failed to enable pll(%d) resource, rc=%d\n",
@@ -671,9 +702,11 @@
reg_val = MDSS_PLL_REG_R(pll->phy_base, PHY_CMN_CLK_CFG0);
*div = (reg_val & 0xF0) >> 4;
- if (*div == 0)
- *div = 1;
- else
+ /**
+ * Common clock framework the divider value is interpreted as one less
+ * hence we return one less for all dividers except when zero
+ */
+ if (*div != 0)
*div -= 1;
(void)mdss_pll_resource_enable(pll, false);
@@ -701,13 +734,15 @@
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
- /* In common clock framework the divider value provided is one less */
+ /**
+ * In common clock framework the divider value provided is one less and
+ * and hence adjusting the divider value by one prior to writing it to
+ * hardware
+ */
div++;
-
pixel_clk_set_div_sub(pll, div);
if (pll->slave)
pixel_clk_set_div_sub(pll->slave, div);
-
(void)mdss_pll_resource_enable(pll, false);
return 0;
@@ -728,12 +763,12 @@
reg_val = MDSS_PLL_REG_R(pll->phy_base, PHY_CMN_CLK_CFG0);
*div = (reg_val & 0x0F);
- /* Common clock framework will add one to divider value sent */
- if (*div == 0)
- *div = 1;
- else
+ /**
+ *Common clock framework the divider value is interpreted as one less
+ * hence we return one less for all dividers except when zero
+ */
+ if (*div != 0)
*div -= 1;
-
(void)mdss_pll_resource_enable(pll, false);
return rc;
@@ -771,6 +806,12 @@
pr_err("Failed to enable dsi pll resources, rc=%d\n", rc);
return rc;
}
+
+ /**
+ * In common clock framework the divider value provided is one less and
+ * and hence adjusting the divider value by one prior to writing it to
+ * hardware
+ */
div++;
bit_clk_set_div_sub(rsc, div);
@@ -806,9 +847,11 @@
else
*div = 1;
- if (*div == 0)
- *div = 1;
- else
+ /**
+ *Common clock framework the divider value is interpreted as one less
+ * hence we return one less for all dividers except when zero
+ */
+ if (*div != 0)
*div -= 1;
(void)mdss_pll_resource_enable(pll, false);
@@ -851,8 +894,12 @@
return rc;
}
+ /**
+ * In common clock framework the divider value provided is one less and
+ * and hence adjusting the divider value by one prior to writing it to
+ * hardware
+ */
div++;
-
rc = post_vco_clk_set_div_sub(pll, div);
if (!rc && pll->slave)
rc = post_vco_clk_set_div_sub(pll->slave, div);
@@ -885,9 +932,11 @@
else
*div = 1;
- if (*div == 0)
- *div = 1;
- else
+ /**
+ *Common clock framework the divider value is interpreted as one less
+ * hence we return one less for all dividers except when zero
+ */
+ if (*div != 0)
*div -= 1;
(void)mdss_pll_resource_enable(pll, false);
@@ -930,8 +979,12 @@
return rc;
}
+ /**
+ * In common clock framework the divider value provided is one less and
+ * and hence adjusting the divider value by one prior to writing it to
+ * hardware
+ */
div++;
-
rc = post_bit_clk_set_div_sub(pll, div);
if (!rc && pll->slave)
rc = post_bit_clk_set_div_sub(pll->slave, div);
@@ -1057,7 +1110,6 @@
};
static struct clk_regmap_div dsi0pll_bitclk_src = {
- .reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
@@ -1072,7 +1124,6 @@
};
static struct clk_regmap_div dsi1pll_bitclk_src = {
- .reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
@@ -1087,9 +1138,8 @@
};
static struct clk_regmap_div dsi0pll_post_vco_div = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 2,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_post_vco_div",
@@ -1102,9 +1152,8 @@
};
static struct clk_regmap_div dsi1pll_post_vco_div = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 2,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_post_vco_div",
@@ -1141,9 +1190,8 @@
};
static struct clk_regmap_div dsi0pll_post_bit_div = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_post_bit_div",
@@ -1156,9 +1204,8 @@
};
static struct clk_regmap_div dsi1pll_post_bit_div = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_post_bit_div",
@@ -1171,12 +1218,11 @@
};
static struct clk_regmap_mux dsi0pll_byteclk_mux = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 0,
.clkr = {
.hw.init = &(struct clk_init_data){
- .name = "dsi0pll_byteclk_mux",
+ .name = "dsi0_phy_pll_out_byteclk",
.parent_names = (const char *[]){"dsi0pll_byteclk_src"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
@@ -1186,12 +1232,11 @@
};
static struct clk_regmap_mux dsi1pll_byteclk_mux = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 0,
.clkr = {
.hw.init = &(struct clk_init_data){
- .name = "dsi1pll_byteclk_mux",
+ .name = "dsi1_phy_pll_out_byteclk",
.parent_names = (const char *[]){"dsi1pll_byteclk_src"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
@@ -1201,15 +1246,14 @@
};
static struct clk_regmap_mux dsi0pll_pclk_src_mux = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 0,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_pclk_src_mux",
.parent_names = (const char *[]){"dsi0pll_post_bit_div",
- "dsi0pll_post_bit_div"},
- .num_parents = 1,
+ "dsi0pll_post_vco_div"},
+ .num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
@@ -1217,15 +1261,14 @@
};
static struct clk_regmap_mux dsi1pll_pclk_src_mux = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 0,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_pclk_src_mux",
.parent_names = (const char *[]){"dsi1pll_post_bit_div",
- "dsi1pll_post_bit_div"},
- .num_parents = 1,
+ "dsi1pll_post_vco_div"},
+ .num_parents = 2,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
.ops = &clk_regmap_mux_closest_ops,
},
@@ -1233,7 +1276,6 @@
};
static struct clk_regmap_div dsi0pll_pclk_src = {
- .reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
@@ -1249,7 +1291,6 @@
};
static struct clk_regmap_div dsi1pll_pclk_src = {
- .reg = 0x48,
.shift = 0,
.width = 4,
.clkr = {
@@ -1265,12 +1306,11 @@
};
static struct clk_regmap_mux dsi0pll_pclk_mux = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 0,
.clkr = {
.hw.init = &(struct clk_init_data){
- .name = "dsi0pll_pclk_mux",
+ .name = "dsi0_phy_pll_out_dsiclk",
.parent_names = (const char *[]){"dsi0pll_pclk_src"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
@@ -1280,12 +1320,11 @@
};
static struct clk_regmap_mux dsi1pll_pclk_mux = {
- .reg = 0x48,
.shift = 0,
- .width = 4,
+ .width = 0,
.clkr = {
.hw.init = &(struct clk_init_data){
- .name = "dsi1pll_pclk_mux",
+ .name = "dsi1_phy_pll_out_dsiclk",
.parent_names = (const char *[]){"dsi1pll_pclk_src"},
.num_parents = 1,
.flags = (CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT),
@@ -1339,8 +1378,8 @@
}
pll_rsc_db[ndx] = pll_res;
- pll_res->priv = &plls[ndx];
plls[ndx].rsc = pll_res;
+ pll_res->priv = &plls[ndx];
pll_res->vco_delay = VCO_DELAY_USEC;
clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
@@ -1386,6 +1425,7 @@
pll_res, &dsi_pll_10nm_config);
dsi0pll_byteclk_mux.clkr.regmap = rmap;
+ dsi0pll_vco_clk.priv = pll_res;
for (i = VCO_CLK_0; i <= PCLK_MUX_0_CLK; i++) {
clk = devm_clk_register(&pdev->dev,
mdss_dsi_pllcc_10nm[i]);
@@ -1431,6 +1471,7 @@
rmap = devm_regmap_init(&pdev->dev, &mdss_mux_regmap_bus,
pll_res, &dsi_pll_10nm_config);
dsi1pll_byteclk_mux.clkr.regmap = rmap;
+ dsi1pll_vco_clk.priv = pll_res;
for (i = VCO_CLK_1; i <= PCLK_MUX_1_CLK; i++) {
clk = devm_clk_register(&pdev->dev,
diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c
index 0a0d303..7f82fda 100644
--- a/drivers/clk/qcom/mdss/mdss-pll.c
+++ b/drivers/clk/qcom/mdss/mdss-pll.c
@@ -150,6 +150,7 @@
switch (pll_res->pll_interface_type) {
case MDSS_DSI_PLL_10NM:
rc = dsi_pll_clock_register_10nm(pdev, pll_res);
+ break;
case MDSS_UNKNOWN_PLL:
default:
rc = -EINVAL;
@@ -370,7 +371,7 @@
return rc;
}
-subsys_initcall(mdss_pll_driver_init);
+fs_initcall(mdss_pll_driver_init);
static void __exit mdss_pll_driver_deinit(void)
{
diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h
index 28b7ca6..eccfcea 100644
--- a/drivers/clk/qcom/mdss/mdss-pll.h
+++ b/drivers/clk/qcom/mdss/mdss-pll.h
@@ -194,9 +194,7 @@
WARN(1, "gdsc_base register is not defined\n");
return true;
}
-
- return ((readl_relaxed(pll_res->gdsc_base + 0x4) & BIT(31)) &&
- (!(readl_relaxed(pll_res->gdsc_base) & BIT(0)))) ? false : true;
+ return readl_relaxed(pll_res->gdsc_base) & BIT(31) ? false : true;
}
static inline int mdss_pll_div_prepare(struct clk_hw *hw)
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 5b85b8d..8f582f6 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -70,6 +70,29 @@
}
/**
+ * devfreq_set_freq_limits() - Set min and max frequency from freq_table
+ * @devfreq: the devfreq instance
+ */
+static void devfreq_set_freq_limits(struct devfreq *devfreq)
+{
+ int idx;
+ unsigned long min = ~0, max = 0;
+
+ if (!devfreq->profile->freq_table)
+ return;
+
+ for (idx = 0; idx < devfreq->profile->max_state; idx++) {
+ if (min > devfreq->profile->freq_table[idx])
+ min = devfreq->profile->freq_table[idx];
+ if (max < devfreq->profile->freq_table[idx])
+ max = devfreq->profile->freq_table[idx];
+ }
+
+ devfreq->min_freq = min;
+ devfreq->max_freq = max;
+}
+
+/**
* devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance
* @freq: the target frequency
@@ -569,6 +592,7 @@
devfreq_set_freq_table(devfreq);
mutex_lock(&devfreq->lock);
}
+ devfreq_set_freq_limits(devfreq);
dev_set_name(&devfreq->dev, "%s", dev_name(dev));
err = device_register(&devfreq->dev);
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index b5d78b1..4112bef 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -109,6 +109,7 @@
dsi-staging/dsi_ctrl_hw_cmn.o \
dsi-staging/dsi_ctrl_hw_1_4.o \
dsi-staging/dsi_ctrl_hw_2_0.o \
+ dsi-staging/dsi_ctrl_hw_2_2.o \
dsi-staging/dsi_ctrl.o \
dsi-staging/dsi_catalog.o \
dsi-staging/dsi_drm.o \
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index 976be99..1434eac 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -83,6 +83,19 @@
ctrl->ops.clamp_enable = NULL;
ctrl->ops.clamp_disable = NULL;
break;
+ case DSI_CTRL_VERSION_2_2:
+ ctrl->ops.phy_reset_config = dsi_ctrl_hw_22_phy_reset_config;
+ ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map;
+ ctrl->ops.wait_for_lane_idle =
+ dsi_ctrl_hw_20_wait_for_lane_idle;
+ ctrl->ops.reg_dump_to_buffer =
+ dsi_ctrl_hw_20_reg_dump_to_buffer;
+ ctrl->ops.ulps_ops.ulps_request = NULL;
+ ctrl->ops.ulps_ops.ulps_exit = NULL;
+ ctrl->ops.ulps_ops.get_lanes_in_ulps = NULL;
+ ctrl->ops.clamp_enable = NULL;
+ ctrl->ops.clamp_disable = NULL;
+ break;
default:
break;
}
@@ -121,6 +134,7 @@
switch (version) {
case DSI_CTRL_VERSION_1_4:
case DSI_CTRL_VERSION_2_0:
+ case DSI_CTRL_VERSION_2_2:
dsi_catalog_cmn_init(ctrl, version);
break;
default:
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 4a6a934..160cd32 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -157,6 +157,8 @@
void dsi_ctrl_hw_dln0_phy_err(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_phy_reset_config(struct dsi_ctrl_hw *ctrl,
bool enable);
+void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl,
+ bool enable);
/* Definitions specific to 1.4 DSI controller hardware */
int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 5df48c3..9a71ea0 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -62,6 +62,7 @@
static const enum dsi_ctrl_version dsi_ctrl_v1_4 = DSI_CTRL_VERSION_1_4;
static const enum dsi_ctrl_version dsi_ctrl_v2_0 = DSI_CTRL_VERSION_2_0;
+static const enum dsi_ctrl_version dsi_ctrl_v2_2 = DSI_CTRL_VERSION_2_2;
static const struct of_device_id msm_dsi_of_match[] = {
{
@@ -72,6 +73,10 @@
.compatible = "qcom,dsi-ctrl-hw-v2.0",
.data = &dsi_ctrl_v2_0,
},
+ {
+ .compatible = "qcom,dsi-ctrl-hw-v2.2",
+ .data = &dsi_ctrl_v2_2,
+ },
{}
};
@@ -428,15 +433,34 @@
pr_debug("[%s] map dsi_ctrl registers to %p\n", ctrl->name,
ctrl->hw.base);
- ptr = msm_ioremap(pdev, "mmss_misc", ctrl->name);
- if (IS_ERR(ptr)) {
- rc = PTR_ERR(ptr);
- return rc;
+ switch (ctrl->version) {
+ case DSI_CTRL_VERSION_1_4:
+ case DSI_CTRL_VERSION_2_0:
+ ptr = msm_ioremap(pdev, "mmss_misc", ctrl->name);
+ if (IS_ERR(ptr)) {
+ pr_err("mmss_misc base address not found for [%s]\n",
+ ctrl->name);
+ rc = PTR_ERR(ptr);
+ return rc;
+ }
+ ctrl->hw.mmss_misc_base = ptr;
+ ctrl->hw.disp_cc_base = NULL;
+ break;
+ case DSI_CTRL_VERSION_2_2:
+ ptr = msm_ioremap(pdev, "disp_cc_base", ctrl->name);
+ if (IS_ERR(ptr)) {
+ pr_err("disp_cc base address not found for [%s]\n",
+ ctrl->name);
+ rc = PTR_ERR(ptr);
+ return rc;
+ }
+ ctrl->hw.disp_cc_base = ptr;
+ ctrl->hw.mmss_misc_base = NULL;
+ break;
+ default:
+ break;
}
- ctrl->hw.mmss_misc_base = ptr;
- pr_debug("[%s] map mmss_misc registers to %p\n", ctrl->name,
- ctrl->hw.mmss_misc_base);
return rc;
}
@@ -532,7 +556,7 @@
goto fail;
}
- link->esc_clk = devm_clk_get(&pdev->dev, "core_clk");
+ link->esc_clk = devm_clk_get(&pdev->dev, "esc_clk");
if (IS_ERR(link->esc_clk)) {
rc = PTR_ERR(link->esc_clk);
pr_err("failed to get esc_clk, rc=%d\n", rc);
@@ -613,10 +637,8 @@
rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&ctrl->pwr_info.digital,
"qcom,core-supply-entries");
- if (rc) {
- pr_err("failed to get digital supply, rc = %d\n", rc);
- goto error;
- }
+ if (rc)
+ pr_debug("failed to get digital supply, rc = %d\n", rc);
rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&ctrl->pwr_info.host_pwr,
@@ -663,10 +685,10 @@
ctrl->pwr_info.host_pwr.vregs = NULL;
ctrl->pwr_info.host_pwr.count = 0;
error_digital:
- devm_kfree(&pdev->dev, ctrl->pwr_info.digital.vregs);
+ if (ctrl->pwr_info.digital.vregs)
+ devm_kfree(&pdev->dev, ctrl->pwr_info.digital.vregs);
ctrl->pwr_info.digital.vregs = NULL;
ctrl->pwr_info.digital.count = 0;
-error:
return rc;
}
@@ -1204,6 +1226,7 @@
}
dsi_ctrl->cell_index = index;
+ dsi_ctrl->version = version;
dsi_ctrl->name = of_get_property(pdev->dev.of_node, "label", NULL);
if (!dsi_ctrl->name)
@@ -1227,7 +1250,6 @@
goto fail_clks;
}
- dsi_ctrl->version = version;
rc = dsi_catalog_ctrl_setup(&dsi_ctrl->hw, dsi_ctrl->version,
dsi_ctrl->cell_index);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index 161024a..859d707 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -41,6 +41,7 @@
DSI_CTRL_VERSION_UNKNOWN,
DSI_CTRL_VERSION_1_4,
DSI_CTRL_VERSION_2_0,
+ DSI_CTRL_VERSION_2_2,
DSI_CTRL_VERSION_MAX
};
@@ -575,18 +576,26 @@
/*
* struct dsi_ctrl_hw - DSI controller hardware object specific to an instance
- * @base: VA for the DSI controller base address.
- * @length: Length of the DSI controller register map.
- * @index: Instance ID of the controller.
- * @feature_map: Features supported by the DSI controller.
- * @ops: Function pointers to the operations supported by the
- * controller.
+ * @base: VA for the DSI controller base address.
+ * @length: Length of the DSI controller register map.
+ * @mmss_misc_base: Base address of mmss_misc register map.
+ * @mmss_misc_length: Length of mmss_misc register map.
+ * @disp_cc_base: Base address of disp_cc register map.
+ * @disp_cc_length: Length of disp_cc register map.
+ * @index: Instance ID of the controller.
+ * @feature_map: Features supported by the DSI controller.
+ * @ops: Function pointers to the operations supported by the
+ * controller.
+ * @supported_interrupts: Number of supported interrupts.
+ * @supported_errors: Number of supported errors.
*/
struct dsi_ctrl_hw {
void __iomem *base;
u32 length;
void __iomem *mmss_misc_base;
u32 mmss_misc_length;
+ void __iomem *disp_cc_base;
+ u32 disp_cc_length;
u32 index;
/* features */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c
new file mode 100644
index 0000000..1b1e811
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+#define pr_fmt(fmt) "dsi-hw:" fmt
+
+#include "dsi_ctrl_hw.h"
+#include "dsi_ctrl_reg.h"
+#include "dsi_hw.h"
+
+/* Equivalent to register DISP_CC_MISC_CMD */
+#define DISP_CC_CLAMP_REG_OFF 0x00
+
+/**
+ * dsi_ctrl_hw_22_phy_reset_config() - to configure clamp control during ulps
+ * @ctrl: Pointer to the controller host hardware.
+ * @enable: boolean to specify enable/disable.
+ */
+void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl,
+ bool enable)
+{
+ u32 reg = 0;
+
+ reg = DSI_DISP_CC_R32(ctrl, DISP_CC_CLAMP_REG_OFF);
+
+ /* Mask/unmask disable PHY reset bit */
+ if (enable)
+ reg &= ~BIT(ctrl->index);
+ else
+ reg |= BIT(ctrl->index);
+ DSI_DISP_CC_W32(ctrl, DISP_CC_CLAMP_REG_OFF, reg);
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
index 8605338..122a63d 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -320,8 +320,8 @@
reg |= (common_cfg->bit_swap_green ? BIT(4) : 0);
reg |= (common_cfg->bit_swap_blue ? BIT(8) : 0);
DSI_W32(ctrl, DSI_VIDEO_MODE_DATA_CTRL, reg);
- /* Enable Timing double buffering */
- DSI_W32(ctrl, DSI_DSI_TIMING_DB_MODE, 0x1);
+ /* Disable Timing double buffering */
+ DSI_W32(ctrl, DSI_DSI_TIMING_DB_MODE, 0x0);
pr_debug("[DSI_%d] Video engine setup done\n", ctrl->index);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h
index 447f613..8250da3 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_hw.h
@@ -33,6 +33,15 @@
writel_relaxed((val), (dsi_hw)->mmss_misc_base + (off)); \
} while (0)
+#define DSI_DISP_CC_R32(dsi_hw, off) \
+ readl_relaxed((dsi_hw)->disp_cc_base + (off))
+#define DSI_DISP_CC_W32(dsi_hw, off, val) \
+ do {\
+ pr_err("[DSI_%d][%s] - [0x%08x]\n", \
+ (dsi_hw)->index, #off, val); \
+ writel_relaxed((val), (dsi_hw)->disp_cc_base + (off)); \
+ } while (0)
+
#define DSI_R64(dsi_hw, off) readq_relaxed((dsi_hw)->base + (off))
#define DSI_W64(dsi_hw, off, val) writeq_relaxed((val), (dsi_hw)->base + (off))
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 96a98bd..cdc1ff6 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -388,7 +388,7 @@
/** TODO: initialize debugfs */
dsi_phy->pdev = pdev;
platform_set_drvdata(pdev, dsi_phy);
- pr_debug("Probe successful for %s\n", dsi_phy->name);
+ pr_info("Probe successful for %s\n", dsi_phy->name);
return 0;
fail_supplies:
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
index 96f5c19..6a3dad8 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
@@ -159,7 +159,7 @@
struct dsi_phy_cfg *cfg)
{
int i;
- u8 tx_dctrl[] = {0x00, 0x00, 0x00, 0x02, 0x01};
+ u8 tx_dctrl[] = {0x00, 0x00, 0x00, 0x04, 0x01};
/* Strength ctrl settings */
for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
@@ -186,6 +186,10 @@
DSI_W32(phy, DSIPHY_LNX_OFFSET_BOT_CTRL(i), 0x0);
DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]);
}
+
+ /* Toggle BIT 0 to release freeze I/0 */
+ DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(3), 0x05);
+ DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(3), 0x04);
}
/**
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 4b263d3..4471d0b 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -138,6 +138,7 @@
CRTC_PROP_MEM_IB,
CRTC_PROP_ROT_PREFILL_BW,
CRTC_PROP_ROT_CLK,
+ CRTC_PROP_ROI_V1,
/* total # of properties */
CRTC_PROP_COUNT
@@ -158,6 +159,7 @@
CONNECTOR_PROP_DST_Y,
CONNECTOR_PROP_DST_W,
CONNECTOR_PROP_DST_H,
+ CONNECTOR_PROP_ROI_V1,
/* enum/bitmask properties */
CONNECTOR_PROP_TOPOLOGY_NAME,
@@ -200,6 +202,38 @@
};
/**
+ * struct msm_roi_alignment - region of interest alignment restrictions
+ * @xstart_pix_align: left x offset alignment restriction
+ * @width_pix_align: width alignment restriction
+ * @ystart_pix_align: top y offset alignment restriction
+ * @height_pix_align: height alignment restriction
+ * @min_width: minimum width restriction
+ * @min_height: minimum height restriction
+ */
+struct msm_roi_alignment {
+ uint32_t xstart_pix_align;
+ uint32_t width_pix_align;
+ uint32_t ystart_pix_align;
+ uint32_t height_pix_align;
+ uint32_t min_width;
+ uint32_t min_height;
+};
+
+/**
+ * struct msm_roi_caps - display's region of interest capabilities
+ * @enabled: true if some region of interest is supported
+ * @merge_rois: merge rois before sending to display
+ * @num_roi: maximum number of rois supported
+ * @align: roi alignment restrictions
+ */
+struct msm_roi_caps {
+ bool enabled;
+ bool merge_rois;
+ uint32_t num_roi;
+ struct msm_roi_alignment align;
+};
+
+/**
* struct msm_display_dsc_info - defines dsc configuration
* @version: DSC version.
* @scr_rev: DSC revision.
@@ -338,6 +372,7 @@
* @vtotal: display vertical total
* @jitter: display jitter configuration
* @comp_info: Compression supported by the display
+ * @roi_caps: Region of interest capability info
*/
struct msm_display_info {
int intf_type;
@@ -361,21 +396,19 @@
uint32_t jitter;
struct msm_compression_info comp_info;
+ struct msm_roi_caps roi_caps;
};
#define MSM_MAX_ROI 4
/**
- * struct msm_roi_mapping - Regions of interest structure for mapping CRTC to
- * Connector output
- * @num_rects: number of valid rectangles in src and dst arrays
- * @src: source roi rectangle
- * @dst: destination roi rectangle
+ * struct msm_roi_list - list of regions of interest for a drm object
+ * @num_rects: number of valid rectangles in the roi array
+ * @roi: list of roi rectangles
*/
-struct msm_roi_mapping {
+struct msm_roi_list {
uint32_t num_rects;
- struct drm_clip_rect src[MSM_MAX_ROI];
- struct drm_clip_rect dst[MSM_MAX_ROI];
+ struct drm_clip_rect roi[MSM_MAX_ROI];
};
/**
@@ -383,7 +416,7 @@
* @rois: Regions of interest structure for mapping CRTC to Connector output
*/
struct msm_display_kickoff_params {
- struct msm_roi_mapping *rois;
+ struct msm_roi_list *rois;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 6c2d643..e3f8261 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -21,6 +21,12 @@
#define BL_NODE_NAME_SIZE 32
+#define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\
+ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__)
+
+#define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\
+ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__)
+
static const struct drm_prop_enum_list e_topology_name[] = {
{SDE_RM_TOPOLOGY_UNKNOWN, "sde_unknown"},
{SDE_RM_TOPOLOGY_SINGLEPIPE, "sde_singlepipe"},
@@ -248,6 +254,25 @@
return rc;
}
+void sde_connector_clk_ctrl(struct drm_connector *connector, bool enable)
+{
+ struct sde_connector *c_conn;
+ struct dsi_display *display;
+ u32 state = enable ? DSI_CLK_ON : DSI_CLK_OFF;
+
+ if (!connector) {
+ SDE_ERROR("invalid connector\n");
+ return;
+ }
+
+ c_conn = to_sde_connector(connector);
+ display = (struct dsi_display *) c_conn->display;
+
+ if (display && c_conn->ops.clk_ctrl)
+ c_conn->ops.clk_ctrl(display->mdp_clk_handle,
+ DSI_ALL_CLKS, state);
+}
+
static void sde_connector_destroy(struct drm_connector *connector)
{
struct sde_connector *c_conn;
@@ -397,6 +422,122 @@
return &c_state->base;
}
+static int _sde_connector_roi_v1_check_roi(
+ struct sde_connector *c_conn,
+ struct drm_clip_rect *roi_conn,
+ const struct msm_roi_caps *caps)
+{
+ const struct msm_roi_alignment *align = &caps->align;
+ int w = roi_conn->x2 - roi_conn->x1;
+ int h = roi_conn->y2 - roi_conn->y1;
+
+ if (w <= 0 || h <= 0) {
+ SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n", w, h);
+ return -EINVAL;
+ }
+
+ if (w < align->min_width || w % align->width_pix_align) {
+ SDE_ERROR_CONN(c_conn,
+ "invalid conn roi width %d min %d align %d\n",
+ w, align->min_width, align->width_pix_align);
+ return -EINVAL;
+ }
+
+ if (h < align->min_height || h % align->height_pix_align) {
+ SDE_ERROR_CONN(c_conn,
+ "invalid conn roi height %d min %d align %d\n",
+ h, align->min_height, align->height_pix_align);
+ return -EINVAL;
+ }
+
+ if (roi_conn->x1 % align->xstart_pix_align) {
+ SDE_ERROR_CONN(c_conn, "invalid conn roi x1 %d align %d\n",
+ roi_conn->x1, align->xstart_pix_align);
+ return -EINVAL;
+ }
+
+ if (roi_conn->y1 % align->ystart_pix_align) {
+ SDE_ERROR_CONN(c_conn, "invalid conn roi y1 %d align %d\n",
+ roi_conn->y1, align->ystart_pix_align);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int _sde_connector_set_roi_v1(
+ struct sde_connector *c_conn,
+ struct sde_connector_state *c_state,
+ void *usr_ptr)
+{
+ struct sde_drm_roi_v1 roi_v1;
+ struct msm_display_info display_info;
+ struct msm_roi_caps *caps;
+ int i, rc;
+
+ if (!c_conn || !c_state) {
+ SDE_ERROR("invalid args\n");
+ return -EINVAL;
+ }
+
+ rc = sde_connector_get_info(&c_conn->base, &display_info);
+ if (rc) {
+ SDE_ERROR_CONN(c_conn, "display get info error: %d\n", rc);
+ return rc;
+ }
+
+ caps = &display_info.roi_caps;
+ if (!caps->enabled) {
+ SDE_ERROR_CONN(c_conn, "display roi capability is disabled\n");
+ return -ENOTSUPP;
+ }
+
+ memset(&c_state->rois, 0, sizeof(c_state->rois));
+
+ if (!usr_ptr) {
+ SDE_DEBUG_CONN(c_conn, "rois cleared\n");
+ return 0;
+ }
+
+ if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) {
+ SDE_ERROR_CONN(c_conn, "failed to copy roi_v1 data\n");
+ return -EINVAL;
+ }
+
+ SDE_DEBUG_CONN(c_conn, "num_rects %d\n", roi_v1.num_rects);
+
+ if (roi_v1.num_rects == 0) {
+ SDE_DEBUG_CONN(c_conn, "rois cleared\n");
+ return 0;
+ }
+
+ if (roi_v1.num_rects > SDE_MAX_ROI_V1 ||
+ roi_v1.num_rects > caps->num_roi) {
+ SDE_ERROR_CONN(c_conn, "too many rects specified: %d\n",
+ roi_v1.num_rects);
+ return -EINVAL;
+ }
+
+ c_state->rois.num_rects = roi_v1.num_rects;
+ for (i = 0; i < roi_v1.num_rects; ++i) {
+ int rc;
+
+ rc = _sde_connector_roi_v1_check_roi(c_conn, &roi_v1.roi[i],
+ caps);
+ if (rc)
+ return rc;
+
+ c_state->rois.roi[i] = roi_v1.roi[i];
+ SDE_DEBUG_CONN(c_conn, "roi%d: roi 0x%x 0x%x 0x%x 0x%x\n", i,
+ c_state->rois.roi[i].x1,
+ c_state->rois.roi[i].y1,
+ c_state->rois.roi[i].x2,
+ c_state->rois.roi[i].y2);
+ }
+
+ return 0;
+}
+
static int sde_connector_atomic_set_property(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property,
@@ -462,6 +603,12 @@
SDE_ERROR("invalid topology_control: 0x%llX\n", val);
}
+ if (idx == CONNECTOR_PROP_ROI_V1) {
+ rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val);
+ if (rc)
+ SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc);
+ }
+
/* check for custom property handling */
if (!rc && c_conn->ops.set_property) {
rc = c_conn->ops.set_property(connector,
@@ -511,13 +658,7 @@
idx = msm_property_index(&c_conn->property_info, property);
if (idx == CONNECTOR_PROP_RETIRE_FENCE)
- /*
- * Set a fence offset if not a virtual connector, so that the
- * fence signals after one additional commit rather than at the
- * end of the current one.
- */
- rc = sde_fence_create(&c_conn->retire_fence, val,
- c_conn->connector_type != DRM_MODE_CONNECTOR_VIRTUAL);
+ rc = sde_fence_create(&c_conn->retire_fence, val, 0);
else
/* get cached property value */
rc = msm_property_atomic_get(&c_conn->property_info,
@@ -706,6 +847,7 @@
struct sde_kms_info *info;
struct sde_connector *c_conn = NULL;
struct dsi_display *dsi_display;
+ struct msm_display_info display_info;
int rc;
if (!dev || !dev->dev_private || !encoder) {
@@ -838,6 +980,13 @@
}
}
+ rc = sde_connector_get_info(&c_conn->base, &display_info);
+ if (!rc && display_info.roi_caps.enabled) {
+ msm_property_install_volatile_range(
+ &c_conn->property_info, "sde_drm_roi_v1", 0x0,
+ 0, ~0, 0, CONNECTOR_PROP_ROI_V1);
+ }
+
msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 70d4952..601299e 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -150,6 +150,14 @@
int (*pre_kickoff)(struct drm_connector *connector,
void *display,
struct msm_display_kickoff_params *params);
+
+ /**
+ * clk_ctrl - perform clk enable/disable on the connector
+ * @handle: Pointer to clk handle
+ * @type: Type of clks
+ * @enable: State of clks
+ */
+ int (*clk_ctrl)(void *handle, u32 type, u32 state);
};
/**
@@ -273,7 +281,7 @@
int mmu_id;
uint64_t property_values[CONNECTOR_PROP_COUNT];
- struct msm_roi_mapping rois;
+ struct msm_roi_list rois;
};
/**
@@ -366,6 +374,13 @@
struct msm_display_info *info);
/**
+ * sde_connector_clk_ctrl - enables/disables the connector clks
+ * @connector: Pointer to drm connector object
+ * @enable: true/false to enable/disable
+ */
+void sde_connector_clk_ctrl(struct drm_connector *connector, bool enable);
+
+/**
* sde_connector_trigger_event - indicate that an event has occurred
* Any callbacks that have been registered against this event will
* be called from the same thread context.
@@ -425,5 +440,22 @@
*/
int sde_connector_pre_kickoff(struct drm_connector *connector);
+/**
+ * sde_connector_needs_offset - adjust the output fence offset based on
+ * display type
+ * @connector: Pointer to drm connector object
+ * Returns: true if offset is required, false for all other cases.
+ */
+static inline bool sde_connector_needs_offset(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn;
+
+ if (!connector)
+ return false;
+
+ c_conn = to_sde_connector(connector);
+ return (c_conn->connector_type != DRM_MODE_CONNECTOR_VIRTUAL);
+}
+
#endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index a44dd68..6bae083 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -656,21 +656,344 @@
}
}
+void sde_crtc_get_crtc_roi(struct drm_crtc_state *state,
+ const struct sde_rect **crtc_roi)
+{
+ struct sde_crtc_state *crtc_state;
+
+ if (!state || !crtc_roi)
+ return;
+
+ crtc_state = to_sde_crtc_state(state);
+ *crtc_roi = &crtc_state->crtc_roi;
+}
+
+static int _sde_crtc_set_roi_v1(struct drm_crtc_state *state,
+ void *usr_ptr)
+{
+ struct drm_crtc *crtc;
+ struct sde_crtc_state *cstate;
+ struct sde_drm_roi_v1 roi_v1;
+ int i;
+
+ if (!state) {
+ SDE_ERROR("invalid args\n");
+ return -EINVAL;
+ }
+
+ cstate = to_sde_crtc_state(state);
+ crtc = cstate->base.crtc;
+
+ memset(&cstate->user_roi_list, 0, sizeof(cstate->user_roi_list));
+
+ if (!usr_ptr) {
+ SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc));
+ return 0;
+ }
+
+ if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) {
+ SDE_ERROR("crtc%d: failed to copy roi_v1 data\n", DRMID(crtc));
+ return -EINVAL;
+ }
+
+ SDE_DEBUG("crtc%d: num_rects %d\n", DRMID(crtc), roi_v1.num_rects);
+
+ if (roi_v1.num_rects == 0) {
+ SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc));
+ return 0;
+ }
+
+ if (roi_v1.num_rects > SDE_MAX_ROI_V1) {
+ SDE_ERROR("crtc%d: too many rects specified: %d\n", DRMID(crtc),
+ roi_v1.num_rects);
+ return -EINVAL;
+ }
+
+ cstate->user_roi_list.num_rects = roi_v1.num_rects;
+ for (i = 0; i < roi_v1.num_rects; ++i) {
+ cstate->user_roi_list.roi[i] = roi_v1.roi[i];
+ SDE_DEBUG("crtc%d: roi%d: roi (%d,%d) (%d,%d)\n",
+ DRMID(crtc), i,
+ cstate->user_roi_list.roi[i].x1,
+ cstate->user_roi_list.roi[i].y1,
+ cstate->user_roi_list.roi[i].x2,
+ cstate->user_roi_list.roi[i].y2);
+ }
+
+ return 0;
+}
+
+static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct drm_connector *conn;
+ struct drm_connector_state *conn_state;
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *crtc_state;
+ struct sde_rect *crtc_roi;
+ struct drm_clip_rect crtc_clip, *user_rect;
+ int i, num_attached_conns = 0;
+
+ if (!crtc || !state)
+ return -EINVAL;
+
+ sde_crtc = to_sde_crtc(crtc);
+ crtc_state = to_sde_crtc_state(state);
+ crtc_roi = &crtc_state->crtc_roi;
+
+ /* init to invalid range maxes */
+ crtc_clip.x1 = ~0;
+ crtc_clip.y1 = ~0;
+ crtc_clip.x2 = 0;
+ crtc_clip.y2 = 0;
+
+ for_each_connector_in_state(state->state, conn, conn_state, i) {
+ struct sde_connector_state *sde_conn_state;
+
+ if (!conn_state || conn_state->crtc != crtc)
+ continue;
+
+ if (num_attached_conns) {
+ SDE_ERROR(
+ "crtc%d: unsupported: roi on crtc w/ >1 connectors\n",
+ DRMID(crtc));
+ return -EINVAL;
+ }
+ ++num_attached_conns;
+
+ sde_conn_state = to_sde_connector_state(conn_state);
+
+ if (memcmp(&sde_conn_state->rois, &crtc_state->user_roi_list,
+ sizeof(crtc_state->user_roi_list))) {
+ SDE_ERROR("%s: crtc -> conn roi scaling unsupported\n",
+ sde_crtc->name);
+ return -EINVAL;
+ }
+ }
+
+ /* aggregate all clipping rectangles together for overall crtc roi */
+ for (i = 0; i < crtc_state->user_roi_list.num_rects; i++) {
+ user_rect = &crtc_state->user_roi_list.roi[i];
+
+ crtc_clip.x1 = min(crtc_clip.x1, user_rect->x1);
+ crtc_clip.y1 = min(crtc_clip.y1, user_rect->y1);
+ crtc_clip.x2 = max(crtc_clip.x2, user_rect->x2);
+ crtc_clip.y2 = max(crtc_clip.y2, user_rect->y2);
+
+ SDE_DEBUG(
+ "%s: conn%d roi%d (%d,%d),(%d,%d) -> crtc (%d,%d),(%d,%d)\n",
+ sde_crtc->name, DRMID(crtc), i,
+ user_rect->x1, user_rect->y1,
+ user_rect->x2, user_rect->y2,
+ crtc_clip.x1, crtc_clip.y1,
+ crtc_clip.x2, crtc_clip.y2);
+
+ }
+
+ if (crtc_clip.x2 && crtc_clip.y2) {
+ crtc_roi->x = crtc_clip.x1;
+ crtc_roi->y = crtc_clip.y1;
+ crtc_roi->w = crtc_clip.x2 - crtc_clip.x1;
+ crtc_roi->h = crtc_clip.y2 - crtc_clip.y1;
+ } else {
+ crtc_roi->x = 0;
+ crtc_roi->y = 0;
+ crtc_roi->w = 0;
+ crtc_roi->h = 0;
+ }
+
+ SDE_DEBUG("%s: crtc roi (%d,%d,%d,%d)\n", sde_crtc->name,
+ crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h);
+
+ return 0;
+}
+
+static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc,
+ struct drm_crtc_state *state, int lm_idx)
+{
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *crtc_state;
+ const struct sde_rect *crtc_roi;
+ const struct sde_rect *lm_bounds;
+ struct sde_rect *lm_roi;
+
+ if (!crtc || !state || lm_idx >= ARRAY_SIZE(crtc_state->lm_bounds))
+ return -EINVAL;
+
+ sde_crtc = to_sde_crtc(crtc);
+ crtc_state = to_sde_crtc_state(state);
+ crtc_roi = &crtc_state->crtc_roi;
+ lm_bounds = &crtc_state->lm_bounds[lm_idx];
+ lm_roi = &crtc_state->lm_roi[lm_idx];
+
+ if (!sde_kms_rect_is_null(crtc_roi)) {
+ sde_kms_rect_intersect(crtc_roi, lm_bounds, lm_roi);
+ if (sde_kms_rect_is_null(lm_roi)) {
+ SDE_ERROR("unsupported R/L only partial update\n");
+ return -EINVAL;
+ }
+ } else {
+ memcpy(lm_roi, lm_bounds, sizeof(*lm_roi));
+ }
+
+ SDE_DEBUG("%s: lm%d roi (%d,%d,%d,%d)\n", sde_crtc->name, lm_idx,
+ lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h);
+
+ return 0;
+}
+
+static int _sde_crtc_check_rois_centered_and_symmetric(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *crtc_state;
+ const struct sde_rect *roi_prv, *roi_cur;
+ int lm_idx;
+
+ if (!crtc || !state)
+ return -EINVAL;
+
+ /*
+ * On certain HW, ROIs must be centered on the split between LMs,
+ * and be of equal width.
+ */
+
+ sde_crtc = to_sde_crtc(crtc);
+ crtc_state = to_sde_crtc_state(state);
+
+ roi_prv = &crtc_state->lm_roi[0];
+ for (lm_idx = 1; lm_idx < sde_crtc->num_mixers; lm_idx++) {
+ roi_cur = &crtc_state->lm_roi[lm_idx];
+
+ /* check lm rois are equal width & first roi ends at 2nd roi */
+ if (((roi_prv->x + roi_prv->w) != roi_cur->x) ||
+ (roi_prv->w != roi_cur->w)) {
+ SDE_ERROR("%s: roi lm%d x %d w %d lm%d x %d w %d\n",
+ sde_crtc->name,
+ lm_idx-1, roi_prv->x, roi_prv->w,
+ lm_idx, roi_cur->x, roi_cur->w);
+ return -EINVAL;
+ }
+ roi_prv = roi_cur;
+ }
+
+ return 0;
+}
+
+static int _sde_crtc_check_planes_within_crtc_roi(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *crtc_state;
+ const struct sde_rect *crtc_roi;
+ struct drm_plane_state *pstate;
+ struct drm_plane *plane;
+
+ if (!crtc || !state)
+ return -EINVAL;
+
+ /*
+ * Reject commit if a Plane CRTC destination coordinates fall outside
+ * the partial CRTC ROI. LM output is determined via connector ROIs,
+ * if they are specified, not Plane CRTC ROIs.
+ */
+
+ sde_crtc = to_sde_crtc(crtc);
+ crtc_state = to_sde_crtc_state(state);
+ crtc_roi = &crtc_state->crtc_roi;
+
+ if (sde_kms_rect_is_null(crtc_roi))
+ return 0;
+
+ drm_atomic_crtc_state_for_each_plane(plane, state) {
+ struct sde_rect plane_roi, intersection;
+
+ pstate = drm_atomic_get_plane_state(state->state, plane);
+ if (IS_ERR_OR_NULL(pstate)) {
+ int rc = PTR_ERR(pstate);
+
+ SDE_ERROR("%s: failed to get plane%d state, %d\n",
+ sde_crtc->name, plane->base.id, rc);
+ return rc;
+ }
+
+ plane_roi.x = pstate->crtc_x;
+ plane_roi.y = pstate->crtc_y;
+ plane_roi.w = pstate->crtc_w;
+ plane_roi.h = pstate->crtc_h;
+ sde_kms_rect_intersect(crtc_roi, &plane_roi, &intersection);
+ if (!sde_kms_rect_is_equal(&plane_roi, &intersection)) {
+ SDE_ERROR(
+ "%s: plane%d crtc roi (%d,%d,%d,%d) outside crtc roi (%d,%d,%d,%d)\n",
+ sde_crtc->name, plane->base.id,
+ plane_roi.x, plane_roi.y,
+ plane_roi.w, plane_roi.h,
+ crtc_roi->x, crtc_roi->y,
+ crtc_roi->w, crtc_roi->h);
+ return -E2BIG;
+ }
+ }
+
+ return 0;
+}
+
+static int _sde_crtc_check_rois(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct sde_crtc *sde_crtc;
+ int lm_idx;
+ int rc;
+
+ if (!crtc || !state)
+ return -EINVAL;
+
+ sde_crtc = to_sde_crtc(crtc);
+
+ rc = _sde_crtc_set_crtc_roi(crtc, state);
+ if (rc)
+ return rc;
+
+ for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
+ rc = _sde_crtc_set_lm_roi(crtc, state, lm_idx);
+ if (rc)
+ return rc;
+ }
+
+ rc = _sde_crtc_check_rois_centered_and_symmetric(crtc, state);
+ if (rc)
+ return rc;
+
+ rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
static void _sde_crtc_program_lm_output_roi(struct drm_crtc *crtc)
{
struct sde_crtc *sde_crtc;
struct sde_crtc_state *crtc_state;
+ const struct sde_rect *lm_roi;
+ struct sde_hw_mixer *hw_lm;
int lm_idx, lm_horiz_position;
+ if (!crtc)
+ return;
+
sde_crtc = to_sde_crtc(crtc);
crtc_state = to_sde_crtc_state(crtc->state);
lm_horiz_position = 0;
for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
- const struct sde_rect *lm_roi = &crtc_state->lm_bounds[lm_idx];
- struct sde_hw_mixer *hw_lm = sde_crtc->mixers[lm_idx].hw_lm;
struct sde_hw_mixer_cfg cfg;
+ lm_roi = &crtc_state->lm_roi[lm_idx];
+ hw_lm = sde_crtc->mixers[lm_idx].hw_lm;
+
+ SDE_EVT32(DRMID(crtc_state->base.crtc), lm_idx,
+ lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h);
+
if (sde_kms_rect_is_null(lm_roi))
continue;
@@ -742,9 +1065,12 @@
format = to_sde_format(msm_framebuffer_format(pstate->base.fb));
- SDE_EVT32(DRMID(plane), state->src_x, state->src_y,
- state->src_w >> 16, state->src_h >> 16, state->crtc_x,
- state->crtc_y, state->crtc_w, state->crtc_h);
+ SDE_EVT32(DRMID(crtc), DRMID(plane),
+ state->fb ? state->fb->base.id : -1,
+ state->src_x >> 16, state->src_y >> 16,
+ state->src_w >> 16, state->src_h >> 16,
+ state->crtc_x, state->crtc_y,
+ state->crtc_w, state->crtc_h);
for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
struct sde_rect intersect;
@@ -877,6 +1203,8 @@
ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx,
&sde_crtc->stage_cfg, i);
}
+
+ _sde_crtc_program_lm_output_roi(crtc);
}
void sde_crtc_prepare_commit(struct drm_crtc *crtc,
@@ -1329,14 +1657,18 @@
crtc_split_width = sde_crtc_mixer_width(sde_crtc, adj_mode);
for (i = 0; i < sde_crtc->num_mixers; i++) {
- struct sde_rect *lm_bound = &cstate->lm_bounds[i];
-
- lm_bound->x = crtc_split_width * i;
- lm_bound->y = 0;
- lm_bound->w = crtc_split_width;
- lm_bound->h = adj_mode->vdisplay;
- SDE_EVT32(DRMID(crtc), i, lm_bound->x, lm_bound->y,
- lm_bound->w, lm_bound->h);
+ cstate->lm_bounds[i].x = crtc_split_width * i;
+ cstate->lm_bounds[i].y = 0;
+ cstate->lm_bounds[i].w = crtc_split_width;
+ cstate->lm_bounds[i].h = adj_mode->vdisplay;
+ memcpy(&cstate->lm_roi[i], &cstate->lm_bounds[i],
+ sizeof(cstate->lm_roi[i]));
+ SDE_EVT32(DRMID(crtc), i,
+ cstate->lm_bounds[i].x, cstate->lm_bounds[i].y,
+ cstate->lm_bounds[i].w, cstate->lm_bounds[i].h);
+ SDE_DEBUG("%s: lm%d bnd&roi (%d,%d,%d,%d)\n", sde_crtc->name, i,
+ cstate->lm_roi[i].x, cstate->lm_roi[i].y,
+ cstate->lm_roi[i].w, cstate->lm_roi[i].h);
}
drm_mode_debug_printmodeline(adj_mode);
@@ -1366,10 +1698,10 @@
sde_crtc = to_sde_crtc(crtc);
dev = crtc->dev;
- if (!sde_crtc->num_mixers)
+ if (!sde_crtc->num_mixers) {
_sde_crtc_setup_mixers(crtc);
-
- _sde_crtc_setup_lm_bounds(crtc, crtc->state);
+ _sde_crtc_setup_lm_bounds(crtc, crtc->state);
+ }
if (sde_crtc->event) {
WARN_ON(sde_crtc->event);
@@ -2117,6 +2449,11 @@
}
}
+ rc = _sde_crtc_check_rois(crtc, state);
+ if (rc) {
+ SDE_ERROR("crtc%d failed roi check %d\n", crtc->base.id, rc);
+ goto end;
+ }
end:
_sde_crtc_rp_free_unused(&cstate->rp);
@@ -2243,6 +2580,9 @@
"dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1);
}
+ msm_property_install_volatile_range(&sde_crtc->property_info,
+ "sde_drm_roi_v1", 0x0, 0, ~0, 0, CRTC_PROP_ROI_V1);
+
sde_kms_info_reset(info);
sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion);
@@ -2315,6 +2655,9 @@
case CRTC_PROP_DIM_LAYER_V1:
_sde_crtc_set_dim_layer_v1(cstate, (void *)val);
break;
+ case CRTC_PROP_ROI_V1:
+ ret = _sde_crtc_set_roi_v1(state, (void *)val);
+ break;
default:
/* nothing to do */
break;
@@ -2364,19 +2707,28 @@
struct sde_crtc *sde_crtc;
struct sde_crtc_state *cstate;
int i, ret = -EINVAL;
+ bool conn_offset = 0;
if (!crtc || !state) {
SDE_ERROR("invalid argument(s)\n");
} else {
sde_crtc = to_sde_crtc(crtc);
cstate = to_sde_crtc_state(state);
+
+ for (i = 0; i < cstate->num_connectors; ++i) {
+ conn_offset = sde_connector_needs_offset(
+ cstate->connectors[i]);
+ if (conn_offset)
+ break;
+ }
+
i = msm_property_index(&sde_crtc->property_info, property);
if (i == CRTC_PROP_OUTPUT_FENCE) {
uint32_t offset = sde_crtc_get_property(cstate,
CRTC_PROP_OUTPUT_FENCE_OFFSET);
- ret = sde_fence_create(
- &sde_crtc->output_fence, val, offset);
+ ret = sde_fence_create(&sde_crtc->output_fence, val,
+ offset + conn_offset);
if (ret)
SDE_ERROR("fence create failed\n");
} else {
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 36231d4..7ad0955 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -250,6 +250,11 @@
* @rsc_client : sde rsc client when mode is valid
* @lm_bounds : LM boundaries based on current mode full resolution, no ROI.
* Origin top left of CRTC.
+ * @crtc_roi : Current CRTC ROI. Possibly sub-rectangle of mode.
+ * Origin top left of CRTC.
+ * @lm_roi : Current LM ROI, possibly sub-rectangle of mode.
+ * Origin top left of CRTC.
+ * @user_roi_list : List of user's requested ROIs as from set property
* @property_values: Current crtc property values
* @input_fence_timeout_ns : Cached input fence timeout, in ns
* @property_blobs: Reference pointers for blob properties
@@ -270,6 +275,9 @@
bool rsc_update;
struct sde_rect lm_bounds[CRTC_DUAL_MIXERS];
+ struct sde_rect crtc_roi;
+ struct sde_rect lm_roi[CRTC_DUAL_MIXERS];
+ struct msm_roi_list user_roi_list;
uint64_t property_values[CRTC_PROP_COUNT];
uint64_t input_fence_timeout_ns;
@@ -433,4 +441,14 @@
*/
void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag);
+/**
+ * sde_crtc_get_crtc_roi - retrieve the crtc_roi from the given state object
+ * used to allow the planes to adjust their final lm out_xy value in the
+ * case of partial update
+ * @crtc_state: Pointer to crtc state
+ * @crtc_roi: Output pointer to crtc roi in the given state
+ */
+void sde_crtc_get_crtc_roi(struct drm_crtc_state *state,
+ const struct sde_rect **crtc_roi);
+
#endif /* _SDE_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 11cca1f..cfa3b5e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -490,8 +490,8 @@
return 0;
for (i = 0, cur_pos = dst_list_pos;
- (cur_pos < (dst_list_size - 1)) && src_list[i].fourcc_format
- && (i < src_list_size); ++i, ++cur_pos)
+ (cur_pos < (dst_list_size - 1)) && (i < src_list_size)
+ && src_list[i].fourcc_format; ++i, ++cur_pos)
dst_list[cur_pos] = src_list[i];
dst_list[cur_pos].fourcc_format = 0;
@@ -565,7 +565,7 @@
rc = -EINVAL;
}
*off_count = 0;
- memset(prop_count, 0, sizeof(int *) * prop_size);
+ memset(prop_count, 0, sizeof(int) * prop_size);
return rc;
}
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 2393e61..4a5479d 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -566,7 +566,8 @@
.get_info = dsi_display_get_info,
.set_backlight = dsi_display_set_backlight,
.soft_reset = dsi_display_soft_reset,
- .pre_kickoff = dsi_conn_pre_kickoff
+ .pre_kickoff = dsi_conn_pre_kickoff,
+ .clk_ctrl = dsi_display_clk_ctrl
};
static const struct sde_connector_ops wb_ops = {
.post_init = sde_wb_connector_post_init,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index bd6b302..8662207 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2204,6 +2204,7 @@
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
struct sde_rect src, dst;
+ const struct sde_rect *crtc_roi;
bool q16_data = true;
int idx;
@@ -2283,6 +2284,11 @@
}
}
+ /* re-program the output rects always in the case of partial update */
+ sde_crtc_get_crtc_roi(crtc->state, &crtc_roi);
+ if (!sde_kms_rect_is_null(crtc_roi))
+ pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
+
if (pstate->dirty & SDE_PLANE_DIRTY_RECTS)
memset(&(psde->pipe_cfg), 0, sizeof(struct sde_hw_pipe_cfg));
@@ -2320,6 +2326,13 @@
src.y &= ~0x1;
}
+ /*
+ * adjust layer mixer position of the sspp in the presence
+ * of a partial update to the active lm origin
+ */
+ dst.x -= crtc_roi->x;
+ dst.y -= crtc_roi->y;
+
psde->pipe_cfg.src_rect = src;
psde->pipe_cfg.dst_rect = dst;
diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c
index 62efe8e..9a68dbe 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.c
+++ b/drivers/gpu/drm/msm/sde_power_handle.c
@@ -25,10 +25,44 @@
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include <linux/sde_io_util.h>
+#include <linux/sde_rsc.h>
#include "sde_power_handle.h"
#include "sde_trace.h"
+static void sde_power_event_trigger_locked(struct sde_power_handle *phandle,
+ u32 event_type)
+{
+ struct sde_power_event *event;
+
+ list_for_each_entry(event, &phandle->event_list, list) {
+ if (event->event_type & event_type)
+ event->cb_fnc(event_type, event->usr);
+ }
+}
+
+static int sde_power_rsc_update(struct sde_power_handle *phandle, bool enable)
+{
+ u32 rsc_state;
+
+ /* creates the rsc client on the first enable */
+ if (!phandle->rsc_client_init) {
+ phandle->rsc_client = sde_rsc_client_create(SDE_RSC_INDEX,
+ "sde_power_handle", false);
+ if (IS_ERR_OR_NULL(phandle->rsc_client)) {
+ pr_debug("sde rsc client create failed :%ld\n",
+ PTR_ERR(phandle->rsc_client));
+ phandle->rsc_client = NULL;
+ }
+ phandle->rsc_client_init = true;
+ }
+
+ rsc_state = enable ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE;
+
+ return sde_rsc_client_state_update(phandle->rsc_client,
+ rsc_state, NULL, -1);
+}
+
struct sde_power_client *sde_power_client_create(
struct sde_power_handle *phandle, char *client_name)
{
@@ -48,6 +82,7 @@
strlcpy(client->name, client_name, MAX_CLIENT_NAME_LEN);
client->usecase_ndx = VOTE_INDEX_DISABLE;
client->id = id;
+ client->active = true;
pr_debug("client %s created:%pK id :%d\n", client_name,
client, id);
id++;
@@ -62,6 +97,9 @@
{
if (!client || !phandle) {
pr_err("reg bus vote: invalid client handle\n");
+ } else if (!client->active) {
+ pr_err("sde power deinit already done\n");
+ kfree(client);
} else {
pr_debug("bus vote client %s destroyed:%pK id:%u\n",
client->name, client, client->id);
@@ -661,6 +699,11 @@
}
INIT_LIST_HEAD(&phandle->power_client_clist);
+ INIT_LIST_HEAD(&phandle->event_list);
+
+ phandle->rsc_client = NULL;
+ phandle->rsc_client_init = false;
+
mutex_init(&phandle->phandle_lock);
return rc;
@@ -672,10 +715,12 @@
clk_err:
msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
vreg_err:
- devm_kfree(&pdev->dev, mp->vreg_config);
+ if (mp->vreg_config)
+ devm_kfree(&pdev->dev, mp->vreg_config);
mp->num_vreg = 0;
parse_vreg_err:
- devm_kfree(&pdev->dev, mp->clk_config);
+ if (mp->clk_config)
+ devm_kfree(&pdev->dev, mp->clk_config);
mp->num_clk = 0;
end:
return rc;
@@ -685,6 +730,8 @@
struct sde_power_handle *phandle)
{
struct dss_module_power *mp;
+ struct sde_power_client *curr_client, *next_client;
+ struct sde_power_event *curr_event, *next_event;
if (!phandle || !pdev) {
pr_err("invalid input param\n");
@@ -692,6 +739,26 @@
}
mp = &phandle->mp;
+ mutex_lock(&phandle->phandle_lock);
+ list_for_each_entry_safe(curr_client, next_client,
+ &phandle->power_client_clist, list) {
+ pr_err("cliend:%s-%d still registered with refcount:%d\n",
+ curr_client->name, curr_client->id,
+ curr_client->refcount);
+ curr_client->active = false;
+ list_del(&curr_client->list);
+ }
+
+ list_for_each_entry_safe(curr_event, next_event,
+ &phandle->event_list, list) {
+ pr_err("event:%d, client:%s still registered\n",
+ curr_event->event_type,
+ curr_event->client_name);
+ curr_event->active = false;
+ list_del(&curr_event->list);
+ }
+ mutex_unlock(&phandle->phandle_lock);
+
sde_power_data_bus_unregister(&phandle->data_bus_handle);
sde_power_reg_bus_unregister(phandle->reg_bus_hdl);
@@ -708,6 +775,9 @@
mp->num_vreg = 0;
mp->num_clk = 0;
+
+ if (phandle->rsc_client)
+ sde_rsc_client_destroy(phandle->rsc_client);
}
int sde_power_resource_enable(struct sde_power_handle *phandle,
@@ -757,6 +827,9 @@
goto end;
if (enable) {
+ sde_power_event_trigger_locked(phandle,
+ SDE_POWER_EVENT_PRE_ENABLE);
+
rc = sde_power_data_bus_update(&phandle->data_bus_handle,
enable);
if (rc) {
@@ -764,10 +837,13 @@
goto data_bus_hdl_err;
}
- rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
- if (rc) {
- pr_err("failed to enable vregs rc=%d\n", rc);
- goto vreg_err;
+ if (!phandle->rsc_client_init) {
+ rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg,
+ enable);
+ if (rc) {
+ pr_err("failed to enable vregs rc=%d\n", rc);
+ goto vreg_err;
+ }
}
rc = sde_power_reg_bus_update(phandle->reg_bus_hdl,
@@ -777,20 +853,39 @@
goto reg_bus_hdl_err;
}
+ rc = sde_power_rsc_update(phandle, true);
+ if (rc) {
+ pr_err("failed to update rsc\n");
+ goto rsc_err;
+ }
+
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
if (rc) {
pr_err("clock enable failed rc:%d\n", rc);
goto clk_err;
}
+
+ sde_power_event_trigger_locked(phandle,
+ SDE_POWER_EVENT_POST_ENABLE);
+
} else {
+ sde_power_event_trigger_locked(phandle,
+ SDE_POWER_EVENT_PRE_DISABLE);
+
msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+ sde_power_rsc_update(phandle, false);
+
sde_power_reg_bus_update(phandle->reg_bus_hdl,
max_usecase_ndx);
- msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
-
+ if (!phandle->rsc_client_init)
+ msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg,
+ enable);
sde_power_data_bus_update(&phandle->data_bus_handle, enable);
+
+ sde_power_event_trigger_locked(phandle,
+ SDE_POWER_EVENT_POST_DISABLE);
}
end:
@@ -798,9 +893,12 @@
return rc;
clk_err:
+ sde_power_rsc_update(phandle, false);
+rsc_err:
sde_power_reg_bus_update(phandle->reg_bus_hdl, prev_usecase_ndx);
reg_bus_hdl_err:
- msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
+ if (!phandle->rsc_client_init)
+ msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
vreg_err:
sde_power_data_bus_update(&phandle->data_bus_handle, 0);
data_bus_hdl_err:
@@ -903,3 +1001,52 @@
return clk;
}
+
+struct sde_power_event *sde_power_handle_register_event(
+ struct sde_power_handle *phandle,
+ u32 event_type, void (*cb_fnc)(u32 event_type, void *usr),
+ void *usr, char *client_name)
+{
+ struct sde_power_event *event;
+
+ if (!phandle) {
+ pr_err("invalid power handle\n");
+ return ERR_PTR(-EINVAL);
+ } else if (!cb_fnc || !event_type) {
+ pr_err("no callback fnc or event type\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ event = kzalloc(sizeof(struct sde_power_event), GFP_KERNEL);
+ if (!event)
+ return ERR_PTR(-ENOMEM);
+
+ event->event_type = event_type;
+ event->cb_fnc = cb_fnc;
+ event->usr = usr;
+ strlcpy(event->client_name, client_name, MAX_CLIENT_NAME_LEN);
+ event->active = true;
+
+ mutex_lock(&phandle->phandle_lock);
+ list_add(&event->list, &phandle->event_list);
+ mutex_unlock(&phandle->phandle_lock);
+
+ return event;
+}
+
+void sde_power_handle_unregister_event(
+ struct sde_power_handle *phandle,
+ struct sde_power_event *event)
+{
+ if (!phandle || !event) {
+ pr_err("invalid phandle or event\n");
+ } else if (!event->active) {
+ pr_err("power handle deinit already done\n");
+ kfree(event);
+ } else {
+ mutex_lock(&phandle->phandle_lock);
+ list_del_init(&event->list);
+ mutex_unlock(&phandle->phandle_lock);
+ kfree(event);
+ }
+}
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index 4e262a3..b26ef9f 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -23,6 +23,18 @@
#include <linux/sde_io_util.h>
+/* event will be triggered before power handler disable */
+#define SDE_POWER_EVENT_PRE_DISABLE 0x1
+
+/* event will be triggered after power handler disable */
+#define SDE_POWER_EVENT_POST_DISABLE 0x2
+
+/* event will be triggered before power handler enable */
+#define SDE_POWER_EVENT_PRE_ENABLE 0x4
+
+/* event will be triggered after power handler enable */
+#define SDE_POWER_EVENT_POST_ENABLE 0x8
+
/**
* mdss_bus_vote_type: register bus vote type
* VOTE_INDEX_DISABLE: removes the client vote
@@ -59,6 +71,7 @@
* @list: list to attach power handle master list
* @ab: arbitrated bandwidth for each bus client
* @ib: instantaneous bandwidth for each bus client
+ * @active: inidcates the state of sde power handle
*/
struct sde_power_client {
char name[MAX_CLIENT_NAME_LEN];
@@ -68,6 +81,7 @@
struct list_head list;
u64 ab[SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX];
u64 ib[SDE_POWER_HANDLE_DATA_BUS_CLIENT_MAX];
+ bool active;
};
/**
@@ -90,6 +104,24 @@
u32 ao_bw_uc_idx;
};
+/*
+ * struct sde_power_event - local event registration structure
+ * @client_name: name of the client registering
+ * @cb_fnc: pointer to desired callback function
+ * @usr: user pointer to pass to callback event trigger
+ * @event: refer to SDE_POWER_HANDLE_EVENT_*
+ * @list: list to attach event master list
+ * @active: indicates the state of sde power handle
+ */
+struct sde_power_event {
+ char client_name[MAX_CLIENT_NAME_LEN];
+ void (*cb_fnc)(u32 event_type, void *usr);
+ void *usr;
+ u32 event_type;
+ struct list_head list;
+ bool active;
+};
+
/**
* struct sde_power_handle: power handle main struct
* @mp: module power for clock and regulator
@@ -99,6 +131,9 @@
* @usecase_ndx: current usecase index
* @reg_bus_hdl: current register bus handle
* @data_bus_handle: context structure for data bus control
+ * @event_list: current power handle event list
+ * @rsc_client: sde rsc client pointer
+ * @rsc_client_init: boolean to control rsc client create
*/
struct sde_power_handle {
struct dss_module_power mp;
@@ -108,6 +143,9 @@
u32 current_usecase_ndx;
u32 reg_bus_hdl;
struct sde_power_data_bus_handle data_bus_handle;
+ struct list_head event_list;
+ struct sde_rsc_client *rsc_client;
+ bool rsc_client_init;
};
/**
@@ -226,4 +264,28 @@
void sde_power_data_bus_bandwidth_ctrl(struct sde_power_handle *phandle,
struct sde_power_client *pclient, int enable);
+/**
+ * sde_power_handle_register_event - register a callback function for an event.
+ * Clients can register for multiple events with a single register.
+ * Any block with access to phandle can register for the event
+ * notification.
+ * @phandle: power handle containing the resources
+ * @event_type: event type to register; refer SDE_POWER_HANDLE_EVENT_*
+ * @cb_fnc: pointer to desired callback function
+ * @usr: user pointer to pass to callback on event trigger
+ *
+ * Return: event pointer if success, or error code otherwise
+ */
+struct sde_power_event *sde_power_handle_register_event(
+ struct sde_power_handle *phandle,
+ u32 event_type, void (*cb_fnc)(u32 event_type, void *usr),
+ void *usr, char *client_name);
+/**
+ * sde_power_handle_unregister_event - unregister callback for event(s)
+ * @phandle: power handle containing the resources
+ * @event: event pointer returned after power handle register
+ */
+void sde_power_handle_unregister_event(struct sde_power_handle *phandle,
+ struct sde_power_event *event);
+
#endif /* _SDE_POWER_HANDLE_H_ */
diff --git a/drivers/mailbox/qti-tcs.c b/drivers/mailbox/qti-tcs.c
index a9ddf0f..96b4472 100644
--- a/drivers/mailbox/qti-tcs.c
+++ b/drivers/mailbox/qti-tcs.c
@@ -15,6 +15,7 @@
#include <linux/atomic.h>
#include <linux/bitmap.h>
+#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
@@ -86,6 +87,7 @@
#define TCS_TYPE_NR 4
#define TCS_MBOX_TOUT_MS 2000
#define MAX_POOL_SIZE (MAX_TCS_PER_TYPE * TCS_TYPE_NR)
+#define TCS_M_INIT 0xFFFF
struct tcs_drv;
@@ -97,10 +99,12 @@
struct tasklet_struct tasklet;
struct delayed_work dwork;
int err;
+ int idx;
+ bool in_use;
};
struct tcs_response_pool {
- struct tcs_response *resp;
+ struct tcs_response resp[MAX_POOL_SIZE];
spinlock_t lock;
DECLARE_BITMAP(avail, MAX_POOL_SIZE);
};
@@ -116,8 +120,6 @@
int ncpt; /* num cmds per tcs */
DECLARE_BITMAP(slots, MAX_TCS_SLOTS);
spinlock_t tcs_lock; /* TCS type lock */
- spinlock_t tcs_m_lock[MAX_TCS_PER_TYPE];
- struct tcs_response *resp[MAX_TCS_PER_TYPE];
};
/* One per MBOX controller */
@@ -148,16 +150,13 @@
if (!pool)
return -ENOMEM;
- pool->resp = devm_kzalloc(&drv->pdev->dev, sizeof(*pool->resp) *
- MAX_POOL_SIZE, GFP_KERNEL);
- if (!pool->resp)
- return -ENOMEM;
-
for (i = 0; i < MAX_POOL_SIZE; i++) {
tasklet_init(&pool->resp[i].tasklet, tcs_notify_tx_done,
(unsigned long) &pool->resp[i]);
- INIT_DELAYED_WORK(&pool->resp[i].dwork,
- tcs_notify_timeout);
+ INIT_DELAYED_WORK(&pool->resp[i].dwork, tcs_notify_timeout);
+ pool->resp[i].drv = drv;
+ pool->resp[i].idx = i;
+ pool->resp[i].m = TCS_M_INIT;
}
spin_lock_init(&pool->lock);
@@ -166,39 +165,59 @@
return 0;
}
-static struct tcs_response *get_response_from_pool(struct tcs_drv *drv)
+static struct tcs_response *setup_response(struct tcs_drv *drv,
+ struct tcs_mbox_msg *msg, struct mbox_chan *chan,
+ u32 m, int err)
{
struct tcs_response_pool *pool = drv->resp_pool;
struct tcs_response *resp = ERR_PTR(-ENOMEM);
- unsigned long flags;
int pos;
- spin_lock_irqsave(&pool->lock, flags);
+ spin_lock(&pool->lock);
pos = find_first_zero_bit(pool->avail, MAX_POOL_SIZE);
if (pos != MAX_POOL_SIZE) {
bitmap_set(pool->avail, pos, 1);
resp = &pool->resp[pos];
- memset(resp, 0, sizeof(*resp));
- tasklet_init(&resp->tasklet, tcs_notify_tx_done,
- (unsigned long) resp);
- INIT_DELAYED_WORK(&resp->dwork, tcs_notify_timeout);
- resp->drv = drv;
+ resp->chan = chan;
+ resp->msg = msg;
+ resp->m = m;
+ resp->err = err;
+ resp->in_use = false;
}
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_unlock(&pool->lock);
return resp;
}
-static void free_response_to_pool(struct tcs_response *resp)
+static void free_response(struct tcs_response *resp)
{
struct tcs_response_pool *pool = resp->drv->resp_pool;
- unsigned long flags;
- int i;
- spin_lock_irqsave(&pool->lock, flags);
- i = resp - pool->resp;
- bitmap_clear(pool->avail, i, 1);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock(&pool->lock);
+ resp->err = -EINVAL;
+ bitmap_clear(pool->avail, resp->idx, 1);
+ spin_unlock(&pool->lock);
+}
+
+static inline struct tcs_response *get_response(struct tcs_drv *drv, u32 m)
+{
+ struct tcs_response_pool *pool = drv->resp_pool;
+ struct tcs_response *resp = NULL;
+ int pos = 0;
+
+ do {
+ pos = find_next_bit(pool->avail, MAX_POOL_SIZE, pos);
+ if (pos == MAX_POOL_SIZE)
+ break;
+ resp = &pool->resp[pos];
+ if (resp->m == m && !resp->in_use) {
+ resp->in_use = true;
+ break;
+ }
+ pos++;
+ } while (1);
+
+ return resp;
}
static inline u32 read_drv_config(void __iomem *base)
@@ -226,7 +245,7 @@
write_tcs_reg(base, reg, m, n, data);
if (data == read_tcs_reg(base, reg, m, n))
break;
- cpu_relax();
+ udelay(1);
} while (1);
}
@@ -311,13 +330,6 @@
return get_tcs_of_type(drv, type);
}
-static inline struct tcs_response *get_tcs_response(struct tcs_drv *drv, int m)
-{
- struct tcs_mbox *tcs = get_tcs_from_index(drv, m);
-
- return tcs ? tcs->resp[m - tcs->tcs_offset] : NULL;
-}
-
static inline void send_tcs_response(struct tcs_response *resp)
{
tasklet_schedule(&resp->tasklet);
@@ -340,7 +352,6 @@
struct tcs_mbox *tcs;
struct tcs_response *resp;
struct tcs_cmd *cmd;
- u32 irq_clear = 0;
u32 data;
/* Know which TCSes were triggered */
@@ -350,8 +361,7 @@
if (!(irq_status & BIT(m)))
continue;
- /* Find the TCS that triggered */
- resp = get_tcs_response(drv, m);
+ resp = get_response(drv, m);
if (!resp) {
pr_err("No resp request for TCS-%d\n", m);
continue;
@@ -397,18 +407,13 @@
write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, 0);
}
- /* Notify the client that this request is completed. */
+ /* Clear the TCS IRQ status */
+ write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, BIT(m));
+
+ /* Clean up response object and notify mbox in tasklet */
send_tcs_response(resp);
- irq_clear |= BIT(m);
- }
- /* Clear the TCS IRQ status */
- write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, irq_clear);
-
- /* Mark the TCS as free */
- for (m = 0; irq_status >= BIT(m); m++) {
- if (!(irq_status & BIT(m)))
- continue;
+ /* Notify the client that this request is completed. */
atomic_set(&drv->tcs_in_use[m], 0);
}
@@ -435,8 +440,8 @@
int err = resp->err;
int m = resp->m;
- free_response_to_pool(resp);
mbox_notify_tx_done(chan, msg, m, err);
+ free_response(resp);
}
/**
@@ -468,7 +473,7 @@
cmd = &msg->payload[i];
addr = read_tcs_reg(drv->reg_base, TCS_DRV_CMD_ADDR,
m, i);
- pending = (cmd->addr == addr);
+ pending |= (cmd->addr == addr);
}
if (pending) {
pr_err("TCS-%d blocked waiting for RPMH to respond.\n",
@@ -481,8 +486,8 @@
}
}
- free_response_to_pool(resp);
mbox_notify_tx_done(chan, msg, -1, -ETIMEDOUT);
+ free_response(resp);
}
static void __tcs_buffer_write(struct tcs_drv *drv, int d, int m, int n,
@@ -525,8 +530,6 @@
write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, cmd_enable);
if (trigger) {
- /* Mark the TCS as busy */
- atomic_set(&drv->tcs_in_use[m], 1);
/* HW req: Clear the DRV_CONTROL and enable TCS again */
write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, 0);
write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, enable);
@@ -590,13 +593,14 @@
}
retry:
if (!is_free)
- cpu_relax();
+ udelay(1);
} while (!is_free);
}
static int find_free_tcs(struct tcs_mbox *tcs)
{
int slot, m = 0;
+ u32 irq_status;
/* Loop until we find a free AMC */
do {
@@ -604,9 +608,14 @@
slot = m * tcs->ncpt;
break;
}
- if (++m >= tcs->num_tcs)
+ if (++m >= tcs->num_tcs) {
m = 0;
- cpu_relax();
+ irq_status = read_tcs_reg(tcs->drv->reg_base,
+ TCS_DRV_IRQ_STATUS, 0, 0);
+ WARN((irq_status & tcs->tcs_mask && in_irq()),
+ "TCS busy. Request should not be made from hard IRQ context.");
+ udelay(10);
+ }
} while (1);
return slot;
@@ -663,26 +672,6 @@
return (slot != MAX_TCS_SLOTS) ? slot : -ENOMEM;
}
-static struct tcs_response *setup_response(struct tcs_mbox *tcs,
- struct mbox_chan *chan, struct tcs_mbox_msg *msg, int m)
-{
- struct tcs_response *resp = get_response_from_pool(tcs->drv);
-
- if (IS_ERR(resp))
- return resp;
-
- if (m < tcs->tcs_offset)
- return ERR_PTR(-EINVAL);
-
- tcs->resp[m - tcs->tcs_offset] = resp;
- resp->msg = msg;
- resp->chan = chan;
- resp->m = m;
- resp->err = 0;
-
- return resp;
-}
-
static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
bool trigger)
{
@@ -697,14 +686,20 @@
if (IS_ERR(tcs))
return PTR_ERR(tcs);
+ if (trigger)
+ resp = setup_response(drv, msg, chan, TCS_M_INIT, 0);
+
/* Identify the sequential slots that we can write to */
spin_lock(&tcs->tcs_lock);
slot = find_slots(tcs, msg);
if (slot < 0) {
dev_err(dev, "No TCS slot found.\n");
spin_unlock(&tcs->tcs_lock);
+ if (resp)
+ free_response(resp);
return slot;
}
+
/* Mark the slots as in-use, before we unlock */
if (tcs->type == SLEEP_TCS || tcs->type == WAKE_TCS)
bitmap_set(tcs->slots, slot, msg->num_payload);
@@ -713,27 +708,16 @@
for (i = 0; tcs->cmd_addr && i < msg->num_payload; i++)
tcs->cmd_addr[slot + i] = msg->payload[i].addr;
- if (trigger)
- resp = setup_response(tcs, chan, msg,
- slot / tcs->ncpt + tcs->tcs_offset);
-
- spin_unlock(&tcs->tcs_lock);
-
- /*
- * Find the TCS corresponding to the slot and start writing.
- * Break down 'slot' into a 'n' position in the 'm'th TCS.
- */
offset = slot / tcs->ncpt;
m = offset + tcs->tcs_offset;
n = slot % tcs->ncpt;
- spin_lock(&tcs->tcs_m_lock[offset]);
+ /* Block, if we have an address from the msg in flight */
if (trigger) {
- /* Block, if we have an address from the msg in flight */
+ resp->m = m;
+ /* Mark the TCS as busy */
+ atomic_set(&drv->tcs_in_use[m], 1);
wait_for_req_inflight(drv, tcs, msg);
- /* If the TCS is busy there is nothing to do but spin wait */
- while (!tcs_is_free(drv, m))
- cpu_relax();
}
/* Write to the TCS or AMC */
@@ -743,7 +727,7 @@
if (trigger)
schedule_tcs_err_response(resp);
- spin_unlock(&tcs->tcs_m_lock[offset]);
+ spin_unlock(&tcs->tcs_lock);
return 0;
}
@@ -769,11 +753,9 @@
spin_lock(&tcs->tcs_lock);
for (i = 0; i < tcs->num_tcs; i++) {
m = i + tcs->tcs_offset;
- spin_lock(&tcs->tcs_m_lock[i]);
while (!tcs_is_free(drv, m))
- cpu_relax();
+ udelay(1);
__tcs_buffer_invalidate(drv->reg_base, m);
- spin_unlock(&tcs->tcs_m_lock[i]);
}
/* Mark the TCS as free */
bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
@@ -840,12 +822,9 @@
tx_fail:
if (ret) {
struct tcs_drv *drv = container_of(chan->mbox,
- struct tcs_drv, mbox);
- struct tcs_response *resp = get_response_from_pool(drv);
-
- resp->chan = chan;
- resp->msg = msg;
- resp->err = ret;
+ struct tcs_drv, mbox);
+ struct tcs_response *resp = setup_response(
+ drv, msg, chan, TCS_M_INIT, ret);
dev_err(dev, "Error sending RPMH message %d\n", ret);
send_tcs_response(resp);
@@ -1040,8 +1019,6 @@
tcs->ncpt = (tcs->type == CONTROL_TCS) ? TCS_HIDDEN_MAX_SLOTS
: ncpt;
spin_lock_init(&tcs->tcs_lock);
- for (j = 0; j < ARRAY_SIZE(tcs->tcs_m_lock); j++)
- spin_lock_init(&tcs->tcs_m_lock[j]);
if (tcs->num_tcs <= 0 || tcs->type == CONTROL_TCS)
continue;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index a0b53bb..3bf6ce0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -140,6 +140,18 @@
int domain;
};
+/*
+ * struct sde_rot_debug_bus: rotator debugbus header structure
+ * @wr_addr: write address for debugbus controller
+ * @block_id: rotator debugbus block id
+ * @test_id: rotator debugbus test id
+ */
+struct sde_rot_debug_bus {
+ u32 wr_addr;
+ u32 block_id;
+ u32 test_id;
+};
+
struct sde_rot_vbif_debug_bus {
u32 disable_bus_addr;
u32 block_bus_addr;
@@ -191,6 +203,8 @@
struct sde_rot_vbif_debug_bus *nrt_vbif_dbg_bus;
u32 nrt_vbif_dbg_bus_size;
+ struct sde_rot_debug_bus *rot_dbg_bus;
+ u32 rot_dbg_bus_size;
struct sde_rot_regdump *regdump;
u32 regdump_size;
@@ -199,6 +213,8 @@
int sec_cam_en;
struct ion_client *iclient;
+
+ bool clk_always_on;
};
int sde_rotator_base_init(struct sde_rot_data_type **pmdata,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index e56c70a..e9ff67c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -30,6 +30,7 @@
#define SDE_EVTLOG_DEFAULT_PANIC 1
#define SDE_EVTLOG_DEFAULT_REGDUMP SDE_ROT_DBG_DUMP_IN_MEM
#define SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP SDE_ROT_DBG_DUMP_IN_MEM
+#define SDE_EVTLOG_DEFAULT_ROT_DBGBUSDUMP SDE_ROT_DBG_DUMP_IN_MEM
/*
* evtlog will print this number of entries when it is called through
@@ -53,6 +54,8 @@
#define GROUP_BYTES 4
#define ROW_BYTES 16
+#define SDE_ROT_TEST_MASK(id, tp) ((id << 4) | (tp << 1) | BIT(0))
+
static DEFINE_SPINLOCK(sde_rot_xlock);
/*
@@ -86,11 +89,14 @@
* @panic_on_err - boolean indicates issue panic after EVTLOG dump
* @enable_reg_dump - control in-log/memory dump for rotator registers
* @enable_vbif_dbgbus_dump - control in-log/memory dump for VBIF debug bus
+ * @enable_rot_dbgbus_dump - control in-log/memroy dump for rotator debug bus
* @evtlog_dump_work - schedule work strucutre for timeout handler
* @work_dump_reg - storage for register dump control in schedule work
* @work_panic - storage for panic control in schedule work
* @work_vbif_dbgbus - storage for VBIF debug bus control in schedule work
+ * @work_rot_dbgbus - storage for rotator debug bus control in schedule work
* @nrt_vbif_dbgbus_dump - memory buffer for VBIF debug bus dumping
+ * @rot_dbgbus_dump - memory buffer for rotator debug bus dumping
* @reg_dump_array - memory buffer for rotator registers dumping
*/
struct sde_rot_dbg_evtlog {
@@ -103,14 +109,88 @@
u32 panic_on_err;
u32 enable_reg_dump;
u32 enable_vbif_dbgbus_dump;
+ u32 enable_rot_dbgbus_dump;
struct work_struct evtlog_dump_work;
bool work_dump_reg;
bool work_panic;
bool work_vbif_dbgbus;
+ bool work_rot_dbgbus;
u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
+ u32 *rot_dbgbus_dump;
u32 *reg_dump_array[SDE_ROT_DEBUG_BASE_MAX];
} sde_rot_dbg_evtlog;
+static void sde_rot_dump_debug_bus(u32 bus_dump_flag, u32 **dump_mem)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ u32 status = 0;
+ struct sde_rot_debug_bus *head;
+ phys_addr_t phys = 0;
+ int i;
+ u32 offset;
+ void __iomem *base;
+
+ in_log = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG);
+ in_mem = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM);
+ base = mdata->sde_io.base;
+
+ if (!base || !mdata->rot_dbg_bus || !mdata->rot_dbg_bus_size)
+ return;
+
+ pr_info("======== SDE Rotator Debug bus DUMP =========\n");
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&mdata->pdev->dev,
+ mdata->rot_dbg_bus_size * 4 * sizeof(u32),
+ &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr,
+ dump_addr + (u32)mdata->rot_dbg_bus_size * 16);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ sde_smmu_ctrl(1);
+
+ for (i = 0; i < mdata->rot_dbg_bus_size; i++) {
+ head = mdata->rot_dbg_bus + i;
+ writel_relaxed(SDE_ROT_TEST_MASK(head->block_id, head->test_id),
+ base + head->wr_addr);
+ wmb(); /* make sure test bits were written */
+
+ offset = head->wr_addr + 0x4;
+
+ status = readl_relaxed(base + offset);
+
+ if (in_log)
+ pr_err("waddr=0x%x blk=%d tst=%d val=0x%x\n",
+ head->wr_addr, head->block_id, head->test_id,
+ status);
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = head->wr_addr;
+ dump_addr[i*4 + 1] = head->block_id;
+ dump_addr[i*4 + 2] = head->test_id;
+ dump_addr[i*4 + 3] = status;
+ }
+
+ /* Disable debug bus once we are done */
+ writel_relaxed(0, base + head->wr_addr);
+ }
+
+ sde_smmu_ctrl(0);
+
+ pr_info("========End Debug bus=========\n");
+}
+
/*
* sde_rot_evtlog_is_enabled - helper function for checking EVTLOG
* enable/disable
@@ -518,18 +598,26 @@
* @dump_vbif_debug_bus: boolean indicates VBIF debug bus dump
*/
static void sde_rot_evtlog_dump_helper(bool dead, const char *panic_name,
- bool dump_rot, bool dump_vbif_debug_bus)
+ bool dump_rot, bool dump_vbif_debug_bus, bool dump_rot_debug_bus)
{
sde_rot_evtlog_dump_all();
- if (dump_rot)
- sde_rot_dump_reg_all();
+ if (dump_rot_debug_bus)
+ sde_rot_dump_debug_bus(
+ sde_rot_dbg_evtlog.enable_rot_dbgbus_dump,
+ &sde_rot_dbg_evtlog.rot_dbgbus_dump);
if (dump_vbif_debug_bus)
sde_rot_dump_vbif_debug_bus(
sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump,
&sde_rot_dbg_evtlog.nrt_vbif_dbgbus_dump);
+ /*
+ * Rotator registers always dump last
+ */
+ if (dump_rot)
+ sde_rot_dump_reg_all();
+
if (dead)
panic(panic_name);
}
@@ -544,7 +632,8 @@
sde_rot_dbg_evtlog.work_panic,
"evtlog_workitem",
sde_rot_dbg_evtlog.work_dump_reg,
- sde_rot_dbg_evtlog.work_vbif_dbgbus);
+ sde_rot_dbg_evtlog.work_vbif_dbgbus,
+ sde_rot_dbg_evtlog.work_rot_dbgbus);
}
/*
@@ -569,6 +658,7 @@
bool dead = false;
bool dump_rot = false;
bool dump_vbif_dbgbus = false;
+ bool dump_rot_dbgbus = false;
char *blk_name = NULL;
va_list args;
@@ -590,6 +680,9 @@
if (!strcmp(blk_name, "vbif_dbg_bus"))
dump_vbif_dbgbus = true;
+ if (!strcmp(blk_name, "rot_dbg_bus"))
+ dump_rot_dbgbus = true;
+
if (!strcmp(blk_name, "panic"))
dead = true;
}
@@ -600,10 +693,11 @@
sde_rot_dbg_evtlog.work_panic = dead;
sde_rot_dbg_evtlog.work_dump_reg = dump_rot;
sde_rot_dbg_evtlog.work_vbif_dbgbus = dump_vbif_dbgbus;
+ sde_rot_dbg_evtlog.work_rot_dbgbus = dump_rot_dbgbus;
schedule_work(&sde_rot_dbg_evtlog.evtlog_dump_work);
} else {
sde_rot_evtlog_dump_helper(dead, name, dump_rot,
- dump_vbif_dbgbus);
+ dump_vbif_dbgbus, dump_rot_dbgbus);
}
}
@@ -836,6 +930,13 @@
return -EINVAL;
}
+ mdata->clk_always_on = false;
+ if (!debugfs_create_bool("clk_always_on", 0644,
+ debugfs_root, &mdata->clk_always_on)) {
+ SDEROT_WARN("failed to create debugfs clk_always_on\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -919,12 +1020,16 @@
&sde_rot_dbg_evtlog.enable_reg_dump);
debugfs_create_u32("vbif_dbgbus_dump", 0644, sde_rot_dbg_evtlog.evtlog,
&sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump);
+ debugfs_create_u32("rot_dbgbus_dump", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.enable_rot_dbgbus_dump);
sde_rot_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
sde_rot_dbg_evtlog.panic_on_err = SDE_EVTLOG_DEFAULT_PANIC;
sde_rot_dbg_evtlog.enable_reg_dump = SDE_EVTLOG_DEFAULT_REGDUMP;
sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump =
SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP;
+ sde_rot_dbg_evtlog.enable_rot_dbgbus_dump =
+ SDE_EVTLOG_DEFAULT_ROT_DBGBUSDUMP;
pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n",
sde_rot_dbg_evtlog.evtlog_enable,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index a152573..8f2746d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -385,11 +385,95 @@
};
static struct sde_rot_vbif_debug_bus nrt_vbif_dbg_bus_r3[] = {
- {0x214, 0x21c, 16, 1, 0x10}, /* arb clients */
+ {0x214, 0x21c, 16, 1, 0x200}, /* arb clients main */
{0x214, 0x21c, 0, 12, 0x13}, /* xin blocks - axi side */
{0x21c, 0x214, 0, 12, 0xc}, /* xin blocks - clock side */
};
+static struct sde_rot_debug_bus rot_dbgbus_r3[] = {
+ /*
+ * rottop - 0xA8850
+ */
+ /* REGDMA */
+ { 0XA8850, 0, 0 },
+ { 0XA8850, 0, 1 },
+ { 0XA8850, 0, 2 },
+ { 0XA8850, 0, 3 },
+ { 0XA8850, 0, 4 },
+
+ /* ROT_WB */
+ { 0XA8850, 1, 0 },
+ { 0XA8850, 1, 1 },
+ { 0XA8850, 1, 2 },
+ { 0XA8850, 1, 3 },
+ { 0XA8850, 1, 4 },
+ { 0XA8850, 1, 5 },
+ { 0XA8850, 1, 6 },
+ { 0XA8850, 1, 7 },
+
+ /* UBWC_DEC */
+ { 0XA8850, 2, 0 },
+
+ /* UBWC_ENC */
+ { 0XA8850, 3, 0 },
+
+ /* ROT_FETCH_0 */
+ { 0XA8850, 4, 0 },
+ { 0XA8850, 4, 1 },
+ { 0XA8850, 4, 2 },
+ { 0XA8850, 4, 3 },
+ { 0XA8850, 4, 4 },
+ { 0XA8850, 4, 5 },
+ { 0XA8850, 4, 6 },
+ { 0XA8850, 4, 7 },
+
+ /* ROT_FETCH_1 */
+ { 0XA8850, 5, 0 },
+ { 0XA8850, 5, 1 },
+ { 0XA8850, 5, 2 },
+ { 0XA8850, 5, 3 },
+ { 0XA8850, 5, 4 },
+ { 0XA8850, 5, 5 },
+ { 0XA8850, 5, 6 },
+ { 0XA8850, 5, 7 },
+
+ /* ROT_FETCH_2 */
+ { 0XA8850, 6, 0 },
+ { 0XA8850, 6, 1 },
+ { 0XA8850, 6, 2 },
+ { 0XA8850, 6, 3 },
+ { 0XA8850, 6, 4 },
+ { 0XA8850, 6, 5 },
+ { 0XA8850, 6, 6 },
+ { 0XA8850, 6, 7 },
+
+ /* ROT_FETCH_3 */
+ { 0XA8850, 7, 0 },
+ { 0XA8850, 7, 1 },
+ { 0XA8850, 7, 2 },
+ { 0XA8850, 7, 3 },
+ { 0XA8850, 7, 4 },
+ { 0XA8850, 7, 5 },
+ { 0XA8850, 7, 6 },
+ { 0XA8850, 7, 7 },
+
+ /* ROT_FETCH_4 */
+ { 0XA8850, 8, 0 },
+ { 0XA8850, 8, 1 },
+ { 0XA8850, 8, 2 },
+ { 0XA8850, 8, 3 },
+ { 0XA8850, 8, 4 },
+ { 0XA8850, 8, 5 },
+ { 0XA8850, 8, 6 },
+ { 0XA8850, 8, 7 },
+
+ /* ROT_UNPACK_0*/
+ { 0XA8850, 9, 0 },
+ { 0XA8850, 9, 1 },
+ { 0XA8850, 9, 2 },
+ { 0XA8850, 9, 3 },
+};
+
static struct sde_rot_regdump sde_rot_r3_regdump[] = {
{ "SDEROT_ROTTOP", SDE_ROT_ROTTOP_OFFSET, 0x100, SDE_ROT_REGDUMP_READ },
{ "SDEROT_SSPP", SDE_ROT_SSPP_OFFSET, 0x200, SDE_ROT_REGDUMP_READ },
@@ -1430,7 +1514,8 @@
sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
if (status & ROT_ERROR_BIT)
- SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic");
+ SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
+ "vbif_dbg_bus", "panic");
return sts;
}
@@ -1614,8 +1699,8 @@
SDEROT_ERR(
"Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
swts, hwts, regdmasts, rotsts);
- SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus",
- "panic");
+ SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
+ "vbif_dbg_bus", "panic");
}
/* Turn off rotator clock after checking rotator registers */
@@ -2134,6 +2219,17 @@
SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
BIT(XIN_WRITEBACK));
+ /*
+ * For debug purpose, disable clock gating, i.e. Clocks always on
+ */
+ if (mdata->clk_always_on) {
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_CLKON, 0x3);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0, 0x3);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL1,
+ 0xFFFF);
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_CLK_CTRL, 1);
+ }
+
return 0;
error:
@@ -2260,6 +2356,9 @@
mdata->nrt_vbif_dbg_bus_size =
ARRAY_SIZE(nrt_vbif_dbg_bus_r3);
+ mdata->rot_dbg_bus = rot_dbgbus_r3;
+ mdata->rot_dbg_bus_size = ARRAY_SIZE(rot_dbgbus_r3);
+
mdata->regdump = sde_rot_r3_regdump;
mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump);
SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, 0);
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index c042a4a..1a1078d 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1965,6 +1965,8 @@
if (device->res->pm_qos_latency_us &&
pm_qos_request_active(&device->qos))
pm_qos_remove_request(&device->qos);
+
+ __resume(device);
__set_state(device, VENUS_STATE_DEINIT);
__unload_fw(device);
@@ -4054,6 +4056,8 @@
__venus_power_off(device);
device->resources.fw.cookie = NULL;
__deinit_resources(device);
+
+ dprintk(VIDC_PROF, "Firmware unloaded successfully\n");
}
static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index bcc296b..d8e9599 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2200,6 +2200,17 @@
int need_retune = card->host->need_retune;
int ecc_err = 0, gen_err = 0;
+ if (card->host->sdr104_wa && mmc_card_sd(card) &&
+ (card->host->ios.timing == MMC_TIMING_UHS_SDR104) &&
+ !card->sdr104_blocked &&
+ (brq->data.error == -EILSEQ ||
+ brq->data.error == -EIO ||
+ brq->data.error == -ETIMEDOUT ||
+ brq->cmd.error == -EILSEQ ||
+ brq->cmd.error == -EIO ||
+ brq->cmd.error == -ETIMEDOUT))
+ card->err_in_sdr104 = true;
+
/*
* sbc.error indicates a problem with the set block count
* command. No data will have been transferred.
@@ -3640,6 +3651,7 @@
struct mmc_async_req *areq;
const u8 packed_nr = 2;
u8 reqs = 0;
+ bool reset = false;
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
unsigned long waitfor = jiffies;
#endif
@@ -3685,6 +3697,26 @@
type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
mmc_queue_bounce_post(mq_rq);
+ if (card->err_in_sdr104) {
+ /*
+ * Data CRC/timeout errors will manifest as CMD/DATA
+ * ERR. But we'd like to retry these too.
+ * Moreover, no harm done if this fails too for multiple
+ * times, we anyway reduce the bus-speed and retry the
+ * same request.
+ * If that fails too, we don't override this status.
+ */
+ if (status == MMC_BLK_ABORT ||
+ status == MMC_BLK_CMD_ERR ||
+ status == MMC_BLK_DATA_ERR ||
+ status == MMC_BLK_RETRY)
+ /* reset on all of these errors and retry */
+ reset = true;
+
+ status = MMC_BLK_RETRY;
+ card->err_in_sdr104 = false;
+ }
+
switch (status) {
case MMC_BLK_SUCCESS:
case MMC_BLK_PARTIAL:
@@ -3725,8 +3757,32 @@
break;
case MMC_BLK_RETRY:
retune_retry_done = brq->retune_retry_done;
- if (retry++ < MMC_BLK_MAX_RETRIES)
+ if (retry++ < MMC_BLK_MAX_RETRIES) {
break;
+ } else if (reset) {
+ reset = false;
+ /*
+ * If we exhaust all the retries due to
+ * CRC/timeout errors in SDR140 mode with UHS SD
+ * cards, re-configure the card in SDR50
+ * bus-speed mode.
+ * All subsequent re-init of this card will be
+ * in SDR50 mode, unless it is removed and
+ * re-inserted. When new UHS SD cards are
+ * inserted, it may start at SDR104 mode if
+ * supported by the card.
+ */
+ pr_err("%s: blocked SDR104, lower the bus-speed (SDR50 / DDR50)\n",
+ req->rq_disk->disk_name);
+ mmc_host_clear_sdr104(card->host);
+ mmc_suspend_clk_scaling(card->host);
+ mmc_blk_reset(md, card->host, type);
+ /* SDR104 mode is blocked from now on */
+ card->sdr104_blocked = true;
+ /* retry 5 times again */
+ retry = 0;
+ break;
+ }
/* Fall through */
case MMC_BLK_ABORT:
if (!mmc_blk_reset(md, card->host, type) &&
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9987859..220ba6d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -4175,6 +4175,10 @@
if (ret) {
mmc_card_set_removed(host->card);
+ if (host->card->sdr104_blocked) {
+ mmc_host_set_sdr104(host);
+ host->card->sdr104_blocked = false;
+ }
pr_debug("%s: card remove detected\n", mmc_hostname(host));
}
@@ -4281,7 +4285,7 @@
mmc_release_host(host);
goto out;
}
-+ mmc_rescan_try_freq(host, host->f_min);
+ mmc_rescan_try_freq(host, host->f_min);
mmc_release_host(host);
out:
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 3f39058..2adf42c 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -80,7 +80,6 @@
extern bool mmc_can_scale_clk(struct mmc_host *host);
extern int mmc_init_clk_scaling(struct mmc_host *host);
-extern int mmc_suspend_clk_scaling(struct mmc_host *host);
extern int mmc_resume_clk_scaling(struct mmc_host *host);
extern int mmc_exit_clk_scaling(struct mmc_host *host);
extern unsigned long mmc_get_max_frequency(struct mmc_host *host);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 7f66ad3..7112f9f 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -432,26 +432,26 @@
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104) &&
(card->host->f_max > UHS_SDR104_MIN_DTR)) {
- card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
- } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50) &&
- (card->host->f_max > UHS_DDR50_MIN_DTR)) {
- card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
+ card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR50) &&
(card->host->f_max > UHS_SDR50_MIN_DTR)) {
- card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
+ card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
+ } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50) &&
+ (card->host->f_max > UHS_DDR50_MIN_DTR)) {
+ card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25) &&
(card->host->f_max > UHS_SDR25_MIN_DTR)) {
- card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
+ card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR12)) {
- card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
+ card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
}
}
@@ -1313,6 +1313,8 @@
#endif
mmc_card_clr_suspended(host->card);
+ if (host->card->sdr104_blocked)
+ goto out;
err = mmc_resume_clk_scaling(host);
if (err) {
pr_err("%s: %s: fail to resume clock scaling (%d)\n",
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 9650085..fe62b69 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1939,6 +1939,8 @@
if (of_get_property(np, "qcom,core_3_0v_support", NULL))
pdata->core_3_0v_support = true;
+ pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa");
+
return pdata;
out:
return NULL;
@@ -4422,6 +4424,8 @@
if (msm_host->pdata->nonhotplug)
msm_host->mmc->caps2 |= MMC_CAP2_NONHOTPLUG;
+ msm_host->mmc->sdr104_wa = msm_host->pdata->sdr104_wa;
+
init_completion(&msm_host->pwr_irq_completion);
if (gpio_is_valid(msm_host->pdata->status_gpio)) {
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 533b241..53b1953 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -151,6 +151,7 @@
unsigned char sup_ice_clk_cnt;
struct sdhci_msm_pm_qos_data pm_qos_data;
bool core_3_0v_support;
+ bool sdr104_wa;
};
struct sdhci_msm_bus_vote {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 744e520..53a6ae8 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -3096,7 +3096,10 @@
mmc_hostname(host->mmc), intmask,
host->data->error, ktime_to_ms(ktime_sub(
ktime_get(), host->data_start_time)));
- sdhci_dumpregs(host);
+
+ if (!host->mmc->sdr104_wa ||
+ (host->mmc->ios.timing != MMC_TIMING_UHS_SDR104))
+ sdhci_dumpregs(host);
}
sdhci_finish_data(host);
} else {
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index efcc4e8..9f04957 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -28,6 +28,7 @@
#include <asm/cacheflush.h>
#include <asm/system_misc.h>
+#include <asm/memory.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/restart.h>
@@ -57,11 +58,17 @@
#ifdef CONFIG_QCOM_DLOAD_MODE
#define EDL_MODE_PROP "qcom,msm-imem-emergency_download_mode"
#define DL_MODE_PROP "qcom,msm-imem-download_mode"
+#ifdef CONFIG_RANDOMIZE_BASE
+#define KASLR_OFFSET_PROP "qcom,msm-imem-kaslr_offset"
+#endif
static int in_panic;
static void *dload_mode_addr;
static bool dload_mode_enabled;
static void *emergency_dload_mode_addr;
+#ifdef CONFIG_RANDOMIZE_BASE
+static void *kaslr_imem_addr;
+#endif
static bool scm_dload_supported;
static int dload_set(const char *val, struct kernel_param *kp);
@@ -420,6 +427,27 @@
pr_err("unable to map imem EDLOAD mode offset\n");
}
+#ifdef CONFIG_RANDOMIZE_BASE
+#define KASLR_OFFSET_BIT_MASK 0x00000000FFFFFFFF
+ np = of_find_compatible_node(NULL, NULL, KASLR_OFFSET_PROP);
+ if (!np) {
+ pr_err("unable to find DT imem KASLR_OFFSET node\n");
+ } else {
+ kaslr_imem_addr = of_iomap(np, 0);
+ if (!kaslr_imem_addr)
+ pr_err("unable to map imem KASLR offset\n");
+ }
+
+ if (kaslr_imem_addr && scm_is_secure_device()) {
+ __raw_writel(0xdead4ead, kaslr_imem_addr);
+ __raw_writel(KASLR_OFFSET_BIT_MASK &
+ (kimage_vaddr - KIMAGE_VADDR), kaslr_imem_addr + 4);
+ __raw_writel(KASLR_OFFSET_BIT_MASK &
+ ((kimage_vaddr - KIMAGE_VADDR) >> 32),
+ kaslr_imem_addr + 8);
+ iounmap(kaslr_imem_addr);
+ }
+#endif
#endif
np = of_find_compatible_node(NULL, NULL,
"qcom,msm-imem-restart_reason");
@@ -484,4 +512,4 @@
{
return platform_driver_register(&msm_restart_driver);
}
-device_initcall(msm_restart_init);
+pure_initcall(msm_restart_init);
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index 1635bab..b3c1150 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -108,7 +108,7 @@
struct rpmh_msg *rpm_msg = container_of(msg, struct rpmh_msg, msg);
atomic_dec(rpm_msg->wait_count);
- wake_up_interruptible(rpm_msg->waitq);
+ wake_up(rpm_msg->waitq);
}
static void rpmh_tx_done(struct mbox_client *cl, void *msg, int r)
@@ -151,7 +151,7 @@
/* Signal the blocking thread we are done */
if (waitq) {
atomic_dec(wc);
- wake_up_interruptible(waitq);
+ wake_up(waitq);
}
}
@@ -329,9 +329,7 @@
if (ret < 0)
return ret;
- ret = wait_event_interruptible(waitq, atomic_read(&wait_count) == 0);
- if (ret)
- return ret;
+ wait_event(waitq, atomic_read(&wait_count) == 0);
return rpm_msg.err;
}
@@ -423,13 +421,11 @@
rpm_msg.msg.num_payload = n;
ret = __rpmh_write(rc, state, &rpm_msg);
- if (ret < 0)
- return ret;
-
- ret = wait_event_interruptible(waitq, atomic_read(&wait_count) == 0);
if (ret)
return ret;
+ wait_event(waitq, atomic_read(&wait_count) == 0);
+
return rpm_msg.err;
}
EXPORT_SYMBOL(rpmh_write);
@@ -494,8 +490,6 @@
rpm_msg[i] = __get_rpmh_msg_async(rc, state, cmd, n[i]);
if (IS_ERR_OR_NULL(rpm_msg[i]))
return PTR_ERR(rpm_msg[i]);
- rpm_msg[i]->waitq = &waitq;
- rpm_msg[i]->wait_count = &wait_count;
cmd += n[i];
}
@@ -504,16 +498,18 @@
might_sleep();
atomic_set(&wait_count, count);
for (i = 0; i < count; i++) {
+ rpm_msg[i]->waitq = &waitq;
+ rpm_msg[i]->wait_count = &wait_count;
/* Bypass caching and write to mailbox directly */
ret = mbox_send_message(rc->chan, &rpm_msg[i]->msg);
if (ret < 0)
return ret;
}
- return wait_event_interruptible(waitq,
- atomic_read(&wait_count) == 0);
+ wait_event(waitq, atomic_read(&wait_count) == 0);
} else {
/* Send Sleep requests to the controller, expect no response */
for (i = 0; i < count; i++) {
+ rpm_msg[i]->waitq = NULL;
ret = mbox_send_controller_data(rc->chan,
&rpm_msg[i]->msg);
/* Clean up our call by spoofing tx_done */
@@ -521,6 +517,8 @@
}
return 0;
}
+
+ return 0;
}
EXPORT_SYMBOL(rpmh_write_passthru);
@@ -623,9 +621,7 @@
return ret;
/* Wait until the response is received from RPMH */
- ret = wait_event_interruptible(waitq, atomic_read(&wait_count) == 0);
- if (ret)
- return ret;
+ wait_event(waitq, atomic_read(&wait_count) == 0);
/* Read the data back from the tcs_mbox_msg structrure */
*resp = rpm_msg.cmd[0].data;
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index f3d6209..7a784aa 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -30,6 +30,7 @@
#include <soc/qcom/scm.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/watchdog.h>
+#include <linux/dma-mapping.h>
#define MODULE_NAME "msm_watchdog"
#define WDT0_ACCSCSSNBARK_INT 0
@@ -49,6 +50,7 @@
#define SCM_SET_REGSAVE_CMD 0x2
#define SCM_SVC_SEC_WDOG_DIS 0x7
#define MAX_CPU_CTX_SIZE 2048
+#define MAX_CPU_SCANDUMP_SIZE 0x10000
static struct msm_watchdog_data *wdog_data;
@@ -557,6 +559,49 @@
return;
}
+static void configure_scandump(struct msm_watchdog_data *wdog_dd)
+{
+ int ret;
+ struct msm_dump_entry dump_entry;
+ struct msm_dump_data *cpu_data;
+ int cpu;
+ static dma_addr_t dump_addr;
+ static void *dump_vaddr;
+
+ for_each_cpu(cpu, cpu_present_mask) {
+ cpu_data = devm_kzalloc(wdog_dd->dev,
+ sizeof(struct msm_dump_data),
+ GFP_KERNEL);
+ if (!cpu_data)
+ continue;
+
+ dump_vaddr = (void *) dma_alloc_coherent(wdog_dd->dev,
+ MAX_CPU_SCANDUMP_SIZE,
+ &dump_addr,
+ GFP_KERNEL);
+ if (!dump_vaddr) {
+ dev_err(wdog_dd->dev, "Couldn't get memory for dump\n");
+ continue;
+ }
+ memset(dump_vaddr, 0x0, MAX_CPU_SCANDUMP_SIZE);
+
+ cpu_data->addr = dump_addr;
+ cpu_data->len = MAX_CPU_SCANDUMP_SIZE;
+ dump_entry.id = MSM_DUMP_DATA_SCANDUMP_PER_CPU + cpu;
+ dump_entry.addr = virt_to_phys(cpu_data);
+ ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
+ &dump_entry);
+ if (ret) {
+ dev_err(wdog_dd->dev, "Dump setup failed, id = %d\n",
+ MSM_DUMP_DATA_SCANDUMP_PER_CPU + cpu);
+ dma_free_coherent(wdog_dd->dev, MAX_CPU_SCANDUMP_SIZE,
+ dump_vaddr,
+ dump_addr);
+ devm_kfree(wdog_dd->dev, cpu_data);
+ }
+ }
+}
+
static int init_watchdog_sysfs(struct msm_watchdog_data *wdog_dd)
{
int error = 0;
@@ -617,6 +662,7 @@
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
wdog_dd->min_slack_ticks = UINT_MAX;
wdog_dd->min_slack_ns = ULLONG_MAX;
+ configure_scandump(wdog_dd);
configure_bark_dump(wdog_dd);
timeout = (wdog_dd->bark_time * WDT_HZ)/1000;
__raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME);
diff --git a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
new file mode 100644
index 0000000..6243588
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 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 _DT_BINDINGS_CLK_MSM_GCC_SDX24_H
+#define _DT_BINDINGS_CLK_MSM_GCC_SDX24_H
+
+/* GCC clock registers */
+#define GCC_BLSP1_AHB_CLK 0
+#define GCC_BLSP1_QUP1_I2C_APPS_CLK 1
+#define GCC_BLSP1_QUP1_I2C_APPS_CLK_SRC 2
+#define GCC_BLSP1_QUP1_SPI_APPS_CLK 3
+#define GCC_BLSP1_QUP1_SPI_APPS_CLK_SRC 4
+#define GCC_BLSP1_QUP2_I2C_APPS_CLK 5
+#define GCC_BLSP1_QUP2_I2C_APPS_CLK_SRC 6
+#define GCC_BLSP1_QUP2_SPI_APPS_CLK 7
+#define GCC_BLSP1_QUP2_SPI_APPS_CLK_SRC 8
+#define GCC_BLSP1_QUP3_I2C_APPS_CLK 9
+#define GCC_BLSP1_QUP3_I2C_APPS_CLK_SRC 10
+#define GCC_BLSP1_QUP3_SPI_APPS_CLK 11
+#define GCC_BLSP1_QUP3_SPI_APPS_CLK_SRC 12
+#define GCC_BLSP1_QUP4_I2C_APPS_CLK 13
+#define GCC_BLSP1_QUP4_I2C_APPS_CLK_SRC 14
+#define GCC_BLSP1_QUP4_SPI_APPS_CLK 15
+#define GCC_BLSP1_QUP4_SPI_APPS_CLK_SRC 16
+#define GCC_BLSP1_SLEEP_CLK 17
+#define GCC_BLSP1_UART1_APPS_CLK 18
+#define GCC_BLSP1_UART1_APPS_CLK_SRC 19
+#define GCC_BLSP1_UART2_APPS_CLK 20
+#define GCC_BLSP1_UART2_APPS_CLK_SRC 21
+#define GCC_BLSP1_UART3_APPS_CLK 22
+#define GCC_BLSP1_UART3_APPS_CLK_SRC 23
+#define GCC_BLSP1_UART4_APPS_CLK 24
+#define GCC_BLSP1_UART4_APPS_CLK_SRC 25
+#define GCC_BOOT_ROM_AHB_CLK 26
+#define GCC_CE1_AHB_CLK 27
+#define GCC_CE1_AXI_CLK 28
+#define GCC_CE1_CLK 29
+#define GCC_CPUSS_AHB_CLK 30
+#define GCC_CPUSS_AHB_CLK_SRC 31
+#define GCC_CPUSS_GNOC_CLK 32
+#define GCC_CPUSS_GPLL0_CLK_SRC 33
+#define GCC_CPUSS_RBCPR_CLK 34
+#define GCC_CPUSS_RBCPR_CLK_SRC 35
+#define GCC_GP1_CLK 36
+#define GCC_GP1_CLK_SRC 37
+#define GCC_GP2_CLK 38
+#define GCC_GP2_CLK_SRC 39
+#define GCC_GP3_CLK 40
+#define GCC_GP3_CLK_SRC 41
+#define GCC_MSS_CFG_AHB_CLK 42
+#define GCC_MSS_GPLL0_DIV_CLK_SRC 43
+#define GCC_MSS_SNOC_AXI_CLK 44
+#define GCC_PCIE_AUX_CLK 45
+#define GCC_PCIE_AUX_PHY_CLK_SRC 46
+#define GCC_PCIE_CFG_AHB_CLK 47
+#define GCC_PCIE_MSTR_AXI_CLK 48
+#define GCC_PCIE_PHY_REFGEN_CLK 49
+#define GCC_PCIE_PHY_REFGEN_CLK_SRC 50
+#define GCC_PCIE_PIPE_CLK 51
+#define GCC_PCIE_SLEEP_CLK 52
+#define GCC_PCIE_SLV_AXI_CLK 53
+#define GCC_PCIE_SLV_Q2A_AXI_CLK 54
+#define GCC_PDM2_CLK 55
+#define GCC_PDM2_CLK_SRC 56
+#define GCC_PDM_AHB_CLK 57
+#define GCC_PDM_XO4_CLK 58
+#define GCC_PRNG_AHB_CLK 59
+#define GCC_SDCC1_AHB_CLK 60
+#define GCC_SDCC1_APPS_CLK 61
+#define GCC_SDCC1_APPS_CLK_SRC 62
+#define GCC_SPMI_FETCHER_AHB_CLK 63
+#define GCC_SPMI_FETCHER_CLK 64
+#define GCC_SPMI_FETCHER_CLK_SRC 65
+#define GCC_SYS_NOC_CPUSS_AHB_CLK 66
+#define GCC_USB30_MASTER_CLK 67
+#define GCC_USB30_MASTER_CLK_SRC 68
+#define GCC_USB30_MOCK_UTMI_CLK 69
+#define GCC_USB30_MOCK_UTMI_CLK_SRC 70
+#define GCC_USB30_SLEEP_CLK 71
+#define GCC_USB3_PHY_AUX_CLK 72
+#define GCC_USB3_PHY_AUX_CLK_SRC 73
+#define GCC_USB3_PHY_PIPE_CLK 74
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 75
+#define GCC_XO_DIV4_CLK 76
+#define GPLL0 77
+#define GPLL0_OUT_EVEN 78
+
+/* GDSCs */
+#define PCIE_GDSC 0
+#define USB30_GDSC 1
+
+/* CPU clocks */
+#define CLOCK_A7SS 0
+
+/* GCC reset clocks */
+#define GCC_BLSP1_QUP1_BCR 0
+#define GCC_BLSP1_QUP2_BCR 1
+#define GCC_BLSP1_QUP3_BCR 2
+#define GCC_BLSP1_QUP4_BCR 3
+#define GCC_BLSP1_UART2_BCR 4
+#define GCC_BLSP1_UART3_BCR 5
+#define GCC_BLSP1_UART4_BCR 6
+#define GCC_CE1_BCR 7
+#define GCC_PCIE_BCR 8
+#define GCC_PCIE_PHY_BCR 9
+#define GCC_PDM_BCR 10
+#define GCC_PRNG_BCR 11
+#define GCC_SDCC1_BCR 12
+#define GCC_SPMI_FETCHER_BCR 13
+#define GCC_USB30_BCR 14
+#define GCC_USB_PHY_CFG_AHB2PHY_BCR 15
+
+#endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d0ec667..72dd7ba 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -439,6 +439,8 @@
u8 *cached_ext_csd;
bool cmdq_init;
struct mmc_bkops_info bkops;
+ bool err_in_sdr104;
+ bool sdr104_blocked;
};
/*
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 5d5aff1..959414b 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -179,6 +179,7 @@
extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
extern int mmc_set_auto_bkops(struct mmc_card *card, bool enable);
+extern int mmc_suspend_clk_scaling(struct mmc_host *host);
#define MMC_ERASE_ARG 0x00000000
#define MMC_SECURE_ERASE_ARG 0x80000000
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ba1e826..ecfc173 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -587,6 +587,8 @@
struct io_latency_state io_lat_s;
#endif
+ bool sdr104_wa;
+
/*
* Set to 1 to just stop the SDCLK to the card without
* actually disabling the clock from it's source.
@@ -751,6 +753,16 @@
MMC_CAP_UHS_DDR50);
}
+static inline void mmc_host_clear_sdr104(struct mmc_host *host)
+{
+ host->caps &= ~MMC_CAP_UHS_SDR104;
+}
+
+static inline void mmc_host_set_sdr104(struct mmc_host *host)
+{
+ host->caps |= MMC_CAP_UHS_SDR104;
+}
+
static inline int mmc_host_packed_wr(struct mmc_host *host)
{
return host->caps2 & MMC_CAP2_PACKED_WR;
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 8ec7c30..931b494 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -260,27 +260,6 @@
#define SCSI_INQ_PQ_NOT_CON 0x01
#define SCSI_INQ_PQ_NOT_CAP 0x03
-
-/*
- * Here are some scsi specific ioctl commands which are sometimes useful.
- *
- * Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
- */
-
-/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
-#define SCSI_IOCTL_GET_IDLUN 0x5382
-
-/* 0x5383 and 0x5384 were used for SCSI_IOCTL_TAGGED_{ENABLE,DISABLE} */
-
-/* Used to obtain the host number of a device. */
-#define SCSI_IOCTL_PROBE_HOST 0x5385
-
-/* Used to obtain the bus number for a device */
-#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
-
-/* Used to obtain the PCI location of a device */
-#define SCSI_IOCTL_GET_PCI 0x5387
-
/* Pull a u32 out of a SCSI message (using BE SCSI conventions) */
static inline __u32 scsi_to_u32(__u8 *ptr)
{
diff --git a/include/soc/qcom/memory_dump.h b/include/soc/qcom/memory_dump.h
index a7b87aa..dbae8e8 100644
--- a/include/soc/qcom/memory_dump.h
+++ b/include/soc/qcom/memory_dump.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014-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
@@ -62,7 +62,7 @@
#define MSM_DUMP_MINOR(val) (val & 0xFFFFF)
-#define MAX_NUM_ENTRIES 0x120
+#define MAX_NUM_ENTRIES 0x140
enum msm_dump_data_ids {
MSM_DUMP_DATA_CPU_CTX = 0x00,
@@ -82,10 +82,12 @@
MSM_DUMP_DATA_VSENSE = 0xE9,
MSM_DUMP_DATA_RPM = 0xEA,
MSM_DUMP_DATA_SCANDUMP = 0xEB,
+ MSM_DUMP_DATA_RPMH = 0xEC,
MSM_DUMP_DATA_TMC_ETF = 0xF0,
MSM_DUMP_DATA_TMC_REG = 0x100,
MSM_DUMP_DATA_LOG_BUF = 0x110,
MSM_DUMP_DATA_LOG_BUF_FIRST_IDX = 0x111,
+ MSM_DUMP_DATA_SCANDUMP_PER_CPU = 0x130,
MSM_DUMP_DATA_MAX = MAX_NUM_ENTRIES,
};
diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h
index b1bf6aa..74034c6 100644
--- a/include/uapi/drm/sde_drm.h
+++ b/include/uapi/drm/sde_drm.h
@@ -344,4 +344,16 @@
uint64_t modes;
};
+#define SDE_MAX_ROI_V1 4
+
+/**
+ * struct sde_drm_roi_v1 - list of regions of interest for a drm object
+ * @num_rects: number of valid rectangles in the roi array
+ * @roi: list of roi rectangles
+ */
+struct sde_drm_roi_v1 {
+ uint32_t num_rects;
+ struct drm_clip_rect roi[SDE_MAX_ROI_V1];
+};
+
#endif /* _SDE_DRM_H_ */
diff --git a/include/uapi/scsi/scsi_ioctl.h b/include/uapi/scsi/scsi_ioctl.h
index 516c581a..d9ce5cc 100644
--- a/include/uapi/scsi/scsi_ioctl.h
+++ b/include/uapi/scsi/scsi_ioctl.h
@@ -17,9 +17,25 @@
#define SCSI_REMOVAL_PREVENT 1
#define SCSI_REMOVAL_ALLOW 0
-#ifdef __KERNEL__
+/*
+ * Here are some scsi specific ioctl commands which are sometimes useful.
+ *
+ * Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
+ */
-struct scsi_device;
+/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
+/* 0x5383 and 0x5384 were used for SCSI_IOCTL_TAGGED_{ENABLE,DISABLE} */
+
+/* Used to obtain the host number of a device. */
+#define SCSI_IOCTL_PROBE_HOST 0x5385
+
+/* Used to obtain the bus number for a device */
+#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
+
+/* Used to obtain the PCI location of a device */
+#define SCSI_IOCTL_GET_PCI 0x5387
/*
* Structures used for scsi_ioctl et al.
@@ -42,9 +58,11 @@
unsigned char host_wwn[8]; // include NULL term.
} Scsi_FCTargAddress;
+#ifdef __KERNEL__
+struct scsi_device;
+
int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev,
int cmd, bool ndelay);
extern int scsi_ioctl(struct scsi_device *, int, void __user *);
-
#endif /* __KERNEL__ */
#endif /* _SCSI_IOCTL_H */
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 826f566..654806e 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -153,27 +153,27 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_voice *voice;
- if (!strcmp("VoLTE", substream->pcm->id)) {
+ if (!strncmp("VoLTE", substream->pcm->id, 5)) {
voice = &voice_info[VOLTE_SESSION_INDEX];
pr_debug("%s: Open VoLTE Substream Id=%s\n",
__func__, substream->pcm->id);
- } else if (!strcmp("Voice2", substream->pcm->id)) {
+ } else if (!strncmp("Voice2", substream->pcm->id, 6)) {
voice = &voice_info[VOICE2_SESSION_INDEX];
pr_debug("%s: Open Voice2 Substream Id=%s\n",
__func__, substream->pcm->id);
- } else if (!strcmp("QCHAT", substream->pcm->id)) {
+ } else if (!strncmp("QCHAT", substream->pcm->id, 5)) {
voice = &voice_info[QCHAT_SESSION_INDEX];
pr_debug("%s: Open QCHAT Substream Id=%s\n",
__func__, substream->pcm->id);
- } else if (!strcmp("VoWLAN", substream->pcm->id)) {
+ } else if (!strncmp("VoWLAN", substream->pcm->id, 6)) {
voice = &voice_info[VOWLAN_SESSION_INDEX];
pr_debug("%s: Open VoWLAN Substream Id=%s\n",
__func__, substream->pcm->id);
- } else if (!strcmp("VoiceMMode1", substream->pcm->id)) {
+ } else if (!strncmp("VoiceMMode1", substream->pcm->id, 11)) {
voice = &voice_info[VOICEMMODE1_INDEX];
pr_debug("%s: Open VoiceMMode1 Substream Id=%s\n",
__func__, substream->pcm->id);
- } else if (!strcmp("VoiceMMode2", substream->pcm->id)) {
+ } else if (!strncmp("VoiceMMode2", substream->pcm->id, 11)) {
voice = &voice_info[VOICEMMODE2_INDEX];
pr_debug("%s: Open VoiceMMode2 Substream Id=%s\n",
__func__, substream->pcm->id);