Merge "sync: use correct signed type when handling SYNC_IOC_WAIT"
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
index 93b5144..d930799 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
@@ -113,8 +113,14 @@
14 = 1.37
15 = 1.28
16 = 1.20
-- qcom,init-head-room: Voltage head room in uV required for the
- regulator
+- qcom,init-head-room: Voltage head room in mV required for the
+ regulator. This head room value should be used
+ in situations where the device connected to the
+ output of the regulator has low noise tolerance.
+ Note that the RPM independently enforces a
+ safety head room value for subregulated LDOs
+ which is sufficient to account for LDO drop-out
+ voltage.
- qcom,init-quiet-mode: Specify that quiet mode is needed for an SMPS
regulator in order to have lower output noise.
Supported values are:
diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
index 9cf57fd..7637adc 100644
--- a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
@@ -8,6 +8,7 @@
- startup-delay-us: startup time in microseconds
- enable-active-high: Polarity of GPIO is Active high
If this property is missing, the default assumed is Active low.
+- parent-supply: phandle to the parent supply/regulator node if one exists.
Any property defined as part of the core regulator
binding, defined in regulator.txt, can also be used.
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
new file mode 100644
index 0000000..3534823
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
@@ -0,0 +1,70 @@
+* Qualcomm MSM HSUART
+
+Required properties:
+- compatible :
+ - "qcom,msm-hsuart-v14" to be used for UARTDM Core v1.4
+- reg : offset and length of the register set for both the device,
+ uart core and bam core
+- reg-names : names of the uart core and bam core.
+- interrupts : should contain the uart interrupt.
+- interrupt-names : names of interrupts to be used.
+- bam-tx-ep-pipe-index : BAM TX Endpoint Pipe Index for HSUART
+- bam-rx-ep-pipe-index : BAM RX Endpoint Pipe Index for HSUART
+
+BLSP has a static pipe allocation and assumes a pair-pipe for each uart core.
+Pipes [2*i : 2*i+1] are allocated for UART cores where i = [0 : 5].
+Hence, Minimum and Maximum permitted value of endpoint pipe index to be used
+with uart core is 0 and 11 respectively.
+
+There is one HSUART block used in MSM devices,
+"qcom,msm-hsuart-v14". The msm-serial-hs driver is
+able to handle this, and matches against the "qcom,msm-hsuart-v14"
+as the compatibility.
+
+The registers for the "qcom,msm-hsuart-v14" device need to specify both
+register blocks - uart core and bam core.
+
+Example:
+
+ uart7@f995d000 {
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0xf995d000 0x1000>,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+ };
+
+Optional properties:
+- qcom,<gpio-name>-gpio : handle to the GPIO node, see "gpios property" in
+Documentation/devicetree/bindings/gpio/gpio.txt.
+"gpio-name" can be "tx", "rx", "cts" and "rfr" based on number of UART GPIOs
+need to configured.
+Gpio's are optional if it is required to be not configured by UART driver or
+case where there is nothing connected and we want to use internal loopback mode
+for uart.
+- qcom, wakeup_irq : UART RX GPIO IRQ line to be configured as wakeup source.
+- qcom,inject_rx_on_wakeup : inject_rx_on_wakeup enables feature where on
+receiving interrupt with UART RX GPIO IRQ line (i.e. above wakeup_irq property),
+HSUART driver injects provided character with property rx_to_inject.
+- qcom, rx_to_inject : The character to be inserted on wakeup.
+
+
+Example:
+
+ uart7: uart@f995d000 {
+ compatible = "qcom,msm-hsuart-v14"
+ reg = <0x19c40000 0x1000">,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ qcom,tx-gpio = <&msmgpio 41 0x00>;
+ qcom,rx-gpio = <&msmgpio 42 0x00>;
+ qcom,cts-gpio = <&msmgpio 43 0x00>;
+ qcom,rfr-gpio = <&msmgpio 44 0x00>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ };
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 5e65ca4..3b3402d 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -180,6 +180,15 @@
qcom,model = "msm8974-taiko-cdp-snd-card";
qcom,hdmi-audio-rx;
};
+
+ usb2_otg_sw: regulator-tpd4s214 {
+ compatible = "regulator-fixed";
+ regulator-name = "usb2_otg_sw";
+ gpio = <&pm8941_gpios 18 0>;
+ parent-supply = <&pm8941_boost>;
+ startup-delay-us = <17000>;
+ enable-active-high;
+ };
};
&spmi_bus {
@@ -374,6 +383,14 @@
};
gpio@d100 { /* GPIO 18 */
+ /* usb2_otg_sw regulator enable */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,invert = <0>; /* Output low initially */
+ qcom,vin-sel = <2>; /* PM8941 S3 = 1.8 V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,out-strength = <2>; /* Medium drive strength */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@d200 { /* GPIO 19 */
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index f464a55..4e9f707 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -253,7 +253,6 @@
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -262,6 +261,8 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_MSM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index e54459d..5d37dd0 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -258,7 +258,6 @@
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -267,6 +266,8 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_MSM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
index d2905d4..cc50955 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
@@ -17,15 +17,34 @@
#include<linux/serial_core.h>
-/* Optional platform device data for msm_serial_hs driver.
- * Used to configure low power wakeup */
+/**
+ * struct msm_serial_hs_platform_data - platform device data
+ * for msm hsuart device
+ * @wakeup_irq : IRQ line to be configured as Wakeup source.
+ * @inject_rx_on_wakeup : Set 1 if specific character to be inserted on wakeup
+ * @rx_to_inject : Character to be inserted on wakeup
+ * @gpio_config : Configure gpios that are used for uart communication
+ * @userid : User-defined number to be used to enumerate device as tty<userid>
+ * @uart_tx_gpio: GPIO number for UART Tx Line.
+ * @uart_rx_gpio: GPIO number for UART Rx Line.
+ * @uart_cts_gpio: GPIO number for UART CTS Line.
+ * @uart_rfr_gpio: GPIO number for UART RFR Line.
+ * @bam_tx_ep_pipe_index : BAM TX Endpoint Pipe Index for HSUART
+ * @bam_tx_ep_pipe_index : BAM RX Endpoint Pipe Index for HSUART
+ */
struct msm_serial_hs_platform_data {
int wakeup_irq; /* wakeup irq */
- /* bool: inject char into rx tty on wakeup */
unsigned char inject_rx_on_wakeup;
char rx_to_inject;
int (*gpio_config)(int);
int userid;
+
+ unsigned uart_tx_gpio;
+ unsigned uart_rx_gpio;
+ unsigned uart_cts_gpio;
+ unsigned uart_rfr_gpio;
+ unsigned bam_tx_ep_pipe_index;
+ unsigned bam_rx_ep_pipe_index;
};
unsigned int msm_hs_tx_empty(struct uart_port *uport);
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index fde43b0..cdacd87 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2288,13 +2288,19 @@
RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
msg.srv.service, msg.srv.instance,
msg.srv.node_id, msg.srv.port_id);
- } else if (port_ptr->type == CLIENT_PORT) {
- msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
- msg.cli.node_id = port_ptr->this_port.node_id;
- msg.cli.port_id = port_ptr->this_port.port_id;
- RR("x REMOVE_CLIENT id=%d:%08x\n",
- msg.cli.node_id, msg.cli.port_id);
+ broadcast_ctl_msg(&msg);
+ broadcast_ctl_msg_locally(&msg);
}
+
+ /*
+ * Server port could have been a client port earlier.
+ * Send REMOVE_CLIENT message in either case.
+ */
+ msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.node_id = port_ptr->this_port.node_id;
+ msg.cli.port_id = port_ptr->this_port.port_id;
+ RR("x REMOVE_CLIENT id=%d:%08x\n",
+ msg.cli.node_id, msg.cli.port_id);
broadcast_ctl_msg(&msg);
broadcast_ctl_msg_locally(&msg);
} else if (port_ptr->type == CONTROL_PORT) {
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 6bdedd7..83ef121 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -83,6 +83,17 @@
If unsure, say N.
+config SATA_AHCI_MSM
+ tristate "Qualcomm MSM AHCI SATA support"
+ depends on ARCH_MSM
+ select SATA_AHCI_PLATFORM
+ help
+ This option enables support for AHCI SATA controller
+ integrated into Qualcomm MSM chipsets. For more
+ information please refer to http://www.qualcomm.com/chipsets.
+
+ If unsure, say N.
+
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6ece5b7..bc40152 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -9,6 +9,7 @@
obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
+obj-$(CONFIG_SATA_AHCI_MSM) += ahci_msm.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_msm.c b/drivers/ata/ahci_msm.c
new file mode 100644
index 0000000..8536040
--- /dev/null
+++ b/drivers/ata/ahci_msm.c
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * SATA init module.
+ * To be used with SATA interface on MSM targets.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ahci_platform.h>
+#include <mach/clk.h>
+
+/* PHY registers */
+#define UNIPHY_PLL_REFCLK_CFG 0x000
+#define UNIPHY_PLL_POSTDIV1_CFG 0x004
+#define UNIPHY_PLL_CHGPUMP_CFG 0x008
+#define UNIPHY_PLL_VCOLPF_CFG 0x00C
+#define UNIPHY_PLL_VREG_CFG 0x010
+#define UNIPHY_PLL_PWRGEN_CFG 0x014
+#define UNIPHY_PLL_DMUX_CFG 0x018
+#define UNIPHY_PLL_AMUX_CFG 0x01C
+#define UNIPHY_PLL_GLB_CFG 0x020
+#define UNIPHY_PLL_POSTDIV2_CFG 0x024
+#define UNIPHY_PLL_POSTDIV3_CFG 0x028
+#define UNIPHY_PLL_LPFR_CFG 0x02C
+#define UNIPHY_PLL_LPFC1_CFG 0x030
+#define UNIPHY_PLL_LPFC2_CFG 0x034
+#define UNIPHY_PLL_SDM_CFG0 0x038
+#define UNIPHY_PLL_SDM_CFG1 0x03C
+#define UNIPHY_PLL_SDM_CFG2 0x040
+#define UNIPHY_PLL_SDM_CFG3 0x044
+#define UNIPHY_PLL_SDM_CFG4 0x048
+#define UNIPHY_PLL_SSC_CFG0 0x04C
+#define UNIPHY_PLL_SSC_CFG1 0x050
+#define UNIPHY_PLL_SSC_CFG2 0x054
+#define UNIPHY_PLL_SSC_CFG3 0x058
+#define UNIPHY_PLL_LKDET_CFG0 0x05C
+#define UNIPHY_PLL_LKDET_CFG1 0x060
+#define UNIPHY_PLL_LKDET_CFG2 0x064
+#define UNIPHY_PLL_TEST_CFG 0x068
+#define UNIPHY_PLL_CAL_CFG0 0x06C
+#define UNIPHY_PLL_CAL_CFG1 0x070
+#define UNIPHY_PLL_CAL_CFG2 0x074
+#define UNIPHY_PLL_CAL_CFG3 0x078
+#define UNIPHY_PLL_CAL_CFG4 0x07C
+#define UNIPHY_PLL_CAL_CFG5 0x080
+#define UNIPHY_PLL_CAL_CFG6 0x084
+#define UNIPHY_PLL_CAL_CFG7 0x088
+#define UNIPHY_PLL_CAL_CFG8 0x08C
+#define UNIPHY_PLL_CAL_CFG9 0x090
+#define UNIPHY_PLL_CAL_CFG10 0x094
+#define UNIPHY_PLL_CAL_CFG11 0x098
+#define UNIPHY_PLL_EFUSE_CFG 0x09C
+#define UNIPHY_PLL_DEBUG_BUS_SEL 0x0A0
+#define UNIPHY_PLL_CTRL_42 0x0A4
+#define UNIPHY_PLL_CTRL_43 0x0A8
+#define UNIPHY_PLL_CTRL_44 0x0AC
+#define UNIPHY_PLL_CTRL_45 0x0B0
+#define UNIPHY_PLL_CTRL_46 0x0B4
+#define UNIPHY_PLL_CTRL_47 0x0B8
+#define UNIPHY_PLL_CTRL_48 0x0BC
+#define UNIPHY_PLL_STATUS 0x0C0
+#define UNIPHY_PLL_DEBUG_BUS0 0x0C4
+#define UNIPHY_PLL_DEBUG_BUS1 0x0C8
+#define UNIPHY_PLL_DEBUG_BUS2 0x0CC
+#define UNIPHY_PLL_DEBUG_BUS3 0x0D0
+#define UNIPHY_PLL_CTRL_54 0x0D4
+
+#define SATA_PHY_SER_CTRL 0x100
+#define SATA_PHY_TX_DRIV_CTRL0 0x104
+#define SATA_PHY_TX_DRIV_CTRL1 0x108
+#define SATA_PHY_TX_DRIV_CTRL2 0x10C
+#define SATA_PHY_TX_DRIV_CTRL3 0x110
+#define SATA_PHY_TX_RESV0 0x114
+#define SATA_PHY_TX_RESV1 0x118
+#define SATA_PHY_TX_IMCAL0 0x11C
+#define SATA_PHY_TX_IMCAL1 0x120
+#define SATA_PHY_TX_IMCAL2 0x124
+#define SATA_PHY_RX_IMCAL0 0x128
+#define SATA_PHY_RX_IMCAL1 0x12C
+#define SATA_PHY_RX_IMCAL2 0x130
+#define SATA_PHY_RX_TERM 0x134
+#define SATA_PHY_RX_TERM_RESV 0x138
+#define SATA_PHY_EQUAL 0x13C
+#define SATA_PHY_EQUAL_RESV 0x140
+#define SATA_PHY_OOB_TERM 0x144
+#define SATA_PHY_CDR_CTRL0 0x148
+#define SATA_PHY_CDR_CTRL1 0x14C
+#define SATA_PHY_CDR_CTRL2 0x150
+#define SATA_PHY_CDR_CTRL3 0x154
+#define SATA_PHY_CDR_CTRL4 0x158
+#define SATA_PHY_FA_LOAD0 0x15C
+#define SATA_PHY_FA_LOAD1 0x160
+#define SATA_PHY_CDR_CTRL_RESV 0x164
+#define SATA_PHY_PI_CTRL0 0x168
+#define SATA_PHY_PI_CTRL1 0x16C
+#define SATA_PHY_DESER_RESV 0x170
+#define SATA_PHY_RX_RESV0 0x174
+#define SATA_PHY_AD_TPA_CTRL 0x178
+#define SATA_PHY_REFCLK_CTRL 0x17C
+#define SATA_PHY_POW_DWN_CTRL0 0x180
+#define SATA_PHY_POW_DWN_CTRL1 0x184
+#define SATA_PHY_TX_DATA_CTRL 0x188
+#define SATA_PHY_BIST_GEN0 0x18C
+#define SATA_PHY_BIST_GEN1 0x190
+#define SATA_PHY_BIST_GEN2 0x194
+#define SATA_PHY_BIST_GEN3 0x198
+#define SATA_PHY_LBK_CTRL 0x19C
+#define SATA_PHY_TEST_DEBUG_CTRL 0x1A0
+#define SATA_PHY_ALIGNP 0x1A4
+#define SATA_PHY_PRBS_CFG0 0x1A8
+#define SATA_PHY_PRBS_CFG1 0x1AC
+#define SATA_PHY_PRBS_CFG2 0x1B0
+#define SATA_PHY_PRBS_CFG3 0x1B4
+#define SATA_PHY_CHAN_COMP_CHK_CNT 0x1B8
+#define SATA_PHY_RESET_CTRL 0x1BC
+#define SATA_PHY_RX_CLR 0x1C0
+#define SATA_PHY_RX_EBUF_CTRL 0x1C4
+#define SATA_PHY_ID0 0x1C8
+#define SATA_PHY_ID1 0x1CC
+#define SATA_PHY_ID2 0x1D0
+#define SATA_PHY_ID3 0x1D4
+#define SATA_PHY_RX_CHK_ERR_CNT0 0x1D8
+#define SATA_PHY_RX_CHK_ERR_CNT1 0x1DC
+#define SATA_PHY_RX_CHK_STAT 0x1E0
+#define SATA_PHY_TX_IMCAL_STAT 0x1E4
+#define SATA_PHY_RX_IMCAL_STAT 0x1E8
+#define SATA_PHY_RX_EBUF_STAT 0x1EC
+#define SATA_PHY_DEBUG_BUS_STAT0 0x1F0
+#define SATA_PHY_DEBUG_BUS_STAT1 0x1F4
+#define SATA_PHY_DEBUG_BUS_STAT2 0x1F8
+#define SATA_PHY_DEBUG_BUS_STAT3 0x1FC
+
+#define AHCI_HOST_CAP 0x00
+#define AHCI_HOST_CAP_MASK 0x1F
+#define AHCI_HOST_CAP_PMP (1 << 17)
+
+struct msm_sata_hba {
+ struct platform_device *ahci_pdev;
+ struct clk *slave_iface_clk;
+ struct clk *bus_clk;
+ struct clk *iface_clk;
+ struct clk *src_clk;
+ struct clk *rxoob_clk;
+ struct clk *pmalive_clk;
+ struct clk *cfg_clk;
+ struct regulator *clk_pwr;
+ struct regulator *pmp_pwr;
+ void __iomem *phy_base;
+ void __iomem *ahci_base;
+};
+
+static inline void msm_sata_delay_us(unsigned int delay)
+{
+ /* sleep for max. 50us more to combine processor wakeups */
+ usleep_range(delay, delay + 50);
+}
+
+static int msm_sata_clk_get_prepare_enable_set_rate(struct device *dev,
+ const char *name, struct clk **out_clk, int rate)
+{
+ int ret = 0;
+ struct clk *clk;
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "failed to get clk: %s err = %d\n", name, ret);
+ goto out;
+ }
+
+ if (rate >= 0) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_err(dev, "failed to set rate: %d clk: %s err = %d\n",
+ rate, name, ret);
+ goto out;
+ }
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ dev_err(dev, "failed to enable clk: %s err = %d\n", name, ret);
+out:
+ if (!ret)
+ *out_clk = clk;
+
+ return ret;
+}
+
+static int msm_sata_clk_get_prepare_enable(struct device *dev,
+ const char *name, struct clk **out_clk)
+{
+ return msm_sata_clk_get_prepare_enable_set_rate(dev, name, out_clk, -1);
+}
+
+static void msm_sata_clk_put_unprepare_disable(struct clk **clk)
+{
+ if (*clk) {
+ clk_disable_unprepare(*clk);
+ clk_put(*clk);
+ *clk = NULL;
+ }
+}
+
+static int msm_sata_hard_reset(struct device *dev)
+{
+ int ret;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ ret = clk_reset(hba->iface_clk, CLK_RESET_ASSERT);
+ if (ret) {
+ dev_err(dev, "iface_clk assert failed %d\n", ret);
+ goto out;
+ }
+
+ ret = clk_reset(hba->iface_clk, CLK_RESET_DEASSERT);
+ if (ret) {
+ dev_err(dev, "iface_clk de-assert failed %d\n", ret);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static int msm_sata_clk_init(struct device *dev)
+{
+ int ret = 0;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Enable AHB clock for system fabric slave port connected to SATA */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "slave_iface_clk", &hba->slave_iface_clk);
+ if (ret)
+ goto out;
+
+ /* Enable AHB clock for system fabric and SATA core interface */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "iface_clk", &hba->iface_clk);
+ if (ret)
+ goto put_dis_slave_iface_clk;
+
+ /* Enable AXI clock for SATA AXI master and slave interfaces */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "bus_clk", &hba->bus_clk);
+ if (ret)
+ goto put_dis_iface_clk;
+
+ /* Enable the source clock for pmalive, rxoob and phy ref clocks */
+ ret = msm_sata_clk_get_prepare_enable_set_rate(dev,
+ "src_clk", &hba->src_clk, 100000000);
+ if (ret)
+ goto put_dis_bus_clk;
+
+ /*
+ * Enable RX OOB detection clock. The clock rate is
+ * same as PHY reference clock (100MHz).
+ */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "core_rxoob_clk", &hba->rxoob_clk);
+ if (ret)
+ goto put_dis_src_clk;
+
+ /*
+ * Enable power management always-on clock. The clock rate
+ * is same as PHY reference clock (100MHz).
+ */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "core_pmalive_clk", &hba->pmalive_clk);
+ if (ret)
+ goto put_dis_rxoob_clk;
+
+ /* Enable PHY configuration AHB clock, fixed 64MHz clock */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "cfg_clk", &hba->cfg_clk);
+ if (ret)
+ goto put_dis_pmalive_clk;
+
+ return ret;
+
+put_dis_pmalive_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk);
+put_dis_rxoob_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk);
+put_dis_src_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->src_clk);
+put_dis_bus_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->bus_clk);
+put_dis_iface_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->iface_clk);
+put_dis_slave_iface_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk);
+out:
+ return ret;
+}
+
+static void msm_sata_clk_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ msm_sata_clk_put_unprepare_disable(&hba->cfg_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->src_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->bus_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->iface_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk);
+}
+
+static int msm_sata_vreg_get_enable_set_vdd(struct device *dev,
+ const char *name, struct regulator **out_vreg,
+ int min_uV, int max_uV, int hpm_uA)
+{
+ int ret = 0;
+ struct regulator *vreg;
+
+ vreg = devm_regulator_get(dev, name);
+ if (IS_ERR(vreg)) {
+ ret = PTR_ERR(vreg);
+ dev_err(dev, "Regulator: %s get failed, err=%d\n", name, ret);
+ goto out;
+ }
+
+ if (regulator_count_voltages(vreg) > 0) {
+ ret = regulator_set_voltage(vreg, min_uV, max_uV);
+ if (ret) {
+ dev_err(dev, "Regulator: %s set voltage failed, err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(vreg, hpm_uA);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set optimum mode(uA_load=%d) failed, err=%d\n",
+ name, hpm_uA, ret);
+ goto err;
+ } else {
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ }
+ }
+
+ ret = regulator_enable(vreg);
+ if (ret)
+ dev_err(dev, "Regulator: %s enable failed, err=%d\n",
+ name, ret);
+err:
+ if (!ret)
+ *out_vreg = vreg;
+ else
+ devm_regulator_put(vreg);
+out:
+ return ret;
+}
+
+static int msm_sata_vreg_put_disable(struct device *dev,
+ struct regulator *reg, const char *name, int max_uV)
+{
+ int ret;
+
+ if (!reg)
+ return 0;
+
+ ret = regulator_disable(reg);
+ if (ret) {
+ dev_err(dev, "Regulator: %s disable failed err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ if (regulator_count_voltages(reg) > 0) {
+ ret = regulator_set_voltage(reg, 0, max_uV);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set voltage to 0 failed, err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(reg, 0);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set optimum mode(uA_load = 0) failed, err=%d\n",
+ name, ret);
+ goto err;
+ } else {
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ }
+ }
+
+err:
+ devm_regulator_put(reg);
+ return ret;
+}
+
+static int msm_sata_vreg_init(struct device *dev)
+{
+ int ret = 0;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /*
+ * The SATA clock generator needs 3.3V supply and can consume
+ * max. 850mA during functional mode.
+ */
+ ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_ext_3p3v",
+ &hba->clk_pwr, 3300000, 3300000, 850000);
+ if (ret)
+ goto out;
+
+ /* Add 1ms regulator ramp-up delay */
+ msm_sata_delay_us(1000);
+
+ /* Read AHCI capability register to check if PMP is supported.*/
+ if (readl_relaxed(hba->ahci_base +
+ AHCI_HOST_CAP) & AHCI_HOST_CAP_PMP) {
+ /* Power up port-multiplier */
+ ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_pmp_pwr",
+ &hba->pmp_pwr, 1800000, 1800000, 200000);
+ if (ret) {
+ msm_sata_vreg_put_disable(dev, hba->clk_pwr,
+ "sata_ext_3p3v", 3300000);
+ goto out;
+ }
+
+ /* Add 1ms regulator ramp-up delay */
+ msm_sata_delay_us(1000);
+ }
+
+out:
+ return ret;
+}
+
+static void msm_sata_vreg_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ msm_sata_vreg_put_disable(dev, hba->clk_pwr,
+ "sata_ext_3p3v", 3300000);
+
+ if (hba->pmp_pwr)
+ msm_sata_vreg_put_disable(dev, hba->pmp_pwr,
+ "sata_pmp_pwr", 1800000);
+}
+
+static void msm_sata_phy_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Power down PHY */
+ writel_relaxed(0xF8, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0xFE, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+
+ /* Power down PLL block */
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+
+ devm_iounmap(dev, hba->phy_base);
+}
+
+static int msm_sata_phy_init(struct device *dev)
+{
+ int ret = 0;
+ u32 reg = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+ struct resource *mem;
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
+ if (!mem) {
+ dev_err(dev, "no mmio space\n");
+ return -EINVAL;
+ }
+
+ hba->phy_base = devm_ioremap(dev, mem->start, resource_size(mem));
+ if (!hba->phy_base) {
+ dev_err(dev, "failed to allocate memory for SATA PHY\n");
+ return -ENOMEM;
+ }
+
+ /* SATA phy initialization */
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_SER_CTRL);
+
+ writel_relaxed(0xB1, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ mb();
+ msm_sata_delay_us(10);
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0);
+ writel_relaxed(0x02, hba->phy_base + SATA_PHY_TX_IMCAL2);
+
+ /* Write UNIPHYPLL registers to configure PLL */
+ writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_REFCLK_CFG);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_PWRGEN_CFG);
+
+ writel_relaxed(0x0A, hba->phy_base + UNIPHY_PLL_CAL_CFG0);
+ writel_relaxed(0xF3, hba->phy_base + UNIPHY_PLL_CAL_CFG8);
+ writel_relaxed(0x01, hba->phy_base + UNIPHY_PLL_CAL_CFG9);
+ writel_relaxed(0xED, hba->phy_base + UNIPHY_PLL_CAL_CFG10);
+ writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_CAL_CFG11);
+
+ writel_relaxed(0x36, hba->phy_base + UNIPHY_PLL_SDM_CFG0);
+ writel_relaxed(0x0D, hba->phy_base + UNIPHY_PLL_SDM_CFG1);
+ writel_relaxed(0xA3, hba->phy_base + UNIPHY_PLL_SDM_CFG2);
+ writel_relaxed(0xF0, hba->phy_base + UNIPHY_PLL_SDM_CFG3);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SDM_CFG4);
+
+ writel_relaxed(0x19, hba->phy_base + UNIPHY_PLL_SSC_CFG0);
+ writel_relaxed(0xE1, hba->phy_base + UNIPHY_PLL_SSC_CFG1);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SSC_CFG2);
+ writel_relaxed(0x11, hba->phy_base + UNIPHY_PLL_SSC_CFG3);
+
+ writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_LKDET_CFG0);
+ writel_relaxed(0xFF, hba->phy_base + UNIPHY_PLL_LKDET_CFG1);
+
+ writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+ msm_sata_delay_us(40);
+
+ writel_relaxed(0x03, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+ msm_sata_delay_us(400);
+
+ writel_relaxed(0x05, hba->phy_base + UNIPHY_PLL_LKDET_CFG2);
+ mb();
+
+ /* poll for ready status, timeout after 1 sec */
+ ret = readl_poll_timeout(hba->phy_base + UNIPHY_PLL_STATUS, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout UNIPHY_PLL_STATUS\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(hba->phy_base + SATA_PHY_TX_IMCAL_STAT, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(hba->phy_base + SATA_PHY_RX_IMCAL_STAT, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
+ goto out;
+ }
+
+ /* SATA phy calibrated succesfully, power up to functional mode */
+ writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0);
+
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x59, hba->phy_base + SATA_PHY_CDR_CTRL0);
+ writel_relaxed(0x04, hba->phy_base + SATA_PHY_CDR_CTRL1);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL2);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_PI_CTRL0);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL3);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+
+ writel_relaxed(0x11, hba->phy_base + SATA_PHY_TX_DATA_CTRL);
+ writel_relaxed(0x43, hba->phy_base + SATA_PHY_ALIGNP);
+ writel_relaxed(0x04, hba->phy_base + SATA_PHY_OOB_TERM);
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_EQUAL);
+ writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL0);
+ writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL1);
+ mb();
+
+ dev_dbg(dev, "SATA PHY powered up in functional mode\n");
+
+out:
+ /* power down PHY in case of failure */
+ if (ret)
+ msm_sata_phy_deinit(dev);
+
+ return ret;
+}
+
+int msm_sata_init(struct device *ahci_dev, void __iomem *mmio)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Save ahci mmio to access vendor specific registers */
+ hba->ahci_base = mmio;
+
+ ret = msm_sata_clk_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA clk init failed with err=%d\n", ret);
+ goto out;
+ }
+
+ ret = msm_sata_vreg_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA vreg init failed with err=%d\n", ret);
+ msm_sata_clk_deinit(dev);
+ goto out;
+ }
+
+ ret = msm_sata_phy_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ msm_sata_vreg_deinit(dev);
+ msm_sata_clk_deinit(dev);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+void msm_sata_deinit(struct device *ahci_dev)
+{
+ struct device *dev = ahci_dev->parent;
+
+ msm_sata_phy_deinit(dev);
+ msm_sata_vreg_deinit(dev);
+ msm_sata_clk_deinit(dev);
+}
+
+static int msm_sata_suspend(struct device *ahci_dev)
+{
+ msm_sata_deinit(ahci_dev);
+
+ return 0;
+}
+
+static int msm_sata_resume(struct device *ahci_dev)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+
+ ret = msm_sata_clk_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA clk init failed with err=%d\n", ret);
+ /*
+ * If clock initialization failed, that means ahci driver
+ * cannot access any register going further. Since there is
+ * no check within ahci driver to check for clock failures,
+ * panic here instead of making an unclocked register access.
+ */
+ BUG();
+ }
+
+ /* Issue asynchronous reset to reset PHY */
+ ret = msm_sata_hard_reset(dev);
+ if (ret)
+ goto out;
+
+ ret = msm_sata_vreg_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA vreg init failed with err=%d\n", ret);
+ /* Do not turn off clks, AHCI driver might do register access */
+ goto out;
+ }
+
+ ret = msm_sata_phy_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ /* Do not turn off clks, AHCI driver might do register access */
+ msm_sata_vreg_deinit(dev);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static struct ahci_platform_data msm_ahci_pdata = {
+ .init = msm_sata_init,
+ .exit = msm_sata_deinit,
+ .suspend = msm_sata_suspend,
+ .resume = msm_sata_resume,
+};
+
+static int __devinit msm_sata_probe(struct platform_device *pdev)
+{
+ struct platform_device *ahci;
+ struct msm_sata_hba *hba;
+ int ret = 0;
+
+ hba = devm_kzalloc(&pdev->dev, sizeof(struct msm_sata_hba), GFP_KERNEL);
+ if (!hba) {
+ dev_err(&pdev->dev, "no memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, hba);
+
+ ahci = platform_device_alloc("ahci", pdev->id);
+ if (!ahci) {
+ dev_err(&pdev->dev, "couldn't allocate ahci device\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ dma_set_coherent_mask(&ahci->dev, pdev->dev.coherent_dma_mask);
+
+ ahci->dev.parent = &pdev->dev;
+ ahci->dev.dma_mask = pdev->dev.dma_mask;
+ ahci->dev.dma_parms = pdev->dev.dma_parms;
+ hba->ahci_pdev = ahci;
+
+ ret = platform_device_add_resources(ahci, pdev->resource,
+ pdev->num_resources);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't add resources to ahci device\n");
+ goto err_put_device;
+ }
+
+ ahci->dev.platform_data = &msm_ahci_pdata;
+ ret = platform_device_add(ahci);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ahci device\n");
+ goto err_put_device;
+ }
+
+ return 0;
+
+err_put_device:
+ platform_device_put(ahci);
+err_free:
+ devm_kfree(&pdev->dev, hba);
+err:
+ return ret;
+}
+
+static int __devexit msm_sata_remove(struct platform_device *pdev)
+{
+ struct msm_sata_hba *hba = platform_get_drvdata(pdev);
+
+ platform_device_unregister(hba->ahci_pdev);
+
+ return 0;
+}
+
+static struct platform_driver msm_sata_driver = {
+ .probe = msm_sata_probe,
+ .remove = __devexit_p(msm_sata_remove),
+ .driver = {
+ .name = "msm_sata",
+ },
+};
+
+module_platform_driver(msm_sata_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("AHCI platform MSM Glue Layer");
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index ced7a80..0f82142 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#include <linux/pm_runtime.h>
#include "ahci.h"
enum ahci_type {
@@ -192,6 +193,14 @@
if (rc)
goto err0;
+ rc = pm_runtime_set_active(dev);
+ if (rc) {
+ dev_warn(dev, "Unable to set runtime pm active err=%d\n", rc);
+ } else {
+ pm_runtime_enable(dev);
+ pm_runtime_forbid(dev);
+ }
+
return 0;
err0:
if (pdata && pdata->exit)
@@ -275,6 +284,8 @@
static struct dev_pm_ops ahci_pm_ops = {
.suspend = &ahci_suspend,
.resume = &ahci_resume,
+ .runtime_suspend = &ahci_suspend,
+ .runtime_resume = &ahci_resume,
};
#endif
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index bc129da..cc021c0 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -334,7 +334,7 @@
{
int ret;
- if (wcd9xxx->reset_gpio) {
+ if (wcd9xxx->reset_gpio && wcd9xxx->slim_device_bootup) {
ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET");
if (ret) {
pr_err("%s: Failed to request gpio %d\n", __func__,
@@ -342,7 +342,8 @@
wcd9xxx->reset_gpio = 0;
return ret;
}
-
+ }
+ if (wcd9xxx->reset_gpio) {
gpio_direction_output(wcd9xxx->reset_gpio, 0);
msleep(20);
gpio_direction_output(wcd9xxx->reset_gpio, 1);
@@ -1259,6 +1260,7 @@
wcd9xxx->reset_gpio = pdata->reset_gpio;
wcd9xxx->dev = &slim->dev;
wcd9xxx->mclk_rate = pdata->mclk_rate;
+ wcd9xxx->slim_device_bootup = true;
ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
if (ret)
@@ -1378,6 +1380,29 @@
return ret;
}
+static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+
+ if (wcd9xxx->slim_device_bootup) {
+ wcd9xxx->slim_device_bootup = false;
+ return 0;
+ }
+ ret = wcd9xxx_reset(wcd9xxx);
+ if (ret)
+ pr_err("%s: Resetting Codec failed\n", __func__);
+
+ wcd9xxx_bring_up(wcd9xxx);
+ wcd9xxx->post_reset(wcd9xxx);
+ return ret;
+}
+
+static int wcd9xxx_slim_device_up(struct slim_device *sldev)
+{
+ struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+ return wcd9xxx_device_up(wcd9xxx);
+}
+
static int wcd9xxx_slim_resume(struct slim_device *sldev)
{
struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
@@ -1533,6 +1558,7 @@
.id_table = taiko_slimtest_id,
.resume = wcd9xxx_slim_resume,
.suspend = wcd9xxx_slim_suspend,
+ .device_up = wcd9xxx_slim_device_up,
};
static const struct slim_device_id tapan_slimtest_id[] = {
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index f4fc7b6..3d3563a 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2277,12 +2277,16 @@
ret = qsee_vote_for_clock(data, CLK_DFAB);
if (ret)
pr_err("Failed to vote for DFAB clock%d\n", ret);
+ ret = qsee_vote_for_clock(data, CLK_SFPB);
+ if (ret)
+ pr_err("Failed to vote for SFPB clock%d\n", ret);
atomic_dec(&data->ioctl_count);
break;
}
case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
atomic_inc(&data->ioctl_count);
qsee_disable_clock_vote(data, CLK_DFAB);
+ qsee_disable_clock_vote(data, CLK_SFPB);
atomic_dec(&data->ioctl_count);
break;
}
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index a09e5ee..5485e27 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -4210,7 +4210,7 @@
return rc;
}
/* Check if die 3.0.1 is present */
- if (subrev == 0x1)
+ if (subrev & 0x1)
pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xA4);
else
pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xAC);
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index 7e74eca..d0410a4 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -104,6 +104,9 @@
if (of_find_property(np, "enable-active-high", NULL))
config->enable_high = true;
+ if (of_find_property(np, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
return config;
}
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 9c69f47..d4fb02b 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1409,7 +1409,7 @@
err_request_irq_failed:
kthread_stop(dev->rx_msgq_thread);
err_thread_create_failed:
- msm_slim_sps_exit(dev);
+ msm_slim_sps_exit(dev, true);
err_sps_init_failed:
if (dev->hclk) {
clk_disable_unprepare(dev->hclk);
@@ -1453,7 +1453,7 @@
clk_put(dev->rclk);
if (dev->hclk)
clk_put(dev->hclk);
- msm_slim_sps_exit(dev);
+ msm_slim_sps_exit(dev, true);
kthread_stop(dev->rx_msgq_thread);
iounmap(dev->bam.base);
iounmap(dev->base);
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 78e8a6f..a37a7803 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -33,6 +33,7 @@
#define NGD_SLIM_NAME "ngd_msm_ctrl"
#define SLIM_LA_MGR 0xFF
#define SLIM_ROOT_FREQ 24576000
+#define LADDR_RETRY 5
#define NGD_BASE_V1(r) (((r) % 2) ? 0x800 : 0xA00)
#define NGD_BASE_V2(r) (((r) % 2) ? 0x1000 : 0x2000)
@@ -78,6 +79,12 @@
NGD_TX_BUSY = 0x0,
};
+enum ngd_status {
+ NGD_LADDR = 1 << 1,
+};
+
+static int ngd_slim_runtime_resume(struct device *device);
+
static irqreturn_t ngd_slim_interrupt(int irq, void *d)
{
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
@@ -144,23 +151,24 @@
return IRQ_HANDLED;
}
-static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
-{
- struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
- return msm_slim_qmi_power_request(dev, true);
-}
-
static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
void *_cmd)
{
struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
pr_info("Slimbus QMI NGD CB received event:%ld", code);
switch (code) {
case QMI_SERVER_ARRIVE:
- complete(&qmi->qmi_comp);
+ schedule_work(&qmi->ssr_up);
break;
case QMI_SERVER_EXIT:
- /* SSR implementation */
+ dev->state = MSM_CTRL_DOWN;
+ /* make sure autosuspend is not called until ADSP comes up*/
+ pm_runtime_get_noresume(dev->dev);
+ /* Reset ctrl_up completion */
+ init_completion(&dev->ctrl_up);
+ schedule_work(&qmi->ssr_down);
break;
default:
break;
@@ -227,22 +235,43 @@
txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
return 0;
}
- msm_slim_get_ctrl(dev);
+ /* If txn is tried when controller is down, wait for ADSP to boot */
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE) {
+ if (dev->state == MSM_CTRL_DOWN) {
+ u8 mc = (u8)txn->mc;
+ int timeout;
+ dev_err(dev->dev, "ADSP slimbus not up yet");
+ /*
+ * Messages related to data channel management can't
+ * wait since they are holding reconfiguration lock.
+ * clk_pause in resume (which can change state back to
+ * MSM_CTRL_AWAKE), will need that lock
+ */
+ if ((txn->mt == SLIM_MSG_MT_CORE) &&
+ ((mc >= SLIM_MSG_MC_CONNECT_SOURCE &&
+ mc <= SLIM_MSG_MC_CHANGE_CONTENT) ||
+ (mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+ mc <= SLIM_MSG_MC_RECONFIGURE_NOW)))
+ return -EREMOTEIO;
+ if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
+ ((mc >= SLIM_USR_MC_DEFINE_CHAN &&
+ mc <= SLIM_USR_MC_DISCONNECT_PORT)))
+ return -EREMOTEIO;
+ timeout = wait_for_completion_timeout(&dev->ctrl_up,
+ HZ);
+ if (!timeout)
+ return -ETIMEDOUT;
+ }
+ msm_slim_get_ctrl(dev);
+ }
mutex_lock(&dev->tx_lock);
+
if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
- (dev->state == MSM_CTRL_ASLEEP ||
- dev->state == MSM_CTRL_SLEEPING)) {
- int timeout;
+ (dev->state != MSM_CTRL_AWAKE)) {
dev_err(dev->dev, "controller not ready");
mutex_unlock(&dev->tx_lock);
- /* Reconf is signalled when master responds */
- timeout = wait_for_completion_timeout(&dev->reconf, HZ);
- if (timeout) {
- mutex_lock(&dev->tx_lock);
- } else {
- msm_slim_put_ctrl(dev);
- return -EBUSY;
- }
+ msm_slim_put_ctrl(dev);
+ return -EREMOTEIO;
}
if (txn->mt == SLIM_MSG_MT_CORE &&
(txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
@@ -370,24 +399,23 @@
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
timeout = wait_for_completion_timeout(txn->comp, HZ);
- if (!timeout) {
- pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
- txn->tid);
+ if (!timeout)
ret = -ETIMEDOUT;
+ else
+ ret = txn->ec;
+ if (ret) {
+ pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
+ txn->tid, ret);
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
- } else {
- ret = txn->ec;
}
- if (ret)
- pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
- txn->tid, ret);
return ret ? ret : dev->err;
}
ngd_xfer_err:
mutex_unlock(&dev->tx_lock);
- msm_slim_put_ctrl(dev);
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE)
+ msm_slim_put_ctrl(dev);
return ret ? ret : dev->err;
}
@@ -398,20 +426,18 @@
if (!ret) {
int timeout;
timeout = wait_for_completion_timeout(txn->comp, HZ);
- if (!timeout) {
- pr_err("master req:0x%x, tid:%d timed out", txn->mc,
- txn->tid);
+ if (!timeout)
ret = -ETIMEDOUT;
- mutex_lock(&ctrl->m_ctrl);
- ctrl->txnt[txn->tid] = NULL;
- mutex_unlock(&ctrl->m_ctrl);
- } else {
+ else
ret = txn->ec;
- }
}
- if (ret)
+ if (ret) {
pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
txn->tid, ret);
+ mutex_lock(&ctrl->m_ctrl);
+ ctrl->txnt[txn->tid] = NULL;
+ mutex_unlock(&ctrl->m_ctrl);
+ }
return ret;
}
@@ -582,26 +608,31 @@
txn.wbuf = wbuf;
txn.len = 4;
pr_info("SLIM SAT: Received master capability");
- dev->use_rx_msgqs = 1;
- msm_slim_sps_init(dev, dev->bam_mem,
- NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
- if (dev->use_rx_msgqs)
- msgq_en |= NGD_CFG_RX_MSGQ_EN;
- writel_relaxed(msgq_en, dev->base +
- NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD MSG-Q config goes through */
- mb();
+ if (dev->state == MSM_CTRL_DOWN) {
+ dev->use_rx_msgqs = 1;
+ msm_slim_sps_init(dev, dev->bam_mem,
+ NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS,
+ true);
+ if (dev->use_rx_msgqs)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ writel_relaxed(msgq_en, dev->base +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD MSG-Q config goes through */
+ mb();
+ }
ret = ngd_xfer_msg(&dev->ctrl, &txn);
if (!ret) {
+ enum msm_ctrl_state prev_state = dev->state;
dev->state = MSM_CTRL_AWAKE;
-
- pm_runtime_use_autosuspend(dev->dev);
- pm_runtime_set_autosuspend_delay(dev->dev,
- MSM_SLIM_AUTOSUSPEND);
- pm_runtime_set_active(dev->dev);
- pm_runtime_enable(dev->dev);
- complete(&dev->reconf);
+ if (prev_state >= MSM_CTRL_ASLEEP)
+ complete(&dev->reconf);
+ else
+ pr_err("SLIM: unexpected capability, state:%d",
+ prev_state);
+ /* ADSP SSR, send device_up notifications */
+ if (prev_state == MSM_CTRL_DOWN)
+ schedule_work(&dev->slave_notify);
}
}
if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
@@ -654,38 +685,116 @@
}
}
-static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev)
{
+ void __iomem *ngd;
+ int timeout, ret;
+ enum msm_ctrl_state cur_state = dev->state;
+ u32 laddr;
+ u32 msgq_en = 1;
u32 ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
- if (enable) {
- int ret = msm_slim_qmi_init(dev, false);
- if (ret)
- return ret;
- ret = msm_slim_qmi_power_request(dev, true);
- if (ret)
- return ret;
- writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
- NGD_BASE(dev->ctrl.nr, dev->ver));
+
+ if (cur_state == MSM_CTRL_DOWN) {
+ int timeout = wait_for_completion_timeout(&dev->qmi.qmi_comp,
+ HZ);
+ if (!timeout)
+ pr_err("slimbus QMI init timed out");
+ }
+
+ ret = msm_slim_qmi_power_request(dev, true);
+ if (ret) {
+ pr_err("SLIM QMI power request failed:%d", ret);
+ return ret;
+ }
+ if (!dev->ver) {
+ dev->ver = readl_relaxed(dev->base);
+ /* Version info in 16 MSbits */
+ dev->ver >>= 16;
+ }
+ ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ laddr = readl_relaxed(ngd + NGD_STATUS);
+ if (laddr & NGD_LADDR) {
/*
- * Enable NGD. Configure NGD in register acc. mode until master
- * announcement is received
+ * ADSP power collapse case, where HW wasn't reset.
+ * Reconnect BAM pipes if disconnected
*/
- writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD enabling goes through */
- mb();
- } else {
- writel_relaxed(0, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- writel_relaxed(0, dev->base + NGD_INT_EN +
+ return 0;
+ } else if (cur_state != MSM_CTRL_DOWN) {
+ pr_info("ADSP P.C. CTRL state:%d NGD not enumerated:0x%x",
+ dev->state, laddr);
+ if (dev->use_rx_msgqs)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ }
+
+ /*
+ * ADSP power collapse case (OR SSR), where HW was reset
+ * BAM programming will happen when capability message is received
+ */
+ writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD disabling goes through */
- mb();
+ /*
+ * Enable NGD. Configure NGD in register acc. mode until master
+ * announcement is received
+ */
+ writel_relaxed(msgq_en, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD enabling goes through */
+ mb();
+
+ timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+ if (!timeout) {
+ pr_err("failed to received master capability");
+ return -ETIMEDOUT;
+ }
+ if (cur_state == MSM_CTRL_DOWN)
+ complete(&dev->ctrl_up);
+ return 0;
+}
+
+static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+{
+ int ret = 0;
+ if (enable) {
+ ret = msm_slim_qmi_init(dev, false);
+ /* controller state should be in sync with framework state */
+ if (!ret) {
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false,
+ SLIM_CLK_UNSPECIFIED);
+ complete(&dev->qmi.qmi_comp);
+ /*
+ * Power-up won't be called if clock pause failed.
+ * This can happen if ADSP SSR happened when audio
+ * session is in progress. Framework will think that
+ * clock pause failed so no need to wakeup controller.
+ * Call power-up explicitly in that case, since slimbus
+ * HW needs to be powered-on to be in sync with
+ * framework state
+ */
+ if (ret)
+ ngd_slim_power_up(dev);
+ if (!pm_runtime_enabled(dev->dev) ||
+ !pm_runtime_suspended(dev->dev))
+ ngd_slim_runtime_resume(dev->dev);
+ else
+ pm_runtime_resume(dev->dev);
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put(dev->dev);
+ } else
+ dev_err(dev->dev, "qmi init fail, ret:%d, state:%d",
+ ret, dev->state);
+ } else {
msm_slim_qmi_exit(dev);
}
- return 0;
+ return ret;
+}
+
+static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ return ngd_slim_power_up(dev);
}
static int ngd_slim_rx_msgq_thread(void *data)
@@ -698,14 +807,6 @@
u32 buffer[10];
u8 msg_len = 0;
- wait_for_completion_interruptible(&dev->qmi.qmi_comp);
- ret = ngd_slim_enable(dev, true);
- /* Exit the thread if component can't be enabled */
- if (ret) {
- pr_err("Enabling NGD failed:%d", ret);
- return 0;
- }
-
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
ret = wait_for_completion_interruptible(notify);
@@ -741,6 +842,64 @@
return 0;
}
+static void ngd_laddr_lookup(struct work_struct *work)
+{
+ struct msm_slim_ctrl *dev =
+ container_of(work, struct msm_slim_ctrl, slave_notify);
+ struct slim_controller *ctrl = &dev->ctrl;
+ struct slim_device *sbdev;
+ int i;
+ mutex_lock(&ctrl->m_ctrl);
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list) {
+ int ret = 0;
+ mutex_unlock(&ctrl->m_ctrl);
+ for (i = 0; i < LADDR_RETRY; i++) {
+ ret = slim_get_logical_addr(sbdev, sbdev->e_addr,
+ 6, &sbdev->laddr);
+ if (!ret)
+ break;
+ else /* time for ADSP to assign LA */
+ msleep(20);
+ }
+ mutex_lock(&ctrl->m_ctrl);
+ }
+ mutex_unlock(&ctrl->m_ctrl);
+}
+
+static void ngd_adsp_down(struct work_struct *work)
+{
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, ssr_down);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ struct slim_controller *ctrl = &dev->ctrl;
+ struct slim_device *sbdev;
+ int i;
+
+ ngd_slim_enable(dev, false);
+ /* disconnect BAM pipes */
+ msm_slim_sps_exit(dev, false);
+ dev->use_rx_msgqs = 0;
+ mutex_lock(&ctrl->m_ctrl);
+ /* device up should be called again after SSR */
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list)
+ sbdev->notified = false;
+ /* invalidate logical addresses */
+ for (i = 0; i < ctrl->num_dev; i++)
+ ctrl->addrt[i].valid = false;
+ mutex_unlock(&ctrl->m_ctrl);
+ pr_info("SLIM ADSP SSR (DOWN) done");
+}
+
+static void ngd_adsp_up(struct work_struct *work)
+{
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, ssr_up);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ ngd_slim_enable(dev, true);
+}
+
static int __devinit ngd_slim_probe(struct platform_device *pdev)
{
struct msm_slim_ctrl *dev;
@@ -834,15 +993,13 @@
dev->bam_mem = bam_mem;
init_completion(&dev->reconf);
+ init_completion(&dev->ctrl_up);
mutex_init(&dev->tx_lock);
spin_lock_init(&dev->rx_lock);
dev->ee = 1;
dev->irq = irq->start;
dev->bam.irq = bam_irq->start;
- dev->ver = readl_relaxed(dev->base);
- /* Version info in 16 MSbits */
- dev->ver >>= 16;
init_completion(&dev->rx_msgq_notify);
/* Register with framework */
@@ -854,7 +1011,7 @@
dev->ctrl.dev.parent = &pdev->dev;
dev->ctrl.dev.of_node = pdev->dev.of_node;
- dev->state = MSM_CTRL_ASLEEP;
+ dev->state = MSM_CTRL_DOWN;
ret = request_irq(dev->irq, ngd_slim_interrupt,
IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
@@ -865,7 +1022,16 @@
}
init_completion(&dev->qmi.qmi_comp);
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_autosuspend_delay(dev->dev, MSM_SLIM_AUTOSUSPEND);
+ pm_runtime_set_suspended(dev->dev);
+ pm_runtime_enable(dev->dev);
+
+ INIT_WORK(&dev->slave_notify, ngd_laddr_lookup);
+ INIT_WORK(&dev->qmi.ssr_down, ngd_adsp_down);
+ INIT_WORK(&dev->qmi.ssr_up, ngd_adsp_up);
dev->qmi.nb.notifier_call = ngd_qmi_available;
+ pm_runtime_get_noresume(dev->dev);
ret = qmi_svc_event_notifier_register(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
if (ret) {
@@ -873,6 +1039,7 @@
goto qmi_register_failed;
}
+
/* Fire up the Rx message queue thread */
dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
NGD_SLIM_NAME "_ngd_msgq_thread");
@@ -914,7 +1081,6 @@
qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
pm_runtime_disable(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
free_irq(dev->irq, dev);
slim_del_controller(&dev->ctrl);
kthread_stop(dev->rx_msgq_thread);
@@ -938,31 +1104,12 @@
* functions to be called from system suspend/resume. So they are not
* inside ifdef CONFIG_PM_RUNTIME
*/
-#ifdef CONFIG_PM_SLEEP
-static int ngd_slim_runtime_suspend(struct device *device)
-{
- struct platform_device *pdev = to_platform_device(device);
- struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
- int ret;
- dev_dbg(device, "pm_runtime: suspending...\n");
- dev->state = MSM_CTRL_SLEEPING;
- ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
- if (ret) {
- dev_err(device, "clk pause not entered:%d", ret);
- dev->state = MSM_CTRL_AWAKE;
- } else {
- dev->state = MSM_CTRL_ASLEEP;
- }
- return ret;
-}
-
static int ngd_slim_runtime_resume(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
int ret = 0;
- dev_dbg(device, "pm_runtime: resuming...\n");
- if (dev->state == MSM_CTRL_ASLEEP)
+ if (dev->state >= MSM_CTRL_ASLEEP)
ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
if (ret) {
dev_err(device, "clk pause not exited:%d", ret);
@@ -973,6 +1120,24 @@
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret = 0;
+ dev->state = MSM_CTRL_SLEEPING;
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+ if (ret) {
+ if (ret != -EBUSY)
+ dev_err(device, "clk pause not entered:%d", ret);
+ dev->state = MSM_CTRL_AWAKE;
+ } else {
+ dev->state = MSM_CTRL_ASLEEP;
+ }
+ return ret;
+}
+
static int ngd_slim_suspend(struct device *dev)
{
int ret = -EBUSY;
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 137f85e..72a8669 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -517,6 +517,8 @@
},
};
+ if (dev->bam.hdl)
+ goto init_rx_msgq;
bam_props.ee = dev->ee;
bam_props.virt_addr = dev->bam.base;
bam_props.phys_addr = bam_mem->start;
@@ -565,7 +567,7 @@
return ret;
}
-void msm_slim_sps_exit(struct msm_slim_ctrl *dev)
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
{
if (dev->use_rx_msgqs) {
struct msm_slim_endp *endpoint = &dev->rx_msgq;
@@ -579,7 +581,10 @@
sps_disconnect(endpoint->sps);
msm_slim_sps_mem_free(dev, descr);
msm_slim_free_endpoint(endpoint);
+ }
+ if (dereg) {
sps_deregister_bam_device(dev->bam.hdl);
+ dev->bam.hdl = 0L;
}
}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 3daf7ee..1c6db32 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -152,6 +152,7 @@
MSM_CTRL_AWAKE,
MSM_CTRL_SLEEPING,
MSM_CTRL_ASLEEP,
+ MSM_CTRL_DOWN,
};
struct msm_slim_sps_bam {
@@ -176,6 +177,8 @@
struct kthread_worker kworker;
struct completion qmi_comp;
struct notifier_block nb;
+ struct work_struct ssr_down;
+ struct work_struct ssr_up;
};
struct msm_slim_ctrl {
@@ -212,8 +215,10 @@
bool reconf_busy;
bool chan_active;
enum msm_ctrl_state state;
+ struct completion ctrl_up;
int nsats;
u32 ver;
+ struct work_struct slave_notify;
struct msm_slim_qmi qmi;
};
@@ -266,7 +271,7 @@
int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
u32 pipe_reg, bool remote);
-void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg);
void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index c320e46..e5b3158 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3047,6 +3047,7 @@
for (i = 0; i < ctrl->last_tid; i++) {
if (ctrl->txnt[i]) {
ret = -EBUSY;
+ pr_info("slim_clk_pause: txn-rsp for %d pending", i);
mutex_unlock(&ctrl->m_ctrl);
return -EBUSY;
}
@@ -3057,6 +3058,7 @@
mutex_lock(&ctrl->sched.m_reconf);
/* Data channels active */
if (ctrl->sched.usedslots) {
+ pr_info("slim_clk_pause: data channel active");
ret = -EBUSY;
goto clk_pause_ret;
}
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 2d73af6..8ff6cff 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -54,18 +54,34 @@
#include <linux/device.h>
#include <linux/wakelock.h>
#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/dma.h>
+#include <mach/sps.h>
#include <mach/msm_serial_hs.h>
#include "msm_serial_hs_hwreg.h"
+#define UART_SPS_CONS_PERIPHERAL 0
+#define UART_SPS_PROD_PERIPHERAL 1
static int hs_serial_debug_mask = 1;
module_param_named(debug_mask, hs_serial_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
+/*
+ * There are 3 different kind of UART Core available on MSM.
+ * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
+ * and BSLP based HSUART.
+ */
+enum uart_core_type {
+ LEGACY_HSUART,
+ GSBI_HSUART,
+ BLSP_HSUART,
+};
enum flush_reason {
FLUSH_NONE,
@@ -92,6 +108,17 @@
CLK_REQ_OFF_RXSTALE_FLUSHED,
};
+/* SPS data structures to support HSUART with BAM
+ * @sps_pipe - This struct defines BAM pipe descriptor
+ * @sps_connect - This struct defines a connection's end point
+ * @sps_register - This struct defines a event registration parameters
+ */
+struct msm_hs_sps_ep_conn_data {
+ struct sps_pipe *pipe_handle;
+ struct sps_connect config;
+ struct sps_register_event event;
+};
+
struct msm_hs_tx {
unsigned int tx_ready_int_en; /* ok to dma more tx */
unsigned int dma_in_flight; /* tx dma in progress */
@@ -105,6 +132,7 @@
int tx_count;
dma_addr_t dma_base;
struct tasklet_struct tlet;
+ struct msm_hs_sps_ep_conn_data cons;
};
struct msm_hs_rx {
@@ -122,6 +150,7 @@
struct wake_lock wake_lock;
struct delayed_work flip_insert_work;
struct tasklet_struct tlet;
+ struct msm_hs_sps_ep_conn_data prod;
};
enum buffer_states {
@@ -168,7 +197,20 @@
struct work_struct clock_off_w; /* work for actual clock off */
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
+ struct work_struct reset_bam_rx; /* work for reset bam rx endpoint */
+ struct work_struct disconnect_rx_endpoint; /* disconnect rx_endpoint */
bool tty_flush_receive;
+ enum uart_core_type uart_type;
+ u32 bam_handle;
+ resource_size_t bam_mem;
+ int bam_irq;
+ unsigned char __iomem *bam_base;
+ unsigned int bam_tx_ep_pipe_index;
+ unsigned int bam_rx_ep_pipe_index;
+ /* struct sps_event_notify is an argument passed when triggering a
+ * callback event object registered for an SPS connection end point.
+ */
+ struct sps_event_notify notify;
};
#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
@@ -176,12 +218,17 @@
#define UARTDM_RX_BUF_SIZE 512
#define RETRY_TIMEOUT 5
#define UARTDM_NR 256
+#define BAM_PIPE_MIN 0
+#define BAM_PIPE_MAX 11
static struct dentry *debug_base;
static struct msm_hs_port q_uart_port[UARTDM_NR];
static struct platform_driver msm_serial_hs_platform_driver;
static struct uart_driver msm_hs_driver;
static struct uart_ops msm_hs_ops;
+static void msm_hs_start_rx_locked(struct uart_port *uport);
+static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr);
+static void flip_insert_work(struct work_struct *work);
#define UARTDM_TO_MSM(uart_port) \
container_of((uart_port), struct msm_hs_port, uport)
@@ -244,7 +291,10 @@
/* assume gsbi uart if gsbi resource found in pdata */
return ((msm_uport->mapped_gsbi != NULL));
}
-
+static unsigned int is_blsp_uart(struct msm_hs_port *msm_uport)
+{
+ return (msm_uport->uart_type == BLSP_HSUART);
+}
static inline unsigned int msm_hs_read(struct uart_port *uport,
unsigned int offset)
{
@@ -320,13 +370,21 @@
if (val) {
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
- ret |= UARTDM_MR2_LOOP_MODE_BMSK;
+ if (is_blsp_uart(msm_uport))
+ ret |= (UARTDM_MR2_LOOP_MODE_BMSK |
+ UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+ else
+ ret |= UARTDM_MR2_LOOP_MODE_BMSK;
msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
spin_unlock_irqrestore(&uport->lock, flags);
} else {
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
- ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
+ if (is_blsp_uart(msm_uport))
+ ret &= ~(UARTDM_MR2_LOOP_MODE_BMSK |
+ UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+ else
+ ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
spin_unlock_irqrestore(&uport->lock, flags);
}
@@ -470,6 +528,89 @@
return 0;
}
+
+/* Connect a UART peripheral's SPS endpoint(consumer endpoint)
+ *
+ * Also registers a SPS callback function for the consumer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_tx(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
+ struct sps_connect *sps_config = &tx->cons.config;
+ struct sps_register_event *sps_event = &tx->cons.event;
+
+ /* Establish connection between peripheral and memory endpoint */
+ ret = sps_connect(sps_pipe_handle, sps_config);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for tx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ return ret;
+ }
+ /* Register callback event for EOT (End of transfer) event. */
+ ret = sps_register_event(sps_pipe_handle, sps_event);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for tx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ goto reg_event_err;
+ }
+ return 0;
+
+reg_event_err:
+ sps_disconnect(sps_pipe_handle);
+ return ret;
+}
+
+/* Connect a UART peripheral's SPS endpoint(producer endpoint)
+ *
+ * Also registers a SPS callback function for the producer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_rx(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+ struct sps_connect *sps_config = &rx->prod.config;
+ struct sps_register_event *sps_event = &rx->prod.event;
+
+ /* Establish connection between peripheral and memory endpoint */
+ ret = sps_connect(sps_pipe_handle, sps_config);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for rx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ return ret;
+ }
+ /* Register callback event for EOT (End of transfer) event. */
+ ret = sps_register_event(sps_pipe_handle, sps_event);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for rx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ goto reg_event_err;
+ }
+ return 0;
+
+reg_event_err:
+ sps_disconnect(sps_pipe_handle);
+ return ret;
+}
+
/*
* programs the UARTDM_CSR register with correct bit rates
*
@@ -479,9 +620,8 @@
* Goal is to have around 8 ms before indicate stale.
* roundup (((Bit Rate * .008) / 10) + 1
*/
-static unsigned long msm_hs_set_bps_locked(struct uart_port *uport,
- unsigned int bps,
- unsigned long flags)
+static void msm_hs_set_bps_locked(struct uart_port *uport,
+ unsigned int bps)
{
unsigned long rxstale;
unsigned long data;
@@ -581,15 +721,11 @@
uport->uartclk = 7372800;
}
- spin_unlock_irqrestore(&uport->lock, flags);
if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
WARN_ON(1);
- spin_lock_irqsave(&uport->lock, flags);
- return flags;
}
- spin_lock_irqsave(&uport->lock, flags);
data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
@@ -601,7 +737,6 @@
*/
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
- return flags;
}
@@ -654,6 +789,23 @@
msm_hs_write(uport, UARTDM_IPR_ADDR, data);
}
+
+/* Reset BAM RX Endpoint Pipe Index from workqueue context*/
+
+static void hsuart_reset_bam_rx_work(struct work_struct *w)
+{
+ struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+ reset_bam_rx);
+ struct uart_port *uport = &msm_uport->uport;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+
+ sps_disconnect(sps_pipe_handle);
+ msm_hs_spsconnect_rx(uport);
+
+ msm_serial_hs_rx_tlet((unsigned long) &rx->tlet);
+}
+
/*
* termios : new ktermios
* oldtermios: old ktermios previous setting
@@ -666,12 +818,12 @@
{
unsigned int bps;
unsigned long data;
- unsigned long flags;
unsigned int c_cflag = termios->c_cflag;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
mutex_lock(&msm_uport->clk_mutex);
- spin_lock_irqsave(&uport->lock, flags);
/*
* Disable Rx channel of UARTDM
@@ -683,7 +835,13 @@
* suggested to do disable/reset or reset/disable at the same time.
*/
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Disable UARTDM RX BAM Interface */
+ data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ data &= ~UARTDM_RX_DM_EN_BMSK;
+ }
+
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* 300 is the minimum baud support by the driver */
@@ -697,7 +855,7 @@
if (!uport->uartclk)
msm_hs_set_std_bps_locked(uport, bps);
else
- flags = msm_hs_set_bps_locked(uport, bps, flags);
+ msm_hs_set_bps_locked(uport, bps);
data = msm_hs_read(uport, UARTDM_MR2_ADDR);
data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
@@ -775,13 +933,18 @@
* dsb requires here.
*/
mb();
- /* do discard flush */
- msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ if (is_blsp_uart(msm_uport)) {
+ sps_disconnect(sps_pipe_handle);
+ msm_hs_spsconnect_rx(uport);
+ msm_serial_hs_rx_tlet((unsigned long) &rx->tlet);
+ } else {
+ /* do discard flush */
+ msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ }
}
msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
mb();
- spin_unlock_irqrestore(&uport->lock, flags);
mutex_unlock(&msm_uport->clk_mutex);
}
@@ -814,6 +977,20 @@
msm_uport->tx.tx_ready_int_en = 0;
}
+/* Disconnect BAM RX Endpoint Pipe Index from workqueue context*/
+static void hsuart_disconnect_rx_endpoint_work(struct work_struct *w)
+{
+ struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+ disconnect_rx_endpoint);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+
+ sps_disconnect(sps_pipe_handle);
+ wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);
+ msm_uport->rx.flush = FLUSH_SHUTDOWN;
+ wake_up(&msm_uport->rx.wait);
+}
+
/*
* Standard API, Stop receiver as soon as possible.
*
@@ -829,7 +1006,10 @@
/* disable dlink */
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport))
+ data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+ else
+ data &= ~UARTDM_RX_DM_EN_BMSK;
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* calling DMOV or CLOCK API. Hence mb() */
@@ -837,10 +1017,17 @@
/* Disable the receiver */
if (msm_uport->rx.flush == FLUSH_NONE) {
wake_lock(&msm_uport->rx.wake_lock);
- /* do discard flush */
- msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->rx.flush = FLUSH_STOP;
+ /* workqueue for BAM rx endpoint disconnect */
+ queue_work(msm_uport->hsuart_wq,
+ &msm_uport->disconnect_rx_endpoint);
+ } else {
+ /* do discard flush */
+ msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ }
}
- if (msm_uport->rx.flush != FLUSH_SHUTDOWN)
+ if (!is_blsp_uart(msm_uport) && msm_uport->rx.flush != FLUSH_SHUTDOWN)
msm_uport->rx.flush = FLUSH_STOP;
}
@@ -852,9 +1039,11 @@
int aligned_tx_count;
dma_addr_t src_addr;
dma_addr_t aligned_src_addr;
+ u32 flags = SPS_IOVEC_FLAG_EOT;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
struct msm_hs_tx *tx = &msm_uport->tx;
struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
+ struct sps_pipe *sps_pipe_handle;
if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
msm_hs_stop_tx_locked(uport);
@@ -882,14 +1071,21 @@
dma_sync_single_for_device(uport->dev, aligned_src_addr,
aligned_tx_count, DMA_TO_DEVICE);
- tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) |
- ((tx_count + 15) >> 4);
- tx->command_ptr->src_row_addr = src_addr;
+ if (is_blsp_uart(msm_uport)) {
+ /* Issue TX BAM Start IFC command */
+ msm_hs_write(uport, UARTDM_CR_ADDR, START_TX_BAM_IFC);
+ } else {
+ tx->command_ptr->num_rows =
+ (((tx_count + 15) >> 4) << 16) |
+ ((tx_count + 15) >> 4);
+ tx->command_ptr->src_row_addr = src_addr;
- dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
- *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+ *tx->command_ptr_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+ }
/* Save tx_count to use in Callback */
tx->tx_count = tx_count;
@@ -901,16 +1097,28 @@
/* Calling next DMOV API. Hence mb() here. */
mb();
- dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
msm_uport->tx.flush = FLUSH_NONE;
- msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+
+ if (is_blsp_uart(msm_uport)) {
+ sps_pipe_handle = tx->cons.pipe_handle;
+ /* Queue transfer request to SPS */
+ sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
+ msm_uport, flags);
+ } else {
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+
+ msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+ }
}
/* Start to receive the next chunk of data */
static void msm_hs_start_rx_locked(struct uart_port *uport)
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle;
+ u32 flags = SPS_IOVEC_FLAG_EOT;
unsigned int buffer_pending = msm_uport->rx.buffer_pending;
unsigned int data;
@@ -919,6 +1127,10 @@
printk(KERN_ERR "Error: rx started in buffer state = %x",
buffer_pending);
+ if (is_blsp_uart(msm_uport)) {
+ /* Issue RX BAM Start IFC command */
+ msm_hs_write(uport, UARTDM_CR_ADDR, START_RX_BAM_IFC);
+ }
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE);
msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE);
@@ -929,15 +1141,29 @@
* disable in set_termios before configuring baud rate.
*/
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data |= UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Enable UARTDM Rx BAM Interface */
+ data |= UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ data |= UARTDM_RX_DM_EN_BMSK;
+ }
+
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
/* Calling next DMOV API. Hence mb() here. */
mb();
msm_uport->rx.flush = FLUSH_NONE;
- msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer);
+ if (is_blsp_uart(msm_uport)) {
+ sps_pipe_handle = rx->prod.pipe_handle;
+ /* Queue transfer request to SPS */
+ sps_transfer_one(sps_pipe_handle, rx->rbuffer,
+ UARTDM_RX_BUF_SIZE, msm_uport, flags);
+ } else {
+ msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel,
+ &msm_uport->rx.xfer);
+ }
}
static void flip_insert_work(struct work_struct *work)
@@ -999,6 +1225,7 @@
{
int retval;
int rx_count;
+ static int remaining_rx_count, bytes_pending;
unsigned long status;
unsigned long flags;
unsigned int error_f = 0;
@@ -1075,6 +1302,20 @@
rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
+ if (is_blsp_uart(msm_uport)) {
+ if (rx_count > UARTDM_RX_BUF_SIZE) {
+ if (bytes_pending) {
+ rx_count = remaining_rx_count;
+ bytes_pending = 0;
+ } else {
+ remaining_rx_count = rx_count -
+ UARTDM_RX_BUF_SIZE;
+ if (remaining_rx_count)
+ bytes_pending = 1;
+ rx_count = UARTDM_RX_BUF_SIZE;
+ }
+ }
+ }
/* order the read of rx.buffer */
rmb();
@@ -1124,6 +1365,31 @@
}
}
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_tx_callback(struct sps_event_notify *notify)
+{
+ struct msm_hs_port *msm_uport =
+ (struct msm_hs_port *)
+ ((struct sps_event_notify *)notify)->user;
+
+ msm_uport->notify = *notify;
+ pr_debug("%s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+ __func__, notify->event_id,
+ notify->data.transfer.iovec.addr,
+ notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
+
+ tasklet_schedule(&msm_uport->tx.tlet);
+}
+
/*
* This routine is called when we are done with a DMA transfer
*
@@ -1170,6 +1436,33 @@
spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
}
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_rx_callback(struct sps_event_notify *notify)
+{
+
+ struct msm_hs_port *msm_uport =
+ (struct msm_hs_port *)
+ ((struct sps_event_notify *)notify)->user;
+
+ msm_uport->notify = *notify;
+ pr_debug("%s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+ __func__, notify->event_id,
+ notify->data.transfer.iovec.addr,
+ notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
+
+ if (msm_uport->rx.flush == FLUSH_NONE)
+ tasklet_schedule(&msm_uport->rx.tlet);
+}
+
/*
* This routine is called when we are done with a DMA transfer or the
* a flush has been sent to the data mover driver.
@@ -1265,7 +1558,8 @@
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_uport->tty_flush_receive = true;
+ if (msm_uport->tx.dma_in_flight)
+ msm_uport->tty_flush_receive = true;
}
/*
@@ -1356,6 +1650,14 @@
switch (msm_uport->clk_req_off_state) {
case CLK_REQ_OFF_START:
msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;
+ if (is_blsp_uart(msm_uport)) {
+ /* Stale interrupt when RX-FIFO is empty
+ * will fire if STALE_IRQ_EMPTY bit is set
+ * for UART Core v1.4
+ */
+ msm_hs_write(uport, UARTDM_BCR_ADDR,
+ UARTDM_BCR_STALE_IRQ_EMPTY);
+ }
msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT);
/*
* Before returning make sure that device writel completed.
@@ -1458,13 +1760,25 @@
*/
mb();
- if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED)
+ if (msm_uport->clk_req_off_state ==
+ CLK_REQ_OFF_RXSTALE_ISSUED) {
msm_uport->clk_req_off_state =
CLK_REQ_OFF_FLUSH_ISSUED;
+ if (is_blsp_uart(msm_uport)) {
+ /* Reset BCR Register for UARTDM Core v14*/
+ msm_hs_write(uport, UARTDM_BCR_ADDR, 0x0);
+ }
+ }
+
if (rx->flush == FLUSH_NONE) {
rx->flush = FLUSH_DATA_READY;
- msm_dmov_flush(msm_uport->dma_rx_channel, 1);
+ if (is_blsp_uart(msm_uport)) {
+ queue_work(msm_uport->hsuart_wq,
+ &msm_uport->reset_bam_rx);
+ } else {
+ msm_dmov_flush(msm_uport->dma_rx_channel, 1);
+ }
}
}
/* tx ready interrupt */
@@ -1581,7 +1895,10 @@
msm_uport->rx.flush == FLUSH_SHUTDOWN) {
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data |= UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport))
+ data |= UARTDM_RX_BAM_ENABLE_BMSK;
+ else
+ data |= UARTDM_RX_DM_EN_BMSK;
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* Complete above device write. Hence mb() here. */
mb();
@@ -1661,6 +1978,9 @@
pdev->dev.platform_data;
struct circ_buf *tx_buf = &uport->state->xmit;
struct msm_hs_tx *tx = &msm_uport->tx;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle_tx = tx->cons.pipe_handle;
+ struct sps_pipe *sps_pipe_handle_rx = rx->prod.pipe_handle;
rfr_level = uport->fifosize;
if (rfr_level > 16)
@@ -1682,6 +2002,24 @@
if (unlikely(pdata->gpio_config(1)))
dev_err(uport->dev, "Cannot configure gpios\n");
+
+ /* SPS Connect for BAM endpoints */
+ if (is_blsp_uart(msm_uport)) {
+ /* SPS connect for TX */
+ ret = msm_hs_spsconnect_tx(uport);
+ if (ret) {
+ pr_err("msm_serial_hs: SPS connect failed for TX");
+ goto deinit_uart_clk;
+ }
+
+ /* SPS connect for RX */
+ ret = msm_hs_spsconnect_rx(uport);
+ if (ret) {
+ pr_err("msm_serial_hs: SPS connect failed for RX");
+ goto sps_disconnect_tx;
+ }
+ }
+
/* Set auto RFR Level */
data = msm_hs_read(uport, UARTDM_MR1_ADDR);
data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
@@ -1697,8 +2035,13 @@
msm_hs_write(uport, UARTDM_IPR_ADDR, data);
}
- /* Enable Data Mover Mode */
- data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Enable BAM mode */
+ data = UARTDM_TX_BAM_ENABLE_BMSK | UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ /* Enable Data Mover Mode */
+ data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ }
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* Reset TX */
@@ -1719,18 +2062,20 @@
tx->tx_ready_int_en = 0;
tx->dma_in_flight = 0;
- tx->xfer.complete_func = msm_hs_dmov_tx_callback;
+ if (!is_blsp_uart(msm_uport)) {
+ tx->xfer.complete_func = msm_hs_dmov_tx_callback;
- tx->command_ptr->cmd = CMD_LC |
- CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
+ tx->command_ptr->cmd = CMD_LC |
+ CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
- tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+ tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
| (MSM_UARTDM_BURST_SIZE);
- tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
+ tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
- tx->command_ptr->dst_row_addr =
- msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+ tx->command_ptr->dst_row_addr =
+ msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+ }
msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK;
/* Enable reading the current CTS, no harm even if CTS is ignored */
@@ -1747,7 +2092,7 @@
ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1);
if (unlikely(ret)) {
pr_err("%s():Err setting wakeup irq\n", __func__);
- goto deinit_uart_clk;
+ goto sps_disconnect_rx;
}
}
@@ -1787,6 +2132,12 @@
free_irq(uport->irq, msm_uport);
free_wake_irq:
irq_set_irq_wake(msm_uport->wakeup.irq, 0);
+sps_disconnect_rx:
+ if (is_blsp_uart(msm_uport))
+ sps_disconnect(sps_pipe_handle_rx);
+sps_disconnect_tx:
+ if (is_blsp_uart(msm_uport))
+ sps_disconnect(sps_pipe_handle_tx);
deinit_uart_clk:
clk_disable_unprepare(msm_uport->clk);
if (msm_uport->pclk)
@@ -1804,24 +2155,6 @@
struct msm_hs_tx *tx = &msm_uport->tx;
struct msm_hs_rx *rx = &msm_uport->rx;
- /* Allocate the command pointer. Needs to be 64 bit aligned */
- tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr)
- return -ENOMEM;
-
- tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr_ptr) {
- ret = -ENOMEM;
- goto free_tx_command_ptr;
- }
-
- tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
- tx->command_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
-
init_waitqueue_head(&rx->wait);
init_waitqueue_head(&tx->wait);
wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
@@ -1848,12 +2181,40 @@
goto free_pool;
}
+ /* Set up Uart Receive */
+ msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+
+ INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
+
+ if (is_blsp_uart(msm_uport))
+ return ret;
+
+ /* Allocate the command pointer. Needs to be 64 bit aligned */
+ tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr) {
+ return -ENOMEM;
+ goto free_rx_buffer;
+ }
+
+ tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr_ptr) {
+ ret = -ENOMEM;
+ goto free_tx_command_ptr;
+ }
+
+ tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+ tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
+ tx->command_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
+
/* Allocate the command pointer. Needs to be 64 bit aligned */
rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
if (!rx->command_ptr) {
pr_err("%s(): cannot allocate rx->command_ptr", __func__);
ret = -ENOMEM;
- goto free_rx_buffer;
+ goto free_tx_command_ptr_ptr;
}
rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
@@ -1868,9 +2229,6 @@
rx->command_ptr->dst_row_addr = rx->rbuffer;
- /* Set up Uart Receive */
- msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
-
rx->xfer.complete_func = msm_hs_dmov_rx_callback;
rx->command_ptr->cmd = CMD_LC |
@@ -1890,13 +2248,21 @@
sizeof(u32), DMA_TO_DEVICE);
rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
- INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
-
return ret;
free_rx_command_ptr:
kfree(rx->command_ptr);
+free_tx_command_ptr_ptr:
+ kfree(msm_uport->tx.command_ptr_ptr);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+
+free_tx_command_ptr:
+ kfree(msm_uport->tx.command_ptr);
+
free_rx_buffer:
dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
msm_uport->rx.rbuffer);
@@ -1909,47 +2275,386 @@
wake_lock_destroy(&msm_uport->dma_wake_lock);
tasklet_kill(&msm_uport->tx.tlet);
tasklet_kill(&msm_uport->rx.tlet);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- kfree(msm_uport->tx.command_ptr_ptr);
-
-free_tx_command_ptr:
- kfree(msm_uport->tx.command_ptr);
return ret;
}
+struct msm_serial_hs_platform_data
+ *msm_hs_dt_to_pdata(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_serial_hs_platform_data *pdata;
+ int rx_to_inject, ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("unable to allocate memory for platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* UART TX GPIO */
+ pdata->uart_tx_gpio = of_get_named_gpio(node,
+ "qcom,tx-gpio", 0);
+ if (pdata->uart_tx_gpio < 0)
+ pr_debug("uart_tx_gpio is not available\n");
+
+ /* UART RX GPIO */
+ pdata->uart_rx_gpio = of_get_named_gpio(node,
+ "qcom,rx-gpio", 0);
+ if (pdata->uart_rx_gpio < 0)
+ pr_debug("uart_rx_gpio is not available\n");
+
+ /* UART CTS GPIO */
+ pdata->uart_cts_gpio = of_get_named_gpio(node,
+ "qcom,cts-gpio", 0);
+ if (pdata->uart_cts_gpio < 0)
+ pr_debug("uart_cts_gpio is not available\n");
+
+ /* UART RFR GPIO */
+ pdata->uart_rfr_gpio = of_get_named_gpio(node,
+ "qcom,rfr-gpio", 0);
+ if (pdata->uart_rfr_gpio < 0)
+ pr_debug("uart_rfr_gpio is not available\n");
+
+ pdata->inject_rx_on_wakeup = of_property_read_bool(node,
+ "qcom,inject-rx-on-wakeup");
+
+ if (pdata->inject_rx_on_wakeup) {
+ ret = of_property_read_u32(node, "qcom,rx-char-to-inject",
+ &rx_to_inject);
+ if (ret < 0) {
+ pr_err("Error: Rx_char_to_inject not specified.\n");
+ return ERR_PTR(ret);
+ }
+ pdata->rx_to_inject = (char)rx_to_inject;
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-tx-ep-pipe-index",
+ &pdata->bam_tx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_tx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_tx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-rx-ep-pipe-index",
+ &pdata->bam_rx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_rx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_rx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pr_debug("tx_ep_pipe_index:%d rx_ep_pipe_index:%d\n"
+ "tx_gpio:%d rx_gpio:%d rfr_gpio:%d cts_gpio:%d",
+ pdata->bam_tx_ep_pipe_index, pdata->bam_rx_ep_pipe_index,
+ pdata->uart_tx_gpio, pdata->uart_rx_gpio, pdata->uart_cts_gpio,
+ pdata->uart_rfr_gpio);
+
+ return pdata;
+}
+
+
+/**
+ * Deallocate UART peripheral's SPS endpoint
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ */
+
+static void msm_hs_exit_ep_conn(struct msm_hs_port *msm_uport,
+ struct msm_hs_sps_ep_conn_data *ep)
+{
+ struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+ struct sps_connect *sps_config = &ep->config;
+
+ dma_free_coherent(msm_uport->uport.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ sps_free_endpoint(sps_pipe_handle);
+}
+
+
+/**
+ * Allocate UART peripheral's SPS endpoint
+ *
+ * This function allocates endpoint context
+ * by calling appropriate SPS driver APIs.
+ *
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ * @is_produce - 1 means Producer endpoint
+ * - 0 means Consumer endpoint
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init_ep_conn(struct msm_hs_port *msm_uport,
+ struct msm_hs_sps_ep_conn_data *ep,
+ bool is_producer)
+{
+ int rc = 0;
+ struct sps_pipe *sps_pipe_handle;
+ struct sps_connect *sps_config = &ep->config;
+ struct sps_register_event *sps_event = &ep->event;
+
+ /* Allocate endpoint context */
+ sps_pipe_handle = sps_alloc_endpoint();
+ if (!sps_pipe_handle) {
+ pr_err("msm_serial_hs: sps_alloc_endpoint() failed!!\n"
+ "is_producer=%d", is_producer);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Get default connection configuration for an endpoint */
+ rc = sps_get_config(sps_pipe_handle, sps_config);
+ if (rc) {
+ pr_err("msm_serial_hs: sps_get_config() failed!!\n"
+ "pipe_handle=0x%x rc=%d", (u32)sps_pipe_handle, rc);
+ goto get_config_err;
+ }
+
+ /* Modify the default connection configuration */
+ if (is_producer) {
+ /* For UART producer transfer, source is UART peripheral
+ where as destination is system memory */
+ sps_config->source = msm_uport->bam_handle;
+ sps_config->destination = SPS_DEV_HANDLE_MEM;
+ sps_config->mode = SPS_MODE_SRC;
+ sps_config->src_pipe_index = msm_uport->bam_rx_ep_pipe_index;
+ sps_config->dest_pipe_index = 0;
+ sps_config->options = SPS_O_EOT;
+ } else {
+ /* For UART consumer transfer, source is system memory
+ where as destination is UART peripheral */
+ sps_config->source = SPS_DEV_HANDLE_MEM;
+ sps_config->destination = msm_uport->bam_handle;
+ sps_config->mode = SPS_MODE_DEST;
+ sps_config->src_pipe_index = 0;
+ sps_config->dest_pipe_index = msm_uport->bam_tx_ep_pipe_index;
+ sps_config->options = SPS_O_EOT;
+ }
+
+ sps_config->event_thresh = 0x10;
+
+ /* Allocate maximum descriptor fifo size */
+ sps_config->desc.size = 65532;
+ sps_config->desc.base = dma_alloc_coherent(msm_uport->uport.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ if (!sps_config->desc.base) {
+ rc = -ENOMEM;
+ pr_err("msm_serial_hs: dma_alloc_coherent() failed!!\n");
+ goto get_config_err;
+ }
+ memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+ sps_event->mode = SPS_TRIGGER_CALLBACK;
+ sps_event->options = SPS_O_EOT;
+ if (is_producer)
+ sps_event->callback = msm_hs_sps_rx_callback;
+ else
+ sps_event->callback = msm_hs_sps_tx_callback;
+
+ sps_event->user = (void *)msm_uport;
+
+ /* Now save the sps pipe handle */
+ ep->pipe_handle = sps_pipe_handle;
+ pr_debug("msm_serial_hs: success !! %s: pipe_handle=0x%x\n"
+ "desc_fifo.phys_base=0x%x\n",
+ is_producer ? "READ" : "WRITE",
+ (u32)sps_pipe_handle, sps_config->desc.phys_base);
+ return 0;
+
+get_config_err:
+ sps_free_endpoint(sps_pipe_handle);
+out:
+ return rc;
+}
+
+/**
+ * Initialize SPS HW connected with UART core
+ *
+ * This function register BAM HW resources with
+ * SPS driver and then initialize 2 SPS endpoints
+ *
+ * msm_uport - Pointer to msm_hs_port structure
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init(struct msm_hs_port *msm_uport)
+{
+ int rc = 0;
+ struct sps_bam_props bam = {0};
+ u32 bam_handle;
+
+ rc = sps_phy2h(msm_uport->bam_mem, &bam_handle);
+ if (rc || !bam_handle) {
+ bam.phys_addr = msm_uport->bam_mem;
+ bam.virt_addr = msm_uport->bam_base;
+ /*
+ * This event thresold value is only significant for BAM-to-BAM
+ * transfer. It's ignored for BAM-to-System mode transfer.
+ */
+ bam.event_threshold = 0x10; /* Pipe event threshold */
+ bam.summing_threshold = 1; /* BAM event threshold */
+
+ /* SPS driver wll handle the UART BAM IRQ */
+ bam.irq = (u32)msm_uport->bam_irq;
+ bam.manage = SPS_BAM_MGR_LOCAL;
+
+ pr_debug("msm_serial_hs: bam physical base=0x%x\n",
+ (u32)bam.phys_addr);
+ pr_debug("msm_serial_hs: bam virtual base=0x%x\n",
+ (u32)bam.virt_addr);
+
+ /* Register UART Peripheral BAM device to SPS driver */
+ rc = sps_register_bam_device(&bam, &bam_handle);
+ if (rc) {
+ pr_err("msm_serial_hs: BAM device register failed\n");
+ return rc;
+ }
+ pr_info("msm_serial_hs: BAM device registered. bam_handle=0x%x",
+ msm_uport->bam_handle);
+ }
+ msm_uport->bam_handle = bam_handle;
+
+ rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->rx.prod,
+ UART_SPS_PROD_PERIPHERAL);
+ if (rc) {
+ pr_err("%s: Failed to Init Producer BAM-pipe", __func__);
+ goto deregister_bam;
+ }
+
+ rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->tx.cons,
+ UART_SPS_CONS_PERIPHERAL);
+ if (rc) {
+ pr_err("%s: Failed to Init Consumer BAM-pipe", __func__);
+ goto deinit_ep_conn_prod;
+ }
+ return 0;
+
+deinit_ep_conn_prod:
+ msm_hs_exit_ep_conn(msm_uport, &msm_uport->rx.prod);
+deregister_bam:
+ sps_deregister_bam_device(msm_uport->bam_handle);
+ return rc;
+}
+
static int __devinit msm_hs_probe(struct platform_device *pdev)
{
int ret;
struct uart_port *uport;
struct msm_hs_port *msm_uport;
+ struct resource *core_resource;
+ struct resource *bam_resource;
struct resource *resource;
+ int core_irqres, bam_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (pdev->dev.of_node) {
+ dev_dbg(&pdev->dev, "device tree enabled\n");
+ pdata = msm_hs_dt_to_pdata(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ of_property_read_u32(node, "cell-index",
+ &pdev->id);
+
+ pdev->dev.platform_data = pdata;
+ }
if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
- printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+ pr_err("Invalid plaform device ID = %d\n", pdev->id);
return -EINVAL;
}
msm_uport = &q_uart_port[pdev->id];
uport = &msm_uport->uport;
-
uport->dev = &pdev->dev;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (unlikely(!resource))
- return -ENXIO;
- uport->mapbase = resource->start; /* virtual address */
+ if (pdev->dev.of_node)
+ msm_uport->uart_type = BLSP_HSUART;
- uport->membase = ioremap(uport->mapbase, PAGE_SIZE);
- if (unlikely(!uport->membase))
- return -ENOMEM;
+ /* Get required resources for BAM HSUART */
+ if (is_blsp_uart(msm_uport)) {
+ core_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "core_mem");
+ bam_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "bam_mem");
+ core_irqres = platform_get_irq_byname(pdev, "core_irq");
+ bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
- uport->irq = platform_get_irq(pdev, 0);
- if (unlikely((int)uport->irq < 0))
- return -ENXIO;
+ if (!core_resource) {
+ pr_err("Invalid core HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!bam_resource) {
+ pr_err("Invalid BAM HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!core_irqres) {
+ pr_err("Invalid core irqres Resources.\n");
+ return -ENXIO;
+ }
+ if (!bam_irqres) {
+ pr_err("Invalid bam irqres Resources.\n");
+ return -ENXIO;
+ }
+
+ uport->mapbase = core_resource->start;
+
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(core_resource));
+ if (unlikely(!uport->membase)) {
+ pr_err("UART Resource ioremap Failed.\n");
+ return -ENOMEM;
+ }
+ msm_uport->bam_mem = bam_resource->start;
+ msm_uport->bam_base = ioremap(msm_uport->bam_mem,
+ resource_size(bam_resource));
+ if (unlikely(!msm_uport->bam_base)) {
+ pr_err("UART BAM Resource ioremap Failed.\n");
+ iounmap(uport->membase);
+ return -ENOMEM;
+ }
+
+ uport->irq = core_irqres;
+ msm_uport->bam_irq = bam_irqres;
+
+ } else {
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource))
+ return -ENXIO;
+ uport->mapbase = resource->start;
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(resource));
+ if (unlikely(!uport->membase))
+ return -ENOMEM;
+
+ uport->irq = platform_get_irq(pdev, 0);
+ if (unlikely((int)uport->irq < 0)) {
+ pr_err("UART IRQ Failed.\n");
+ iounmap(uport->membase);
+ return -ENXIO;
+ }
+ }
if (pdata == NULL)
msm_uport->wakeup.irq = -1;
@@ -1959,24 +2664,41 @@
msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
- if (unlikely(msm_uport->wakeup.irq < 0))
- return -ENXIO;
+ if (unlikely(msm_uport->wakeup.irq < 0)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->bam_tx_ep_pipe_index =
+ pdata->bam_tx_ep_pipe_index;
+ msm_uport->bam_rx_ep_pipe_index =
+ pdata->bam_rx_ep_pipe_index;
+ }
}
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_channels");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_channel = resource->start;
- msm_uport->dma_rx_channel = resource->end;
+ if (!is_blsp_uart(msm_uport)) {
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_crci");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_crci = resource->start;
- msm_uport->dma_rx_crci = resource->end;
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_channels");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_channel = resource->start;
+ msm_uport->dma_rx_channel = resource->end;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_crci");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_crci = resource->start;
+ msm_uport->dma_rx_crci = resource->end;
+ }
uport->iotype = UPIO_MEM;
uport->fifosize = 64;
@@ -1986,8 +2708,10 @@
msm_uport->imr_reg = 0x0;
msm_uport->clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(msm_uport->clk))
- return PTR_ERR(msm_uport->clk);
+ if (IS_ERR(msm_uport->clk)) {
+ ret = PTR_ERR(msm_uport->clk);
+ goto unmap_memory;
+ }
msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
/*
@@ -2000,7 +2724,7 @@
ret = clk_set_rate(msm_uport->clk, uport->uartclk);
if (ret) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
- return ret;
+ goto unmap_memory;
}
msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
@@ -2008,12 +2732,29 @@
if (!msm_uport->hsuart_wq) {
pr_err("%s(): Unable to create workqueue hsuart_wq\n",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto unmap_memory;
}
INIT_WORK(&msm_uport->clock_off_w, hsuart_clock_off_work);
+
+ /* Init work for Reset Rx bam endpoints */
+ INIT_WORK(&msm_uport->reset_bam_rx, hsuart_reset_bam_rx_work);
+
+ /* Init work for sps_disconnect in stop_rx_locked */
+ INIT_WORK(&msm_uport->disconnect_rx_endpoint,
+ hsuart_disconnect_rx_endpoint_work);
mutex_init(&msm_uport->clk_mutex);
+ /* Initialize SPS HW connected with UART core */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_hs_sps_init(msm_uport);
+ if (unlikely(ret)) {
+ pr_err("SPS Initialization failed ! err=%d", ret);
+ goto unmap_memory;
+ }
+ }
+
clk_prepare_enable(msm_uport->clk);
if (msm_uport->pclk)
clk_prepare_enable(msm_uport->pclk);
@@ -2023,7 +2764,7 @@
clk_disable_unprepare(msm_uport->clk);
if (msm_uport->pclk)
clk_disable_unprepare(msm_uport->pclk);
- return ret;
+ goto unmap_memory;
}
/* configure the CR Protection to Enable */
@@ -2048,14 +2789,23 @@
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
if (unlikely(ret))
- return ret;
+ goto unmap_memory;
msm_serial_debugfs_init(msm_uport, pdev->id);
uport->line = pdev->id;
if (pdata != NULL && pdata->userid && pdata->userid <= UARTDM_NR)
uport->line = pdata->userid;
- return uart_add_one_port(&msm_hs_driver, uport);
+ ret = uart_add_one_port(&msm_hs_driver, uport);
+ if (!ret)
+ return ret;
+
+unmap_memory:
+ iounmap(uport->membase);
+ if (is_blsp_uart(msm_uport))
+ iounmap(msm_uport->bam_base);
+
+ return ret;
}
static int __init msm_serial_hs_init(void)
@@ -2102,27 +2852,35 @@
struct platform_device *pdev = to_platform_device(uport->dev);
const struct msm_serial_hs_platform_data *pdata =
pdev->dev.platform_data;
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
if (msm_uport->tx.dma_in_flight) {
- spin_lock_irqsave(&uport->lock, flags);
- /* disable UART TX interface to DM */
- data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_TX_DM_EN_BMSK;
- msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
- /* turn OFF UART Transmitter */
- msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
- /* reset UART TX */
- msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
- /* reset UART TX Error */
- msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
- msm_uport->tx.flush = FLUSH_STOP;
- spin_unlock_irqrestore(&uport->lock, flags);
- /* discard flush */
- msm_dmov_flush(msm_uport->dma_tx_channel, 0);
- ret = wait_event_timeout(msm_uport->tx.wait,
- msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
- if (!ret)
- pr_err("%s():HSUART TX Stalls.\n", __func__);
+ if (!is_blsp_uart(msm_uport)) {
+ spin_lock_irqsave(&uport->lock, flags);
+ /* disable UART TX interface to DM */
+ data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+ data &= ~UARTDM_TX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+ /* turn OFF UART Transmitter */
+ msm_hs_write(uport, UARTDM_CR_ADDR,
+ UARTDM_CR_TX_DISABLE_BMSK);
+ /* reset UART TX */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+ /* reset UART TX Error */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
+ msm_uport->tx.flush = FLUSH_STOP;
+ spin_unlock_irqrestore(&uport->lock, flags);
+ /* discard flush */
+ msm_dmov_flush(msm_uport->dma_tx_channel, 0);
+ ret = wait_event_timeout(msm_uport->tx.wait,
+ msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
+ if (!ret)
+ pr_err("%s():HSUART TX Stalls.\n", __func__);
+ } else {
+ /* BAM Disconnect for TX */
+ sps_disconnect(sps_pipe_handle);
+ }
}
tasklet_kill(&msm_uport->tx.tlet);
BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
@@ -2211,12 +2969,18 @@
.runtime_idle = msm_hs_runtime_idle,
};
+static struct of_device_id msm_hs_match_table[] = {
+ { .compatible = "qcom,msm-hsuart-v14" },
+ {}
+};
+
static struct platform_driver msm_serial_hs_platform_driver = {
.probe = msm_hs_probe,
.remove = __devexit_p(msm_hs_remove),
.driver = {
.name = "msm_serial_hs",
.pm = &msm_hs_dev_pm_ops,
+ .of_match_table = msm_hs_match_table,
},
};
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 02cdd71..ed0a385 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -955,6 +955,7 @@
int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr);
int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr);
int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg);
+int mdp4_calib_config(struct mdp_calib_config_data *cfg);
int mdp4_qseed_access_cfg(struct mdp_qseed_cfg *cfg, uint32_t base);
u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 01ec10e..4858073 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3080,6 +3080,108 @@
error:
return ret;
}
+
+static int is_valid_calib_addr(void *addr)
+{
+ int ret = 0;
+ unsigned int ptr;
+
+ if (addr == NULL)
+ goto end;
+
+ ptr = (unsigned int) addr;
+
+ if (mdp_rev >= MDP_REV_30 && mdp_rev < MDP_REV_40) {
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr > 0xF0600 || ptr % 0x4)
+ goto end;
+
+ if (ptr >= 0x90000 && ptr < 0x94000) {
+ if (ptr == 0x90000 || ptr == 0x90070)
+ ret = 1;
+ else if (ptr >= 0x93400 && ptr <= 0x93420)
+ ret = 1;
+ else if (ptr >= 0x93500 && ptr <= 0x93508)
+ ret = 1;
+ else if (ptr >= 0x93580 && ptr <= 0x93588)
+ ret = 1;
+ else if (ptr >= 0x93600 && ptr <= 0x93614)
+ ret = 1;
+ else if (ptr >= 0x93680 && ptr <= 0x93694)
+ ret = 1;
+ else if (ptr >= 0x93800 && ptr <= 0x93BFC)
+ ret = 1;
+ }
+ } else if (mdp_rev >= MDP_REV_40 && mdp_rev <= MDP_REV_44) {
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr > 0xF0600 || ptr % 0x4)
+ goto end;
+
+ if (ptr < 0x90000) {
+ if (ptr == 0x4 || ptr == 0x28200 || ptr == 0x28204)
+ ret = 1;
+ } else if (ptr < 0x95000) {
+ if (ptr == 0x90000 || ptr == 0x90070)
+ ret = 1;
+ else if (ptr >= 0x93400 && ptr <= 0x93420)
+ ret = 1;
+ else if (ptr >= 0x93500 && ptr <= 0x93508)
+ ret = 1;
+ else if (ptr >= 0x93580 && ptr <= 0x93588)
+ ret = 1;
+ else if (ptr >= 0x93600 && ptr <= 0x93614)
+ ret = 1;
+ else if (ptr >= 0x93680 && ptr <= 0x93694)
+ ret = 1;
+ else if (ptr >= 0x94800 && ptr <= 0x94BFC)
+ ret = 1;
+ } else if (ptr < 0x9A000) {
+ if (ptr >= 0x98800 && ptr <= 0x9883C)
+ ret = 1;
+ else if (ptr >= 0x98880 && ptr <= 0x988AC)
+ ret = 1;
+ else if (ptr >= 0x98900 && ptr <= 0x9893C)
+ ret = 1;
+ else if (ptr >= 0x98980 && ptr <= 0x989BC)
+ ret = 1;
+ else if (ptr >= 0x98A00 && ptr <= 0x98A3C)
+ ret = 1;
+ else if (ptr >= 0x98A80 && ptr <= 0x98ABC)
+ ret = 1;
+ else if (ptr >= 0x99000 && ptr <= 0x993FC)
+ ret = 1;
+ else if (ptr >= 0x99800 && ptr <= 0x99BFC)
+ ret = 1;
+ } else if (ptr >= 0x9A000 && ptr <= 0x9a08c) {
+ ret = 1;
+ }
+ }
+end:
+ return ret;
+}
+
+int mdp4_calib_config(struct mdp_calib_config_data *cfg)
+{
+ int ret = -1;
+ void *ptr = (void *) cfg->addr;
+
+ if (is_valid_calib_addr(ptr))
+ ret = 0;
+ else
+ return ret;
+
+ ptr = (void *)(((unsigned int) ptr) + MDP_BASE);
+ mdp_clk_ctrl(1);
+ if (cfg->ops & MDP_PP_OPS_READ) {
+ cfg->data = inpdw(ptr);
+ ret = 1;
+ } else if (cfg->ops & MDP_PP_OPS_WRITE) {
+ outpdw(ptr, cfg->data);
+ }
+ mdp_clk_ctrl(0);
+ return ret;
+}
+
u32 mdp4_get_mixer_num(u32 panel_type)
{
u32 mixer_num;
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 50723e7..4404b9d 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -2,7 +2,7 @@
* Core MDSS framebuffer driver.
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. 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
@@ -889,20 +889,6 @@
var->hsync_len = panel_info->lcdc.h_pulse_width;
var->pixclock = panel_info->clk_rate / 1000;
- if (panel_info->type == MIPI_VIDEO_PANEL) {
- var->reserved[4] = panel_info->mipi.frame_rate;
- } else {
- var->reserved[4] = panel_info->clk_rate /
- ((panel_info->lcdc.h_back_porch +
- panel_info->lcdc.h_front_porch +
- panel_info->lcdc.h_pulse_width +
- panel_info->xres) *
- (panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres));
- }
-
/* id field for fb app */
id = (int *)&mfd->panel;
@@ -1589,6 +1575,23 @@
return ret;
}
+static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata)
+{
+ int ret = 0;
+ switch (metadata->op) {
+ case metadata_op_frame_rate:
+ metadata->data.panel_frame_rate =
+ mdss_get_panel_framerate(mfd);
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -1600,6 +1603,7 @@
struct mdp_page_protection fb_page_protection;
int ret = -ENOSYS;
struct mdp_buf_sync buf_sync;
+ struct msmfb_metadata metadata;
mdss_fb_power_setting_idle(mfd);
@@ -1683,6 +1687,15 @@
ret = mdss_fb_display_commit(info, argp);
break;
+ case MSMFB_METADATA_GET:
+ ret = copy_from_user(&metadata, argp, sizeof(metadata));
+ if (ret)
+ return ret;
+ ret = mdss_fb_get_metadata(mfd, &metadata);
+ if (!ret)
+ ret = copy_to_user(argp, &metadata, sizeof(metadata));
+ break;
+
default:
if (mfd->ioctl_handler)
ret = mfd->ioctl_handler(mfd, cmd, argp);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index e9427da..ef6e5b4 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -341,6 +341,7 @@
struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format);
int mdss_mdp_put_img(struct mdss_mdp_img_data *data);
int mdss_mdp_get_img(struct msmfb_data *img, struct mdss_mdp_img_data *data);
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_kickoff(struct mdss_mdp_ctl *ctl);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 9f2df85..75a926a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,8 @@
#include "mdss_mdp.h"
#include "mdss_mdp_formats.h"
+#define DEFAULT_FRAME_RATE 60
+
enum {
MDP_INTR_VSYNC_INTF_0,
MDP_INTR_VSYNC_INTF_1,
@@ -433,3 +435,26 @@
return ret;
}
+
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd)
+{
+ u32 frame_rate = DEFAULT_FRAME_RATE;
+ u32 pixel_total;
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+
+ if (panel_info->type == MIPI_VIDEO_PANEL) {
+ frame_rate = panel_info->mipi.frame_rate;
+ } else {
+ pixel_total = (panel_info->lcdc.h_back_porch +
+ panel_info->lcdc.h_front_porch +
+ panel_info->lcdc.h_pulse_width +
+ panel_info->xres) *
+ (panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width +
+ panel_info->yres);
+ if (pixel_total)
+ frame_rate = panel_info->clk_rate / pixel_total;
+ }
+ return frame_rate;
+}
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 89e8e1e..3841498 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -1262,8 +1262,6 @@
((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page;
var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
- var->reserved[4] = mdp_get_panel_framerate(mfd);
-
/*
* id field for fb app
*/
@@ -3249,6 +3247,10 @@
ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *)
&pp_ptr->data.qseed_cfg_data);
break;
+ case mdp_op_calib_cfg:
+ ret = mdp4_calib_config((struct mdp_calib_config_data *)
+ &pp_ptr->data.calib_cfg);
+ break;
#endif
case mdp_bl_scale_cfg:
ret = mdp_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
@@ -3281,6 +3283,24 @@
}
return ret;
}
+
+static int msmfb_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata_ptr)
+{
+ int ret = 0;
+ switch (metadata_ptr->op) {
+ case metadata_op_frame_rate:
+ metadata_ptr->data.panel_frame_rate =
+ mdp_get_panel_framerate(mfd);
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -3583,6 +3603,8 @@
return ret;
ret = msmfb_handle_pp_ioctl(mfd, &mdp_pp);
+ if (ret == 1)
+ ret = copy_to_user(argp, &mdp_pp, sizeof(mdp_pp));
break;
case MSMFB_METADATA_SET:
@@ -3592,6 +3614,16 @@
ret = msmfb_handle_metadata_ioctl(mfd, &mdp_metadata);
break;
+ case MSMFB_METADATA_GET:
+ ret = copy_from_user(&mdp_metadata, argp, sizeof(mdp_metadata));
+ if (ret)
+ return ret;
+ ret = msmfb_get_metadata(mfd, &mdp_metadata);
+ if (!ret)
+ ret = copy_to_user(argp, &mdp_metadata,
+ sizeof(mdp_metadata));
+ break;
+
default:
MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
ret = -EINVAL;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index f6a2923..d31cb68 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -279,7 +279,7 @@
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
- __u32 reserved[5]; /* Reserved for future compatibility */
+ __u32 reserved[4]; /* Reserved for future compatibility */
};
struct fb_cmap {
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index c947e45..458f060 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -157,6 +157,10 @@
int bytes, void *dest, bool interface_reg);
int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src, bool interface_reg);
+ int (*post_reset)(struct wcd9xxx *wcd9xxx);
+
+ void *ssr_priv;
+ bool slim_device_bootup;
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 7e67db0..fdb8fb6 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -1,7 +1,7 @@
/* include/linux/msm_mdp.h
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013 The Linux Foundation. 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
@@ -75,6 +75,7 @@
#define MSMFB_BUFFER_SYNC _IOW(MSMFB_IOCTL_MAGIC, 164, struct mdp_buf_sync)
#define MSMFB_DISPLAY_COMMIT _IOW(MSMFB_IOCTL_MAGIC, 165, \
struct mdp_display_commit)
+#define MSMFB_METADATA_GET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
#define FB_TYPE_3D_PANEL 0x10101010
#define MDP_IMGTYPE2_START 0x10000
@@ -555,6 +556,7 @@
enum {
metadata_op_none,
metadata_op_base_blend,
+ metadata_op_frame_rate,
metadata_op_max
};
@@ -567,6 +569,7 @@
uint32_t flags;
union {
struct mdp_blend_cfg blend_cfg;
+ uint32_t panel_frame_rate;
} data;
};
diff --git a/include/linux/qmi_encdec.h b/include/linux/qmi_encdec.h
index 4c5f6d3..b1fd217 100644
--- a/include/linux/qmi_encdec.h
+++ b/include/linux/qmi_encdec.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -150,6 +150,15 @@
int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
void *in_buf, uint32_t in_buf_len);
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ * descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc);
+
#else
static inline int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
@@ -164,6 +173,11 @@
{
return -EOPNOTSUPP;
}
+
+static inline bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+ return false;
+}
#endif
#endif
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 5afbfad..c12cbbe 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1453,6 +1453,23 @@
u32 read_format;
} __attribute__((packed));
+#define ASM_STREAM_CMD_OPEN_LOOPBACK 0x00010D6E
+struct asm_stream_cmd_open_loopback {
+ struct apr_hdr hdr;
+ u32 mode_flags;
+/* Mode flags.
+ * Bit 0-31: reserved; client should set these bits to 0
+ */
+ u16 src_endpointype;
+ /* Endpoint type. 0 = Tx Matrix */
+ u16 sink_endpointype;
+ /* Endpoint type. 0 = Rx Matrix */
+ u32 postprocopo_id;
+/* Postprocessor topology ID. Specifies the topology of
+ * postprocessing algorithms.
+ */
+} __packed;
+
#define ADM_CMD_CONNECT_AFE_PORT 0x00010320
#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321
@@ -1898,5 +1915,4 @@
int srs_ss3d_open(int port_id, int srs_tech_id, void *srs_params);
/* SRS Studio Sound 3D end */
-
#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index dcdd816..275cdbe 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -206,6 +206,8 @@
uint32_t rd_format,
uint32_t wr_format);
+int q6asm_open_loopack(struct audio_client *ac);
+
int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
uint32_t lsw_ts, uint32_t flags);
int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
index 5c489cf..3f618cb 100644
--- a/lib/qmi_encdec.c
+++ b/lib/qmi_encdec.c
@@ -90,6 +90,71 @@
int dec_level);
/**
+ * qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message
+ * @ei_array: Struct info array describing the structure.
+ * @level: Level to identify the depth of the nested structures.
+ *
+ * @return: expected maximum length of the QMI message or 0 on failure.
+ */
+static int qmi_calc_max_msg_len(struct elem_info *ei_array,
+ int level)
+{
+ int max_msg_len = 0;
+ struct elem_info *temp_ei;
+
+ if (!ei_array)
+ return max_msg_len;
+
+ for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) {
+ /* Flag to identify the optional element is not encoded */
+ if (temp_ei->data_type == QMI_OPT_FLAG)
+ continue;
+
+ if (temp_ei->data_type == QMI_DATA_LEN) {
+ max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
+ sizeof(uint8_t) : sizeof(uint16_t));
+ continue;
+ } else if (temp_ei->data_type == QMI_STRUCT) {
+ max_msg_len += qmi_calc_max_msg_len(temp_ei->ei_array,
+ (level + 1));
+ } else {
+ max_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
+ }
+
+ /*
+ * Type & Length info. not prepended for elements in the
+ * nested structure.
+ */
+ if (level == 1)
+ max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+ }
+ return max_msg_len;
+}
+
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ * descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+ int calc_max_msg_len;
+
+ if (!desc)
+ return false;
+
+ calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+ if (calc_max_msg_len != desc->max_msg_len) {
+ pr_err("%s: Calc. len %d != Passed len %d\n",
+ __func__, calc_max_msg_len, desc->max_msg_len);
+ return false;
+ }
+ return true;
+}
+
+/**
* qmi_kernel_encode() - Encode to QMI message wire format
* @desc: Pointer to structure descriptor.
* @out_buf: Buffer to hold the encoded QMI message.
@@ -103,6 +168,7 @@
void *in_c_struct)
{
int enc_level = 1;
+ int ret, calc_max_msg_len;
if (!desc || !desc->ei_array)
return -EINVAL;
@@ -113,8 +179,14 @@
if (desc->max_msg_len < out_buf_len)
return -ETOOSMALL;
- return _qmi_kernel_encode(desc->ei_array, out_buf,
- in_c_struct, out_buf_len, enc_level);
+ ret = _qmi_kernel_encode(desc->ei_array, out_buf,
+ in_c_struct, out_buf_len, enc_level);
+ if (ret == -ETOOSMALL) {
+ calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+ pr_err("%s: Calc. len %d != Out buf len %d\n",
+ __func__, calc_max_msg_len, out_buf_len);
+ }
+ return ret;
}
EXPORT_SYMBOL(qmi_kernel_encode);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 1546b9e..ad1546d 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -4892,12 +4892,53 @@
}
EXPORT_SYMBOL_GPL(taiko_hs_detect);
+static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec;
+ struct taiko_priv *taiko;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ taiko = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&codec->mutex);
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ if (spkr_drv_wrnd == 1) {
+ wcd9xxx_resmgr_post_ssr(&taiko->resmgr);
+ snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
+ }
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
+ if (codec->reg_def_copy) {
+ pr_debug("%s: Update ASOC cache", __func__);
+ kfree(codec->reg_cache);
+ codec->reg_cache = kmemdup(codec->reg_def_copy,
+ codec->reg_size, GFP_KERNEL);
+ }
+
+ taiko_update_reg_defaults(codec);
+ taiko_codec_init_reg(codec);
+ ret = taiko_handle_pdata(taiko);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: bad pdata\n", __func__);
+ mutex_unlock(&codec->mutex);
+ return ret;
+}
+
+
static struct wcd9xxx_reg_address taiko_reg_address = {
.micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
.micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
.micb_4_ctl = TAIKO_A_MICB_4_CTL,
};
+static int wcd9xxx_ssr_register(struct wcd9xxx *control,
+ int (*post_reset_cb)(struct wcd9xxx *wcd9xxx), void *priv)
+{
+ control->post_reset = post_reset_cb;
+ control->ssr_priv = priv;
+ return 0;
+}
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
@@ -4912,6 +4953,8 @@
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
+ wcd9xxx_ssr_register(control, taiko_post_reset_cb, (void *)codec);
+
dev_info(codec->dev, "%s()\n", __func__);
taiko = kzalloc(sizeof(struct taiko_priv), GFP_KERNEL);
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index d744cb9..0ab650e 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -195,6 +195,24 @@
pr_debug("%s: leave\n", __func__);
}
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr)
+{
+ int old_bg_audio_users, old_bg_mbhc_users;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+ old_bg_audio_users = resmgr->bg_audio_users;
+ resmgr->bg_audio_users = 0;
+ old_bg_mbhc_users = resmgr->bg_mbhc_users;
+ resmgr->bg_mbhc_users = 0;
+
+ while (old_bg_audio_users && --old_bg_audio_users)
+ wcd9xxx_resmgr_get_bandgap(resmgr, WCD9XXX_BANDGAP_AUDIO_MODE);
+
+ while (old_bg_mbhc_users && --old_bg_mbhc_users)
+ wcd9xxx_resmgr_get_bandgap(resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+}
+
/*
* wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
* choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
index 2d04102..6c30eeb 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.h
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -158,6 +158,7 @@
enum wcd9xxx_cfilt_sel cfilt_sel);
void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr);
#define WCD9XXX_BCL_LOCK(resmgr) \
{ \
pr_debug("%s: Acquiring BCL\n", __func__); \
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index 4c8d2e3..e5f0208 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -55,8 +55,7 @@
# for MSM 8960 sound card driver
obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
-
-snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
+snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-loopback.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o msm-pcm-dtmf.o
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index cf0d4cd..784b650 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -229,6 +229,17 @@
.rate_min = 8000,
.rate_max = 48000,
},
+ .capture = {
+ .stream_name = "MultiMedia6 Capture",
+ .aif_name = "MM_UL6",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
.ops = &msm_fe_Multimedia_dai_ops,
.name = "MultiMedia6",
},
diff --git a/sound/soc/msm/msm-pcm-loopback.c b/sound/soc/msm/msm-pcm-loopback.c
new file mode 100644
index 0000000..55f29a5
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-loopback.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/apr_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/q6asm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm-routing.h"
+
+struct msm_pcm {
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ int instance;
+
+ struct mutex lock;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ int playback_start;
+ int capture_start;
+ int session_id;
+ struct audio_client *audio_client;
+};
+
+static void stop_pcm(struct msm_pcm *pcm);
+
+static struct msm_pcm pcm_info;
+
+static const struct snd_pcm_hardware dummy_pcm_hardware = {
+ .formats = 0xffffffff,
+ .channels_min = 1,
+ .channels_max = UINT_MAX,
+
+ /* Random values to keep userspace happy when checking constraints */
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .buffer_bytes_max = 128*1024,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = PAGE_SIZE*2,
+ .periods_min = 2,
+ .periods_max = 128,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case APR_BASIC_RSP_RESULT:
+ pr_debug("%s: opcode[0x%x]\n", __func__, opcode);
+ break;
+ default:
+ pr_err("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = &pcm_info;
+ int ret = 0;
+
+ mutex_lock(&pcm->lock);
+
+ snd_soc_set_runtime_hwparams(substream, &dummy_pcm_hardware);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_substream = substream;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_substream = substream;
+
+ pcm->instance++;
+ pr_debug("%s: pcm out open: %d,%d\n", __func__,
+ pcm->instance, substream->stream);
+ if (pcm->instance == 2) {
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+ if (pcm->audio_client != NULL)
+ stop_pcm(pcm);
+
+ pcm->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, pcm);
+ if (!pcm->audio_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ pcm->session_id = pcm->audio_client->session;
+ pcm->audio_client->perf_mode = false;
+ ret = q6asm_open_loopack(pcm->audio_client);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(pcm->audio_client);
+ mutex_unlock(&pcm->lock);
+ return -ENOMEM;
+ }
+ msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->capture_substream->stream);
+ msm_pcm_routing_reg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ pcm->audio_client->perf_mode,
+ pcm->session_id, pcm->playback_substream->stream);
+ }
+ pr_debug("%s: Instance = %d, Stream ID = %s\n",
+ __func__ , pcm->instance, substream->pcm->id);
+ runtime->private_data = pcm;
+
+ mutex_unlock(&pcm->lock);
+
+ return 0;
+}
+
+int msm_set_lb_volume(unsigned volume)
+{
+ int rc = 0;
+ if (pcm_info.audio_client != NULL) {
+ pr_debug("%s: apply loopback vol:%d\n", __func__, volume);
+ rc = q6asm_set_volume(pcm_info.audio_client, volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed" \
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ return rc;
+}
+
+static void stop_pcm(struct msm_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *soc_pcm_rx =
+ pcm->playback_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_tx =
+ pcm->capture_substream->private_data;
+
+ if (pcm->audio_client == NULL)
+ return;
+ q6asm_cmd(pcm->audio_client, CMD_CLOSE);
+
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_audio_client_free(pcm->audio_client);
+ pcm->audio_client = NULL;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = runtime->private_data;
+ int ret = 0;
+
+ mutex_lock(&pcm->lock);
+
+ pr_debug("%s: end pcm call:%d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm->playback_start = 0;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm->capture_start = 0;
+
+ pcm->instance--;
+ if (!pcm->playback_start || !pcm->capture_start) {
+ pr_debug("%s: end pcm call\n", __func__);
+ stop_pcm(pcm);
+ }
+
+ mutex_unlock(&pcm->lock);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = runtime->private_data;
+
+ mutex_lock(&pcm->lock);
+
+ pr_debug("%s: ASM loopback stream:%d\n", __func__, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!pcm->playback_start)
+ pcm->playback_start = 1;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!pcm->capture_start)
+ pcm->capture_start = 1;
+ }
+ mutex_unlock(&pcm->lock);
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_pcm *pcm = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: playback_start:%d,capture_start:%d\n", __func__,
+ pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
+ __func__, pcm->playback_start, pcm->capture_start);
+ if (pcm->playback_start && pcm->capture_start)
+ q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+
+ pr_debug("%s: ASM loopback\n", __func__);
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int msm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .hw_free = msm_pcm_hw_free,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+};
+
+
+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_debug("%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-loopback",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ memset(&pcm_info, 0, sizeof(struct msm_pcm));
+ mutex_init(&pcm_info.lock);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ mutex_destroy(&pcm_info.lock);
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("ASM loopback module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 6cf74d5..aaae373 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1608,6 +1608,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_SCO_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
@@ -1644,6 +1647,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
@@ -1662,6 +1668,9 @@
SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
@@ -1758,6 +1767,12 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 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,
@@ -2549,6 +2564,7 @@
SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 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_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
@@ -2656,6 +2672,8 @@
mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul6_mixer_controls, ARRAY_SIZE(mmul6_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)),
SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -2830,6 +2848,7 @@
{"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
@@ -2848,6 +2867,7 @@
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
{"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2862,12 +2882,14 @@
{"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
{"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"},
@@ -2876,12 +2898,14 @@
{"MM_UL2", NULL, "MultiMedia2 Mixer"},
{"MM_UL4", NULL, "MultiMedia4 Mixer"},
{"MM_UL5", NULL, "MultiMedia5 Mixer"},
+ {"MM_UL6", NULL, "MultiMedia6 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 Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index b571483..202e7ea 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -145,6 +145,8 @@
int lpa_set_volume(unsigned volume);
+int msm_set_lb_volume(unsigned volume);
+
int msm_routing_check_backend_enabled(int fedai_id);
int multi_ch_pcm_set_volume(unsigned volume);
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 8fd5840..8ec2e94 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -912,6 +912,7 @@
case ASM_STREAM_CMD_OPEN_WRITE:
case ASM_STREAM_CMD_OPEN_WRITE_V2_1:
case ASM_STREAM_CMD_OPEN_READWRITE:
+ case ASM_STREAM_CMD_OPEN_LOOPBACK:
case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
@@ -1848,6 +1849,45 @@
return -EINVAL;
}
+int q6asm_open_loopack(struct audio_client *ac)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_loopback open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK;
+
+ open.mode_flags = 0;
+ open.src_endpointype = 0;
+ open.sink_endpointype = 0;
+ /* source endpoint : matrix */
+ open.postprocopo_id = get_asm_topology();
+ if (open.postprocopo_id == 0)
+ open.postprocopo_id = DEFAULT_POPP_TOPOLOGY;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for OPEN_LOOPBACK rc[%d]\n", rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
int q6asm_run(struct audio_client *ac, uint32_t flags,
uint32_t msw_ts, uint32_t lsw_ts)
{