Merge "clk: Add support to dump state of all clocks into ftrace" into msm-4.9
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt b/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt
new file mode 100644
index 0000000..2ed913c
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt
@@ -0,0 +1,130 @@
+* Qualcomm Technologies, Inc. MSM Camera SMMU
+
+The MSM camera SMMU device provides SMMU context bank definitions
+for all HW blocks that need to map IOVA to physical memory. These
+definitions consist of various properties that define how the
+IOVA address space is laid out for each HW block in the camera
+subsystem.
+
+=======================
+Required Node Structure
+=======================
+The camera SMMU device must be described in three levels of device nodes. The
+first level describes the overall SMMU device. Within it, second level nodes
+describe individual context banks that map different stream ids. There can
+also be second level nodes describing firmware device nodes. Each HW block
+such as IFE, ICP maps into these second level device nodes. All context bank
+specific properties that define how the IOVA is laid out is contained within
+third level device nodes within the second level device nodes.
+
+During the kernel initialization all the devices are probed recursively and
+a device pointer is created for each context bank keeping track of the IOVA
+mapping information.
+
+Duplicate regions of the same type are not allowed within the same
+context bank. All context banks must contain an IO region at the very least.
+
+==================================
+First Level Node - CAM SMMU device
+==================================
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,msm-cam-smmu".
+
+===================================================================
+Second Level Node - CAM SMMU context bank device or firmware device
+===================================================================
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Should be "qcom,msm-cam-smmu-cb" or "qcom,msm-cam-smmu-fw-dev".
+
+- memory-region
+ Usage: optional
+ Value type: <phandle>
+ Definition: Should specify the phandle of the memory region for firmware.
+ allocation
+
+- iommus
+ Usage: required
+ Value type: <phandle>
+ Definition: Should specify the phandle of the iommu sid.
+
+- label
+ Usage: required
+ Value type: <string>
+ Definition: Should specify a string label to identify the context bank.
+
+=============================================
+Third Level Node - CAM SMMU memory map device
+=============================================
+- iova-region-name
+ Usage: required
+ Value type: <string>
+ Definition: Should specify a string label to identify the IOVA region.
+
+- iova-region-start
+ Usage: required
+ Value type: <u32>
+ Definition: Should specify start IOVA for region.
+
+- iova-region-len
+ Usage: required
+ Value type: <u32>
+ Definition: Should specify length for IOVA region.
+
+- iova-region-id
+ Usage: required
+ Value type: <u32>
+ Definition: Should specify the numerical identifier for IOVA region.
+ Allowed values are: 0x00 to 0x03
+ - Firmware region: 0x00
+ - Shared region: 0x01
+ - Scratch region: 0x02
+ - IO region: 0x03
+
+Example:
+ qcom,cam_smmu@0 {
+ compatible = "qcom,msm-cam-smmu";
+
+ msm_cam_smmu_icp {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_smmu 0x1078>,
+ <&apps_smmu 0x1020>,
+ <&apps_smmu 0x1028>,
+ <&apps_smmu 0x1040>,
+ <&apps_smmu 0x1048>,
+ <&apps_smmu 0x1030>,
+ <&apps_smmu 0x1050>;
+ label = "icp";
+ icp_iova_mem_map: iova-mem-map {
+ iova-mem-region-firmware {
+ /* Firmware region is 5MB */
+ iova-region-name = "firmware";
+ iova-region-start = <0x0>;
+ iova-region-len = <0x500000>;
+ iova-region-id = <0x0>;
+ status = "ok";
+ };
+
+ iova-mem-region-shared {
+ /* Shared region is 100MB long */
+ iova-region-name = "shared";
+ iova-region-start = <0x7400000>;
+ iova-region-len = <0x6400000>;
+ iova-region-id = <0x1>;
+ status = "ok";
+ };
+
+ iova-mem-region-io {
+ /* IO region is approximately 3.5 GB */
+ iova-region-name = "io";
+ iova-region-start = <0xd800000>;
+ iova-region-len = <0xd2800000>;
+ iova-region-id = <0x3>;
+ status = "ok";
+ };
+ };
+ };
+ };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
index 967e71f..78a26c2 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
@@ -26,4 +26,5 @@
&blsp1_uart2 {
pinctrl-names = "default";
pinctrl-0 = <&uart2_console_active>;
+ status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 0afa5a8..0078617 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -153,5 +153,8 @@
reg = <0x831000 0x200>;
interrupts = <0 26 0>;
status = "disabled";
+ clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index 434de76..1d471f5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -380,6 +380,32 @@
};
+ hwevent: hwevent@0x014066f0 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0x14066f0 0x4>,
+ <0x14166f0 0x4>,
+ <0x1406038 0x4>,
+ <0x1416038 0x4>;
+ reg-names = "ddr-ch0-cfg", "ddr-ch23-cfg", "ddr-ch0-ctrl",
+ "ddr-ch23-ctrl";
+
+ coresight-name = "coresight-hwevent";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ csr: csr@6001000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6001000 0x1000>;
+ reg-names = "csr-base";
+
+ coresight-name = "coresight-csr";
+
+ qcom,blk-size = <1>;
+ };
+
funnel_in0: funnel@0x6041000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b908>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 6989f326..5c39e86 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -74,7 +74,7 @@
qcom,cpr-step-quot-init-min = <11>;
qcom,cpr-step-quot-init-max = <12>;
qcom,cpr-count-mode = <0>; /* All at once */
- qcom,cpr-count-repeat = <1>;
+ qcom,cpr-count-repeat = <20>;
qcom,cpr-down-error-step-limit = <1>;
qcom,cpr-up-error-step-limit = <1>;
qcom,cpr-corner-switch-delay-time = <1042>;
@@ -103,7 +103,7 @@
thread@1 {
qcom,cpr-thread-id = <1>;
qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <2>;
+ qcom,cpr-consecutive-down = <0>;
qcom,cpr-up-threshold = <2>;
qcom,cpr-down-threshold = <2>;
@@ -180,7 +180,7 @@
thread@0 {
qcom,cpr-thread-id = <0>;
qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <2>;
+ qcom,cpr-consecutive-down = <0>;
qcom,cpr-up-threshold = <2>;
qcom,cpr-down-threshold = <2>;
@@ -264,7 +264,7 @@
qcom,cpr-step-quot-init-min = <9>;
qcom,cpr-step-quot-init-max = <14>;
qcom,cpr-count-mode = <0>; /* All at once */
- qcom,cpr-count-repeat = <1>;
+ qcom,cpr-count-repeat = <20>;
qcom,cpr-down-error-step-limit = <1>;
qcom,cpr-up-error-step-limit = <1>;
qcom,cpr-corner-switch-delay-time = <1042>;
@@ -296,7 +296,7 @@
thread@0 {
qcom,cpr-thread-id = <0>;
qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <2>;
+ qcom,cpr-consecutive-down = <0>;
qcom,cpr-up-threshold = <2>;
qcom,cpr-down-threshold = <2>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
index e56073c..7f24c8b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
@@ -107,8 +107,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
@@ -130,8 +130,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
@@ -153,8 +153,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
@@ -176,8 +176,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
@@ -222,8 +222,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
@@ -245,8 +245,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
@@ -268,8 +268,8 @@
qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
- clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
- <&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>;
+ clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+ <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
clock-names = "src_byte_clk", "src_pixel_clk";
pinctrl-names = "panel_active", "panel_suspend";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index c80343a..aac63ee 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -303,6 +303,13 @@
qcom,reset-ep-after-lpm-resume;
};
+ usb_audio_qmi_dev {
+ compatible = "qcom,usb-audio-qmi-dev";
+ iommus = <&apps_smmu 0x182c>;
+ qcom,usb-audio-stream-id = <0xc>;
+ qcom,usb-audio-intr-num = <2>;
+ };
+
usb_nop_phy: usb_nop_phy {
compatible = "usb-nop-xceiv";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 4fdf383..efd8c32 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -16,3 +16,7 @@
model = "Qualcomm Technologies, Inc. SDM845 V2";
qcom,msm-id = <321 0x20000>;
};
+
+&spmi_debug_bus {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 54e0162..5a8715c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -553,6 +553,7 @@
qcom,fuse-disable-bit = <12>;
#address-cells = <2>;
#size-cells = <0>;
+ status = "disabled";
qcom,pm8998-debug@0 {
compatible = "qcom,spmi-pmic";
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index a71aa64..1f0418f 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -596,6 +596,7 @@
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_DUMMY=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
index 3daefbc..9ce0afc 100644
--- a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
@@ -23,9 +23,10 @@
#define VCO_DELAY_USEC 1
-#define MHZ_375 375000000UL
-#define MHZ_750 750000000UL
-#define MHZ_1500 1500000000UL
+#define MHZ_250 250000000UL
+#define MHZ_500 500000000UL
+#define MHZ_1000 1000000000UL
+#define MHZ_1100 1100000000UL
#define MHZ_1900 1900000000UL
#define MHZ_3000 3000000000UL
@@ -99,6 +100,7 @@
u32 frac_div_start_low;
u32 frac_div_start_mid;
u32 frac_div_start_high;
+ u32 pll_clock_inverters;
u32 ssc_stepsize_low;
u32 ssc_stepsize_high;
u32 ssc_div_per_low;
@@ -209,20 +211,36 @@
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
+ u32 i;
target_freq = rsc->vco_current_rate;
pr_debug("target_freq = %llu\n", target_freq);
if (config->div_override) {
computed_output_div = config->output_div;
+
+ /*
+ * Computed_output_div = 2 ^ div_log
+ * To get div_log from output div just get the index of the
+ * 1 bit in the value.
+ * div_log ranges from 0-3. so check the 4 lsbs
+ */
+
+ for (i = 0; i < 4; i++) {
+ if (computed_output_div & (1 << i)) {
+ div_log = i;
+ break;
+ }
+ }
+
} else {
- if (target_freq < MHZ_375) {
+ if (target_freq < MHZ_250) {
computed_output_div = 8;
div_log = 3;
- } else if (target_freq < MHZ_750) {
+ } else if (target_freq < MHZ_500) {
computed_output_div = 4;
div_log = 2;
- } else if (target_freq < MHZ_1500) {
+ } else if (target_freq < MHZ_1000) {
computed_output_div = 2;
div_log = 1;
} else {
@@ -251,6 +269,10 @@
regs->pll_prop_gain_rate = 10;
else
regs->pll_prop_gain_rate = 12;
+ if (pll_freq < MHZ_1100)
+ regs->pll_clock_inverters = 8;
+ else
+ regs->pll_clock_inverters = 0;
regs->pll_outdiv_rate = div_log;
regs->pll_lockdet_rate = config->lock_timer;
@@ -375,7 +397,7 @@
MDSS_PLL_REG_W(pll_base, PLL_PLL_OUTDIV_RATE, reg->pll_outdiv_rate);
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);
+ MDSS_PLL_REG_W(pll_base, PLL_CLOCK_INVERTERS, reg->pll_clock_inverters);
}
@@ -1085,7 +1107,7 @@
static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
.ref_clk_rate = 19200000UL,
- .min_rate = 1500000000UL,
+ .min_rate = 1000000000UL,
.max_rate = 3500000000UL,
.hw.init = &(struct clk_init_data){
.name = "dsi0pll_vco_clk",
@@ -1098,7 +1120,7 @@
static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
.ref_clk_rate = 19200000UL,
- .min_rate = 1500000000UL,
+ .min_rate = 1000000000UL,
.max_rate = 3500000000UL,
.hw.init = &(struct clk_init_data){
.name = "dsi1pll_vco_clk",
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 550a59c..5db1897 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -441,15 +441,14 @@
{
u32 cntkctl = arch_timer_get_cntkctl();
- /* Disable user access to the timers and the physical counter */
+ /* Disable user access to the timers */
/* Also disable virtual event stream */
cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
| ARCH_TIMER_USR_VT_ACCESS_EN
- | ARCH_TIMER_VIRT_EVT_EN
- | ARCH_TIMER_USR_PCT_ACCESS_EN);
+ | ARCH_TIMER_VIRT_EVT_EN);
- /* Enable user access to the virtual counter */
- cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
+ /* Enable user access to the virtual and physical counters */
+ cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN | ARCH_TIMER_USR_PCT_ACCESS_EN;
arch_timer_set_cntkctl(cntkctl);
}
diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c
index d7cc425..53c0f8a 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-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
@@ -143,25 +143,27 @@
{ \
struct devfreq *df = to_devfreq(dev); \
struct hwmon_node *hw = df->data; \
- int ret; \
+ int ret, numvals; \
unsigned int i = 0, val; \
+ char **strlist; \
\
- do { \
- ret = kstrtoint(buf, 10, &val); \
+ strlist = argv_split(GFP_KERNEL, buf, &numvals); \
+ if (!strlist) \
+ return -ENOMEM; \
+ numvals = min(numvals, n - 1); \
+ for (i = 0; i < numvals; i++) { \
+ ret = kstrtouint(strlist[i], 10, &val); \
if (ret) \
- break; \
- buf = strnchr(buf, PAGE_SIZE, ' '); \
- if (buf) \
- buf++; \
+ goto out; \
val = max(val, _min); \
val = min(val, _max); \
hw->name[i] = val; \
- i++; \
- } while (buf && i < n - 1); \
- if (i < 1) \
- return -EINVAL; \
+ } \
+ ret = count; \
+out: \
+ argv_free(strlist); \
hw->name[i] = 0; \
- return count; \
+ return ret; \
}
#define gov_list_attr(__attr, n, min, max) \
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 29f00f7..df099d3 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -227,16 +227,18 @@
struct sde_encoder_phys_vid *vid_enc =
to_sde_encoder_phys_vid(phys_enc);
struct intf_prog_fetch f = { 0 };
- struct intf_timing_params *timing = &vid_enc->timing_params;
+ struct intf_timing_params *timing;
u32 vfp_fetch_lines = 0;
u32 horiz_total = 0;
u32 vert_total = 0;
u32 rot_fetch_start_vsync_counter = 0;
unsigned long lock_flags;
- if (WARN_ON_ONCE(!vid_enc->hw_intf->ops.setup_rot_start))
+ if (!phys_enc || !vid_enc->hw_intf ||
+ !vid_enc->hw_intf->ops.setup_rot_start)
return;
+ timing = &vid_enc->timing_params;
vfp_fetch_lines = programmable_fetch_get_num_lines(vid_enc, timing);
if (vfp_fetch_lines && rot_fetch_lines) {
vert_total = get_vertical_total(timing);
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 8662207..93268be 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -1961,21 +1961,29 @@
ret = sde_plane_rot_submit_command(plane, state,
SDE_HW_ROT_CMD_VALIDATE);
- } else if (sde_plane_enabled(state)) {
+ } else {
SDE_DEBUG("plane%d.%d bypass rotator\n", plane->base.id,
rstate->sequence_id);
/* bypass rotator - initialize output setting as input */
+ for (i = 0; i < ARRAY_SIZE(rstate->out_fb_modifier); i++)
+ rstate->out_fb_modifier[i] = state->fb ?
+ state->fb->modifier[i] : 0x0;
+
+ if (state->fb) {
+ rstate->out_fb_pixel_format = state->fb->pixel_format;
+ rstate->out_fb_flags = state->fb->flags;
+ rstate->out_fb_width = state->fb->width;
+ rstate->out_fb_height = state->fb->height;
+ } else {
+ rstate->out_fb_pixel_format = 0x0;
+ rstate->out_fb_flags = 0x0;
+ rstate->out_fb_width = 0;
+ rstate->out_fb_height = 0;
+ }
+
rstate->out_rotation = rstate->in_rotation;
- rstate->out_fb_pixel_format = state->fb->pixel_format;
-
- for (i = 0.; i < ARRAY_SIZE(rstate->out_fb_modifier); i++)
- rstate->out_fb_modifier[i] = state->fb->modifier[i];
-
- rstate->out_fb_flags = state->fb->flags;
- rstate->out_fb_width = state->fb->width;
- rstate->out_fb_height = state->fb->height;
rstate->out_src_x = state->src_x;
rstate->out_src_y = state->src_y;
rstate->out_src_w = state->src_w;
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 94b2e2f9..e233e76 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/clk.h>
+#include <dt-bindings/clock/qcom,aop-qmp.h>
#include <linux/coresight.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
@@ -940,6 +941,14 @@
atomic_t *refcnts = NULL;
struct coresight_device *csdev;
struct coresight_connection *conns = NULL;
+ struct clk *pclk;
+
+ pclk = clk_get(desc->dev, "apb_pclk");
+ if (!IS_ERR(pclk)) {
+ ret = clk_set_rate(pclk, QDSS_CLK_LEVEL_DYNAMIC);
+ if (ret)
+ dev_err(desc->dev, "clk set rate failed\n");
+ }
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
if (!csdev) {
diff --git a/drivers/media/platform/msm/camera/Makefile b/drivers/media/platform/msm/camera/Makefile
index c897669..19de267 100644
--- a/drivers/media/platform/msm/camera/Makefile
+++ b/drivers/media/platform/msm/camera/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_SPECTRA_CAMERA) += cam_utils/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_core/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_sync/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_smmu/
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/Makefile b/drivers/media/platform/msm/camera/cam_req_mgr/Makefile
index f8c864f..87707b1 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/Makefile
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/Makefile
@@ -1 +1,3 @@
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_dev.o cam_req_mgr_util.o cam_req_mgr_core.o cam_req_mgr_workq.o
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_dev.o cam_req_mgr_util.o cam_req_mgr_core.o cam_req_mgr_workq.o cam_mem_mgr.o
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
new file mode 100644
index 0000000..f3ef0e9
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
@@ -0,0 +1,968 @@
+/* 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 pr_fmt(fmt) "CAM-MEM-MGR %s:%d " fmt, __func__, __LINE__
+
+#ifdef CONFIG_MEM_MGR_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/msm_ion.h>
+#include <asm/cacheflush.h>
+
+#include "cam_req_mgr_util.h"
+#include "cam_mem_mgr.h"
+#include "cam_smmu_api.h"
+
+static struct cam_mem_table tbl;
+
+static int cam_mem_util_map_cpu_va(struct ion_handle *hdl,
+ uint64_t *vaddr,
+ size_t *len)
+{
+ *vaddr = (uintptr_t)ion_map_kernel(tbl.client, hdl);
+ if (IS_ERR_OR_NULL((void *)*vaddr)) {
+ pr_err("kernel map fail");
+ return -ENOSPC;
+ }
+
+ if (ion_handle_get_size(tbl.client, hdl, len)) {
+ pr_err("kernel get len failed");
+ ion_unmap_kernel(tbl.client, hdl);
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+static int cam_mem_util_get_dma_dir(uint32_t flags)
+{
+ int rc = -EINVAL;
+
+ if (flags & CAM_MEM_FLAG_HW_READ_ONLY)
+ rc = DMA_TO_DEVICE;
+ else if (flags & CAM_MEM_FLAG_HW_WRITE_ONLY)
+ rc = DMA_FROM_DEVICE;
+ else if (flags & CAM_MEM_FLAG_HW_READ_WRITE)
+ rc = DMA_BIDIRECTIONAL;
+
+ return rc;
+}
+
+static int cam_mem_util_client_create(void)
+{
+ int rc = 0;
+
+ tbl.client = msm_ion_client_create("camera_global_pool");
+ if (IS_ERR_OR_NULL(tbl.client)) {
+ pr_err("fail to create client\n");
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static void cam_mem_util_client_destroy(void)
+{
+ ion_client_destroy(tbl.client);
+ tbl.client = NULL;
+}
+
+int cam_mem_mgr_init(void)
+{
+ int rc;
+ int i;
+ int bitmap_size;
+
+ memset(tbl.bufq, 0, sizeof(tbl.bufq));
+
+ rc = cam_mem_util_client_create();
+ if (rc < 0) {
+ pr_err("fail to create ion client\n");
+ goto client_fail;
+ }
+
+ bitmap_size = BITS_TO_LONGS(CAM_MEM_BUFQ_MAX) * sizeof(long);
+ tbl.bitmap = kzalloc(sizeof(bitmap_size), GFP_KERNEL);
+ if (!tbl.bitmap) {
+ rc = -ENOMEM;
+ goto bitmap_fail;
+ }
+ tbl.bits = bitmap_size * BITS_PER_BYTE;
+ bitmap_zero(tbl.bitmap, tbl.bits);
+ /* We need to reserve slot 0 because 0 is invalid */
+ set_bit(0, tbl.bitmap);
+
+ for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) {
+ tbl.bufq[i].fd = -1;
+ tbl.bufq[i].buf_handle = -1;
+ }
+ mutex_init(&tbl.m_lock);
+ return rc;
+
+bitmap_fail:
+ cam_mem_util_client_destroy();
+client_fail:
+ return rc;
+}
+
+static int cam_mem_mgr_cleanup_table(void)
+{
+ int i;
+
+ mutex_lock(&tbl.m_lock);
+ for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) {
+ if (!tbl.bufq[i].active) {
+ CDBG("Buffer inactive at idx=%d, continuing\n", i);
+ continue;
+ } else {
+ pr_err("Active buffer at idx=%d, possible leak\n", i);
+ }
+
+ mutex_lock(&tbl.bufq[i].q_lock);
+ ion_free(tbl.client, tbl.bufq[i].i_hdl);
+ tbl.bufq[i].fd = -1;
+ tbl.bufq[i].flags = 0;
+ tbl.bufq[i].buf_handle = -1;
+ tbl.bufq[i].vaddr = 0;
+ tbl.bufq[i].len = 0;
+ memset(tbl.bufq[i].hdls, 0,
+ sizeof(int32_t) * tbl.bufq[i].num_hdl);
+ tbl.bufq[i].num_hdl = 0;
+ tbl.bufq[i].i_hdl = NULL;
+ tbl.bufq[i].active = false;
+ mutex_unlock(&tbl.bufq[i].q_lock);
+ mutex_destroy(&tbl.bufq[i].q_lock);
+ }
+ bitmap_zero(tbl.bitmap, tbl.bits);
+ /* We need to reserve slot 0 because 0 is invalid */
+ set_bit(0, tbl.bitmap);
+ mutex_unlock(&tbl.m_lock);
+
+ return 0;
+}
+
+void cam_mem_mgr_deinit(void)
+{
+ cam_mem_mgr_cleanup_table();
+ mutex_lock(&tbl.m_lock);
+ bitmap_zero(tbl.bitmap, tbl.bits);
+ kfree(tbl.bitmap);
+ tbl.bitmap = NULL;
+ cam_mem_util_client_destroy();
+ mutex_unlock(&tbl.m_lock);
+ mutex_destroy(&tbl.m_lock);
+}
+
+static int32_t cam_mem_get_slot(void)
+{
+ int32_t idx;
+
+ mutex_lock(&tbl.m_lock);
+ idx = find_first_zero_bit(tbl.bitmap, tbl.bits);
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
+ mutex_unlock(&tbl.m_lock);
+ return -ENOMEM;
+ }
+
+ set_bit(idx, tbl.bitmap);
+ tbl.bufq[idx].active = true;
+ mutex_init(&tbl.bufq[idx].q_lock);
+ mutex_unlock(&tbl.m_lock);
+
+ return idx;
+}
+
+static void cam_mem_put_slot(int32_t idx)
+{
+ mutex_lock(&tbl.m_lock);
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ tbl.bufq[idx].active = false;
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+ mutex_destroy(&tbl.bufq[idx].q_lock);
+ clear_bit(idx, tbl.bitmap);
+ mutex_unlock(&tbl.m_lock);
+}
+
+int cam_mem_get_io_buf(int32_t buf_handle, int32_t mmu_handle,
+ uint64_t *iova_ptr, size_t *len_ptr)
+{
+ int rc = 0, idx;
+
+ idx = CAM_MEM_MGR_GET_HDL_IDX(buf_handle);
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0)
+ return -EINVAL;
+
+ if (!tbl.bufq[idx].active)
+ return -EINVAL;
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ if (buf_handle != tbl.bufq[idx].buf_handle) {
+ rc = -EINVAL;
+ goto handle_mismatch;
+ }
+
+ rc = cam_smmu_get_iova(mmu_handle,
+ tbl.bufq[idx].fd,
+ iova_ptr,
+ len_ptr);
+ if (rc < 0)
+ pr_err("fail to get buf hdl :%d", buf_handle);
+
+handle_mismatch:
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_mem_get_io_buf);
+
+int cam_mem_get_cpu_buf(int32_t buf_handle, uint64_t *vaddr_ptr, size_t *len)
+{
+ int rc = 0;
+ int idx;
+ struct ion_handle *ion_hdl = NULL;
+ uint64_t kvaddr = 0;
+ size_t klen = 0;
+
+ if (!buf_handle || !vaddr_ptr || !len)
+ return -EINVAL;
+
+ idx = CAM_MEM_MGR_GET_HDL_IDX(buf_handle);
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0)
+ return -EINVAL;
+
+ if (!tbl.bufq[idx].active)
+ return -EPERM;
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ if (buf_handle != tbl.bufq[idx].buf_handle) {
+ rc = -EINVAL;
+ goto exit_func;
+ }
+
+ ion_hdl = tbl.bufq[idx].i_hdl;
+ if (!ion_hdl) {
+ pr_err("Invalid ION handle\n");
+ rc = -EINVAL;
+ goto exit_func;
+ }
+
+ if (tbl.bufq[idx].flags & CAM_MEM_FLAG_KMD_ACCESS) {
+ if (!tbl.bufq[idx].kmdvaddr) {
+ rc = cam_mem_util_map_cpu_va(ion_hdl,
+ &kvaddr, &klen);
+ if (rc)
+ goto exit_func;
+ tbl.bufq[idx].kmdvaddr = kvaddr;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit_func;
+ }
+
+ *vaddr_ptr = tbl.bufq[idx].kmdvaddr;
+ *len = tbl.bufq[idx].len;
+
+exit_func:
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_mem_get_cpu_buf);
+
+int cam_mem_mgr_cache_ops(struct cam_mem_cache_ops_cmd *cmd)
+{
+ int rc = 0, idx;
+ uint32_t ion_cache_ops;
+ unsigned long ion_flag = 0;
+
+ if (!cmd)
+ return -EINVAL;
+
+ idx = CAM_MEM_MGR_GET_HDL_IDX(cmd->buf_handle);
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0)
+ return -EINVAL;
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+
+ if (!tbl.bufq[idx].active) {
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ if (cmd->buf_handle != tbl.bufq[idx].buf_handle) {
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ rc = ion_handle_get_flags(tbl.client, tbl.bufq[idx].i_hdl,
+ &ion_flag);
+ if (rc) {
+ pr_err("cache get flags failed %d\n", rc);
+ goto fail;
+ }
+
+ if (ION_IS_CACHED(ion_flag)) {
+ switch (cmd->mem_cache_ops) {
+ case CAM_MEM_CLEAN_CACHE:
+ ion_cache_ops = ION_IOC_CLEAN_CACHES;
+ break;
+ case CAM_MEM_INV_CACHE:
+ ion_cache_ops = ION_IOC_INV_CACHES;
+ break;
+ case CAM_MEM_CLEAN_INV_CACHE:
+ ion_cache_ops = ION_IOC_CLEAN_INV_CACHES;
+ break;
+ default:
+ pr_err("invalid cache ops :%d", cmd->mem_cache_ops);
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ rc = msm_ion_do_cache_op(tbl.client,
+ tbl.bufq[idx].i_hdl,
+ (void *)tbl.bufq[idx].vaddr,
+ tbl.bufq[idx].len,
+ ion_cache_ops);
+ if (rc)
+ pr_err("cache operation failed %d\n", rc);
+ }
+fail:
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_mem_mgr_cache_ops);
+
+static int cam_mem_util_get_ion_buffer(size_t len,
+ size_t align,
+ unsigned int heap_id_mask,
+ unsigned int flags,
+ struct ion_handle **hdl,
+ int *fd)
+{
+ int rc = 0;
+
+ *hdl = ion_alloc(tbl.client, len, align, heap_id_mask, flags);
+ if (IS_ERR_OR_NULL(*hdl))
+ return -ENOMEM;
+
+ *fd = ion_share_dma_buf_fd(tbl.client, *hdl);
+ if (*fd < 0) {
+ pr_err("dma buf get fd fail");
+ rc = -EINVAL;
+ goto get_fd_fail;
+ }
+
+ return rc;
+
+get_fd_fail:
+ ion_free(tbl.client, *hdl);
+ return rc;
+}
+
+static int cam_mem_util_ion_alloc(struct cam_mem_mgr_alloc_cmd *cmd,
+ struct ion_handle **hdl,
+ int *fd)
+{
+ uint32_t heap_id;
+ uint32_t ion_flag = 0;
+ int rc;
+
+ if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)
+ heap_id = ION_HEAP(ION_SECURE_DISPLAY_HEAP_ID);
+ else
+ heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+
+ if (cmd->flags & CAM_MEM_FLAG_CACHE)
+ ion_flag |= ION_FLAG_CACHED;
+ else
+ ion_flag &= ~ION_FLAG_CACHED;
+
+ rc = cam_mem_util_get_ion_buffer(cmd->len,
+ cmd->align,
+ heap_id,
+ ion_flag,
+ hdl,
+ fd);
+
+ return rc;
+}
+
+
+static int cam_mem_util_check_flags(struct cam_mem_mgr_alloc_cmd *cmd)
+{
+ if (!cmd->flags) {
+ pr_err("Invalid flags\n");
+ return -EINVAL;
+ }
+
+ if (cmd->num_hdl > CAM_MEM_MMU_MAX_HANDLE) {
+ pr_err("Num of mmu hdl exceeded maximum(%d)\n",
+ CAM_MEM_MMU_MAX_HANDLE);
+ return -EINVAL;
+ }
+
+ if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE &&
+ cmd->flags & CAM_MEM_FLAG_KMD_ACCESS) {
+ pr_err("Kernel mapping in secure mode not allowed");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cam_mem_util_check_map_flags(struct cam_mem_mgr_map_cmd *cmd)
+{
+ if (!cmd->flags) {
+ pr_err("Invalid flags\n");
+ return -EINVAL;
+ }
+
+ if (cmd->num_hdl > CAM_MEM_MMU_MAX_HANDLE) {
+ pr_err("Num of mmu hdl exceeded maximum(%d)\n",
+ CAM_MEM_MMU_MAX_HANDLE);
+ return -EINVAL;
+ }
+
+ if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE &&
+ cmd->flags & CAM_MEM_FLAG_KMD_ACCESS) {
+ pr_err("Kernel mapping in secure mode not allowed");
+ return -EINVAL;
+ }
+
+ if (cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) {
+ pr_err("Shared memory buffers are not allowed to be mapped\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cam_mem_util_map_hw_va(uint32_t flags,
+ int32_t *mmu_hdls,
+ int32_t num_hdls,
+ int fd,
+ dma_addr_t *hw_vaddr,
+ size_t *len,
+ enum cam_smmu_region_id region)
+{
+ int i;
+ int rc = -1;
+ int dir = cam_mem_util_get_dma_dir(flags);
+
+ if (dir < 0) {
+ pr_err("fail to map DMA direction\n");
+ return dir;
+ }
+
+ if (flags & CAM_MEM_FLAG_PROTECTED_MODE) {
+ for (i = 0; i < num_hdls; i++) {
+ rc = cam_smmu_map_sec_iova(mmu_hdls[i],
+ fd,
+ dir,
+ (dma_addr_t *)hw_vaddr,
+ len);
+
+ if (rc < 0) {
+ pr_err("Failed to securely map to smmu");
+ goto multi_map_fail;
+ }
+ }
+ } else {
+ for (i = 0; i < num_hdls; i++) {
+ rc = cam_smmu_map_iova(mmu_hdls[i],
+ fd,
+ dir,
+ (dma_addr_t *)hw_vaddr,
+ len,
+ region);
+
+ if (rc < 0) {
+ pr_err("Failed to map to smmu");
+ goto multi_map_fail;
+ }
+ }
+ }
+
+ return rc;
+multi_map_fail:
+ if (flags & CAM_MEM_FLAG_PROTECTED_MODE)
+ for (--i; i > 0; i--)
+ cam_smmu_unmap_sec_iova(mmu_hdls[i], fd);
+ else
+ for (--i; i > 0; i--)
+ cam_smmu_unmap_iova(mmu_hdls[i],
+ fd,
+ CAM_SMMU_REGION_IO);
+ return rc;
+
+}
+
+int cam_mem_mgr_alloc_and_map(struct cam_mem_mgr_alloc_cmd *cmd)
+{
+ int rc;
+ int32_t idx;
+ struct ion_handle *ion_hdl;
+ int ion_fd;
+ dma_addr_t hw_vaddr = 0;
+ size_t len;
+
+ if (!cmd) {
+ pr_err(" Invalid argument\n");
+ return -EINVAL;
+ }
+ len = cmd->len;
+
+ rc = cam_mem_util_check_flags(cmd);
+ if (rc) {
+ pr_err("Invalid flags: flags = %X\n", cmd->flags);
+ return rc;
+ }
+
+ rc = cam_mem_util_ion_alloc(cmd,
+ &ion_hdl,
+ &ion_fd);
+ if (rc) {
+ pr_err("Ion allocation failed\n");
+ return rc;
+ }
+
+ idx = cam_mem_get_slot();
+ if (idx < 0) {
+ rc = -ENOMEM;
+ goto slot_fail;
+ }
+
+ if (cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE ||
+ cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) {
+
+ enum cam_smmu_region_id region;
+
+ if (cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE)
+ region = CAM_SMMU_REGION_IO;
+
+
+ if (cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS)
+ region = CAM_SMMU_REGION_SHARED;
+
+ rc = cam_mem_util_map_hw_va(cmd->flags,
+ cmd->mmu_hdls,
+ cmd->num_hdl,
+ ion_fd,
+ &hw_vaddr,
+ &len,
+ region);
+ if (rc)
+ goto map_hw_fail;
+ }
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ tbl.bufq[idx].fd = ion_fd;
+ tbl.bufq[idx].flags = cmd->flags;
+ tbl.bufq[idx].buf_handle = GET_MEM_HANDLE(idx, ion_fd);
+ tbl.bufq[idx].kmdvaddr = 0;
+
+ if (cmd->num_hdl > 0)
+ tbl.bufq[idx].vaddr = hw_vaddr;
+ else
+ tbl.bufq[idx].vaddr = 0;
+
+ tbl.bufq[idx].i_hdl = ion_hdl;
+ tbl.bufq[idx].len = cmd->len;
+ tbl.bufq[idx].num_hdl = cmd->num_hdl;
+ memcpy(tbl.bufq[idx].hdls, cmd->mmu_hdls,
+ sizeof(int32_t) * cmd->num_hdl);
+ tbl.bufq[idx].is_imported = false;
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+
+ cmd->out.buf_handle = tbl.bufq[idx].buf_handle;
+ cmd->out.fd = tbl.bufq[idx].fd;
+ cmd->out.vaddr = 0;
+
+ CDBG("buf handle: %x, fd: %d, len: %zu\n",
+ cmd->out.buf_handle, cmd->out.fd,
+ tbl.bufq[idx].len);
+
+ return rc;
+
+map_hw_fail:
+ cam_mem_put_slot(idx);
+slot_fail:
+ ion_free(tbl.client, ion_hdl);
+ return rc;
+}
+
+int cam_mem_mgr_map(struct cam_mem_mgr_map_cmd *cmd)
+{
+ int32_t idx;
+ int rc;
+ struct ion_handle *ion_hdl;
+ dma_addr_t hw_vaddr = 0;
+ size_t len = 0;
+
+ if (!cmd || (cmd->fd < 0)) {
+ pr_err("Invalid argument\n");
+ return -EINVAL;
+ }
+
+ if (cmd->num_hdl > CAM_MEM_MMU_MAX_HANDLE)
+ return -EINVAL;
+
+ rc = cam_mem_util_check_map_flags(cmd);
+ if (rc) {
+ pr_err("Invalid flags: flags = %X\n", cmd->flags);
+ return rc;
+ }
+
+ ion_hdl = ion_import_dma_buf_fd(tbl.client, cmd->fd);
+ if (IS_ERR_OR_NULL((void *)(ion_hdl))) {
+ pr_err("Failed to import ion fd\n");
+ return -EINVAL;
+ }
+
+ if (cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) {
+ rc = cam_mem_util_map_hw_va(cmd->flags,
+ cmd->mmu_hdls,
+ cmd->num_hdl,
+ cmd->fd,
+ &hw_vaddr,
+ &len,
+ CAM_SMMU_REGION_IO);
+ if (rc)
+ goto map_fail;
+ }
+
+ idx = cam_mem_get_slot();
+ if (idx < 0) {
+ rc = -ENOMEM;
+ goto map_fail;
+ }
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ tbl.bufq[idx].fd = cmd->fd;
+ tbl.bufq[idx].flags = cmd->flags;
+ tbl.bufq[idx].buf_handle = GET_MEM_HANDLE(idx, cmd->fd);
+ tbl.bufq[idx].kmdvaddr = 0;
+
+ if (cmd->num_hdl > 0)
+ tbl.bufq[idx].vaddr = hw_vaddr;
+ else
+ tbl.bufq[idx].vaddr = 0;
+
+ tbl.bufq[idx].i_hdl = ion_hdl;
+ tbl.bufq[idx].len = len;
+ tbl.bufq[idx].num_hdl = cmd->num_hdl;
+ memcpy(tbl.bufq[idx].hdls, cmd->mmu_hdls,
+ sizeof(int32_t) * cmd->num_hdl);
+ tbl.bufq[idx].is_imported = true;
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+
+ cmd->out.buf_handle = tbl.bufq[idx].buf_handle;
+ cmd->out.vaddr = 0;
+
+ return rc;
+
+map_fail:
+ ion_free(tbl.client, ion_hdl);
+ return rc;
+}
+
+static int cam_mem_util_unmap_hw_va(int32_t idx,
+ enum cam_smmu_region_id region)
+{
+ int i;
+ uint32_t flags;
+ int32_t *mmu_hdls;
+ int num_hdls;
+ int fd;
+ int rc = -EINVAL;
+
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
+ pr_err("Incorrect index\n");
+ return rc;
+ }
+
+ flags = tbl.bufq[idx].flags;
+ mmu_hdls = tbl.bufq[idx].hdls;
+ num_hdls = tbl.bufq[idx].num_hdl;
+ fd = tbl.bufq[idx].fd;
+
+ if (flags & CAM_MEM_FLAG_PROTECTED_MODE) {
+ for (i = 0; i < num_hdls; i++) {
+ rc = cam_smmu_unmap_sec_iova(mmu_hdls[i], fd);
+ if (rc < 0)
+ goto unmap_end;
+ }
+ } else {
+ for (i = 0; i < num_hdls; i++) {
+ rc = cam_smmu_unmap_iova(mmu_hdls[i],
+ fd,
+ region);
+ if (rc < 0)
+ goto unmap_end;
+ }
+ }
+
+unmap_end:
+ return rc;
+}
+
+static int cam_mem_util_unmap(int32_t idx)
+{
+ int rc = 0;
+ enum cam_smmu_region_id region;
+
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
+ pr_err("Incorrect index\n");
+ return -EINVAL;
+ }
+
+ CDBG("Flags = %X\n", tbl.bufq[idx].flags);
+
+ if (tbl.bufq[idx].flags & CAM_MEM_FLAG_KMD_ACCESS)
+ if (tbl.bufq[idx].i_hdl && tbl.bufq[idx].kmdvaddr)
+ ion_unmap_kernel(tbl.client, tbl.bufq[idx].i_hdl);
+
+ if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE)
+ region = CAM_SMMU_REGION_IO;
+
+ if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS)
+ region = CAM_SMMU_REGION_SHARED;
+
+ rc = cam_mem_util_unmap_hw_va(idx,
+ region);
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ tbl.bufq[idx].flags = 0;
+ tbl.bufq[idx].buf_handle = -1;
+ tbl.bufq[idx].vaddr = 0;
+ memset(tbl.bufq[idx].hdls, 0,
+ sizeof(int32_t) * CAM_MEM_MMU_MAX_HANDLE);
+
+ CDBG("Ion handle at idx = %d freeing = %pK, fd = %d\n",
+ idx, tbl.bufq[idx].i_hdl, tbl.bufq[idx].fd);
+
+ if (tbl.bufq[idx].i_hdl && !tbl.bufq[idx].is_imported) {
+ CDBG("Freeing up non-imported buffer at fd = %d, hdl = %pK",
+ tbl.bufq[idx].fd,
+ tbl.bufq[idx].i_hdl);
+ ion_free(tbl.client, tbl.bufq[idx].i_hdl);
+ tbl.bufq[idx].i_hdl = NULL;
+ } else {
+ CDBG("Not freeing up imported buffer at fd = %d",
+ tbl.bufq[idx].fd);
+ }
+
+ tbl.bufq[idx].fd = -1;
+ tbl.bufq[idx].is_imported = false;
+ tbl.bufq[idx].i_hdl = NULL;
+ tbl.bufq[idx].len = 0;
+ tbl.bufq[idx].num_hdl = 0;
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+ cam_mem_put_slot(idx);
+
+ return rc;
+}
+
+int cam_mem_mgr_release(struct cam_mem_mgr_release_cmd *cmd)
+{
+ int idx;
+ int rc;
+
+ if (!cmd) {
+ pr_err("Invalid argument\n");
+ return -EINVAL;
+ }
+
+ idx = CAM_MEM_MGR_GET_HDL_IDX(cmd->buf_handle);
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
+ pr_err("Incorrect index extracted from mem handle\n");
+ return -EINVAL;
+ }
+
+ if (!tbl.bufq[idx].active) {
+ pr_err("Released buffer state should be active\n");
+ return -EINVAL;
+ }
+
+ if (tbl.bufq[idx].buf_handle != cmd->buf_handle) {
+ pr_err("Released buf handle not matching within table\n");
+ return -EINVAL;
+ }
+
+ CDBG("Releasing hdl = %u\n", cmd->buf_handle);
+ rc = cam_mem_util_unmap(idx);
+
+ return rc;
+}
+
+int cam_mem_mgr_request_mem(struct cam_mem_mgr_request_desc *inp,
+ struct cam_mem_mgr_memory_desc *out)
+{
+ struct ion_handle *hdl;
+ int ion_fd;
+ int rc = 0;
+ uint32_t heap_id;
+ int32_t ion_flag = 0;
+ uint64_t kvaddr;
+ dma_addr_t iova = 0;
+ size_t request_len = 0;
+ int32_t idx;
+ uint32_t mem_handle;
+ int32_t smmu_hdl = 0;
+ int32_t num_hdl = 0;
+ enum cam_smmu_region_id region;
+
+ if (!inp || !out) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (inp->region != CAM_MEM_MGR_REGION_SHARED &&
+ inp->region != CAM_MEM_MGR_REGION_NON_SECURE_IO) {
+ pr_err("Invalid flags for request mem\n");
+ return -EINVAL;
+ }
+
+ if (inp->flags & CAM_MEM_FLAG_CACHE)
+ ion_flag |= ION_FLAG_CACHED;
+ else
+ ion_flag &= ~ION_FLAG_CACHED;
+
+ heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+
+ rc = cam_mem_util_get_ion_buffer(inp->size,
+ inp->align,
+ heap_id,
+ ion_flag,
+ &hdl,
+ &ion_fd);
+
+ if (rc) {
+ pr_err("ION alloc failed for shared buffer\n");
+ goto ion_fail;
+ } else {
+ CDBG("Got ION fd = %d, hdl = %pK\n", ion_fd, hdl);
+ }
+
+ rc = cam_mem_util_map_cpu_va(hdl, &kvaddr, &request_len);
+ if (rc) {
+ pr_err("Failed to get kernel vaddr\n");
+ goto map_fail;
+ }
+
+ if (!inp->smmu_hdl) {
+ pr_err("Invalid SMMU handle\n");
+ rc = -EINVAL;
+ goto smmu_fail;
+ }
+
+ if (inp->region == CAM_MEM_MGR_REGION_SHARED)
+ region = CAM_SMMU_REGION_SHARED;
+
+ if (inp->region == CAM_MEM_MGR_REGION_NON_SECURE_IO)
+ region = CAM_SMMU_REGION_IO;
+
+ rc = cam_smmu_map_iova(inp->smmu_hdl,
+ ion_fd,
+ CAM_SMMU_MAP_RW,
+ &iova,
+ &request_len,
+ region);
+
+ if (rc < 0) {
+ pr_err("SMMU mapping failed\n");
+ goto smmu_fail;
+ }
+
+ smmu_hdl = inp->smmu_hdl;
+ num_hdl = 1;
+
+ idx = cam_mem_get_slot();
+ if (idx < 0) {
+ rc = -ENOMEM;
+ goto slot_fail;
+ }
+
+ mutex_lock(&tbl.bufq[idx].q_lock);
+ mem_handle = GET_MEM_HANDLE(idx, ion_fd);
+ tbl.bufq[idx].fd = ion_fd;
+ tbl.bufq[idx].flags = inp->flags;
+ tbl.bufq[idx].buf_handle = mem_handle;
+ tbl.bufq[idx].kmdvaddr = kvaddr;
+
+ tbl.bufq[idx].vaddr = iova;
+
+ tbl.bufq[idx].i_hdl = hdl;
+ tbl.bufq[idx].len = inp->size;
+ tbl.bufq[idx].num_hdl = num_hdl;
+ memcpy(tbl.bufq[idx].hdls, &smmu_hdl,
+ sizeof(int32_t));
+ tbl.bufq[idx].is_imported = false;
+ mutex_unlock(&tbl.bufq[idx].q_lock);
+
+ out->kva = kvaddr;
+ out->iova = (uint32_t)iova;
+ out->smmu_hdl = smmu_hdl;
+ out->mem_handle = mem_handle;
+ out->len = inp->size;
+ out->region = inp->region;
+
+ return rc;
+slot_fail:
+ cam_smmu_unmap_iova(inp->smmu_hdl,
+ ion_fd,
+ inp->region);
+smmu_fail:
+ ion_unmap_kernel(tbl.client, hdl);
+map_fail:
+ ion_free(tbl.client, hdl);
+ion_fail:
+ return rc;
+}
+EXPORT_SYMBOL(cam_mem_mgr_request_mem);
+
+int cam_mem_mgr_release_mem(struct cam_mem_mgr_memory_desc *inp)
+{
+ int32_t idx;
+ int rc;
+
+ if (!inp) {
+ pr_err("Invalid argument\n");
+ return -EINVAL;
+ }
+
+ idx = CAM_MEM_MGR_GET_HDL_IDX(inp->mem_handle);
+ if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
+ pr_err("Incorrect index extracted from mem handle\n");
+ return -EINVAL;
+ }
+
+ if (!tbl.bufq[idx].active) {
+ pr_err("Released buffer state should be active\n");
+ return -EINVAL;
+ }
+
+ if (tbl.bufq[idx].buf_handle != inp->mem_handle) {
+ pr_err("Released buf handle not matching within table\n");
+ return -EINVAL;
+ }
+
+ CDBG("Releasing hdl = %X\n", inp->mem_handle);
+ rc = cam_mem_util_unmap(idx);
+
+ return rc;
+}
+EXPORT_SYMBOL(cam_mem_mgr_release_mem);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h
new file mode 100644
index 0000000..c5f839b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h
@@ -0,0 +1,121 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_MEM_MGR_H_
+#define _CAM_MEM_MGR_H_
+
+#include <media/cam_req_mgr.h>
+#include "cam_mem_mgr_api.h"
+
+#define CAM_MEM_BUFQ_MAX 1024
+
+/**
+ * struct cam_mem_buf_queue
+ *
+ * @i_hdl: ion handle for the buffer
+ * @q_lock: mutex lock for buffer
+ * @hdls: list of mapped handles
+ * @num_hdl: number of handles
+ * @fd: file descriptor of buffer
+ * @buf_handle: unique handle for buffer
+ * @align: alignment for allocation
+ * @len: size of buffer
+ * @flags: attributes of buffer
+ * @vaddr: IOVA of buffer
+ * @kmdvaddr: Kernel virtual address
+ * @active: state of the buffer
+ * @is_imported: Flag indicating if buffer is imported from an FD in user space
+ */
+struct cam_mem_buf_queue {
+ struct ion_handle *i_hdl;
+ struct mutex q_lock;
+ int32_t hdls[CAM_MEM_MMU_MAX_HANDLE];
+ int32_t num_hdl;
+ int32_t fd;
+ int32_t buf_handle;
+ int32_t align;
+ size_t len;
+ uint32_t flags;
+ uint64_t vaddr;
+ uint64_t kmdvaddr;
+ bool active;
+ bool is_imported;
+};
+
+/**
+ * struct cam_mem_table
+ *
+ * @m_lock: mutex lock for table
+ * @bitmap: bitmap of the mem mgr utility
+ * @bits: max bits of the utility
+ * @client: ion client pointer
+ * @bufq: array of buffers
+ */
+struct cam_mem_table {
+ struct mutex m_lock;
+ void *bitmap;
+ size_t bits;
+ struct ion_client *client;
+ struct cam_mem_buf_queue bufq[CAM_MEM_BUFQ_MAX];
+};
+
+/**
+ * @brief: Allocates and maps buffer
+ *
+ * @cmd: Allocation information
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_alloc_and_map(struct cam_mem_mgr_alloc_cmd *cmd);
+
+/**
+ * @brief: Releases a buffer reference
+ *
+ * @cmd: Buffer release information
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_release(struct cam_mem_mgr_release_cmd *cmd);
+
+/**
+ * @brief Maps a buffer
+ *
+ * @cmd: Buffer mapping information
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_map(struct cam_mem_mgr_map_cmd *cmd);
+
+/**
+ * @brief: Perform cache ops on the buffer
+ *
+ * @cmd: Cache ops information
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_cache_ops(struct cam_mem_cache_ops_cmd *cmd);
+
+/**
+ * @brief: Initializes the memory manager
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_init(void);
+
+/**
+ * @brief: Tears down the memory manager
+ *
+ * @return None
+ */
+void cam_mem_mgr_deinit(void);
+
+#endif /* _CAM_MEM_MGR_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h
new file mode 100644
index 0000000..32a754e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h
@@ -0,0 +1,105 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_MEM_MGR_API_H_
+#define _CAM_MEM_MGR_API_H_
+
+#include <media/cam_req_mgr.h>
+
+/* Region IDs for memory manager */
+#define CAM_MEM_MGR_REGION_FIRMWARE 0
+#define CAM_MEM_MGR_REGION_SHARED 1
+#define CAM_MEM_MGR_REGION_NON_SECURE_IO 2
+#define CAM_MEM_MGR_REGION_SECURE_IO 3
+#define CAM_MEM_MGR_REGION_SCRATCH 4
+
+/**
+ * struct cam_mem_mgr_request_desc
+ *
+ * @size : Size of memory requested for allocation
+ * @align : Alignment of requested memory
+ * @smmu_hdl: SMMU handle to identify context bank where memory will be mapped
+ * @flags : Flags to indicate cached/uncached property
+ * @region : Region where memory should be allocated
+ */
+struct cam_mem_mgr_request_desc {
+ uint64_t size;
+ uint64_t align;
+ int32_t smmu_hdl;
+ uint32_t flags;
+ uint32_t region;
+};
+
+/**
+ * struct cam_mem_mgr_memory_desc
+ *
+ * @kva : Kernel virtual address of allocated memory
+ * @iova : IOVA of allocated memory
+ * @smmu_hdl : SMMU handle of allocated memory
+ * @mem_handle : Mem handle identifying allocated memory
+ * @len : Length of allocated memory
+ * @region : Region to which allocated memory belongs
+ */
+struct cam_mem_mgr_memory_desc {
+ uint64_t kva;
+ uint32_t iova;
+ int32_t smmu_hdl;
+ uint32_t mem_handle;
+ uint64_t len;
+ uint32_t region;
+};
+
+/**
+ * @brief: Requests a memory buffer
+ *
+ * @inp: Information specifying requested buffer properties
+ * @out: Information about allocated buffer
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_request_mem(struct cam_mem_mgr_request_desc *inp,
+ struct cam_mem_mgr_memory_desc *out);
+
+/**
+ * @brief: Releases a memory buffer
+ *
+ * @inp: Information specifying buffer to be released
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_release_mem(struct cam_mem_mgr_memory_desc *inp);
+
+/**
+ * @brief: Returns IOVA information about buffer
+ *
+ * @buf_handle: Handle of the buffer
+ * @mmu_handle: SMMU handle where buffer is mapped
+ * @iova_ptr : Pointer to mmu's iova
+ * @len_ptr : Length of the buffer
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_get_io_buf(int32_t buf_handle, int32_t mmu_handle,
+ uint64_t *iova_ptr, size_t *len_ptr);
+/**
+ * @brief: Returns CPU address information about buffer
+ *
+ * @buf_handle: Handle for the buffer
+ * @vaddr_ptr : pointer to kernel virtual address
+ * @len_ptr : Length of the buffer
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_get_cpu_buf(int32_t buf_handle, uint64_t *vaddr_ptr,
+ size_t *len);
+
+#endif /* _CAM_MEM_MGR_API_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index f3af1bd..43b020c6 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
#include "cam_req_mgr_util.h"
#include "cam_req_mgr_core.h"
#include "cam_subdev.h"
+#include "cam_mem_mgr.h"
#define CAM_REQ_MGR_EVENT_MAX 30
@@ -115,7 +116,18 @@
spin_unlock_bh(&g_dev.cam_eventq_lock);
g_dev.open_cnt++;
+ rc = cam_mem_mgr_init();
+ if (rc) {
+ g_dev.open_cnt--;
+ pr_err("mem mgr init failed\n");
+ goto mem_mgr_init_fail;
+ }
+ mutex_unlock(&g_dev.cam_lock);
+ return rc;
+
+mem_mgr_init_fail:
+ v4l2_fh_release(filep);
end:
mutex_unlock(&g_dev.cam_lock);
return rc;
@@ -154,6 +166,7 @@
spin_unlock_bh(&g_dev.cam_eventq_lock);
cam_req_mgr_util_free_hdls();
+ cam_mem_mgr_deinit();
mutex_unlock(&g_dev.cam_lock);
return 0;
@@ -316,6 +329,84 @@
rc = cam_req_mgr_sync_mode(&sync_mode);
}
break;
+ case CAM_REQ_MGR_ALLOC_BUF: {
+ struct cam_mem_mgr_alloc_cmd cmd;
+
+ if (k_ioctl->size != sizeof(cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd,
+ (void *)k_ioctl->handle,
+ k_ioctl->size)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = cam_mem_mgr_alloc_and_map(&cmd);
+ if (!rc)
+ if (copy_to_user((void *)k_ioctl->handle,
+ &cmd, k_ioctl->size)) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ break;
+ case CAM_REQ_MGR_MAP_BUF: {
+ struct cam_mem_mgr_map_cmd cmd;
+
+ if (k_ioctl->size != sizeof(cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd,
+ (void *)k_ioctl->handle,
+ k_ioctl->size)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = cam_mem_mgr_map(&cmd);
+ if (!rc)
+ if (copy_to_user((void *)k_ioctl->handle,
+ &cmd, k_ioctl->size)) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ break;
+ case CAM_REQ_MGR_RELEASE_BUF: {
+ struct cam_mem_mgr_release_cmd cmd;
+
+ if (k_ioctl->size != sizeof(cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd,
+ (void *)k_ioctl->handle,
+ k_ioctl->size)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = cam_mem_mgr_release(&cmd);
+ }
+ break;
+ case CAM_REQ_MGR_CACHE_OPS: {
+ struct cam_mem_cache_ops_cmd cmd;
+
+ if (k_ioctl->size != sizeof(cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd,
+ (void *)k_ioctl->handle,
+ k_ioctl->size)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = cam_mem_mgr_cache_ops(&cmd);
+ if (rc)
+ rc = -EINVAL;
+ }
+ break;
default:
return -ENOIOCTLCMD;
}
@@ -444,6 +535,7 @@
static int cam_req_mgr_remove(struct platform_device *pdev)
{
cam_req_mgr_core_device_deinit();
+ cam_mem_mgr_deinit();
cam_req_mgr_util_deinit();
cam_media_device_cleanup();
cam_video_device_cleanup();
@@ -482,6 +574,12 @@
goto req_mgr_util_fail;
}
+ rc = cam_mem_mgr_init();
+ if (rc) {
+ pr_err("mem mgr init failed\n");
+ goto mem_mgr_init_fail;
+ }
+
rc = cam_req_mgr_core_device_init();
if (rc) {
pr_err("core device setup failed\n");
@@ -493,8 +591,12 @@
return rc;
req_mgr_core_fail:
+ cam_mem_mgr_deinit();
+mem_mgr_init_fail:
cam_req_mgr_util_deinit();
req_mgr_util_fail:
+ mutex_destroy(&g_dev.dev_lock);
+ mutex_destroy(&g_dev.cam_lock);
cam_video_device_cleanup();
video_setup_fail:
cam_media_device_cleanup();
diff --git a/drivers/media/platform/msm/camera/cam_smmu/Makefile b/drivers/media/platform/msm/camera/cam_smmu/Makefile
new file mode 100644
index 0000000..3619da7
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_smmu/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_smmu_api.o
diff --git a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
new file mode 100644
index 0000000..f4215b5
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
@@ -0,0 +1,2284 @@
+/* Copyright (c) 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
+ * 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) "CAM-SMMU %s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/dma-buf.h>
+#include <asm/dma-iommu.h>
+#include <linux/dma-direction.h>
+#include <linux/of_platform.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_dma_iommu_mapping.h>
+#include <linux/workqueue.h>
+#include <linux/genalloc.h>
+
+#include "cam_smmu_api.h"
+
+#define SHARED_MEM_POOL_GRANULARITY 12
+
+#define IOMMU_INVALID_DIR -1
+#define BYTE_SIZE 8
+#define COOKIE_NUM_BYTE 2
+#define COOKIE_SIZE (BYTE_SIZE*COOKIE_NUM_BYTE)
+#define COOKIE_MASK ((1<<COOKIE_SIZE)-1)
+#define HANDLE_INIT (-1)
+#define CAM_SMMU_CB_MAX 2
+
+#define GET_SMMU_HDL(x, y) (((x) << COOKIE_SIZE) | ((y) & COOKIE_MASK))
+#define GET_SMMU_TABLE_IDX(x) (((x) >> COOKIE_SIZE) & COOKIE_MASK)
+
+#ifdef CONFIG_CAM_SMMU_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+struct firmware_alloc_info {
+ struct device *fw_dev;
+ void *fw_kva;
+ dma_addr_t fw_dma_hdl;
+};
+
+struct firmware_alloc_info icp_fw;
+
+struct cam_smmu_work_payload {
+ int idx;
+ struct iommu_domain *domain;
+ struct device *dev;
+ unsigned long iova;
+ int flags;
+ void *token;
+ struct list_head list;
+};
+
+enum cam_protection_type {
+ CAM_PROT_INVALID,
+ CAM_NON_SECURE,
+ CAM_SECURE,
+ CAM_PROT_MAX,
+};
+
+enum cam_iommu_type {
+ CAM_SMMU_INVALID,
+ CAM_QSMMU,
+ CAM_ARM_SMMU,
+ CAM_SMMU_MAX,
+};
+
+enum cam_smmu_buf_state {
+ CAM_SMMU_BUFF_EXIST,
+ CAM_SMMU_BUFF_NOT_EXIST
+};
+
+enum cam_smmu_init_dir {
+ CAM_SMMU_TABLE_INIT,
+ CAM_SMMU_TABLE_DEINIT,
+};
+
+struct scratch_mapping {
+ void *bitmap;
+ size_t bits;
+ unsigned int order;
+ dma_addr_t base;
+};
+
+struct cam_smmu_region_info {
+ dma_addr_t iova_start;
+ size_t iova_len;
+};
+
+struct cam_context_bank_info {
+ struct device *dev;
+ struct dma_iommu_mapping *mapping;
+ dma_addr_t va_start;
+ size_t va_len;
+ const char *name;
+ bool is_secure;
+ uint8_t scratch_buf_support;
+ uint8_t firmware_support;
+ uint8_t shared_support;
+ uint8_t io_support;
+ bool is_fw_allocated;
+
+ struct scratch_mapping scratch_map;
+ struct gen_pool *shared_mem_pool;
+
+ struct cam_smmu_region_info scratch_info;
+ struct cam_smmu_region_info firmware_info;
+ struct cam_smmu_region_info shared_info;
+ struct cam_smmu_region_info io_info;
+
+ struct list_head smmu_buf_list;
+ struct mutex lock;
+ int handle;
+ enum cam_smmu_ops_param state;
+
+ void (*handler[CAM_SMMU_CB_MAX])(struct iommu_domain *,
+ struct device *, unsigned long,
+ int, void*);
+ void *token[CAM_SMMU_CB_MAX];
+ int cb_count;
+};
+
+struct cam_iommu_cb_set {
+ struct cam_context_bank_info *cb_info;
+ u32 cb_num;
+ u32 cb_init_count;
+ struct work_struct smmu_work;
+ struct mutex payload_list_lock;
+ struct list_head payload_list;
+};
+
+static const struct of_device_id msm_cam_smmu_dt_match[] = {
+ { .compatible = "qcom,msm-cam-smmu", },
+ { .compatible = "qcom,msm-cam-smmu-cb", },
+ { .compatible = "qcom,msm-cam-smmu-fw-dev", },
+ {}
+};
+
+struct cam_dma_buff_info {
+ struct dma_buf *buf;
+ struct dma_buf_attachment *attach;
+ struct sg_table *table;
+ enum dma_data_direction dir;
+ enum cam_smmu_region_id region_id;
+ int iommu_dir;
+ int ref_count;
+ dma_addr_t paddr;
+ struct list_head list;
+ int ion_fd;
+ size_t len;
+ size_t phys_len;
+};
+
+static struct cam_iommu_cb_set iommu_cb_set;
+
+static enum dma_data_direction cam_smmu_translate_dir(
+ enum cam_smmu_map_dir dir);
+
+static int cam_smmu_check_handle_unique(int hdl);
+
+static int cam_smmu_create_iommu_handle(int idx);
+
+static int cam_smmu_create_add_handle_in_table(char *name,
+ int *hdl);
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx,
+ int ion_fd);
+
+static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map,
+ dma_addr_t base, size_t size,
+ int order);
+
+static int cam_smmu_alloc_scratch_va(struct scratch_mapping *mapping,
+ size_t size,
+ dma_addr_t *iova);
+
+static int cam_smmu_free_scratch_va(struct scratch_mapping *mapping,
+ dma_addr_t addr, size_t size);
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_virt_address(int idx,
+ dma_addr_t virt_addr);
+
+static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
+ enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr,
+ enum cam_smmu_region_id region_id);
+
+static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx,
+ size_t virt_len,
+ size_t phys_len,
+ unsigned int iommu_dir,
+ dma_addr_t *virt_addr);
+
+static int cam_smmu_unmap_buf_and_remove_from_list(
+ struct cam_dma_buff_info *mapping_info, int idx);
+
+static int cam_smmu_free_scratch_buffer_remove_from_list(
+ struct cam_dma_buff_info *mapping_info,
+ int idx);
+
+static void cam_smmu_clean_buffer_list(int idx);
+
+static void cam_smmu_print_list(int idx);
+
+static void cam_smmu_print_table(void);
+
+static int cam_smmu_probe(struct platform_device *pdev);
+
+static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr);
+
+static void cam_smmu_page_fault_work(struct work_struct *work)
+{
+ int j;
+ int idx;
+ struct cam_smmu_work_payload *payload;
+
+ mutex_lock(&iommu_cb_set.payload_list_lock);
+ if (list_empty(&iommu_cb_set.payload_list)) {
+ pr_err("Payload list empty\n");
+ mutex_unlock(&iommu_cb_set.payload_list_lock);
+ return;
+ }
+
+ payload = list_first_entry(&iommu_cb_set.payload_list,
+ struct cam_smmu_work_payload,
+ list);
+ list_del(&payload->list);
+ mutex_unlock(&iommu_cb_set.payload_list_lock);
+
+ /* Dereference the payload to call the handler */
+ idx = payload->idx;
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ cam_smmu_check_vaddr_in_range(idx, (void *)payload->iova);
+ for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
+ if ((iommu_cb_set.cb_info[idx].handler[j])) {
+ iommu_cb_set.cb_info[idx].handler[j](
+ payload->domain,
+ payload->dev,
+ payload->iova,
+ payload->flags,
+ iommu_cb_set.cb_info[idx].token[j]);
+ }
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ kfree(payload);
+}
+
+static void cam_smmu_print_list(int idx)
+{
+ struct cam_dma_buff_info *mapping;
+
+ pr_err("index = %d\n", idx);
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ pr_err("ion_fd = %d, paddr= 0x%pK, len = %u, region = %d\n",
+ mapping->ion_fd, (void *)mapping->paddr,
+ (unsigned int)mapping->len,
+ mapping->region_id);
+ }
+}
+
+static void cam_smmu_print_table(void)
+{
+ int i;
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ pr_err("i= %d, handle= %d, name_addr=%pK\n", i,
+ (int)iommu_cb_set.cb_info[i].handle,
+ (void *)iommu_cb_set.cb_info[i].name);
+ pr_err("dev = %pK\n", iommu_cb_set.cb_info[i].dev);
+ }
+}
+
+static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
+{
+ struct cam_dma_buff_info *mapping;
+ unsigned long start_addr, end_addr, current_addr;
+
+ current_addr = (unsigned long)vaddr;
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ start_addr = (unsigned long)mapping->paddr;
+ end_addr = (unsigned long)mapping->paddr + mapping->len;
+
+ if (start_addr <= current_addr && current_addr < end_addr) {
+ pr_err("va %pK valid: range:%pK-%pK, fd = %d cb: %s\n",
+ vaddr, (void *)start_addr, (void *)end_addr,
+ mapping->ion_fd,
+ iommu_cb_set.cb_info[idx].name);
+ goto end;
+ } else {
+ CDBG("va %pK is not in this range: %pK-%pK, fd = %d\n",
+ vaddr, (void *)start_addr, (void *)end_addr,
+ mapping->ion_fd);
+ }
+ }
+ pr_err("Cannot find vaddr:%pK in SMMU %s uses invalid virt address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
+end:
+ return;
+}
+
+void cam_smmu_reg_client_page_fault_handler(int handle,
+ void (*client_page_fault_handler)(struct iommu_domain *,
+ struct device *, unsigned long,
+ int, void*), void *token)
+{
+ int idx, i = 0;
+
+ if (!token || (handle == HANDLE_INIT)) {
+ pr_err("Error: token is NULL or invalid handle\n");
+ return;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return;
+ }
+
+ if (client_page_fault_handler) {
+ if (iommu_cb_set.cb_info[idx].cb_count == CAM_SMMU_CB_MAX) {
+ pr_err("%s Should not regiester more handlers\n",
+ iommu_cb_set.cb_info[idx].name);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return;
+ }
+ iommu_cb_set.cb_info[idx].cb_count++;
+ for (i = 0; i < iommu_cb_set.cb_info[idx].cb_count; i++) {
+ if (iommu_cb_set.cb_info[idx].token[i] == NULL) {
+ iommu_cb_set.cb_info[idx].token[i] = token;
+ iommu_cb_set.cb_info[idx].handler[i] =
+ client_page_fault_handler;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < CAM_SMMU_CB_MAX; i++) {
+ if (iommu_cb_set.cb_info[idx].token[i] == token) {
+ iommu_cb_set.cb_info[idx].token[i] = NULL;
+ iommu_cb_set.cb_info[idx].handler[i] =
+ NULL;
+ iommu_cb_set.cb_info[idx].cb_count--;
+ break;
+ }
+ }
+ if (i == CAM_SMMU_CB_MAX)
+ pr_err("Error: hdl %x no matching tokens: %s\n",
+ handle, iommu_cb_set.cb_info[idx].name);
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+}
+
+static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova,
+ int flags, void *token)
+{
+ char *cb_name;
+ int idx;
+ struct cam_smmu_work_payload *payload;
+
+ if (!token) {
+ pr_err("Error: token is NULL\n");
+ pr_err("Error: domain = %pK, device = %pK\n", domain, dev);
+ pr_err("iova = %lX, flags = %d\n", iova, flags);
+ return 0;
+ }
+
+ cb_name = (char *)token;
+ /* Check whether it is in the table */
+ for (idx = 0; idx < iommu_cb_set.cb_num; idx++) {
+ if (!strcmp(iommu_cb_set.cb_info[idx].name, cb_name))
+ break;
+ }
+
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: index is not valid, index = %d, token = %s\n",
+ idx, cb_name);
+ return 0;
+ }
+
+ payload = kzalloc(sizeof(struct cam_smmu_work_payload), GFP_ATOMIC);
+ if (!payload)
+ return 0;
+
+ payload->domain = domain;
+ payload->dev = dev;
+ payload->iova = iova;
+ payload->flags = flags;
+ payload->token = token;
+ payload->idx = idx;
+
+ mutex_lock(&iommu_cb_set.payload_list_lock);
+ list_add_tail(&payload->list, &iommu_cb_set.payload_list);
+ mutex_unlock(&iommu_cb_set.payload_list_lock);
+
+ schedule_work(&iommu_cb_set.smmu_work);
+
+ return 0;
+}
+
+static int cam_smmu_translate_dir_to_iommu_dir(
+ enum cam_smmu_map_dir dir)
+{
+ switch (dir) {
+ case CAM_SMMU_MAP_READ:
+ return IOMMU_READ;
+ case CAM_SMMU_MAP_WRITE:
+ return IOMMU_WRITE;
+ case CAM_SMMU_MAP_RW:
+ return IOMMU_READ|IOMMU_WRITE;
+ case CAM_SMMU_MAP_INVALID:
+ default:
+ pr_err("Error: Direction is invalid. dir = %d\n", dir);
+ break;
+ };
+ return IOMMU_INVALID_DIR;
+}
+
+static enum dma_data_direction cam_smmu_translate_dir(
+ enum cam_smmu_map_dir dir)
+{
+ switch (dir) {
+ case CAM_SMMU_MAP_READ:
+ return DMA_FROM_DEVICE;
+ case CAM_SMMU_MAP_WRITE:
+ return DMA_TO_DEVICE;
+ case CAM_SMMU_MAP_RW:
+ return DMA_BIDIRECTIONAL;
+ case CAM_SMMU_MAP_INVALID:
+ default:
+ pr_err("Error: Direction is invalid. dir = %d\n", (int)dir);
+ break;
+ }
+ return DMA_NONE;
+}
+
+void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops)
+{
+ unsigned int i;
+ int j = 0;
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ iommu_cb_set.cb_info[i].handle = HANDLE_INIT;
+ INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].smmu_buf_list);
+ iommu_cb_set.cb_info[i].state = CAM_SMMU_DETACH;
+ iommu_cb_set.cb_info[i].dev = NULL;
+ iommu_cb_set.cb_info[i].cb_count = 0;
+ for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
+ iommu_cb_set.cb_info[i].token[j] = NULL;
+ iommu_cb_set.cb_info[i].handler[j] = NULL;
+ }
+ if (ops == CAM_SMMU_TABLE_INIT)
+ mutex_init(&iommu_cb_set.cb_info[i].lock);
+ else
+ mutex_destroy(&iommu_cb_set.cb_info[i].lock);
+ }
+}
+
+static int cam_smmu_check_handle_unique(int hdl)
+{
+ int i;
+
+ if (hdl == HANDLE_INIT) {
+ CDBG("iommu handle is init number. Need to try again\n");
+ return 1;
+ }
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ if (iommu_cb_set.cb_info[i].handle == HANDLE_INIT)
+ continue;
+
+ if (iommu_cb_set.cb_info[i].handle == hdl) {
+ CDBG("iommu handle %d conflicts\n", (int)hdl);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * use low 2 bytes for handle cookie
+ */
+static int cam_smmu_create_iommu_handle(int idx)
+{
+ int rand, hdl = 0;
+
+ get_random_bytes(&rand, COOKIE_NUM_BYTE);
+ hdl = GET_SMMU_HDL(idx, rand);
+ CDBG("create handle value = %x\n", (int)hdl);
+ return hdl;
+}
+
+static int cam_smmu_attach_device(int idx)
+{
+ int rc;
+ struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx];
+
+ /* attach the mapping to device */
+ rc = arm_iommu_attach_device(cb->dev, cb->mapping);
+ if (rc < 0) {
+ pr_err("Error: ARM IOMMU attach failed. ret = %d\n", rc);
+ rc = -ENODEV;
+ }
+
+ return rc;
+}
+
+static int cam_smmu_create_add_handle_in_table(char *name,
+ int *hdl)
+{
+ int i;
+ int handle;
+
+ /* create handle and add in the iommu hardware table */
+ for (i = 0; i < iommu_cb_set.cb_num; i++) {
+ if (!strcmp(iommu_cb_set.cb_info[i].name, name)) {
+ mutex_lock(&iommu_cb_set.cb_info[i].lock);
+ if (iommu_cb_set.cb_info[i].handle != HANDLE_INIT) {
+ pr_err("Error: %s already got handle 0x%x\n",
+ name,
+ iommu_cb_set.cb_info[i].handle);
+ mutex_unlock(&iommu_cb_set.cb_info[i].lock);
+ return -EINVAL;
+ }
+
+ /* make sure handle is unique */
+ do {
+ handle = cam_smmu_create_iommu_handle(i);
+ } while (cam_smmu_check_handle_unique(handle));
+
+ /* put handle in the table */
+ iommu_cb_set.cb_info[i].handle = handle;
+ iommu_cb_set.cb_info[i].cb_count = 0;
+ *hdl = handle;
+ CDBG("%s creates handle 0x%x\n", name, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[i].lock);
+ return 0;
+ }
+ }
+
+ pr_err("Error: Cannot find name %s or all handle exist!\n",
+ name);
+ cam_smmu_print_table();
+ return -EINVAL;
+}
+
+static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map,
+ dma_addr_t base, size_t size,
+ int order)
+{
+ unsigned int count = size >> (PAGE_SHIFT + order);
+ unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long);
+ int err = 0;
+
+ if (!count) {
+ err = -EINVAL;
+ pr_err("Page count is zero, size passed = %zu\n", size);
+ goto bail;
+ }
+
+ scratch_map->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!scratch_map->bitmap) {
+ err = -ENOMEM;
+ goto bail;
+ }
+
+ scratch_map->base = base;
+ scratch_map->bits = BITS_PER_BYTE * bitmap_size;
+ scratch_map->order = order;
+
+bail:
+ return err;
+}
+
+static int cam_smmu_alloc_scratch_va(struct scratch_mapping *mapping,
+ size_t size,
+ dma_addr_t *iova)
+{
+ unsigned int order = get_order(size);
+ unsigned int align = 0;
+ unsigned int count, start;
+
+ count = ((PAGE_ALIGN(size) >> PAGE_SHIFT) +
+ (1 << mapping->order) - 1) >> mapping->order;
+
+ /*
+ * Transparently, add a guard page to the total count of pages
+ * to be allocated
+ */
+ count++;
+
+ if (order > mapping->order)
+ align = (1 << (order - mapping->order)) - 1;
+
+ start = bitmap_find_next_zero_area(mapping->bitmap, mapping->bits, 0,
+ count, align);
+
+ if (start > mapping->bits)
+ return -ENOMEM;
+
+ bitmap_set(mapping->bitmap, start, count);
+ *iova = mapping->base + (start << (mapping->order + PAGE_SHIFT));
+
+ return 0;
+}
+
+static int cam_smmu_free_scratch_va(struct scratch_mapping *mapping,
+ dma_addr_t addr, size_t size)
+{
+ unsigned int start = (addr - mapping->base) >>
+ (mapping->order + PAGE_SHIFT);
+ unsigned int count = ((size >> PAGE_SHIFT) +
+ (1 << mapping->order) - 1) >> mapping->order;
+
+ if (!addr) {
+ pr_err("Error: Invalid address\n");
+ return -EINVAL;
+ }
+
+ if (start + count > mapping->bits) {
+ pr_err("Error: Invalid page bits in scratch map\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Transparently, add a guard page to the total count of pages
+ * to be freed
+ */
+ count++;
+ bitmap_clear(mapping->bitmap, start, count);
+
+ return 0;
+}
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_virt_address(int idx,
+ dma_addr_t virt_addr)
+{
+ struct cam_dma_buff_info *mapping;
+
+ list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->paddr == virt_addr) {
+ CDBG("Found virtual address %lx\n",
+ (unsigned long)virt_addr);
+ return mapping;
+ }
+ }
+
+ pr_err("Error: Cannot find virtual address %lx by index %d\n",
+ (unsigned long)virt_addr, idx);
+ return NULL;
+}
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx,
+ int ion_fd)
+{
+ struct cam_dma_buff_info *mapping;
+
+ list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+ list) {
+ if (mapping->ion_fd == ion_fd) {
+ CDBG(" find ion_fd %d\n", ion_fd);
+ return mapping;
+ }
+ }
+
+ pr_err("Error: Cannot find fd %d by index %d\n",
+ ion_fd, idx);
+ return NULL;
+}
+
+static void cam_smmu_clean_buffer_list(int idx)
+{
+ int ret;
+ struct cam_dma_buff_info *mapping_info, *temp;
+
+ list_for_each_entry_safe(mapping_info, temp,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ CDBG("Free mapping address %pK, i = %d, fd = %d\n",
+ (void *)mapping_info->paddr, idx,
+ mapping_info->ion_fd);
+
+ if (mapping_info->ion_fd == 0xDEADBEEF)
+ /* Clean up scratch buffers */
+ ret = cam_smmu_free_scratch_buffer_remove_from_list(
+ mapping_info, idx);
+ else
+ /* Clean up regular mapped buffers */
+ ret = cam_smmu_unmap_buf_and_remove_from_list(
+ mapping_info,
+ idx);
+
+ if (ret < 0) {
+ pr_err("Buffer delete failed: idx = %d\n", idx);
+ pr_err("Buffer delete failed: addr = %lx, fd = %d\n",
+ (unsigned long)mapping_info->paddr,
+ mapping_info->ion_fd);
+ /*
+ * Ignore this error and continue to delete other
+ * buffers in the list
+ */
+ continue;
+ }
+ }
+}
+
+static int cam_smmu_attach(int idx)
+{
+ int ret;
+
+ if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_ATTACH) {
+ ret = -EALREADY;
+ } else if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) {
+ ret = cam_smmu_attach_device(idx);
+ if (ret < 0) {
+ pr_err("Error: ATTACH fail\n");
+ return -ENODEV;
+ }
+ iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH;
+ ret = 0;
+ } else {
+ pr_err("Error: Not detach/attach: %d\n",
+ iommu_cb_set.cb_info[idx].state);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cam_smmu_detach_device(int idx)
+{
+ int rc = 0;
+ struct cam_context_bank_info *cb = &iommu_cb_set.cb_info[idx];
+
+ /* detach the mapping to device if not already detached */
+ if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) {
+ rc = -EALREADY;
+ } else if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_ATTACH) {
+ arm_iommu_detach_device(cb->dev);
+ iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH;
+ }
+
+ return rc;
+}
+
+static int cam_smmu_alloc_iova(size_t size,
+ int32_t smmu_hdl, uint32_t *iova)
+{
+ int rc = 0;
+ int idx;
+ uint32_t vaddr = 0;
+
+ if (!iova || !size || (smmu_hdl == HANDLE_INIT)) {
+ pr_err("Error: Input args are invalid\n");
+ return -EINVAL;
+ }
+
+ CDBG("Allocating iova size = %zu for smmu hdl=%X\n", size, smmu_hdl);
+
+ idx = GET_SMMU_TABLE_IDX(smmu_hdl);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, smmu_hdl);
+ return -EINVAL;
+ }
+
+ if (iommu_cb_set.cb_info[idx].handle != smmu_hdl) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, smmu_hdl);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].shared_support) {
+ pr_err("Error: Shared memory not supported for hdl = %X\n",
+ smmu_hdl);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ vaddr = gen_pool_alloc(iommu_cb_set.cb_info[idx].shared_mem_pool, size);
+ if (!vaddr)
+ return -ENOMEM;
+
+ *iova = vaddr;
+
+get_addr_end:
+ return rc;
+}
+
+static int cam_smmu_free_iova(uint32_t addr, size_t size,
+ int32_t smmu_hdl)
+{
+ int rc = 0;
+ int idx;
+
+ if (!size || (smmu_hdl == HANDLE_INIT)) {
+ pr_err("Error: Input args are invalid\n");
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(smmu_hdl);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, smmu_hdl);
+ return -EINVAL;
+ }
+
+ if (iommu_cb_set.cb_info[idx].handle != smmu_hdl) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, smmu_hdl);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ gen_pool_free(iommu_cb_set.cb_info[idx].shared_mem_pool, addr, size);
+
+get_addr_end:
+ return rc;
+}
+
+int cam_smmu_alloc_firmware(int32_t smmu_hdl,
+ dma_addr_t *iova,
+ uint64_t *cpuva,
+ size_t *len)
+{
+ int rc;
+ int32_t idx;
+ size_t firmware_len = 0;
+ size_t firmware_start = 0;
+ struct iommu_domain *domain;
+
+ if (!iova || !len || !cpuva || (smmu_hdl == HANDLE_INIT)) {
+ pr_err("Error: Input args are invalid\n");
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(smmu_hdl);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, smmu_hdl);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].firmware_support) {
+ pr_err("Firmware memory not supported for this SMMU handle\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].is_fw_allocated) {
+ pr_err("Trying to allocate twice\n");
+ rc = -ENOMEM;
+ goto unlock_and_end;
+ }
+
+ firmware_len = iommu_cb_set.cb_info[idx].firmware_info.iova_len;
+ firmware_start = iommu_cb_set.cb_info[idx].firmware_info.iova_start;
+ CDBG("Firmware area len from DT = %zu\n", firmware_len);
+
+ icp_fw.fw_kva = dma_alloc_coherent(icp_fw.fw_dev,
+ firmware_len,
+ &icp_fw.fw_dma_hdl,
+ GFP_KERNEL);
+ if (!icp_fw.fw_kva) {
+ pr_err("FW memory alloc failed\n");
+ rc = -ENOMEM;
+ goto unlock_and_end;
+ } else {
+ CDBG("DMA alloc returned fw = %pK, hdl = %pK\n",
+ icp_fw.fw_kva, (void *)icp_fw.fw_dma_hdl);
+ }
+
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+ rc = iommu_map(domain,
+ firmware_start,
+ icp_fw.fw_dma_hdl,
+ firmware_len,
+ IOMMU_READ|IOMMU_WRITE|IOMMU_PRIV);
+
+ if (rc) {
+ pr_err("Failed to map FW into IOMMU\n");
+ rc = -ENOMEM;
+ goto alloc_fail;
+ }
+ iommu_cb_set.cb_info[idx].is_fw_allocated = true;
+
+ *iova = iommu_cb_set.cb_info[idx].firmware_info.iova_start;
+ *cpuva = (uint64_t)icp_fw.fw_kva;
+ *len = firmware_len;
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+
+ return rc;
+
+alloc_fail:
+ dma_free_coherent(icp_fw.fw_dev,
+ firmware_len,
+ icp_fw.fw_kva,
+ icp_fw.fw_dma_hdl);
+unlock_and_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+end:
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_alloc_firmware);
+
+int cam_smmu_dealloc_firmware(int32_t smmu_hdl)
+{
+ int rc = 0;
+ int32_t idx;
+ size_t firmware_len = 0;
+ size_t firmware_start = 0;
+ struct iommu_domain *domain;
+ size_t unmapped = 0;
+
+ if (smmu_hdl == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(smmu_hdl);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, smmu_hdl);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].firmware_support) {
+ pr_err("Firmware memory not supported for this SMMU handle\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (!iommu_cb_set.cb_info[idx].is_fw_allocated) {
+ pr_err("Trying to deallocate firmware that is not allocated\n");
+ rc = -ENOMEM;
+ goto unlock_and_end;
+ }
+
+ firmware_len = iommu_cb_set.cb_info[idx].firmware_info.iova_len;
+ firmware_start = iommu_cb_set.cb_info[idx].firmware_info.iova_start;
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+ unmapped = iommu_unmap(domain,
+ firmware_start,
+ firmware_len);
+
+ if (unmapped != firmware_len) {
+ pr_err("Only %zu unmapped out of total %zu\n",
+ unmapped,
+ firmware_len);
+ rc = -EINVAL;
+ }
+
+ dma_free_coherent(icp_fw.fw_dev,
+ firmware_len,
+ icp_fw.fw_kva,
+ icp_fw.fw_dma_hdl);
+
+ icp_fw.fw_kva = 0;
+ icp_fw.fw_dma_hdl = 0;
+
+ iommu_cb_set.cb_info[idx].is_fw_allocated = false;
+
+unlock_and_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+end:
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_dealloc_firmware);
+
+static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
+ enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr,
+ enum cam_smmu_region_id region_id)
+{
+ int rc = -1;
+ struct cam_dma_buff_info *mapping_info;
+ struct dma_buf *buf = NULL;
+ struct dma_buf_attachment *attach = NULL;
+ struct sg_table *table = NULL;
+ struct iommu_domain *domain;
+ size_t size = 0;
+ uint32_t iova = 0;
+
+ /* allocate memory for each buffer information */
+ buf = dma_buf_get(ion_fd);
+ if (IS_ERR_OR_NULL(buf)) {
+ rc = PTR_ERR(buf);
+ pr_err("Error: dma get buf failed. fd = %d\n", ion_fd);
+ goto err_out;
+ }
+
+ attach = dma_buf_attach(buf, iommu_cb_set.cb_info[idx].dev);
+ if (IS_ERR_OR_NULL(attach)) {
+ rc = PTR_ERR(attach);
+ pr_err("Error: dma buf attach failed\n");
+ goto err_put;
+ }
+
+ table = dma_buf_map_attachment(attach, dma_dir);
+ if (IS_ERR_OR_NULL(table)) {
+ rc = PTR_ERR(table);
+ pr_err("Error: dma buf map attachment failed\n");
+ goto err_detach;
+ }
+
+ if (region_id == CAM_SMMU_REGION_SHARED) {
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+ if (!domain) {
+ pr_err("CB has no domain set\n");
+ goto err_unmap_sg;
+ }
+
+ rc = cam_smmu_alloc_iova(*len_ptr,
+ iommu_cb_set.cb_info[idx].handle,
+ &iova);
+
+ if (rc < 0) {
+ pr_err("IOVA alloc failed for shared memory\n");
+ goto err_unmap_sg;
+ }
+
+ size = iommu_map_sg(domain,
+ iova,
+ table->sgl,
+ table->nents,
+ IOMMU_READ | IOMMU_WRITE);
+
+ if (size < 0) {
+ pr_err("IOMMU mapping failed\n");
+ rc = cam_smmu_free_iova(iova,
+ size,
+ iommu_cb_set.cb_info[idx].handle);
+
+ if (rc)
+ pr_err("IOVA free failed\n");
+ rc = -ENOMEM;
+ goto err_unmap_sg;
+ } else {
+ CDBG("iommu_map_sg returned %zu\n", size);
+ *paddr_ptr = iova;
+ *len_ptr = size;
+ }
+ } else if (region_id == CAM_SMMU_REGION_IO) {
+ rc = msm_dma_map_sg_lazy(iommu_cb_set.cb_info[idx].dev,
+ table->sgl, table->nents, dma_dir, buf);
+
+ if (rc != table->nents) {
+ pr_err("Error: msm_dma_map_sg_lazy failed\n");
+ rc = -ENOMEM;
+ goto err_unmap_sg;
+ } else {
+ *paddr_ptr = sg_dma_address(table->sgl);
+ *len_ptr = (size_t)sg_dma_len(table->sgl);
+ }
+ } else {
+ pr_err("Error: Wrong region id passed for %s\n", __func__);
+ rc = -EINVAL;
+ goto err_unmap_sg;
+ }
+
+ if (table->sgl) {
+ CDBG("DMA buf: %pK, device: %pK, attach: %pK, table: %pK\n",
+ (void *)buf,
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)attach, (void *)table);
+ CDBG("table sgl: %pK, rc: %d, dma_address: 0x%x\n",
+ (void *)table->sgl, rc,
+ (unsigned int)table->sgl->dma_address);
+ } else {
+ rc = -EINVAL;
+ pr_err("Error: table sgl is null\n");
+ goto err_unmap_sg;
+ }
+
+ /* fill up mapping_info */
+ mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL);
+ if (!mapping_info) {
+ rc = -ENOSPC;
+ goto err_alloc;
+ }
+ mapping_info->ion_fd = ion_fd;
+ mapping_info->buf = buf;
+ mapping_info->attach = attach;
+ mapping_info->table = table;
+ mapping_info->paddr = *paddr_ptr;
+ mapping_info->len = *len_ptr;
+ mapping_info->dir = dma_dir;
+ mapping_info->ref_count = 1;
+ mapping_info->region_id = region_id;
+
+ if (!*paddr_ptr || !*len_ptr) {
+ pr_err("Error: Space Allocation failed!\n");
+ kfree(mapping_info);
+ rc = -ENOSPC;
+ goto err_alloc;
+ }
+ CDBG("ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n", ion_fd,
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)*paddr_ptr, (unsigned int)*len_ptr);
+
+ /* add to the list */
+ list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
+ return 0;
+
+err_alloc:
+ if (region_id == CAM_SMMU_REGION_SHARED) {
+ cam_smmu_free_iova(iova,
+ size,
+ iommu_cb_set.cb_info[idx].handle);
+
+ iommu_unmap(iommu_cb_set.cb_info[idx].mapping->domain,
+ *paddr_ptr,
+ *len_ptr);
+ } else if (region_id == CAM_SMMU_REGION_IO) {
+ msm_dma_unmap_sg(iommu_cb_set.cb_info[idx].dev,
+ table->sgl,
+ table->nents,
+ dma_dir,
+ buf);
+ }
+err_unmap_sg:
+ dma_buf_unmap_attachment(attach, table, dma_dir);
+err_detach:
+ dma_buf_detach(buf, attach);
+err_put:
+ dma_buf_put(buf);
+err_out:
+ return rc;
+}
+
+static int cam_smmu_unmap_buf_and_remove_from_list(
+ struct cam_dma_buff_info *mapping_info,
+ int idx)
+{
+ int rc;
+ size_t size;
+ struct iommu_domain *domain;
+
+ if ((!mapping_info->buf) || (!mapping_info->table) ||
+ (!mapping_info->attach)) {
+ pr_err("Error: Invalid params dev = %pK, table = %pK\n",
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)mapping_info->table);
+ pr_err("Error:dma_buf = %pK, attach = %pK\n",
+ (void *)mapping_info->buf,
+ (void *)mapping_info->attach);
+ return -EINVAL;
+ }
+
+ if (mapping_info->region_id == CAM_SMMU_REGION_SHARED) {
+ CDBG("Removing SHARED buffer paddr = %pK, len = %zu\n",
+ (void *)mapping_info->paddr, mapping_info->len);
+
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+
+ size = iommu_unmap(domain,
+ mapping_info->paddr,
+ mapping_info->len);
+
+ if (size != mapping_info->len) {
+ pr_err("IOMMU unmap failed\n");
+ pr_err("Unmapped = %zu, requested = %zu\n",
+ size,
+ mapping_info->len);
+ }
+
+ rc = cam_smmu_free_iova(mapping_info->paddr,
+ mapping_info->len,
+ iommu_cb_set.cb_info[idx].handle);
+
+ if (rc)
+ pr_err("IOVA free failed\n");
+
+ } else if (mapping_info->region_id == CAM_SMMU_REGION_IO) {
+ msm_dma_unmap_sg(iommu_cb_set.cb_info[idx].dev,
+ mapping_info->table->sgl, mapping_info->table->nents,
+ mapping_info->dir, mapping_info->buf);
+ }
+
+ dma_buf_unmap_attachment(mapping_info->attach,
+ mapping_info->table, mapping_info->dir);
+ dma_buf_detach(mapping_info->buf, mapping_info->attach);
+ dma_buf_put(mapping_info->buf);
+
+ mapping_info->buf = NULL;
+
+ list_del_init(&mapping_info->list);
+
+ /* free one buffer */
+ kfree(mapping_info);
+ return 0;
+}
+
+static enum cam_smmu_buf_state cam_smmu_check_fd_in_list(int idx,
+ int ion_fd, dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ struct cam_dma_buff_info *mapping;
+
+ list_for_each_entry(mapping,
+ &iommu_cb_set.cb_info[idx].smmu_buf_list, list) {
+ if (mapping->ion_fd == ion_fd) {
+ mapping->ref_count++;
+ *paddr_ptr = mapping->paddr;
+ *len_ptr = mapping->len;
+ return CAM_SMMU_BUFF_EXIST;
+ }
+ }
+
+ return CAM_SMMU_BUFF_NOT_EXIST;
+}
+
+int cam_smmu_get_handle(char *identifier, int *handle_ptr)
+{
+ int ret = 0;
+
+ if (!identifier) {
+ pr_err("Error: iommu hardware name is NULL\n");
+ return -EINVAL;
+ }
+
+ if (!handle_ptr) {
+ pr_err("Error: handle pointer is NULL\n");
+ return -EINVAL;
+ }
+
+ /* create and put handle in the table */
+ ret = cam_smmu_create_add_handle_in_table(identifier, handle_ptr);
+ if (ret < 0)
+ pr_err("Error: %s get handle fail\n", identifier);
+
+ return ret;
+}
+EXPORT_SYMBOL(cam_smmu_get_handle);
+
+int cam_smmu_ops(int handle, enum cam_smmu_ops_param ops)
+{
+ int ret = 0, idx;
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: Index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return -EINVAL;
+ }
+
+ switch (ops) {
+ case CAM_SMMU_ATTACH: {
+ ret = cam_smmu_attach(idx);
+ break;
+ }
+ case CAM_SMMU_DETACH: {
+ ret = cam_smmu_detach_device(idx);
+ break;
+ }
+ case CAM_SMMU_VOTE:
+ case CAM_SMMU_DEVOTE:
+ default:
+ pr_err("Error: idx = %d, ops = %d\n", idx, ops);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return ret;
+}
+EXPORT_SYMBOL(cam_smmu_ops);
+
+static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx,
+ size_t virt_len,
+ size_t phys_len,
+ unsigned int iommu_dir,
+ dma_addr_t *virt_addr)
+{
+ unsigned long nents = virt_len / phys_len;
+ struct cam_dma_buff_info *mapping_info = NULL;
+ size_t unmapped;
+ dma_addr_t iova = 0;
+ struct scatterlist *sg;
+ int i = 0;
+ int rc;
+ struct iommu_domain *domain = NULL;
+ struct page *page;
+ struct sg_table *table = NULL;
+
+ CDBG("%s: nents = %lu, idx = %d, virt_len = %zx\n",
+ __func__, nents, idx, virt_len);
+ CDBG("%s: phys_len = %zx, iommu_dir = %d, virt_addr = %pK\n",
+ __func__, phys_len, iommu_dir, virt_addr);
+
+ /*
+ * This table will go inside the 'mapping' structure
+ * where it will be held until put_scratch_buffer is called
+ */
+ table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table) {
+ rc = -ENOMEM;
+ goto err_table_alloc;
+ }
+
+ rc = sg_alloc_table(table, nents, GFP_KERNEL);
+ if (rc < 0) {
+ rc = -EINVAL;
+ goto err_sg_alloc;
+ }
+
+ page = alloc_pages(GFP_KERNEL, get_order(phys_len));
+ if (!page) {
+ rc = -ENOMEM;
+ goto err_page_alloc;
+ }
+
+ /* Now we create the sg list */
+ for_each_sg(table->sgl, sg, table->nents, i)
+ sg_set_page(sg, page, phys_len, 0);
+
+
+ /* Get the domain from within our cb_set struct and map it*/
+ domain = iommu_cb_set.cb_info[idx].mapping->domain;
+
+ rc = cam_smmu_alloc_scratch_va(&iommu_cb_set.cb_info[idx].scratch_map,
+ virt_len, &iova);
+
+ if (rc < 0) {
+ pr_err("Could not find valid iova for scratch buffer");
+ goto err_iommu_map;
+ }
+
+ if (iommu_map_sg(domain,
+ iova,
+ table->sgl,
+ table->nents,
+ iommu_dir) != virt_len) {
+ pr_err("iommu_map_sg() failed");
+ goto err_iommu_map;
+ }
+
+ /* Now update our mapping information within the cb_set struct */
+ mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL);
+ if (!mapping_info) {
+ rc = -ENOMEM;
+ goto err_mapping_info;
+ }
+
+ mapping_info->ion_fd = 0xDEADBEEF;
+ mapping_info->buf = NULL;
+ mapping_info->attach = NULL;
+ mapping_info->table = table;
+ mapping_info->paddr = iova;
+ mapping_info->len = virt_len;
+ mapping_info->iommu_dir = iommu_dir;
+ mapping_info->ref_count = 1;
+ mapping_info->phys_len = phys_len;
+ mapping_info->region_id = CAM_SMMU_REGION_SCRATCH;
+
+ CDBG("%s: paddr = %pK, len = %zx, phys_len = %zx",
+ __func__, (void *)mapping_info->paddr,
+ mapping_info->len, mapping_info->phys_len);
+
+ list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
+
+ *virt_addr = (dma_addr_t)iova;
+
+ CDBG("%s: mapped virtual address = %lx\n", __func__,
+ (unsigned long)*virt_addr);
+ return 0;
+
+err_mapping_info:
+ unmapped = iommu_unmap(domain, iova, virt_len);
+ if (unmapped != virt_len)
+ pr_err("Unmapped only %zx instead of %zx", unmapped, virt_len);
+err_iommu_map:
+ __free_pages(page, get_order(phys_len));
+err_page_alloc:
+ sg_free_table(table);
+err_sg_alloc:
+ kfree(table);
+err_table_alloc:
+ return rc;
+}
+
+static int cam_smmu_free_scratch_buffer_remove_from_list(
+ struct cam_dma_buff_info *mapping_info,
+ int idx)
+{
+ int rc = 0;
+ size_t unmapped;
+ struct iommu_domain *domain =
+ iommu_cb_set.cb_info[idx].mapping->domain;
+ struct scratch_mapping *scratch_map =
+ &iommu_cb_set.cb_info[idx].scratch_map;
+
+ if (!mapping_info->table) {
+ pr_err("Error: Invalid params: dev = %pK, table = %pK",
+ (void *)iommu_cb_set.cb_info[idx].dev,
+ (void *)mapping_info->table);
+ return -EINVAL;
+ }
+
+ /* Clean up the mapping_info struct from the list */
+ unmapped = iommu_unmap(domain, mapping_info->paddr, mapping_info->len);
+ if (unmapped != mapping_info->len)
+ pr_err("Unmapped only %zx instead of %zx",
+ unmapped, mapping_info->len);
+
+ rc = cam_smmu_free_scratch_va(scratch_map,
+ mapping_info->paddr,
+ mapping_info->len);
+ if (rc < 0) {
+ pr_err("Error: Invalid iova while freeing scratch buffer\n");
+ rc = -EINVAL;
+ }
+
+ __free_pages(sg_page(mapping_info->table->sgl),
+ get_order(mapping_info->phys_len));
+ sg_free_table(mapping_info->table);
+ kfree(mapping_info->table);
+ list_del_init(&mapping_info->list);
+
+ kfree(mapping_info);
+ mapping_info = NULL;
+
+ return rc;
+}
+
+int cam_smmu_get_scratch_iova(int handle,
+ enum cam_smmu_map_dir dir,
+ dma_addr_t *paddr_ptr,
+ size_t virt_len,
+ size_t phys_len)
+{
+ int idx, rc;
+ unsigned int iommu_dir;
+
+ if (!paddr_ptr || !virt_len || !phys_len) {
+ pr_err("Error: Input pointer or lengths invalid\n");
+ return -EINVAL;
+ }
+
+ if (virt_len < phys_len) {
+ pr_err("Error: virt_len > phys_len\n");
+ return -EINVAL;
+ }
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ iommu_dir = cam_smmu_translate_dir_to_iommu_dir(dir);
+ if (iommu_dir == IOMMU_INVALID_DIR) {
+ pr_err("Error: translate direction failed. dir = %d\n", dir);
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].scratch_buf_support) {
+ pr_err("Error: Context bank does not support scratch bufs\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ CDBG("%s: smmu handle = %x, idx = %d, dir = %d\n",
+ __func__, handle, idx, dir);
+ CDBG("%s: virt_len = %zx, phys_len = %zx\n",
+ __func__, phys_len, virt_len);
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Err:Dev %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (!IS_ALIGNED(virt_len, PAGE_SIZE)) {
+ pr_err("Requested scratch buffer length not page aligned\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (!IS_ALIGNED(virt_len, phys_len)) {
+ pr_err("Requested virt length not aligned with phys length\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ rc = cam_smmu_alloc_scratch_buffer_add_to_list(idx,
+ virt_len,
+ phys_len,
+ iommu_dir,
+ paddr_ptr);
+ if (rc < 0)
+ pr_err("Error: mapping or add list fail\n");
+
+error:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+
+int cam_smmu_put_scratch_iova(int handle,
+ dma_addr_t paddr)
+{
+ int idx;
+ int rc = -1;
+ struct cam_dma_buff_info *mapping_info;
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto handle_err;
+ }
+
+ if (!iommu_cb_set.cb_info[idx].scratch_buf_support) {
+ pr_err("Error: Context bank does not support scratch buffers\n");
+ rc = -EINVAL;
+ goto handle_err;
+ }
+
+ /* Based on virtual address and index, we can find mapping info
+ * of the scratch buffer
+ */
+ mapping_info = cam_smmu_find_mapping_by_virt_address(idx, paddr);
+ if (!mapping_info) {
+ pr_err("Error: Invalid params\n");
+ rc = -ENODEV;
+ goto handle_err;
+ }
+
+ /* unmapping one buffer from device */
+ rc = cam_smmu_free_scratch_buffer_remove_from_list(mapping_info, idx);
+ if (rc < 0) {
+ pr_err("Error: unmap or remove list fail\n");
+ goto handle_err;
+ }
+
+handle_err:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+
+int cam_smmu_map_sec_iova(int handle, int ion_fd,
+ enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr)
+{
+ /* not implemented yet */
+ return -EPERM;
+}
+EXPORT_SYMBOL(cam_smmu_map_sec_iova);
+
+int cam_smmu_map_iova(int handle, int ion_fd,
+ enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
+ size_t *len_ptr, enum cam_smmu_region_id region_id)
+{
+ int idx, rc;
+ enum dma_data_direction dma_dir;
+ enum cam_smmu_buf_state buf_state;
+
+ if (!paddr_ptr || !len_ptr) {
+ pr_err("Input pointers are invalid\n");
+ return -EINVAL;
+ }
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Invalid handle\n");
+ return -EINVAL;
+ }
+
+ /* clean the content from clients */
+ *paddr_ptr = (dma_addr_t)NULL;
+ if (region_id != CAM_SMMU_REGION_SHARED)
+ *len_ptr = (size_t)0;
+
+ dma_dir = cam_smmu_translate_dir(dir);
+ if (dma_dir == DMA_NONE) {
+ pr_err("translate direction failed. dir = %d\n", dir);
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+ pr_err("Err:Dev %s should call SMMU attach before map buffer\n",
+ iommu_cb_set.cb_info[idx].name);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr,
+ len_ptr);
+ if (buf_state == CAM_SMMU_BUFF_EXIST) {
+ CDBG("ion_fd:%d already in the list, give same addr back",
+ ion_fd);
+ rc = 0;
+ goto get_addr_end;
+ }
+ rc = cam_smmu_map_buffer_and_add_to_list(idx, ion_fd, dma_dir,
+ paddr_ptr, len_ptr, region_id);
+ if (rc < 0)
+ pr_err("mapping or add list fail\n");
+
+get_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_map_iova);
+
+
+int cam_smmu_get_iova(int handle, int ion_fd,
+ dma_addr_t *paddr_ptr, size_t *len_ptr)
+{
+ int idx, rc = 0;
+ enum cam_smmu_buf_state buf_state;
+
+ if (!paddr_ptr || !len_ptr) {
+ pr_err("Error: Input pointers are invalid\n");
+ return -EINVAL;
+ }
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ /* clean the content from clients */
+ *paddr_ptr = (dma_addr_t)NULL;
+ *len_ptr = (size_t)0;
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+ buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr, len_ptr);
+ if (buf_state == CAM_SMMU_BUFF_NOT_EXIST) {
+ CDBG("ion_fd:%d not in the mapped list", ion_fd);
+ rc = -EINVAL;
+ goto get_addr_end;
+ }
+
+get_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_get_iova);
+
+int cam_smmu_unmap_sec_iova(int handle, int ion_fd)
+{
+ /* not implemented yet */
+ return -EPERM;
+}
+EXPORT_SYMBOL(cam_smmu_unmap_sec_iova);
+
+int cam_smmu_unmap_iova(int handle,
+ int ion_fd,
+ enum cam_smmu_region_id region_id)
+{
+ int idx, rc;
+ struct cam_dma_buff_info *mapping_info;
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto unmap_end;
+ }
+
+ /* Based on ion fd and index, we can find mapping info of buffer */
+ mapping_info = cam_smmu_find_mapping_by_ion_index(idx, ion_fd);
+ if (!mapping_info) {
+ pr_err("Error: Invalid params! idx = %d, fd = %d\n",
+ idx, ion_fd);
+ rc = -EINVAL;
+ goto unmap_end;
+ }
+
+ mapping_info->ref_count--;
+ if (mapping_info->ref_count > 0) {
+ CDBG("There are still %u buffer(s) with same fd %d",
+ mapping_info->ref_count, mapping_info->ion_fd);
+ rc = 0;
+ goto unmap_end;
+ }
+
+ /* Unmapping one buffer from device */
+ CDBG("SMMU: removing buffer idx = %d\n", idx);
+ rc = cam_smmu_unmap_buf_and_remove_from_list(mapping_info, idx);
+ if (rc < 0)
+ pr_err("Error: unmap or remove list fail\n");
+
+unmap_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_unmap_iova);
+
+int cam_smmu_put_iova(int handle, int ion_fd)
+{
+ int idx;
+ int rc = 0;
+ struct cam_dma_buff_info *mapping_info;
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ /* find index in the iommu_cb_set.cb_info */
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ rc = -EINVAL;
+ goto put_addr_end;
+ }
+
+ /* based on ion fd and index, we can find mapping info of buffer */
+ mapping_info = cam_smmu_find_mapping_by_ion_index(idx, ion_fd);
+ if (!mapping_info) {
+ pr_err("Error: Invalid params! idx = %d, fd = %d\n",
+ idx, ion_fd);
+ rc = -EINVAL;
+ goto put_addr_end;
+ }
+
+ mapping_info->ref_count--;
+
+put_addr_end:
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return rc;
+}
+EXPORT_SYMBOL(cam_smmu_put_iova);
+
+int cam_smmu_destroy_handle(int handle)
+{
+ int idx;
+
+ if (handle == HANDLE_INIT) {
+ pr_err("Error: Invalid handle\n");
+ return -EINVAL;
+ }
+
+ idx = GET_SMMU_TABLE_IDX(handle);
+ if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+ pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+ idx, handle);
+ return -EINVAL;
+ }
+
+ mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+ if (iommu_cb_set.cb_info[idx].handle != handle) {
+ pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+ iommu_cb_set.cb_info[idx].handle, handle);
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return -EINVAL;
+ }
+
+ if (!list_empty_careful(&iommu_cb_set.cb_info[idx].smmu_buf_list)) {
+ pr_err("Client %s buffer list is not clean!\n",
+ iommu_cb_set.cb_info[idx].name);
+ cam_smmu_print_list(idx);
+ cam_smmu_clean_buffer_list(idx);
+ }
+
+ iommu_cb_set.cb_info[idx].cb_count = 0;
+ iommu_cb_set.cb_info[idx].handle = HANDLE_INIT;
+ mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+ return 0;
+}
+EXPORT_SYMBOL(cam_smmu_destroy_handle);
+
+static void cam_smmu_deinit_cb(struct cam_context_bank_info *cb)
+{
+ arm_iommu_detach_device(cb->dev);
+
+ if (cb->io_support && cb->mapping) {
+ arm_iommu_release_mapping(cb->mapping);
+ cb->mapping = NULL;
+ }
+
+ if (cb->shared_support) {
+ gen_pool_destroy(cb->shared_mem_pool);
+ cb->shared_mem_pool = NULL;
+ }
+
+ if (cb->scratch_buf_support) {
+ kfree(cb->scratch_map.bitmap);
+ cb->scratch_map.bitmap = NULL;
+ }
+}
+
+static void cam_smmu_release_cb(struct platform_device *pdev)
+{
+ int i = 0;
+
+ for (i = 0; i < iommu_cb_set.cb_num; i++)
+ cam_smmu_deinit_cb(&iommu_cb_set.cb_info[i]);
+
+ devm_kfree(&pdev->dev, iommu_cb_set.cb_info);
+ iommu_cb_set.cb_num = 0;
+}
+
+static int cam_smmu_setup_cb(struct cam_context_bank_info *cb,
+ struct device *dev)
+{
+ int rc = 0;
+
+ if (!cb || !dev) {
+ pr_err("Error: invalid input params\n");
+ return -EINVAL;
+ }
+
+ cb->dev = dev;
+ cb->is_fw_allocated = false;
+
+ /* Create a pool with 4K granularity for supporting shared memory */
+ if (cb->shared_support) {
+ cb->shared_mem_pool = gen_pool_create(
+ SHARED_MEM_POOL_GRANULARITY, -1);
+
+ if (!cb->shared_mem_pool)
+ return -ENOMEM;
+
+ rc = gen_pool_add(cb->shared_mem_pool,
+ cb->shared_info.iova_start,
+ cb->shared_info.iova_len,
+ -1);
+
+ CDBG("Shared mem start->%lX\n",
+ (unsigned long)cb->shared_info.iova_start);
+ CDBG("Shared mem len->%zu\n", cb->shared_info.iova_len);
+
+ if (rc) {
+ pr_err("Genpool chunk creation failed\n");
+ gen_pool_destroy(cb->shared_mem_pool);
+ cb->shared_mem_pool = NULL;
+ return rc;
+ }
+ }
+
+ if (cb->scratch_buf_support) {
+ rc = cam_smmu_init_scratch_map(&cb->scratch_map,
+ cb->scratch_info.iova_start,
+ cb->scratch_info.iova_len,
+ 0);
+ if (rc < 0) {
+ pr_err("Error: failed to create scratch map\n");
+ rc = -ENODEV;
+ goto end;
+ }
+ }
+
+ /* create a virtual mapping */
+ if (cb->io_support) {
+ cb->mapping = arm_iommu_create_mapping(&platform_bus_type,
+ cb->io_info.iova_start, cb->io_info.iova_len);
+ if (IS_ERR(cb->mapping)) {
+ pr_err("Error: create mapping Failed\n");
+ rc = -ENODEV;
+ goto end;
+ }
+ } else {
+ pr_err("Context bank does not have IO region\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ return rc;
+end:
+ if (cb->shared_support) {
+ gen_pool_destroy(cb->shared_mem_pool);
+ cb->shared_mem_pool = NULL;
+ }
+
+ if (cb->scratch_buf_support) {
+ kfree(cb->scratch_map.bitmap);
+ cb->scratch_map.bitmap = NULL;
+ }
+
+ return rc;
+}
+
+static int cam_alloc_smmu_context_banks(struct device *dev)
+{
+ struct device_node *domains_child_node = NULL;
+
+ if (!dev) {
+ pr_err("Error: Invalid device\n");
+ return -ENODEV;
+ }
+
+ iommu_cb_set.cb_num = 0;
+
+ /* traverse thru all the child nodes and increment the cb count */
+ for_each_available_child_of_node(dev->of_node, domains_child_node) {
+ if (of_device_is_compatible(domains_child_node,
+ "qcom,msm-cam-smmu-cb"))
+ iommu_cb_set.cb_num++;
+
+ if (of_device_is_compatible(domains_child_node,
+ "qcom,qsmmu-cam-cb"))
+ iommu_cb_set.cb_num++;
+ }
+
+ if (iommu_cb_set.cb_num == 0) {
+ pr_err("Error: no context banks present\n");
+ return -ENOENT;
+ }
+
+ /* allocate memory for the context banks */
+ iommu_cb_set.cb_info = devm_kzalloc(dev,
+ iommu_cb_set.cb_num * sizeof(struct cam_context_bank_info),
+ GFP_KERNEL);
+
+ if (!iommu_cb_set.cb_info) {
+ pr_err("Error: cannot allocate context banks\n");
+ return -ENOMEM;
+ }
+
+ cam_smmu_reset_iommu_table(CAM_SMMU_TABLE_INIT);
+ iommu_cb_set.cb_init_count = 0;
+
+ CDBG("no of context banks :%d\n", iommu_cb_set.cb_num);
+ return 0;
+}
+
+static int cam_smmu_get_memory_regions_info(struct device_node *of_node,
+ struct cam_context_bank_info *cb)
+{
+ int rc = 0;
+ struct device_node *mem_map_node = NULL;
+ struct device_node *child_node = NULL;
+ const char *region_name;
+ int num_regions = 0;
+
+ if (!of_node || !cb) {
+ pr_err("Invalid argument(s)\n");
+ return -EINVAL;
+ }
+
+ mem_map_node = of_get_child_by_name(of_node, "iova-mem-map");
+ if (!mem_map_node) {
+ pr_err("iova-mem-map not present\n");
+ return -EINVAL;
+ }
+
+ for_each_available_child_of_node(mem_map_node, child_node) {
+ uint32_t region_start;
+ uint32_t region_len;
+ uint32_t region_id;
+
+ num_regions++;
+ rc = of_property_read_string(child_node,
+ "iova-region-name", ®ion_name);
+ if (rc < 0) {
+ of_node_put(mem_map_node);
+ pr_err("IOVA region not found\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(child_node,
+ "iova-region-start", ®ion_start);
+ if (rc < 0) {
+ of_node_put(mem_map_node);
+ pr_err("Failed to read iova-region-start\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(child_node,
+ "iova-region-len", ®ion_len);
+ if (rc < 0) {
+ of_node_put(mem_map_node);
+ pr_err("Failed to read iova-region-len\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(child_node,
+ "iova-region-id", ®ion_id);
+ if (rc < 0) {
+ of_node_put(mem_map_node);
+ pr_err("Failed to read iova-region-id\n");
+ return -EINVAL;
+ }
+
+ switch (region_id) {
+ case CAM_SMMU_REGION_FIRMWARE:
+ cb->firmware_support = 1;
+ cb->firmware_info.iova_start = region_start;
+ cb->firmware_info.iova_len = region_len;
+ break;
+ case CAM_SMMU_REGION_SHARED:
+ cb->shared_support = 1;
+ cb->shared_info.iova_start = region_start;
+ cb->shared_info.iova_len = region_len;
+ break;
+ case CAM_SMMU_REGION_SCRATCH:
+ cb->scratch_buf_support = 1;
+ cb->scratch_info.iova_start = region_start;
+ cb->scratch_info.iova_len = region_len;
+ break;
+ case CAM_SMMU_REGION_IO:
+ cb->io_support = 1;
+ cb->io_info.iova_start = region_start;
+ cb->io_info.iova_len = region_len;
+ break;
+ default:
+ pr_err("Incorrect region id present in DT file: %d\n",
+ region_id);
+ }
+
+ CDBG("Found label -> %s\n", cb->name);
+ CDBG("Found region -> %s\n", region_name);
+ CDBG("region_start -> %X\n", region_start);
+ CDBG("region_len -> %X\n", region_len);
+ CDBG("region_id -> %X\n", region_id);
+ }
+ of_node_put(mem_map_node);
+
+ if (!num_regions) {
+ pr_err("No memory regions found, at least one needed\n");
+ rc = -ENODEV;
+ }
+
+ return rc;
+}
+
+static int cam_populate_smmu_context_banks(struct device *dev,
+ enum cam_iommu_type type)
+{
+ int rc = 0;
+ struct cam_context_bank_info *cb;
+ struct device *ctx = NULL;
+
+ if (!dev) {
+ pr_err("Error: Invalid device\n");
+ return -ENODEV;
+ }
+
+ /* check the bounds */
+ if (iommu_cb_set.cb_init_count >= iommu_cb_set.cb_num) {
+ pr_err("Error: populate more than allocated cb\n");
+ rc = -EBADHANDLE;
+ goto cb_init_fail;
+ }
+
+ /* read the context bank from cb set */
+ cb = &iommu_cb_set.cb_info[iommu_cb_set.cb_init_count];
+
+ /* set the name of the context bank */
+ rc = of_property_read_string(dev->of_node, "label", &cb->name);
+ if (rc < 0) {
+ pr_err("Error: failed to read label from sub device\n");
+ goto cb_init_fail;
+ }
+
+ rc = cam_smmu_get_memory_regions_info(dev->of_node,
+ cb);
+ if (rc < 0) {
+ pr_err("Error: Getting region info\n");
+ return rc;
+ }
+
+ /* set up the iommu mapping for the context bank */
+ if (type == CAM_QSMMU) {
+ pr_err("Error: QSMMU ctx not supported for : %s\n", cb->name);
+ return -ENODEV;
+ }
+
+ ctx = dev;
+ CDBG("getting Arm SMMU ctx : %s\n", cb->name);
+
+ rc = cam_smmu_setup_cb(cb, ctx);
+ if (rc < 0) {
+ pr_err("Error: failed to setup cb : %s\n", cb->name);
+ goto cb_init_fail;
+ }
+
+ if (cb->io_support && cb->mapping)
+ iommu_set_fault_handler(cb->mapping->domain,
+ cam_smmu_iommu_fault_handler,
+ (void *)cb->name);
+
+ /* increment count to next bank */
+ iommu_cb_set.cb_init_count++;
+
+ CDBG("X: cb init count :%d\n", iommu_cb_set.cb_init_count);
+
+cb_init_fail:
+ return rc;
+}
+
+static int cam_smmu_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct device *dev = &pdev->dev;
+
+ if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu")) {
+ rc = cam_alloc_smmu_context_banks(dev);
+ if (rc < 0) {
+ pr_err("Error: allocating context banks\n");
+ return -ENOMEM;
+ }
+ }
+ if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu-cb")) {
+ rc = cam_populate_smmu_context_banks(dev, CAM_ARM_SMMU);
+ if (rc < 0) {
+ pr_err("Error: populating context banks\n");
+ return -ENOMEM;
+ }
+ return rc;
+ }
+ if (of_device_is_compatible(dev->of_node, "qcom,qsmmu-cam-cb")) {
+ rc = cam_populate_smmu_context_banks(dev, CAM_QSMMU);
+ if (rc < 0) {
+ pr_err("Error: populating context banks\n");
+ return -ENOMEM;
+ }
+ return rc;
+ }
+
+ if (of_device_is_compatible(dev->of_node, "qcom,msm-cam-smmu-fw-dev")) {
+ icp_fw.fw_dev = &pdev->dev;
+ icp_fw.fw_kva = NULL;
+ icp_fw.fw_dma_hdl = 0;
+ return rc;
+ }
+
+ /* probe through all the subdevices */
+ rc = of_platform_populate(pdev->dev.of_node, msm_cam_smmu_dt_match,
+ NULL, &pdev->dev);
+ if (rc < 0) {
+ pr_err("Error: populating devices\n");
+ } else {
+ INIT_WORK(&iommu_cb_set.smmu_work, cam_smmu_page_fault_work);
+ mutex_init(&iommu_cb_set.payload_list_lock);
+ INIT_LIST_HEAD(&iommu_cb_set.payload_list);
+ }
+
+ return rc;
+}
+
+static int cam_smmu_remove(struct platform_device *pdev)
+{
+ /* release all the context banks and memory allocated */
+ cam_smmu_reset_iommu_table(CAM_SMMU_TABLE_DEINIT);
+ if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-cam-smmu"))
+ cam_smmu_release_cb(pdev);
+ return 0;
+}
+
+static struct platform_driver cam_smmu_driver = {
+ .probe = cam_smmu_probe,
+ .remove = cam_smmu_remove,
+ .driver = {
+ .name = "msm_cam_smmu",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cam_smmu_dt_match,
+ },
+};
+
+static int __init cam_smmu_init_module(void)
+{
+ return platform_driver_register(&cam_smmu_driver);
+}
+
+static void __exit cam_smmu_exit_module(void)
+{
+ platform_driver_unregister(&cam_smmu_driver);
+}
+
+module_init(cam_smmu_init_module);
+module_exit(cam_smmu_exit_module);
+MODULE_DESCRIPTION("MSM Camera SMMU driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h
new file mode 100644
index 0000000..76e9135
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h
@@ -0,0 +1,255 @@
+/* Copyright (c) 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
+ * 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 _CAM_SMMU_API_H_
+#define _CAM_SMMU_API_H_
+
+#include <linux/dma-direction.h>
+#include <linux/module.h>
+#include <linux/dma-buf.h>
+#include <asm/dma-iommu.h>
+#include <linux/dma-direction.h>
+#include <linux/of_platform.h>
+#include <linux/iommu.h>
+#include <linux/random.h>
+#include <linux/spinlock_types.h>
+#include <linux/mutex.h>
+#include <linux/msm_ion.h>
+
+/*Enum for possible CAM SMMU operations */
+enum cam_smmu_ops_param {
+ CAM_SMMU_ATTACH,
+ CAM_SMMU_DETACH,
+ CAM_SMMU_VOTE,
+ CAM_SMMU_DEVOTE,
+ CAM_SMMU_OPS_INVALID
+};
+
+enum cam_smmu_map_dir {
+ CAM_SMMU_MAP_READ,
+ CAM_SMMU_MAP_WRITE,
+ CAM_SMMU_MAP_RW,
+ CAM_SMMU_MAP_INVALID
+};
+
+enum cam_smmu_region_id {
+ CAM_SMMU_REGION_FIRMWARE,
+ CAM_SMMU_REGION_SHARED,
+ CAM_SMMU_REGION_SCRATCH,
+ CAM_SMMU_REGION_IO
+};
+
+/**
+ * @brief : Gets an smmu handle
+ *
+ * @param identifier: Unique identifier to be used by clients which they
+ * should get from device tree. CAM SMMU driver will
+ * not enforce how this string is obtained and will
+ * only validate this against the list of permitted
+ * identifiers
+ * @param handle_ptr: Based on the indentifier, CAM SMMU drivier will
+ * fill the handle pointed by handle_ptr
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_get_handle(char *identifier, int *handle_ptr);
+
+/**
+ * @brief : Performs IOMMU operations
+ *
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param op : Operation to be performed. Can be either CAM_SMMU_ATTACH
+ * or CAM_SMMU_DETACH
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_ops(int handle, enum cam_smmu_ops_param op);
+
+/**
+ * @brief : Maps IOVA for calling driver
+ *
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param ion_fd: ION handle identifying the memory buffer.
+ * @dir : Mapping direction: which will traslate toDMA_BIDIRECTIONAL,
+ * DMA_TO_DEVICE or DMA_FROM_DEVICE
+ * @dma_addr : Pointer to physical address where mapped address will be
+ * returned if region_id is CAM_SMMU_REGION_IO. If region_id is
+ * CAM_SMMU_REGION_SHARED, dma_addr is used as an input parameter
+ * which specifies the cpu virtual address to map.
+ * @len : Length of buffer mapped returned by CAM SMMU driver.
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_map_iova(int handle,
+ int ion_fd, enum cam_smmu_map_dir dir,
+ dma_addr_t *dma_addr, size_t *len_ptr,
+ enum cam_smmu_region_id region_id);
+
+/**
+ * @brief : Unmaps IOVA for calling driver
+ *
+ * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * @param ion_fd: ION handle identifying the memory buffer.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_unmap_iova(int handle,
+ int ion_fd,
+ enum cam_smmu_region_id region_id);
+
+/**
+ * @brief : Allocates a scratch buffer
+ *
+ * This function allocates a scratch virtual buffer of length virt_len in the
+ * device virtual address space mapped to phys_len physically contiguous bytes
+ * in that device's SMMU.
+ *
+ * virt_len and phys_len are expected to be aligned to PAGE_SIZE and with each
+ * other, otherwise -EINVAL is returned.
+ *
+ * -EINVAL will be returned if virt_len is less than phys_len.
+ *
+ * Passing a too large phys_len might also cause failure if that much size is
+ * not available for allocation in a physically contiguous way.
+ *
+ * @param handle : Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * @param dir : Direction of mapping which will translate to IOMMU_READ
+ * IOMMU_WRITE or a bit mask of both.
+ * @param paddr_ptr: Device virtual address that the client device will be
+ * able to read from/write to
+ * @param virt_len : Virtual length of the scratch buffer
+ * @param phys_len : Physical length of the scratch buffer
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int cam_smmu_get_scratch_iova(int handle,
+ enum cam_smmu_map_dir dir,
+ dma_addr_t *paddr_ptr,
+ size_t virt_len,
+ size_t phys_len);
+
+/**
+ * @brief : Frees a scratch buffer
+ *
+ * This function frees a scratch buffer and releases the corresponding SMMU
+ * mappings.
+ *
+ * @param handle : Handle to identify the CAMSMMU client (IFE, ICP, etc.)
+ * @param paddr : Device virtual address of client's scratch buffer that
+ * will be freed.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int cam_smmu_put_scratch_iova(int handle,
+ dma_addr_t paddr);
+
+/**
+ *@brief : Destroys an smmu handle
+ *
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_destroy_handle(int handle);
+
+/**
+ * @brief : Finds index by handle in the smmu client table
+ *
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @return Index of SMMU client. Nagative in case of error.
+ */
+int cam_smmu_find_index_by_handle(int hdl);
+
+/**
+ * @brief : Registers smmu fault handler for client
+ *
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param client_page_fault_handler: It is triggered in IOMMU page fault
+ * @param token: It is input param when trigger page fault handler
+ */
+void cam_smmu_reg_client_page_fault_handler(int handle,
+ void (*client_page_fault_handler)(struct iommu_domain *,
+ struct device *, unsigned long,
+ int, void*), void *token);
+
+/**
+ * @brief Maps memory from an ION fd into IOVA space
+ *
+ * @param handle: SMMU handle identifying the context bank to map to
+ * @param ion_fd: ION fd of memory to map to
+ * @param paddr_ptr: Pointer IOVA address that will be returned
+ * @param len_ptr: Length of memory mapped
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_get_iova(int handle, int ion_fd,
+ dma_addr_t *paddr_ptr, size_t *len_ptr);
+/**
+ * @brief Unmaps memory from context bank
+ *
+ * @param handle: SMMU handle identifying the context bank
+ * @param ion_fd: ION fd of memory to unmap
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_put_iova(int handle, int ion_fd);
+
+/**
+ * @brief Maps secure memory for SMMU handle
+ *
+ * @param handle: SMMU handle identifying context bank
+ * @param ion_fd: ION fd to map securely
+ * @param dir: DMA Direction for the mapping
+ * @param dma_addr: Returned IOVA address after mapping
+ * @param len_ptr: Length of memory mapped
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_map_sec_iova(int handle,
+ int ion_fd, enum cam_smmu_map_dir dir,
+ dma_addr_t *dma_addr, size_t *len_ptr);
+
+/**
+ * @brief Unmaps secure memopry for SMMU handle
+ *
+ * @param handle: SMMU handle identifying context bank
+ * @param ion_fd: ION fd to unmap
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_unmap_sec_iova(int handle, int ion_fd);
+
+
+/**
+ * @brief Allocates firmware for context bank
+ *
+ * @param smmu_hdl: SMMU handle identifying context bank
+ * @param iova: IOVA address of allocated firmware
+ * @param kvaddr: CPU mapped address of allocated firmware
+ * @param len: Length of allocated firmware memory
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_alloc_firmware(int32_t smmu_hdl,
+ dma_addr_t *iova,
+ uint64_t *kvaddr,
+ size_t *len);
+
+/**
+ * @brief Deallocates firmware memory for context bank
+ *
+ * @param smmu_hdl: SMMU handle identifying the context bank
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_dealloc_firmware(int32_t smmu_hdl);
+#endif /* _CAM_SMMU_API_H_ */
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 3bf6ce0..9194b44 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -96,7 +96,6 @@
* @SDE_CAPS_R1_WB: MDSS V1.x WB block
* @SDE_CAPS_R3_WB: MDSS V3.x WB block
* @SDE_CAPS_R3_1P5_DOWNSCALE: 1.5x downscale rotator support
- * @SDE_CAPS_MIN_BUS_VOTE: minimum bus vote prior to power enable
* @SDE_CAPS_SBUF_1: stream buffer support for inline rotation
* @SDE_CAPS_UBWC_2: universal bandwidth compression version 2
*/
@@ -105,7 +104,6 @@
SDE_CAPS_R3_WB,
SDE_CAPS_R3_1P5_DOWNSCALE,
SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
- SDE_CAPS_MIN_BUS_VOTE,
SDE_CAPS_SBUF_1,
SDE_CAPS_UBWC_2,
SDE_CAPS_MAX,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 9a28700..8bd5d4d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -300,7 +300,6 @@
static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
{
- struct sde_rot_data_type *mdata = sde_rot_get_mdata();
int ret;
if (WARN_ON(mgr->regulator_enable == on)) {
@@ -311,7 +310,7 @@
SDEROT_EVTLOG(on);
SDEROT_DBG("%s: rotator regulators\n", on ? "Enable" : "Disable");
- if (test_bit(SDE_CAPS_MIN_BUS_VOTE, mdata->sde_caps_map) && on) {
+ if (on) {
mgr->minimum_bw_vote = mgr->enable_bw_vote;
sde_rotator_update_perf(mgr);
}
@@ -330,7 +329,7 @@
if (mgr->ops_hw_post_pmevent)
mgr->ops_hw_post_pmevent(mgr, on);
- if (test_bit(SDE_CAPS_MIN_BUS_VOTE, mdata->sde_caps_map) && !on) {
+ if (!on) {
mgr->minimum_bw_vote = 0;
sde_rotator_update_perf(mgr);
}
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 8f2746d..2672f1e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -2366,7 +2366,6 @@
/* features exposed via mdss h/w version */
if (IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version, SDE_MDP_HW_REV_400)) {
SDEROT_DBG("Supporting sys cache inline rotation\n");
- set_bit(SDE_CAPS_MIN_BUS_VOTE, mdata->sde_caps_map);
set_bit(SDE_CAPS_SBUF_1, mdata->sde_caps_map);
set_bit(SDE_CAPS_UBWC_2, mdata->sde_caps_map);
rot->inpixfmts = sde_hw_rotator_v4_inpixfmts;
@@ -2597,6 +2596,11 @@
}
if ((src_w != dst_w) || (src_h != dst_h)) {
+ if (!dst_w || !dst_h) {
+ SDEROT_DBG("zero output width/height not support\n");
+ ret = -EINVAL;
+ goto dnsc_err;
+ }
if ((src_w % dst_w) || (src_h % dst_h)) {
SDEROT_DBG("non integral scale not support\n");
ret = -EINVAL;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index ac6ded0..6c49ff5 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1273,6 +1273,7 @@
hfi->qp_packed = hal_quant->qpi | hal_quant->qpp << 8 |
hal_quant->qpb << 16;
hfi->layer_id = hal_quant->layer_id;
+ hfi->enable = hal_quant->enable;
pkt->size += sizeof(u32) + sizeof(struct hfi_quantization);
break;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index e198d8e..91d657d 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -286,6 +286,17 @@
.qmenu = NULL,
},
{
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK,
+ .name = "QP mask for diff frame types",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 7,
+ .default_value = 7,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
.name = "Intra Period for B frames",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -1644,43 +1655,65 @@
pdata = &baselayerid;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP: {
- struct v4l2_ctrl *qpp, *qpb;
+ struct v4l2_ctrl *qpp, *qpb, *mask;
property_id = HAL_CONFIG_VENC_FRAME_QP;
qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP);
qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP);
+ mask = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK);
quant.qpi = ctrl->val;
quant.qpp = qpp->val;
quant.qpb = qpb->val;
+ quant.enable = mask->val;
quant.layer_id = MSM_VIDC_ALL_LAYER_ID;
pdata = &quant;
break;
}
case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP: {
- struct v4l2_ctrl *qpi, *qpb;
+ struct v4l2_ctrl *qpi, *qpb, *mask;
property_id = HAL_CONFIG_VENC_FRAME_QP;
qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP);
qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP);
+ mask = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK);
quant.qpp = ctrl->val;
quant.qpi = qpi->val;
quant.qpb = qpb->val;
+ quant.enable = mask->val;
quant.layer_id = MSM_VIDC_ALL_LAYER_ID;
pdata = &quant;
break;
}
case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP: {
- struct v4l2_ctrl *qpp, *qpi;
+ struct v4l2_ctrl *qpp, *qpi, *mask;
property_id = HAL_CONFIG_VENC_FRAME_QP;
qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP);
qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP);
+ mask = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK);
quant.qpb = ctrl->val;
quant.qpp = qpp->val;
quant.qpi = qpi->val;
+ quant.enable = mask->val;
+ quant.layer_id = MSM_VIDC_ALL_LAYER_ID;
+ pdata = &quant;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK: {
+ struct v4l2_ctrl *qpi, *qpp, *qpb;
+
+ property_id = HAL_CONFIG_VENC_FRAME_QP;
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP);
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP);
+ qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP);
+
+ quant.qpi = qpi->val;
+ quant.qpp = qpp->val;
+ quant.qpb = qpb->val;
+ quant.enable = ctrl->val;
quant.layer_id = MSM_VIDC_ALL_LAYER_ID;
pdata = &quant;
break;
@@ -1852,7 +1885,7 @@
struct v4l2_ext_control *control;
struct hfi_device *hdev;
struct hal_ltr_mode ltr_mode;
- u32 property_id = 0, layer_id = MSM_VIDC_ALL_LAYER_ID;
+ u32 property_id = 0;
void *pdata = NULL;
struct msm_vidc_capability *cap = NULL;
struct hal_aspect_ratio sar;
@@ -1930,76 +1963,75 @@
pdata = &blur_res;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_LAYER_ID:
- layer_id = control[i].value;
+ qp.layer_id = control[i].value;
+ /* Enable QP for all frame types by default */
+ qp.enable = 7;
+ qp_range.layer_id = control[i].value;
i++;
while (i < ctrl->count) {
switch (control[i].id) {
case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP:
qp.qpi = control[i].value;
- qp.layer_id = layer_id;
property_id =
HAL_CONFIG_VENC_FRAME_QP;
pdata = &qp;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP:
qp.qpp = control[i].value;
- qp.layer_id = layer_id;
property_id =
HAL_CONFIG_VENC_FRAME_QP;
pdata = &qp;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP:
qp.qpb = control[i].value;
- qp.layer_id = layer_id;
+ property_id =
+ HAL_CONFIG_VENC_FRAME_QP;
+ pdata = &qp;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK:
+ qp.enable = control[i].value;
property_id =
HAL_CONFIG_VENC_FRAME_QP;
pdata = &qp;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN:
qp_range.qpi_min = control[i].value;
- qp_range.layer_id = layer_id;
property_id =
HAL_PARAM_VENC_SESSION_QP_RANGE;
pdata = &qp_range;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN:
qp_range.qpp_min = control[i].value;
- qp_range.layer_id = layer_id;
property_id =
- HAL_PARAM_VENC_SESSION_QP_RANGE;
+ HAL_PARAM_VENC_SESSION_QP_RANGE;
pdata = &qp_range;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN:
qp_range.qpb_min = control[i].value;
- qp_range.layer_id = layer_id;
property_id =
HAL_PARAM_VENC_SESSION_QP_RANGE;
pdata = &qp_range;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX:
qp_range.qpi_max = control[i].value;
- qp_range.layer_id = layer_id;
property_id =
HAL_PARAM_VENC_SESSION_QP_RANGE;
pdata = &qp_range;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX:
qp_range.qpp_max = control[i].value;
- qp_range.layer_id = layer_id;
property_id =
HAL_PARAM_VENC_SESSION_QP_RANGE;
pdata = &qp_range;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX:
qp_range.qpb_max = control[i].value;
- qp_range.layer_id = layer_id;
property_id =
HAL_PARAM_VENC_SESSION_QP_RANGE;
pdata = &qp_range;
break;
case V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE:
bitrate.bit_rate = control[i].value;
- bitrate.layer_id = layer_id;
property_id =
HAL_CONFIG_VENC_TARGET_BITRATE;
pdata = &bitrate;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index f8e0a6a..8752378 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -595,6 +595,7 @@
u32 qpp;
u32 qpb;
u32 layer_id;
+ u32 enable;
};
struct hal_quantization_range {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index d6c4bcb..77164be 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -536,7 +536,8 @@
struct hfi_quantization {
u32 qp_packed;
u32 layer_id;
- u32 reserved[4];
+ u32 enable;
+ u32 reserved[3];
};
struct hfi_quantization_range {
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index a62870e..cf96ac1 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1061,6 +1061,8 @@
(V4L2_CID_MPEG_MSM_VIDC_BASE + 106)
#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 107)
+#define V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 108)
enum v4l2_mpeg_vidc_video_venc_iframesize_type {
V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT,
diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h
index 68db5e1..b736755 100644
--- a/include/uapi/media/cam_req_mgr.h
+++ b/include/uapi/media/cam_req_mgr.h
@@ -190,9 +190,6 @@
#define CAM_REQ_MGR_MAP_BUF (CAM_COMMON_OPCODE_MAX + 10)
#define CAM_REQ_MGR_RELEASE_BUF (CAM_COMMON_OPCODE_MAX + 11)
#define CAM_REQ_MGR_CACHE_OPS (CAM_COMMON_OPCODE_MAX + 12)
-#define CAM_REQ_MGR_GET_MMU_HDLS_DEBUG (CAM_COMMON_OPCODE_MAX + 13)
-#define CAM_REQ_MGR_GET_IO_BUF_DEBUG (CAM_COMMON_OPCODE_MAX + 14)
-#define CAM_REQ_MGR_GET_KMD_BUF_DEBUG (CAM_COMMON_OPCODE_MAX + 15)
/* end of cam_req_mgr opcodes */
#define CAM_MEM_FLAG_HW_READ_WRITE (1<<0)
@@ -206,6 +203,7 @@
#define CAM_MEM_FLAG_STATS_BUF_TYPE (1<<8)
#define CAM_MEM_FLAG_PACKET_BUF_TYPE (1<<9)
#define CAM_MEM_FLAG_CACHE (1<<10)
+#define CAM_MEM_FLAG_HW_SHARED_ACCESS (1<<11)
#define CAM_MEM_MMU_MAX_HANDLE 16
diff --git a/sound/soc/msm/sdm845.c b/sound/soc/msm/sdm845.c
index 2b8c9c7..0f41387 100644
--- a/sound/soc/msm/sdm845.c
+++ b/sound/soc/msm/sdm845.c
@@ -509,6 +509,7 @@
.key_code[7] = 0,
.linein_th = 5000,
.moisture_en = true,
+ .mbhc_micbias = MIC_BIAS_2,
.anc_micbias = MIC_BIAS_2,
.enable_anc_mic_detect = false,
};