Merge "msm: kgsl: Change polling to use do while loop" into msm-4.9
diff --git a/Documentation/devicetree/bindings/arm/msm/board-id.txt b/Documentation/devicetree/bindings/arm/msm/board-id.txt
new file mode 100644
index 0000000..e07a1c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/board-id.txt
@@ -0,0 +1,64 @@
+* BOARD-ID
+
+The qcom,board-id entry specifies the MSM platform and subtype revision.
+It can optionally be an array of these to indicate multiple hardware that use
+the same device tree. It is expected that the bootloader will use this
+information at boot-up to decide which device tree to use when given multiple
+device trees, some of which may not be compatible with the actual hardware. It
+is the bootloader's responsibility to pass the correct device tree to the kernel.
+
+Legacy format:
+
+It is expected that the qcom,board-id entry be at the top level of the device
+tree structure. The format of the entry is:
+
+ qcom,board-id = <platform_id, subtype_id> [, <p2, s2> ...]
+
+where platform_id and subtype_id are the numeric values for the platform and
+subtype of the current hardware.
+
+The "subtype_id" cell is a 32-bit integer whose bit values are defined as follows:
+ bits 31-20 = Reserved bits
+ bits 19-16 = Boot Device Type.
+ MSM:
+ 0: default (eMMC)
+ 2: EMMC_SDC1
+ 4: BOOT_UFS
+ MDM:
+ 0: default (NAND)
+ 3: EMMC_SDC1
+ bits 15-8 = DDR Size. For devices with DDR Size as 512MB the value is 0x1, default value as 0x0
+ bits 7-0 = Platform Subtype
+
+In the event that a given device tree is applicable to all hardware versions
+matching a given Platform Type / Subtype ID, the major/minior platform version
+fields in the board_id property shall both be specified as 0xff.
+
+Modern format:
+The cell layout of the qcom,board-id property is as follows:
+
+ qcom,board-id = <board_id, reserved>
+
+where board_id is a 32-bit integer whose bit values are defined as follows:
+ bits 31-24 = Platform Subtype ID
+ bits 23-16 = Platform Version (Major)
+ bits 15-8 = Platform Version (Minor)
+ bits 7-0 = Platform Type ID
+
+and the 'reserved' cell is a 32-bit integer whose bit values are defined as follows:
+ bits 31-13 = Reserved Bits
+ bits 12-11 = Panel Detection. 00 - limit to HD, 01 - limit to 720p,
+ 10 - limit to qHD, 11 - limit to FWVGA
+ bits 10-8 = DDR Size. For devices with DDR Size as 512MB the value is 0x1,
+ default value as 0x0
+ bits 7-0 = Platform Subtype
+
+In the event that a given device tree is applicable to all hardware versions
+matching a given Platform Type / Subtype ID, the major/minior platform version
+fields in the board_id property shall both be specified as 0xff.
+
+Example:
+ qcom,board-id = <15 0>;
+ qcom,board-id = <0x01040708, 0>;
+ qcom,board-id = <0x01ffff08, 0>;
+ qcom,board-id = <8, 0x100>;
diff --git a/Documentation/devicetree/bindings/arm/msm/msm-id.txt b/Documentation/devicetree/bindings/arm/msm/msm-id.txt
new file mode 100644
index 0000000..c243154
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/msm-id.txt
@@ -0,0 +1,33 @@
+* MSM-ID
+
+The qcom,msm-id entry specifies the MSM chipset, platform, hardware revision
+and optional manufactured foundry. It can optionally be an array of these to
+indicate multiple hardware that use the same device tree. It is expected that
+the bootloader will use this information at boot-up to decide which device tree
+to use when given multiple device trees, some of which may not be compatible
+with the actual hardware. It is the bootloader's responsibility to pass the
+correct device tree to the kernel.
+
+Format:
+
+It is expected that the qcom,msm-id entry be at the top level of the device
+tree structure. The format can take one of the two forms below:
+
+ qcom,msm-id = <chipset_foundry_id, platform_id, rev_id> [, <c2, p2, r2> ...]
+ qcom,msm-id = <chipset_foundry_id, rev_id> [, <c2, r2> ...]
+
+If the second format is used one must also define the board-id.
+
+The "chipset_foundry_id" consists of three fields as below:
+
+ bits 0-15 = The unique MSM chipset id.
+ bits 16-23 = The optional foundry id. If bootloader doesn't find a device
+ tree which has exact matching foundry-id with hardware it
+ chooses the device tree with foundry-id = 0.
+ bits 24-31 = Reserved.
+
+Example:
+ qcom,msm-id = <0x1007e 15 0>;
+
+ qcom,board-id= <15 2>;
+ qcom,msm-id = <0x1007e 0>;
diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dp.txt b/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
new file mode 100644
index 0000000..790da12
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
@@ -0,0 +1,146 @@
+Qualcomm Technologies, Inc.
+sde-dp is the master Display Port device which supports DP host controllers that are compatible with VESA Display Port interface specification.
+DP Controller: Required properties:
+- compatible: Should be "qcom,dp-display".
+- reg: Base address and length of DP hardware's memory mapped regions.
+- reg-names: A list of strings that name the list of regs. "dp_ctrl" - DP controller memory region.
+ "dp_phy" - DP PHY memory region.
+ "dp_ln_tx0" - USB3 DP PHY combo TX-0 lane memory region.
+ "dp_ln_tx1" - USB3 DP PHY combo TX-1 lane memory region.
+ "dp_mmss_cc" - Display Clock Control memory region.
+ "qfprom_physical" - QFPROM Phys memory region.
+ "dp_pll" - USB3 DP combo PLL memory region.
+ "hdcp_physical" - DP HDCP memory region.
+- cell-index: Specifies the controller instance.
+- clocks: Clocks required for Display Port operation.
+- clock-names: Names of the clocks corresponding to handles. Following clocks are required:
+ "core_aux_clk", "core_usb_ref_clk_src","core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+ "core_usb_pipe_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk",
+ "ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent".
+- gdsc-supply: phandle to gdsc regulator node.
+- vdda-1p2-supply: phandle to vdda 1.2V regulator node.
+- vdda-0p9-supply: phandle to vdda 0.9V regulator node.
+- interrupt-parent phandle to the interrupt parent device node.
+- interrupts: The interrupt signal from the DSI block.
+- qcom,aux-en-gpio: Specifies the aux-channel enable gpio.
+- qcom,aux-sel-gpio: Specifies the aux-channel select gpio.
+- qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio.
+- qcom,aux-cfg-settings: An array that specifies the DP AUX configuration settings.
+- qcom,max-pclk-frequency-khz: An integer specifying the max. pixel clock in KHz supported by Display Port.
+- qcom,dp-usbpd-detection: Phandle for the PMI regulator node for USB PHY PD detection.
+- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the a particular "type" of DSI module. The module "types"
+ can be "core", "ctrl", and "phy". Within the same type,
+ there can be more than one instance of this binding,
+ in which case the entry would be appended with the
+ supply entry index.
+ e.g. qcom,ctrl-supply-entry@0
+ -- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+ -- qcom,supply-min-voltage: minimum voltage level (uV)
+ -- qcom,supply-max-voltage: maximum voltage level (uV)
+ -- qcom,supply-enable-load: load drawn (uA) from enabled supply
+ -- qcom,supply-disable-load: load drawn (uA) from disabled supply
+ -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+ -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
+ -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
+ -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node
+ Refer to pinctrl-bindings.txt
+- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin
+ controller. These pin configurations are installed in the pinctrl
+ device node. Refer to pinctrl-bindings.txt
+
+Example:
+ sde_dp: qcom,dp_display@0{
+ cell-index = <0>;
+ compatible = "qcom,dp-display";
+
+ gdsc-supply = <&mdss_core_gdsc>;
+ vdda-1p2-supply = <&pm8998_l26>;
+ vdda-0p9-supply = <&pm8998_l1>;
+
+ reg = <0xae90000 0xa84>,
+ <0x88eaa00 0x200>,
+ <0x88ea200 0x200>,
+ <0x88ea600 0x200>,
+ <0xaf02000 0x1a0>,
+ <0x780000 0x621c>,
+ <0x88ea030 0x10>,
+ <0x88e8000 0x621c>,
+ <0x0aee1000 0x034>;
+ reg-names = "dp_ctrl", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+ "dp_mmss_cc", "qfprom_physical", "dp_pll",
+ "usb3_dp_com", "hdcp_physical";
+
+ interrupt-parent = <&mdss_mdp>;
+ interrupts = <12 0>;
+
+ clocks = <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+ <&clock_rpmh RPMH_CXO_CLK>,
+ <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
+ <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+ <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+ <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
+ <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
+ clock-names = "core_aux_clk", "core_usb_ref_clk_src",
+ "core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+ "core_usb_pipe_clk", "ctrl_link_clk",
+ "ctrl_link_iface_clk", "ctrl_crypto_clk",
+ "ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent";
+
+ qcom,dp-usbpd-detection = <&pmi8998_pdphy>;
+
+ qcom,aux-cfg-settings = [00 13 04 00 0a 26 0a 03 bb 03];
+ qcom,max-pclk-frequency-khz = <593470>;
+ pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
+ pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>;
+ pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>;
+ qcom,aux-en-gpio = <&tlmm 43 0>;
+ qcom,aux-sel-gpio = <&tlmm 51 0>;
+ qcom,usbplug-cc-gpio = <&tlmm 38 0>;
+ qcom,core-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,core-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda-1p2";
+ qcom,supply-min-voltage = <1200000>;
+ qcom,supply-max-voltage = <1200000>;
+ qcom,supply-enable-load = <21800>;
+ qcom,supply-disable-load = <4>;
+ };
+ };
+
+ qcom,phy-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,phy-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda-0p9";
+ qcom,supply-min-voltage = <880000>;
+ qcom,supply-max-voltage = <880000>;
+ qcom,supply-enable-load = <36000>;
+ qcom,supply-disable-load = <32>;
+ };
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/qbt1000/qbt1000.txt b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt
new file mode 100644
index 0000000..c9861e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt
@@ -0,0 +1,54 @@
+Qualcomm Technologies, Inc. QBT1000 Specific Bindings
+
+QBT is a fingerprint sensor ASIC capable of performing fingerprint image scans
+and detecting finger presence on the sensor using programmable firmware.
+
+=======================
+Required Node Structure
+=======================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: "qcom,qbt1000".
+
+- clock-names
+ Usage: required
+ Value type: <stringlist>
+ Definition: List of clock names that need to be voted on/off.
+
+- clocks
+ Usage: required
+ Value type: <prop_encoded-array>
+ Definition: Property pair that represents the clock controller and the clock
+ id. This in combination with the clock-name is used to obtain
+ the handle for the clock that needs to be voted on/off.
+
+- clock-frequency
+ Usage: required
+ Value type: <u32>
+ Definition: Frequency of clock in Hz.
+
+- qcom,ipc-gpio
+ Usage: required
+ Value type: <phandle>
+ Definition: phandle for GPIO to be used for IPC.
+
+- qcom,finger-detect-gpio
+ Usage: required
+ Value type: <phandle>
+ Definition: phandle for GPIO to be used for finger detect.
+
+=======
+Example
+=======
+
+qcom,qbt1000 {
+ compatible = "qcom,qbt1000";
+ clock-names = "core", "iface";
+ clocks = <&clock_gcc clk_gcc_blsp2_qup6_spi_apps_clk>,
+ <&clock_gcc clk_gcc_blsp2_ahb_clk>;
+ clock-frequency = <15000000>;
+ qcom,ipc-gpio = <&tlmm 121 0>;
+ qcom,finger-detect-gpio = <&pmcobalt_gpios 2 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 79ac3b1..7ae63af 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -425,10 +425,44 @@
2003 1675>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
- <100000 100000 100000>;
+ /* Speed bin 0 */
+ <100000 100000 100000>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ /* Speed bin 1 */
+ <100000 100000 100000>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
- <100000 100000 100000>;
+ /* Speed bin 0 */
+ <100000 100000 100000>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ /* Speed bin 1 */
+ <100000 100000 100000>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 6038b6e..6818615 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2486,6 +2486,14 @@
qcom,pipe-attr-ee;
};
+ qcom,qbt1000 {
+ compatible = "qcom,qbt1000";
+ clock-names = "core", "iface";
+ clock-frequency = <25000000>;
+ qcom,ipc-gpio = <&tlmm 121 0>;
+ qcom,finger-detect-gpio = <&pm8998_gpios 5 0>;
+ };
+
qcom_seecom: qseecom@86d00000 {
compatible = "qcom,qseecom";
reg = <0x86d00000 0x2200000>;
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 10b44f8..19a6db8 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -491,6 +491,7 @@
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_PM=y
+CONFIG_MSM_QBT1000=y
CONFIG_APSS_CORE_EA=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 737f47f..04a0d3e 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -513,6 +513,7 @@
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_PM=y
+CONFIG_MSM_QBT1000=y
CONFIG_APSS_CORE_EA=y
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index da845fd..fe8f94a 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -277,12 +277,10 @@
}
if (!user_mode(regs))
show_extra_register_data(regs, 64);
- printk("\n");
}
void show_regs(struct pt_regs * regs)
{
- printk("\n");
__show_regs(regs);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
new file mode 100644
index 0000000..722c436
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -0,0 +1,585 @@
+/*
+ * 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
+ * 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) "[drm-dp] %s: " fmt, __func__
+
+#include <linux/of_gpio.h>
+
+#include "dp_parser.h"
+
+static void dp_parser_unmap_io_resources(struct dp_parser *parser)
+{
+ struct dp_io *io = &parser->io;
+
+ if (&io->ctrl_io)
+ msm_dss_iounmap(&io->ctrl_io);
+ if (&io->phy_io)
+ msm_dss_iounmap(&io->phy_io);
+ if (&io->ln_tx0_io)
+ msm_dss_iounmap(&io->ln_tx0_io);
+ if (&io->ln_tx1_io)
+ msm_dss_iounmap(&io->ln_tx0_io);
+ if (&io->dp_pll_io)
+ msm_dss_iounmap(&io->dp_pll_io);
+ if (&io->dp_cc_io)
+ msm_dss_iounmap(&io->dp_cc_io);
+ if (&io->qfprom_io)
+ msm_dss_iounmap(&io->qfprom_io);
+ if (&io->hdcp_io)
+ msm_dss_iounmap(&io->hdcp_io);
+}
+
+static int dp_parser_ctrl_res(struct dp_parser *parser)
+{
+ int rc = 0;
+ u32 index;
+ struct platform_device *pdev = parser->pdev;
+ struct device_node *of_node = parser->pdev->dev.of_node;
+ struct dp_io *io = &parser->io;
+
+ rc = of_property_read_u32(of_node, "cell-index", &index);
+ if (rc) {
+ pr_err("cell-index not specified, rc=%d\n", rc);
+ goto err;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &io->ctrl_io, "dp_ctrl");
+ if (rc) {
+ pr_err("unable to remap dp io resources\n");
+ goto err;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &io->phy_io, "dp_phy");
+ if (rc) {
+ pr_err("unable to remap dp PHY resources\n");
+ goto err;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &io->ln_tx0_io, "dp_ln_tx0");
+ if (rc) {
+ pr_err("unable to remap dp TX0 resources\n");
+ goto err;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &io->ln_tx1_io, "dp_ln_tx1");
+ if (rc) {
+ pr_err("unable to remap dp TX1 resources\n");
+ goto err;
+ }
+
+ rc = msm_dss_ioremap_byname(pdev, &io->dp_pll_io, "dp_pll");
+ if (rc) {
+ pr_err("unable to remap DP PLL resources\n");
+ goto err;
+ }
+
+ if (msm_dss_ioremap_byname(pdev, &io->dp_cc_io, "dp_mmss_cc")) {
+ pr_err("unable to remap dp MMSS_CC resources\n");
+ goto err;
+ }
+
+ if (msm_dss_ioremap_byname(pdev, &io->qfprom_io, "qfprom_physical"))
+ pr_warn("unable to remap dp qfprom resources\n");
+
+ if (msm_dss_ioremap_byname(pdev, &io->hdcp_io, "hdcp_physical"))
+ pr_warn("unable to remap dp hdcp resources\n");
+
+ return 0;
+err:
+ dp_parser_unmap_io_resources(parser);
+ return rc;
+}
+
+static int dp_parser_aux(struct dp_parser *parser)
+{
+ int len = 0, i = 0, rc = 0;
+ struct device_node *of_node = parser->pdev->dev.of_node;
+ const char *data;
+
+ data = of_get_property(of_node, "qcom,aux-cfg-settings", &len);
+ if (!data || (len != AUX_CFG_LEN)) {
+ pr_err("Unable to read DP AUX CFG settings\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ for (i = 0; i < len; i++)
+ parser->aux_cfg[i] = data[i];
+end:
+ return rc;
+}
+
+static int dp_parser_misc(struct dp_parser *parser)
+{
+ int rc = 0;
+ struct device_node *of_node = parser->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node,
+ "qcom,max-pclk-frequency-khz", &parser->max_pclk_khz);
+ if (rc)
+ parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
+
+ return 0;
+}
+
+static int dp_parser_pinctrl(struct dp_parser *parser)
+{
+ int rc = 0;
+ struct dp_pinctrl *pinctrl = &parser->pinctrl;
+
+ pinctrl->pin = devm_pinctrl_get(&parser->pdev->dev);
+
+ if (IS_ERR_OR_NULL(pinctrl->pin)) {
+ rc = PTR_ERR(pinctrl->pin);
+ pr_err("failed to get pinctrl, rc=%d\n", rc);
+ goto error;
+ }
+
+ pinctrl->state_active = pinctrl_lookup_state(pinctrl->pin,
+ "mdss_dp_active");
+ if (IS_ERR_OR_NULL(pinctrl->state_active)) {
+ rc = PTR_ERR(pinctrl->state_active);
+ pr_err("failed to get pinctrl active state, rc=%d\n", rc);
+ goto error;
+ }
+
+ pinctrl->state_suspend = pinctrl_lookup_state(pinctrl->pin,
+ "mdss_dp_sleep");
+ if (IS_ERR_OR_NULL(pinctrl->state_suspend)) {
+ rc = PTR_ERR(pinctrl->state_suspend);
+ pr_err("failed to get pinctrl suspend state, rc=%d\n", rc);
+ goto error;
+ }
+error:
+ return rc;
+}
+
+static int dp_parser_gpio(struct dp_parser *parser)
+{
+ int i = 0;
+ struct device *dev = &parser->pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
+ static const char * const dp_gpios[] = {
+ "qcom,aux-en-gpio",
+ "qcom,aux-sel-gpio",
+ "qcom,usbplug-cc-gpio",
+ };
+
+ mp->gpio_config = devm_kzalloc(dev,
+ sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
+ mp->num_gpio = ARRAY_SIZE(dp_gpios);
+
+ for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) {
+ mp->gpio_config[i].gpio = of_get_named_gpio(of_node,
+ dp_gpios[i], 0);
+
+ if (!gpio_is_valid(mp->gpio_config[i].gpio)) {
+ pr_err("%s gpio not specified\n", dp_gpios[i]);
+ return -EINVAL;
+ }
+
+ strlcpy(mp->gpio_config[i].gpio_name, dp_gpios[i],
+ sizeof(mp->gpio_config[i].gpio_name));
+
+ mp->gpio_config[i].value = 0;
+ }
+
+ return 0;
+}
+
+static const char *dp_parser_supply_node_name(enum dp_pm_type module)
+{
+ switch (module) {
+ case DP_CORE_PM: return "qcom,core-supply-entries";
+ case DP_CTRL_PM: return "qcom,ctrl-supply-entries";
+ case DP_PHY_PM: return "qcom,phy-supply-entries";
+ default: return "???";
+ }
+}
+
+static int dp_parser_get_vreg(struct dp_parser *parser,
+ enum dp_pm_type module)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ const char *pm_supply_name = NULL;
+ struct device_node *supply_node = NULL;
+ struct device_node *of_node = parser->pdev->dev.of_node;
+ struct device_node *supply_root_node = NULL;
+ struct dss_module_power *mp = &parser->mp[module];
+
+ mp->num_vreg = 0;
+ pm_supply_name = dp_parser_supply_node_name(module);
+ supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
+ if (!supply_root_node) {
+ pr_err("no supply entry present: %s\n", pm_supply_name);
+ goto novreg;
+ }
+
+ mp->num_vreg = of_get_available_child_count(supply_root_node);
+
+ if (mp->num_vreg == 0) {
+ pr_debug("no vreg\n");
+ goto novreg;
+ } else {
+ pr_debug("vreg found. count=%d\n", mp->num_vreg);
+ }
+
+ mp->vreg_config = devm_kzalloc(&parser->pdev->dev,
+ sizeof(struct dss_vreg) * mp->num_vreg, GFP_KERNEL);
+ if (!mp->vreg_config) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node) {
+ const char *st = NULL;
+ /* vreg-name */
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("error reading name. rc=%d\n",
+ rc);
+ goto error;
+ }
+ snprintf(mp->vreg_config[i].vreg_name,
+ ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
+ /* vreg-min-voltage */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err("error reading min volt. rc=%d\n",
+ rc);
+ goto error;
+ }
+ mp->vreg_config[i].min_voltage = tmp;
+
+ /* vreg-max-voltage */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err("error reading max volt. rc=%d\n",
+ rc);
+ goto error;
+ }
+ mp->vreg_config[i].max_voltage = tmp;
+
+ /* enable-load */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-enable-load", &tmp);
+ if (rc) {
+ pr_err("error reading enable load. rc=%d\n",
+ rc);
+ goto error;
+ }
+ mp->vreg_config[i].enable_load = tmp;
+
+ /* disable-load */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-disable-load", &tmp);
+ if (rc) {
+ pr_err("error reading disable load. rc=%d\n",
+ rc);
+ goto error;
+ }
+ mp->vreg_config[i].disable_load = tmp;
+
+ pr_debug("%s min=%d, max=%d, enable=%d, disable=%d\n",
+ mp->vreg_config[i].vreg_name,
+ mp->vreg_config[i].min_voltage,
+ mp->vreg_config[i].max_voltage,
+ mp->vreg_config[i].enable_load,
+ mp->vreg_config[i].disable_load
+ );
+ ++i;
+ }
+
+ return rc;
+
+error:
+ if (mp->vreg_config) {
+ devm_kfree(&parser->pdev->dev, mp->vreg_config);
+ mp->vreg_config = NULL;
+ }
+novreg:
+ mp->num_vreg = 0;
+
+ return rc;
+}
+
+static void dp_parser_put_vreg_data(struct device *dev,
+ struct dss_module_power *mp)
+{
+ if (!mp) {
+ DEV_ERR("invalid input\n");
+ return;
+ }
+
+ if (mp->vreg_config) {
+ devm_kfree(dev, mp->vreg_config);
+ mp->vreg_config = NULL;
+ }
+ mp->num_vreg = 0;
+}
+
+static int dp_parser_regulator(struct dp_parser *parser)
+{
+ int i, rc = 0;
+ struct platform_device *pdev = parser->pdev;
+
+ /* Parse the regulator information */
+ for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+ rc = dp_parser_get_vreg(parser, i);
+ if (rc) {
+ pr_err("get_dt_vreg_data failed for %s. rc=%d\n",
+ dp_parser_pm_name(i), rc);
+ i--;
+ for (; i >= DP_CORE_PM; i--)
+ dp_parser_put_vreg_data(&pdev->dev,
+ &parser->mp[i]);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static bool dp_parser_check_prefix(const char *clk_prefix, const char *clk_name)
+{
+ return !!strnstr(clk_name, clk_prefix, strlen(clk_name));
+}
+
+static void dp_parser_put_clk_data(struct device *dev,
+ struct dss_module_power *mp)
+{
+ if (!mp) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (mp->clk_config) {
+ devm_kfree(dev, mp->clk_config);
+ mp->clk_config = NULL;
+ }
+
+ mp->num_clk = 0;
+}
+
+static int dp_parser_init_clk_data(struct dp_parser *parser)
+{
+ int num_clk = 0, i = 0, rc = 0;
+ int core_clk_count = 0, ctrl_clk_count = 0;
+ const char *core_clk = "core";
+ const char *ctrl_clk = "ctrl";
+ const char *clk_name;
+ struct device *dev = &parser->pdev->dev;
+ struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
+ struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
+
+ num_clk = of_property_count_strings(dev->of_node, "clock-names");
+ if (num_clk <= 0) {
+ pr_err("no clocks are defined\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ for (i = 0; i < num_clk; i++) {
+ of_property_read_string_index(dev->of_node,
+ "clock-names", i, &clk_name);
+
+ if (dp_parser_check_prefix(core_clk, clk_name))
+ core_clk_count++;
+
+ if (dp_parser_check_prefix(ctrl_clk, clk_name))
+ ctrl_clk_count++;
+ }
+
+ /* Initialize the CORE power module */
+ if (core_clk_count <= 0) {
+ pr_err("no core clocks are defined\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ core_power->num_clk = core_clk_count;
+ core_power->clk_config = devm_kzalloc(dev,
+ sizeof(struct dss_clk) * core_power->num_clk,
+ GFP_KERNEL);
+ if (!core_power->clk_config) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Initialize the CTRL power module */
+ if (ctrl_clk_count <= 0) {
+ pr_err("no ctrl clocks are defined\n");
+ rc = -EINVAL;
+ goto ctrl_clock_error;
+ }
+
+ ctrl_power->num_clk = ctrl_clk_count;
+ ctrl_power->clk_config = devm_kzalloc(dev,
+ sizeof(struct dss_clk) * ctrl_power->num_clk,
+ GFP_KERNEL);
+ if (!ctrl_power->clk_config) {
+ ctrl_power->num_clk = 0;
+ rc = -EINVAL;
+ goto ctrl_clock_error;
+ }
+
+ return rc;
+
+ctrl_clock_error:
+ dp_parser_put_clk_data(dev, core_power);
+exit:
+ return rc;
+}
+
+static int dp_parser_clock(struct dp_parser *parser)
+{
+ int rc = 0, i = 0;
+ int num_clk = 0;
+ int core_clk_index = 0, ctrl_clk_index = 0;
+ int core_clk_count = 0, ctrl_clk_count = 0;
+ const char *clk_name;
+ const char *core_clk = "core";
+ const char *ctrl_clk = "ctrl";
+ struct device *dev = &parser->pdev->dev;
+ struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
+ struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
+
+ core_power = &parser->mp[DP_CORE_PM];
+ ctrl_power = &parser->mp[DP_CTRL_PM];
+
+ rc = dp_parser_init_clk_data(parser);
+ if (rc) {
+ pr_err("failed to initialize power data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ core_clk_count = core_power->num_clk;
+ ctrl_clk_count = ctrl_power->num_clk;
+
+ num_clk = core_clk_count + ctrl_clk_count;
+
+ for (i = 0; i < num_clk; i++) {
+ of_property_read_string_index(dev->of_node, "clock-names",
+ i, &clk_name);
+
+ if (dp_parser_check_prefix(core_clk, clk_name) &&
+ core_clk_index < core_clk_count) {
+ struct dss_clk *clk =
+ &core_power->clk_config[core_clk_index];
+ strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ clk->type = DSS_CLK_AHB;
+ core_clk_index++;
+ } else if (dp_parser_check_prefix(ctrl_clk, clk_name) &&
+ ctrl_clk_index < ctrl_clk_count) {
+ struct dss_clk *clk =
+ &ctrl_power->clk_config[ctrl_clk_index];
+ strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ ctrl_clk_index++;
+
+ if (!strcmp(clk_name, "ctrl_link_clk") ||
+ !strcmp(clk_name, "ctrl_pixel_clk") ||
+ !strcmp(clk_name, "ctrl_crypto_clk"))
+ clk->type = DSS_CLK_PCLK;
+ else
+ clk->type = DSS_CLK_AHB;
+ }
+ }
+
+ pr_debug("clock parsing successful\n");
+
+exit:
+ return rc;
+}
+
+static int dp_parser_parse(struct dp_parser *parser)
+{
+ int rc = 0;
+
+ if (!parser) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ rc = dp_parser_ctrl_res(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_aux(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_misc(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_clock(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_regulator(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_gpio(parser);
+ if (rc)
+ goto err;
+
+ rc = dp_parser_pinctrl(parser);
+err:
+ return rc;
+}
+
+struct dp_parser *dp_parser_get(struct platform_device *pdev)
+{
+ struct dp_parser *parser;
+
+ parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return ERR_PTR(-ENOMEM);
+
+ parser->parse = dp_parser_parse;
+ parser->pdev = pdev;
+
+ return parser;
+}
+
+void dp_parser_put(struct dp_parser *parser)
+{
+ int i = 0;
+ struct dss_module_power *power = NULL;
+
+ if (!parser) {
+ pr_err("invalid parser module\n");
+ return;
+ }
+
+ power = parser->mp;
+
+ for (i = 0; i < DP_MAX_PM; i++) {
+ struct dss_module_power *mp = &power[i];
+
+ devm_kfree(&parser->pdev->dev, mp->clk_config);
+ devm_kfree(&parser->pdev->dev, mp->vreg_config);
+ devm_kfree(&parser->pdev->dev, mp->gpio_config);
+ }
+
+ devm_kfree(&parser->pdev->dev, parser);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
new file mode 100644
index 0000000..fdcdd3a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -0,0 +1,139 @@
+/*
+ * 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
+ * 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 _DP_PARSER_H_
+#define _DP_PARSER_H_
+
+#include <linux/sde_io_util.h>
+
+#define DP_LABEL "MDSS DP DISPLAY"
+#define AUX_CFG_LEN 10
+#define DP_MAX_PIXEL_CLK_KHZ 675000
+
+enum dp_pm_type {
+ DP_CORE_PM,
+ DP_CTRL_PM,
+ DP_PHY_PM,
+ DP_MAX_PM
+};
+
+static inline const char *dp_parser_pm_name(enum dp_pm_type module)
+{
+ switch (module) {
+ case DP_CORE_PM: return "DP_CORE_PM";
+ case DP_CTRL_PM: return "DP_CTRL_PM";
+ case DP_PHY_PM: return "DP_PHY_PM";
+ default: return "???";
+ }
+}
+
+/**
+ * struct dp_display_data - display related device tree data.
+ *
+ * @ctrl_node: referece to controller device
+ * @phy_node: reference to phy device
+ * @is_active: is the controller currently active
+ * @name: name of the display
+ * @display_type: type of the display
+ */
+struct dp_display_data {
+ struct device_node *ctrl_node;
+ struct device_node *phy_node;
+ bool is_active;
+ const char *name;
+ const char *display_type;
+};
+
+/**
+ * struct dp_ctrl_resource - controller's IO related data
+ *
+ * @ctrl_io: controller's mapped memory address
+ * @phy_io: phy's mapped memory address
+ * @ln_tx0_io: USB-DP lane TX0's mapped memory address
+ * @ln_tx1_io: USB-DP lane TX1's mapped memory address
+ * @dp_cc_io: DP cc's mapped memory address
+ * @qfprom_io: qfprom's mapped memory address
+ * @dp_pll_io: DP PLL mapped memory address
+ * @hdcp_io: hdcp's mapped memory address
+ */
+struct dp_io {
+ struct dss_io_data ctrl_io;
+ struct dss_io_data phy_io;
+ struct dss_io_data ln_tx0_io;
+ struct dss_io_data ln_tx1_io;
+ struct dss_io_data dp_cc_io;
+ struct dss_io_data qfprom_io;
+ struct dss_io_data dp_pll_io;
+ struct dss_io_data hdcp_io;
+};
+
+/**
+ * struct dp_pinctrl - DP's pin control
+ *
+ * @pin: pin-controller's instance
+ * @state_active: active state pin control
+ * @state_hpd_active: hpd active state pin control
+ * @state_suspend: suspend state pin control
+ */
+struct dp_pinctrl {
+ struct pinctrl *pin;
+ struct pinctrl_state *state_active;
+ struct pinctrl_state *state_hpd_active;
+ struct pinctrl_state *state_suspend;
+};
+
+/**
+ * struct dp_parser - DP parser's data exposed to clients
+ *
+ * @pdev: platform data of the client
+ * @mp: gpio, regulator and clock related data
+ * @pinctrl: pin-control related data
+ * @ctrl_resouce: controller's register address realated data
+ * @disp_data: controller's display related data
+ * @parse: function to be called by client to parse device tree.
+ */
+struct dp_parser {
+ struct platform_device *pdev;
+ struct dss_module_power mp[DP_MAX_PM];
+ struct dp_pinctrl pinctrl;
+ struct dp_io io;
+ struct dp_display_data disp_data;
+
+ u8 l_map[4];
+ u32 aux_cfg[AUX_CFG_LEN];
+ u32 max_pclk_khz;
+
+ int (*parse)(struct dp_parser *parser);
+};
+
+/**
+ * dp_parser_get() - get the DP's device tree parser module
+ *
+ * @pdev: platform data of the client
+ * return: pointer to dp_parser structure.
+ *
+ * This function provides client capability to parse the
+ * device tree and populate the data structures. The data
+ * related to clock, regulators, pin-control and other
+ * can be parsed using this module.
+ */
+struct dp_parser *dp_parser_get(struct platform_device *pdev);
+
+/**
+ * dp_parser_put() - cleans the dp_parser module
+ *
+ * @parser: pointer to the parser's data.
+ */
+void dp_parser_put(struct dp_parser *parser);
+#endif
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
new file mode 100644
index 0000000..54d4a10
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.c
@@ -0,0 +1,531 @@
+/*
+ * 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
+ * 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) "[drm-dp] %s: " fmt, __func__
+
+#include <linux/clk.h>
+#include "dp_power.h"
+
+struct dp_power_private {
+ struct dp_parser *parser;
+ struct platform_device *pdev;
+ struct clk *pixel_clk_rcg;
+ struct clk *pixel_parent;
+
+ struct dp_power dp_power;
+
+ bool core_clks_on;
+ bool link_clks_on;
+};
+
+static int dp_power_regulator_init(struct dp_power_private *power)
+{
+ int rc = 0, i = 0, j = 0;
+ struct platform_device *pdev;
+ struct dp_parser *parser;
+
+ parser = power->parser;
+ pdev = power->pdev;
+
+ for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) {
+ rc = msm_dss_config_vreg(&pdev->dev,
+ parser->mp[i].vreg_config,
+ parser->mp[i].num_vreg, 1);
+ if (rc) {
+ pr_err("failed to init vregs for %s\n",
+ dp_parser_pm_name(i));
+ for (j = i - 1; j >= DP_CORE_PM; j--) {
+ msm_dss_config_vreg(&pdev->dev,
+ parser->mp[j].vreg_config,
+ parser->mp[j].num_vreg, 0);
+ }
+
+ goto error;
+ }
+ }
+error:
+ return rc;
+}
+
+static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
+{
+ int rc = 0, i = 0, j = 0;
+ struct dp_parser *parser;
+
+ parser = power->parser;
+
+ for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+ rc = msm_dss_enable_vreg(
+ parser->mp[i].vreg_config,
+ parser->mp[i].num_vreg, enable);
+ if (rc) {
+ pr_err("failed to '%s' vregs for %s\n",
+ enable ? "enable" : "disable",
+ dp_parser_pm_name(i));
+ if (enable) {
+ for (j = i-1; j >= DP_CORE_PM; j--) {
+ msm_dss_enable_vreg(
+ parser->mp[j].vreg_config,
+ parser->mp[j].num_vreg, 0);
+ }
+ }
+ goto error;
+ }
+ }
+error:
+ return rc;
+}
+
+static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
+{
+ int rc = -EFAULT;
+ struct pinctrl_state *pin_state;
+ struct dp_parser *parser;
+
+ parser = power->parser;
+
+ if (IS_ERR_OR_NULL(parser->pinctrl.pin))
+ return PTR_ERR(parser->pinctrl.pin);
+
+ pin_state = active ? parser->pinctrl.state_active
+ : parser->pinctrl.state_suspend;
+ if (!IS_ERR_OR_NULL(pin_state)) {
+ rc = pinctrl_select_state(parser->pinctrl.pin,
+ pin_state);
+ if (rc)
+ pr_err("can not set %s pins\n",
+ active ? "dp_active"
+ : "dp_sleep");
+ } else {
+ pr_err("invalid '%s' pinstate\n",
+ active ? "dp_active"
+ : "dp_sleep");
+ }
+
+ return rc;
+}
+
+static int dp_power_clk_init(struct dp_power_private *power, bool enable)
+{
+ int rc = 0;
+ struct dss_module_power *core, *ctrl;
+ struct device *dev;
+
+ core = &power->parser->mp[DP_CORE_PM];
+ ctrl = &power->parser->mp[DP_CTRL_PM];
+
+ dev = &power->pdev->dev;
+
+ if (!core || !ctrl) {
+ pr_err("invalid power_data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (enable) {
+ rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
+ if (rc) {
+ pr_err("failed to get %s clk. err=%d\n",
+ dp_parser_pm_name(DP_CORE_PM), rc);
+ goto exit;
+ }
+
+ rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk);
+ if (rc) {
+ pr_err("failed to get %s clk. err=%d\n",
+ dp_parser_pm_name(DP_CTRL_PM), rc);
+ goto ctrl_get_error;
+ }
+
+ power->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg");
+ if (IS_ERR(power->pixel_clk_rcg)) {
+ pr_debug("Unable to get DP pixel clk RCG\n");
+ power->pixel_clk_rcg = NULL;
+ }
+
+ power->pixel_parent = devm_clk_get(dev, "pixel_parent");
+ if (IS_ERR(power->pixel_parent)) {
+ pr_debug("Unable to get DP pixel RCG parent\n");
+ power->pixel_parent = NULL;
+ }
+ } else {
+ if (power->pixel_parent)
+ devm_clk_put(dev, power->pixel_parent);
+
+ if (power->pixel_clk_rcg)
+ devm_clk_put(dev, power->pixel_clk_rcg);
+
+ msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk);
+ msm_dss_put_clk(core->clk_config, core->num_clk);
+ }
+
+ return rc;
+
+ctrl_get_error:
+ msm_dss_put_clk(core->clk_config, core->num_clk);
+exit:
+ return rc;
+}
+
+static int dp_power_clk_set_rate(struct dp_power_private *power,
+ enum dp_pm_type module, bool enable)
+{
+ int rc = 0;
+ struct dss_module_power *mp;
+
+ if (!power) {
+ pr_err("invalid power data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ mp = &power->parser->mp[module];
+
+ if (enable) {
+ rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+ if (rc) {
+ pr_err("failed to set clks rate.\n");
+ goto exit;
+ }
+
+ rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 1);
+ if (rc) {
+ pr_err("failed to enable clks\n");
+ goto exit;
+ }
+ } else {
+ rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 0);
+ if (rc) {
+ pr_err("failed to disable clks\n");
+ goto exit;
+ }
+ }
+exit:
+ return rc;
+}
+
+static int dp_power_clk_enable(struct dp_power *dp_power,
+ enum dp_pm_type pm_type, bool enable)
+{
+ int rc = 0;
+ struct dss_module_power *mp;
+ struct dp_power_private *power;
+
+ if (!dp_power) {
+ pr_err("invalid power data\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ mp = &power->parser->mp[pm_type];
+
+ if ((pm_type != DP_CORE_PM) && (pm_type != DP_CTRL_PM)) {
+ pr_err("unsupported power module: %s\n",
+ dp_parser_pm_name(pm_type));
+ return -EINVAL;
+ }
+
+ if (enable) {
+ if ((pm_type == DP_CORE_PM)
+ && (power->core_clks_on)) {
+ pr_debug("core clks already enabled\n");
+ return 0;
+ }
+
+ if ((pm_type == DP_CTRL_PM)
+ && (power->link_clks_on)) {
+ pr_debug("links clks already enabled\n");
+ return 0;
+ }
+
+ if ((pm_type == DP_CTRL_PM) && (!power->core_clks_on)) {
+ pr_debug("Need to enable core clks before link clks\n");
+
+ rc = dp_power_clk_set_rate(power, pm_type, enable);
+ if (rc) {
+ pr_err("failed to enable clks: %s. err=%d\n",
+ dp_parser_pm_name(DP_CORE_PM), rc);
+ goto error;
+ } else {
+ power->core_clks_on = true;
+ }
+ }
+ }
+
+ rc = dp_power_clk_set_rate(power, pm_type, enable);
+ if (rc) {
+ pr_err("failed to '%s' clks for: %s. err=%d\n",
+ enable ? "enable" : "disable",
+ dp_parser_pm_name(pm_type), rc);
+ goto error;
+ }
+
+ if (pm_type == DP_CORE_PM)
+ power->core_clks_on = enable;
+ else
+ power->link_clks_on = enable;
+
+ pr_debug("%s clocks for %s\n",
+ enable ? "enable" : "disable",
+ dp_parser_pm_name(pm_type));
+ pr_debug("link_clks:%s core_clks:%s\n",
+ power->link_clks_on ? "on" : "off",
+ power->core_clks_on ? "on" : "off");
+error:
+ return rc;
+}
+
+static int dp_power_request_gpios(struct dp_power_private *power)
+{
+ int rc = 0, i;
+ struct device *dev;
+ struct dss_module_power *mp;
+ static const char * const gpio_names[] = {
+ "aux_enable", "aux_sel", "usbplug_cc",
+ };
+
+ if (!power) {
+ pr_err("invalid power data\n");
+ return -EINVAL;
+ }
+
+ dev = &power->pdev->dev;
+ mp = &power->parser->mp[DP_CORE_PM];
+
+ for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
+ unsigned int gpio = mp->gpio_config[i].gpio;
+
+ if (gpio_is_valid(gpio)) {
+ rc = devm_gpio_request(dev, gpio, gpio_names[i]);
+ if (rc) {
+ pr_err("request %s gpio failed, rc=%d\n",
+ gpio_names[i], rc);
+ goto error;
+ }
+ }
+ }
+ return 0;
+error:
+ for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
+ unsigned int gpio = mp->gpio_config[i].gpio;
+
+ if (gpio_is_valid(gpio))
+ gpio_free(gpio);
+ }
+ return rc;
+}
+
+static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
+{
+ return !!strnstr(gpio1, gpio2, strlen(gpio1));
+}
+
+static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
+{
+ int i;
+ struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
+ struct dss_gpio *config = mp->gpio_config;
+
+ for (i = 0; i < mp->num_gpio; i++) {
+ if (dp_power_find_gpio(config->gpio_name, "aux-sel"))
+ config->value = flip;
+
+ if (gpio_is_valid(config->gpio)) {
+ pr_debug("gpio %s, value %d\n", config->gpio_name,
+ config->value);
+
+ if (dp_power_find_gpio(config->gpio_name, "aux-en") ||
+ dp_power_find_gpio(config->gpio_name, "aux-sel"))
+ gpio_direction_output(config->gpio,
+ config->value);
+ else
+ gpio_set_value(config->gpio, config->value);
+
+ }
+ config++;
+ }
+}
+
+static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
+ bool enable)
+{
+ int rc = 0, i;
+ struct dss_module_power *mp;
+ struct dss_gpio *config;
+
+ mp = &power->parser->mp[DP_CORE_PM];
+ config = mp->gpio_config;
+
+ if (enable) {
+ rc = dp_power_request_gpios(power);
+ if (rc) {
+ pr_err("gpio request failed\n");
+ return rc;
+ }
+
+ dp_power_set_gpio(power, flip);
+ } else {
+ for (i = 0; i < mp->num_gpio; i++) {
+ gpio_set_value(config[i].gpio, 0);
+ gpio_free(config[i].gpio);
+ }
+ }
+
+ return 0;
+}
+
+static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
+{
+ int rc = 0;
+ struct dp_power_private *power;
+
+ if (!dp_power) {
+ pr_err("invalid power data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ if (power->pixel_clk_rcg && power->pixel_parent)
+ clk_set_parent(power->pixel_clk_rcg, power->pixel_parent);
+exit:
+ return rc;
+}
+
+static int dp_power_init(struct dp_power *dp_power, bool flip)
+{
+ int rc = 0;
+ struct dp_power_private *power;
+
+ if (!dp_power) {
+ pr_err("invalid power data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ rc = dp_power_regulator_ctrl(power, true);
+ if (rc) {
+ pr_err("failed to enable regulators\n");
+ goto exit;
+ }
+
+ rc = dp_power_pinctrl_set(power, true);
+ if (rc) {
+ pr_err("failed to set pinctrl state\n");
+ goto err_pinctrl;
+ }
+
+ rc = dp_power_config_gpios(power, flip, true);
+ if (rc) {
+ pr_err("failed to enable gpios\n");
+ goto err_gpio;
+ }
+
+ rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
+ if (rc) {
+ pr_err("failed to enable DP core clocks\n");
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ dp_power_config_gpios(power, flip, false);
+err_gpio:
+ dp_power_pinctrl_set(power, false);
+err_pinctrl:
+ dp_power_regulator_ctrl(power, false);
+exit:
+ return rc;
+}
+
+static int dp_power_deinit(struct dp_power *dp_power)
+{
+ int rc = 0;
+ struct dp_power_private *power;
+
+ if (!dp_power) {
+ pr_err("invalid power data\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ dp_power_clk_enable(dp_power, DP_CORE_PM, false);
+ dp_power_config_gpios(power, false, false);
+ dp_power_pinctrl_set(power, false);
+ dp_power_regulator_ctrl(power, false);
+exit:
+ return rc;
+}
+
+struct dp_power *dp_power_get(struct dp_parser *parser)
+{
+ int rc = 0;
+ struct dp_power_private *power;
+ struct dp_power *dp_power;
+
+ if (!parser) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
+ if (!power) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ power->parser = parser;
+ power->pdev = parser->pdev;
+
+ dp_power = &power->dp_power;
+
+ dp_power->init = dp_power_init;
+ dp_power->deinit = dp_power_deinit;
+ dp_power->clk_enable = dp_power_clk_enable;
+ dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent;
+
+ rc = dp_power_regulator_init(power);
+ if (rc) {
+ pr_err("failed to init regulators\n");
+ goto error;
+ }
+
+ rc = dp_power_clk_init(power, true);
+ if (rc) {
+ pr_err("failed to init clocks\n");
+ goto error;
+ }
+
+ return dp_power;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_power_put(struct dp_power *dp_power)
+{
+ struct dp_power_private *power = container_of(dp_power,
+ struct dp_power_private, dp_power);
+
+ (void)dp_power_clk_init(power, false);
+ devm_kfree(&power->pdev->dev, power);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_power.h b/drivers/gpu/drm/msm/dp/dp_power.h
new file mode 100644
index 0000000..3d71695
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.h
@@ -0,0 +1,54 @@
+/*
+ * 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
+ * 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 _DP_POWER_H_
+#define _DP_POWER_H_
+
+#include "dp_parser.h"
+
+/**
+ * sruct dp_power - DisplayPort's power related data
+ *
+ * @init: initializes the regulators/core clocks/GPIOs/pinctrl
+ * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
+ * @clk_enable: enable/disable the DP clocks
+ * @set_pixel_clk_parent: set the parent of DP pixel clock
+ */
+struct dp_power {
+ int (*init)(struct dp_power *power, bool flip);
+ int (*deinit)(struct dp_power *power);
+ int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
+ bool enable);
+ int (*set_pixel_clk_parent)(struct dp_power *power);
+};
+
+/**
+ * dp_power_get() - configure and get the DisplayPort power module data
+ *
+ * @parser: instance of parser module
+ * return: pointer to allocated power module data
+ *
+ * This API will configure the DisplayPort's power module and provides
+ * methods to be called by the client to configure the power related
+ * modueles.
+ */
+struct dp_power *dp_power_get(struct dp_parser *parser);
+
+/**
+ * dp_power_put() - release the power related resources
+ *
+ * @power: pointer to the power module's data
+ */
+void dp_power_put(struct dp_power *power);
+#endif /* _DP_POWER_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index dcf3c08..a3a3d28 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -3054,6 +3054,11 @@
state->crtc_h);
seq_printf(s, "\tmultirect: mode: %d index: %d\n",
pstate->multirect_mode, pstate->multirect_index);
+
+ seq_printf(s, "\texcl_rect: x:%4d y:%4d w:%4d h:%4d\n",
+ pstate->excl_rect.x, pstate->excl_rect.y,
+ pstate->excl_rect.w, pstate->excl_rect.h);
+
seq_puts(s, "\n");
}
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 3a6de75..ad207d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2447,7 +2447,7 @@
pstate->excl_rect.h != old_pstate->excl_rect.h ||
pstate->excl_rect.x != old_pstate->excl_rect.x ||
pstate->excl_rect.y != old_pstate->excl_rect.y) {
- SDE_DEBUG_PLANE(psde, "excl rect updated\n");
+ SDE_DEBUG_PLANE(psde, "excl_rect updated\n");
pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
}
@@ -2660,6 +2660,9 @@
(char *)&fmt->base.pixel_format);
ret = -EINVAL;
}
+ SDE_DEBUG_PLANE(psde, "excl_rect: {%d,%d,%d,%d}\n",
+ pstate->excl_rect.x, pstate->excl_rect.y,
+ pstate->excl_rect.w, pstate->excl_rect.h);
}
modeset_update:
@@ -3480,20 +3483,24 @@
}
if (!usr_ptr) {
- SDE_DEBUG_PLANE(psde, "excl rect data removed\n");
+ SDE_DEBUG_PLANE(psde, "invalid excl_rect user data\n");
return;
}
if (copy_from_user(&excl_rect_v1, usr_ptr, sizeof(excl_rect_v1))) {
- SDE_ERROR_PLANE(psde, "failed to copy excl rect data\n");
+ SDE_ERROR_PLANE(psde, "failed to copy excl_rect data\n");
return;
}
/* populate from user space */
pstate->excl_rect.x = excl_rect_v1.x1;
pstate->excl_rect.y = excl_rect_v1.y1;
- pstate->excl_rect.w = excl_rect_v1.x2 - excl_rect_v1.x1 + 1;
- pstate->excl_rect.h = excl_rect_v1.y2 - excl_rect_v1.y1 + 1;
+ pstate->excl_rect.w = excl_rect_v1.x2 - excl_rect_v1.x1;
+ pstate->excl_rect.h = excl_rect_v1.y2 - excl_rect_v1.y1;
+
+ SDE_DEBUG_PLANE(psde, "excl_rect: {%d,%d,%d,%d}\n",
+ pstate->excl_rect.x, pstate->excl_rect.y,
+ pstate->excl_rect.w, pstate->excl_rect.h);
}
static int sde_plane_atomic_set_property(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index da68139..38bf21f 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -16,9 +16,9 @@
#define MAX_CLIENT_NAME_LEN 128
-#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA 2000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA 6000000000
#define SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA 0
-#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA 2000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA 6000000000
#define SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA 0
#include <linux/sde_io_util.h>
diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c
index 50710cd..cab7e0f 100644
--- a/drivers/gpu/drm/msm/sde_rsc.c
+++ b/drivers/gpu/drm/msm/sde_rsc.c
@@ -442,7 +442,7 @@
return rc;
}
-static bool sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc)
+static int sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc)
{
struct sde_rsc_client *client;
int rc = STATE_UPDATE_NOT_ALLOWED;
@@ -467,7 +467,7 @@
return rc;
}
-static bool sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
+static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
struct sde_rsc_cmd_config *config,
struct sde_rsc_client *caller_client)
{
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 697381b..e157e7b 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -952,7 +952,7 @@
int ret = 0;
if (!kgsl_gmu_isenabled(device))
- return -ENODEV;
+ return 0;
kgsl_gmu_regwrite(device, A6XX_GMU_HOST2GMU_INTR_SET, set_mask);
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index 63dbde0..17ee6e6 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -640,6 +640,9 @@
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
int i, j;
+ if (!device->snapshot_legacy)
+ return 0;
+
if (remain < sizeof(*header)) {
SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
return 0;
@@ -748,6 +751,9 @@
unsigned int read_sel;
int i, j;
+ if (!device->snapshot_legacy)
+ return 0;
+
/* Figure out how many registers we are going to dump */
for (i = 0; i < regs->num_sets; i++) {
int start = regs->regs[i * 2];
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 876b668..be379e3 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -291,6 +291,8 @@
/* Use CP Crash dumper to get GPU snapshot*/
bool snapshot_crashdumper;
+ /* Use HOST side register reads to get GPU snapshot*/
+ bool snapshot_legacy;
struct kobject snapshot_kobj;
diff --git a/drivers/gpu/msm/kgsl_hfi.c b/drivers/gpu/msm/kgsl_hfi.c
index 30e1d7c..b05e18d 100644
--- a/drivers/gpu/msm/kgsl_hfi.c
+++ b/drivers/gpu/msm/kgsl_hfi.c
@@ -573,44 +573,41 @@
if (result)
return result;
- if (boot_state == GMU_COLD_BOOT) {
- major = adreno_dev->gpucore->gpmu_major;
- minor = adreno_dev->gpucore->gpmu_minor;
+ major = adreno_dev->gpucore->gpmu_major;
+ minor = adreno_dev->gpucore->gpmu_minor;
+ result = hfi_get_fw_version(gmu,
+ FW_VERSION(major, minor), &ver);
+ if (result)
+ dev_err(dev, "Failed to get FW version via HFI\n");
- result = hfi_get_fw_version(gmu,
- FW_VERSION(major, minor), &ver);
- if (result)
- dev_err(dev, "Failed to get FW version via HFI\n");
+ gmu->ver = ver;
+ if (major != FW_VER_MAJOR(ver))
+ dev_err(dev, "FW version major %d error (expect %d)\n",
+ FW_VER_MAJOR(ver),
+ adreno_dev->gpucore->gpmu_major);
- gmu->ver = ver;
- if (major != FW_VER_MAJOR(ver))
- dev_err(dev, "FW version major %d error (expect %d)\n",
- FW_VER_MAJOR(ver),
- adreno_dev->gpucore->gpmu_major);
+ if (minor > FW_VER_MINOR(ver))
+ dev_err(dev, "FW version minor %d error (expect %d)\n",
+ FW_VER_MINOR(ver),
+ adreno_dev->gpucore->gpmu_minor);
- if (minor > FW_VER_MINOR(ver))
- dev_err(dev, "FW version minor %d error (expect %d)\n",
- FW_VER_MINOR(ver),
- adreno_dev->gpucore->gpmu_minor);
+ result = hfi_send_perftbl(gmu);
+ if (result)
+ return result;
- result = hfi_send_perftbl(gmu);
- if (result)
- return result;
+ result = hfi_send_bwtbl(gmu);
+ if (result)
+ return result;
- result = hfi_send_bwtbl(gmu);
- if (result)
- return result;
-
- /*
- * FW is not ready for LM configuration
- * without powering on GPU.
- */
- /*
- * result = hfi_send_lmconfig(gmu);
- * if (result)
- * return result;
- */
- }
+ /*
+ * FW is not ready for LM configuration
+ * without powering on GPU.
+ */
+ /*
+ * result = hfi_send_lmconfig(gmu);
+ * if (result)
+ * return result;
+ */
set_bit(GMU_HFI_ON, &gmu->flags);
return 0;
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 40d239c..7cbda72 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -875,6 +875,25 @@
return snprintf(buf, PAGE_SIZE, "%lu\n", timestamp);
}
+static ssize_t snapshot_legacy_show(struct kgsl_device *device, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", device->snapshot_legacy);
+}
+
+static ssize_t snapshot_legacy_store(struct kgsl_device *device,
+ const char *buf, size_t count)
+{
+ unsigned int val = 0;
+ int ret;
+
+ ret = kgsl_sysfs_store(buf, &val);
+
+ if (!ret && device)
+ device->snapshot_legacy = (bool)val;
+
+ return (ssize_t) ret < 0 ? ret : count;
+}
+
static struct bin_attribute snapshot_attr = {
.attr.name = "dump",
.attr.mode = 0444,
@@ -894,6 +913,8 @@
static SNAPSHOT_ATTR(force_panic, 0644, force_panic_show, force_panic_store);
static SNAPSHOT_ATTR(snapshot_crashdumper, 0644, snapshot_crashdumper_show,
snapshot_crashdumper_store);
+static SNAPSHOT_ATTR(snapshot_legacy, 0644, snapshot_legacy_show,
+ snapshot_legacy_store);
static ssize_t snapshot_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
@@ -975,6 +996,7 @@
device->snapshot_faultcount = 0;
device->force_panic = 0;
device->snapshot_crashdumper = 1;
+ device->snapshot_legacy = 0;
ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
&device->dev->kobj, "snapshot");
@@ -1000,6 +1022,12 @@
ret = sysfs_create_file(&device->snapshot_kobj,
&attr_snapshot_crashdumper.attr);
+ if (ret)
+ goto done;
+
+ ret = sysfs_create_file(&device->snapshot_kobj,
+ &attr_snapshot_legacy.attr);
+
done:
return ret;
}
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 0bdcc99..a50e901 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -616,6 +616,15 @@
This enables bare minimum support of power management at platform level.
i.e WFI
+config MSM_QBT1000
+ bool "QBT1000 Ultrasonic Fingerprint Sensor"
+ help
+ This driver provides services for configuring the fingerprint
+ sensor hardware and for communicating with the trusted app which
+ uses it. It enables clocks and provides commands for loading
+ trusted apps, unloading them and marshalling buffers to the
+ trusted fingerprint app.
+
config APSS_CORE_EA
depends on CPU_FREQ && PM_OPP
bool "Qualcomm Technology Inc specific power aware driver"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 9d175cd..9a7262e 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -65,6 +65,7 @@
obj-y += ramdump.o
endif
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
+obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o
obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
new file mode 100644
index 0000000..67a5e05
--- /dev/null
+++ b/drivers/soc/qcom/qbt1000.c
@@ -0,0 +1,1207 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define DEBUG
+#define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <uapi/linux/qbt1000.h>
+#include <soc/qcom/scm.h>
+#include "../../misc/qseecom_kernel.h"
+
+#define QBT1000_DEV "qbt1000"
+#define QBT1000_IN_DEV_NAME "qbt1000_key_input"
+#define QBT1000_IN_DEV_VERSION 0x0100
+#define MAX_FW_EVENTS 128
+#define FP_APP_CMD_RX_IPC 132
+#define FW_MAX_IPC_MSG_DATA_SIZE 0x500
+#define IPC_MSG_ID_CBGE_REQUIRED 29
+
+/*
+ * shared buffer size - init with max value,
+ * user space will provide new value upon tz app load
+ */
+static uint32_t g_app_buf_size = SZ_256K;
+static char const *const FP_APP_NAME = "fingerpr";
+
+struct finger_detect_gpio {
+ int gpio;
+ int active_low;
+ int irq;
+ struct work_struct work;
+ unsigned int key_code;
+ int power_key_enabled;
+ int last_gpio_state;
+ int event_reported;
+};
+
+struct fw_event_desc {
+ enum qbt1000_fw_event ev;
+};
+
+struct fw_ipc_info {
+ int gpio;
+ int irq;
+};
+
+struct qbt1000_drvdata {
+ struct class *qbt1000_class;
+ struct cdev qbt1000_cdev;
+ struct device *dev;
+ char *qbt1000_node;
+ struct clk **clocks;
+ unsigned int clock_count;
+ uint8_t clock_state;
+ unsigned int root_clk_idx;
+ unsigned int frequency;
+ atomic_t available;
+ struct mutex mutex;
+ struct mutex fw_events_mutex;
+ struct input_dev *in_dev;
+ struct fw_ipc_info fw_ipc;
+ struct finger_detect_gpio fd_gpio;
+ DECLARE_KFIFO(fw_events, struct fw_event_desc, MAX_FW_EVENTS);
+ wait_queue_head_t read_wait_queue;
+ struct qseecom_handle *app_handle;
+ struct qseecom_handle *fp_app_handle;
+};
+
+/*
+ * struct fw_ipc_cmd -
+ * used to store IPC commands to/from firmware
+ * @status - indicates whether sending/getting the IPC message was successful
+ * @msg_type - the type of IPC message
+ * @msg_len - the length of the message data
+ * @resp_needed - whether a response is needed for this message
+ * @msg_data - any extra data associated with the message
+ */
+struct fw_ipc_cmd {
+ uint32_t status;
+ uint32_t numMsgs;
+ uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE];
+};
+
+struct fw_ipc_header {
+ uint32_t msg_type;
+ uint32_t msg_len;
+ uint32_t resp_needed;
+};
+
+/*
+ * struct ipc_msg_type_to_fw_event -
+ * entry in mapping between an IPC message type to a firmware event
+ * @msg_type - IPC message type, as reported by firmware
+ * @fw_event - corresponding firmware event code to report to driver client
+ */
+struct ipc_msg_type_to_fw_event {
+ uint32_t msg_type;
+ enum qbt1000_fw_event fw_event;
+};
+
+/* mapping between firmware IPC message types to HLOS firmware events */
+struct ipc_msg_type_to_fw_event g_msg_to_event[] = {
+ {IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED}
+};
+
+/**
+ * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
+ * aligns buffer lengths
+ * @hdl: index of qseecom_handle
+ * @cmd: req buffer - set to qseecom_handle.sbuf
+ * @cmd_len: ptr to req buffer len
+ * @rsp: rsp buffer - set to qseecom_handle.sbuf + offset
+ * @rsp_len: ptr to rsp buffer len
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int get_cmd_rsp_buffers(struct qseecom_handle *hdl,
+ void **cmd,
+ uint32_t *cmd_len,
+ void **rsp,
+ uint32_t *rsp_len)
+{
+ /* 64 bytes alignment for QSEECOM */
+ *cmd_len = ALIGN(*cmd_len, 64);
+ *rsp_len = ALIGN(*rsp_len, 64);
+
+ if (((uint64_t)*rsp_len + (uint64_t)*cmd_len)
+ > (uint64_t)g_app_buf_size) {
+ pr_err("buffer too small to hold cmd=%d and rsp=%d\n",
+ *cmd_len, *rsp_len);
+ return -ENOMEM;
+ }
+
+ *cmd = hdl->sbuf;
+ *rsp = hdl->sbuf + *cmd_len;
+ return 0;
+}
+
+/**
+ * send_tz_cmd() - Function sends a command to TZ
+ *
+ * @drvdata: pointer to driver data
+ * @app_handle: handle to tz app
+ * @is_user_space: 1 if the cmd buffer is in user space, 0
+ * otherwise
+ * @cmd: command buffer to send
+ * @cmd_len: length of the command buffer
+ * @rsp: output, will be set to location of response buffer
+ * @rsp_len: max size of response
+ *
+ * Return: 0 on success.
+ */
+static int send_tz_cmd(struct qbt1000_drvdata *drvdata,
+ struct qseecom_handle *app_handle,
+ int is_user_space,
+ void *cmd, uint32_t cmd_len,
+ void **rsp, uint32_t rsp_len)
+{
+ int rc = 0;
+ void *aligned_cmd;
+ void *aligned_rsp;
+ uint32_t aligned_cmd_len;
+ uint32_t aligned_rsp_len;
+
+ /* init command and response buffers and align lengths */
+ aligned_cmd_len = cmd_len;
+ aligned_rsp_len = rsp_len;
+
+ rc = get_cmd_rsp_buffers(app_handle,
+ (void **)&aligned_cmd,
+ &aligned_cmd_len,
+ (void **)&aligned_rsp,
+ &aligned_rsp_len);
+
+ if (rc != 0)
+ goto end;
+
+ if (!aligned_cmd) {
+ dev_err(drvdata->dev, "%s: Null command buffer\n",
+ __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (aligned_cmd - cmd + cmd_len > g_app_buf_size) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ if (is_user_space) {
+ rc = copy_from_user(aligned_cmd, (void __user *)cmd,
+ cmd_len);
+ if (rc != 0) {
+ pr_err("failure to copy user space buf %d\n", rc);
+ rc = -EFAULT;
+ goto end;
+ }
+ } else
+ memcpy(aligned_cmd, cmd, cmd_len);
+
+ /* send cmd to TZ */
+ rc = qseecom_send_command(app_handle,
+ aligned_cmd,
+ aligned_cmd_len,
+ aligned_rsp,
+ aligned_rsp_len);
+
+ if (rc != 0) {
+ pr_err("failure to send tz cmd %d\n", rc);
+ goto end;
+ }
+
+ *rsp = aligned_rsp;
+
+end:
+ return rc;
+}
+
+/**
+ * qbt1000_open() - Function called when user space opens device.
+ * Successful if driver not currently open.
+ * @inode: ptr to inode object
+ * @file: ptr to file object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ struct qbt1000_drvdata *drvdata = container_of(inode->i_cdev,
+ struct qbt1000_drvdata,
+ qbt1000_cdev);
+ file->private_data = drvdata;
+
+ pr_debug("qbt1000_open begin\n");
+ /* disallowing concurrent opens */
+ if (!atomic_dec_and_test(&drvdata->available)) {
+ atomic_inc(&drvdata->available);
+ rc = -EBUSY;
+ }
+
+ pr_debug("qbt1000_open end : %d\n", rc);
+ return rc;
+}
+
+/**
+ * qbt1000_release() - Function called when user space closes device.
+
+ * @inode: ptr to inode object
+ * @file: ptr to file object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_release(struct inode *inode, struct file *file)
+{
+ struct qbt1000_drvdata *drvdata;
+
+ if (!file || !file->private_data) {
+ pr_err("qbt1000_release: NULL pointer passed");
+ return -EINVAL;
+ }
+ drvdata = file->private_data;
+ atomic_inc(&drvdata->available);
+ return 0;
+}
+
+/**
+ * qbt1000_ioctl() - Function called when user space calls ioctl.
+ * @file: struct file - not used
+ * @cmd: cmd identifier:QBT1000_LOAD_APP,QBT1000_UNLOAD_APP,
+ * QBT1000_SEND_TZCMD
+ * @arg: ptr to relevant structe: either qbt1000_app or
+ * qbt1000_send_tz_cmd depending on which cmd is passed
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static long qbt1000_ioctl(
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ void __user *priv_arg = (void __user *)arg;
+ struct qbt1000_drvdata *drvdata;
+
+ if (!file || !file->private_data) {
+ pr_err("qbt1000_ioctl: NULL pointer passed");
+ return -EINVAL;
+ }
+
+ drvdata = file->private_data;
+
+ mutex_lock(&drvdata->mutex);
+
+ pr_debug("qbt1000_ioctl %d\n", cmd);
+
+ switch (cmd) {
+ case QBT1000_LOAD_APP:
+ {
+ struct qbt1000_app app;
+ struct qseecom_handle *app_handle;
+
+ if (copy_from_user(&app, priv_arg,
+ sizeof(app)) != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space-LOAD\n");
+ goto end;
+ }
+
+ if (!app.app_handle) {
+ dev_err(drvdata->dev, "%s: LOAD app_handle is null\n",
+ __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (drvdata->app_handle) {
+ dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n",
+ __func__);
+ drvdata->fp_app_handle = 0;
+ rc = qseecom_shutdown_app(&drvdata->app_handle);
+ if (rc != 0) {
+ dev_err(drvdata->dev, "%s: LOAD current app failed to shutdown\n",
+ __func__);
+ goto end;
+ }
+ }
+
+ pr_debug("app %s load before\n", app.name);
+
+ /* start the TZ app */
+ rc = qseecom_start_app(
+ &drvdata->app_handle, app.name, app.size);
+ if (rc == 0) {
+ g_app_buf_size = app.size;
+ rc = qseecom_set_bandwidth(drvdata->app_handle,
+ app.high_band_width == 1 ? true : false);
+ if (rc != 0) {
+ /* log error, allow to continue */
+ pr_err("App %s failed to set bw\n", app.name);
+ }
+ } else {
+ pr_err("app %s failed to load\n", app.name);
+ goto end;
+ }
+
+ /* copy a fake app handle to user */
+ app_handle = drvdata->app_handle ?
+ (struct qseecom_handle *)123456 : 0;
+ rc = copy_to_user((void __user *)app.app_handle, &app_handle,
+ sizeof(*app.app_handle));
+
+ if (rc != 0) {
+ dev_err(drvdata->dev,
+ "%s: Failed copy 2us LOAD rc:%d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ pr_debug("app %s load after\n", app.name);
+
+ if (!strcmp(app.name, FP_APP_NAME))
+ drvdata->fp_app_handle = drvdata->app_handle;
+
+ break;
+ }
+ case QBT1000_UNLOAD_APP:
+ {
+ struct qbt1000_app app;
+ struct qseecom_handle *app_handle = 0;
+
+ if (copy_from_user(&app, priv_arg,
+ sizeof(app)) != 0) {
+ rc = -ENOMEM;
+ pr_err("failed copy from user space-UNLOAD\n");
+ goto end;
+ }
+
+ if (!app.app_handle) {
+ dev_err(drvdata->dev, "%s: UNLOAD app_handle is null\n",
+ __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = copy_from_user(&app_handle, app.app_handle,
+ sizeof(app_handle));
+
+ if (rc != 0) {
+ dev_err(drvdata->dev,
+ "%s: Failed copy from user space-UNLOAD handle rc:%d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ /* if the app hasn't been loaded already, return err */
+ if (!drvdata->app_handle) {
+ pr_err("app not loaded\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (drvdata->fp_app_handle == drvdata->app_handle)
+ drvdata->fp_app_handle = 0;
+
+ /* set bw & shutdown the TZ app */
+ qseecom_set_bandwidth(drvdata->app_handle,
+ app.high_band_width == 1 ? true : false);
+ rc = qseecom_shutdown_app(&drvdata->app_handle);
+ if (rc != 0) {
+ pr_err("app failed to shutdown\n");
+ goto end;
+ }
+
+ /* copy the app handle (should be null) to user */
+ rc = copy_to_user((void __user *)app.app_handle, &app_handle,
+ sizeof(*app.app_handle));
+
+ if (rc != 0) {
+ dev_err(drvdata->dev,
+ "%s: Failed copy 2us UNLOAD rc:%d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ break;
+ }
+ case QBT1000_SEND_TZCMD:
+ {
+ struct qbt1000_send_tz_cmd tzcmd;
+ void *rsp_buf;
+
+ if (copy_from_user(&tzcmd, priv_arg,
+ sizeof(tzcmd))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ if (tzcmd.req_buf_len > g_app_buf_size ||
+ tzcmd.rsp_buf_len > g_app_buf_size) {
+ rc = -ENOMEM;
+ pr_err("invalid cmd buf len, req=%d, rsp=%d\n",
+ tzcmd.req_buf_len, tzcmd.rsp_buf_len);
+ goto end;
+ }
+
+ /* if the app hasn't been loaded already, return err */
+ if (!drvdata->app_handle) {
+ pr_err("app not loaded\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = send_tz_cmd(drvdata,
+ drvdata->app_handle, 1,
+ tzcmd.req_buf, tzcmd.req_buf_len,
+ &rsp_buf, tzcmd.rsp_buf_len);
+
+ if (rc < 0) {
+ pr_err("failure sending command to tz\n");
+ goto end;
+ }
+
+ /* copy rsp buf back to user space buffer */
+ rc = copy_to_user((void __user *)tzcmd.rsp_buf,
+ rsp_buf, tzcmd.rsp_buf_len);
+ if (rc != 0) {
+ pr_err("failed copy 2us rc:%d bytes %d:\n",
+ rc, tzcmd.rsp_buf_len);
+ rc = -EFAULT;
+ goto end;
+ }
+
+ break;
+ }
+ case QBT1000_SET_FINGER_DETECT_KEY:
+ {
+ struct qbt1000_set_finger_detect_key set_fd_key;
+
+ if (copy_from_user(&set_fd_key, priv_arg,
+ sizeof(set_fd_key))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ drvdata->fd_gpio.key_code = set_fd_key.key_code;
+
+ break;
+ }
+ case QBT1000_CONFIGURE_POWER_KEY:
+ {
+ struct qbt1000_configure_power_key power_key;
+
+ if (copy_from_user(&power_key, priv_arg,
+ sizeof(power_key))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ drvdata->fd_gpio.power_key_enabled = power_key.enable;
+
+ break;
+ }
+ default:
+ pr_err("invalid cmd %d\n", cmd);
+ rc = -ENOIOCTLCMD;
+ goto end;
+ }
+
+end:
+ mutex_unlock(&drvdata->mutex);
+ return rc;
+}
+
+static int get_events_fifo_len_locked(struct qbt1000_drvdata *drvdata)
+{
+ int len;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ len = kfifo_len(&drvdata->fw_events);
+ mutex_unlock(&drvdata->fw_events_mutex);
+
+ return len;
+}
+
+static ssize_t qbt1000_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct fw_event_desc fw_event;
+ struct qbt1000_drvdata *drvdata = filp->private_data;
+
+ if (cnt < sizeof(fw_event.ev))
+ return -EINVAL;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+
+ while (kfifo_len(&drvdata->fw_events) == 0) {
+ mutex_unlock(&drvdata->fw_events_mutex);
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ pr_debug("fw_events fifo: empty, waiting\n");
+
+ if (wait_event_interruptible(drvdata->read_wait_queue,
+ (get_events_fifo_len_locked(drvdata) > 0)))
+ return -ERESTARTSYS;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ }
+
+ if (!kfifo_get(&drvdata->fw_events, &fw_event)) {
+ pr_debug("fw_events fifo: unexpectedly empty\n");
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+
+ pr_debug("fw_event: %d\n", (int)fw_event.ev);
+ return copy_to_user(ubuf, &fw_event.ev, sizeof(fw_event.ev));
+}
+
+static unsigned int qbt1000_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct qbt1000_drvdata *drvdata = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &drvdata->read_wait_queue, wait);
+
+ if (kfifo_len(&drvdata->fw_events) > 0)
+ mask |= (POLLIN | POLLRDNORM);
+
+ return mask;
+}
+
+static const struct file_operations qbt1000_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = qbt1000_ioctl,
+ .open = qbt1000_open,
+ .release = qbt1000_release,
+ .read = qbt1000_read,
+ .poll = qbt1000_poll
+};
+
+static int qbt1000_dev_register(struct qbt1000_drvdata *drvdata)
+{
+ dev_t dev_no;
+ int ret = 0;
+ size_t node_size;
+ char *node_name = QBT1000_DEV;
+ struct device *dev = drvdata->dev;
+ struct device *device;
+
+ node_size = strlen(node_name) + 1;
+
+ drvdata->qbt1000_node = devm_kzalloc(dev, node_size, GFP_KERNEL);
+ if (!drvdata->qbt1000_node) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ strlcpy(drvdata->qbt1000_node, node_name, node_size);
+
+ ret = alloc_chrdev_region(&dev_no, 0, 1, drvdata->qbt1000_node);
+ if (ret) {
+ pr_err("alloc_chrdev_region failed %d\n", ret);
+ goto err_alloc;
+ }
+
+ cdev_init(&drvdata->qbt1000_cdev, &qbt1000_fops);
+
+ drvdata->qbt1000_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&drvdata->qbt1000_cdev, dev_no, 1);
+ if (ret) {
+ pr_err("cdev_add failed %d\n", ret);
+ goto err_cdev_add;
+ }
+
+ drvdata->qbt1000_class = class_create(THIS_MODULE,
+ drvdata->qbt1000_node);
+ if (IS_ERR(drvdata->qbt1000_class)) {
+ ret = PTR_ERR(drvdata->qbt1000_class);
+ pr_err("class_create failed %d\n", ret);
+ goto err_class_create;
+ }
+
+ device = device_create(drvdata->qbt1000_class, NULL,
+ drvdata->qbt1000_cdev.dev, drvdata,
+ drvdata->qbt1000_node);
+ if (IS_ERR(device)) {
+ ret = PTR_ERR(device);
+ pr_err("device_create failed %d\n", ret);
+ goto err_dev_create;
+ }
+
+ return 0;
+err_dev_create:
+ class_destroy(drvdata->qbt1000_class);
+err_class_create:
+ cdev_del(&drvdata->qbt1000_cdev);
+err_cdev_add:
+ unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1);
+err_alloc:
+ return ret;
+}
+
+/**
+ * qbt1000_create_input_device() - Function allocates an input
+ * device, configures it for key events and registers it
+ *
+ * @drvdata: ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+
+ drvdata->in_dev = input_allocate_device();
+ if (drvdata->in_dev == NULL) {
+ dev_err(drvdata->dev, "%s: input_allocate_device() failed\n",
+ __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ drvdata->in_dev->name = QBT1000_IN_DEV_NAME;
+ drvdata->in_dev->phys = NULL;
+ drvdata->in_dev->id.bustype = BUS_HOST;
+ drvdata->in_dev->id.vendor = 0x0001;
+ drvdata->in_dev->id.product = 0x0001;
+ drvdata->in_dev->id.version = QBT1000_IN_DEV_VERSION;
+
+ drvdata->in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ drvdata->in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ drvdata->in_dev->keybit[BIT_WORD(KEY_HOMEPAGE)] |=
+ BIT_MASK(KEY_HOMEPAGE);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |=
+ BIT_MASK(KEY_CAMERA);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_VOLUMEDOWN)] |=
+ BIT_MASK(KEY_VOLUMEDOWN);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |=
+ BIT_MASK(KEY_POWER);
+
+ input_set_abs_params(drvdata->in_dev, ABS_X,
+ 0,
+ 1000,
+ 0, 0);
+ input_set_abs_params(drvdata->in_dev, ABS_Y,
+ 0,
+ 1000,
+ 0, 0);
+
+ rc = input_register_device(drvdata->in_dev);
+ if (rc) {
+ dev_err(drvdata->dev, "%s: input_reg_dev() failed %d\n",
+ __func__, rc);
+ goto end;
+ }
+
+end:
+ if (rc)
+ input_free_device(drvdata->in_dev);
+ return rc;
+}
+
+static void purge_finger_events(struct qbt1000_drvdata *drvdata)
+{
+ int i, fifo_len;
+ struct fw_event_desc fw_event;
+
+ fifo_len = kfifo_len(&drvdata->fw_events);
+
+ for (i = 0; i < fifo_len; i++) {
+ if (!kfifo_get(&drvdata->fw_events, &fw_event))
+ pr_err("fw events fifo: could not remove oldest item\n");
+ else if (fw_event.ev != FW_EVENT_FINGER_DOWN
+ && fw_event.ev != FW_EVENT_FINGER_UP)
+ kfifo_put(&drvdata->fw_events, fw_event);
+ }
+}
+
+static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata)
+{
+ int state;
+ struct fw_event_desc fw_event;
+
+ state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0)
+ ^ drvdata->fd_gpio.active_low;
+
+ if (drvdata->fd_gpio.event_reported
+ && state == drvdata->fd_gpio.last_gpio_state)
+ return;
+
+ pr_debug("gpio %d: report state %d\n", drvdata->fd_gpio.gpio, state);
+
+ drvdata->fd_gpio.event_reported = 1;
+ drvdata->fd_gpio.last_gpio_state = state;
+
+ if (drvdata->fd_gpio.key_code) {
+ input_event(drvdata->in_dev, EV_KEY,
+ drvdata->fd_gpio.key_code, !!state);
+ input_sync(drvdata->in_dev);
+ }
+
+ if (state && drvdata->fd_gpio.power_key_enabled) {
+ input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1);
+ input_sync(drvdata->in_dev);
+ input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0);
+ input_sync(drvdata->in_dev);
+ }
+
+ fw_event.ev = (state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP);
+
+ mutex_lock(&drvdata->fw_events_mutex);
+
+ if (kfifo_is_full(&drvdata->fw_events)) {
+ struct fw_event_desc dummy_fw_event;
+
+ pr_warn("fw events fifo: full, dropping oldest item\n");
+ if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event))
+ pr_err("fw events fifo: could not remove oldest item\n");
+ }
+
+ purge_finger_events(drvdata);
+
+ if (!kfifo_put(&drvdata->fw_events, fw_event))
+ pr_err("fw events fifo: error adding item\n");
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ wake_up_interruptible(&drvdata->read_wait_queue);
+}
+
+static void qbt1000_gpio_work_func(struct work_struct *work)
+{
+ struct qbt1000_drvdata *drvdata =
+ container_of(work, struct qbt1000_drvdata, fd_gpio.work);
+
+ qbt1000_gpio_report_event(drvdata);
+
+ pm_relax(drvdata->dev);
+}
+
+static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id)
+{
+ struct qbt1000_drvdata *drvdata = dev_id;
+
+ if (irq != drvdata->fd_gpio.irq) {
+ pr_warn("invalid irq %d (expected %d)\n",
+ irq, drvdata->fd_gpio.irq);
+ return IRQ_HANDLED;
+ }
+
+ pm_stay_awake(drvdata->dev);
+ schedule_work(&drvdata->fd_gpio.work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * qbt1000_ipc_irq_handler() - function processes IPC
+ * interrupts on its own thread
+ * @irq: the interrupt that occurred
+ * @dev_id: pointer to the qbt1000_drvdata
+ *
+ * Return: IRQ_HANDLED when complete
+ */
+static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
+{
+ uint8_t *msg_buffer;
+ struct fw_ipc_cmd *rx_cmd;
+ struct fw_ipc_header *header;
+ int i, j;
+ uint32_t rxipc = FP_APP_CMD_RX_IPC;
+ struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id;
+ int rc = 0;
+ uint32_t retry_count = 10;
+
+ pm_stay_awake(drvdata->dev);
+
+ mutex_lock(&drvdata->mutex);
+
+ if (irq != drvdata->fw_ipc.irq) {
+ pr_warn("invalid irq %d (expected %d)\n",
+ irq, drvdata->fw_ipc.irq);
+ goto end;
+ }
+
+ pr_debug("firmware interrupt received (irq %d)\n", irq);
+
+ if (!drvdata->fp_app_handle)
+ goto end;
+
+ while (retry_count > 0) {
+ /*
+ * send the TZ command to fetch the message from firmware
+ * TZ will process the message if it can
+ */
+ rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0,
+ &rxipc, sizeof(rxipc),
+ (void *)&rx_cmd, sizeof(*rx_cmd));
+ if (rc < 0) {
+ msleep(50); // sleep for 50ms before retry
+ retry_count -= 1;
+ continue;
+ } else {
+ pr_err("retry_count %d\n", retry_count);
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("failure sending tz cmd %d\n", rxipc);
+ goto end;
+ }
+
+ if (rx_cmd->status != 0) {
+ pr_err("tz command failed to complete\n");
+ goto end;
+ }
+
+ msg_buffer = rx_cmd->msg_data;
+
+ for (j = 0; j < rx_cmd->numMsgs; j++) {
+ header = (struct fw_ipc_header *) msg_buffer;
+ /*
+ * given the IPC message type, search for a corresponding
+ * event for the driver client. If found, add to the events
+ * FIFO
+ */
+ for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
+ if (g_msg_to_event[i].msg_type == header->msg_type) {
+ enum qbt1000_fw_event ev =
+ g_msg_to_event[i].fw_event;
+ struct fw_event_desc fw_ev_desc;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ pr_debug("fw events: add %d\n", (int) ev);
+ fw_ev_desc.ev = ev;
+
+ if (!kfifo_put(&drvdata->fw_events, fw_ev_desc))
+ pr_err("fw events: fifo full, drop event %d\n",
+ (int) ev);
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ break;
+ }
+ }
+ msg_buffer += sizeof(*header) + header->msg_len;
+ }
+ wake_up_interruptible(&drvdata->read_wait_queue);
+end:
+ mutex_unlock(&drvdata->mutex);
+ pm_relax(drvdata->dev);
+ return IRQ_HANDLED;
+}
+
+static int setup_fd_gpio_irq(struct platform_device *pdev,
+ struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ int irq;
+ const char *desc = "qbt_finger_detect";
+
+ rc = devm_gpio_request_one(&pdev->dev, drvdata->fd_gpio.gpio,
+ GPIOF_IN, desc);
+
+ if (rc < 0) {
+ pr_err("failed to request gpio %d, error %d\n",
+ drvdata->fd_gpio.gpio, rc);
+ goto end;
+ }
+
+ irq = gpio_to_irq(drvdata->fd_gpio.gpio);
+ if (irq < 0) {
+ rc = irq;
+ pr_err("unable to get irq number for gpio %d, error %d\n",
+ drvdata->fd_gpio.gpio, rc);
+ goto end;
+ }
+
+ drvdata->fd_gpio.irq = irq;
+ INIT_WORK(&drvdata->fd_gpio.work, qbt1000_gpio_work_func);
+
+ rc = devm_request_any_context_irq(&pdev->dev, drvdata->fd_gpio.irq,
+ qbt1000_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ desc, drvdata);
+
+ if (rc < 0) {
+ pr_err("unable to claim irq %d; error %d\n",
+ drvdata->fd_gpio.irq, rc);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+static int setup_ipc_irq(struct platform_device *pdev,
+ struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ const char *desc = "qbt_ipc";
+
+ drvdata->fw_ipc.irq = gpio_to_irq(drvdata->fw_ipc.gpio);
+ pr_debug("\nirq %d gpio %d\n",
+ drvdata->fw_ipc.irq, drvdata->fw_ipc.gpio);
+ if (drvdata->fw_ipc.irq < 0) {
+ rc = drvdata->fw_ipc.irq;
+ pr_err("no irq for gpio %d, error=%d\n",
+ drvdata->fw_ipc.gpio, rc);
+ goto end;
+ }
+
+ rc = devm_gpio_request_one(&pdev->dev, drvdata->fw_ipc.gpio,
+ GPIOF_IN, desc);
+
+ if (rc < 0) {
+ pr_err("failed to request gpio %d, error %d\n",
+ drvdata->fw_ipc.gpio, rc);
+ goto end;
+ }
+
+ rc = devm_request_threaded_irq(&pdev->dev,
+ drvdata->fw_ipc.irq,
+ NULL,
+ qbt1000_ipc_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ desc,
+ drvdata);
+
+ if (rc < 0) {
+ pr_err("failed to register for ipc irq %d, rc = %d\n",
+ drvdata->fw_ipc.irq, rc);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+/**
+ * qbt1000_read_device_tree() - Function reads device tree
+ * properties into driver data
+ * @pdev: ptr to platform device object
+ * @drvdata: ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_read_device_tree(struct platform_device *pdev,
+ struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ uint32_t rate;
+ int gpio;
+ enum of_gpio_flags flags;
+
+ /* read clock frequency */
+ if (of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &rate) == 0) {
+ pr_debug("clk frequency %d\n", rate);
+ drvdata->frequency = rate;
+ }
+
+ /* read IPC gpio */
+ drvdata->fw_ipc.gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,ipc-gpio", 0);
+ if (drvdata->fw_ipc.gpio < 0) {
+ rc = drvdata->fw_ipc.gpio;
+ pr_err("ipc gpio not found, error=%d\n", rc);
+ goto end;
+ }
+
+ /**
+ * TODO: Need to revisit after adding GPIO in DTSI- read
+ * finger detect GPIO configuration
+ */
+
+ gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+ "qcom,finger-detect-gpio", 0, &flags);
+ if (gpio < 0) {
+ pr_err("failed to get gpio flags\n");
+ rc = gpio;
+ goto end;
+ }
+
+ drvdata->fd_gpio.gpio = gpio;
+ drvdata->fd_gpio.active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+end:
+ return rc;
+}
+
+/**
+ * qbt1000_probe() - Function loads hardware config from device tree
+ * @pdev: ptr to platform device object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qbt1000_drvdata *drvdata;
+ int rc = 0;
+
+ pr_debug("qbt1000_probe begin\n");
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ rc = qbt1000_read_device_tree(pdev, drvdata);
+ if (rc < 0)
+ goto end;
+
+ atomic_set(&drvdata->available, 1);
+
+ mutex_init(&drvdata->mutex);
+ mutex_init(&drvdata->fw_events_mutex);
+
+ rc = qbt1000_dev_register(drvdata);
+ if (rc < 0)
+ goto end;
+
+ INIT_KFIFO(drvdata->fw_events);
+ init_waitqueue_head(&drvdata->read_wait_queue);
+
+ rc = qbt1000_create_input_device(drvdata);
+ if (rc < 0)
+ goto end;
+
+ rc = setup_fd_gpio_irq(pdev, drvdata);
+ if (rc < 0)
+ goto end;
+
+ rc = setup_ipc_irq(pdev, drvdata);
+ if (rc < 0)
+ goto end;
+
+ rc = device_init_wakeup(&pdev->dev, 1);
+ if (rc < 0)
+ goto end;
+
+end:
+ pr_debug("qbt1000_probe end : %d\n", rc);
+ return rc;
+}
+
+static int qbt1000_remove(struct platform_device *pdev)
+{
+ struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ input_unregister_device(drvdata->in_dev);
+
+ mutex_destroy(&drvdata->mutex);
+ mutex_destroy(&drvdata->fw_events_mutex);
+
+ device_destroy(drvdata->qbt1000_class, drvdata->qbt1000_cdev.dev);
+ class_destroy(drvdata->qbt1000_class);
+ cdev_del(&drvdata->qbt1000_cdev);
+ unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+static int qbt1000_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int rc = 0;
+ struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ /*
+ * Returning an error code if driver currently making a TZ call.
+ * Note: The purpose of this driver is to ensure that the clocks are on
+ * while making a TZ call. Hence the clock check to determine if the
+ * driver will allow suspend to occur.
+ */
+ if (!mutex_trylock(&drvdata->mutex))
+ return -EBUSY;
+
+ if (drvdata->clock_state)
+ rc = -EBUSY;
+ else {
+ enable_irq_wake(drvdata->fd_gpio.irq);
+ enable_irq_wake(drvdata->fw_ipc.irq);
+ }
+
+ mutex_unlock(&drvdata->mutex);
+
+ return rc;
+}
+
+static int qbt1000_resume(struct platform_device *pdev)
+{
+ struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ disable_irq_wake(drvdata->fd_gpio.irq);
+ disable_irq_wake(drvdata->fw_ipc.irq);
+
+ return 0;
+}
+
+static const struct of_device_id qbt1000_match[] = {
+ { .compatible = "qcom,qbt1000" },
+ {}
+};
+
+static struct platform_driver qbt1000_plat_driver = {
+ .probe = qbt1000_probe,
+ .remove = qbt1000_remove,
+ .suspend = qbt1000_suspend,
+ .resume = qbt1000_resume,
+ .driver = {
+ .name = "qbt1000",
+ .owner = THIS_MODULE,
+ .of_match_table = qbt1000_match,
+ },
+};
+
+module_platform_driver(qbt1000_plat_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QBT1000 driver");
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 82718c8..57f38d3 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -266,7 +266,6 @@
pd->total_domains = resp->total_domains;
if (!resp->total_domains) {
pr_err("No matching domains found\n");
- rc = -EIO;
goto out;
}
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 7c1899e..7cf7779 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -382,6 +382,7 @@
header-y += psci.h
header-y += ptp_clock.h
header-y += ptrace.h
+header-y += qbt1000.h
header-y += qcedev.h
header-y += qcota.h
header-y += qnx4_fs.h
diff --git a/include/uapi/linux/qbt1000.h b/include/uapi/linux/qbt1000.h
new file mode 100644
index 0000000..a4f0dca
--- /dev/null
+++ b/include/uapi/linux/qbt1000.h
@@ -0,0 +1,99 @@
+#ifndef _UAPI_QBT1000_H_
+#define _UAPI_QBT1000_H_
+
+#define MAX_NAME_SIZE 32
+
+/*
+ * enum qbt1000_commands -
+ * enumeration of command options
+ * @QBT1000_LOAD_APP - cmd loads TZ app
+ * @QBT1000_UNLOAD_APP - cmd unloads TZ app
+ * @QBT1000_SEND_TZCMD - sends cmd to TZ app
+ * @QBT1000_SET_FINGER_DETECT_KEY - sets the input key to send on finger detect
+ * @QBT1000_CONFIGURE_POWER_KEY - enables/disables sending the power key on
+ finger down events
+*/
+enum qbt1000_commands {
+ QBT1000_LOAD_APP = 100,
+ QBT1000_UNLOAD_APP = 101,
+ QBT1000_SEND_TZCMD = 102,
+ QBT1000_SET_FINGER_DETECT_KEY = 103,
+ QBT1000_CONFIGURE_POWER_KEY = 104
+};
+
+/*
+ * enum qbt1000_fw_event -
+ * enumeration of firmware events
+ * @FW_EVENT_FINGER_DOWN - finger down detected
+ * @FW_EVENT_FINGER_UP - finger up detected
+ * @FW_EVENT_INDICATION - an indication IPC from the firmware is pending
+ */
+enum qbt1000_fw_event {
+ FW_EVENT_FINGER_DOWN = 1,
+ FW_EVENT_FINGER_UP = 2,
+ FW_EVENT_CBGE_REQUIRED = 3,
+};
+
+/*
+ * struct qbt1000_app -
+ * used to load and unload apps in TZ
+ * @app_handle - qseecom handle for clients
+ * @name - Name of secure app to load
+ * @size - Size of requested buffer of secure app
+ * @high_band_width - 1 - for high bandwidth usage
+ * 0 - for normal bandwidth usage
+ */
+struct qbt1000_app {
+ struct qseecom_handle **app_handle;
+ char name[MAX_NAME_SIZE];
+ uint32_t size;
+ uint8_t high_band_width;
+};
+
+/*
+ * struct qbt1000_send_tz_cmd -
+ * used to cmds to TZ App
+ * @app_handle - qseecom handle for clients
+ * @req_buf - Buffer containing request for secure app
+ * @req_buf_len - Length of request buffer
+ * @rsp_buf - Buffer containing response from secure app
+ * @rsp_buf_len - Length of response buffer
+ */
+struct qbt1000_send_tz_cmd {
+ struct qseecom_handle *app_handle;
+ uint8_t *req_buf;
+ uint32_t req_buf_len;
+ uint8_t *rsp_buf;
+ uint32_t rsp_buf_len;
+};
+
+/*
+ * struct qbt1000_erie_event -
+ * used to receive events from Erie
+ * @buf - Buffer containing event from Erie
+ * @buf_len - Length of buffer
+ */
+struct qbt1000_erie_event {
+ uint8_t *buf;
+ uint32_t buf_len;
+};
+
+/*
+ * struct qbt1000_set_finger_detect_key -
+ * used to configure the input key which is sent on finger down/up event
+ * @key_code - Key code to send on finger down/up. 0 disables sending key events
+ */
+struct qbt1000_set_finger_detect_key {
+ unsigned int key_code;
+};
+
+/*
+ * struct qbt1000_configure_power_key -
+ * used to configure whether the power key is sent on finger down
+ * @enable - if non-zero, power key is sent on finger down
+ */
+struct qbt1000_configure_power_key {
+ unsigned int enable;
+};
+
+#endif /* _UAPI_QBT1000_H_ */
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
index 37dd31f..421769e 100644
--- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -1165,28 +1165,27 @@
break;
case SNDRV_LSM_SET_FWK_MODE_CONFIG: {
- u32 *mode = NULL;
+ u32 mode;
- if (!arg) {
- dev_err(rtd->dev,
- "%s: Invalid param arg for ioctl %s session %d\n",
- __func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG",
- prtd->lsm_client->session);
- rc = -EINVAL;
- break;
+ if (copy_from_user(&mode, arg, sizeof(mode))) {
+ dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n",
+ __func__, "LSM_SET_FWK_MODE_CONFIG");
+ return -EFAULT;
}
- mode = (u32 *)arg;
- if (prtd->lsm_client->event_mode == *mode) {
+
+ dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n",
+ __func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG", mode);
+ if (prtd->lsm_client->event_mode == mode) {
dev_dbg(rtd->dev,
"%s: mode for %d already set to %d\n",
- __func__, prtd->lsm_client->session, *mode);
+ __func__, prtd->lsm_client->session, mode);
rc = 0;
} else {
dev_dbg(rtd->dev, "%s: Event mode = %d\n",
- __func__, *mode);
- rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, *mode);
+ __func__, mode);
+ rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, mode);
if (!rc)
- prtd->lsm_client->event_mode = *mode;
+ prtd->lsm_client->event_mode = mode;
else
dev_err(rtd->dev,
"%s: set event mode failed %d\n",