Merge "usb: gadget: Fix f_rmnet to maintain notifications after set_alt" into msm-3.0
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 8898a95..70fa101 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -8,6 +8,23 @@
force_ro Enforce read-only access even if write protect switch is off.
+ num_wr_reqs_to_start_packing This attribute is used to determine
+ the trigger for activating the write packing, in case the write
+ packing control feature is enabled.
+
+ When the MMC manages to reach a point where num_wr_reqs_to_start_packing
+ write requests could be packed, it enables the write packing feature.
+ This allows us to start the write packing only when it is beneficial
+ and has minimum affect on the read latency.
+
+ The number of potential packed requests that will trigger the packing
+ can be configured via sysfs by writing the required value to:
+ /sys/block/<block_dev_name>/num_wr_reqs_to_start_packing.
+
+ The default value of num_wr_reqs_to_start_packing was determined by
+ running parallel lmdd write and lmdd read operations and calculating
+ the max number of packed writes requests.
+
SD and MMC Device Attributes
============================
diff --git a/arch/arm/boot/dts/msm9625.dts b/arch/arm/boot/dts/msm9625.dts
new file mode 100644
index 0000000..6756927
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625.dts
@@ -0,0 +1,41 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ */
+
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 9625";
+ compatible = "qcom,msm9625";
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@F9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xF9000000 0x1000>,
+ <0xF9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ };
+
+ timer {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ interrupts = <0 7 0>;
+ clock-frequency = <5000000>;
+ };
+};
diff --git a/arch/arm/boot/dts/msmcopper-rumi.dts b/arch/arm/boot/dts/msmcopper-rumi.dts
index d6e23ad..5bfb228 100644
--- a/arch/arm/boot/dts/msmcopper-rumi.dts
+++ b/arch/arm/boot/dts/msmcopper-rumi.dts
@@ -75,6 +75,23 @@
};
};
+ i2c@f9966000 {
+ status = "disable";
+ };
+
+ i2c@f9967000 {
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ reg = <0Xf9967000 0x1000>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 105 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+ gpios = <&msmgpio 83 0>, /* DAT */
+ <&msmgpio 84 0>; /* CLK */
+ };
+
slim@fe12f000 {
status = "disable";
};
@@ -83,10 +100,6 @@
status = "disable";
};
- i2c@f9966000 {
- status = "disable";
- };
-
qcom,ssusb@F9200000 {
status = "disable";
};
diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig
index feb5aa5..e9360d1 100644
--- a/arch/arm/configs/msm-copper_defconfig
+++ b/arch/arm/configs/msm-copper_defconfig
@@ -43,6 +43,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_PIL_LPASS_QDSP6V5=y
+CONFIG_MSM_PIL_PRONTO=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_OCMEM=y
CONFIG_NO_HZ=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 4cbd1d1..f524828 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -309,6 +309,7 @@
CONFIG_SMB349_CHARGER=y
CONFIG_PM8921_CHARGER=y
CONFIG_PM8921_BMS=y
+CONFIG_PM8921_BCL=y
CONFIG_SENSORS_PM8XXX_ADC=y
CONFIG_SENSORS_EPM_ADC=y
CONFIG_THERMAL=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index a901684..1c2876d 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -318,6 +318,7 @@
CONFIG_SMB349_CHARGER=y
CONFIG_PM8921_CHARGER=y
CONFIG_PM8921_BMS=y
+CONFIG_PM8921_BCL=y
CONFIG_SENSORS_PM8XXX_ADC=y
CONFIG_SENSORS_EPM_ADC=y
CONFIG_THERMAL=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 50919f4..4fd15b3 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -884,7 +884,7 @@
default "0x80200000" if ARCH_APQ8064
default "0x80200000" if ARCH_MSM8960
default "0x80200000" if ARCH_MSM8930
- default "0x20200000" if ARCH_MSMCOPPER
+ default "0x00000000" if ARCH_MSMCOPPER
default "0x10000000" if ARCH_FSM9XXX
default "0x20200000" if ARCH_MSM9625
default "0x00200000" if !MSM_STACKED_MEMORY
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 58c630e..ca44859 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -46,7 +46,7 @@
zreladdr-$(CONFIG_ARCH_APQ8064) := 0x80208000
# MSMCOPPER
- zreladdr-$(CONFIG_ARCH_MSMCOPPER) := 0x20208000
+ zreladdr-$(CONFIG_ARCH_MSMCOPPER) := 0x00008000
# MSM9615
zreladdr-$(CONFIG_ARCH_MSM9615) := 0x40808000
diff --git a/arch/arm/mach-msm/acpuclock-8x60.c b/arch/arm/mach-msm/acpuclock-8x60.c
index 787483b..48efa18 100644
--- a/arch/arm/mach-msm/acpuclock-8x60.c
+++ b/arch/arm/mach-msm/acpuclock-8x60.c
@@ -217,40 +217,7 @@
};
/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_slowest[] = {
- { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
- /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
- { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
- { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000},
- { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000},
- { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000},
- { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000},
- { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000},
- { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000},
- { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000},
- { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000},
- { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 975000, 0x03006000},
- { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 975000, 0x03006000},
- { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 1000000, 0x03006000},
- { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1025000, 0x03006000},
- { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1025000, 0x03006000},
- { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1050000, 0x03006000},
- { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1075000, 0x03006000},
- { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1100000, 0x03006000},
- { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1125000, 0x03006000},
- { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1150000, 0x03006000},
- { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1175000, 0x03006000},
- { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1200000, 0x03006000},
- { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1250000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1275000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1300000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1325000, 0x03006000},
- { {0, 0}, 0 },
-};
-
-/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_slower[] = {
+static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_slow[] = {
{ {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
/* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
{ {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
@@ -276,47 +243,11 @@
{ {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1175000, 0x03006000},
{ {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1200000, 0x03006000},
{ {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1250000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1275000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1300000, 0x03006000},
{ {0, 0}, 0 },
};
/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_slow[] = {
- { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
- /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
- { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
- { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000},
- { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000},
- { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000},
- { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000},
- { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000},
- { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000},
- { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000},
- { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000},
- { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 975000, 0x03006000},
- { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 975000, 0x03006000},
- { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 1000000, 0x03006000},
- { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1025000, 0x03006000},
- { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1025000, 0x03006000},
- { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1050000, 0x03006000},
- { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1075000, 0x03006000},
- { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1100000, 0x03006000},
- { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1125000, 0x03006000},
- { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1150000, 0x03006000},
- { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1150000, 0x03006000},
- { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1175000, 0x03006000},
- { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1200000, 0x03006000},
- { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1225000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1250000, 0x03006000},
- { {0, 0}, 0 },
-};
-
-/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_nom[] = {
+static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_nom[] = {
{ {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
/* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
{ {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
@@ -342,14 +273,11 @@
{ {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1150000, 0x03006000},
{ {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1150000, 0x03006000},
{ {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1175000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1175000, 0x03006000},
- { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1200000, 0x03006000},
- { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1200000, 0x03006000},
{ {0, 0}, 0 },
};
/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
-static struct clkctl_acpu_speed acpu_freq_tbl_fast[] = {
+static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_fast[] = {
{ {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000},
/* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
{ {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000},
@@ -375,13 +303,141 @@
{ {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1100000, 0x03006000},
{ {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1100000, 0x03006000},
{ {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1125000, 0x03006000},
- { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1125000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_slower[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 825000, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 837500, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 850000, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 875000, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 900000, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 912500, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 937500, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 962500, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 987500, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1012500, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1025000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1062500, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1087500, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1100000, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1125000, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1150000, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1187500, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1225000, 0x03006000},
+ { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1262500, 0x03006000},
+ { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1300000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_slow[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 825000, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 837500, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 850000, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 862500, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 887500, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 900000, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 925000, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 937500, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 962500, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 987500, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1000000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1025000, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1050000, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1062500, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1087500, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1112500, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1150000, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1175000, 0x03006000},
+ { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1212500, 0x03006000},
+ { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1250000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_nom[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 812500, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 825000, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 837500, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 850000, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 875000, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 887500, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 900000, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 912500, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 937500, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 950000, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 975000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 987500, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1012500, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1025000, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1050000, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1075000, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1112500, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1137500, 0x03006000},
+ { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1175000, 0x03006000},
+ { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1200000, 0x03006000},
+ { {0, 0}, 0 },
+};
+
+/* SCPLL frequencies = 2 * 27 MHz * L_VAL */
+static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_fast[] = {
+ { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000},
+ /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */
+ { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000},
+ { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000},
+ { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000},
+ { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000},
+ { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 775000, 0x03006000},
+ { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 787500, 0x03006000},
+ { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 800000, 0x03006000},
+ { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 812500, 0x03006000},
+ { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 825000, 0x03006000},
+ { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 837500, 0x03006000},
+ { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 862500, 0x03006000},
+ { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 875000, 0x03006000},
+ { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 887500, 0x03006000},
+ { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 900000, 0x03006000},
+ { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 925000, 0x03006000},
+ { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 937500, 0x03006000},
+ { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 950000, 0x03006000},
+ { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 962500, 0x03006000},
+ { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 975000, 0x03006000},
+ { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1000000, 0x03006000},
+ { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1025000, 0x03006000},
+ { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1050000, 0x03006000},
+ { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1075000, 0x03006000},
+ { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1100000, 0x03006000},
{ {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1125000, 0x03006000},
{ {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1150000, 0x03006000},
{ {0, 0}, 0 },
};
-
/* acpu_freq_tbl row to use when reconfiguring SC/L2 PLLs. */
#define CAL_IDX 1
@@ -930,7 +986,7 @@
static __init struct clkctl_acpu_speed *select_freq_plan(void)
{
- uint32_t pte_efuse, speed_bin, pvs, max_khz;
+ uint32_t pte_efuse, speed_bin, pvs;
struct clkctl_acpu_speed *f;
pte_efuse = readl_relaxed(QFPROM_PTE_EFUSE_ADDR);
@@ -944,67 +1000,55 @@
pvs = (pte_efuse >> 13) & 0x7;
if (speed_bin == 0x2) {
- max_khz = 1674000;
switch (pvs) {
case 0x7:
- case 0x5:
- acpu_freq_tbl = acpu_freq_tbl_slowest;
- pr_info("ACPU PVS: Slowest\n");
- break;
case 0x4:
- acpu_freq_tbl = acpu_freq_tbl_slower;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_slower;
pr_info("ACPU PVS: Slower\n");
break;
case 0x0:
- acpu_freq_tbl = acpu_freq_tbl_slow;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_slow;
pr_info("ACPU PVS: Slow\n");
break;
case 0x1:
- acpu_freq_tbl = acpu_freq_tbl_nom;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_nom;
pr_info("ACPU PVS: Nominal\n");
break;
case 0x3:
- acpu_freq_tbl = acpu_freq_tbl_fast;
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_fast;
pr_info("ACPU PVS: Fast\n");
break;
default:
- acpu_freq_tbl = acpu_freq_tbl_slowest;
- pr_warn("ACPU PVS: Unknown. Defaulting to slowest.\n");
+ acpu_freq_tbl = acpu_freq_tbl_1674mhz_slower;
+ pr_warn("ACPU PVS: Unknown. Defaulting to slower.\n");
break;
}
} else if (speed_bin == 0x1) {
- max_khz = 1512000;
switch (pvs) {
case 0x0:
case 0x7:
- acpu_freq_tbl = acpu_freq_tbl_slow;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_slow;
pr_info("ACPU PVS: Slow\n");
break;
case 0x1:
- acpu_freq_tbl = acpu_freq_tbl_nom;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_nom;
pr_info("ACPU PVS: Nominal\n");
break;
case 0x3:
- acpu_freq_tbl = acpu_freq_tbl_fast;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_fast;
pr_info("ACPU PVS: Fast\n");
break;
default:
- acpu_freq_tbl = acpu_freq_tbl_slow;
+ acpu_freq_tbl = acpu_freq_tbl_1512mhz_slow;
pr_warn("ACPU PVS: Unknown. Defaulting to slow.\n");
break;
}
} else {
- max_khz = 1188000;
acpu_freq_tbl = acpu_freq_tbl_1188mhz;
}
- /* Truncate the table based to max_khz. */
- for (f = acpu_freq_tbl; f->acpuclk_khz != 0; f++) {
- if (f->acpuclk_khz > max_khz) {
- f->acpuclk_khz = 0;
- break;
- }
- }
+ for (f = acpu_freq_tbl; f->acpuclk_khz != 0; f++)
+ ;
f--;
pr_info("Max ACPU freq: %u KHz\n", f->acpuclk_khz);
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 0c95aa4..f973d56 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1956,7 +1956,7 @@
/* T6 Object */
0, 0, 0, 0, 0, 0,
/* T38 Object */
- 12, 2, 0, 17, 1, 12, 0, 0, 0, 0,
+ 12, 3, 0, 24, 5, 12, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1968,7 +1968,7 @@
/* T8 Object */
25, 0, 20, 20, 0, 0, 20, 50, 0, 0,
/* T9 Object */
- 131, 0, 0, 26, 42, 0, 32, 80, 2, 5,
+ 139, 0, 0, 26, 42, 0, 32, 80, 2, 5,
0, 5, 5, 0, 10, 30, 10, 10, 255, 2,
85, 5, 10, 10, 10, 10, 135, 55, 70, 40,
10, 5, 0, 0, 0,
@@ -1978,13 +1978,13 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
/* T25 Object */
- 3, 0, 60, 115, 156, 99,
+ 1, 0, 60, 115, 156, 99,
/* T27 Object */
0, 0, 0, 0, 0, 0, 0,
/* T40 Object */
0, 0, 0, 0, 0,
/* T42 Object */
- 2, 0, 255, 0, 255, 0, 0, 0, 0, 0,
+ 0, 0, 255, 0, 255, 0, 0, 0, 0, 0,
/* T43 Object */
0, 0, 0, 0, 0, 0, 0, 64, 0, 8,
16,
@@ -1993,7 +1993,7 @@
/* T47 Object */
0, 0, 0, 0, 0, 0, 3, 64, 66, 0,
/* T48 Object */
- 31, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 1, 64, 64, 0, 0, 0, 0, 0, 0, 0,
48, 40, 0, 10, 10, 0, 0, 100, 10, 80,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
52, 0, 12, 0, 17, 0, 1, 0, 0, 0,
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
new file mode 100644
index 0000000..7a7d511
--- /dev/null
+++ b/arch/arm/mach-msm/board-9625.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/memory.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/gic.h>
+#include <asm/arch_timer.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/socinfo.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include "clock.h"
+
+static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("phy_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("core_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("iface_clk", NULL, "msm_otg", OFF),
+ CLK_DUMMY("xo", NULL, "msm_otg", OFF),
+ CLK_DUMMY("dfab_clk", DFAB_CLK, NULL, 0),
+ CLK_DUMMY("dma_bam_pclk", DMA_BAM_P_CLK, NULL, 0),
+ CLK_DUMMY("mem_clk", NULL, NULL, 0),
+ CLK_DUMMY("core_clk", NULL, "spi_qsd.1", OFF),
+ CLK_DUMMY("iface_clk", NULL, "spi_qsd.1", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9966000.i2c", 0),
+ CLK_DUMMY("iface_clk", NULL, "f9966000.i2c", 0),
+ CLK_DUMMY("core_clk", NULL, "fe12f000.slim", OFF),
+};
+
+struct clock_init_data msm_dummy_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
+
+static struct of_device_id irq_match[] __initdata = {
+ { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
+ { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ {}
+};
+
+static const char *msm9625_dt_match[] __initconst = {
+ "qcom,msm9625",
+ NULL
+};
+
+static struct of_dev_auxdata msm9625_auxdata_lookup[] __initdata = {
+ {}
+};
+
+void __init msm9625_init_irq(void)
+{
+ of_irq_init(irq_match);
+}
+
+static void __init msm_dt_timer_init(void)
+{
+ arch_timer_of_register();
+}
+
+static struct sys_timer msm_dt_timer = {
+ .init = msm_dt_timer_init
+};
+
+void __init msm9625_init(void)
+{
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+ msm_clock_init(&msm_dummy_clock_init_data);
+ of_platform_populate(NULL, of_default_bus_match_table,
+ msm9625_auxdata_lookup, NULL);
+}
+
+DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
+ .map_io = msm_map_msm9625_io,
+ .init_irq = msm9625_init_irq,
+ .init_machine = msm9625_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = msm9625_dt_match,
+ .nr_irqs = -1,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-copper-gpiomux.c b/arch/arm/mach-msm/board-copper-gpiomux.c
index 0ea33c7..caba698 100644
--- a/arch/arm/mach-msm/board-copper-gpiomux.c
+++ b/arch/arm/mach-msm/board-copper-gpiomux.c
@@ -54,6 +54,12 @@
},
};
#endif
+static struct gpiomux_setting gpio_i2c_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
@@ -83,6 +89,18 @@
},
#endif
{
+ .gpio = 83, /* BLSP11 QUP I2C_DAT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ .gpio = 84, /* BLSP11 QUP I2C_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
.gpio = 45, /* BLSP8 UART TX */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_uart_config,
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index 75d5f15..4567c76 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -627,13 +627,16 @@
for (i = 0; i < ARRAY_SIZE(bt_vregs); i++) {
if (IS_ERR_OR_NULL(bt_vregs[i].reg)) {
- rc = bt_vregs[i].reg ?
- PTR_ERR(bt_vregs[i].reg) :
- -ENODEV;
- dev_err(&msm_bt_power_device.dev,
- "%s: invalid regulator handle for %s: %d\n",
+ bt_vregs[i].reg =
+ regulator_get(&msm_bt_power_device.dev,
+ bt_vregs[i].name);
+ if (IS_ERR(bt_vregs[i].reg)) {
+ rc = PTR_ERR(bt_vregs[i].reg);
+ dev_err(&msm_bt_power_device.dev,
+ "%s: could not get regulator %s: %d\n",
__func__, bt_vregs[i].name, rc);
- goto reg_disable;
+ goto reg_disable;
+ }
}
rc = on ? regulator_set_voltage(bt_vregs[i].reg,
@@ -687,6 +690,7 @@
i--;
regulator_disable(bt_vregs[i].reg);
regulator_put(bt_vregs[i].reg);
+ bt_vregs[i].reg = NULL;
}
}
return rc;
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 3ee59b1..a1b9c1c 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -770,6 +770,8 @@
.b = {
.ctl_reg = MAXI_EN5_REG,
.en_mask = BIT(12),
+ .hwcg_reg = MAXI_EN5_REG,
+ .hwcg_mask = BIT(11),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(16),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -787,6 +789,8 @@
.b = {
.ctl_reg = MAXI_EN5_REG,
.en_mask = BIT(25),
+ .hwcg_reg = MAXI_EN5_REG,
+ .hwcg_mask = BIT(24),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(17),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -1176,6 +1180,8 @@
.b = {
.ctl_reg = AHB_EN3_REG,
.en_mask = BIT(1),
+ .hwcg_reg = AHB_EN3_REG,
+ .hwcg_mask = BIT(0),
.reset_reg = SW_RESET_AHB2_REG,
.reset_mask = BIT(2),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -5991,7 +5997,7 @@
* the clock is halted. The sleep and wake-up delays are set to safe
* values.
*/
- if (cpu_is_msm8960()) {
+ if (cpu_is_msm8960() || cpu_is_apq8064()) {
rmwreg(0x44000000, AHB_EN_REG, 0x6C000103);
writel_relaxed(0x3C7097F9, AHB_EN2_REG);
} else {
@@ -5999,7 +6005,7 @@
writel_relaxed(0x000007F9, AHB_EN2_REG);
}
if (cpu_is_apq8064())
- rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
+ rmwreg(0x00000001, AHB_EN3_REG, 0x00000001);
/* Deassert all locally-owned MM AHB resets. */
rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
@@ -6008,8 +6014,9 @@
/* Initialize MM AXI registers: Enable HW gating for all clocks that
* support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
* delays to safe values. */
- if (cpu_is_msm8960() &&
- SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) {
+ if ((cpu_is_msm8960() &&
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) ||
+ cpu_is_apq8064()) {
rmwreg(0x0003AFF9, MAXI_EN_REG, 0x0803FFFF);
rmwreg(0x3A27FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
@@ -6020,10 +6027,10 @@
}
rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
if (cpu_is_apq8064())
- rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
+ rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
if (cpu_is_msm8930())
rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF);
- if (cpu_is_msm8960())
+ if (cpu_is_msm8960() || cpu_is_apq8064())
rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF);
else
rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
diff --git a/arch/arm/mach-msm/clock-copper.c b/arch/arm/mach-msm/clock-copper.c
index 7812321..896806d 100644
--- a/arch/arm/mach-msm/clock-copper.c
+++ b/arch/arm/mach-msm/clock-copper.c
@@ -4716,6 +4716,7 @@
static struct clk_lookup msm_clocks_copper[] = {
CLK_LOOKUP("xo", cxo_clk_src.c, "msm_otg"),
CLK_LOOKUP("xo", cxo_clk_src.c, "pil-q6v5-lpass"),
+ CLK_LOOKUP("xo", cxo_clk_src.c, "pil_pronto"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"),
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 17ac3ac..5e2eaf1 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -556,6 +556,7 @@
void msm_map_fsm9xxx_io(void);
void msm_map_copper_io(void);
void msm_map_msm8625_io(void);
+void msm_map_msm9625_io(void);
void msm_init_irq(void);
void msm_copper_init_irq(void);
void vic_handle_irq(struct pt_regs *regs);
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-copper.h b/arch/arm/mach-msm/include/mach/msm_iomap-copper.h
index b560276..441f82a 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-copper.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-copper.h
@@ -23,7 +23,7 @@
*
*/
-#define COPPER_MSM_SHARED_RAM_PHYS 0x18D00000
+#define COPPER_MSM_SHARED_RAM_PHYS 0x0FA00000
#define COPPER_QGIC_DIST_PHYS 0xF9000000
#define COPPER_QGIC_DIST_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h b/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h
index 333f5af..2eb59f5 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h
@@ -17,7 +17,7 @@
struct rpm_regulator;
-#ifdef CONFIG_MSM_RPM_REGULATOR_SMD
+#if defined(CONFIG_MSM_RPM_REGULATOR_SMD) || defined(CONFIG_MSM_RPM_REGULATOR)
struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply);
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator.h b/arch/arm/mach-msm/include/mach/rpm-regulator.h
index f9fc487..a010257 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator.h
@@ -81,6 +81,20 @@
};
/**
+ * enum rpm_vreg_voter - RPM regulator voter IDs for private APIs
+ */
+enum rpm_vreg_voter {
+ RPM_VREG_VOTER_REG_FRAMEWORK, /* for internal use only */
+ RPM_VREG_VOTER1, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER2, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER3, /* for use by other drivers */
+ RPM_VREG_VOTER4, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER5, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER6, /* for use by the acpu-clock driver */
+ RPM_VREG_VOTER_COUNT,
+};
+
+/**
* struct rpm_regulator_init_data - RPM regulator initialization data
* @init_data: regulator constraints
* @id: regulator id; from enum rpm_vreg_id
@@ -132,28 +146,27 @@
};
/**
- * struct rpm_regulator_platform_data - RPM regulator platform data
+ * struct rpm_regulator_consumer_mapping - mapping used by private consumers
*/
-struct rpm_regulator_platform_data {
- struct rpm_regulator_init_data *init_data;
- int num_regulators;
- enum rpm_vreg_version version;
- int vreg_id_vdd_mem;
- int vreg_id_vdd_dig;
+struct rpm_regulator_consumer_mapping {
+ const char *dev_name;
+ const char *supply;
+ int vreg_id;
+ enum rpm_vreg_voter voter;
+ int sleep_also;
};
/**
- * enum rpm_vreg_voter - RPM regulator voter IDs for private APIs
+ * struct rpm_regulator_platform_data - RPM regulator platform data
*/
-enum rpm_vreg_voter {
- RPM_VREG_VOTER_REG_FRAMEWORK, /* for internal use only */
- RPM_VREG_VOTER1, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER2, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER3, /* for use by other drivers */
- RPM_VREG_VOTER4, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER5, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER6, /* for use by the acpu-clock driver */
- RPM_VREG_VOTER_COUNT,
+struct rpm_regulator_platform_data {
+ struct rpm_regulator_init_data *init_data;
+ int num_regulators;
+ enum rpm_vreg_version version;
+ int vreg_id_vdd_mem;
+ int vreg_id_vdd_dig;
+ struct rpm_regulator_consumer_mapping *consumer_map;
+ int consumer_map_len;
};
#ifdef CONFIG_MSM_RPM_REGULATOR
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index c708df5..f663695 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -19,10 +19,12 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <mach/rpm.h>
#include <mach/rpm-regulator.h>
+#include <mach/rpm-regulator-smd.h>
#include <mach/socinfo.h>
#include "rpm_resources.h"
@@ -42,6 +44,15 @@
debug_mask, msm_rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR
);
+/* Used for access via the rpm_regulator_* API. */
+struct rpm_regulator {
+ int vreg_id;
+ enum rpm_vreg_voter voter;
+ int sleep_also;
+ int min_uV;
+ int max_uV;
+};
+
struct vreg_config *(*get_config[])(void) = {
[RPM_VREG_VERSION_8660] = get_config_8660,
[RPM_VREG_VERSION_8960] = get_config_8960,
@@ -49,6 +60,9 @@
[RPM_VREG_VERSION_8930] = get_config_8930,
};
+static struct rpm_regulator_consumer_mapping *consumer_map;
+static int consumer_map_len;
+
#define SET_PART(_vreg, _part, _val) \
_vreg->req[_vreg->part->_part.word].value \
= (_vreg->req[_vreg->part->_part.word].value \
@@ -615,6 +629,243 @@
}
EXPORT_SYMBOL_GPL(rpm_vreg_set_frequency);
+#define MAX_NAME_LEN 64
+/**
+ * rpm_regulator_get() - lookup and obtain a handle to an RPM regulator
+ * @dev: device for regulator consumer
+ * @supply: supply name
+ *
+ * Returns a struct rpm_regulator corresponding to the regulator producer,
+ * or ERR_PTR() containing errno.
+ *
+ * This function may only be called from nonatomic context. The mapping between
+ * <dev, supply> tuples and rpm_regulators struct pointers is specified via
+ * rpm-regulator platform data.
+ */
+struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply)
+{
+ struct rpm_regulator_consumer_mapping *mapping = NULL;
+ const char *devname = NULL;
+ struct rpm_regulator *regulator;
+ int i;
+
+ if (!config) {
+ pr_err("rpm-regulator driver has not probed yet.\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (consumer_map == NULL || consumer_map_len == 0) {
+ pr_err("No private consumer mapping has been specified.\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (supply == NULL) {
+ pr_err("supply name must be specified\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (dev)
+ devname = dev_name(dev);
+
+ for (i = 0; i < consumer_map_len; i++) {
+ /* If the mapping has a device set up it must match */
+ if (consumer_map[i].dev_name &&
+ (!devname || strncmp(consumer_map[i].dev_name, devname,
+ MAX_NAME_LEN)))
+ continue;
+
+ if (strncmp(consumer_map[i].supply, supply, MAX_NAME_LEN)
+ == 0) {
+ mapping = &consumer_map[i];
+ break;
+ }
+ }
+
+ if (mapping == NULL) {
+ pr_err("could not find mapping for dev=%s, supply=%s\n",
+ (devname ? devname : "(null)"), supply);
+ return ERR_PTR(-ENODEV);
+ }
+
+ regulator = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL);
+ if (regulator == NULL) {
+ pr_err("could not allocate memory for regulator\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ regulator->vreg_id = mapping->vreg_id;
+ regulator->voter = mapping->voter;
+ regulator->sleep_also = mapping->sleep_also;
+
+ return regulator;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_get);
+
+static int rpm_regulator_check_input(struct rpm_regulator *regulator)
+{
+ int rc = 0;
+
+ if (regulator == NULL) {
+ rc = -EINVAL;
+ pr_err("invalid (null) rpm_regulator pointer\n");
+ } else if (IS_ERR(regulator)) {
+ rc = PTR_ERR(regulator);
+ pr_err("invalid rpm_regulator pointer, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+/**
+ * rpm_regulator_put() - free the RPM regulator handle
+ * @regulator: RPM regulator handle
+ *
+ * Parameter reaggregation does not take place when rpm_regulator_put is called.
+ * Therefore, regulator enable state and voltage must be configured
+ * appropriately before calling rpm_regulator_put.
+ *
+ * This function may be called from either atomic or nonatomic context.
+ */
+void rpm_regulator_put(struct rpm_regulator *regulator)
+{
+ kfree(regulator);
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_put);
+
+/**
+ * rpm_regulator_enable() - enable regulator output
+ * @regulator: RPM regulator handle
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * This function may be called from either atomic or nonatomic context. This
+ * function may only be called for regulators which have the sleep_selectable
+ * flag set in their configuration data.
+ *
+ * rpm_regulator_set_voltage must be called before rpm_regulator_enable because
+ * enabling is defined by the RPM interface to be requesting the desired
+ * non-zero regulator output voltage.
+ */
+int rpm_regulator_enable(struct rpm_regulator *regulator)
+{
+ int rc = rpm_regulator_check_input(regulator);
+ struct vreg *vreg;
+
+ if (rc)
+ return rc;
+
+ if (regulator->vreg_id < config->vreg_id_min
+ || regulator->vreg_id > config->vreg_id_max) {
+ pr_err("invalid regulator id=%d\n", regulator->vreg_id);
+ return -EINVAL;
+ }
+
+ vreg = &config->vregs[regulator->vreg_id];
+
+ /*
+ * Handle voltage switches which can be enabled without
+ * rpm_regulator_set_voltage ever being called.
+ */
+ if (regulator->min_uV == 0 && regulator->max_uV == 0
+ && vreg->part->uV.mask == 0 && vreg->part->mV.mask == 0) {
+ regulator->min_uV = 1;
+ regulator->max_uV = 1;
+ }
+
+ if (regulator->min_uV == 0 && regulator->max_uV == 0) {
+ pr_err("Voltage must be set with rpm_regulator_set_voltage "
+ "before calling rpm_regulator_enable; vreg_id=%d, "
+ "voter=%d\n", regulator->vreg_id, regulator->voter);
+ return -EINVAL;
+ }
+
+ rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter,
+ regulator->min_uV, regulator->max_uV, regulator->sleep_also);
+
+ if (rc)
+ pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_enable);
+
+/**
+ * rpm_regulator_disable() - disable regulator output
+ * @regulator: RPM regulator handle
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * The enable state of the regulator is determined by aggregating the requests
+ * of all consumers. Therefore, it is possible that the regulator will remain
+ * enabled even after rpm_regulator_disable is called.
+ *
+ * This function may be called from either atomic or nonatomic context. This
+ * function may only be called for regulators which have the sleep_selectable
+ * flag set in their configuration data.
+ */
+int rpm_regulator_disable(struct rpm_regulator *regulator)
+{
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return rc;
+
+ rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, 0, 0,
+ regulator->sleep_also);
+
+ if (rc)
+ pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_disable);
+
+/**
+ * rpm_regulator_set_voltage() - set regulator output voltage
+ * @regulator: RPM regulator handle
+ * @min_uV: minimum required voltage in uV
+ * @max_uV: maximum acceptable voltage in uV
+ *
+ * Sets a voltage regulator to the desired output voltage. This can be set
+ * while the regulator is disabled or enabled. If the regulator is disabled,
+ * then rpm_regulator_set_voltage will both enable the regulator and set it to
+ * output at the requested voltage.
+ *
+ * The min_uV to max_uV voltage range requested must intersect with the
+ * voltage constraint range configured for the regulator.
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * The final voltage value that is sent to the RPM is aggregated based upon the
+ * values requested by all consumers of the regulator. This corresponds to the
+ * maximum min_uV value.
+ *
+ * This function may be called from either atomic or nonatomic context. This
+ * function may only be called for regulators which have the sleep_selectable
+ * flag set in their configuration data.
+ */
+int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV,
+ int max_uV)
+{
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return rc;
+
+ rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, min_uV,
+ max_uV, regulator->sleep_also);
+
+ if (rc) {
+ pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc);
+ } else {
+ regulator->min_uV = min_uV;
+ regulator->max_uV = max_uV;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rpm_regulator_set_voltage);
+
static inline int vreg_hpm_min_uA(struct vreg *vreg)
{
return vreg->hpm_min_load;
@@ -1374,6 +1625,8 @@
static int __devinit rpm_vreg_probe(struct platform_device *pdev)
{
struct rpm_regulator_platform_data *platform_data;
+ static struct rpm_regulator_consumer_mapping *prev_consumer_map;
+ static int prev_consumer_map_len;
int rc = 0;
int i, id;
@@ -1416,6 +1669,42 @@
mutex_init(&config->vregs[i].pc_lock);
}
+ /* Copy the list of private API consumers. */
+ if (platform_data->consumer_map_len > 0) {
+ if (consumer_map_len == 0) {
+ consumer_map_len = platform_data->consumer_map_len;
+ consumer_map = kmemdup(platform_data->consumer_map,
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * consumer_map_len, GFP_KERNEL);
+ if (consumer_map == NULL) {
+ pr_err("memory allocation failed\n");
+ consumer_map_len = 0;
+ return -ENOMEM;
+ }
+ } else {
+ /* Concatenate new map with the existing one. */
+ prev_consumer_map = consumer_map;
+ prev_consumer_map_len = consumer_map_len;
+ consumer_map_len += platform_data->consumer_map_len;
+ consumer_map = kmalloc(
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * consumer_map_len, GFP_KERNEL);
+ if (consumer_map == NULL) {
+ pr_err("memory allocation failed\n");
+ consumer_map_len = 0;
+ return -ENOMEM;
+ }
+ memcpy(consumer_map, prev_consumer_map,
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * prev_consumer_map_len);
+ memcpy(&consumer_map[prev_consumer_map_len],
+ platform_data->consumer_map,
+ sizeof(struct rpm_regulator_consumer_mapping)
+ * platform_data->consumer_map_len);
+ }
+
+ }
+
/* Initialize all of the regulators listed in the platform data. */
for (i = 0; i < platform_data->num_regulators; i++) {
rc = rpm_vreg_init_regulator(&platform_data->init_data[i],
@@ -1492,6 +1781,8 @@
platform_driver_unregister(&rpm_vreg_driver);
+ kfree(consumer_map);
+
for (i = 0; i < config->vregs_len; i++)
mutex_destroy(&config->vregs[i].pc_lock);
}
diff --git a/arch/arm/mach-msm/timer.h b/arch/arm/mach-msm/timer.h
index 368dd7b..5d18bb4 100644
--- a/arch/arm/mach-msm/timer.h
+++ b/arch/arm/mach-msm/timer.h
@@ -17,12 +17,12 @@
extern struct sys_timer msm_timer;
void __iomem *msm_timer_get_timer0_base(void);
-int64_t msm_timer_get_sclk_time(int64_t *period);
uint32_t msm_timer_get_sclk_ticks(void);
int msm_timer_init_time_sync(void (*timeout)(void));
#ifndef CONFIG_ARM_ARCH_TIMER
int64_t msm_timer_enter_idle(void);
void msm_timer_exit_idle(int low_power);
+int64_t msm_timer_get_sclk_time(int64_t *period);
#else
static inline int64_t msm_timer_enter_idle(void) { return 0; }
static inline void msm_timer_exit_idle(int low_power) { return; }
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 3011a2b..6de3f25 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -26,6 +26,7 @@
#define MAX_NUM_OUTPUT_BUFFERS 6
static const char *const mpeg_video_vidc_divx_format[] = {
+ "DIVX Format 3",
"DIVX Format 4",
"DIVX Format 5",
"DIVX Format 6",
@@ -153,8 +154,7 @@
static u32 get_frame_size_nv12(int plane,
u32 height, u32 width)
{
- int stride = (width + 31) & (~31);
- return height * stride * 3/2;
+ return (ALIGN(height, 32) * ALIGN(width, 32) * 3) / 2;
}
static u32 get_frame_size_nv21(int plane,
u32 height, u32 width)
@@ -217,6 +217,14 @@
.get_frame_size = get_frame_size_nv21,
.type = CAPTURE_PORT,
},
+ {
+ .name = "DIVX 311",
+ .description = "DIVX 311 compressed format",
+ .fourcc = V4L2_PIX_FMT_DIVX_311,
+ .num_planes = 1,
+ .get_frame_size = get_frame_size_compressed,
+ .type = OUTPUT_PORT,
+ }
};
int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
@@ -654,7 +662,7 @@
break;
}
if (property_id) {
- pr_err("Control: HAL property=%d,ctrl_id=%d,ctrl_value=%d\n",
+ pr_err("Control: HAL property=%x,ctrl_id=%x,ctrl_value=%d\n",
property_id,
msm_vdec_ctrls[control_idx].id,
control.value);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 31879b7..6b06943 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -149,7 +149,7 @@
enum command_response cmd)
{
int rc = 0;
- rc = wait_for_completion_timeout(
+ rc = wait_for_completion_interruptible_timeout(
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
if (!rc) {
@@ -418,6 +418,33 @@
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS)
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ } else {
+ /*
+ * FIXME:
+ * Special handling for EOS case: if we sent a 0 length input
+ * buf with EOS set, Venus doesn't return a valid output buffer.
+ * So pick up a random buffer that's with us, and send it to
+ * v4l2 client with EOS flag set.
+ *
+ * This would normally be OK unless client decides to send
+ * frames even after EOS.
+ *
+ * This should be fixed in upcoming versions of firmware
+ */
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS
+ && fill_buf_done->filled_len1 == 0) {
+ struct vb2_queue *q = &inst->vb2_bufq[CAPTURE_PORT];
+
+ if (!list_empty(&q->queued_list)) {
+ vb = list_first_entry(&q->queued_list,
+ struct vb2_buffer, queued_entry);
+ vb->v4l2_planes[0].bytesused = 0;
+ vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ }
+
}
}
@@ -599,8 +626,10 @@
case V4L2_PIX_FMT_VC1_ANNEX_L:
codec = HAL_VIDEO_CODEC_VC1;
break;
+ case V4L2_PIX_FMT_DIVX_311:
+ codec = HAL_VIDEO_CODEC_DIVX_311;
+ break;
/*HAL_VIDEO_CODEC_MVC
- HAL_VIDEO_CODEC_DIVX_311
HAL_VIDEO_CODEC_DIVX
HAL_VIDEO_CODEC_SPARK
HAL_VIDEO_CODEC_VP6
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index f4c7878..1f33c2c 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -1793,7 +1793,7 @@
static void vidc_hal_core_work_handler(struct work_struct *work)
{
- struct hal_device *device = list_first_entry(
+ struct hal_device *device = list_first_entry(
&hal_ctxt.dev_head, struct hal_device, list);
HAL_MSG_INFO(" GOT INTERRUPT %s() ", __func__);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a427494..e1c87e3 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -114,6 +114,7 @@
*/
unsigned int part_curr;
struct device_attribute force_ro;
+ struct device_attribute num_wr_reqs_to_start_packing;
};
static DEFINE_MUTEX(open_lock);
@@ -212,6 +213,38 @@
return ret;
}
+static ssize_t
+num_wr_reqs_to_start_packing_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int num_wr_reqs_to_start_packing;
+ int ret;
+
+ num_wr_reqs_to_start_packing = md->queue.num_wr_reqs_to_start_packing;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", num_wr_reqs_to_start_packing);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+
+ sscanf(buf, "%d", &value);
+ if (value >= 0)
+ md->queue.num_wr_reqs_to_start_packing = value;
+
+ mmc_blk_put(md);
+ return count;
+}
+
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
{
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -1236,6 +1269,48 @@
mmc_queue_bounce_pre(mqrq);
}
+static void mmc_blk_write_packing_control(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_host *host = mq->card->host;
+ int data_dir;
+
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR))
+ return;
+
+ /*
+ * In case the packing control is not supported by the host, it should
+ * not have an effect on the write packing. Therefore we have to enable
+ * the write packing
+ */
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR_CONTROL)) {
+ mq->wr_packing_enabled = true;
+ return;
+ }
+
+ if (!req || (req && (req->cmd_flags & REQ_FLUSH))) {
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+ return;
+ }
+
+ data_dir = rq_data_dir(req);
+
+ if (data_dir == READ) {
+ mq->num_of_potential_packed_wr_reqs = 0;
+ mq->wr_packing_enabled = false;
+ return;
+ } else if (data_dir == WRITE) {
+ mq->num_of_potential_packed_wr_reqs++;
+ }
+
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+
+}
+
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
{
struct request_queue *q = mq->queue;
@@ -1255,6 +1330,9 @@
!card->ext_csd.packed_event_en)
goto no_packed;
+ if (!mq->wr_packing_enabled)
+ goto no_packed;
+
if ((rq_data_dir(cur) == WRITE) &&
(card->host->caps2 & MMC_CAP2_PACKED_WR))
max_packed_rw = card->ext_csd.max_packed_writes;
@@ -1319,6 +1397,8 @@
break;
}
+ if (rq_data_dir(next) == WRITE)
+ mq->num_of_potential_packed_wr_reqs++;
list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
cur = next;
reqs++;
@@ -1702,6 +1782,8 @@
goto out;
}
+ mmc_blk_write_packing_control(mq, req);
+
if (req && req->cmd_flags & REQ_SANITIZE) {
/* complete ongoing async transfer before issuing sanitize */
if (card->host && card->host->areq)
@@ -1943,6 +2025,8 @@
static void mmc_blk_remove_req(struct mmc_blk_data *md)
{
if (md) {
+ device_remove_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
@@ -1981,9 +2065,27 @@
md->force_ro.attr.name = "force_ro";
md->force_ro.attr.mode = S_IRUGO | S_IWUSR;
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
- if (ret)
+ if (ret) {
del_gendisk(md->disk);
+ goto out;
+ }
+ md->num_wr_reqs_to_start_packing.show =
+ num_wr_reqs_to_start_packing_show;
+ md->num_wr_reqs_to_start_packing.store =
+ num_wr_reqs_to_start_packing_store;
+ sysfs_attr_init(&md->num_wr_reqs_to_start_packing.attr);
+ md->num_wr_reqs_to_start_packing.attr.name =
+ "num_wr_reqs_to_start_packing";
+ md->num_wr_reqs_to_start_packing.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
+ if (ret) {
+ device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+ del_gendisk(md->disk);
+ }
+
+out:
return ret;
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 652e13a..c3a718a 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -25,6 +25,13 @@
#define MMC_QUEUE_SUSPENDED (1 << 0)
/*
+ * Based on benchmark tests the default num of requests to trigger the write
+ * packing was determined, to keep the read latency as low as possible and
+ * manage to keep the high write throughput.
+ */
+#define DEFAULT_NUM_REQS_TO_START_PACK 17
+
+/*
* Prepare a MMC request. This just filters out odd stuff.
*/
static int mmc_prep_request(struct request_queue *q, struct request *req)
@@ -193,6 +200,7 @@
mq->mqrq_cur = mqrq_cur;
mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq;
+ mq->num_wr_reqs_to_start_packing = DEFAULT_NUM_REQS_TO_START_PACK;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d761bf1..6c29e0e 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -44,6 +44,9 @@
struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
struct mmc_queue_req *mqrq_prev;
+ bool wr_packing_enabled;
+ int num_of_potential_packed_wr_reqs;
+ int num_wr_reqs_to_start_packing;
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6c82c74..e0217d0 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -40,6 +40,12 @@
#include "sd_ops.h"
#include "sdio_ops.h"
+/*
+ * The Background operations can take a long time, depends on the house keeping
+ * operations the card has to perform
+ */
+#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
+
static struct workqueue_struct *workqueue;
/*
@@ -235,6 +241,7 @@
{
int err;
unsigned long flags;
+ int timeout;
BUG_ON(!card);
if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
@@ -258,8 +265,12 @@
return;
mmc_claim_host(card->host);
+
+ timeout = (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) ?
+ MMC_BKOPS_MAX_TIMEOUT : 0;
+
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BKOPS_START, 1, 0);
+ EXT_CSD_BKOPS_START, 1, timeout);
if (err) {
pr_warning("%s: error %d starting bkops\n",
mmc_hostname(card->host), err);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index b1a16bb..2c81f84 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -258,7 +258,6 @@
int thermal_levels;
struct delayed_work update_heartbeat_work;
struct delayed_work eoc_work;
- struct work_struct unplug_ovp_fet_open_work;
struct delayed_work unplug_check_work;
struct delayed_work vin_collapse_check_work;
struct wake_lock eoc_wake_lock;
@@ -1998,11 +1997,8 @@
#define WRITE_BANK_4 0xC0
#define USB_OVP_DEBOUNCE_TIME 0x06
-static void unplug_ovp_fet_open_worker(struct work_struct *work)
+static void unplug_ovp_fet_open(struct pm8921_chg_chip *chip)
{
- struct pm8921_chg_chip *chip = container_of(work,
- struct pm8921_chg_chip,
- unplug_ovp_fet_open_work);
int chg_gone, usb_chg_plugged_in;
int count = 0;
@@ -2348,7 +2344,7 @@
/* run the worker directly */
pr_debug(" ver5 step: chg_gone=%d, usb_valid = %d\n",
chg_gone, usb_chg_plugged_in);
- schedule_work(&chip->unplug_ovp_fet_open_work);
+ unplug_ovp_fet_open(chip);
}
if (!(reg_loop & VIN_ACTIVE_BIT)) {
@@ -2453,8 +2449,7 @@
static irqreturn_t chg_gone_irq_handler(int irq, void *data)
{
struct pm8921_chg_chip *chip = data;
- u8 reg;
- int rc, chg_gone, usb_chg_plugged_in;
+ int chg_gone, usb_chg_plugged_in;
usb_chg_plugged_in = is_usb_chg_plugged_in(chip);
chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ);
@@ -2462,14 +2457,6 @@
pr_debug("chg_gone=%d, usb_valid = %d\n", chg_gone, usb_chg_plugged_in);
pr_debug("Chg gone fsm_state=%d\n", pm_chg_get_fsm_state(data));
- rc = pm8xxx_readb(chip->dev->parent, CHG_CNTRL_3, ®);
- if (rc)
- pr_err("Failed to read CHG_CNTRL_3 rc=%d\n", rc);
-
- if (reg & CHG_USB_SUSPEND_BIT)
- return IRQ_HANDLED;
- schedule_work(&chip->unplug_ovp_fet_open_work);
-
power_supply_changed(&chip->batt_psy);
power_supply_changed(&chip->usb_psy);
return IRQ_HANDLED;
@@ -3885,8 +3872,6 @@
INIT_DELAYED_WORK(&chip->eoc_work, eoc_worker);
INIT_DELAYED_WORK(&chip->vin_collapse_check_work,
vin_collapse_check_worker);
- INIT_WORK(&chip->unplug_ovp_fet_open_work,
- unplug_ovp_fet_open_worker);
INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker);
rc = request_irqs(chip, pdev);
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 70131fa..ccb8012 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -29,14 +29,17 @@
*
*/
-#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/oom.h>
-#include <linux/sched.h>
-#include <linux/notifier.h>
+#include <linux/kobject.h>
#include <linux/memory.h>
#include <linux/memory_hotplug.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/oom.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
static uint32_t lowmem_debug_level = 2;
static int lowmem_adj[6] = {
@@ -54,9 +57,12 @@
};
static int lowmem_minfree_size = 4;
+static size_t lowmem_minfree_notif_trigger;
+
static unsigned int offlining;
static struct task_struct *lowmem_deathpending;
static unsigned long lowmem_deathpending_timeout;
+static struct kobject *lowmem_kobj;
#define lowmem_print(level, x...) \
do { \
@@ -108,6 +114,29 @@
+static void lowmem_notify_killzone_approach(void);
+
+static inline void get_free_ram(int *other_free, int *other_file)
+{
+ struct zone *zone;
+ *other_free = global_page_state(NR_FREE_PAGES);
+ *other_file = global_page_state(NR_FILE_PAGES) -
+ global_page_state(NR_SHMEM);
+
+ if (offlining) {
+ /* Discount all free space in the section being offlined */
+ for_each_zone(zone) {
+ if (zone_idx(zone) == ZONE_MOVABLE) {
+ *other_free -= zone_page_state(zone,
+ NR_FREE_PAGES);
+ lowmem_print(4, "lowmem_shrink discounted "
+ "%lu pages in movable zone\n",
+ zone_page_state(zone, NR_FREE_PAGES));
+ }
+ }
+ }
+}
+
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *p;
@@ -119,23 +148,8 @@
int selected_tasksize = 0;
int selected_oom_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
- int other_free = global_page_state(NR_FREE_PAGES);
- int other_file = global_page_state(NR_FILE_PAGES) -
- global_page_state(NR_SHMEM);
- struct zone *zone;
-
- if (offlining) {
- /* Discount all free space in the section being offlined */
- for_each_zone(zone) {
- if (zone_idx(zone) == ZONE_MOVABLE) {
- other_free -= zone_page_state(zone,
- NR_FREE_PAGES);
- lowmem_print(4, "lowmem_shrink discounted "
- "%lu pages in movable zone\n",
- zone_page_state(zone, NR_FREE_PAGES));
- }
- }
- }
+ int other_free;
+ int other_file;
/*
* If we already have a death outstanding, then
* bail out right away; indicating to vmscan
@@ -147,6 +161,13 @@
time_before_eq(jiffies, lowmem_deathpending_timeout))
return 0;
+ get_free_ram(&other_free, &other_file);
+
+ if (other_free < lowmem_minfree_notif_trigger &&
+ other_file < lowmem_minfree_notif_trigger) {
+ lowmem_notify_killzone_approach();
+ }
+
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
@@ -228,18 +249,91 @@
.seeks = DEFAULT_SEEKS * 16
};
+static void lowmem_notify_killzone_approach(void)
+{
+ lowmem_print(3, "notification trigger activated\n");
+ sysfs_notify(lowmem_kobj, NULL, "notify_trigger_active");
+}
+
+static ssize_t lowmem_notify_trigger_active_show(struct kobject *k,
+ struct kobj_attribute *attr, char *buf)
+{
+ int other_free, other_file;
+ get_free_ram(&other_free, &other_file);
+ if (other_free < lowmem_minfree_notif_trigger &&
+ other_file < lowmem_minfree_notif_trigger)
+ return snprintf(buf, 3, "1\n");
+ else
+ return snprintf(buf, 3, "0\n");
+}
+
+static struct kobj_attribute lowmem_notify_trigger_active_attr =
+ __ATTR(notify_trigger_active, S_IRUGO,
+ lowmem_notify_trigger_active_show, NULL);
+
+static struct attribute *lowmem_default_attrs[] = {
+ &lowmem_notify_trigger_active_attr.attr,
+ NULL,
+};
+
+static ssize_t lowmem_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+ struct kobj_attribute *kobj_attr;
+ kobj_attr = container_of(attr, struct kobj_attribute, attr);
+ return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops lowmem_ops = {
+ .show = lowmem_show,
+};
+
+static void lowmem_kobj_release(struct kobject *kobj)
+{
+ /* Nothing to be done here */
+}
+
+static struct kobj_type lowmem_kobj_type = {
+ .release = lowmem_kobj_release,
+ .sysfs_ops = &lowmem_ops,
+ .default_attrs = lowmem_default_attrs,
+};
+
static int __init lowmem_init(void)
{
+ int rc;
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
#ifdef CONFIG_MEMORY_HOTPLUG
hotplug_memory_notifier(lmk_hotplug_callback, 0);
#endif
+
+ lowmem_kobj = kzalloc(sizeof(*lowmem_kobj), GFP_KERNEL);
+ if (!lowmem_kobj) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = kobject_init_and_add(lowmem_kobj, &lowmem_kobj_type,
+ mm_kobj, "lowmemkiller");
+ if (rc)
+ goto err_kobj;
+
return 0;
+
+err_kobj:
+ kfree(lowmem_kobj);
+
+err:
+ unregister_shrinker(&lowmem_shrinker);
+ task_free_unregister(&task_nb);
+
+ return rc;
}
static void __exit lowmem_exit(void)
{
+ kobject_put(lowmem_kobj);
+ kfree(lowmem_kobj);
unregister_shrinker(&lowmem_shrinker);
task_free_unregister(&task_nb);
}
@@ -250,6 +344,8 @@
module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
+module_param_named(notify_trigger, lowmem_minfree_notif_trigger, uint,
+ S_IRUGO | S_IWUSR);
module_init(lowmem_init);
module_exit(lowmem_exit);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 5b05c5b..2d3a01d 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -75,6 +75,7 @@
static struct regulator *hsusb_vddcx;
static struct regulator *vbus_otg;
static struct regulator *mhl_analog_switch;
+static struct power_supply *psy;
static bool aca_id_turned_on;
static inline bool aca_enabled(void)
@@ -994,9 +995,7 @@
static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
{
- struct power_supply *psy;
- psy = power_supply_get_by_name("usb");
if (!psy)
goto psy_not_supported;
@@ -2001,6 +2000,9 @@
case OTG_STATE_UNDEFINED:
msm_otg_reset(otg);
msm_otg_init_sm(motg);
+ psy = power_supply_get_by_name("usb");
+ if (!psy)
+ pr_err("couldn't get usb power supply\n");
otg->state = OTG_STATE_B_IDLE;
if (!test_bit(B_SESS_VLD, &motg->inputs) &&
test_bit(ID, &motg->inputs)) {
diff --git a/drivers/video/msm/mipi_dsi.h b/drivers/video/msm/mipi_dsi.h
index ff2910f..d54c5b5 100644
--- a/drivers/video/msm/mipi_dsi.h
+++ b/drivers/video/msm/mipi_dsi.h
@@ -259,6 +259,8 @@
char *mipi_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen);
char *mipi_dsi_buf_init(struct dsi_buf *dp);
void mipi_dsi_init(void);
+void mipi_dsi_lane_cfg(void);
+void mipi_dsi_bist_ctrl(void);
int mipi_dsi_buf_alloc(struct dsi_buf *, int size);
int mipi_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm);
int mipi_dsi_cmds_tx(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
index 48bdb1d..2a8610b 100644
--- a/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
+++ b/drivers/video/msm/mipi_toshiba_video_wsvga_pt.c
@@ -20,7 +20,7 @@
static struct mipi_dsi_phy_ctrl dsi_video_mode_phy_db = {
/* 600*1024, RGB888, 3 Lane 55 fps video mode */
/* regulator */
- {0x03, 0x0a, 0x04, 0x00, 0x20},
+ {0x09, 0x08, 0x05, 0x00, 0x20},
/* timing */
{0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c,
0x0c, 0x03, 0x04, 0xa0},
diff --git a/drivers/video/msm/msm_dss_io_8960.c b/drivers/video/msm/msm_dss_io_8960.c
index d1ddafb..b17c195 100644
--- a/drivers/video/msm/msm_dss_io_8960.c
+++ b/drivers/video/msm/msm_dss_io_8960.c
@@ -231,6 +231,45 @@
__func__, (int) ahb, MIPI_INP_SECURE(ahb));
}
+void mipi_dsi_lane_cfg(void)
+{
+ int i, ln_offset;
+
+ ln_offset = 0x300;
+ for (i = 0; i < 4; i++) {
+ /* DSI1_DSIPHY_LN_CFG0 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset, 0x80);
+ /* DSI1_DSIPHY_LN_CFG1 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x04, 0x45);
+ /* DSI1_DSIPHY_LN_CFG2 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x08, 0x0);
+ /* DSI1_DSIPHY_LN_TEST_DATAPATH */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x0c, 0x0);
+ /* DSI1_DSIPHY_LN_TEST_STR0 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x14, 0x1);
+ /* DSI1_DSIPHY_LN_TEST_STR1 */
+ MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x18, 0x66);
+ ln_offset += 0x40;
+ }
+
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0400, 0x40); /* DSI1_DSIPHY_LNCK_CFG0 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0404, 0x67); /* DSI1_DSIPHY_LNCK_CFG1 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0408, 0x0); /* DSI1_DSIPHY_LNCK_CFG2 */
+ /* DSI1_DSIPHY_LNCK_TEST_DATAPATH */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x040c, 0x0);
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0414, 0x1); /* DSI1_DSIPHY_LNCK_TEST_STR0 */
+ /* DSI1_DSIPHY_LNCK_TEST_STR1 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0418, 0x88);
+}
+
+void mipi_dsi_bist_ctrl(void)
+{
+ MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0f); /* DSI1_DSIPHY_BIST_CTRL4 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0490, 0x03); /* DSI1_DSIPHY_BIST_CTRL1 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x048c, 0x03); /* DSI1_DSIPHY_BIST_CTRL0 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0); /* DSI1_DSIPHY_BIST_CTRL4 */
+}
+
static void mipi_dsi_calibration(void)
{
int i = 0;
@@ -238,7 +277,7 @@
int cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
/* DSI1_DSIPHY_REGULATOR_CAL_PWR_CFG */
- MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x01);
+ MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x03);
/* DSI1_DSIPHY_CAL_SW_CFG2 */
MIPI_OUTP(MIPI_DSI_BASE + 0x0534, 0x0);
@@ -498,6 +537,8 @@
MIPI_OUTP(MIPI_DSI_BASE + 0x50c, 0x0000);/* regulator_ctrl_3 */
MIPI_OUTP(MIPI_DSI_BASE + 0x510, 0x0100);/* regulator_ctrl_4 */
+ MIPI_OUTP(MIPI_DSI_BASE + 0x4b0, 0x04);/* DSIPHY_LDO_CNTRL */
+
pd = (panel_info->mipi).dsi_phy_db;
off = 0x0480; /* strength 0 - 2 */
@@ -521,6 +562,8 @@
off += 4;
}
mipi_dsi_calibration();
+ mipi_dsi_lane_cfg(); /* lane cfgs */
+ mipi_dsi_bist_ctrl(); /* bist ctrl */
off = 0x0204; /* pll ctrl 1 - 19, skip 0 */
for (i = 1; i < 20; i++) {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 4416e95..ae06ccf 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -255,6 +255,8 @@
#define MMC_CAP2_PACKED_WR (1 << 11) /* Allow packed write */
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
MMC_CAP2_PACKED_WR) /* Allow packed commands */
+#define MMC_CAP2_PACKED_WR_CONTROL (1 << 12) /* Allow write packing control */
+
#define MMC_CAP2_BKOPS (1 << 14) /* BKOPS supported */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index eda60c0..fe0f1a8 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -386,6 +386,7 @@
#define V4L2_PIX_FMT_XVID v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid */
#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
+#define V4L2_PIX_FMT_DIVX_311 v4l2_fourcc('D', 'I', 'V', '3') /* DIVX */
/* Vendor-specific formats */
#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 134d044..96795a3 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -611,6 +611,19 @@
#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72
#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75
+/* SRS TRUMEDIA GUIDS */
+/* topology */
+#define SRS_TRUMEDIA_TOPOLOGY_ID 0x00010D90
+/* module */
+#define SRS_TRUMEDIA_MODULE_ID 0x10005010
+/* parameters */
+#define SRS_TRUMEDIA_PARAMS 0x10005011
+#define SRS_TRUMEDIA_PARAMS_WOWHD 0x10005012
+#define SRS_TRUMEDIA_PARAMS_CSHP 0x10005013
+#define SRS_TRUMEDIA_PARAMS_HPF 0x10005014
+#define SRS_TRUMEDIA_PARAMS_PEQ 0x10005015
+#define SRS_TRUMEDIA_PARAMS_HL 0x10005016
+
#define ASM_MAX_EQ_BANDS 12
struct asm_eq_band {
@@ -1427,4 +1440,96 @@
#define ADSP_ENOTIMPL 0x00000011 /* Operation is not implemented. */
#define ADSP_ENEEDMORE 0x00000012 /* Operation needs more data or resources*/
+/* SRS TRUMEDIA start */
+#define SRS_ID_GLOBAL 0x00000001
+#define SRS_ID_WOWHD 0x00000002
+#define SRS_ID_CSHP 0x00000003
+#define SRS_ID_HPF 0x00000004
+#define SRS_ID_PEQ 0x00000005
+#define SRS_ID_HL 0x00000006
+
+#define SRS_CMD_UPLOAD 0x7FFF0000
+#define SRS_PARAM_INDEX_MASK 0x80000000
+#define SRS_PARAM_OFFSET_MASK 0x3FFF0000
+#define SRS_PARAM_VALUE_MASK 0x0000FFFF
+
+struct srs_trumedia_params_GLOBAL {
+ uint8_t v1;
+ uint8_t v2;
+ uint8_t v3;
+ uint8_t v4;
+ uint8_t v5;
+ uint8_t v6;
+ uint8_t v7;
+ uint8_t v8;
+} __packed;
+
+struct srs_trumedia_params_WOWHD {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v5;
+ uint16_t v6;
+ uint16_t v7;
+ uint16_t v8;
+ uint16_t v____A1;
+ uint32_t v9;
+ uint16_t v10;
+ uint16_t v11;
+ uint32_t v12[16];
+} __packed;
+
+struct srs_trumedia_params_CSHP {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v5;
+ uint16_t v6;
+ uint16_t v____A1;
+ uint32_t v7;
+ uint16_t v8;
+ uint16_t v9;
+ uint32_t v10[16];
+} __packed;
+
+struct srs_trumedia_params_HPF {
+ uint32_t v1;
+ uint32_t v2[26];
+} __packed;
+
+struct srs_trumedia_params_PEQ {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v____A1;
+ uint32_t v5[26];
+ uint32_t v6[26];
+} __packed;
+
+struct srs_trumedia_params_HL {
+ uint16_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v____A1;
+ int32_t v4;
+ uint32_t v5;
+ uint16_t v6;
+ uint16_t v____A2;
+ uint32_t v7;
+} __packed;
+
+struct srs_trumedia_params {
+ struct srs_trumedia_params_GLOBAL global;
+ struct srs_trumedia_params_WOWHD wowhd;
+ struct srs_trumedia_params_CSHP cshp;
+ struct srs_trumedia_params_HPF hpf;
+ struct srs_trumedia_params_PEQ peq;
+ struct srs_trumedia_params_HL hl;
+} __packed;
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params);
+/* SRS TruMedia end */
+
#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
new file mode 100644
index 0000000..3d5ffdd
--- /dev/null
+++ b/include/sound/msm-dai-q6-v2.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 __MSM_DAI_Q6_PDATA_H__
+
+#define __MSM_DAI_Q6_PDATA_H__
+
+#define MSM_MI2S_SD0 (1 << 0)
+#define MSM_MI2S_SD1 (1 << 1)
+#define MSM_MI2S_SD2 (1 << 2)
+#define MSM_MI2S_SD3 (1 << 3)
+#define MSM_MI2S_CAP_RX 0
+#define MSM_MI2S_CAP_TX 1
+
+struct msm_dai_auxpcm_pdata {
+ const char *clk;
+ u16 mode;
+ u16 sync;
+ u16 frame;
+ u16 quant;
+ /* modify slot to arr[4] to specify
+ * the slot number for each channel
+ * in multichannel scenario */
+ u16 slot;
+ u16 data;
+ int pcm_clk_rate;
+};
+
+struct msm_i2s_data {
+ u32 capability; /* RX or TX */
+ u16 sd_lines;
+};
+#endif
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 80f4bd6..86ae763 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -308,7 +308,7 @@
cmd = list_entry(p, struct pending_cmd, list);
- if (cmd->opcode != opcode)
+ if (opcode > 0 && cmd->opcode != opcode)
continue;
if (index >= 0 && cmd->index != index)
@@ -2475,6 +2475,14 @@
return err;
}
+static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
+{
+ u8 *status = data;
+
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+ mgmt_pending_remove(cmd);
+}
+
int mgmt_index_added(u16 index)
{
BT_DBG("%d", index);
@@ -2483,7 +2491,12 @@
int mgmt_index_removed(u16 index)
{
+ u8 status = ENODEV;
+
BT_DBG("%d", index);
+
+ mgmt_pending_foreach(0, index, cmd_status_rsp, &status);
+
return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL);
}
@@ -2522,6 +2535,11 @@
mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
+ if (!powered) {
+ u8 status = ENETDOWN;
+ mgmt_pending_foreach(0, index, cmd_status_rsp, &status);
+ }
+
ev.val = powered;
ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk);
diff --git a/scripts/gcc-wrapper.py b/scripts/gcc-wrapper.py
index 1497452..f7cb424 100755
--- a/scripts/gcc-wrapper.py
+++ b/scripts/gcc-wrapper.py
@@ -44,8 +44,8 @@
"alignment.c:720",
"async.c:122",
"async.c:270",
- "block.c:847",
- "block.c:848",
+ "block.c:880",
+ "block.c:881",
"dir.c:43",
"dm.c:1053",
"dm.c:1080",
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 269334f..652c951 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -2001,6 +2001,7 @@
(choice == SITAR_BANDGAP_AUDIO_MODE)) {
sitar_codec_enable_audio_mode_bandgap(codec);
} else if (choice == SITAR_BANDGAP_MBHC_MODE) {
+ snd_soc_update_bits(codec, SITAR_A_BIAS_CURR_CTL_2, 0x0C, 0x08);
snd_soc_update_bits(codec, SITAR_A_BIAS_CENTRAL_BG_CTL, 0x2,
0x2);
snd_soc_update_bits(codec, SITAR_A_BIAS_CENTRAL_BG_CTL, 0x80,
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index b3cffd1..0df028c 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -62,7 +62,7 @@
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
-snd-soc-msm8960-objs := msm8960.o apq8064.o msm8930.o mpq8064.o
+snd-soc-msm8960-objs := msm8960.o apq8064.o msm8930.o
obj-$(CONFIG_SND_SOC_MSM8960) += snd-soc-msm8960.o
# Generic MSM drivers
@@ -75,3 +75,12 @@
# for MDM 9615 sound card driver
snd-soc-mdm9615-objs := mdm9615.o
obj-$(CONFIG_SND_SOC_MDM9615) += snd-soc-mdm9615.o
+
+# for MSM 8974 sound card driver
+obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/
+snd-soc-msm8974-objs := msm8974.o
+obj-$(CONFIG_SND_SOC_MSM8974) += snd-soc-msm8974.o
+
+snd-soc-qdsp6v2-objs := msm-dai-fe.o msm-dai-stub.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 7fbb592..9ca6569 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -110,6 +110,43 @@
/* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
* If new back-end is defined, add new back-end DAI ID at the end of enum
*/
+
+union srs_trumedia_params_u {
+ struct srs_trumedia_params srs_params;
+ unsigned short int raw_params[1];
+};
+static union srs_trumedia_params_u msm_srs_trumedia_params[2];
+static int srs_port_id = -1;
+
+static void srs_send_params(int port_id, unsigned int techs,
+ int param_block_idx) {
+ pr_debug("SRS %s: called, port_id = %d, techs flags = %u,"
+ " paramblockidx %d", __func__, port_id, techs,
+ param_block_idx);
+ /* force all if techs is set to 1 */
+ if (techs == 1)
+ techs = 0xFFFFFFFF;
+
+ if (techs & (1 << SRS_ID_WOWHD))
+ srs_trumedia_open(port_id, SRS_ID_WOWHD,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.wowhd);
+ if (techs & (1 << SRS_ID_CSHP))
+ srs_trumedia_open(port_id, SRS_ID_CSHP,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.cshp);
+ if (techs & (1 << SRS_ID_HPF))
+ srs_trumedia_open(port_id, SRS_ID_HPF,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.hpf);
+ if (techs & (1 << SRS_ID_PEQ))
+ srs_trumedia_open(port_id, SRS_ID_PEQ,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.peq);
+ if (techs & (1 << SRS_ID_HL))
+ srs_trumedia_open(port_id, SRS_ID_HL,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.hl);
+ if (techs & (1 << SRS_ID_GLOBAL))
+ srs_trumedia_open(port_id, SRS_ID_GLOBAL,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.global);
+}
+
static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
{ PRIMARY_I2S_RX, 0, 0, 0, 0, 0},
{ PRIMARY_I2S_TX, 0, 0, 0, 0, 0},
@@ -749,6 +786,104 @@
return 0;
}
+static int msm_routing_get_srs_trumedia_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_routing_set_srs_trumedia_control_(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int techs = 0;
+ unsigned short offset, value, max, index;
+
+ max = sizeof(msm_srs_trumedia_params) >> 1;
+ index = (unsigned short)((ucontrol->value.integer.value[0] &
+ SRS_PARAM_INDEX_MASK) >> 31);
+ if (SRS_CMD_UPLOAD ==
+ (ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) {
+ techs = ucontrol->value.integer.value[0] & 0xFF;
+ pr_debug("SRS %s: send params request, flags = %u",
+ __func__, techs);
+ if (srs_port_id >= 0 && techs)
+ srs_send_params(srs_port_id, techs, index);
+ return 0;
+ }
+ offset = (unsigned short)((ucontrol->value.integer.value[0] &
+ SRS_PARAM_OFFSET_MASK) >> 16);
+ value = (unsigned short)(ucontrol->value.integer.value[0] &
+ SRS_PARAM_VALUE_MASK);
+ if (offset < max) {
+ msm_srs_trumedia_params[index].raw_params[offset] = value;
+ pr_debug("SRS %s: index set... (max %d, requested %d,"
+ " val %d, paramblockidx %d)", __func__, max, offset,
+ value, index);
+ } else {
+ pr_err("SRS %s: index out of bounds! (max %d, requested %d)",
+ __func__, max, offset);
+ }
+ if (offset == 4) {
+ int i;
+ for (i = 0; i < max; i++) {
+ if (i == 0) {
+ pr_debug("SRS %s: global block start",
+ __func__);
+ }
+ if (i ==
+ (sizeof(struct srs_trumedia_params_GLOBAL) >> 1)) {
+ break;
+ pr_debug("SRS %s: wowhd block start at"
+ " offset %d word offset %d", __func__,
+ i, i>>1);
+ }
+ pr_debug("SRS %s: param_index %d index %d val %d",
+ __func__, index, i,
+ msm_srs_trumedia_params[index].raw_params[i]);
+ }
+ }
+ return 0;
+}
+
+static int msm_routing_set_srs_trumedia_control(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int ret;
+
+ pr_debug("SRS control normal called");
+ mutex_lock(&routing_lock);
+ srs_port_id = SLIMBUS_0_RX;
+ ret = msm_routing_set_srs_trumedia_control_(kcontrol, ucontrol);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static int msm_routing_set_srs_trumedia_control_I2S(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int ret;
+
+ pr_debug("SRS control I2S called");
+ mutex_lock(&routing_lock);
+ srs_port_id = PRIMARY_I2S_RX;
+ ret = msm_routing_set_srs_trumedia_control_(kcontrol, ucontrol);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
+static int msm_routing_set_srs_trumedia_control_HDMI(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol) {
+ int ret;
+
+ pr_debug("SRS control HDMI called");
+ mutex_lock(&routing_lock);
+ srs_port_id = HDMI_RX;
+ ret = msm_routing_set_srs_trumedia_control_(kcontrol, ucontrol);
+ mutex_unlock(&routing_lock);
+ return ret;
+}
+
static void msm_send_eq_values(int eq_idx)
{
int result;
@@ -1378,6 +1513,63 @@
msm_routing_set_compressed_vol_mixer, compressed_rx_vol_gain),
};
+static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw, \
+ .get = msm_routing_get_srs_trumedia_control,
+ .put = msm_routing_set_srs_trumedia_control,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_SRS_trumedia_controls_HDMI[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia HDMI",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw, \
+ .get = msm_routing_get_srs_trumedia_control,
+ .put = msm_routing_set_srs_trumedia_control_HDMI,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
+static const struct snd_kcontrol_new lpa_SRS_trumedia_controls_I2S[] = {
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SRS TruMedia I2S",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_soc_info_volsw, \
+ .get = msm_routing_get_srs_trumedia_control,
+ .put = msm_routing_set_srs_trumedia_control_I2S,
+ .private_value = ((unsigned long)&(struct soc_mixer_control)
+ {.reg = SND_SOC_NOPM,
+ .shift = 0,
+ .rshift = 0,
+ .max = 0xFFFFFFFF,
+ .platform_max = 0xFFFFFFFF,
+ .invert = 0
+ })
+ }
+};
+
static const struct snd_kcontrol_new eq_enable_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_eq_enable_mixer,
@@ -1980,8 +2172,10 @@
mutex_lock(&routing_lock);
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
- if (fe_dai_map[i][session_type] != INVALID_SESSION)
+ if (fe_dai_map[i][session_type] != INVALID_SESSION) {
adm_close(bedai->port_id);
+ srs_port_id = -1;
+ }
}
bedai->active = 0;
@@ -2047,6 +2241,8 @@
msm_pcm_routing_build_matrix(i,
fe_dai_map[i][session_type], path_type);
+ srs_port_id = bedai->port_id;
+ srs_send_params(srs_port_id, 1, 0);
}
}
@@ -2115,6 +2311,18 @@
compressed_vol_mixer_controls,
ARRAY_SIZE(compressed_vol_mixer_controls));
+ snd_soc_add_platform_controls(platform,
+ lpa_SRS_trumedia_controls,
+ ARRAY_SIZE(lpa_SRS_trumedia_controls));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_SRS_trumedia_controls_HDMI,
+ ARRAY_SIZE(lpa_SRS_trumedia_controls_HDMI));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_SRS_trumedia_controls_I2S,
+ ARRAY_SIZE(lpa_SRS_trumedia_controls_I2S));
+
return 0;
}
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 9061dec..72bb9b5 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -42,7 +42,7 @@
#define DEFAULT_PMIC_SPK_GAIN 0x0D
#define SITAR_EXT_CLK_RATE 12288000
-#define SITAR_MBHC_DEF_BUTTONS 3
+#define SITAR_MBHC_DEF_BUTTONS 8
#define SITAR_MBHC_DEF_RLOADS 5
static int msm8930_spk_control;
@@ -668,7 +668,7 @@
}
err = snd_soc_jack_new(codec, "Button Jack",
- SND_JACK_BTN_0, &button_jack);
+ SITAR_JACK_BUTTON_MASK, &button_jack);
if (err) {
pr_err("failed to create new jack\n");
return err;
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
new file mode 100644
index 0000000..d47910b
--- /dev/null
+++ b/sound/soc/msm/msm8974.c
@@ -0,0 +1,752 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dsp.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <asm/mach-types.h>
+#include <mach/socinfo.h>
+#include <qdsp6v2/msm-pcm-routing-v2.h>
+#include "../codecs/wcd9310.h"
+
+/* 8974 machine driver */
+
+#define PM8921_GPIO_BASE NR_GPIO_IRQS
+#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
+
+#define MSM8974_SPK_ON 1
+#define MSM8974_SPK_OFF 0
+
+#define MSM_SLIM_0_RX_MAX_CHANNELS 2
+#define MSM_SLIM_0_TX_MAX_CHANNELS 4
+
+#define BTSCO_RATE_8KHZ 8000
+#define BTSCO_RATE_16KHZ 16000
+
+#define BOTTOM_SPK_AMP_POS 0x1
+#define BOTTOM_SPK_AMP_NEG 0x2
+#define TOP_SPK_AMP_POS 0x4
+#define TOP_SPK_AMP_NEG 0x8
+
+#define GPIO_AUX_PCM_DOUT 43
+#define GPIO_AUX_PCM_DIN 44
+#define GPIO_AUX_PCM_SYNC 45
+#define GPIO_AUX_PCM_CLK 46
+
+#define TABLA_EXT_CLK_RATE 12288000
+
+#define TABLA_MBHC_DEF_BUTTONS 8
+#define TABLA_MBHC_DEF_RLOADS 5
+
+/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
+enum {
+ SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
+ SLIM_1_TX_1 = 146, /* BT-SCO and USB RX */
+ SLIM_2_RX_1 = 147, /* HDMI RX */
+ SLIM_3_RX_1 = 148, /* In-call recording RX */
+ SLIM_3_RX_2 = 149, /* In-call recording RX */
+ SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
+};
+
+static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
+static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
+static int msm_spk_control;
+static int msm_ext_bottom_spk_pamp;
+static int msm_ext_top_spk_pamp;
+static int msm_slim_0_rx_ch = 1;
+static int msm_slim_0_tx_ch = 1;
+
+static int msm_btsco_rate = BTSCO_RATE_8KHZ;
+static int msm_headset_gpios_configured;
+
+static struct snd_soc_jack hs_jack;
+static struct snd_soc_jack button_jack;
+
+static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct tabla_mbhc_config mbhc_cfg = {
+ .headset_jack = &hs_jack,
+ .button_jack = &button_jack,
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = TABLA_MICBIAS2,
+ .mclk_cb_fn = msm_enable_codec_ext_clk,
+ .mclk_rate = TABLA_EXT_CLK_RATE,
+ .gpio = 0, /* MBHC GPIO is not configured */
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+};
+
+static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
+{
+ int ret = 0;
+
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .
+ function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ if (spk_amp_gpio == bottom_spk_pamp_gpio) {
+
+ ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
+ if (ret) {
+ pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
+ __func__, bottom_spk_pamp_gpio);
+ return;
+ }
+ ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure Bottom Spk Ampl"
+ " gpio %u\n", __func__, bottom_spk_pamp_gpio);
+ else {
+ pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
+ gpio_direction_output(bottom_spk_pamp_gpio, 1);
+ }
+
+ } else if (spk_amp_gpio == top_spk_pamp_gpio) {
+
+ ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
+ if (ret) {
+ pr_err("%s: Error requesting GPIO %d\n", __func__,
+ top_spk_pamp_gpio);
+ return;
+ }
+ ret = pm8xxx_gpio_config(top_spk_pamp_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure Top Spk Ampl"
+ " gpio %u\n", __func__, top_spk_pamp_gpio);
+ else {
+ pr_debug("%s: enable Top spkr amp gpio\n", __func__);
+ gpio_direction_output(top_spk_pamp_gpio, 1);
+ }
+ } else {
+ pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO."
+ " gpio = %u\n", __func__, spk_amp_gpio);
+ return;
+ }
+}
+
+static void msm_ext_spk_power_amp_on(u32 spk)
+{
+ if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+
+ if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
+ (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+
+ pr_debug("%s() External Bottom Speaker Ampl already "
+ "turned on. spk = 0x%08x\n", __func__, spk);
+ return;
+ }
+
+ msm_ext_bottom_spk_pamp |= spk;
+
+ if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
+ (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+
+ msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
+ pr_debug("%s: slepping 4 ms after turning on external "
+ " Bottom Speaker Ampl\n", __func__);
+ usleep_range(4000, 4000);
+ }
+
+ } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
+
+ if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
+ (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
+
+ pr_debug("%s() External Top Speaker Ampl already"
+ "turned on. spk = 0x%08x\n", __func__, spk);
+ return;
+ }
+
+ msm_ext_top_spk_pamp |= spk;
+
+ if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
+ (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
+
+ msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
+ pr_debug("%s: sleeping 4 ms after turning on "
+ " external Top Speaker Ampl\n", __func__);
+ usleep_range(4000, 4000);
+ }
+ } else {
+
+ pr_err("%s: ERROR : Invalid External Speaker Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void msm_ext_spk_power_amp_off(u32 spk)
+{
+ if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+
+ if (!msm_ext_bottom_spk_pamp)
+ return;
+
+ gpio_direction_output(bottom_spk_pamp_gpio, 0);
+ gpio_free(bottom_spk_pamp_gpio);
+ msm_ext_bottom_spk_pamp = 0;
+
+ pr_debug("%s: sleeping 4 ms after turning off external Bottom"
+ " Speaker Ampl\n", __func__);
+
+ usleep_range(4000, 4000);
+
+ } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
+
+ if (!msm_ext_top_spk_pamp)
+ return;
+
+ gpio_direction_output(top_spk_pamp_gpio, 0);
+ gpio_free(top_spk_pamp_gpio);
+ msm_ext_top_spk_pamp = 0;
+
+ pr_debug("%s: sleeping 4 ms after turning off external Top"
+ " Spkaker Ampl\n", __func__);
+
+ usleep_range(4000, 4000);
+ } else {
+
+ pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void msm_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+ if (msm_spk_control == MSM8974_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
+ }
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int msm_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+ ucontrol->value.integer.value[0] = msm_spk_control;
+ return 0;
+}
+static int msm_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ pr_debug("%s()\n", __func__);
+ if (msm_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ msm_spk_control = ucontrol->value.integer.value[0];
+ msm_ext_control(codec);
+ return 1;
+}
+static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
+ msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
+ msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
+ else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
+ msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
+ msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ } else {
+ if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
+ msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
+ msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
+ else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
+ msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
+ msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
+{
+ return 0;
+}
+
+static int msm_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ msm_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
+
+ SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
+
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+
+};
+
+static const struct snd_soc_dapm_route common_audio_map[] = {
+
+ {"RX_BIAS", NULL, "MCLK"},
+ {"LDO_H", NULL, "MCLK"},
+
+ /* Speaker path */
+ {"Ext Spk Bottom Pos", NULL, "LINEOUT1"},
+ {"Ext Spk Bottom Neg", NULL, "LINEOUT3"},
+
+ {"Ext Spk Top Pos", NULL, "LINEOUT2"},
+ {"Ext Spk Top Neg", NULL, "LINEOUT4"},
+
+ /* Microphone path */
+ {"AMIC1", NULL, "MIC BIAS1 Internal1"},
+ {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
+
+ {"AMIC2", NULL, "MIC BIAS2 External"},
+ {"MIC BIAS2 External", NULL, "Headset Mic"},
+
+ /**
+ * AMIC3 and AMIC4 inputs are connected to ANC microphones
+ * These mics are biased differently on CDP and FLUID
+ * routing entries below are based on bias arrangement
+ * on FLUID.
+ */
+ {"AMIC3", NULL, "MIC BIAS3 Internal1"},
+ {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
+
+ {"AMIC4", NULL, "MIC BIAS1 Internal2"},
+ {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
+
+ {"HEADPHONE", NULL, "LDO_H"},
+
+ /**
+ * The digital Mic routes are setup considering
+ * fluid as default device.
+ */
+
+ /**
+ * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
+ * Digital Mic GM5 on CDP mainboard.
+ * Conncted to DMIC2 Input on Tabla codec.
+ */
+ {"DMIC2", NULL, "MIC BIAS1 External"},
+ {"MIC BIAS1 External", NULL, "Digital Mic1"},
+
+ /**
+ * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
+ * Digital Mic GM6 on CDP mainboard.
+ * Conncted to DMIC1 Input on Tabla codec.
+ */
+ {"DMIC1", NULL, "MIC BIAS1 External"},
+ {"MIC BIAS1 External", NULL, "Digital Mic2"},
+
+ /**
+ * Digital Mic3. Back Bottom Digital Mic on Fluid.
+ * Digital Mic GM1 on CDP mainboard.
+ * Conncted to DMIC4 Input on Tabla codec.
+ */
+ {"DMIC4", NULL, "MIC BIAS3 External"},
+ {"MIC BIAS3 External", NULL, "Digital Mic3"},
+
+ /**
+ * Digital Mic4. Back top Digital Mic on Fluid.
+ * Digital Mic GM2 on CDP mainboard.
+ * Conncted to DMIC3 Input on Tabla codec.
+ */
+ {"DMIC3", NULL, "MIC BIAS3 External"},
+ {"MIC BIAS3 External", NULL, "Digital Mic4"},
+
+ /**
+ * Digital Mic5. Front top Digital Mic on Fluid.
+ * Digital Mic GM3 on CDP mainboard.
+ * Conncted to DMIC5 Input on Tabla codec.
+ */
+ {"DMIC5", NULL, "MIC BIAS4 External"},
+ {"MIC BIAS4 External", NULL, "Digital Mic5"},
+
+ /* Tabla digital Mic6 - back bottom digital Mic on Liquid and
+ * bottom mic on CDP. FLUID/MTP do not have dmic6 installed.
+ */
+ {"DMIC6", NULL, "MIC BIAS4 External"},
+ {"MIC BIAS4 External", NULL, "Digital Mic6"},
+};
+
+static const char *spk_function[] = {"Off", "On"};
+static const char *slim0_rx_ch_text[] = {"One", "Two"};
+static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+
+static const struct soc_enum msm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+ SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+};
+
+static const char *btsco_rate_text[] = {"8000", "16000"};
+static const struct soc_enum msm_btsco_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
+};
+
+static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ return 1;
+}
+
+static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ return 1;
+}
+
+static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_btsco_rate = %d", __func__,
+ msm_btsco_rate);
+ ucontrol->value.integer.value[0] = msm_btsco_rate;
+ return 0;
+}
+
+static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ case 1:
+ msm_btsco_rate = BTSCO_RATE_16KHZ;
+ break;
+ default:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: msm_btsco_rate = %d\n", __func__,
+ msm_btsco_rate);
+ return 0;
+}
+
+static const struct snd_kcontrol_new tabla_msm_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
+ msm_set_spk),
+ SOC_ENUM_EXT("SLIM_0_RX Channels", msm_enum[1],
+ msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", msm_enum[2],
+ msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+};
+
+static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
+ SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
+ msm_btsco_rate_get, msm_btsco_rate_put),
+};
+
+static struct snd_soc_dsp_link lpa_fe_media = {
+ .playback = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+static struct snd_soc_dsp_link fe_media = {
+ .playback = true,
+ .capture = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* PCM only supports mono output with 8khz sample rate */
+ rate->min = rate->max = 8000;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+static int msm_aux_pcm_get_gpios(void)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
+ __func__, GPIO_AUX_PCM_DOUT);
+ goto fail_dout;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
+ __func__, GPIO_AUX_PCM_DIN);
+ goto fail_din;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
+ __func__, GPIO_AUX_PCM_SYNC);
+ goto fail_sync;
+ }
+ ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
+ __func__, GPIO_AUX_PCM_CLK);
+ goto fail_clk;
+ }
+
+ return 0;
+
+fail_clk:
+ gpio_free(GPIO_AUX_PCM_SYNC);
+fail_sync:
+ gpio_free(GPIO_AUX_PCM_DIN);
+fail_din:
+ gpio_free(GPIO_AUX_PCM_DOUT);
+fail_dout:
+
+ return ret;
+}
+static int msm_aux_pcm_free_gpios(void)
+{
+ gpio_free(GPIO_AUX_PCM_DIN);
+ gpio_free(GPIO_AUX_PCM_DOUT);
+ gpio_free(GPIO_AUX_PCM_SYNC);
+ gpio_free(GPIO_AUX_PCM_CLK);
+
+ return 0;
+}
+
+static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ ret = msm_aux_pcm_get_gpios();
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ msm_aux_pcm_free_gpios();
+}
+static struct snd_soc_ops msm_auxpcm_be_ops = {
+ .startup = msm_auxpcm_startup,
+ .shutdown = msm_auxpcm_shutdown,
+};
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_dai[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8974 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8974 LPA",
+ .stream_name = "LPA",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-lpa",
+ .dynamic = 1,
+ .dsp_link = &lpa_fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.4106",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.4107",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ },
+
+};
+
+struct snd_soc_card snd_soc_card_msm = {
+ .name = "msm8974-taiko-snd-card",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+};
+
+static struct platform_device *msm_snd_device;
+
+static void msm_free_headset_mic_gpios(void)
+{
+ if (msm_headset_gpios_configured) {
+ gpio_free(PM8921_GPIO_PM_TO_SYS(23));
+ gpio_free(PM8921_GPIO_PM_TO_SYS(35));
+ }
+}
+
+static int __init msm_audio_init(void)
+{
+ int ret = 0;
+ if (!machine_is_copper_sim()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return -ENODEV;
+ }
+ msm_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!msm_snd_device) {
+ pr_err("Platform device allocation failed\n");
+ kfree(mbhc_cfg.calibration);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(msm_snd_device, &snd_soc_card_msm);
+ ret = platform_device_add(msm_snd_device);
+ if (ret) {
+ platform_device_put(msm_snd_device);
+ kfree(mbhc_cfg.calibration);
+ return ret;
+ }
+ return ret;
+
+}
+module_init(msm_audio_init);
+
+static void __exit msm_audio_exit(void)
+{
+ if (!machine_is_copper_sim()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return ;
+ }
+ msm_free_headset_mic_gpios();
+ platform_device_unregister(msm_snd_device);
+ kfree(mbhc_cfg.calibration);
+}
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index 2ada94f..bf6f743 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -45,6 +45,189 @@
static struct adm_ctl this_adm;
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params)
+{
+ struct asm_pp_params_command *open = NULL;
+ int ret = 0, sz = 0;
+ int index;
+
+ pr_debug("SRS - %s", __func__);
+ switch (srs_tech_id) {
+ case SRS_ID_GLOBAL: {
+ struct srs_trumedia_params_GLOBAL *glb_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_GLOBAL) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ glb_params = (struct srs_trumedia_params_GLOBAL *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(glb_params, srs_params,
+ sizeof(struct srs_trumedia_params_GLOBAL));
+ pr_debug("SRS - %s: Global params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x\n",
+ __func__, (int)glb_params->v1,
+ (int)glb_params->v2, (int)glb_params->v3,
+ (int)glb_params->v4, (int)glb_params->v5,
+ (int)glb_params->v6, (int)glb_params->v7,
+ (int)glb_params->v8);
+ break;
+ }
+ case SRS_ID_WOWHD: {
+ struct srs_trumedia_params_WOWHD *whd_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_WOWHD);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_WOWHD) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_WOWHD;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_WOWHD);
+ whd_params = (struct srs_trumedia_params_WOWHD *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(whd_params, srs_params,
+ sizeof(struct srs_trumedia_params_WOWHD));
+ pr_debug("SRS - %s: WOWHD params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x, 9 = %x,"
+ " 10 = %x, 11 = %x\n", __func__, (int)whd_params->v1,
+ (int)whd_params->v2, (int)whd_params->v3,
+ (int)whd_params->v4, (int)whd_params->v5,
+ (int)whd_params->v6, (int)whd_params->v7,
+ (int)whd_params->v8, (int)whd_params->v9,
+ (int)whd_params->v10, (int)whd_params->v11);
+ break;
+ }
+ case SRS_ID_CSHP: {
+ struct srs_trumedia_params_CSHP *chp_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_CSHP);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_CSHP) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_CSHP;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_CSHP);
+ chp_params = (struct srs_trumedia_params_CSHP *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(chp_params, srs_params,
+ sizeof(struct srs_trumedia_params_CSHP));
+ pr_debug("SRS - %s: CSHP params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x,"
+ " 9 = %x\n", __func__, (int)chp_params->v1,
+ (int)chp_params->v2, (int)chp_params->v3,
+ (int)chp_params->v4, (int)chp_params->v5,
+ (int)chp_params->v6, (int)chp_params->v7,
+ (int)chp_params->v8, (int)chp_params->v9);
+ break;
+ }
+ case SRS_ID_HPF: {
+ struct srs_trumedia_params_HPF *hpf_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_HPF);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_HPF) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_HPF;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_HPF);
+ hpf_params = (struct srs_trumedia_params_HPF *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(hpf_params, srs_params,
+ sizeof(struct srs_trumedia_params_HPF));
+ pr_debug("SRS - %s: HPF params - 1 = %x\n", __func__,
+ (int)hpf_params->v1);
+ break;
+ }
+ case SRS_ID_PEQ: {
+ struct srs_trumedia_params_PEQ *peq_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_PEQ);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_PEQ) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_PEQ;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_PEQ);
+ peq_params = (struct srs_trumedia_params_PEQ *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(peq_params, srs_params,
+ sizeof(struct srs_trumedia_params_PEQ));
+ pr_debug("SRS - %s: PEQ params - 1 = %x 2 = %x, 3 = %x,"
+ " 4 = %x\n", __func__, (int)peq_params->v1,
+ (int)peq_params->v2, (int)peq_params->v3,
+ (int)peq_params->v4);
+ break;
+ }
+ case SRS_ID_HL: {
+ struct srs_trumedia_params_HL *hl_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_HL);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_HL) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_HL;
+ open->params.param_size = sizeof(struct srs_trumedia_params_HL);
+ hl_params = (struct srs_trumedia_params_HL *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(hl_params, srs_params,
+ sizeof(struct srs_trumedia_params_HL));
+ pr_debug("SRS - %s: HL params - 1 = %x, 2 = %x, 3 = %x, 4 = %x,"
+ " 5 = %x, 6 = %x, 7 = %x\n", __func__,
+ (int)hl_params->v1, (int)hl_params->v2,
+ (int)hl_params->v3, (int)hl_params->v4,
+ (int)hl_params->v5, (int)hl_params->v6,
+ (int)hl_params->v7);
+ break;
+ }
+ default:
+ goto fail_cmd;
+ }
+
+ open->payload = NULL;
+ open->params.module_id = SRS_TRUMEDIA_MODULE_ID;
+ open->params.reserved = 0;
+ open->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ open->hdr.pkt_size = sz;
+ open->hdr.src_svc = APR_SVC_ADM;
+ open->hdr.src_domain = APR_DOMAIN_APPS;
+ open->hdr.src_port = port_id;
+ open->hdr.dest_svc = APR_SVC_ADM;
+ open->hdr.dest_domain = APR_DOMAIN_ADSP;
+ index = afe_get_port_index(port_id);
+ open->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ open->hdr.token = port_id;
+ open->hdr.opcode = ADM_CMD_SET_PARAMS;
+ pr_debug("SRS - %s: Command was sent now check Q6 - port id = %d,"
+ " size %d, module id %x, param id %x.\n", __func__,
+ open->hdr.dest_port, open->payload_size,
+ open->params.module_id, open->params.param_id);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)open);
+ if (ret < 0) {
+ pr_err("SRS - %s: ADM enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait, 1,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("SRS - %s: ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ kfree(open);
+ return ret;
+}
+
static int32_t adm_callback(struct apr_client_data *data, void *priv)
{
uint32_t *payload;
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index 434197a..6f765d1 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1 +1,4 @@
-obj-y := q6adm.o q6afe.o q6asm.o q6audio-v2.o
+snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
new file mode 100644
index 0000000..daba79d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -0,0 +1,666 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#include "msm-compr-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+struct snd_msm {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm compressed_audio = {NULL, 0x2000} ;
+
+static struct audio_locks the_locks;
+
+static struct snd_pcm_hardware msm_compr_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 1200 * 1024 * 2,
+ .period_bytes_min = 4800,
+ .period_bytes_max = 1200 * 1024,
+ .periods_min = 2,
+ .periods_max = 512,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void compr_event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct compr_audio *compr = priv;
+ struct msm_audio *prtd = &compr->prtd;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = NULL;
+ int i = 0;
+
+ pr_debug("%s opcode =%08x\n", __func__, opcode);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ uint32_t *ptrmem = (uint32_t *)¶m;
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start)) {
+ atomic_set(&prtd->pending_buffer, 1);
+ break;
+ } else
+ atomic_set(&prtd->pending_buffer, 0);
+
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
+ break;
+ buf = prtd->audio_client->port[IN].buf;
+ pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+ __func__, prtd->pcm_count, prtd->out_head);
+ pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+ __func__, prtd->out_head,
+ ((unsigned int)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)));
+
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
+ i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2: {
+ if (!atomic_read(&prtd->pending_buffer))
+ break;
+ pr_debug("%s:writing %d bytes"
+ " of buffer[%d] to dsp\n",
+ __func__, prtd->pcm_count, prtd->out_head);
+ buf = prtd->audio_client->port[IN].buf;
+ pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+ __func__, prtd->out_head,
+ ((unsigned int)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count)));
+ param.paddr = (unsigned long)buf[prtd->out_head].phys;
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[prtd->out_head].phys;
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1)
+ & (runtime->periods - 1);
+ atomic_set(&prtd->pending_buffer, 0);
+ }
+ break;
+ case ASM_STREAM_CMD_FLUSH:
+ pr_debug("ASM_STREAM_CMD_FLUSH\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_compr_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct asm_aac_cfg aac_cfg;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ prtd->out_head = 0;
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ if (prtd->enabled)
+ return 0;
+
+ switch (compr->info.codec_param.codec.id) {
+ case SND_AUDIOCODEC_MP3:
+ /* No media format block for mp3 */
+ break;
+ case SND_AUDIOCODEC_AAC:
+ pr_debug("SND_AUDIOCODEC_AAC\n");
+ memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ aac_cfg.format = 0x03;
+ aac_cfg.ch_cfg = runtime->channels;
+ aac_cfg.sample_rate = runtime->rate;
+ ret = q6asm_media_format_block_aac(prtd->audio_client,
+ &aac_cfg);
+ if (ret < 0)
+ pr_err("%s: CMD Format block failed\n", __func__);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->pcm_irq_pos = 0;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void populate_codec_list(struct compr_audio *compr,
+ struct snd_pcm_runtime *runtime)
+{
+ pr_debug("%s\n", __func__);
+ /* MP3 Block */
+ compr->info.compr_cap.num_codecs = 1;
+ compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
+ compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
+ compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
+ compr->info.compr_cap.max_fragments = runtime->hw.periods_max;
+ compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
+ compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
+ /* Add new codecs here */
+}
+
+static int msm_compr_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct compr_audio *compr;
+ struct msm_audio *prtd;
+ int ret = 0;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EINVAL;
+
+ pr_debug("%s\n", __func__);
+ compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
+ if (compr == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd = &compr->prtd;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)compr_event_handler, compr);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ runtime->hw = msm_compr_hardware_playback;
+
+ pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
+
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ prtd->cmd_ack = 1;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ atomic_set(&prtd->pending_buffer, 1);
+ compr->codec = FORMAT_MP3;
+ populate_codec_list(compr, runtime);
+ runtime->private_data = compr;
+ compressed_audio.prtd = &compr->prtd;
+ ret = compressed_set_volume(compressed_audio.volume);
+ if (ret < 0)
+ pr_err("%s : Set Volume failed : %d", __func__, ret);
+
+ ret = q6asm_set_softpause(compressed_audio.prtd->audio_client,
+ &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(compressed_audio.prtd->audio_client,
+ &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+int compressed_set_volume(unsigned volume)
+{
+ int rc = 0;
+ if (compressed_audio.prtd && compressed_audio.prtd->audio_client) {
+ rc = q6asm_set_volume(compressed_audio.prtd->audio_client,
+ volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ compressed_audio.volume = volume;
+ return rc;
+}
+
+static int msm_compr_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ int dir = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ atomic_set(&prtd->pending_buffer, 0);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ compressed_audio.prtd = NULL;
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_compr_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_compr_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = EINVAL;
+ return ret;
+}
+static int msm_compr_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_compr_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = EINVAL;
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_compr_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+ return result;
+}
+
+static int msm_compr_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ pr_debug("%s\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ return -EINVAL;
+
+ ret = q6asm_open_write(prtd->audio_client, compr->codec);
+ if (ret < 0) {
+ pr_err("%s: Session out open failed\n", __func__);
+ return -ENOMEM;
+ }
+ ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed "
+ "rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int msm_compr_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct compr_audio *compr = runtime->private_data;
+ struct msm_audio *prtd = &compr->prtd;
+ uint64_t timestamp;
+ uint64_t temp;
+
+ switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP: {
+ struct snd_compr_tstamp tstamp;
+ pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+
+ memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+ timestamp = q6asm_get_session_time(prtd->audio_client);
+ if (timestamp < 0) {
+ pr_err("%s: Get Session Time return value =%lld\n",
+ __func__, timestamp);
+ return -EAGAIN;
+ }
+ temp = (timestamp * 2 * runtime->channels);
+ temp = temp * (runtime->rate/1000);
+ temp = div_u64(temp, 1000);
+ tstamp.sampling_rate = runtime->rate;
+ tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
+ tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
+ tstamp.timestamp = timestamp;
+ pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
+ "timestamp = %lld,\n",
+ __func__, tstamp.rendered, tstamp.decoded,
+ tstamp.timestamp);
+ if (copy_to_user((void *) arg, &tstamp,
+ sizeof(struct snd_compr_tstamp)))
+ return -EFAULT;
+ return 0;
+ }
+ case SNDRV_COMPRESS_GET_CAPS:
+ pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
+ if (copy_to_user((void *) arg, &compr->info.compr_cap,
+ sizeof(struct snd_compr_caps))) {
+ rc = -EFAULT;
+ pr_err("%s: ERROR: copy to user\n", __func__);
+ return rc;
+ }
+ return 0;
+ case SNDRV_COMPRESS_SET_PARAMS:
+ pr_debug("SNDRV_COMPRESS_SET_PARAMS: ");
+ if (copy_from_user(&compr->info.codec_param, (void *) arg,
+ sizeof(struct snd_compr_params))) {
+ rc = -EFAULT;
+ pr_err("%s: ERROR: copy from user\n", __func__);
+ return rc;
+ }
+ switch (compr->info.codec_param.codec.id) {
+ case SND_AUDIOCODEC_MP3:
+ /* For MP3 we dont need any other parameter */
+ pr_debug("SND_AUDIOCODEC_MP3\n");
+ compr->codec = FORMAT_MP3;
+ break;
+ case SND_AUDIOCODEC_AAC:
+ pr_debug("SND_AUDIOCODEC_AAC\n");
+ compr->codec = FORMAT_MPEG4_AAC;
+ break;
+ default:
+ pr_debug("FORMAT_LINEAR_PCM\n");
+ compr->codec = FORMAT_LINEAR_PCM;
+ break;
+ }
+ return 0;
+ case SNDRV_PCM_IOCTL1_RESET:
+ prtd->cmd_ack = 0;
+ rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ if (rc < 0)
+ pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
+ rc = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (rc < 0)
+ pr_err("Flush cmd timeout\n");
+ prtd->pcm_irq_pos = 0;
+ break;
+ default:
+ break;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static struct snd_pcm_ops msm_compr_ops = {
+ .open = msm_compr_open,
+ .hw_params = msm_compr_hw_params,
+ .close = msm_compr_close,
+ .ioctl = msm_compr_ioctl,
+ .prepare = msm_compr_prepare,
+ .trigger = msm_compr_trigger,
+ .pointer = msm_compr_pointer,
+ .mmap = msm_compr_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_compr_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_compr_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_compr_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_compr_driver = {
+ .driver = {
+ .name = "msm-compr-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_compr_probe,
+ .remove = __devexit_p(msm_compr_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_compr_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_compr_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
new file mode 100644
index 0000000..2183690
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. 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 _MSM_COMPR_H
+#define _MSM_COMPR_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/snd_compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+
+#include "msm-pcm-q6-v2.h"
+
+struct compr_info {
+ struct snd_compr_caps compr_cap;
+ struct snd_compr_codec_caps codec_caps;
+ struct snd_compr_params codec_param;
+};
+
+struct compr_audio {
+ struct msm_audio prtd;
+ struct compr_info info;
+ uint32_t codec;
+};
+
+#endif /*_MSM_COMPR_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
new file mode 100644
index 0000000..1605062
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -0,0 +1,1229 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <sound/pcm_params.h>
+#include <mach/clk.h>
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static struct clk *pcm_clk;
+static DEFINE_MUTEX(aux_pcm_mutex);
+static int aux_pcm_count;
+static struct msm_dai_auxpcm_pdata *auxpcm_plat_data;
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+ u8 num_bits_set = 0;
+
+ while (sd_line_mask) {
+ num_bits_set++;
+ sd_line_mask = sd_line_mask & (sd_line_mask - 1);
+ }
+ return num_bits_set;
+}
+
+static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
+ dev_dbg(dai->dev, " channel %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->port_config.i2s.channel_mode = 1;
+ return 0;
+}
+
+static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_i2s_data *i2s_pdata =
+ (struct msm_i2s_data *) dai->dev->platform_data;
+
+ dai_data->channels = params_channels(params);
+ if (num_of_bits_set(i2s_pdata->sd_lines) == 1) {
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ pr_warn("greater than stereo has not been validated");
+ break;
+ }
+ }
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.sample_rate = dai_data->rate;
+ dai_data->port_config.i2s.i2s_cfg_minor_version =
+ AFE_API_VERSION_I2S_CONFIG;
+ dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->port_config.i2s.channel_mode = 1;
+
+ return 0;
+}
+
+static int msm_dai_q6_i2s_platform_data_validation(
+ struct snd_soc_dai *dai)
+{
+ u8 num_of_sd_lines;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_i2s_data *i2s_pdata =
+ (struct msm_i2s_data *)dai->dev->platform_data;
+ struct snd_soc_dai_driver *dai_driver =
+ (struct snd_soc_dai_driver *)dai->driver;
+
+ num_of_sd_lines = num_of_bits_set(i2s_pdata->sd_lines);
+
+ switch (num_of_sd_lines) {
+ case 1:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD0;
+ break;
+ case MSM_MI2S_SD1:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD1;
+ break;
+ case MSM_MI2S_SD2:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD2;
+ break;
+ case MSM_MI2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD3;
+ break;
+ default:
+ pr_err("%s: invalid SD line\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 2:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_QUAD01;
+ break;
+ case MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_QUAD23;
+ break;
+ default:
+ pr_err("%s: invalid SD line\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 3:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_6CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 4:
+ switch (i2s_pdata->sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_8CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n", __func__);
+ goto error_invalid_data;
+ }
+ if (i2s_pdata->capability == MSM_MI2S_CAP_RX)
+ dai_driver->playback.channels_max = num_of_sd_lines << 1;
+
+ return 0;
+
+error_invalid_data:
+ return -EINVAL;
+}
+
+static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_data->port_config.i2s.ws_src = 1; /* CPU is master */
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.slim_sch.sb_cfg_minor_version =
+ AFE_API_VERSION_SLIMBUS_CONFIG;
+ dai_data->port_config.slim_sch.bit_width = 16;
+ dai_data->port_config.slim_sch.data_format = 0;
+ dai_data->port_config.slim_sch.num_channels = dai_data->channels;
+ dai_data->port_config.slim_sch.sample_rate = dai_data->rate;
+
+ dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n"
+ "num_channel %hu shared_ch_mapping[0] %hu\n"
+ "slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n"
+ "sample_rate %d\n", __func__,
+ dai_data->port_config.slim_sch.slimbus_dev_id,
+ dai_data->port_config.slim_sch.bit_width,
+ dai_data->port_config.slim_sch.data_format,
+ dai_data->port_config.slim_sch.num_channels,
+ dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1],
+ dai_data->port_config.slim_sch.shared_ch_mapping[2],
+ dai_data->rate);
+
+ return 0;
+}
+
+static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ dev_dbg(dai->dev, "channels %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ memset(&dai_data->port_config, 0, sizeof(dai_data->port_config));
+
+ return 0;
+}
+static int msm_dai_q6_auxpcm_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+ (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+
+ if (params_channels(params) != 1) {
+ dev_err(dai->dev, "AUX PCM supports only mono stream\n");
+ return -EINVAL;
+ }
+ dai_data->channels = params_channels(params);
+
+ if (params_rate(params) != 8000) {
+ dev_err(dai->dev, "AUX PCM supports only 8KHz sampling rate\n");
+ return -EINVAL;
+ }
+ dai_data->rate = params_rate(params);
+
+ dai_data->port_config.pcm.pcm_cfg_minor_version =
+ AFE_API_VERSION_PCM_CONFIG;
+ dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode;
+ dai_data->port_config.pcm.sync_src = auxpcm_pdata->sync;
+ dai_data->port_config.pcm.frame_setting = auxpcm_pdata->frame;
+ dai_data->port_config.pcm.quantype = auxpcm_pdata->quant;
+ dai_data->port_config.pcm.ctrl_data_out_enable = auxpcm_pdata->data;
+ dai_data->port_config.pcm.sample_rate = dai_data->rate;
+ dai_data->port_config.pcm.num_channels = dai_data->channels;
+ dai_data->port_config.pcm.bit_width = 16;
+ dai_data->port_config.pcm.slot_number_mapping[0] = auxpcm_pdata->slot;
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.rtproxy.num_channels = params_channels(params);
+ dai_data->port_config.rtproxy.sample_rate = params_rate(params);
+
+ pr_debug("channel %d entered,dai_id: %d,rate: %d\n",
+ dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate);
+
+ dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version =
+ AFE_API_VERSION_RT_PROXY_CONFIG;
+ dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */
+ dai_data->port_config.rtproxy.interleaved = 1;
+ dai_data->port_config.rtproxy.frame_size = params_period_bytes(params);
+ dai_data->port_config.rtproxy.jitter_allowance =
+ dai_data->port_config.rtproxy.frame_size/2;
+ dai_data->port_config.rtproxy.low_water_mark = 0;
+ dai_data->port_config.rtproxy.high_water_mark = 0;
+
+ return 0;
+}
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
+ break;
+ case MI2S_RX:
+ rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream);
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ rc = msm_dai_q6_slim_bus_hw_params(params, dai,
+ substream->stream);
+ break;
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream);
+ break;
+ case RT_PROXY_DAI_001_TX:
+ case RT_PROXY_DAI_001_RX:
+ case RT_PROXY_DAI_002_TX:
+ case RT_PROXY_DAI_002_RX:
+ rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
+ break;
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = 0;
+ break;
+ default:
+ dev_err(dai->dev, "invalid AFE port ID\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (aux_pcm_count == 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. Just"
+ " return\n", __func__, dai->id);
+ mutex_unlock(&aux_pcm_mutex);
+ return;
+ }
+
+ aux_pcm_count--;
+
+ if (aux_pcm_count > 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
+ __func__, dai->id, aux_pcm_count);
+ mutex_unlock(&aux_pcm_mutex);
+ return;
+ } else if (aux_pcm_count < 0) {
+ dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+ " aux_pcm_count = %d < 0\n",
+ __func__, dai->id, aux_pcm_count);
+ aux_pcm_count = 0;
+ mutex_unlock(&aux_pcm_mutex);
+ return;
+ }
+
+ pr_debug("%s: dai->id = %d aux_pcm_count = %d\n", __func__,
+ dai->id, aux_pcm_count);
+
+ rc = afe_close(PCM_RX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close PCM_RX AFE port\n");
+
+ rc = afe_close(PCM_TX);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+
+ mutex_unlock(&aux_pcm_mutex);
+}
+
+static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ pr_debug("%s, stop pseudo port:%d\n",
+ __func__, dai->id);
+ rc = afe_stop_pseudo_port(dai->id);
+ break;
+ default:
+ rc = afe_close(dai->id); /* can block */
+ break;
+ }
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+}
+
+static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (aux_pcm_count == 2) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 2. Just"
+ " return.\n", __func__, dai->id);
+ mutex_unlock(&aux_pcm_mutex);
+ return 0;
+ } else if (aux_pcm_count > 2) {
+ dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+ " aux_pcm_count = %d > 2\n",
+ __func__, dai->id, aux_pcm_count);
+ mutex_unlock(&aux_pcm_mutex);
+ return 0;
+ }
+
+ aux_pcm_count++;
+ if (aux_pcm_count == 2) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d after "
+ " increment\n", __func__, dai->id, aux_pcm_count);
+ mutex_unlock(&aux_pcm_mutex);
+ return 0;
+ }
+
+ pr_debug("%s:dai->id:%d aux_pcm_count = %d. opening afe\n",
+ __func__, dai->id, aux_pcm_count);
+
+ rc = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE APR\n");
+
+ /*
+ * For AUX PCM Interface the below sequence of clk
+ * settings and afe_open is a strict requirement.
+ *
+ * Also using afe_open instead of afe_port_start_nowait
+ * to make sure the port is open before deasserting the
+ * clock line. This is required because pcm register is
+ * not written before clock deassert. Hence the hw does
+ * not get updated with new setting if the below clock
+ * assert/deasset and afe_open sequence is not followed.
+ */
+
+ afe_open(PCM_RX, &dai_data->port_config, dai_data->rate);
+
+ afe_open(PCM_TX, &dai_data->port_config, dai_data->rate);
+
+ mutex_unlock(&aux_pcm_mutex);
+
+ return rc;
+}
+
+static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* PORT START should be set if prepare called in active state */
+ rc = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE APR\n");
+ }
+ return rc;
+}
+
+static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ pr_debug("%s:port:%d cmd:%d aux_pcm_count= %d",
+ __func__, dai->id, cmd, aux_pcm_count);
+
+ switch (cmd) {
+
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* afe_open will be called from prepare */
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ return 0;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+
+}
+
+static int msm_dai_q6_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ /* Start/stop port without waiting for Q6 AFE response. Need to have
+ * native q6 AFE driver propagates AFE response in order to handle
+ * port start/stop command error properly if error does arise.
+ */
+ pr_debug("%s:port:%d cmd:%d dai_data->status_mask = %ld",
+ __func__, dai->id, cmd, *dai_data->status_mask);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ afe_pseudo_port_start_nowait(dai->id);
+ break;
+ default:
+ afe_port_start_nowait(dai->id,
+ &dai_data->port_config, dai_data->rate);
+ break;
+ }
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ afe_pseudo_port_stop_nowait(dai->id);
+ break;
+ default:
+ afe_port_stop_nowait(dai->id);
+ break;
+ }
+ clear_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+static int msm_dai_q6_dai_auxpcm_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+ (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (!auxpcm_plat_data)
+ auxpcm_plat_data = auxpcm_pdata;
+ else if (auxpcm_plat_data != auxpcm_pdata) {
+
+ dev_err(dai->dev, "AUX PCM RX and TX devices does not have"
+ " same platform data\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The clk name for AUX PCM operation is passed as platform
+ * data to the cpu driver, since cpu drive is unaware of any
+ * boarc specific configuration.
+ */
+ if (!pcm_clk)
+ pcm_clk = clk_get(dai->dev, auxpcm_pdata->clk);
+
+ mutex_unlock(&aux_pcm_mutex);
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ pr_err("%s : probe done for dai->id %d\n", __func__, dai->id);
+ return rc;
+}
+
+static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ mutex_lock(&aux_pcm_mutex);
+
+ if (aux_pcm_count == 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. clean"
+ " up and return\n", __func__, dai->id);
+ goto done;
+ }
+
+ aux_pcm_count--;
+
+ if (aux_pcm_count > 0) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
+ __func__, dai->id, aux_pcm_count);
+ goto done;
+ } else if (aux_pcm_count < 0) {
+ dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+ " aux_pcm_count = %d < 0\n",
+ __func__, dai->id, aux_pcm_count);
+ goto done;
+ }
+
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d."
+ "closing afe\n",
+ __func__, dai->id, aux_pcm_count);
+
+ rc = afe_close(PCM_RX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM RX AFE port\n");
+
+ rc = afe_close(PCM_TX);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AUX PCM TX AFE port\n");
+
+done:
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ mutex_unlock(&aux_pcm_mutex);
+
+ return 0;
+}
+static int msm_dai_q6_dai_i2s_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ goto rtn;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ rc = msm_dai_q6_i2s_platform_data_validation(dai);
+ if (rc != 0) {
+ pr_err("%s: The msm_dai_q6_i2s_platform_data_validation failed\n",
+ __func__);
+ kfree(dai_data);
+ }
+rtn:
+ return rc;
+}
+
+static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ return rc;
+}
+
+static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ pr_debug("%s, stop pseudo port:%d\n",
+ __func__, dai->id);
+ rc = afe_stop_pseudo_port(dai->id);
+ break;
+ default:
+ rc = afe_close(dai->id); /* can block */
+ }
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ return 0;
+}
+
+static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int rc = 0;
+
+ dev_dbg(dai->dev, "enter %s, id = %d fmt[%d]\n", __func__,
+ dai->id, fmt);
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case MI2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
+ break;
+ default:
+ dev_err(dai->dev, "invalid cpu_dai set_fmt\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+
+{
+ int rc = 0;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ unsigned int i = 0;
+
+ dev_dbg(dai->dev, "enter %s, id = %d\n", __func__,
+ dai->id);
+ switch (dai->id) {
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ /* channel number to be between 128 and 255. For RX port
+ * use channel numbers from 138 to 144, for TX port
+ * use channel numbers from 128 to 137
+ * For ports between MDM-APQ use channel numbers from 145
+ */
+ if (!rx_slot)
+ return -EINVAL;
+ for (i = 0; i < rx_num; i++) {
+ dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+ rx_slot[i];
+ pr_debug("%s: find number of channels[%d] ch[%d]\n",
+ __func__, i,
+ rx_slot[i]);
+ }
+ dai_data->port_config.slim_sch.num_channels = rx_num;
+ pr_debug("%s:SLIMBUS_0_RX cnt[%d] ch[%d %d]\n", __func__,
+ rx_num, dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+
+ break;
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ /* channel number to be between 128 and 255. For RX port
+ * use channel numbers from 138 to 144, for TX port
+ * use channel numbers from 128 to 137
+ * For ports between MDM-APQ use channel numbers from 145
+ */
+ if (!tx_slot)
+ return -EINVAL;
+ for (i = 0; i < tx_num; i++) {
+ dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+ tx_slot[i];
+ pr_debug("%s: find number of channels[%d] ch[%d]\n",
+ __func__, i, tx_slot[i]);
+ }
+ dai_data->port_config.slim_sch.num_channels = tx_num;
+ pr_debug("%s:SLIMBUS_0_TX cnt[%d] ch[%d %d]\n", __func__,
+ tx_num, dai_data->port_config.slim_sch.shared_ch_mapping[0],
+ dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+ break;
+ default:
+ dev_err(dai->dev, "invalid cpu_dai set_fmt\n");
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_ops = {
+ .prepare = msm_dai_q6_prepare,
+ .trigger = msm_dai_q6_trigger,
+ .hw_params = msm_dai_q6_hw_params,
+ .shutdown = msm_dai_q6_shutdown,
+ .set_fmt = msm_dai_q6_set_fmt,
+ .set_channel_map = msm_dai_q6_set_channel_map,
+};
+
+static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = {
+ .prepare = msm_dai_q6_auxpcm_prepare,
+ .trigger = msm_dai_q6_auxpcm_trigger,
+ .hw_params = msm_dai_q6_auxpcm_hw_params,
+ .shutdown = msm_dai_q6_auxpcm_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_i2s_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_i2s_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_i2s_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_voice_playback_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 8000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_dai_auxpcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 8000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_dai_auxpcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+};
+
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static __devinit int msm_dai_q6_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ switch (pdev->id) {
+ case PRIMARY_I2S_RX:
+ case SECONDARY_I2S_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
+ break;
+ case PRIMARY_I2S_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
+ break;
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_aux_pcm_rx_dai);
+ break;
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_aux_pcm_tx_dai);
+ break;
+ case MI2S_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_i2s_rx_dai);
+ break;
+ case SLIMBUS_0_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_rx_dai);
+ break;
+ case SLIMBUS_0_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_tx_dai);
+ break;
+
+ case SLIMBUS_1_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_1_rx_dai);
+ break;
+ case SLIMBUS_1_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_1_tx_dai);
+ break;
+ case INT_BT_SCO_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_bt_sco_rx_dai);
+ break;
+ case INT_BT_SCO_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_bt_sco_tx_dai);
+ break;
+ case INT_FM_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+ break;
+ case INT_FM_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+ break;
+ case RT_PROXY_DAI_001_RX:
+ case RT_PROXY_DAI_002_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_rx_dai);
+ break;
+ case RT_PROXY_DAI_001_TX:
+ case RT_PROXY_DAI_002_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_tx_dai);
+ break;
+ case VOICE_PLAYBACK_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_voice_playback_tx_dai);
+ break;
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_incall_record_dai);
+ break;
+ default:
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static __devexit int msm_dai_q6_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_dai_q6_driver = {
+ .probe = msm_dai_q6_dev_probe,
+ .remove = msm_dai_q6_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_dai_q6_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_driver);
+}
+module_init(msm_dai_q6_init);
+
+static void __exit msm_dai_q6_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_driver);
+}
+module_exit(msm_dai_q6_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
new file mode 100644
index 0000000..cab689d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
@@ -0,0 +1,777 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+struct snd_msm_volume {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm_volume multi_ch_pcm_audio = {NULL, 0x2000};
+
+#define PLAYBACK_NUM_PERIODS 8
+#define PLAYBACK_PERIOD_SIZE 4032
+#define CAPTURE_NUM_PERIODS 16
+#define CAPTURE_PERIOD_SIZE 320
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_PERIOD_SIZE,
+ .periods_min = CAPTURE_NUM_PERIODS,
+ .periods_max = CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 6,
+ .buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_PERIOD_SIZE,
+ .periods_min = PLAYBACK_NUM_PERIODS,
+ .periods_max = PLAYBACK_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ int i = 0;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ if (q6asm_is_cpu_buf_avail_nolock(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+ pr_debug("token = 0x%08x\n", token);
+ for (i = 0; i < 8; i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ in_frame_info[token][0] = payload[2];
+ in_frame_info[token][1] = payload[3];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag
+ && q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx))
+ q6asm_read_nolock(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ atomic_set(&prtd->start, 1);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client,
+ runtime->rate, runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ int ret = 0;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = msm_pcm_hardware_playback;
+ ret = q6asm_open_write(prtd->audio_client,
+ FORMAT_MULTI_CHANNEL_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = msm_pcm_hardware_capture;
+ ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->cmd_ack = 1;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+ pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
+ pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
+ multi_ch_pcm_audio.prtd = prtd;
+ ret = multi_ch_pcm_set_volume(multi_ch_pcm_audio.volume);
+ if (ret < 0)
+ pr_err("%s : Set Volume failed : %d", __func__, ret);
+
+ ret = q6asm_set_softpause(multi_ch_pcm_audio.prtd->audio_client,
+ &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(multi_ch_pcm_audio.prtd->audio_client,
+ &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+int multi_ch_pcm_set_volume(unsigned volume)
+{
+ int rc = 0;
+ pr_err("multi_ch_pcm_set_volume\n");
+
+ if (multi_ch_pcm_audio.prtd && multi_ch_pcm_audio.prtd->audio_client) {
+ pr_err("%s q6asm_set_volume\n", __func__);
+ rc = q6asm_set_volume(multi_ch_pcm_audio.prtd->audio_client,
+ volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ multi_ch_pcm_audio.volume = volume;
+ return rc;
+}
+
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ multi_ch_pcm_audio.prtd = NULL;
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-multi-ch-pcm-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Multi channel PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
new file mode 100644
index 0000000..4593784
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -0,0 +1,581 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <asm/dma.h>
+#include "msm-pcm-afe-v2.h"
+
+#define MIN_PERIOD_SIZE (128 * 2)
+#define MAX_PERIOD_SIZE (128 * 2 * 2 * 6)
+static struct snd_pcm_hardware msm_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MAX_PERIOD_SIZE * 32,
+ .period_bytes_min = MIN_PERIOD_SIZE,
+ .period_bytes_max = MAX_PERIOD_SIZE,
+ .periods_min = 32,
+ .periods_max = 384,
+ .fifo_size = 0,
+};
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
+{
+ struct pcm_afe_info *prtd =
+ container_of(hrt, struct pcm_afe_info, hrt);
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u32 mem_map_handle = 0;
+ if (prtd->start) {
+ pr_debug("sending frame to DSP: poll_time: %d\n",
+ prtd->poll_time);
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+ pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+ afe_rt_proxy_port_write(
+ (prtd->dma_addr +
+ (prtd->dsp_cnt *
+ snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+ prtd->dsp_cnt++;
+ hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+ * 1000));
+
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt)
+{
+ struct pcm_afe_info *prtd =
+ container_of(hrt, struct pcm_afe_info, hrt);
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u32 mem_map_handle = 0;
+ if (prtd->start) {
+ if (prtd->dsp_cnt == runtime->periods)
+ prtd->dsp_cnt = 0;
+ pr_err("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+ afe_rt_proxy_port_read(
+ (prtd->dma_addr + (prtd->dsp_cnt
+ * snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+ snd_pcm_lib_period_bytes(prtd->substream));
+ prtd->dsp_cnt++;
+ pr_debug("sending frame rec to DSP: poll_time: %d\n",
+ prtd->poll_time);
+ hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+ * 1000));
+
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+static void pcm_afe_process_tx_pkt(uint32_t opcode,
+ uint32_t token, uint32_t *payload,
+ void *priv)
+{
+ struct pcm_afe_info *prtd = priv;
+ unsigned long dsp_flags;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
+ uint16_t event;
+
+ if (prtd == NULL)
+ return;
+ substream = prtd->substream;
+ runtime = substream->runtime;
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ switch (opcode) {
+ case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+ event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+ switch (event) {
+ case AFE_EVENT_RTPORT_START: {
+ prtd->dsp_cnt = 0;
+ prtd->poll_time = ((unsigned long)((
+ snd_pcm_lib_period_bytes
+ (prtd->substream) *
+ 1000 * 1000)/
+ (runtime->rate *
+ runtime->channels * 2)));
+ pr_debug("prtd->poll_time: %d",
+ prtd->poll_time);
+ hrtimer_start(&prtd->hrt,
+ ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ break;
+ }
+ case AFE_EVENT_RTPORT_STOP:
+ pr_debug("%s: event!=0\n", __func__);
+ prtd->start = 0;
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ case AFE_EVENT_RTPORT_LOW_WM:
+ pr_debug("%s: Underrun\n", __func__);
+ break;
+ case AFE_EVENT_RTPORT_HI_WM:
+ pr_debug("%s: Overrun\n", __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
+ pr_debug("write done\n");
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static void pcm_afe_process_rx_pkt(uint32_t opcode,
+ uint32_t token, uint32_t *payload,
+ void *priv)
+{
+ struct pcm_afe_info *prtd = priv;
+ unsigned long dsp_flags;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
+ uint16_t event;
+
+ if (prtd == NULL)
+ return;
+ substream = prtd->substream;
+ runtime = substream->runtime;
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ switch (opcode) {
+ case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+ event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+ switch (event) {
+ case AFE_EVENT_RTPORT_START: {
+ prtd->dsp_cnt = 0;
+ prtd->poll_time = ((unsigned long)((
+ snd_pcm_lib_period_bytes(prtd->substream)
+ * 1000 * 1000)/(runtime->rate
+ * runtime->channels * 2)));
+ hrtimer_start(&prtd->hrt,
+ ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ pr_debug("prtd->poll_time : %d", prtd->poll_time);
+ break;
+ }
+ case AFE_EVENT_RTPORT_STOP:
+ pr_debug("%s: event!=0\n", __func__);
+ prtd->start = 0;
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ case AFE_EVENT_RTPORT_LOW_WM:
+ pr_debug("%s: Underrun\n", __func__);
+ break;
+ case AFE_EVENT_RTPORT_HI_WM:
+ pr_debug("%s: Overrun\n", __func__);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
+ pr_debug("Read done\n");
+ prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+ (prtd->substream);
+ snd_pcm_period_elapsed(prtd->substream);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static int msm_afe_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate);
+
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ ret = afe_register_get_events(dai->id,
+ pcm_afe_process_tx_pkt, prtd);
+ if (ret < 0) {
+ pr_err("afe-pcm:register for events failed\n");
+ return ret;
+ }
+ pr_debug("%s:success\n", __func__);
+ prtd->prepared++;
+ return ret;
+}
+
+static int msm_afe_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ ret = afe_register_get_events(dai->id,
+ pcm_afe_process_rx_pkt, prtd);
+ if (ret < 0) {
+ pr_err("afe-pcm:register for events failed\n");
+ return ret;
+ }
+ pr_debug("%s:success\n", __func__);
+ prtd->prepared++;
+ return 0;
+}
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 16000, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_afe_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = NULL;
+ int ret = 0;
+
+ prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ } else
+ pr_debug("prtd %x\n", (unsigned int)prtd);
+
+ mutex_init(&prtd->lock);
+ spin_lock_init(&prtd->dsp_lock);
+ prtd->dsp_cnt = 0;
+
+ mutex_lock(&prtd->lock);
+
+ runtime->hw = msm_afe_hardware;
+ prtd->substream = substream;
+ runtime->private_data = prtd;
+ mutex_unlock(&prtd->lock);
+ hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->hrt.function = afe_hrtimer_callback;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->hrt.function = afe_hrtimer_rec_callback;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+ return 0;
+}
+
+static int msm_afe_close(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct snd_dma_buffer *dma_buf;
+ struct snd_pcm_runtime *runtime;
+ struct pcm_afe_info *prtd;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ struct snd_soc_dai *dai = NULL;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (substream == NULL) {
+ pr_err("substream is NULL\n");
+ return -EINVAL;
+ }
+ rtd = substream->private_data;
+ dai = rtd->cpu_dai;
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = afe_unregister_get_events(dai->id);
+ if (ret < 0)
+ pr_err("AFE unregister for events failed\n");
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = afe_unregister_get_events(dai->id);
+ if (ret < 0)
+ pr_err("AFE unregister for events failed\n");
+ }
+ hrtimer_cancel(&prtd->hrt);
+
+ rc = afe_cmd_memory_unmap(runtime->dma_addr);
+ if (rc < 0)
+ pr_err("AFE memory unmap failed\n");
+
+ pr_debug("release all buffer\n");
+ dma_buf = &substream->dma_buffer;
+ if (dma_buf == NULL) {
+ pr_debug("dma_buf is NULL\n");
+ goto done;
+ }
+ if (dma_buf->area != NULL) {
+ dma_free_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, dma_buf->area,
+ dma_buf->addr);
+ dma_buf->area = NULL;
+ }
+done:
+ pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+ mutex_unlock(&prtd->lock);
+ prtd->prepared--;
+ kfree(prtd);
+ return 0;
+}
+static int msm_afe_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ prtd->pcm_irq_pos = 0;
+ if (prtd->prepared)
+ return 0;
+ mutex_lock(&prtd->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_afe_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_afe_capture_prepare(substream);
+ mutex_unlock(&prtd->lock);
+ return ret;
+}
+static int msm_afe_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
+ prtd->start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+ prtd->start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+static int msm_afe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct pcm_afe_info *prtd = runtime->private_data;
+ int rc;
+
+ pr_debug("%s:\n", __func__);
+
+ mutex_lock(&prtd->lock);
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = dma_alloc_coherent(dma_buf->dev.dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+
+ pr_debug("%s: dma_buf->area: 0x%p, dma_buf->addr: 0x%x", __func__,
+ (unsigned int *) dma_buf->area, dma_buf->addr);
+ if (!dma_buf->area) {
+ pr_err("%s:MSM AFE memory allocation failed\n", __func__);
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+ prtd->dma_addr = (u32) dma_buf->addr;
+
+ mutex_unlock(&prtd->lock);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ rc = afe_cmd_memory_map(dma_buf->addr, dma_buf->bytes);
+ if (rc < 0)
+ pr_err("fail to map memory to DSP\n");
+
+ return rc;
+}
+static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcm_afe_info *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream))
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static struct snd_pcm_ops msm_afe_ops = {
+ .open = msm_afe_open,
+ .hw_params = msm_afe_hw_params,
+ .trigger = msm_afe_trigger,
+ .close = msm_afe_close,
+ .prepare = msm_afe_prepare,
+ .mmap = msm_afe_mmap,
+ .pointer = msm_afe_pointer,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static int msm_afe_afe_probe(struct snd_soc_platform *platform)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_afe_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_afe_afe_probe,
+};
+
+static __devinit int msm_afe_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_afe_remove(struct platform_device *pdev)
+{
+ pr_debug("%s\n", __func__);
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_afe_driver = {
+ .driver = {
+ .name = "msm-pcm-afe",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_afe_probe,
+ .remove = __devexit_p(msm_afe_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ pr_debug("%s\n", __func__);
+ return platform_driver_register(&msm_afe_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&msm_afe_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("AFE PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
new file mode 100644
index 0000000..20d6377
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 _MSM_PCM_AFE_H
+#define _MSM_PCM_AFE_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+
+
+struct pcm_afe_info {
+ unsigned long dma_addr;
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ struct mutex lock;
+ spinlock_t dsp_lock;
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint8_t start;
+ uint32_t dsp_cnt;
+ uint32_t buf_phys;
+ int32_t mmap_flag;
+ int prepared;
+ struct hrtimer hrt;
+ int poll_time;
+};
+
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+ }
+
+#endif /*_MSM_PCM_AFE_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
new file mode 100644
index 0000000..ee92753
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -0,0 +1,609 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <sound/snd_compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+#include <sound/timer.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm lpa_audio;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .period_bytes_min = 128 * 1024,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 4,
+ .periods_max = 16,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = NULL;
+ unsigned long flag = 0;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&the_locks.event_lock, flag);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ uint32_t *ptrmem = (uint32_t *)¶m;
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ else
+ if (substream->timer_running)
+ snd_timer_interrupt(substream->timer, 1);
+
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start)) {
+ atomic_set(&prtd->pending_buffer, 1);
+ break;
+ } else
+ atomic_set(&prtd->pending_buffer, 0);
+ if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
+ break;
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+
+ buf = prtd->audio_client->port[IN].buf;
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
+ i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+ atomic_set(&prtd->pending_buffer, 0);
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2: {
+ if (!atomic_read(&prtd->pending_buffer))
+ break;
+ if (runtime->status->hw_ptr >=
+ runtime->control->appl_ptr)
+ break;
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__, prtd->pcm_count);
+ buf = prtd->audio_client->port[IN].buf;
+ param.paddr = (unsigned long)buf[prtd->out_head].phys;
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[prtd->out_head].phys;
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1)
+ & (runtime->periods - 1);
+ atomic_set(&prtd->pending_buffer, 0);
+ }
+ break;
+ case ASM_STREAM_CMD_FLUSH:
+ pr_debug("ASM_STREAM_CMD_FLUSH\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+ spin_unlock_irqrestore(&the_locks.event_lock, flag);
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ prtd->out_head = 0;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_debug("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->pcm_irq_pos = 0;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("SNDRV_PCM_TRIGGER_START\n");
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ struct asm_softpause_params softpause = {
+ .enable = SOFT_PAUSE_ENABLE,
+ .period = SOFT_PAUSE_PERIOD,
+ .step = SOFT_PAUSE_STEP,
+ .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+ };
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ runtime->hw = msm_pcm_hardware;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_debug("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EPERM;
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ atomic_set(&prtd->pending_buffer, 1);
+ runtime->private_data = prtd;
+ lpa_audio.prtd = prtd;
+ lpa_set_volume(lpa_audio.volume);
+ ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
+ if (ret < 0)
+ pr_err("%s: Send SoftPause Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+int lpa_set_volume(unsigned volume)
+{
+ int rc = 0;
+ if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
+ rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ lpa_audio.volume = volume;
+ return rc;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int rc = 0;
+
+ /*
+ If routing is still enabled, we need to issue EOS to
+ the DSP
+ To issue EOS to dsp, we need to be run state otherwise
+ EOS is not honored.
+ */
+ if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id)) {
+ rc = q6asm_run(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->pending_buffer, 0);
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ pr_debug("%s\n", __func__);
+ rc = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (rc < 0)
+ pr_err("EOS cmd timeout\n");
+ prtd->pcm_irq_pos = 0;
+ }
+
+ dir = IN;
+ atomic_set(&prtd->pending_buffer, 0);
+ lpa_audio.prtd = NULL;
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ pr_debug("%s\n", __func__);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pr_debug("%s\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ return -EPERM;
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed "
+ "rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ if (buf == NULL || buf[0].data == NULL)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ int rc = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ uint64_t timestamp;
+ uint64_t temp;
+
+ switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP: {
+ struct snd_compr_tstamp tstamp;
+ pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+
+ memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
+ timestamp = q6asm_get_session_time(prtd->audio_client);
+ if (timestamp < 0) {
+ pr_err("%s: Get Session Time return value =%lld\n",
+ __func__, timestamp);
+ return -EAGAIN;
+ }
+ temp = (timestamp * 2 * runtime->channels);
+ temp = temp * (runtime->rate/1000);
+ temp = div_u64(temp, 1000);
+ tstamp.sampling_rate = runtime->rate;
+ tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
+ tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
+ tstamp.timestamp = timestamp;
+ pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
+ "timestamp = %lld,\n",
+ __func__, tstamp.rendered, tstamp.decoded,
+ tstamp.timestamp);
+ if (copy_to_user((void *) arg, &tstamp,
+ sizeof(struct snd_compr_tstamp)))
+ return -EFAULT;
+ return 0;
+ }
+ case SNDRV_PCM_IOCTL1_RESET:
+ prtd->cmd_ack = 0;
+ rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ if (rc < 0)
+ pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
+ rc = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (rc < 0)
+ pr_err("Flush cmd timeout\n");
+ prtd->pcm_irq_pos = 0;
+ break;
+ default:
+ break;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = msm_pcm_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-lpa",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ spin_lock_init(&the_locks.event_lock);
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
new file mode 100644
index 0000000..f94e6c1
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -0,0 +1,725 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+#define PLAYBACK_NUM_PERIODS 8
+#define PLAYBACK_PERIOD_SIZE 2048
+#define CAPTURE_NUM_PERIODS 16
+#define CAPTURE_PERIOD_SIZE 512
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_PERIOD_SIZE,
+ .periods_min = CAPTURE_NUM_PERIODS,
+ .periods_max = CAPTURE_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_PERIOD_SIZE,
+ .periods_min = PLAYBACK_NUM_PERIODS,
+ .periods_max = PLAYBACK_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static uint32_t in_frame_info[CAPTURE_NUM_PERIODS][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_err("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ if (q6asm_is_cpu_buf_avail_nolock(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n");
+ pr_debug("token = 0x%08x\n", token);
+ in_frame_info[token][0] = payload[4];
+ in_frame_info[token][1] = payload[5];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag
+ && q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx))
+ q6asm_read_nolock(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ atomic_set(&prtd->start, 1);
+ break;
+ default:
+ pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = msm_pcm_hardware_playback;
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = msm_pcm_hardware_capture;
+ ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->cmd_ack = 1;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+pr_err("%s: before buf alloc\n", __func__);
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed "
+ "rc = %d\n", ret);
+ return -ENOMEM;
+ }
+pr_err("%s: after buf alloc\n", __func__);
+ buf = prtd->audio_client->port[dir].buf;
+ if (buf == NULL || buf[0].data == NULL)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
new file mode 100644
index 0000000..44395b7
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+
+
+
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+extern int copy_count;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ spinlock_t event_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+};
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct audio_client *audio_client;
+
+ uint16_t session_id;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t dsp_cnt;
+
+ int abort; /* set when error, like sample rate mismatch */
+
+ int enabled;
+ int close_ack;
+ int cmd_ack;
+ atomic_t start;
+ atomic_t out_count;
+ atomic_t in_count;
+ atomic_t out_needed;
+ int out_head;
+ int periods;
+ int mmap_flag;
+ atomic_t pending_buffer;
+};
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
new file mode 100644
index 0000000..2eebae5
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -0,0 +1,1834 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/tlv.h>
+#include "msm-pcm-routing-v2.h"
+#include "../qdsp6/q6voice.h"
+
+struct msm_pcm_routing_bdai_data {
+ u16 port_id; /* AFE port ID */
+ u8 active; /* track if this backend is enabled */
+ struct snd_pcm_hw_params *hw_params; /* to get freq and channel mode */
+ unsigned long fe_sessions; /* Front-end sessions */
+ unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
+};
+
+#define INVALID_SESSION -1
+#define SESSION_TYPE_RX 0
+#define SESSION_TYPE_TX 1
+
+static struct mutex routing_lock;
+
+static int fm_switch_enable;
+
+#define INT_FM_RX_VOL_MAX_STEPS 100
+#define INT_FM_RX_VOL_GAIN 2000
+
+static int msm_route_fm_vol_control;
+static const DECLARE_TLV_DB_SCALE(fm_rx_vol_gain, 0,
+ INT_FM_RX_VOL_MAX_STEPS, 0);
+
+#define INT_RX_VOL_MAX_STEPS 100
+#define INT_RX_VOL_GAIN 0x2000
+
+static int msm_route_lpa_vol_control;
+static const DECLARE_TLV_DB_SCALE(lpa_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS, 0);
+
+static int msm_route_multimedia2_vol_control;
+static const DECLARE_TLV_DB_SCALE(multimedia2_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS, 0);
+
+static int msm_route_compressed_vol_control;
+static const DECLARE_TLV_DB_SCALE(compressed_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS, 0);
+
+
+
+/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
+#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE
+
+enum {
+ EQ_BAND1 = 0,
+ EQ_BAND2,
+ EQ_BAND3,
+ EQ_BAND4,
+ EQ_BAND5,
+ EQ_BAND6,
+ EQ_BAND7,
+ EQ_BAND8,
+ EQ_BAND9,
+ EQ_BAND10,
+ EQ_BAND11,
+ EQ_BAND12,
+ EQ_BAND_MAX,
+};
+
+struct msm_audio_eq_band {
+ uint16_t band_idx; /* The band index, 0 .. 11 */
+ uint32_t filter_type; /* Filter band type */
+ uint32_t center_freq_hz; /* Filter band center frequency */
+ uint32_t filter_gain; /* Filter band initial gain (dB) */
+ /* Range is +12 dB to -12 dB with 1dB increments. */
+ uint32_t q_factor;
+} __packed;
+
+struct msm_audio_eq_stream_config {
+ uint32_t enable; /* Number of consequtive bands specified */
+ uint32_t num_bands;
+ struct msm_audio_eq_band eq_bands[EQ_BAND_MAX];
+} __packed;
+
+struct msm_audio_eq_stream_config eq_data[MAX_EQ_SESSIONS];
+
+static void msm_send_eq_values(int eq_idx);
+/* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
+ * If new back-end is defined, add new back-end DAI ID at the end of enum
+ */
+static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
+ { PRIMARY_I2S_RX, 0, NULL, 0, 0},
+ { PRIMARY_I2S_TX, 0, NULL, 0, 0},
+ { SLIMBUS_0_RX, 0, NULL, 0, 0},
+ { SLIMBUS_0_TX, 0, NULL, 0, 0},
+ { HDMI_RX, 0, NULL, 0, 0},
+ { INT_BT_SCO_RX, 0, NULL, 0, 0},
+ { INT_BT_SCO_TX, 0, NULL, 0, 0},
+ { INT_FM_RX, 0, NULL, 0, 0},
+ { INT_FM_TX, 0, NULL, 0, 0},
+ { RT_PROXY_PORT_001_RX, 0, NULL, 0, 0},
+ { RT_PROXY_PORT_001_TX, 0, NULL, 0, 0},
+ { PCM_RX, 0, NULL, 0, 0},
+ { PCM_TX, 0, NULL, 0, 0},
+ { VOICE_PLAYBACK_TX, 0, NULL, 0, 0},
+ { VOICE_RECORD_RX, 0, NULL, 0, 0},
+ { VOICE_RECORD_TX, 0, NULL, 0, 0},
+ { MI2S_RX, 0, NULL, 0, 0},
+ { SECONDARY_I2S_RX, 0, NULL, 0, 0},
+ { SLIMBUS_1_RX, 0, NULL, 0, 0},
+ { SLIMBUS_1_TX, 0, NULL, 0, 0},
+ { SLIMBUS_INVALID, 0, NULL, 0, 0},
+};
+
+
+/* Track ASM playback & capture sessions of DAI */
+static int fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
+ /* MULTIMEDIA1 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA2 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA3 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA4 */
+ {INVALID_SESSION, INVALID_SESSION},
+};
+
+static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
+ int path_type)
+{
+ int i, port_type;
+ struct route_payload payload;
+
+ payload.num_copps = 0;
+ port_type = (path_type == ADM_PATH_PLAYBACK ?
+ MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) &&
+ msm_bedais[i].active && (test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions)))
+ payload.copp_ids[payload.num_copps++] =
+ msm_bedais[i].port_id;
+ }
+
+ if (payload.num_copps)
+ adm_matrix_map(dspst_id, path_type,
+ payload.num_copps, payload.copp_ids, 0);
+}
+
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
+{
+ int i, session_type, path_type, port_type;
+ struct route_payload payload;
+ u32 channels;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID %d\n", __func__, fedai_id);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ payload.num_copps = 0; /* only RX needs to use payload */
+ fe_dai_map[fedai_id][session_type] = dspst_id;
+ /* re-enable EQ if active */
+ if (eq_data[fedai_id].enable)
+ msm_send_eq_values(fedai_id);
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) && msm_bedais[i].active &&
+ (test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions))) {
+
+ channels = params_channels(msm_bedais[i].hw_params);
+
+ if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[i].port_id,
+ path_type,
+ params_rate(msm_bedais[i].hw_params),
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+ else
+ adm_open(msm_bedais[i].port_id,
+ path_type,
+ params_rate(msm_bedais[i].hw_params),
+ params_channels(msm_bedais[i].hw_params),
+ DEFAULT_COPP_TOPOLOGY);
+
+ payload.copp_ids[payload.num_copps++] =
+ msm_bedais[i].port_id;
+ }
+ }
+ if (payload.num_copps)
+ adm_matrix_map(dspst_id, path_type,
+ payload.num_copps, payload.copp_ids, 0);
+
+ mutex_unlock(&routing_lock);
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+ int i, port_type, session_type;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((afe_get_port_type(msm_bedais[i].port_id) ==
+ port_type) && msm_bedais[i].active &&
+ (test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions)))
+ adm_close(msm_bedais[i].port_id);
+ }
+
+ fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
+
+ mutex_unlock(&routing_lock);
+}
+
+/* Check if FE/BE route is set */
+static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id)
+{
+ bool rc = false;
+
+ if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return rc;
+ }
+
+ if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions))
+ rc = true;
+
+ return rc;
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+ int session_type, path_type;
+ u32 channels;
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ if (val > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ }
+
+ mutex_lock(&routing_lock);
+
+ if (set) {
+ set_bit(val, &msm_bedais[reg].fe_sessions);
+ if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
+ INVALID_SESSION) {
+
+ channels = params_channels(msm_bedais[reg].hw_params);
+
+ if ((session_type == SESSION_TYPE_RX) && (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[reg].port_id,
+ path_type,
+ params_rate(msm_bedais[reg].hw_params),
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+ else
+ adm_open(msm_bedais[reg].port_id,
+ path_type,
+ params_rate(msm_bedais[reg].hw_params),
+ params_channels(msm_bedais[reg].hw_params),
+ DEFAULT_COPP_TOPOLOGY);
+
+ msm_pcm_routing_build_matrix(val,
+ fe_dai_map[val][session_type], path_type);
+ }
+ } else {
+ clear_bit(val, &msm_bedais[reg].fe_sessions);
+ if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
+ INVALID_SESSION) {
+ adm_close(msm_bedais[reg].port_id);
+ msm_pcm_routing_build_matrix(val,
+ fe_dai_map[val][session_type], path_type);
+ }
+ }
+ mutex_unlock(&routing_lock);
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_info("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+
+ if (ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->reg, mc->shift) == false) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else if (!ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->reg, mc->shift) == true) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+ pr_info("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
+{
+ return;
+}
+
+static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ mutex_lock(&routing_lock);
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ mutex_unlock(&routing_lock);
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ mutex_lock(&routing_lock);
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ mutex_unlock(&routing_lock);
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (ucontrol->value.integer.value[0]) {
+ mutex_lock(&routing_lock);
+ set_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions);
+ mutex_unlock(&routing_lock);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ mutex_lock(&routing_lock);
+ clear_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions);
+ mutex_unlock(&routing_lock);
+
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 1;
+}
+
+static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = fm_switch_enable;
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+ pr_debug("%s: FM Switch enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ else
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ fm_switch_enable = ucontrol->value.integer.value[0];
+ return 1;
+}
+
+static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].port_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
+ mc->shift, ucontrol->value.integer.value[0]);
+
+ if (ucontrol->value.integer.value[0]) {
+ afe_loopback(1, msm_bedais[mc->reg].port_id,
+ msm_bedais[mc->shift].port_id);
+ set_bit(mc->shift,
+ &msm_bedais[mc->reg].port_sessions);
+ } else {
+ afe_loopback(0, msm_bedais[mc->reg].port_id,
+ msm_bedais[mc->shift].port_id);
+ clear_bit(mc->shift,
+ &msm_bedais[mc->reg].port_sessions);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
+
+ msm_route_fm_vol_control = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!lpa_set_volume(ucontrol->value.integer.value[0]))
+ msm_route_lpa_vol_control =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+
+}
+
+static int msm_routing_get_multimedia2_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] = msm_route_multimedia2_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_multimedia2_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!multi_ch_pcm_set_volume(ucontrol->value.integer.value[0]))
+ msm_route_multimedia2_vol_control =
+ ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int msm_routing_get_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] = msm_route_compressed_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (!compressed_set_volume(ucontrol->value.integer.value[0]))
+ msm_route_compressed_vol_control =
+ ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static void msm_send_eq_values(int eq_idx)
+{
+ int result;
+ struct audio_client *ac =
+ q6asm_get_audio_client(fe_dai_map[eq_idx][SESSION_TYPE_RX]);
+
+ if (ac == NULL) {
+ pr_err("%s: Could not get audio client for session: %d\n",
+ __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX]);
+ goto done;
+ }
+
+ result = q6asm_equalizer(ac, &eq_data[eq_idx]);
+
+ if (result < 0)
+ pr_err("%s: Call to ASM equalizer failed, returned = %d\n",
+ __func__, result);
+done:
+ return;
+}
+
+static int msm_routing_get_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].enable;
+
+ pr_debug("%s: EQ #%d enable %d\n", __func__,
+ eq_idx, eq_data[eq_idx].enable);
+ return 0;
+}
+
+static int msm_routing_put_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: EQ #%d enable %d\n", __func__,
+ eq_idx, value);
+ eq_data[eq_idx].enable = value;
+
+ msm_send_eq_values(eq_idx);
+ return 0;
+}
+
+static int msm_routing_get_eq_band_count_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands;
+
+ pr_debug("%s: EQ #%d bands %d\n", __func__,
+ eq_idx, eq_data[eq_idx].num_bands);
+ return eq_data[eq_idx].num_bands;
+}
+
+static int msm_routing_put_eq_band_count_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int value = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: EQ #%d bands %d\n", __func__,
+ eq_idx, value);
+ eq_data[eq_idx].num_bands = value;
+ return 0;
+}
+
+static int msm_routing_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ eq_data[eq_idx].eq_bands[band_idx].band_idx;
+ ucontrol->value.integer.value[1] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_type;
+ ucontrol->value.integer.value[2] =
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz;
+ ucontrol->value.integer.value[3] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain;
+ ucontrol->value.integer.value[4] =
+ eq_data[eq_idx].eq_bands[band_idx].q_factor;
+
+ pr_debug("%s: band_idx = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].band_idx);
+ pr_debug("%s: filter_type = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].filter_type);
+ pr_debug("%s: center_freq_hz = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz);
+ pr_debug("%s: filter_gain = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain);
+ pr_debug("%s: q_factor = %d\n", __func__,
+ eq_data[eq_idx].eq_bands[band_idx].q_factor);
+ return 0;
+}
+
+static int msm_routing_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ eq_data[eq_idx].eq_bands[band_idx].band_idx =
+ ucontrol->value.integer.value[0];
+ eq_data[eq_idx].eq_bands[band_idx].filter_type =
+ ucontrol->value.integer.value[1];
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz =
+ ucontrol->value.integer.value[2];
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain =
+ ucontrol->value.integer.value[3];
+ eq_data[eq_idx].eq_bands[band_idx].q_factor =
+ ucontrol->value.integer.value[4];
+ return 0;
+}
+
+static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_I2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_I2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_MI2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+ /* incall music delivery mixer */
+static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_INVALID,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice",
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_Voice", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_Voice", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_Voip", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_Voip", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
+ SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_INVALID,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new auxpcm_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new fm_switch_mixer_controls =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+ 0, 1, 0, msm_routing_get_switch_mixer,
+ msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
+ INT_FM_RX_VOL_GAIN, 0, msm_routing_get_fm_vol_mixer,
+ msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
+ msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new multimedia2_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("HIFI2 RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_multimedia2_vol_mixer,
+ msm_routing_set_multimedia2_vol_mixer, multimedia2_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new compressed_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("COMPRESSED RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_compressed_vol_mixer,
+ msm_routing_set_compressed_vol_mixer, compressed_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new eq_enable_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_eq_enable_mixer,
+ msm_routing_put_eq_enable_mixer),
+ SOC_SINGLE_EXT("MultiMedia2 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_eq_enable_mixer,
+ msm_routing_put_eq_enable_mixer),
+ SOC_SINGLE_EXT("MultiMedia3 EQ Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_eq_enable_mixer,
+ msm_routing_put_eq_enable_mixer),
+};
+
+static const struct snd_kcontrol_new eq_band_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 11, 0,
+ msm_routing_get_eq_band_count_audio_mixer,
+ msm_routing_put_eq_band_count_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 11, 0,
+ msm_routing_get_eq_band_count_audio_mixer,
+ msm_routing_put_eq_band_count_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3 EQ Band Count", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 11, 0,
+ msm_routing_get_eq_band_count_audio_mixer,
+ msm_routing_put_eq_band_count_audio_mixer),
+};
+
+static const struct snd_kcontrol_new eq_coeff_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band1", EQ_BAND1,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band2", EQ_BAND2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band3", EQ_BAND3,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band4", EQ_BAND4,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band5", EQ_BAND5,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band6", EQ_BAND6,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band7", EQ_BAND7,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band8", EQ_BAND8,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band9", EQ_BAND9,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band10", EQ_BAND10,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band11", EQ_BAND11,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band12", EQ_BAND12,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+ msm_routing_get_eq_band_audio_mixer,
+ msm_routing_put_eq_band_audio_mixer),
+};
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Frontend AIF */
+ /* Widget name equals to Front-End DAI name<Need confirmation>,
+ * Stream name must contains substring of front-end dai name
+ */
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+
+ /* Backend AIF */
+ /* Stream name equals to backend dai link stream name
+ */
+ SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SEC_I2S_RX", "Secondary I2S Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
+ 0, 0, 0 , 0),
+ /* incall */
+ SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("STUB_TX", "Stub Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
+
+ /* Switch Definitions */
+ SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0,
+ &fm_switch_mixer_controls),
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ sec_i2s_rx_mixer_controls, ARRAY_SIZE(sec_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
+ /* incall */
+ SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
+ incall_music_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music_delivery_mixer_controls)),
+ /* Voice Mixer */
+ SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
+ ARRAY_SIZE(pri_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SEC_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ sec_i2s_rx_voice_mixer_controls,
+ ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ slimbus_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_voice_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ afe_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ hdmi_rx_voice_mixer_controls,
+ ARRAY_SIZE(hdmi_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
+ ARRAY_SIZE(tx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
+ ARRAY_SIZE(tx_voip_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+ tx_voice_stub_mixer_controls, ARRAY_SIZE(tx_voice_stub_mixer_controls)),
+ SND_SOC_DAPM_MIXER("STUB_RX Mixer", SND_SOC_NOPM, 0, 0,
+ stub_rx_mixer_controls, ARRAY_SIZE(stub_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_1_rx_mixer_controls, ARRAY_SIZE(slimbus_1_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ sbus_1_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_1_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_port_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_port_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
+
+ {"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SEC_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
+
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
+
+ {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+ {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+ {"HDMI Mixer", "MultiMedia3", "MM_DL3"},
+ {"HDMI Mixer", "MultiMedia4", "MM_DL4"},
+ {"HDMI", NULL, "HDMI Mixer"},
+
+ /* incall */
+ {"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
+
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
+
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+ {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
+ {"MM_UL1", NULL, "MultiMedia1 Mixer"},
+ {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MM_UL2", NULL, "MultiMedia2 Mixer"},
+
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
+
+ {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
+
+ {"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
+
+ {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
+
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
+
+ {"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
+
+ {"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
+
+ {"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"HDMI", NULL, "HDMI_RX_Voice Mixer"},
+ {"HDMI", NULL, "HDMI_DL_HL"},
+
+ {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
+ {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
+ {"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
+ {"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
+ {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
+ {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
+ {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
+ {"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
+ {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
+
+ {"VOIP_UL", NULL, "Voip_Tx Mixer"},
+ {"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"},
+ {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+ {"INT_FM_RX", NULL, "INTFM_DL_HL"},
+ {"INTFM_UL_HL", NULL, "INT_FM_TX"},
+ {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
+ {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
+
+ {"AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"AUX_PCM_RX", NULL, "AUXPCM_RX Port Mixer"},
+
+ {"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+ {"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
+
+ {"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"STUB_RX", NULL, "STUB_RX Mixer"},
+ {"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+
+ {"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"},
+ {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"},
+};
+
+static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&routing_lock);
+ msm_bedais[be_id].hw_params = params;
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, session_type;
+ struct msm_pcm_routing_bdai_data *bedai;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ bedai = &msm_bedais[be_id];
+
+ session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ 0 : 1);
+
+ mutex_lock(&routing_lock);
+
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+ if (fe_dai_map[i][session_type] != INVALID_SESSION)
+ adm_close(bedai->port_id);
+ }
+
+ bedai->active = 0;
+ bedai->hw_params = NULL;
+
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, path_type, session_type;
+ struct msm_pcm_routing_bdai_data *bedai;
+ u32 channels;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+
+ bedai = &msm_bedais[be_id];
+
+ if (bedai->hw_params == NULL) {
+ pr_err("%s: HW param is not configured", __func__);
+ return -EINVAL;
+ }
+
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ path_type = ADM_PATH_PLAYBACK;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ path_type = ADM_PATH_LIVE_REC;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ if (bedai->active == 1)
+ goto done; /* Ignore prepare if back-end already active */
+
+ /* AFE port is not active at this point. However, still
+ * go ahead setting active flag under the notion that
+ * QDSP6 is able to handle ADM starting before AFE port
+ * is started.
+ */
+ bedai->active = 1;
+
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+ if (fe_dai_map[i][session_type] != INVALID_SESSION) {
+
+ channels = params_channels(bedai->hw_params);
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (channels > 2))
+ adm_multi_ch_copp_open(bedai->port_id,
+ path_type,
+ params_rate(bedai->hw_params),
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+ else
+ adm_open(bedai->port_id,
+ path_type,
+ params_rate(bedai->hw_params),
+ params_channels(bedai->hw_params),
+ DEFAULT_COPP_TOPOLOGY);
+
+ msm_pcm_routing_build_matrix(i,
+ fe_dai_map[i][session_type], path_type);
+ }
+ }
+
+done:
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_routing_pcm_ops = {
+ .hw_params = msm_pcm_routing_hw_params,
+ .close = msm_pcm_routing_close,
+ .prepare = msm_pcm_routing_prepare,
+};
+
+static unsigned int msm_routing_read(struct snd_soc_platform *platform,
+ unsigned int reg)
+{
+ dev_dbg(platform->dev, "reg %x\n", reg);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_write(struct snd_soc_platform *platform,
+ unsigned int reg, unsigned int val)
+{
+ dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
+ ARRAY_SIZE(msm_qdsp6_widgets));
+ snd_soc_dapm_add_routes(&platform->dapm, intercon,
+ ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(&platform->dapm);
+
+ snd_soc_add_platform_controls(platform,
+ int_fm_vol_mixer_controls,
+ ARRAY_SIZE(int_fm_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ lpa_vol_mixer_controls,
+ ARRAY_SIZE(lpa_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ eq_enable_mixer_controls,
+ ARRAY_SIZE(eq_enable_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ eq_band_mixer_controls,
+ ARRAY_SIZE(eq_band_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ eq_coeff_mixer_controls,
+ ARRAY_SIZE(eq_coeff_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ multimedia2_vol_mixer_controls,
+ ARRAY_SIZE(multimedia2_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
+ compressed_vol_mixer_controls,
+ ARRAY_SIZE(compressed_vol_mixer_controls));
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+ .ops = &msm_routing_pcm_ops,
+ .probe = msm_routing_probe,
+ .read = msm_routing_read,
+ .write = msm_routing_write,
+};
+
+static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_routing_platform);
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_routing_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-routing",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_routing_pcm_probe,
+ .remove = __devexit_p(msm_routing_pcm_remove),
+};
+
+int msm_routing_check_backend_enabled(int fedai_id)
+{
+ int i;
+ if (fedai_id >= MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return 0;
+ }
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if ((test_bit(fedai_id,
+ &msm_bedais[i].fe_sessions))) {
+ return msm_bedais[i].active;
+ }
+ }
+ return 0;
+}
+
+static int __init msm_soc_routing_platform_init(void)
+{
+ mutex_init(&routing_lock);
+ return platform_driver_register(&msm_routing_pcm_driver);
+}
+module_init(msm_soc_routing_platform_init);
+
+static void __exit msm_soc_routing_platform_exit(void)
+{
+ platform_driver_unregister(&msm_routing_pcm_driver);
+}
+module_exit(msm_soc_routing_platform_exit);
+
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
new file mode 100644
index 0000000..b971787
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 _MSM_PCM_ROUTING_H
+#define _MSM_PCM_ROUTING_H
+#include <sound/apr_audio-v2.h>
+
+#define LPASS_BE_PRI_I2S_RX "(Backend) PRIMARY_I2S_RX"
+#define LPASS_BE_PRI_I2S_TX "(Backend) PRIMARY_I2S_TX"
+#define LPASS_BE_SLIMBUS_0_RX "(Backend) SLIMBUS_0_RX"
+#define LPASS_BE_SLIMBUS_0_TX "(Backend) SLIMBUS_0_TX"
+#define LPASS_BE_HDMI "(Backend) HDMI"
+#define LPASS_BE_INT_BT_SCO_RX "(Backend) INT_BT_SCO_RX"
+#define LPASS_BE_INT_BT_SCO_TX "(Backend) INT_BT_SCO_TX"
+#define LPASS_BE_INT_FM_RX "(Backend) INT_FM_RX"
+#define LPASS_BE_INT_FM_TX "(Backend) INT_FM_TX"
+#define LPASS_BE_AFE_PCM_RX "(Backend) RT_PROXY_DAI_001_RX"
+#define LPASS_BE_AFE_PCM_TX "(Backend) RT_PROXY_DAI_002_TX"
+#define LPASS_BE_AUXPCM_RX "(Backend) AUX_PCM_RX"
+#define LPASS_BE_AUXPCM_TX "(Backend) AUX_PCM_TX"
+#define LPASS_BE_VOICE_PLAYBACK_TX "(Backend) VOICE_PLAYBACK_TX"
+#define LPASS_BE_INCALL_RECORD_RX "(Backend) INCALL_RECORD_TX"
+#define LPASS_BE_INCALL_RECORD_TX "(Backend) INCALL_RECORD_RX"
+#define LPASS_BE_SEC_I2S_RX "(Backend) SECONDARY_I2S_RX"
+
+#define LPASS_BE_MI2S_RX "(Backend) MI2S_RX"
+#define LPASS_BE_STUB_RX "(Backend) STUB_RX"
+#define LPASS_BE_STUB_TX "(Backend) STUB_TX"
+#define LPASS_BE_SLIMBUS_1_RX "(Backend) SLIMBUS_1_RX"
+#define LPASS_BE_SLIMBUS_1_TX "(Backend) SLIMBUS_1_TX"
+
+/* For multimedia front-ends, asm session is allocated dynamically.
+ * Hence, asm session/multimedia front-end mapping has to be maintained.
+ * Due to this reason, additional multimedia front-end must be placed before
+ * non-multimedia front-ends.
+ */
+
+enum {
+ MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4,
+ MSM_FRONTEND_DAI_CS_VOICE,
+ MSM_FRONTEND_DAI_VOIP,
+ MSM_FRONTEND_DAI_AFE_RX,
+ MSM_FRONTEND_DAI_AFE_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB,
+ MSM_FRONTEND_DAI_MAX,
+};
+
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA4 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA4
+
+enum {
+ MSM_BACKEND_DAI_PRI_I2S_RX = 0,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_BACKEND_DAI_INVALID,
+ MSM_BACKEND_DAI_MAX,
+};
+
+/* dai_id: front-end ID,
+ * dspst_id: DSP audio stream ID
+ * stream_type: playback or capture
+ */
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
+ int stream_type);
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+int lpa_set_volume(unsigned volume);
+
+int msm_routing_check_backend_enabled(int fedai_id);
+
+int multi_ch_pcm_set_volume(unsigned volume);
+
+int compressed_set_volume(unsigned volume);
+
+#endif /*_MSM_PCM_H*/