Merge "msm: mdm: Fix error value for SHUTDOWN_CHARM ioctl"
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 213da90..c47d442 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -72,6 +72,11 @@
- compatible : "msm-dai-q6"
+Optional properties:
+
+ - qcom,ext-spk-amp-supply : External speaker amplifier power supply.
+ - qcom,ext-spk-amp-gpio : External speaker amplifier enable signal.
+
[Second Level Nodes]
Required properties:
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
index 9514e5a..9b5c9b1 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -193,6 +193,14 @@
interrupts = <0 168 1>;
qcom,irq-no-suspend;
};
+
+ qcom,wdt@f9017000 {
+ compatible = "qcom,msm-watchdog";
+ reg = <0xf9017000 0x1000>;
+ interrupts = <0 3 0>, <0 4 0>;
+ qcom,bark-time = <11000>;
+ qcom,pet-time = <10000>;
+ qcom,ipi-ping = <1>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-rumi.dtsi b/arch/arm/boot/dts/msm8974-rumi.dtsi
index c9b022d..4919391 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dtsi
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -107,4 +107,36 @@
qcom,mss@fc880000 {
status = "disable";
};
+
+ qcom,kgsl-3d0@fdb00000 {
+ status = "disabled";
+ };
+};
+
+&gdsc_venus {
+ status = "disabled";
+};
+
+&gdsc_mdss {
+ status = "disabled";
+};
+
+&gdsc_jpeg {
+ status = "disabled";
+};
+
+&gdsc_vfe {
+ status = "disabled";
+};
+
+&gdsc_oxili_gx {
+ status = "disabled";
+};
+
+&gdsc_oxili_cx {
+ status = "disabled";
+};
+
+&gdsc_usb_hsic {
+ status = "disabled";
};
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index 2dd4b30..72c2969 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -99,6 +99,9 @@
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_MSM_QPNP_INT=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 42e250d..90aed03 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1538,6 +1538,16 @@
help
SMD Transport Layer for IPC Router
+config MSM_IPC_ROUTER_SECURITY
+ depends on MSM_IPC_ROUTER
+ bool "MSM IPC Router Security support"
+ help
+ This feature of IPC Router will enforce security rules
+ configured by a security script from the user-space. IPC Router
+ once configured with the security rules will ensure that the
+ sender of the message to a service belongs to the relevant
+ Linux group as configured by the security script.
+
config MSM_QMI_INTERFACE
depends on MSM_IPC_ROUTER
depends on QMI_ENCDEC
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 2c7424e..548f40e 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -138,6 +138,7 @@
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
obj-$(CONFIG_MSM_IPC_ROUTER) += ipc_router.o
obj-$(CONFIG_MSM_IPC_ROUTER)+= ipc_socket.o
+obj-$(CONFIG_MSM_IPC_ROUTER_SECURITY)+= msm_ipc_router_security.o
obj-$(CONFIG_MSM_QMI_INTERFACE) += msm_qmi_interface.o
obj-$(CONFIG_DEBUG_FS) += smd_rpc_sym.o
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 89ad4ef..ad74182 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -16,6 +16,7 @@
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/gpiomux.h>
+#include <mach/socinfo.h>
#define KS8851_IRQ_GPIO 94
@@ -249,6 +250,21 @@
},
};
+static struct msm_gpiomux_config msm_rumi_blsp_configs[] __initdata = {
+ {
+ .gpio = 45, /* BLSP2 UART8 TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_uart_config,
+ },
+ },
+ {
+ .gpio = 46, /* BLSP2 UART8 RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_uart_config,
+ },
+ },
+};
+
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
{
@@ -624,4 +640,8 @@
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
msm_gpiomux_install(msm_mhl_configs, ARRAY_SIZE(msm_mhl_configs));
+
+ if (machine_is_msm8974_rumi())
+ msm_gpiomux_install(msm_rumi_blsp_configs,
+ ARRAY_SIZE(msm_rumi_blsp_configs));
}
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index c47b688..ca8f68a 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -304,8 +304,6 @@
"usb_bam", NULL),
OF_DEV_AUXDATA("qcom,spi-qup-v2", 0xF9924000, \
"spi_qsd.1", NULL),
- OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
- "spmi-pmic-arb.0", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
"msm_sdcc.1", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
diff --git a/arch/arm/mach-msm/board-msm7627a-io.c b/arch/arm/mach-msm/board-msm7627a-io.c
index 2983dc0..8c52456 100644
--- a/arch/arm/mach-msm/board-msm7627a-io.c
+++ b/arch/arm/mach-msm/board-msm7627a-io.c
@@ -727,6 +727,15 @@
[KP_INDEX_SKU3(1, 1)] = KEY_CAMERA,
};
+static unsigned int kp_row_gpios_skud[] = {31, 32};
+static unsigned int kp_col_gpios_skud[] = {37};
+
+static const unsigned short keymap_skud[] = {
+ [KP_INDEX_SKU3(0, 0)] = KEY_VOLUMEUP,
+ [KP_INDEX_SKU3(0, 1)] = KEY_VOLUMEDOWN,
+};
+
+
static struct gpio_event_matrix_info kp_matrix_info_sku3 = {
.info.func = gpio_event_matrix_func,
.keymap = keymap_sku3,
@@ -887,13 +896,23 @@
#endif
/* keypad */
+
+ if (machine_is_qrd_skud_prime()) {
+ kp_matrix_info_sku3.keymap = keymap_skud;
+ kp_matrix_info_sku3.output_gpios = kp_row_gpios_skud;
+ kp_matrix_info_sku3.input_gpios = kp_col_gpios_skud;
+ kp_matrix_info_sku3.noutputs = ARRAY_SIZE(kp_row_gpios_skud);
+ kp_matrix_info_sku3.ninputs = ARRAY_SIZE(kp_col_gpios_skud);
+ }
+
if (machine_is_msm8625_evt())
kp_matrix_info_8625.keymap = keymap_8625_evt;
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
machine_is_msm8625_evt())
platform_device_register(&kp_pdev_8625);
- else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7())
+ else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
platform_device_register(&kp_pdev_sku3);
/* leds */
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 68bffa5..216de11 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -522,6 +522,14 @@
#define dsipll0_pixel_mm_source_val 1
#define hdmipll_mm_source_val 3
+#define F_GCC_GND \
+ { \
+ .freq_hz = 0, \
+ .m_val = 0, \
+ .n_val = 0, \
+ .div_src_val = BVAL(4, 0, 1) | BVAL(10, 8, gnd_source_val), \
+ }
+
#define F(f, s, div, m, n) \
{ \
.freq_hz = (f), \
@@ -914,6 +922,7 @@
};
static struct clk_freq_tbl ftbl_gcc_blsp1_2_uart1_6_apps_clk[] = {
+ F_GCC_GND,
F( 3686400, gpll0, 1, 96, 15625),
F( 7372800, gpll0, 1, 192, 15625),
F(14745600, gpll0, 1, 384, 15625),
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index b284168..d3a4bba 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -246,6 +246,14 @@
#define lpapll0_lpass_source_val 1
#define gpll0_lpass_source_val 5
+#define F_GCC_GND \
+ { \
+ .freq_hz = 0, \
+ .m_val = 0, \
+ .n_val = 0, \
+ .div_src_val = BVAL(4, 0, 1) | BVAL(10, 8, gnd_source_val), \
+ }
+
#define F(f, s, div, m, n) \
{ \
.freq_hz = (f), \
@@ -611,6 +619,7 @@
};
static struct clk_freq_tbl ftbl_gcc_blsp1_uart1_6_apps_clk[] = {
+ F_GCC_GND,
F( 3686400, gpll0, 1, 96, 15625),
F( 7372800, gpll0, 1, 192, 15625),
F(14745600, gpll0, 1, 384, 15625),
diff --git a/arch/arm/mach-msm/include/mach/irqs-8092.h b/arch/arm/mach-msm/include/mach/irqs-8092.h
index ae9634e..dfe21c2 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8092.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8092.h
@@ -34,12 +34,5 @@
#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105)
-#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
-#define NR_GPIO_IRQS 146
-#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
-#define NR_BOARD_IRQS NR_QPNP_IRQS
-#define NR_TLMM_MSM_DIR_CONN_IRQ 8
-#define NR_MSM_GPIOS NR_GPIO_IRQS
-
#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs-8226.h b/arch/arm/mach-msm/include/mach/irqs-8226.h
index 72602b1..abc62d2 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8226.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8226.h
@@ -31,11 +31,4 @@
#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ
#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
-#define NR_MSM_IRQS 256
-#define NR_GPIO_IRQS 117
-#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
-#define NR_BOARD_IRQS NR_QPNP_IRQS
-#define NR_TLMM_MSM_DIR_CONN_IRQ 8
-#define NR_MSM_GPIOS NR_GPIO_IRQS
-
#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs-8910.h b/arch/arm/mach-msm/include/mach/irqs-8910.h
index e883214..635c044 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8910.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8910.h
@@ -30,11 +30,4 @@
#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ
#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
-#define NR_MSM_IRQS 256
-#define NR_GPIO_IRQS 102
-#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
-#define NR_BOARD_IRQS NR_QPNP_IRQS
-#define NR_TLMM_MSM_DIR_CONN_IRQ 8
-#define NR_MSM_GPIOS NR_GPIO_IRQS
-
#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs-8974.h b/arch/arm/mach-msm/include/mach/irqs-8974.h
index 8152eca..150b2ee 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8974.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8974.h
@@ -32,12 +32,5 @@
#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105)
-#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
-#define NR_GPIO_IRQS 146
-#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
-#define NR_BOARD_IRQS NR_QPNP_IRQS
-#define NR_TLMM_MSM_DIR_CONN_IRQ 8
-#define NR_MSM_GPIOS NR_GPIO_IRQS
-
#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index 8d96192..7837c79 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -19,7 +19,40 @@
#define MSM_IRQ_BIT(irq) (1 << ((irq) & 31))
-#if defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \
+#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MPQ8092)
+
+#ifdef CONFIG_ARCH_MSM8974
+#include "irqs-8974.h"
+#endif
+
+#ifdef CONFIG_ARCH_MPQ8092
+#include "irqs-8092.h"
+#endif
+
+#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
+#define NR_GPIO_IRQS 146
+#define NR_QPNP_IRQS 32768
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#elif defined(CONFIG_ARCH_MSM8910) || defined(CONFIG_ARCH_MSM8226)
+#ifdef CONFIG_ARCH_MSM8910
+#include "irqs-8910.h"
+#endif
+
+#ifdef CONFIG_ARCH_MSM8226
+#include "irqs-8226.h"
+#endif
+
+#define NR_MSM_IRQS 256
+#define NR_GPIO_IRQS 117
+#define NR_QPNP_IRQS 32768
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#elif defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \
defined(CONFIG_ARCH_MSM8930)
#ifdef CONFIG_ARCH_MSM8960
@@ -56,18 +89,10 @@
#else
-#if defined(CONFIG_ARCH_MSM8974)
-#include "irqs-8974.h"
-#elif defined(CONFIG_ARCH_MSM8910)
-#include "irqs-8910.h"
-#elif defined(CONFIG_ARCH_MPQ8092)
-#include "irqs-8092.h"
-#elif defined(CONFIG_ARCH_MSM9615)
+#if defined(CONFIG_ARCH_MSM9615)
#include "irqs-9615.h"
#elif defined(CONFIG_ARCH_MSM9625)
#include "irqs-9625.h"
-#elif defined(CONFIG_ARCH_MSM8226)
-#include "irqs-8226.h"
#elif defined(CONFIG_ARCH_MSM7X30)
#include "irqs-7x30.h"
#elif defined(CONFIG_ARCH_QSD8X50)
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_router.h b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
index 45a7e19..c68c783 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_router.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
@@ -55,6 +55,7 @@
void *endpoint;
void (*notify)(unsigned event, void *priv);
+ int (*check_send_permissions)(void *data);
uint32_t num_tx;
uint32_t num_rx;
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 8f1d197..fde43b0 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -37,6 +37,7 @@
#include "ipc_router.h"
#include "modem_notifier.h"
+#include "msm_ipc_router_security.h"
enum {
SMEM_LOG = 1U << 0,
@@ -114,6 +115,7 @@
struct msm_ipc_port_name name;
char pdev_name[32];
int next_pdev_id;
+ int synced_sec_rule;
struct list_head server_port_list;
};
@@ -133,6 +135,7 @@
wait_queue_head_t quota_wait;
uint32_t tx_quota_cnt;
struct mutex quota_lock;
+ void *sec_rule;
};
struct msm_ipc_router_xprt_info {
@@ -651,6 +654,7 @@
rport_ptr->port_id = port_id;
rport_ptr->node_id = node_id;
rport_ptr->restart_state = RESTART_NORMAL;
+ rport_ptr->sec_rule = NULL;
rport_ptr->tx_quota_cnt = 0;
init_waitqueue_head(&rport_ptr->quota_wait);
mutex_init(&rport_ptr->quota_lock);
@@ -770,6 +774,7 @@
}
server->name.service = service;
server->name.instance = instance;
+ server->synced_sec_rule = 0;
INIT_LIST_HEAD(&server->server_port_list);
list_add_tail(&server->list, &server_list[key]);
scnprintf(server->pdev_name, sizeof(server->pdev_name),
@@ -1312,6 +1317,95 @@
msm_ipc_cleanup_routing_table(xprt_info);
}
+/**
+ * sync_sec_rule() - Synchrnoize the security rule into the server structure
+ * @server: Server structure where the rule has to be synchronized.
+ * @rule: Security tule to be synchronized.
+ *
+ * This function is used to update the server structure with the security
+ * rule configured for the <service:instance> corresponding to that server.
+ */
+static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
+{
+ struct msm_ipc_server_port *server_port;
+ struct msm_ipc_router_remote_port *rport_ptr = NULL;
+
+ list_for_each_entry(server_port, &server->server_port_list, list) {
+ rport_ptr = msm_ipc_router_lookup_remote_port(
+ server_port->server_addr.node_id,
+ server_port->server_addr.port_id);
+ if (!rport_ptr)
+ continue;
+ rport_ptr->sec_rule = rule;
+ }
+ server->synced_sec_rule = 1;
+}
+
+/**
+ * msm_ipc_sync_sec_rule() - Sync the security rule to the service
+ * @service: Service for which the rule has to be synchronized.
+ * @instance: Instance for which the rule has to be synchronized.
+ * @rule: Security rule to be synchronized.
+ *
+ * This function is used to syncrhonize the security rule with the server
+ * hash table, if the user-space script configures the rule after the service
+ * has come up. This function is used to synchronize the security rule to a
+ * specific service and optionally a specific instance.
+ */
+void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
+{
+ int key = (service & (SRV_HASH_SIZE - 1));
+ struct msm_ipc_server *server;
+
+ mutex_lock(&server_list_lock);
+ list_for_each_entry(server, &server_list[key], list) {
+ if (server->name.service != service)
+ continue;
+
+ if (server->name.instance != instance &&
+ instance != ALL_INSTANCE)
+ continue;
+
+ /*
+ * If the rule applies to all instances and if the specific
+ * instance of a service has a rule synchronized already,
+ * do not apply the rule for that specific instance.
+ */
+ if (instance == ALL_INSTANCE && server->synced_sec_rule)
+ continue;
+
+ sync_sec_rule(server, rule);
+ }
+ mutex_unlock(&server_list_lock);
+}
+
+/**
+ * msm_ipc_sync_default_sec_rule() - Default security rule to all services
+ * @rule: Security rule to be synchronized.
+ *
+ * This function is used to syncrhonize the security rule with the server
+ * hash table, if the user-space script configures the rule after the service
+ * has come up. This function is used to synchronize the security rule that
+ * applies to all services, if the concerned service do not have any rule
+ * defined.
+ */
+void msm_ipc_sync_default_sec_rule(void *rule)
+{
+ int key;
+ struct msm_ipc_server *server;
+
+ mutex_lock(&server_list_lock);
+ for (key = 0; key < SRV_HASH_SIZE; key++) {
+ list_for_each_entry(server, &server_list[key], list) {
+ if (server->synced_sec_rule)
+ continue;
+
+ sync_sec_rule(server, rule);
+ }
+ }
+ mutex_unlock(&server_list_lock);
+}
+
static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
struct rr_header *hdr)
{
@@ -1491,6 +1585,9 @@
if (!rport_ptr)
pr_err("%s: Remote port create "
"failed\n", __func__);
+ rport_ptr->sec_rule =
+ msm_ipc_get_security_rule(
+ msg->srv.service, msg->srv.instance);
}
wake_up(&newserver_wait);
}
@@ -1989,6 +2086,15 @@
return -ENOMEM;
}
+ if (src->check_send_permissions) {
+ ret = src->check_send_permissions(rport_ptr->sec_rule);
+ if (ret <= 0) {
+ pr_err("%s: permission failure for %s\n",
+ __func__, current->comm);
+ return -EPERM;
+ }
+ }
+
pkt = create_pkt(data);
if (!pkt) {
pr_err("%s: Pkt creation failed\n", __func__);
@@ -2781,6 +2887,10 @@
if (ret < 0)
pr_err("%s: Init sockets failed\n", __func__);
+ ret = msm_ipc_router_security_init();
+ if (ret < 0)
+ pr_err("%s: Security Init failed\n", __func__);
+
complete_all(&msm_ipc_local_router_up);
return ret;
}
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index 39038f2..39bde30 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -52,6 +52,9 @@
#define ALIGN_SIZE(x) ((4 - ((x) & 3)) & 3)
+#define ALL_SERVICE 0xFFFFFFFF
+#define ALL_INSTANCE 0xFFFFFFFF
+
union rr_control_msg {
uint32_t cmd;
struct {
@@ -139,6 +142,10 @@
int msm_ipc_router_init_sockets(void);
void msm_ipc_router_exit_sockets(void);
+void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule);
+
+void msm_ipc_sync_default_sec_rule(void *rule);
+
#if defined CONFIG_MSM_IPC_ROUTER_SMD_XPRT
extern void *msm_ipc_load_default_node(void);
diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index 3a6abbd..5d21fa5 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -21,10 +21,6 @@
#include <linux/gfp.h>
#include <linux/msm_ipc.h>
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-#include <linux/android_aid.h>
-#endif
-
#include <asm/string.h>
#include <asm/atomic.h>
@@ -33,6 +29,7 @@
#include <mach/msm_ipc_router.h>
#include "ipc_router.h"
+#include "msm_ipc_router_security.h"
#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
@@ -41,21 +38,6 @@
static struct proto msm_ipc_proto;
static const struct proto_ops msm_ipc_proto_ops;
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-static inline int check_permissions(void)
-{
- int rc = 0;
- if (!current_euid() || in_egroup_p(AID_NET_RAW))
- rc = 1;
- return rc;
-}
-# else
-static inline int check_permissions(void)
-{
- return 1;
-}
-#endif
-
static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
struct iovec const *msg_sect,
size_t total_len)
@@ -193,7 +175,8 @@
void *pil;
if (!check_permissions()) {
- pr_err("%s: Do not have permissions\n", __func__);
+ pr_err("%s: %s Do not have permissions\n",
+ __func__, current->comm);
return -EPERM;
}
@@ -223,6 +206,7 @@
return -ENOMEM;
}
+ port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
sock->ops = &msm_ipc_proto_ops;
sock_init_data(sock, sk);
sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
@@ -445,6 +429,10 @@
ret = msm_ipc_router_bind_control_port(port_ptr);
break;
+ case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
+ ret = msm_ipc_config_sec_rules((void *)arg);
+ break;
+
default:
ret = -EINVAL;
}
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 41afd24..3b9656e 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -65,6 +65,7 @@
struct kobj_attribute thermal_poll_ms;
struct kobj_attribute freq_tbl;
+ struct kobj_attribute offset_tbl;
struct attribute_group attrib_group;
};
@@ -295,9 +296,14 @@
continue;
if (gpu->pending_freq != STOP_FREQ_CHANGE &&
- gpu->set_floor_frequency)
+ gpu->set_floor_frequency) {
gpu->set_floor_frequency(gpu->type_core_num,
gpu_floor_freq);
+ /* TZ will know about a freq change (if any)
+ * at next idle exit. */
+ gpu->actual_freq =
+ gpu->get_frequency(gpu->type_core_num);
+ }
}
mutex_unlock(&gpu_floor_mutex);
@@ -715,6 +721,77 @@
DCVS_PARAM_STORE(thermal_poll_ms)
+static ssize_t msm_dcvs_attr_offset_tbl_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ struct msm_dcvs_freq_entry *freq_tbl;
+ char *buf_idx = buf;
+ int i, len;
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
+
+ freq_tbl = core->info->freq_tbl;
+ *buf_idx = '\0';
+
+ /* limit the number of frequencies we will print into
+ * the PAGE_SIZE sysfs show buffer. */
+ if (core->info->power_param.num_freq > 64)
+ return 0;
+
+ for (i = 0; i < core->info->power_param.num_freq; i++) {
+ len = snprintf(buf_idx, 30, "%7d %7d %7d\n",
+ freq_tbl[i].freq,
+ freq_tbl[i].active_energy_offset,
+ freq_tbl[i].leakage_energy_offset);
+ /* buf_idx always points at terminating null */
+ buf_idx += len;
+ }
+ return buf_idx - buf;
+}
+
+static ssize_t msm_dcvs_attr_offset_tbl_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct msm_dcvs_freq_entry *freq_tbl;
+ uint32_t freq, active_energy_offset, leakage_energy_offset;
+ int i, ret;
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
+
+ freq_tbl = core->info->freq_tbl;
+
+ ret = sscanf(buf, "%u %u %u",
+ &freq, &active_energy_offset, &leakage_energy_offset);
+ if (ret != 3) {
+ __err("Invalid input %s for offset_tbl\n", buf);
+ return count;
+ }
+
+ for (i = 0; i < core->info->power_param.num_freq; i++)
+ if (freq_tbl[i].freq == freq) {
+ freq_tbl[i].active_energy_offset =
+ active_energy_offset;
+ freq_tbl[i].leakage_energy_offset =
+ leakage_energy_offset;
+ break;
+ }
+
+ if (i >= core->info->power_param.num_freq) {
+ __err("Invalid frequency for offset_tbl: %d\n", freq);
+ return count;
+ }
+
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+ &core->info->power_param,
+ &core->info->freq_tbl[0],
+ &core->coeffs);
+ if (ret)
+ __err("Error %d in updating active/leakage energy\n", ret);
+
+ return count;
+}
+
static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
@@ -791,7 +868,7 @@
{
int ret = 0;
struct kobject *core_kobj = NULL;
- const int attr_count = 25;
+ const int attr_count = 26;
BUG_ON(!cores_kobj);
@@ -830,8 +907,9 @@
DCVS_RW_ATTRIB(22, thermal_poll_ms);
DCVS_RW_ATTRIB(23, freq_tbl);
+ DCVS_RW_ATTRIB(24, offset_tbl);
- core->attrib.attrib_group.attrs[24] = NULL;
+ core->attrib.attrib_group.attrs[25] = NULL;
core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
if (!core_kobj) {
@@ -919,27 +997,6 @@
num_cpu_freqs++;
}
-static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
-{
- int i;
-
- BUG_ON(num_cpu_freqs == 0);
-
- info->freq_tbl = cpu_freq_tbl;
- info->power_param.num_freq = num_cpu_freqs;
-
- if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
- return;
-
- /* the first sync rule shows what the turbo frequencies are -
- * these frequencies need energy offsets set */
- for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
- if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
- cpu_freq_tbl[i].active_energy_offset = 100;
- cpu_freq_tbl[i].leakage_energy_offset = 100;
- }
-}
-
int msm_dcvs_register_core(
enum msm_dcvs_core_type type,
int type_core_num,
@@ -975,8 +1032,11 @@
core->set_floor_frequency = set_floor_frequency;
core->info = info;
- if (type == MSM_DCVS_CORE_TYPE_CPU)
- update_cpu_dcvs_params(info);
+ if (type == MSM_DCVS_CORE_TYPE_CPU) {
+ BUG_ON(num_cpu_freqs == 0);
+ info->freq_tbl = cpu_freq_tbl;
+ info->power_param.num_freq = num_cpu_freqs;
+ }
memcpy(&core->algo_param, &info->algo_param,
sizeof(struct msm_dcvs_algo_param));
diff --git a/arch/arm/mach-msm/msm_ipc_router_security.c b/arch/arm/mach-msm/msm_ipc_router_security.c
new file mode 100644
index 0000000..27cf524
--- /dev/null
+++ b/arch/arm/mach-msm/msm_ipc_router_security.c
@@ -0,0 +1,271 @@
+/* Copyright (c) 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
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/gfp.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/msm_ipc.h>
+
+#include <asm/uaccess.h>
+
+#include <net/sock.h>
+#include "ipc_router.h"
+#include "msm_ipc_router_security.h"
+
+#define SEC_RULES_HASH_SZ 32
+struct security_rule {
+ struct list_head list;
+ uint32_t service_id;
+ uint32_t instance_id;
+ unsigned reserved;
+ int num_group_info;
+ int *group_id;
+};
+
+static DEFINE_MUTEX(security_rules_lock);
+static struct list_head security_rules[SEC_RULES_HASH_SZ];
+
+/**
+ * check_permisions() - Check whether the process has permissions to
+ * create an interface handle with IPC Router
+ *
+ * @return: true if the process has permissions, else false.
+ */
+int check_permissions(void)
+{
+ int rc = 0;
+ if (!current_euid() || in_egroup_p(AID_NET_RAW))
+ rc = 1;
+ return rc;
+}
+EXPORT_SYMBOL(check_permissions);
+
+/**
+ * msm_ipc_config_sec_rules() - Add a security rule to the database
+ * @arg: Pointer to the buffer containing the rule.
+ *
+ * @return: 0 if successfully added, < 0 for error.
+ *
+ * A security rule is defined using <Service_ID: Group_ID> tuple. The rule
+ * implies that a user-space process in order to send a QMI message to
+ * service Service_ID should belong to the Linux group Group_ID.
+ */
+int msm_ipc_config_sec_rules(void *arg)
+{
+ struct config_sec_rules_args sec_rules_arg;
+ struct security_rule *rule, *temp_rule;
+ int key;
+ int ret;
+
+ if (current_euid())
+ return -EPERM;
+
+ ret = copy_from_user(&sec_rules_arg, (void *)arg,
+ sizeof(sec_rules_arg));
+ if (ret)
+ return -EFAULT;
+
+ if (sec_rules_arg.num_group_info <= 0)
+ return -EINVAL;
+
+ rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL);
+ if (!rule) {
+ pr_err("%s: security_rule alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ rule->group_id = kzalloc((sec_rules_arg.num_group_info * sizeof(int)),
+ GFP_KERNEL);
+ if (!rule->group_id) {
+ pr_err("%s: group_id alloc failed\n", __func__);
+ kfree(rule);
+ return -ENOMEM;
+ }
+
+ rule->service_id = sec_rules_arg.service_id;
+ rule->instance_id = sec_rules_arg.instance_id;
+ rule->reserved = sec_rules_arg.reserved;
+ rule->num_group_info = sec_rules_arg.num_group_info;
+ ret = copy_from_user(rule->group_id,
+ ((void *)(arg + sizeof(sec_rules_arg))),
+ (rule->num_group_info * sizeof(uint32_t)));
+ if (ret) {
+ kfree(rule->group_id);
+ kfree(rule);
+ return -EFAULT;
+ }
+
+ key = rule->service_id & (SEC_RULES_HASH_SZ - 1);
+ mutex_lock(&security_rules_lock);
+ if (rule->service_id == ALL_SERVICE) {
+ temp_rule = list_first_entry(&security_rules[key],
+ struct security_rule, list);
+ list_del(&temp_rule->list);
+ kfree(temp_rule->group_id);
+ kfree(temp_rule);
+ }
+ list_add_tail(&rule->list, &security_rules[key]);
+ mutex_unlock(&security_rules_lock);
+
+ if (rule->service_id == ALL_SERVICE)
+ msm_ipc_sync_default_sec_rule((void *)rule);
+ else
+ msm_ipc_sync_sec_rule(rule->service_id, rule->instance_id,
+ (void *)rule);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ipc_config_sec_rules);
+
+/**
+ * msm_ipc_add_default_rule() - Add default security rule
+ *
+ * @return: 0 on success, < 0 on error/
+ *
+ * This function is used to ensure the basic security, if there is no
+ * security rule defined for a service. It can be overwritten by the
+ * default security rule from user-space script.
+ */
+static int msm_ipc_add_default_rule(void)
+{
+ struct security_rule *rule;
+ int key;
+
+ rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL);
+ if (!rule) {
+ pr_err("%s: security_rule alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ rule->group_id = kzalloc(sizeof(int), GFP_KERNEL);
+ if (!rule->group_id) {
+ pr_err("%s: group_id alloc failed\n", __func__);
+ kfree(rule);
+ return -ENOMEM;
+ }
+
+ rule->service_id = ALL_SERVICE;
+ rule->instance_id = ALL_INSTANCE;
+ rule->num_group_info = 1;
+ *(rule->group_id) = AID_NET_RAW;
+ mutex_lock(&security_rules_lock);
+ key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
+ list_add_tail(&rule->list, &security_rules[key]);
+ mutex_unlock(&security_rules_lock);
+ return 0;
+}
+
+/**
+ * msm_ipc_get_security_rule() - Get the security rule corresponding to a
+ * service
+ * @service_id: Service ID for which the rule has to be got.
+ * @instance_id: Instance ID for which the rule has to be got.
+ *
+ * @return: Returns the rule info on success, NULL on error.
+ *
+ * This function is used when the service comes up and gets registered with
+ * the IPC Router.
+ */
+void *msm_ipc_get_security_rule(uint32_t service_id, uint32_t instance_id)
+{
+ int key;
+ struct security_rule *rule;
+
+ key = (service_id & (SEC_RULES_HASH_SZ - 1));
+ mutex_lock(&security_rules_lock);
+ /* Return the rule for a specific <service:instance>, if found. */
+ list_for_each_entry(rule, &security_rules[key], list) {
+ if ((rule->service_id == service_id) &&
+ (rule->instance_id == instance_id)) {
+ mutex_unlock(&security_rules_lock);
+ return (void *)rule;
+ }
+ }
+
+ /* Return the rule for a specific service, if found. */
+ list_for_each_entry(rule, &security_rules[key], list) {
+ if ((rule->service_id == service_id) &&
+ (rule->instance_id == ALL_INSTANCE)) {
+ mutex_unlock(&security_rules_lock);
+ return (void *)rule;
+ }
+ }
+
+ /* Return the default rule, if no rule defined for a service. */
+ key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
+ list_for_each_entry(rule, &security_rules[key], list) {
+ if ((rule->service_id == ALL_SERVICE) &&
+ (rule->instance_id == ALL_INSTANCE)) {
+ mutex_unlock(&security_rules_lock);
+ return (void *)rule;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(msm_ipc_get_security_rule);
+
+/**
+ * msm_ipc_check_send_permissions() - Check if the sendng process has
+ * permissions specified as per the rule
+ * @data: Security rule to be checked.
+ *
+ * @return: true if the process has permissions, else false.
+ *
+ * This function is used to check if the current executing process has
+ * permissions to send message to the remote entity. The security rule
+ * corresponding to the remote entity is specified by "data" parameter
+ */
+int msm_ipc_check_send_permissions(void *data)
+{
+ int i;
+ struct security_rule *rule = (struct security_rule *)data;
+
+ /* Source/Sender is Root user */
+ if (!current_euid())
+ return 1;
+
+ /* Destination has no rules defined, possibly a client. */
+ if (!rule)
+ return 1;
+
+ for (i = 0; i < rule->num_group_info; i++) {
+ if (in_egroup_p(rule->group_id[i]))
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(msm_ipc_check_send_permissions);
+
+/**
+ * msm_ipc_router_security_init() - Initialize the security rule database
+ *
+ * @return: 0 if successful, < 0 for error.
+ */
+int msm_ipc_router_security_init(void)
+{
+ int i;
+
+ for (i = 0; i < SEC_RULES_HASH_SZ; i++)
+ INIT_LIST_HEAD(&security_rules[i]);
+
+ msm_ipc_add_default_rule();
+ return 0;
+}
+EXPORT_SYMBOL(msm_ipc_router_security_init);
diff --git a/arch/arm/mach-msm/msm_ipc_router_security.h b/arch/arm/mach-msm/msm_ipc_router_security.h
new file mode 100644
index 0000000..8701343
--- /dev/null
+++ b/arch/arm/mach-msm/msm_ipc_router_security.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_IPC_ROUTER_SECURITY_H
+#define _MSM_IPC_ROUTER_SECURITY_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+
+#ifdef CONFIG_MSM_IPC_ROUTER_SECURITY
+#include <linux/android_aid.h>
+
+/**
+ * check_permisions() - Check whether the process has permissions to
+ * create an interface handle with IPC Router
+ *
+ * @return: true if the process has permissions, else false.
+ */
+int check_permissions(void);
+
+/**
+ * msm_ipc_config_sec_rules() - Add a security rule to the database
+ * @arg: Pointer to the buffer containing the rule.
+ *
+ * @return: 0 if successfully added, < 0 for error.
+ *
+ * A security rule is defined using <Service_ID: Group_ID> tuple. The rule
+ * implies that a user-space process in order to send a QMI message to
+ * service Service_ID should belong to the Linux group Group_ID.
+ */
+int msm_ipc_config_sec_rules(void *arg);
+
+/**
+ * msm_ipc_get_security_rule() - Get the security rule corresponding to a
+ * service
+ * @service_id: Service ID for which the rule has to be got.
+ * @instance_id: Instance ID for which the rule has to be got.
+ *
+ * @return: Returns the rule info on success, NULL on error.
+ *
+ * This function is used when the service comes up and gets registered with
+ * the IPC Router.
+ */
+void *msm_ipc_get_security_rule(uint32_t service_id, uint32_t instance_id);
+
+/**
+ * msm_ipc_check_send_permissions() - Check if the sendng process has
+ * permissions specified as per the rule
+ * @data: Security rule to be checked.
+ *
+ * @return: true if the process has permissions, else false.
+ *
+ * This function is used to check if the current executing process has
+ * permissions to send message to the remote entity. The security rule
+ * corresponding to the remote entity is specified by "data" parameter
+ */
+int msm_ipc_check_send_permissions(void *data);
+
+/**
+ * msm_ipc_router_security_init() - Initialize the security rule database
+ *
+ * @return: 0 if successful, < 0 for error.
+ */
+int msm_ipc_router_security_init(void);
+
+#else
+
+static inline int check_permissions(void)
+{
+ return 1;
+}
+
+static inline int msm_ipc_config_sec_rules(void *arg)
+{
+ return -ENODEV;
+}
+
+static inline void *msm_ipc_get_security_rule(uint32_t service_id,
+ uint32_t instance_id)
+{
+ return NULL;
+}
+
+static inline int msm_ipc_check_send_permissions(void *data)
+{
+ return 1;
+}
+
+static inline int msm_ipc_router_security_init(void)
+{
+ return 0;
+}
+#endif
+#endif
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 65e05a9..e3a3563 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -382,6 +382,7 @@
if (priv->region)
ion_free(ion, priv->region);
+ priv->region = NULL;
list_for_each_entry_safe(p, tmp, &priv->segs, list) {
list_del(&p->list);
kfree(p);
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index 7993090..74fae98 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -542,7 +542,7 @@
}
ret = devm_request_irq(&pdev->dev, drv->irq, riva_wdog_bite_irq_hdlr,
- IRQF_TRIGGER_HIGH, "riva_wdog", drv);
+ IRQF_TRIGGER_RISING, "riva_wdog", drv);
if (ret < 0)
goto err;
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 0933d20..b1d2464 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -175,6 +175,9 @@
{
BUG_ON(cpu >= get_core_count());
+ if (machine_is_msm8974_rumi())
+ return 0;
+
if (cpu_is_msm8x60())
return scorpion_release_secondary();
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index fb0ace7..a4a6b906 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -1142,6 +1142,11 @@
mutex_unlock(&audio->lock);
break;
}
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+ __func__, audio);
+ wake_up(&audio->write_wait);
+ }
mutex_unlock(&audio->lock);
break;
}
@@ -1178,6 +1183,11 @@
mutex_lock(&audio->lock);
audio->rflush = 1;
audio->wflush = 1;
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+ __func__, audio);
+ wake_up(&audio->write_wait);
+ }
/* Flush DSP */
rc = audio_aio_flush(audio);
/* Flush input / Output buffer in software*/
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index e78a2aa..24fc99a 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -38,6 +38,8 @@
struct mutex dci_log_mask_mutex;
struct mutex dci_event_mask_mutex;
+smd_channel_t *ch_dci_temp;
+
#define DCI_CHK_CAPACITY(entry, new_data_len) \
((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
@@ -306,28 +308,100 @@
diag_smd_dci_send_req(MODEM_PROC);
}
-static void diag_smd_dci_notify(void *ctxt, unsigned event)
+void diag_update_smd_dci_work_fn(struct work_struct *work)
{
- queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
+ int i, j;
+ char dirty_bits[16];
+ uint8_t *client_log_mask_ptr;
+ uint8_t *log_mask_ptr;
+ int ret;
+
+ /* Update the peripheral(s) with the dci log and event masks */
+
+ /* If the cntl channel is not up, we can't update logs and events */
+ if (!driver->ch_cntl)
+ return;
+
+ memset(dirty_bits, 0, 16 * sizeof(uint8_t));
+
+ /*
+ * From each log entry used by each client, determine
+ * which log entries in the cumulative logs that need
+ * to be updated on the peripheral.
+ */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client) {
+ client_log_mask_ptr =
+ driver->dci_client_tbl[i].dci_log_mask;
+ for (j = 0; j < 16; j++) {
+ if (*(client_log_mask_ptr+1))
+ dirty_bits[j] = 1;
+ client_log_mask_ptr += 514;
+ }
+ }
+ }
+
+ mutex_lock(&dci_log_mask_mutex);
+ /* Update the appropriate dirty bits in the cumulative mask */
+ log_mask_ptr = dci_cumulative_log_mask;
+ for (i = 0; i < 16; i++) {
+ if (dirty_bits[i])
+ *(log_mask_ptr+1) = dirty_bits[i];
+
+ log_mask_ptr += 514;
+ }
+ mutex_unlock(&dci_log_mask_mutex);
+
+ ret = diag_send_dci_log_mask(driver->ch_cntl);
+
+ ret = diag_send_dci_event_mask(driver->ch_cntl);
}
-void diag_dci_notify_client(int peripheral_mask)
+void diag_dci_notify_client(int peripheral_mask, int data)
{
int i, stat;
+ struct siginfo info;
+ memset(&info, 0, sizeof(struct siginfo));
+ info.si_code = SI_QUEUE;
+ info.si_int = (peripheral_mask | data);
/* Notify the DCI process that the peripheral DCI Channel is up */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
if (driver->dci_client_tbl[i].list & peripheral_mask) {
- pr_info("diag: sending signal now\n");
- stat = send_sig(driver->dci_client_tbl[i].signal_type,
- driver->dci_client_tbl[i].client, 0);
+ info.si_signo = driver->dci_client_tbl[i].signal_type;
+ stat = send_sig_info(
+ driver->dci_client_tbl[i].signal_type,
+ &info, driver->dci_client_tbl[i].client);
if (stat)
- pr_err("diag: Err send sig stat: %d\n", stat);
+ pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
+ info.si_int, stat);
break;
}
} /* end of loop for all DCI clients */
}
+static void diag_smd_dci_notify(void *ctxt, unsigned event)
+{
+ if (event == SMD_EVENT_CLOSE) {
+ driver->ch_dci = 0;
+ /* Notify the clients of the close */
+ diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_CLOSED);
+ return;
+ } else if (event == SMD_EVENT_OPEN) {
+
+ if (ch_dci_temp)
+ driver->ch_dci = ch_dci_temp;
+
+ queue_work(driver->diag_dci_wq,
+ &(driver->diag_update_smd_dci_work));
+
+ /* Notify the clients of the open */
+ diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_OPEN);
+ }
+
+ queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
+}
+
static int diag_dci_probe(struct platform_device *pdev)
{
int err = 0;
@@ -339,12 +413,11 @@
pr_err("diag: cannot open DCI port, Id = %d, err ="
" %d\n", pdev->id, err);
else
- diag_dci_notify_client(DIAG_CON_MPSS);
+ ch_dci_temp = driver->ch_dci;
}
return err;
}
-
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index)
{
@@ -414,6 +487,11 @@
uint8_t *event_mask_ptr;
int offset = 0;
+ if (!driver->ch_dci) {
+ pr_err("diag: ch_dci not valid for dci updates\n");
+ return DIAG_DCI_SEND_DATA_FAIL;
+ }
+
/* This is Pkt request/response transaction */
if (*(int *)temp > 0) {
/* enter this UID into kernel table and return index */
@@ -535,7 +613,7 @@
ret = DIAG_DCI_NO_ERROR;
}
/* send updated mask to peripherals */
- diag_send_dci_log_mask(driver->ch_cntl);
+ ret = diag_send_dci_log_mask(driver->ch_cntl);
} else if (*(int *)temp == DCI_EVENT_TYPE) {
/* find client id and table */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
@@ -581,7 +659,7 @@
ret = DIAG_DCI_NO_ERROR;
}
/* send updated mask to peripherals */
- diag_send_dci_event_mask(driver->ch_cntl);
+ ret = diag_send_dci_event_mask(driver->ch_cntl);
} else {
pr_alert("diag: Incorrect DCI transaction\n");
}
@@ -614,11 +692,12 @@
mutex_unlock(&dci_event_mask_mutex);
}
-void diag_send_dci_event_mask(smd_channel_t *ch)
+int diag_send_dci_event_mask(smd_channel_t *ch)
{
void *buf = driver->buf_event_mask_update;
int header_size = sizeof(struct diag_ctrl_event_mask);
int wr_size = -ENOMEM, retry_count = 0, timer;
+ int ret = DIAG_DCI_NO_ERROR;
mutex_lock(&driver->diag_cntl_mutex);
/* send event mask update */
@@ -642,12 +721,18 @@
break;
}
}
- if (wr_size != header_size + DCI_EVENT_MASK_SIZE)
+ if (wr_size != header_size + DCI_EVENT_MASK_SIZE) {
pr_err("diag: error writing dci event mask %d, tried %d\n",
wr_size, header_size + DCI_EVENT_MASK_SIZE);
- } else
+ ret = DIAG_DCI_SEND_DATA_FAIL;
+ }
+ } else {
pr_err("diag: ch not valid for dci event mask update\n");
+ ret = DIAG_DCI_SEND_DATA_FAIL;
+ }
mutex_unlock(&driver->diag_cntl_mutex);
+
+ return ret;
}
void update_dci_cumulative_log_mask(int offset, int byte_index,
@@ -686,12 +771,18 @@
mutex_unlock(&dci_log_mask_mutex);
}
-void diag_send_dci_log_mask(smd_channel_t *ch)
+int diag_send_dci_log_mask(smd_channel_t *ch)
{
void *buf = driver->buf_log_mask_update;
int header_size = sizeof(struct diag_ctrl_log_mask);
uint8_t *log_mask_ptr = dci_cumulative_log_mask;
int i, wr_size = -ENOMEM, retry_count = 0, timer;
+ int ret = DIAG_DCI_NO_ERROR;
+
+ if (!ch) {
+ pr_err("diag: ch not valid for dci log mask update\n");
+ return DIAG_DCI_SEND_DATA_FAIL;
+ }
mutex_lock(&driver->diag_cntl_mutex);
for (i = 0; i < 16; i++) {
@@ -715,10 +806,12 @@
} else
break;
}
- if (wr_size != header_size + 512)
+ if (wr_size != header_size + 512) {
pr_err("diag: dci log mask update failed %d, tried %d",
wr_size, header_size + 512);
- else {
+ ret = DIAG_DCI_SEND_DATA_FAIL;
+
+ } else {
*(log_mask_ptr+1) = 0; /* clear dirty byte */
pr_debug("diag: updated dci log equip ID %d\n",
*log_mask_ptr);
@@ -727,6 +820,8 @@
log_mask_ptr += 514;
}
mutex_unlock(&driver->diag_cntl_mutex);
+
+ return ret;
}
void create_dci_log_mask_tbl(unsigned char *tbl_buf)
@@ -778,6 +873,8 @@
{
int success = 0;
+ ch_dci_temp = NULL;
+
driver->dci_tag = 0;
driver->dci_client_id = 0;
driver->num_dci_client = 0;
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 435c750..3f62e5e 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -79,6 +79,7 @@
int diag_dci_init(void);
void diag_dci_exit(void);
void diag_read_smd_dci_work_fn(struct work_struct *);
+void diag_update_smd_dci_work_fn(struct work_struct *);
int diag_process_dci_transaction(unsigned char *buf, int len);
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index);
@@ -87,11 +88,11 @@
void create_dci_log_mask_tbl(unsigned char *tbl_buf);
void update_dci_cumulative_log_mask(int offset, int byte_index,
uint8_t byte_mask);
-void diag_send_dci_log_mask(smd_channel_t *ch);
+int diag_send_dci_log_mask(smd_channel_t *ch);
void extract_dci_log(unsigned char *buf);
/* DCI event streaming functions */
void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask);
-void diag_send_dci_event_mask(smd_channel_t *ch);
+int diag_send_dci_event_mask(smd_channel_t *ch);
void extract_dci_events(unsigned char *buf);
void create_dci_event_mask_tbl(unsigned char *tbl_buf);
#endif
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 7863f74..c404229 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -113,7 +113,8 @@
"diag_modem_mask_update_work: %d\n"
"diag_lpass_mask_update_work: %d\n"
"diag_wcnss_mask_update_work: %d\n"
- "diag_read_smd_dci_work: %d\n",
+ "diag_read_smd_dci_work: %d\n"
+ "diag_update_smd_dci_work: %d\n",
work_pending(&(driver->diag_drain_work)),
work_pending(&(driver->diag_read_smd_work)),
work_pending(&(driver->diag_read_smd_cntl_work)),
@@ -124,7 +125,8 @@
work_pending(&(driver->diag_modem_mask_update_work)),
work_pending(&(driver->diag_lpass_mask_update_work)),
work_pending(&(driver->diag_wcnss_mask_update_work)),
- work_pending(&(driver->diag_read_smd_dci_work)));
+ work_pending(&(driver->diag_read_smd_dci_work)),
+ work_pending(&(driver->diag_update_smd_dci_work)));
#ifdef CONFIG_DIAG_OVER_USB
ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index d1ec5f2..ec3bb81 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -67,6 +67,15 @@
#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
#define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */
+/*
+ * The status bit masks when received in a signal handler are to be
+ * used in conjunction with the peripheral list bit mask to determine the
+ * status for a peripheral. For instance, 0x00010002 would denote an open
+ * status on the MPSS
+ */
+#define DIAG_STATUS_OPEN (0x00010000) /* DCI channel open status mask */
+#define DIAG_STATUS_CLOSED (0x00020000) /* DCI channel closed status mask */
+
/* Maximum number of pkt reg supported at initialization*/
extern unsigned int diag_max_reg;
extern unsigned int diag_threshold_reg;
@@ -235,6 +244,7 @@
struct work_struct diag_lpass_mask_update_work;
struct work_struct diag_wcnss_mask_update_work;
struct work_struct diag_read_smd_dci_work;
+ struct work_struct diag_update_smd_dci_work;
struct work_struct diag_clean_modem_reg_work;
struct work_struct diag_clean_lpass_reg_work;
struct work_struct diag_clean_wcnss_reg_work;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 645d916..34ff345 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1613,7 +1613,9 @@
INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work),
diag_read_smd_wcnss_cntl_work_fn);
INIT_WORK(&(driver->diag_read_smd_dci_work),
- diag_read_smd_dci_work_fn);
+ diag_read_smd_dci_work_fn);
+ INIT_WORK(&(driver->diag_update_smd_dci_work),
+ diag_update_smd_dci_work_fn);
INIT_WORK(&(driver->diag_clean_modem_reg_work),
diag_clean_modem_reg_fn);
INIT_WORK(&(driver->diag_clean_lpass_reg_work),
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 6d66d45..978cb38d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -42,8 +42,6 @@
*/
// #define DVB_DEMUX_SECTION_LOSS_LOG
-#define TIMESTAMP_LEN 4
-
static int dvb_demux_tscheck;
module_param(dvb_demux_tscheck, int, 0644);
MODULE_PARM_DESC(dvb_demux_tscheck,
@@ -608,7 +606,7 @@
((f)->feed.ts.is_filtering) && \
(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
-static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
const u8 timestamp[TIMESTAMP_LEN])
{
struct dvb_demux_feed *feed;
@@ -688,6 +686,7 @@
dvb_dmx_swfilter_output_packet(feed, buf, timestamp);
}
}
+EXPORT_SYMBOL(dvb_dmx_swfilter_packet);
void dvb_dmx_swfilter_section_packets(struct dvb_demux *demux, const u8 *buf,
size_t count)
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 4e6dfaf..f89367b 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -47,6 +47,8 @@
#define MAX_PID 0x1fff
+#define TIMESTAMP_LEN 4
+
#define SPEED_PKTS_INTERVAL 50000
struct dvb_demux_filter {
@@ -189,6 +191,8 @@
struct dvb_demux *demux, const u8 *buf,
size_t count,
enum dmx_tsp_format_t tsp_format);
+void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN]);
#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
index 51d66cd..e2ef6a0 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -473,16 +473,23 @@
* when dvb-demux is terminated.
*/
mpq_demux->hw_notification_count = 0;
- mpq_demux->hw_notification_rate = 0;
+ mpq_demux->hw_notification_interval = 0;
mpq_demux->hw_notification_size = 0;
mpq_demux->decoder_tsp_drop_count = 0;
+ mpq_demux->hw_notification_min_size = 0xFFFFFFFF;
if (mpq_demux->demux.dmx.debugfs_demux_dir != NULL) {
debugfs_create_u32(
- "hw_notification_rate",
+ "hw_notification_interval",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->hw_notification_rate);
+ &mpq_demux->hw_notification_interval);
+
+ debugfs_create_u32(
+ "hw_notification_min_interval",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_min_interval);
debugfs_create_u32(
"hw_notification_count",
@@ -497,6 +504,12 @@
&mpq_demux->hw_notification_size);
debugfs_create_u32(
+ "hw_notification_min_size",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_min_size);
+
+ debugfs_create_u32(
"decoder_tsp_drop_count",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
@@ -520,10 +533,17 @@
curr_time,
mpq_demux->last_notification_time);
- delta_time_ms = (u64)timespec_to_ns(&delta_time);
- delta_time_ms = div64_u64(delta_time_ms, 1000000); /* ns->ms */
+ delta_time_ms = ((u64)delta_time.tv_sec * MSEC_PER_SEC) +
+ delta_time.tv_nsec / NSEC_PER_MSEC;
- mpq_demux->hw_notification_rate = delta_time_ms;
+ mpq_demux->hw_notification_interval = delta_time_ms;
+
+ if ((mpq_demux->hw_notification_count == 1) ||
+ (mpq_demux->hw_notification_interval &&
+ mpq_demux->hw_notification_interval <
+ mpq_demux->hw_notification_min_interval))
+ mpq_demux->hw_notification_min_interval =
+ mpq_demux->hw_notification_interval;
}
mpq_demux->hw_notification_count++;
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
index e9987c2..69305b6 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -44,9 +44,14 @@
* initialized or not.
* @ion_client: ION demux client used to allocate memory from ION.
* @feed_lock: Lock used to protect against private feed data
- * @hw_notification_rate: Notification rate in msec, exposed in debugfs.
+ * @hw_notification_interval: Notification interval in msec,
+ * exposed in debugfs.
+ * @hw_notification_min_interval: Minimum notification internal in msec,
+ * exposed in debugfs.
* @hw_notification_count: Notification count, exposed in debugfs.
* @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @hw_notification_min_size: Minimum notification size in bytes,
+ * exposed in debugfs.
* @decoder_tsp_drop_count: Counter of number of dropped TS packets
* due to decoder buffer fullness, exposed in debugfs.
* @last_notification_time: Time of last HW notification.
@@ -61,9 +66,11 @@
spinlock_t feed_lock;
/* debug-fs */
- u32 hw_notification_rate;
+ u32 hw_notification_interval;
+ u32 hw_notification_min_interval;
u32 hw_notification_count;
u32 hw_notification_size;
+ u32 hw_notification_min_size;
u32 decoder_tsp_drop_count;
struct timespec last_notification_time;
};
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
index f5c01e1..ac03e43 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -35,45 +35,36 @@
#define TSPP_GET_TSIF_NUM(ch_id) (ch_id >> 1)
/* mask that set to care for all bits in pid filter */
-#define TSPP_PID_MASK 0x1FFF
+#define TSPP_PID_MASK 0x1FFF
/* dvb-demux defines pid 0x2000 as full capture pid */
-#define TSPP_PASS_THROUGH_PID 0x2000
+#define TSPP_PASS_THROUGH_PID 0x2000
-/* TODO - NEED TO SET THESE PROPERLY
- * once TSPP driver is ready, reduce TSPP_BUFFER_SIZE
- * to single packet and set TSPP_BUFFER_COUNT accordingly
- */
+#define TSPP_RAW_TTS_SIZE 192
+#define TSPP_RAW_SIZE 188
-#define TSPP_RAW_TTS_SIZE 192
+#define MAX_BAM_DESCRIPTOR_SIZE (32*1024 - 1)
-#define MAX_BAM_DESCRIPTOR_SIZE (32*1024 - 1)
+#define TSPP_BUFFER_SIZE (500 * 1024) /* 500KB */
-/* Size of single descriptor for PES/rec pipe.
+#define TSPP_PES_BUFFER_SIZE (TSPP_RAW_TTS_SIZE)
+
+#define TSPP_SECTION_BUFFER_SIZE (TSPP_RAW_TTS_SIZE)
+
+#define TSPP_BUFFER_COUNT(buffer_size) \
+ ((buffer_size) / TSPP_RAW_TTS_SIZE)
+
+/* When TSPP notifies demux that new packets are received.
* Using max descriptor size (170 packets).
* Assuming 20MBit/sec stream, with 170 packets
* per descriptor there would be about 82 descriptors,
* Meanning about 82 notifications per second.
*/
-#define TSPP_PES_BUFFER_SIZE \
- ((MAX_BAM_DESCRIPTOR_SIZE / TSPP_RAW_TTS_SIZE) * TSPP_RAW_TTS_SIZE)
-
-/* Size of single descriptor for section pipe.
- * Assuming 8MBit/sec section rate, with 65 packets
- * per descriptor there would be about 85 descriptors,
- * Meanning about 85 notifications per second.
- */
-#define TSPP_SECTION_BUFFER_SIZE \
- (65 * TSPP_RAW_TTS_SIZE)
-
-/* Number of descriptors, total size: TSPP_BUFFER_SIZE*TSPP_BUFFER_COUNT */
-#define TSPP_BUFFER_COUNT (32)
-
-/* When TSPP notifies demux that new packets are received */
-#define TSPP_NOTIFICATION_SIZE 1
+#define TSPP_NOTIFICATION_SIZE(desc_size) \
+ (MAX_BAM_DESCRIPTOR_SIZE / (desc_size))
/* Channel timeout in msec */
-#define TSPP_CHANNEL_TIMEOUT 16
+#define TSPP_CHANNEL_TIMEOUT 100
enum mem_buffer_allocation_mode {
MPQ_DMX_TSPP_INTERNAL_ALLOC = 0,
@@ -84,9 +75,16 @@
static int clock_inv;
static int tsif_mode = 2;
static int allocation_mode = MPQ_DMX_TSPP_INTERNAL_ALLOC;
+static int tspp_out_buffer_size = TSPP_BUFFER_SIZE;
+static int tspp_notification_size = TSPP_NOTIFICATION_SIZE(TSPP_RAW_TTS_SIZE);
+static int tspp_channel_timeout = TSPP_CHANNEL_TIMEOUT;
+
module_param(tsif_mode, int, S_IRUGO | S_IWUSR);
module_param(clock_inv, int, S_IRUGO | S_IWUSR);
module_param(allocation_mode, int, S_IRUGO);
+module_param(tspp_out_buffer_size, int, S_IRUGO);
+module_param(tspp_notification_size, int, S_IRUGO | S_IWUSR);
+module_param(tspp_channel_timeout, int, S_IRUGO | S_IWUSR);
/* The following structure hold singelton information
@@ -134,6 +132,8 @@
/* buffer allocation index */
int section_index;
+ u32 buffer_count;
+
/*
* Holds PIDs of allocated TSPP filters along with
* how many feeds are opened on same PID.
@@ -168,7 +168,8 @@
int i = TSPP_GET_TSIF_NUM(channel_id);
if (TSPP_IS_PES_CHANNEL(channel_id)) {
- if (mpq_dmx_tspp_info.tsif[i].pes_index == TSPP_BUFFER_COUNT)
+ if (mpq_dmx_tspp_info.tsif[i].pes_index ==
+ mpq_dmx_tspp_info.tsif[i].buffer_count)
return NULL;
virt_addr =
(mpq_dmx_tspp_info.tsif[i].pes_mem_heap_virt_base +
@@ -179,7 +180,7 @@
mpq_dmx_tspp_info.tsif[i].pes_index++;
} else {
if (mpq_dmx_tspp_info.tsif[i].section_index ==
- TSPP_BUFFER_COUNT)
+ mpq_dmx_tspp_info.tsif[i].buffer_count)
return NULL;
virt_addr =
(mpq_dmx_tspp_info.tsif[i].section_mem_heap_virt_base +
@@ -277,9 +278,11 @@
struct mpq_demux *mpq_demux;
const struct tspp_data_descriptor *tspp_data_desc;
atomic_t *data_cnt;
+ u32 notif_size;
int ref_count;
int ret;
int i;
+ int j;
do {
ret = wait_event_interruptible(
@@ -331,16 +334,20 @@
*/
while ((tspp_data_desc =
tspp_get_buffer(0, channel_id)) != NULL) {
+ notif_size =
+ tspp_data_desc->size /
+ TSPP_RAW_TTS_SIZE;
+
mpq_demux->hw_notification_size +=
- (tspp_data_desc->size /
- TSPP_RAW_TTS_SIZE);
+ notif_size;
- dvb_dmx_swfilter_format(
- &mpq_demux->demux,
- tspp_data_desc->virt_base,
- tspp_data_desc->size,
- DMX_TSP_FORMAT_192_TAIL);
-
+ for (j = 0; j < notif_size; j++)
+ dvb_dmx_swfilter_packet(
+ &mpq_demux->demux,
+ ((u8 *)tspp_data_desc->virt_base) +
+ j * TSPP_RAW_TTS_SIZE,
+ ((u8 *)tspp_data_desc->virt_base) +
+ j * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
/*
* Notify TSPP that the buffer
* is no longer needed
@@ -348,6 +355,12 @@
tspp_release_buffer(0,
channel_id, tspp_data_desc->id);
}
+
+ if (mpq_demux->hw_notification_size &&
+ (mpq_demux->hw_notification_size <
+ mpq_demux->hw_notification_min_size))
+ mpq_demux->hw_notification_min_size =
+ mpq_demux->hw_notification_size;
}
mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
@@ -466,7 +479,6 @@
/* check if required TSPP pipe is already allocated or not */
if (*channel_ref_count == 0) {
ret = tspp_open_channel(0, channel_id);
-
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_open_channel(%d) failed (%d)\n",
@@ -495,7 +507,7 @@
channel_id,
mpq_tspp_callback,
(void *)tsif,
- TSPP_CHANNEL_TIMEOUT);
+ tspp_channel_timeout);
/* register allocater and provide allocation function
* that allocates from continous memory so that we can have
@@ -507,23 +519,15 @@
* allocate TSPP data buffers
*/
if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
- ret = tspp_allocate_buffers(0,
- channel_id,
- TSPP_BUFFER_COUNT,
- buffer_size,
- TSPP_NOTIFICATION_SIZE,
- tspp_mem_allocator,
- tspp_mem_free,
- NULL);
+ ret = tspp_allocate_buffers(0, channel_id,
+ mpq_dmx_tspp_info.tsif[tsif].buffer_count,
+ buffer_size, tspp_notification_size,
+ tspp_mem_allocator, tspp_mem_free, NULL);
} else {
- ret = tspp_allocate_buffers(0,
- channel_id,
- TSPP_BUFFER_COUNT,
- buffer_size,
- TSPP_NOTIFICATION_SIZE,
- NULL,
- NULL,
- NULL);
+ ret = tspp_allocate_buffers(0, channel_id,
+ mpq_dmx_tspp_info.tsif[tsif].buffer_count,
+ buffer_size, tspp_notification_size,
+ NULL, NULL, NULL);
}
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
@@ -767,7 +771,7 @@
ret = mpq_dmx_init_video_feed(feed);
if (ret < 0) {
- MPQ_DVB_DBG_PRINT(
+ MPQ_DVB_ERR_PRINT(
"%s: mpq_dmx_init_video_feed failed(%d)\n",
__func__,
ret);
@@ -949,11 +953,11 @@
for (i = 0; i < TSIF_COUNT; i++) {
mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle =
ion_alloc(mpq_dmx_tspp_info.ion_client,
- (TSPP_BUFFER_COUNT *
- TSPP_PES_BUFFER_SIZE),
- TSPP_RAW_TTS_SIZE,
- ION_HEAP(ION_CP_MM_HEAP_ID),
- 0); /* non-cached */
+ (mpq_dmx_tspp_info.tsif[i].buffer_count *
+ TSPP_PES_BUFFER_SIZE),
+ TSPP_RAW_TTS_SIZE,
+ ION_HEAP(ION_CP_MM_HEAP_ID),
+ 0); /* non-cached */
if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
pes_mem_heap_handle)) {
MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n",
@@ -988,11 +992,11 @@
mpq_dmx_tspp_info.tsif[i].section_mem_heap_handle =
ion_alloc(mpq_dmx_tspp_info.ion_client,
- (TSPP_BUFFER_COUNT *
- TSPP_SECTION_BUFFER_SIZE),
- TSPP_RAW_TTS_SIZE,
- ION_HEAP(ION_CP_MM_HEAP_ID),
- 0); /* non-cached */
+ (mpq_dmx_tspp_info.tsif[i].buffer_count *
+ TSPP_SECTION_BUFFER_SIZE),
+ TSPP_RAW_TTS_SIZE,
+ ION_HEAP(ION_CP_MM_HEAP_ID),
+ 0); /* non-cached */
if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
section_mem_heap_handle)) {
MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n",
@@ -1106,6 +1110,9 @@
MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
for (i = 0; i < TSIF_COUNT; i++) {
+ mpq_dmx_tspp_info.tsif[i].buffer_count =
+ TSPP_BUFFER_COUNT(tspp_out_buffer_size);
+
mpq_dmx_tspp_info.tsif[i].pes_channel_ref = 0;
mpq_dmx_tspp_info.tsif[i].pes_index = 0;
mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle = NULL;
diff --git a/drivers/media/video/msm_vidc/msm_smem.c b/drivers/media/video/msm_vidc/msm_smem.c
index 83f33a1..e1b73ef 100644
--- a/drivers/media/video/msm_vidc/msm_smem.c
+++ b/drivers/media/video/msm_vidc/msm_smem.c
@@ -32,8 +32,6 @@
clnt, hndl, iova, buffer_size);
return -EINVAL;
}
- if (align < 4096)
- align = 4096;
dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
domain_num, partition_num);
if (flags & SMEM_SECURE) {
@@ -74,6 +72,7 @@
unsigned long iova = 0;
unsigned long buffer_size = 0;
int rc = 0;
+ int align = SZ_4K;
hndl = ion_import_dma_buf(client->clnt, fd);
if (IS_ERR_OR_NULL(hndl)) {
dprintk(VIDC_ERR, "Failed to get handle: %p, %d, %d, %p\n",
@@ -85,8 +84,12 @@
mem->domain = domain;
mem->partition_num = partition;
mem->flags = flags;
+
+ if (flags & SMEM_SECURE)
+ align = ALIGN(align, SZ_1M);
+
rc = get_device_address(client->clnt, hndl, mem->domain,
- mem->partition_num, 4096, &iova, &buffer_size, flags);
+ mem->partition_num, align, &iova, &buffer_size, flags);
if (rc) {
dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc);
goto fail_device_address;
@@ -121,15 +124,16 @@
else
ionflags = ION_SET_UNCACHED(ionflags);
+ align = ALIGN(align, SZ_4K);
+ size = ALIGN(size, SZ_4K);
+
if (flags & SMEM_SECURE) {
ionflags |= ION_SECURE;
- size = (size + 0xfffff) & (~0xfffff);
+ size = ALIGN(size, SZ_1M);
+ align = ALIGN(align, SZ_1M);
}
heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
- if (align < 4096)
- align = 4096;
- size = (size + 4095) & (~4095);
dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
domain, partition);
hndl = ion_alloc(client->clnt, size, align, heap_mask, ionflags);
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 4f2373e..38e8de1 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -591,7 +591,7 @@
}
v4l2_inst->vidc_inst = msm_vidc_open(core->id, vid_dev->type);
- if (rc) {
+ if (!v4l2_inst->vidc_inst) {
dprintk(VIDC_ERR,
"Failed to create video instance, core: %d, type = %d\n",
core->id, vid_dev->type);
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index c281f9c..e0a341a 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -462,7 +462,7 @@
b->m.planes[extra_idx].m.userptr;
else
buffer_info.extradata_addr = 0;
-
+ buffer_info.response_required = false;
rc = vidc_hal_session_release_buffers(
(void *)inst->session, &buffer_info);
if (rc)
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index f4c973f..f436cf3 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -1569,6 +1569,7 @@
b->m.planes[i].m.userptr;
buffer_info.extradata_size = 0;
buffer_info.extradata_addr = 0;
+ buffer_info.response_required = false;
rc = vidc_hal_session_release_buffers(
(void *)inst->session, &buffer_info);
if (rc)
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 0ff0d04..46a88c2 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -354,6 +354,48 @@
}
}
+static void handle_session_release_buf_done(enum command_response cmd,
+ void *data)
+{
+ struct msm_vidc_cb_cmd_done *response = data;
+ struct msm_vidc_inst *inst;
+ struct internal_buf *buf;
+ struct list_head *ptr, *next;
+ struct hal_buffer_info *buffer;
+ u32 address, buf_found = false;
+
+ if (!response || !response->data) {
+ dprintk(VIDC_ERR, "Invalid release_buf_done response\n");
+ return;
+ }
+
+ inst = (struct msm_vidc_inst *)response->session_id;
+ buffer = (struct hal_buffer_info *) response->data;
+ address = (u32) buffer->buffer_addr;
+
+ list_for_each_safe(ptr, next, &inst->internalbufs) {
+ buf = list_entry(ptr, struct internal_buf, list);
+ if (address == buf->handle->device_addr) {
+ dprintk(VIDC_DBG, "releasing scratch: 0x%x",
+ (u32) buf->handle->device_addr);
+ buf_found = true;
+ }
+ }
+
+ list_for_each_safe(ptr, next, &inst->persistbufs) {
+ buf = list_entry(ptr, struct internal_buf, list);
+ if (address == (u32) buf->handle->device_addr) {
+ dprintk(VIDC_DBG, "releasing persist: 0x%x",
+ (u32) buf->handle->device_addr);
+ buf_found = true;
+ }
+ }
+
+ if (!buf_found)
+ dprintk(VIDC_ERR, "invalid buffer received from firmware");
+ complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
+}
+
static void handle_sys_release_res_done(
enum command_response cmd, void *data)
{
@@ -892,6 +934,9 @@
case SESSION_ERROR:
handle_session_error(cmd, data);
break;
+ case SESSION_RELEASE_BUFFER_DONE:
+ handle_session_release_buf_done(cmd, data);
+ break;
default:
dprintk(VIDC_ERR, "response unhandled\n");
break;
@@ -1840,6 +1885,10 @@
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
+ buffer_info.response_required = true;
+ init_completion(
+ &inst->completions[SESSION_MSG_INDEX
+ (SESSION_RELEASE_BUFFER_DONE)]);
rc = vidc_hal_session_release_buffers(
(void *) inst->session,
&buffer_info);
@@ -1848,6 +1897,10 @@
"Rel scrtch buf fail:0x%x, %d",
buffer_info.align_device_addr,
buffer_info.buffer_size);
+ spin_unlock_irqrestore(&inst->lock, flags);
+ rc = wait_for_sess_signal_receipt(inst,
+ SESSION_RELEASE_BUFFER_DONE);
+ spin_lock_irqsave(&inst->lock, flags);
}
list_del(&buf->list);
spin_unlock_irqrestore(&inst->lock, flags);
@@ -1892,6 +1945,10 @@
buffer_info.align_device_addr = handle->device_addr;
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
+ buffer_info.response_required = true;
+ init_completion(
+ &inst->completions[SESSION_MSG_INDEX
+ (SESSION_RELEASE_BUFFER_DONE)]);
rc = vidc_hal_session_release_buffers(
(void *) inst->session,
&buffer_info);
@@ -1900,6 +1957,10 @@
"Rel prst buf fail:0x%x, %d",
buffer_info.align_device_addr,
buffer_info.buffer_size);
+ spin_unlock_irqrestore(&inst->lock, flags);
+ rc = wait_for_sess_signal_receipt(inst,
+ SESSION_RELEASE_BUFFER_DONE);
+ spin_lock_irqsave(&inst->lock, flags);
}
list_del(&buf->list);
spin_unlock_irqrestore(&inst->lock, flags);
@@ -1982,6 +2043,8 @@
buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
+ dprintk(VIDC_DBG, "Scratch buffer address: %x",
+ buffer_info.align_device_addr);
rc = vidc_hal_session_set_buffers(
(void *) inst->session, &buffer_info);
if (rc) {
@@ -2053,6 +2116,8 @@
buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
+ dprintk(VIDC_DBG, "Persist buffer address: %x",
+ buffer_info.align_device_addr);
rc = vidc_hal_session_set_buffers(
(void *) inst->session, &buffer_info);
if (rc) {
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index e449821..207bfe4 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -1954,6 +1954,7 @@
pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
((buffer_info->num_buffers - 1) * sizeof(u32));
}
+ pkt->response_req = buffer_info->response_required;
buffer = get_hfi_buffer(buffer_info->buffer_type);
if (buffer)
pkt->buffer_type = buffer;
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
index 9d20a31..3b83424 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -777,6 +777,12 @@
u32 align_device_addr;
u32 extradata_size;
u32 extradata_addr;
+ u32 response_required;
+};
+
+struct hal_buffer_info {
+ u32 buffer_addr;
+ u32 extra_data_addr;
};
struct vidc_frame_plane_config {
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index 8231bd4..abce25f 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -732,6 +732,31 @@
device->callback(SESSION_RELEASE_RESOURCE_DONE, &cmd_done);
}
+static void hal_process_session_rel_buf_done(struct hal_device *device,
+ struct hfi_msg_session_release_buffers_done_packet *pkt)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ if (!pkt || pkt->size !=
+ sizeof(struct
+ hfi_msg_session_release_buffers_done_packet)) {
+ dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size);
+ return;
+ }
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ cmd_done.device_id = device->device_id;
+ cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done);
+ cmd_done.session_id =
+ ((struct hal_session *) pkt->session_id)->session_id;
+ cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+ if (pkt->rg_buffer_info) {
+ cmd_done.data = (void *) &pkt->rg_buffer_info;
+ cmd_done.size = sizeof(struct hfi_buffer_info);
+ } else {
+ dprintk(VIDC_ERR, "invalid payload in rel_buff_done\n");
+ }
+ device->callback(SESSION_RELEASE_BUFFER_DONE, &cmd_done);
+}
+
static void hal_process_session_end_done(struct hal_device *device,
struct hfi_msg_sys_session_end_done_packet *pkt)
{
@@ -870,6 +895,11 @@
hfi_msg_session_get_sequence_header_done_packet
*) msg_hdr);
break;
+ case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE:
+ hal_process_session_rel_buf_done(device, (struct
+ hfi_msg_session_release_buffers_done_packet
+ *) msg_hdr);
+ break;
default:
dprintk(VIDC_ERR, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
break;
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index f310524..eebaab9 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -35,6 +35,8 @@
#include <mach/sps.h> /* BAM stuff */
#include <mach/gpio.h>
#include <linux/wakelock.h> /* Locking functions */
+#include <linux/timer.h> /* Timer services */
+#include <linux/jiffies.h> /* Jiffies counter */
#include <mach/dma.h>
#include <mach/msm_tspp.h>
#include <linux/debugfs.h>
@@ -49,11 +51,24 @@
#define TSPP_NUM_PRIORITIES 16
#define TSPP_NUM_KEYS 8
#define INVALID_CHANNEL 0xFFFFFFFF
-#define TSPP_SPS_DESCRIPTOR_COUNT 128
+
+/*
+ * BAM descriptor FIFO size (in number of descriptors).
+ * Max number of descriptors allowed by SPS which is 8K-1.
+ * Restrict it to half of this to save DMA memory.
+ */
+#define TSPP_SPS_DESCRIPTOR_COUNT (4 * 1024 - 1)
#define TSPP_PACKET_LENGTH 188
#define TSPP_MIN_BUFFER_SIZE (TSPP_PACKET_LENGTH)
-#define TSPP_MAX_BUFFER_SIZE (32 * 1024)
-#define TSPP_NUM_BUFFERS 64
+
+/* Max descriptor buffer size allowed by SPS */
+#define TSPP_MAX_BUFFER_SIZE (32 * 1024 - 1)
+
+/*
+ * Max allowed TSPP buffers/descriptors.
+ * If SPS desc FIFO holds X descriptors, we can queue up to X-1 descriptors.
+ */
+#define TSPP_NUM_BUFFERS (TSPP_SPS_DESCRIPTOR_COUNT - 1)
#define TSPP_TSIF_DEFAULT_TIME_LIMIT 60
#define SPS_DESCRIPTOR_SIZE 8
#define MIN_ACCEPTABLE_BUFFER_COUNT 2
@@ -307,6 +322,8 @@
#define CONTEXT_UNSPEC_LENGTH BIT(11)
#define CONTEXT_GET_CONT_COUNT(_a) ((_a >> 12) & 0xF)
+#define MSEC_TO_JIFFIES(msec) ((msec) * HZ / 1000)
+
struct tspp_pipe_performance_regs {
u32 tsp_total;
u32 ps_duplicate_tsp;
@@ -379,7 +396,8 @@
enum tspp_mode mode;
tspp_notifier *notifier; /* used only with kernel api */
void *notify_data; /* data to be passed with the notifier */
- u32 notify_timer; /* notification for partially filled buffers */
+ u32 expiration_period_ms; /* notification on partially filled buffers */
+ struct timer_list expiration_timer;
tspp_memfree *memfree; /* user defined memory free function */
void *user_info; /* user cookie passed to memory alloc/free function */
};
@@ -539,6 +557,14 @@
tasklet_schedule(&pdev->tlet);
}
+static void tspp_expiration_timer(unsigned long data)
+{
+ struct tspp_device *pdev = (struct tspp_device *)data;
+
+ if (pdev)
+ tasklet_schedule(&pdev->tlet);
+}
+
/*** tasklet ***/
static void tspp_sps_complete_tlet(unsigned long data)
{
@@ -553,9 +579,14 @@
for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
complete = 0;
channel = &device->channels[i];
+
if (!channel->used || !channel->waiting)
continue;
+ /* stop the expiration timer */
+ if (channel->expiration_period_ms)
+ del_timer(&channel->expiration_timer);
+
/* get completions */
while (channel->waiting->state == TSPP_BUF_STATE_WAITING) {
if (sps_get_iovec(channel->pipe, &iovec) != 0) {
@@ -593,6 +624,13 @@
channel->notifier(channel->id,
channel->notify_data);
}
+
+ /* restart expiration timer */
+ if (channel->expiration_period_ms)
+ mod_timer(&channel->expiration_timer,
+ jiffies +
+ MSEC_TO_JIFFIES(
+ channel->expiration_period_ms));
}
spin_unlock_irqrestore(&device->spinlock, flags);
@@ -897,7 +935,7 @@
/* generate interrupt according to requested frequency */
if (buffer->desc.id % channel->int_freq == channel->int_freq-1)
- flags = SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOB;
+ flags = SPS_IOVEC_FLAG_INT;
/* start the transfer */
rc = sps_transfer_one(channel->pipe,
@@ -1034,7 +1072,7 @@
channel->mode = TSPP_MODE_DISABLED;
channel->notifier = NULL;
channel->notify_data = NULL;
- channel->notify_timer = 0;
+ channel->expiration_period_ms = 0;
channel->memfree = NULL;
channel->user_info = NULL;
init_waitqueue_head(&channel->in_queue);
@@ -1394,10 +1432,11 @@
SPS_O_AUTO_ENABLE | /* connection is auto-enabled */
SPS_O_STREAMING | /* streaming mode */
SPS_O_DESC_DONE | /* interrupt on end of descriptor */
- SPS_O_ACK_TRANSFERS; /* must use sps_get_iovec() */
+ SPS_O_ACK_TRANSFERS | /* must use sps_get_iovec() */
+ SPS_O_HYBRID; /* Read actual descriptors in sps_get_iovec() */
config->src_pipe_index = channel->id;
config->desc.size =
- (TSPP_SPS_DESCRIPTOR_COUNT + 1) * SPS_DESCRIPTOR_SIZE;
+ TSPP_SPS_DESCRIPTOR_COUNT * SPS_DESCRIPTOR_SIZE;
config->desc.base = dma_alloc_coherent(NULL,
config->desc.size,
&config->desc.phys_base,
@@ -1428,6 +1467,11 @@
goto err_event;
}
+ init_timer(&channel->expiration_timer);
+ channel->expiration_timer.function = tspp_expiration_timer;
+ channel->expiration_timer.data = (unsigned long)pdev;
+ channel->expiration_timer.expires = 0xffffffffL;
+
rc = pm_runtime_get(&pdev->pdev->dev);
if (rc < 0) {
dev_err(&pdev->pdev->dev,
@@ -1482,9 +1526,12 @@
if (!channel->used)
return 0;
+ if (channel->expiration_period_ms)
+ del_timer(&channel->expiration_timer);
+
channel->notifier = NULL;
channel->notify_data = NULL;
- channel->notify_timer = 0;
+ channel->expiration_period_ms = 0;
config = &channel->config;
pdev = channel->pdev;
@@ -1833,7 +1880,8 @@
channel = &pdev->channels[channel_id];
channel->notifier = pNotify;
channel->notify_data = userdata;
- channel->notify_timer = timer_ms;
+ channel->expiration_period_ms = timer_ms;
+
return 0;
}
EXPORT_SYMBOL(tspp_register_notification);
@@ -2113,6 +2161,13 @@
channel->read = channel->data;
channel->locked = channel->data;
+ /* Now that buffers are scheduled to HW, kick data expiration timer */
+ if (channel->expiration_period_ms)
+ mod_timer(&channel->expiration_timer,
+ jiffies +
+ MSEC_TO_JIFFIES(
+ channel->expiration_period_ms));
+
return 0;
}
EXPORT_SYMBOL(tspp_allocate_buffers);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 48516b6..f91ba89 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -29,6 +29,7 @@
#include <linux/wakelock.h>
#include <linux/pm.h>
#include <linux/slab.h>
+#include <linux/jiffies.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -44,6 +45,8 @@
#include "sd_ops.h"
#include "sdio_ops.h"
+static void mmc_clk_scaling(struct mmc_host *host, bool from_wq);
+
/*
* Background operations can take a long time, depending on the housekeeping
* operations the card has to perform.
@@ -171,6 +174,10 @@
#ifdef CONFIG_MMC_PERF_PROFILING
ktime_t diff;
#endif
+ if (host->card && host->clk_scaling.enable)
+ host->clk_scaling.busy_time_us +=
+ ktime_to_us(ktime_sub(ktime_get(),
+ host->clk_scaling.start_busy));
if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
@@ -300,6 +307,19 @@
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
+
+ if (host->card && host->clk_scaling.enable) {
+ /*
+ * Check if we need to scale the clocks. Clocks
+ * will be scaled up immediately if necessary
+ * conditions are satisfied. Scaling down the
+ * frequency will be done after current thread
+ * releases host.
+ */
+ mmc_clk_scaling(host, false);
+ host->clk_scaling.start_busy = ktime_get();
+ }
+
host->ops->request(host, mrq);
}
@@ -2286,6 +2306,289 @@
}
EXPORT_SYMBOL(mmc_hw_reset_check);
+/**
+ * mmc_reset_clk_scale_stats() - reset clock scaling statistics
+ * @host: pointer to mmc host structure
+ */
+void mmc_reset_clk_scale_stats(struct mmc_host *host)
+{
+ host->clk_scaling.busy_time_us = 0;
+ host->clk_scaling.window_time = jiffies;
+}
+EXPORT_SYMBOL_GPL(mmc_reset_clk_scale_stats);
+
+/**
+ * mmc_get_max_frequency() - get max. frequency supported
+ * @host: pointer to mmc host structure
+ *
+ * Returns max. frequency supported by card/host. If the
+ * timing mode is SDR50/SDR104/HS200/DDR50 return appropriate
+ * max. frequency in these modes else, use the current frequency.
+ * Also, allow host drivers to overwrite the frequency in case
+ * they support "get_max_frequency" host ops.
+ */
+unsigned long mmc_get_max_frequency(struct mmc_host *host)
+{
+ unsigned long freq;
+
+ if (host->ops && host->ops->get_max_frequency) {
+ freq = host->ops->get_max_frequency(host);
+ goto out;
+ }
+
+ switch (host->ios.timing) {
+ case MMC_TIMING_UHS_SDR50:
+ freq = UHS_SDR50_MAX_DTR;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ freq = UHS_SDR104_MAX_DTR;
+ break;
+ case MMC_TIMING_MMC_HS200:
+ freq = MMC_HS200_MAX_DTR;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ freq = UHS_DDR50_MAX_DTR;
+ break;
+ default:
+ mmc_host_clk_hold(host);
+ freq = host->ios.clock;
+ mmc_host_clk_release(host);
+ break;
+ }
+
+out:
+ return freq;
+}
+EXPORT_SYMBOL_GPL(mmc_get_max_frequency);
+
+/**
+ * mmc_get_min_frequency() - get min. frequency supported
+ * @host: pointer to mmc host structure
+ *
+ * Returns min. frequency supported by card/host which doesn't impair
+ * performance for most usecases. If the timing mode is SDR50/SDR104/HS200
+ * return 50MHz value. If timing mode is DDR50 return 25MHz so that
+ * throughput would be equivalent to SDR50/SDR104 in 50MHz. Also, allow
+ * host drivers to overwrite the frequency in case they support
+ * "get_min_frequency" host ops.
+ */
+static unsigned long mmc_get_min_frequency(struct mmc_host *host)
+{
+ unsigned long freq;
+
+ if (host->ops && host->ops->get_min_frequency) {
+ freq = host->ops->get_min_frequency(host);
+ goto out;
+ }
+
+ switch (host->ios.timing) {
+ case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_SDR104:
+ freq = UHS_SDR25_MAX_DTR;
+ break;
+ case MMC_TIMING_MMC_HS200:
+ freq = MMC_HIGH_52_MAX_DTR;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ freq = UHS_DDR50_MAX_DTR / 2;
+ break;
+ default:
+ mmc_host_clk_hold(host);
+ freq = host->ios.clock;
+ mmc_host_clk_release(host);
+ break;
+ }
+
+out:
+ return freq;
+}
+
+/*
+ * Scale down clocks to minimum frequency supported.
+ * The delayed work re-arms itself in case it cannot
+ * claim the host.
+ */
+static void mmc_clk_scale_work(struct work_struct *work)
+{
+ struct mmc_host *host = container_of(work, struct mmc_host,
+ clk_scaling.work.work);
+
+ if (!host->card || !host->bus_ops ||
+ !host->bus_ops->change_bus_speed ||
+ !host->clk_scaling.enable || !host->ios.clock)
+ goto out;
+
+ if (!mmc_try_claim_host(host)) {
+ /* retry after a timer tick */
+ queue_delayed_work(system_nrt_wq, &host->clk_scaling.work, 1);
+ goto out;
+ }
+
+ mmc_clk_scaling(host, true);
+ mmc_release_host(host);
+out:
+ return;
+}
+
+
+/**
+ * mmc_clk_scaling() - clock scaling decision algorithm
+ * @host: pointer to mmc host structure
+ * @from_wq: variable that specifies the context in which
+ * mmc_clk_scaling() is called.
+ *
+ * Calculate load percentage based on host busy time
+ * and total sampling interval and decide clock scaling
+ * based on scale up/down thresholds.
+ * If load is greater than up threshold increase the
+ * frequency to maximum as supported by host. Else,
+ * if load is less than down threshold, scale down the
+ * frequency to minimum supported by the host. Otherwise,
+ * retain current frequency and do nothing.
+ */
+static void mmc_clk_scaling(struct mmc_host *host, bool from_wq)
+{
+ int err = 0;
+ struct mmc_card *card = host->card;
+ unsigned long total_time_ms = 0;
+ unsigned long busy_time_ms = 0;
+ unsigned long freq;
+ unsigned int up_threshold = host->clk_scaling.up_threshold;
+ unsigned int down_threshold = host->clk_scaling.down_threshold;
+ bool queue_scale_down_work = false;
+
+ if (!card || !host->bus_ops || !host->bus_ops->change_bus_speed) {
+ pr_err("%s: %s: invalid entry\n", mmc_hostname(host), __func__);
+ goto out;
+ }
+
+ /* Check if the clocks are already gated. */
+ if (!host->ios.clock)
+ goto out;
+
+ if (time_is_after_jiffies(host->clk_scaling.window_time +
+ msecs_to_jiffies(host->clk_scaling.polling_delay_ms)))
+ goto out;
+
+ /* handle time wrap */
+ total_time_ms = jiffies_to_msecs((long)jiffies -
+ (long)host->clk_scaling.window_time);
+
+ /* Check if we re-enter during clock switching */
+ if (unlikely(host->clk_scaling.in_progress))
+ goto out;
+
+ host->clk_scaling.in_progress = true;
+
+ busy_time_ms = host->clk_scaling.busy_time_us / USEC_PER_MSEC;
+
+ freq = host->clk_scaling.curr_freq;
+
+ /*
+ * Note that the max. and min. frequency should be based
+ * on the timing modes that the card and host handshake
+ * during initialization.
+ */
+ if ((busy_time_ms * 100 > total_time_ms * up_threshold)) {
+ freq = mmc_get_max_frequency(host);
+ } else if ((busy_time_ms * 100 < total_time_ms * down_threshold)) {
+ if (!from_wq)
+ queue_scale_down_work = true;
+ freq = mmc_get_min_frequency(host);
+ }
+
+ if (freq != host->clk_scaling.curr_freq) {
+ if (!queue_scale_down_work) {
+ if (!from_wq)
+ cancel_delayed_work_sync(
+ &host->clk_scaling.work);
+ err = host->bus_ops->change_bus_speed(host, &freq);
+ if (!err)
+ host->clk_scaling.curr_freq = freq;
+ else
+ pr_err("%s: %s: failed (%d) at freq=%lu\n",
+ mmc_hostname(host), __func__, err,
+ freq);
+ } else {
+ /*
+ * We hold claim host while queueing the scale down
+ * work, so delay atleast one timer tick to release
+ * host and re-claim while scaling down the clocks.
+ */
+ queue_delayed_work(system_nrt_wq,
+ &host->clk_scaling.work, 1);
+ host->clk_scaling.in_progress = false;
+ goto out;
+ }
+ }
+
+ mmc_reset_clk_scale_stats(host);
+ host->clk_scaling.in_progress = false;
+out:
+ return;
+}
+
+/**
+ * mmc_disable_clk_scaling() - Disable clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Disables clock scaling temporarily by setting enable
+ * property to false. To disable completely, one also
+ * need to set 'initialized' variable to false.
+ */
+void mmc_disable_clk_scaling(struct mmc_host *host)
+{
+ cancel_delayed_work_sync(&host->clk_scaling.work);
+ host->clk_scaling.enable = false;
+}
+EXPORT_SYMBOL_GPL(mmc_disable_clk_scaling);
+
+/**
+ * mmc_can_scale_clk() - Check if clock scaling is initialized
+ * @host: pointer to mmc host structure
+ */
+bool mmc_can_scale_clk(struct mmc_host *host)
+{
+ return host->clk_scaling.initialized;
+}
+EXPORT_SYMBOL_GPL(mmc_can_scale_clk);
+
+/**
+ * mmc_init_clk_scaling() - Initialize clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Initialize clock scaling for supported hosts.
+ * It is assumed that the caller ensure clock is
+ * running at maximum possible frequency before
+ * calling this function.
+ */
+void mmc_init_clk_scaling(struct mmc_host *host)
+{
+ if (!host->card || !(host->caps2 & MMC_CAP2_CLK_SCALE))
+ return;
+
+ INIT_DELAYED_WORK(&host->clk_scaling.work, mmc_clk_scale_work);
+ host->clk_scaling.curr_freq = mmc_get_max_frequency(host);
+ mmc_reset_clk_scale_stats(host);
+ host->clk_scaling.enable = true;
+ host->clk_scaling.initialized = true;
+ pr_debug("%s: clk scaling enabled\n", mmc_hostname(host));
+}
+EXPORT_SYMBOL_GPL(mmc_init_clk_scaling);
+
+/**
+ * mmc_exit_clk_scaling() - Disable clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Disable clock scaling permanently.
+ */
+void mmc_exit_clk_scaling(struct mmc_host *host)
+{
+ cancel_delayed_work_sync(&host->clk_scaling.work);
+ memset(&host->clk_scaling, 0, sizeof(host->clk_scaling));
+}
+EXPORT_SYMBOL_GPL(mmc_exit_clk_scaling);
+
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index de87e82..c85f5aa 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -81,5 +81,11 @@
void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card);
+extern void mmc_disable_clk_scaling(struct mmc_host *host);
+extern bool mmc_can_scale_clk(struct mmc_host *host);
+extern void mmc_init_clk_scaling(struct mmc_host *host);
+extern void mmc_exit_clk_scaling(struct mmc_host *host);
+extern void mmc_reset_clk_scale_stats(struct mmc_host *host);
+extern unsigned long mmc_get_max_frequency(struct mmc_host *host);
#endif
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 850872d..d30f10f 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -163,6 +163,9 @@
if (host->clk_gated) {
spin_unlock_irqrestore(&host->clk_lock, flags);
mmc_ungate_clock(host);
+
+ /* Reset clock scaling stats as host is out of idle */
+ mmc_reset_clk_scale_stats(host);
spin_lock_irqsave(&host->clk_lock, flags);
pr_debug("%s: ungated MCI clock\n", mmc_hostname(host));
}
@@ -446,6 +449,10 @@
#endif
mmc_host_clk_sysfs_init(host);
+ host->clk_scaling.up_threshold = 35;
+ host->clk_scaling.down_threshold = 5;
+ host->clk_scaling.polling_delay_ms = 100;
+
err = sysfs_create_group(&host->parent->kobj, &dev_attr_grp);
if (err)
pr_err("%s: failed to create sysfs group with err %d\n",
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a98ed3d..d3dc133 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1460,6 +1460,7 @@
mmc_claim_host(host);
host->card = NULL;
+ mmc_exit_clk_scaling(host);
mmc_release_host(host);
}
@@ -1510,6 +1511,12 @@
BUG_ON(!host);
BUG_ON(!host->card);
+ /*
+ * Disable clock scaling before suspend and enable it after resume so
+ * as to avoid clock scaling decisions kicking in during this window.
+ */
+ mmc_disable_clk_scaling(host);
+
mmc_claim_host(host);
if (mmc_can_poweroff_notify(host->card))
err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
@@ -1540,6 +1547,13 @@
err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
+ /*
+ * We have done full initialization of the card,
+ * reset the clk scale stats and current frequency.
+ */
+ if (mmc_can_scale_clk(host))
+ mmc_init_clk_scaling(host);
+
return err;
}
@@ -1547,11 +1561,17 @@
{
int ret;
+ /* Disable clk scaling to avoid switching frequencies intermittently */
+ mmc_disable_clk_scaling(host);
+
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_claim_host(host);
ret = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
+ if (mmc_can_scale_clk(host))
+ mmc_init_clk_scaling(host);
+
return ret;
}
@@ -1686,6 +1706,10 @@
if (err)
goto remove_card;
+ /* Initialize clock scaling only for high frequency modes */
+ if (mmc_card_hs200(host->card) || mmc_card_ddr_mode(host->card))
+ mmc_init_clk_scaling(host);
+
return 0;
remove_card:
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 8661929..318d590 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1121,6 +1121,7 @@
mmc_claim_host(host);
host->card = NULL;
+ mmc_exit_clk_scaling(host);
mmc_release_host(host);
}
@@ -1187,6 +1188,12 @@
BUG_ON(!host);
BUG_ON(!host->card);
+ /*
+ * Disable clock scaling before suspend and enable it after resume so
+ * as to avoid clock scaling decisions kicking in during this window.
+ */
+ mmc_disable_clk_scaling(host);
+
mmc_claim_host(host);
if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
@@ -1235,6 +1242,13 @@
#endif
mmc_release_host(host);
+ /*
+ * We have done full initialization of the card,
+ * reset the clk scale stats and current frequency.
+ */
+ if (mmc_can_scale_clk(host))
+ mmc_init_clk_scaling(host);
+
return err;
}
@@ -1242,11 +1256,17 @@
{
int ret;
+ /* Disable clk scaling to avoid switching frequencies intermittently */
+ mmc_disable_clk_scaling(host);
+
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
ret = mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host);
+ if (mmc_can_scale_clk(host))
+ mmc_init_clk_scaling(host);
+
return ret;
}
@@ -1385,6 +1405,10 @@
if (err)
goto remove_card;
+ /* Initialize clock scaling only for high frequency modes */
+ if (mmc_card_uhs(host->card))
+ mmc_init_clk_scaling(host);
+
return 0;
remove_card:
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 3b813d8..6623d81 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -176,7 +176,8 @@
int soc_at_cv;
int prev_chg_soc;
int calculated_soc;
- int last_vbat_read_uv;
+ int prev_voltage_based_soc;
+ bool use_voltage_soc;
};
static struct of_device_id qpnp_bms_match_table[] = {
@@ -196,37 +197,6 @@
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
-static bool use_voltage_soc;
-
-/* module params */
-static int bms_param_set_bool(const char *val, const struct kernel_param *kp)
-{
- int rc;
- struct power_supply *bms_psy;
-
- rc = param_set_bool(val, kp);
- if (rc) {
- pr_err("failed to set %s, rc = %d\n", kp->name, rc);
- return rc;
- }
-
- bms_psy = power_supply_get_by_name("bms");
-
- if (bms_psy)
- power_supply_changed(bms_psy);
- else
- pr_debug("%s changed but bms has not been initialized yet\n",
- kp->name);
-
- return 0;
-}
-
-static struct kernel_param_ops bms_param_ops = {
- .set = bms_param_set_bool,
- .get = param_get_bool,
-};
-
-module_param_cb(use_voltage_soc, &bms_param_ops, &use_voltage_soc, 0644);
static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
u16 base, int count)
@@ -370,6 +340,10 @@
s64 result_uv;
pr_debug("adjusting_uv = %lld\n", uv);
+ if (gain == 0) {
+ pr_debug("gain is %d, not adjusting\n", gain);
+ return uv;
+ }
pr_debug("adjusting by factor: %lld/%hu = %lld%%\n",
QPNP_ADC_GAIN_NV, gain,
div_s64(QPNP_ADC_GAIN_NV * 100LL, (s64)gain));
@@ -1381,12 +1355,12 @@
}
chip->calculated_soc = new_calculated_soc;
- pr_debug("Set calculated SOC = %d\n", chip->calculated_soc);
+ pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc);
chip->first_time_calc_soc = 0;
return chip->calculated_soc;
}
-static void read_vbat(struct qpnp_bms_chip *chip)
+static int read_vbat(struct qpnp_bms_chip *chip)
{
int rc;
struct qpnp_vadc_result result;
@@ -1395,9 +1369,35 @@
if (rc) {
pr_err("error reading vadc VBAT_SNS = %d, rc = %d\n",
VBAT_SNS, rc);
- return;
+ return rc;
}
- chip->last_vbat_read_uv = (int)result.physical;
+ pr_debug("read %duv from vadc\n", (int)result.physical);
+ return (int)result.physical;
+}
+
+static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
+{
+ int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
+ int vbat_uv;
+
+ vbat_uv = read_vbat(chip);
+
+ voltage_range_uv = chip->max_voltage_uv - chip->v_cutoff_uv;
+ voltage_remaining_uv = vbat_uv - chip->v_cutoff_uv;
+ voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv;
+
+ voltage_based_soc = clamp(voltage_based_soc, 0, 100);
+
+ if (chip->prev_voltage_based_soc != voltage_based_soc
+ && chip->bms_psy.name != NULL) {
+ power_supply_changed(&chip->bms_psy);
+ pr_debug("power supply changed\n");
+ }
+ chip->prev_voltage_based_soc = voltage_based_soc;
+
+ pr_debug("vbat used = %duv\n", vbat_uv);
+ pr_debug("Calculated voltage based soc = %d\n", voltage_based_soc);
+ return voltage_based_soc;
}
static void calculate_soc_work(struct work_struct *work)
@@ -1409,22 +1409,25 @@
struct qpnp_vadc_result result;
struct raw_soc_params raw;
- read_vbat(chip);
-
- rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
- if (rc) {
- pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
- LR_MUX1_BATT_THERM, rc);
- return;
- }
- pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ if (chip->use_voltage_soc) {
+ soc = calculate_soc_from_voltage(chip);
+ } else {
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+ if (rc) {
+ pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n",
+ result.physical,
result.measurement);
- batt_temp = (int)result.physical;
+ batt_temp = (int)result.physical;
- mutex_lock(&chip->last_ocv_uv_mutex);
- read_soc_params_raw(chip, &raw);
- soc = calculate_state_of_charge(chip, &raw, batt_temp);
- mutex_unlock(&chip->last_ocv_uv_mutex);
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ read_soc_params_raw(chip, &raw);
+ soc = calculate_state_of_charge(chip, &raw, batt_temp);
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+ }
if (soc < chip->low_soc_calc_threshold)
schedule_delayed_work(&chip->calculate_soc_delayed_work,
@@ -1514,7 +1517,14 @@
static int bms_fake_battery = -EINVAL;
module_param(bms_fake_battery, int, 0644);
-static int report_state_of_charge(struct qpnp_bms_chip *chip)
+static int report_voltage_based_soc(struct qpnp_bms_chip *chip)
+{
+ pr_debug("Reported voltage based soc = %d\n",
+ chip->prev_voltage_based_soc);
+ return chip->prev_voltage_based_soc;
+}
+
+static int report_cc_based_soc(struct qpnp_bms_chip *chip)
{
int soc;
int delta_time_us;
@@ -1523,11 +1533,6 @@
int batt_temp;
int rc;
- if (bms_fake_battery != -EINVAL) {
- pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
- return bms_fake_battery;
- }
-
soc = chip->calculated_soc;
rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
@@ -1598,27 +1603,21 @@
return chip->last_soc;
}
-static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
{
- int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
-
- if (chip->last_vbat_read_uv < 0)
- read_vbat(chip);
-
- voltage_range_uv = chip->max_voltage_uv - chip->v_cutoff_uv;
- voltage_remaining_uv = chip->last_vbat_read_uv - chip->v_cutoff_uv;
- voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv;
-
- return clamp(voltage_based_soc, 0, 100);
+ if (bms_fake_battery != -EINVAL) {
+ pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+ return bms_fake_battery;
+ } else if (chip->use_voltage_soc)
+ return report_voltage_based_soc(chip);
+ else
+ return report_cc_based_soc(chip);
}
/* Returns capacity as a SoC percentage between 0 and 100 */
static int get_prop_bms_capacity(struct qpnp_bms_chip *chip)
{
- if (use_voltage_soc)
- return calculate_soc_from_voltage(chip);
- else
- return report_state_of_charge(chip);
+ return report_state_of_charge(chip);
}
/* Returns instantaneous current in uA */
@@ -1873,7 +1872,7 @@
chip->ignore_shutdown_soc = of_property_read_bool(
chip->spmi->dev.of_node,
"qcom,bms-ignore-shutdown-soc");
- use_voltage_soc = of_property_read_bool(chip->spmi->dev.of_node,
+ chip->use_voltage_soc = of_property_read_bool(chip->spmi->dev.of_node,
"qcom,bms-use-voltage-soc");
if (chip->adjust_soc_low_threshold >= 45)
@@ -1889,7 +1888,7 @@
chip->adjust_soc_high_threshold, chip->chg_term_ua,
chip->batt_type);
pr_debug("ignore_shutdown_soc:%d, use_voltage_soc:%d\n",
- chip->ignore_shutdown_soc, use_voltage_soc);
+ chip->ignore_shutdown_soc, chip->use_voltage_soc);
return 0;
}
@@ -1902,7 +1901,6 @@
chip->soc_at_cv = -EINVAL;
chip->calculated_soc = -EINVAL;
chip->last_soc = -EINVAL;
- chip->last_vbat_read_uv = -EINVAL;
chip->last_soc_est = -EINVAL;
chip->first_time_calc_soc = 1;
chip->first_time_calc_uuc = 1;
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 0497a32..7b6b890 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -2658,7 +2658,8 @@
err_probe_reg_master:
err_probe_irq:
err_probe_state:
- dd->dma_teardown(dd);
+ if (dd->dma_teardown)
+ dd->dma_teardown(dd);
err_probe_dma:
err_probe_gsbi:
if (pclk_enabled)
@@ -2741,7 +2742,8 @@
spi_debugfs_exit(dd);
sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
- dd->dma_teardown(dd);
+ if (dd->dma_teardown)
+ dd->dma_teardown(dd);
clk_put(dd->clk);
clk_put(dd->pclk);
destroy_workqueue(dd->workqueue);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a315587..b9e95ab 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -774,10 +774,12 @@
if (aca_enabled())
return 0;
- if (atomic_read(&motg->in_lpm) == suspend &&
- !atomic_read(&motg->suspend_work_pending))
- return 0;
-
+ /*
+ * UDC and HCD call usb_phy_set_suspend() to enter/exit LPM
+ * during bus suspend/resume. Update the relevant state
+ * machine inputs and trigger LPM entry/exit. Checking
+ * in_lpm flag would avoid unnecessary work scheduling.
+ */
if (suspend) {
switch (phy->state) {
case OTG_STATE_A_WAIT_BCON:
@@ -787,16 +789,18 @@
case OTG_STATE_A_HOST:
pr_debug("host bus suspend\n");
clear_bit(A_BUS_REQ, &motg->inputs);
- queue_work(system_nrt_wq, &motg->sm_work);
+ if (!atomic_read(&motg->in_lpm))
+ queue_work(system_nrt_wq, &motg->sm_work);
break;
case OTG_STATE_B_PERIPHERAL:
pr_debug("peripheral bus suspend\n");
if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
break;
set_bit(A_BUS_SUSPEND, &motg->inputs);
- atomic_set(&motg->suspend_work_pending, 1);
- queue_delayed_work(system_nrt_wq, &motg->suspend_work,
- USB_SUSPEND_DELAY_TIME);
+ if (!atomic_read(&motg->in_lpm))
+ queue_delayed_work(system_nrt_wq,
+ &motg->suspend_work,
+ USB_SUSPEND_DELAY_TIME);
break;
default:
@@ -804,20 +808,29 @@
}
} else {
switch (phy->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ /* Remote wakeup or resume */
+ set_bit(A_BUS_REQ, &motg->inputs);
+ /* ensure hardware is not in low power mode */
+ if (atomic_read(&motg->in_lpm))
+ pm_runtime_resume(phy->dev);
+ break;
case OTG_STATE_A_SUSPEND:
/* Remote wakeup or resume */
set_bit(A_BUS_REQ, &motg->inputs);
phy->state = OTG_STATE_A_HOST;
/* ensure hardware is not in low power mode */
- pm_runtime_resume(phy->dev);
+ if (atomic_read(&motg->in_lpm))
+ pm_runtime_resume(phy->dev);
break;
case OTG_STATE_B_PERIPHERAL:
pr_debug("peripheral bus resume\n");
if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
break;
clear_bit(A_BUS_SUSPEND, &motg->inputs);
- queue_work(system_nrt_wq, &motg->sm_work);
+ if (atomic_read(&motg->in_lpm))
+ queue_work(system_nrt_wq, &motg->sm_work);
break;
default:
break;
@@ -2948,8 +2961,10 @@
{
struct msm_otg *motg =
container_of(w, struct msm_otg, suspend_work.work);
- atomic_set(&motg->suspend_work_pending, 0);
- msm_otg_sm_work(&motg->sm_work);
+
+ /* This work is only for device bus suspend */
+ if (test_bit(A_BUS_SUSPEND, &motg->inputs))
+ msm_otg_sm_work(&motg->sm_work);
}
static irqreturn_t msm_otg_irq(int irq, void *data)
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index ee2405e..67ef8bf 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -96,11 +96,6 @@
#define MDP4_PANEL_WRITEBACK BIT(6)
enum {
- OVERLAY_MODE_NONE,
- OVERLAY_MODE_BLT
-};
-
-enum {
OVERLAY_REFRESH_ON_DEMAND,
OVERLAY_REFRESH_VSYNC,
OVERLAY_REFRESH_VSYNC_HALF,
@@ -451,7 +446,6 @@
void mdp4_intr_clear_set(ulong clear, ulong set);
void mdp4_dma_p_cfg(void);
unsigned is_mdp4_hw_reset(void);
-void mdp4_overlay_cfg_init(void);
void mdp4_hw_init(void);
void mdp4_isr_read(int);
void mdp4_clear_lcdc(void);
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 05344fc..f4332dd 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -53,7 +53,6 @@
uint32 flush[MDP4_MIXER_MAX];
struct iommu_free_list iommu_free[MDP4_MIXER_MAX];
uint32 cs_controller;
- uint32 hw_version;
uint32 panel_3d;
uint32 panel_mode;
uint32 mixer0_played;
@@ -253,7 +252,7 @@
pipe->pipe_ndx, plane);
if (ion_map_iommu(display_iclient, *srcp_ihdl,
DISPLAY_READ_DOMAIN, GEN_POOL, SZ_4K, 0, start,
- len, 0, ION_IOMMU_UNMAP_DELAYED)) {
+ len, 0, 0)) {
ion_free(display_iclient, *srcp_ihdl);
pr_err("ion_map_iommu() failed\n");
return -EINVAL;
@@ -354,23 +353,9 @@
return ctrl->panel_mode;
}
-void mdp4_overlay_cfg_init(void)
-{
- if (ctrl->hw_version == 0) {
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- ctrl->hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- }
-
- if (ctrl->hw_version >= 0x0402030b) {
- /* MDP_LAYERMIXER_IN_CFG_UPDATE_METHOD */
- outpdw(MDP_BASE + 0x100fc, 0x01);
- }
-}
-
int mdp4_overlay_borderfill_supported(void)
{
- return (ctrl->hw_version >= 0x0402030b);
+ return (mdp_rev >= MDP_REV_42);
}
void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv)
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 239d9f5..450f1de 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -56,7 +56,8 @@
int wait_vsync_cnt;
int blt_change;
int blt_free;
- int blt_ctrl;
+ u32 blt_ctrl;
+ u32 blt_mode;
int sysfs_created;
struct mutex update_lock;
struct completion ov_comp;
@@ -197,18 +198,6 @@
}
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- if (vctrl->blt_change) {
- pipe = vctrl->base_pipe;
- spin_lock_irqsave(&vctrl->spin_lock, flags);
- INIT_COMPLETION(vctrl->dmap_comp);
- INIT_COMPLETION(vctrl->ov_comp);
- vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
- spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- mdp4_dsi_video_wait4dmap(0);
- if (pipe->ov_blt_addr)
- mdp4_dsi_video_wait4ov(0);
- }
-
pipe = vp->plist;
for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
@@ -498,7 +487,6 @@
vctrl = &vsync_ctrl_db[cndx];
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
- pinfo = &mfd->panel_info;
if (!mfd)
return -ENODEV;
@@ -508,7 +496,9 @@
vctrl->mfd = mfd;
vctrl->dev = mfd->fbi->dev;
+ pinfo = &mfd->panel_info;
vctrl->blt_ctrl = pinfo->lcd.blt_ctrl;
+ vctrl->blt_mode = pinfo->lcd.blt_mode;
/* mdp clock on */
mdp_clk_ctrl(1);
@@ -551,6 +541,7 @@
mfd->cont_splash_done = 1;
mdp4_dsi_video_wait4dmap_done(0);
MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+ dsi_video_enabled = 0;
mipi_dsi_controller_cfg(0);
/* Clks are enabled in probe.
Disabling clocks now */
@@ -940,20 +931,8 @@
if (vctrl->blt_change) {
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_dmap_xy(pipe);
- if (pipe->ov_blt_addr) {
- mdp4_dsi_video_blt_ov_update(pipe);
- pipe->ov_cnt++;
- /* Prefill one frame */
- vsync_irq_enable(INTR_OVERLAY0_DONE,
- MDP_OVERLAY0_TERM);
- /* kickoff overlay0 engine */
- mdp4_stat.kickoff_ov0++;
- vctrl->ov_koff++; /* make up for prefill */
- outpdw(MDP_BASE + 0x0004, 0);
- }
vctrl->blt_change = 0;
}
-
complete_all(&vctrl->dmap_comp);
mdp4_overlay_dma_commit(cndx);
spin_unlock(&vctrl->spin_lock);
@@ -997,19 +976,43 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
long long vtime;
+ u32 mode, ctrl;
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
- mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+ mode = (dbg_force_ov0_blt & 0x0f) ?
+ (dbg_force_ov0_blt & 0x0f) : vctrl->blt_mode;
+ ctrl = (dbg_force_ov0_blt >> 4) ?
+ (dbg_force_ov0_blt >> 4) : vctrl->blt_ctrl;
- if (mfd->ov0_wb_buf->write_addr == 0) {
- pr_info("%s: no blt_base assigned\n", __func__);
+ pr_debug("%s: mode=%d, enable=%d ov_blt_addr=%x\n",
+ __func__, mode, enable, (int)pipe->ov_blt_addr);
+
+ if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF) &&
+ !pipe->ov_blt_addr)
return;
+ else if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_ON) &&
+ pipe->ov_blt_addr)
+ return;
+ else if (enable && pipe->ov_blt_addr)
+ return;
+ else if (!enable && !pipe->ov_blt_addr)
+ return;
+
+ if (pipe->ov_blt_addr == 0) {
+ mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+ if (mfd->ov0_wb_buf->write_addr == 0) {
+ pr_warning("%s: no blt_base assigned\n", __func__);
+ return;
+ }
}
+ pr_debug("%s: mode=%d, enable=%d ov_blt_addr=%x\n",
+ __func__, mode, enable, (int)pipe->ov_blt_addr);
+
spin_lock_irqsave(&vctrl->spin_lock, flag);
- if (enable && pipe->ov_blt_addr == 0) {
+ if (pipe->ov_blt_addr == 0) {
pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
pipe->ov_cnt = 0;
@@ -1018,36 +1021,38 @@
vctrl->ov_done = 0;
vctrl->blt_free = 0;
mdp4_stat.blt_dsi_video++;
- vctrl->blt_change++;
- } else if (enable == 0 && pipe->ov_blt_addr) {
+ } else {
pipe->ov_blt_addr = 0;
pipe->dma_blt_addr = 0;
vctrl->blt_free = 4; /* 4 commits to free wb buf */
- vctrl->blt_change++;
- }
-
- pr_info("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__,
- vctrl->blt_change, enable, (int)pipe->ov_blt_addr);
-
- if (!vctrl->blt_change) {
- spin_unlock_irqrestore(&vctrl->spin_lock, flag);
- return;
}
spin_unlock_irqrestore(&vctrl->spin_lock, flag);
- if (vctrl->blt_ctrl == BLT_SWITCH_TG_OFF) {
- int tg_enabled;
-
- vctrl->blt_change = 0;
- tg_enabled = inpdw(MDP_BASE + DSI_VIDEO_BASE) & 0x01;
- if (tg_enabled) {
+ if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_ON) {
+ spin_lock_irqsave(&vctrl->spin_lock, flag);
+ if (!dsi_video_enabled) {
+ pr_debug("%s: blt switched not in ISR dsi_video_enabled=%d\n",
+ __func__, dsi_video_enabled);
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmap_xy(pipe);
+ } else {
+ pr_debug("%s: blt switched in ISR dsi_video_enabled=%d\n",
+ __func__, dsi_video_enabled);
+ vctrl->blt_change++;
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flag);
+ if (dsi_video_enabled)
+ mdp4_dsi_video_wait4dmap_done(0);
+ } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_OFF) {
+ pr_debug("%s: blt switched by turning TG off\n", __func__);
+ if (dsi_video_enabled) {
mdp4_dsi_video_wait4vsync(0, &vtime);
MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
mdp4_dsi_video_wait4dmap_done(0);
}
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_dmap_xy(pipe);
- if (tg_enabled) {
+ if (dsi_video_enabled) {
/*
* need wait for more than 1 ms to
* make sure dsi lanes' fifo is empty and
@@ -1058,7 +1063,15 @@
mipi_dsi_sw_reset();
MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1);
}
- }
+ } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_POLL) {
+ pr_debug("%s: blt switched by polling mdp status\n", __func__);
+ if (dsi_video_enabled)
+ while (inpdw(MDP_BASE + 0x0018) & 0x05)
+ cpu_relax();
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmap_xy(pipe);
+ } else
+ pr_err("%s: ctrl=%d is not supported\n", __func__, ctrl);
}
void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
@@ -1125,4 +1138,3 @@
mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
}
-
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 1de5d6e..7de2e2a 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -66,6 +66,9 @@
int dmae_wait_cnt;
int wait_vsync_cnt;
int blt_change;
+ int blt_ctrl;
+ int blt_mode;
+ int blt_free;
int sysfs_created;
struct mutex update_lock;
struct completion ov_comp;
@@ -159,6 +162,7 @@
}
static void mdp4_dtv_blt_ov_update(struct mdp4_overlay_pipe *pipe);
+static void mdp4_dtv_wait4ov(int cndx);
static void mdp4_dtv_wait4dmae(int cndx);
int mdp4_dtv_pipe_commit(int cndx, int wait)
@@ -191,6 +195,13 @@
vctrl->update_ndx++;
vctrl->update_ndx &= 0x01;
vp->update_cnt = 0; /* reset */
+
+ if (vctrl->blt_free) {
+ vctrl->blt_free--;
+ if (vctrl->blt_free == 0)
+ mdp4_free_writeback_buf(vctrl->mfd, mixer);
+ }
+
mutex_unlock(&vctrl->update_lock);
pipe = vp->plist;
@@ -220,6 +231,7 @@
if (pipe->ov_blt_addr) {
mdp4_dtv_blt_ov_update(pipe);
pipe->blt_ov_done++;
+ INIT_COMPLETION(vctrl->ov_comp);
vsync_irq_enable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM);
mb();
pipe->blt_ov_koff++;
@@ -234,9 +246,12 @@
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
mdp4_stat.overlay_commit[pipe->mixer_num]++;
- if (wait)
- mdp4_dtv_wait4dmae(cndx);
-
+ if (wait) {
+ if (pipe->ov_blt_addr)
+ mdp4_dtv_wait4ov(cndx);
+ else
+ mdp4_dtv_wait4dmae(cndx);
+ }
return cnt;
}
@@ -296,6 +311,23 @@
*vtime = ktime_to_ns(vctrl->vsync_time);
}
+static void mdp4_dtv_wait4ov(int cndx)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ wait_for_completion(&vctrl->ov_comp);
+}
+
static void mdp4_dtv_wait4dmae(int cndx)
{
struct vsycn_ctrl *vctrl;
@@ -367,6 +399,28 @@
return ret;
}
+static void mdp4_dtv_wait4dmae_done(int cndx)
+{
+ unsigned long flags;
+ struct vsycn_ctrl *vctrl;
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+
+ if (atomic_read(&vctrl->suspend) > 0)
+ return;
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ INIT_COMPLETION(vctrl->dmae_comp);
+ vsync_irq_enable(INTR_DMA_E_DONE, MDP_DMA_E_TERM);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+ mdp4_dtv_wait4dmae(cndx);
+}
+
void mdp4_dtv_vsync_init(int cndx)
{
struct vsycn_ctrl *vctrl;
@@ -554,6 +608,7 @@
/* enable DTV block */
MDP_OUTP(MDP_BASE + DTV_BASE, 1);
+ dtv_enabled = 1;
return 0;
}
@@ -568,6 +623,7 @@
return -EINVAL;
MDP_OUTP(MDP_BASE + DTV_BASE, 0);
+ dtv_enabled = 0;
return 0;
}
@@ -578,6 +634,7 @@
int ret = 0;
int cndx = 0;
struct vsycn_ctrl *vctrl;
+ struct msm_panel_info *pinfo;
vctrl = &vsync_ctrl_db[cndx];
@@ -589,7 +646,12 @@
if (mfd->key != MFD_KEY)
return -EINVAL;
+ vctrl->mfd = mfd;
vctrl->dev = mfd->fbi->dev;
+ pinfo = &mfd->panel_info;
+
+ vctrl->blt_ctrl = pinfo->lcd.blt_ctrl;
+ vctrl->blt_mode = pinfo->lcd.blt_mode;
mdp_footswitch_ctrl(TRUE);
/* Mdp clock enable */
@@ -673,7 +735,7 @@
if (vctrl->vsync_irq_enabled) {
vctrl->vsync_irq_enabled = 0;
- vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM);
+ vsync_irq_disable(INTR_EXTERNAL_VSYNC, MDP_EXTER_VSYNC_TERM);
}
undx = vctrl->update_ndx;
@@ -748,11 +810,6 @@
MDP_OUTP(MDP_BASE + 0xb0008, addr);
}
-void mdp4_overlay_dtv_set_perf(struct msm_fb_data_type *mfd)
-{
-
-}
-
static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd,
int32 ptype, struct vsycn_ctrl *vctrl)
{
@@ -940,18 +997,8 @@
spin_lock(&vctrl->spin_lock);
if (vctrl->blt_change) {
- if (pipe->ov_blt_addr) {
- mdp4_overlayproc_cfg(pipe);
- mdp4_overlay_dmae_xy(pipe);
- mdp4_dtv_blt_ov_update(pipe);
- pipe->blt_ov_done++;
-
- /* Prefill one frame */
- vsync_irq_enable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM);
- /* kickoff overlay1 engine */
- mdp4_stat.kickoff_ov1++;
- outpdw(MDP_BASE + 0x0008, 0);
- }
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmae_xy(pipe);
vctrl->blt_change = 0;
}
@@ -986,6 +1033,7 @@
}
mdp4_dtv_blt_dmae_update(pipe);
+ complete_all(&vctrl->ov_comp);
pipe->blt_dmap_done++;
vsync_irq_disable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM);
spin_unlock(&vctrl->spin_lock);
@@ -1039,63 +1087,100 @@
static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable)
{
unsigned long flag;
- int data;
int cndx = 0;
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ u32 mode, ctrl;
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
- mdp4_allocate_writeback_buf(mfd, MDP4_MIXER1);
+ mode = (dbg_force_ov1_blt & 0x0f) ?
+ (dbg_force_ov1_blt & 0x0f) : vctrl->blt_mode;
+ ctrl = (dbg_force_ov1_blt >> 4) ?
+ (dbg_force_ov1_blt >> 4) : vctrl->blt_ctrl;
- if (!mfd->ov1_wb_buf->write_addr) {
- pr_info("%s: ctrl=%d blt_base NOT assigned\n", __func__, cndx);
+ pr_debug("%s: mode=%d, ctrl = %d, enable=%d ov_blt_addr=%x\n",
+ __func__, mode, ctrl, enable, (int)pipe->ov_blt_addr);
+
+ if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF) &&
+ !pipe->ov_blt_addr)
return;
+ else if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_ON) &&
+ pipe->ov_blt_addr)
+ return;
+ else if (enable && pipe->ov_blt_addr)
+ return;
+ else if (!enable && !pipe->ov_blt_addr)
+ return;
+
+ if (pipe->ov_blt_addr == 0) {
+ mdp4_allocate_writeback_buf(vctrl->mfd, MDP4_MIXER1);
+ if (!vctrl->mfd->ov1_wb_buf->write_addr) {
+ pr_warning("%s: ctrl=%d blt_base NOT assigned\n",
+ __func__, cndx);
+ return;
+ }
}
+ pr_debug("%s: mode=%d, ctrl=%d, enable=%d ov_blt_addr=%x\n",
+ __func__, mode, ctrl, enable, (int)pipe->ov_blt_addr);
+
spin_lock_irqsave(&vctrl->spin_lock, flag);
if (enable && pipe->ov_blt_addr == 0) {
- pipe->ov_blt_addr = mfd->ov1_wb_buf->write_addr;
- pipe->dma_blt_addr = mfd->ov1_wb_buf->read_addr;
+ pipe->ov_blt_addr = vctrl->mfd->ov1_wb_buf->write_addr;
+ pipe->dma_blt_addr = vctrl->mfd->ov1_wb_buf->read_addr;
pipe->blt_cnt = 0;
pipe->ov_cnt = 0;
pipe->blt_dmap_done = 0;
pipe->blt_ov_koff = 0;
pipe->blt_ov_done = 0;
mdp4_stat.blt_dtv++;
- vctrl->blt_change++;
+ vctrl->blt_free = 0;
} else if (enable == 0 && pipe->ov_blt_addr) {
pipe->ov_blt_addr = 0;
pipe->dma_blt_addr = 0;
- vctrl->blt_change++;
+ vctrl->blt_free = 4;
}
-
- pr_info("%s: enable=%d change=%d blt_addr=%x\n", __func__,
- enable, vctrl->blt_change, (int)pipe->ov_blt_addr);
-
- if (!vctrl->blt_change) {
- spin_unlock_irqrestore(&vctrl->spin_lock, flag);
- return;
- }
-
- atomic_set(&vctrl->suspend, 1);
spin_unlock_irqrestore(&vctrl->spin_lock, flag);
- data = inpdw(MDP_BASE + DTV_BASE);
- data &= 0x01;
- if (data) /* timing generator enabled */
- mdp4_dtv_wait4dmae(0);
+ if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_ON) {
+ spin_lock_irqsave(&vctrl->spin_lock, flag);
+ if (!dtv_enabled) {
+ pr_debug("%s: blt switched not in isr dtv_enabled=%d\n",
+ __func__, dtv_enabled);
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmae_xy(pipe);
+ } else {
+ pr_debug("%s: blt switched in ISR dtv_enabled=%d\n",
+ __func__, dtv_enabled);
+ vctrl->blt_change++;
- if (pipe->ov_blt_addr == 0) {
- MDP_OUTP(MDP_BASE + DTV_BASE, 0); /* stop dtv */
- msleep(20);
+ }
+ spin_unlock_irqrestore(&vctrl->spin_lock, flag);
+ if (dtv_enabled)
+ mdp4_dtv_wait4dmae_done(0);
+ } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_OFF) {
+ pr_debug("%s: dtv blt switched by turning TG off\n",
+ __func__);
+ if (dtv_enabled) {
+ mdp4_dtv_wait4dmae_done(0);
+ MDP_OUTP(MDP_BASE + DTV_BASE, 0);
+ msleep(20);
+ }
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_dmae_xy(pipe);
- MDP_OUTP(MDP_BASE + DTV_BASE, 1); /* start dtv */
- }
-
- atomic_set(&vctrl->suspend, 0);
+ if (dtv_enabled)
+ MDP_OUTP(MDP_BASE + DTV_BASE, 1);
+ } else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_POLL) {
+ pr_debug("%s: dtv blt change by polling status\n",
+ __func__);
+ while (inpdw(MDP_BASE + 0x0018) & 0x12)
+ cpu_relax();
+ mdp4_overlayproc_cfg(pipe);
+ mdp4_overlay_dmae_xy(pipe);
+ } else
+ pr_err("%s: ctrl=%d is not supported\n", __func__, ctrl);
}
void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd)
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index ee9ca3c..b6c2634 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -454,8 +454,6 @@
clk_rate = mdp_get_core_clk();
mdp4_fetch_cfg(clk_rate);
- mdp4_overlay_cfg_init();
-
/* Mark hardware as initialized. Only revisions > v2.1 have a register
* for tracking core reset status. */
if (mdp_hw_revision > MDP4_REVISION_V2_1)
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index b760388..5e57de6 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -64,6 +64,9 @@
u32 fb_imgType;
u32 dst_format;
int vsync_pending;
+ ktime_t vsync_time;
+ struct completion vsync_comp;
+ spinlock_t vsync_lock;
int hw_refresh;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 9508846..052d78c 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 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
@@ -13,8 +13,6 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
-#include <linux/workqueue.h>
-
#include "mdss_fb.h"
#include "mdss_mdp.h"
@@ -39,7 +37,6 @@
#define MAX_SESSIONS 3
struct mdss_mdp_video_ctx {
- u32 ctl_num;
u32 pp_num;
u8 ref_cnt;
@@ -47,11 +44,9 @@
struct completion pp_comp;
struct completion vsync_comp;
- struct mutex vsync_lock;
- struct work_struct vsync_work;
+ atomic_t vsync_ref;
+ spinlock_t vsync_lock;
mdp_vsync_handler_t vsync_handler;
- void *vsync_ptr;
- ktime_t vsync_time;
};
struct mdss_mdp_video_ctx mdss_mdp_video_ctx_list[MAX_SESSIONS];
@@ -157,43 +152,43 @@
return 0;
}
-static void send_vsync_work(struct work_struct *work)
-{
- struct mdss_mdp_video_ctx *ctx;
- ctx = container_of(work, typeof(*ctx), vsync_work);
- mutex_lock(&ctx->vsync_lock);
- if (ctx->vsync_handler)
- ctx->vsync_handler(ctx->vsync_ptr, ctx->vsync_time);
- mutex_unlock(&ctx->vsync_lock);
+static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+
+ if (atomic_inc_return(&ctx->vsync_ref) == 1)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+}
+
+static inline void video_vsync_irq_disable(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+
+ if (atomic_dec_return(&ctx->vsync_ref) == 0)
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
}
static int mdss_mdp_video_set_vsync_handler(struct mdss_mdp_ctl *ctl,
mdp_vsync_handler_t vsync_handler)
{
struct mdss_mdp_video_ctx *ctx;
+ unsigned long flags;
ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
if (!ctx) {
pr_err("invalid ctx for ctl=%d\n", ctl->num);
return -ENODEV;
}
- if (mutex_lock_interruptible(&ctx->vsync_lock))
- return -EINTR;
- if (vsync_handler && !ctx->timegen_en) {
- ctx->vsync_time = ktime_get();
- schedule_work(&ctx->vsync_work);
- }
-
+ spin_lock_irqsave(&ctx->vsync_lock, flags);
if (!ctx->vsync_handler && vsync_handler)
- mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+ video_vsync_irq_enable(ctl);
else if (ctx->vsync_handler && !vsync_handler)
- mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+ video_vsync_irq_disable(ctl);
ctx->vsync_handler = vsync_handler;
- ctx->vsync_ptr = ctl;
- mutex_unlock(&ctx->vsync_lock);
+ spin_unlock_irqrestore(&ctx->vsync_lock, flags);
return 0;
}
@@ -229,8 +224,7 @@
WARN(rc, "intf %d timegen off error (%d)\n", ctl->intf_num, rc);
}
- if (ctx->vsync_handler)
- mdss_mdp_video_set_vsync_handler(ctl, NULL);
+ mdss_mdp_video_set_vsync_handler(ctl, NULL);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
NULL, NULL);
@@ -244,9 +238,9 @@
static void mdss_mdp_video_pp_intr_done(void *arg)
{
- struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *ctl = arg;
+ struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
- ctx = (struct mdss_mdp_video_ctx *) arg;
if (!ctx) {
pr_err("invalid ctx\n");
return;
@@ -259,20 +253,24 @@
static void mdss_mdp_video_vsync_intr_done(void *arg)
{
- struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *ctl = arg;
+ struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+ ktime_t vsync_time;
- ctx = (struct mdss_mdp_video_ctx *) arg;
if (!ctx) {
pr_err("invalid ctx\n");
return;
}
- ctx->vsync_time = ktime_get();
- pr_debug("intr ctl=%d\n", ctx->ctl_num);
+ vsync_time = ktime_get();
+
+ pr_debug("intr ctl=%d\n", ctl->num);
complete(&ctx->vsync_comp);
+ spin_lock(&ctx->vsync_lock);
if (ctx->vsync_handler)
- schedule_work(&ctx->vsync_work);
+ ctx->vsync_handler(ctl, vsync_time);
+ spin_unlock(&ctx->vsync_lock);
}
static int mdss_mdp_video_prepare(struct mdss_mdp_ctl *ctl, void *arg)
@@ -309,11 +307,7 @@
return -ENODEV;
}
INIT_COMPLETION(ctx->vsync_comp);
-
- if (mutex_lock_interruptible(&ctx->vsync_lock))
- return -EINTR;
- if (!ctx->vsync_handler)
- mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+ video_vsync_irq_enable(ctl);
if (!ctx->timegen_en) {
int off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
@@ -335,9 +329,8 @@
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_TIMEGEN_ON, NULL);
WARN(rc, "intf %d timegen on error (%d)\n", ctl->intf_num, rc);
}
- if (!ctx->vsync_handler)
- mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
- mutex_unlock(&ctx->vsync_lock);
+
+ video_vsync_irq_disable(ctl);
return 0;
}
@@ -376,17 +369,16 @@
return -ENOMEM;
}
ctl->priv_data = ctx;
- ctx->ctl_num = ctl->num;
ctx->pp_num = mixer->num;
init_completion(&ctx->pp_comp);
init_completion(&ctx->vsync_comp);
+ spin_lock_init(&ctx->vsync_lock);
+ atomic_set(&ctx->vsync_ref, 0);
- INIT_WORK(&ctx->vsync_work, send_vsync_work);
- mutex_init(&ctx->vsync_lock);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
- mdss_mdp_video_vsync_intr_done, ctx);
+ mdss_mdp_video_vsync_intr_done, ctl);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
- mdss_mdp_video_pp_intr_done, ctx);
+ mdss_mdp_video_pp_intr_done, ctl);
itp.width = pinfo->xres + pinfo->lcdc.xres_pad;
itp.height = pinfo->yres + pinfo->lcdc.yres_pad;
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index f537c39..9c62ea2 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -26,6 +26,7 @@
#include "mdss_mdp.h"
#include "mdss_mdp_rotator.h"
+#define VSYNC_PERIOD 16
#define CHECK_BOUNDS(offset, size, max_size) \
(((size) > (max_size)) || ((offset) > ((max_size) - (size))))
@@ -837,30 +838,27 @@
mdss_mdp_overlay_kickoff(mfd->ctl);
}
+/* function is called in irq context should have minimum processing */
static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t)
{
- struct device *dev;
- char buf[64];
- char *envp[2];
-
- if (!ctl || !ctl->mfd || !ctl->mfd->fbi) {
+ struct msm_fb_data_type *mfd = ctl->mfd;
+ if (!mfd) {
pr_warn("Invalid handle for vsync\n");
return;
}
- dev = ctl->mfd->fbi->dev;
+ pr_debug("vsync on fb%d play_cnt=%d\n", mfd->index, ctl->play_cnt);
- snprintf(buf, sizeof(buf), "VSYNC=%llu", ktime_to_ns(t));
- envp[0] = buf;
- envp[1] = NULL;
- kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
-
- pr_debug("sent vsync on ctl=%d ts=%llu\n", ctl->num, ktime_to_ns(t));
+ spin_lock(&mfd->vsync_lock);
+ mfd->vsync_time = t;
+ complete(&mfd->vsync_comp);
+ spin_unlock(&mfd->vsync_lock);
}
int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en)
{
struct mdss_mdp_ctl *ctl = mfd->ctl;
+ unsigned long flags;
int rc;
if (!ctl)
@@ -868,13 +866,23 @@
if (!ctl->set_vsync_handler)
return -ENOTSUPP;
- pr_debug("vsync en=%d\n", en);
-
if (!ctl->power_on) {
+ pr_debug("fb%d vsync pending first update en=%d\n",
+ mfd->index, en);
mfd->vsync_pending = en;
return 0;
}
+ pr_debug("fb%d vsync en=%d\n", mfd->index, en);
+
+ spin_lock_irqsave(&mfd->vsync_lock, flags);
+ INIT_COMPLETION(mfd->vsync_comp);
+ if (en && ctl->play_cnt == 0) {
+ mfd->vsync_time = ktime_get();
+ complete(&mfd->vsync_comp);
+ }
+ spin_unlock_irqrestore(&mfd->vsync_lock, flags);
+
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
if (en)
rc = ctl->set_vsync_handler(ctl, mdss_mdp_overlay_handle_vsync);
@@ -885,6 +893,47 @@
return rc;
}
+static ssize_t mdss_mdp_vsync_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+ unsigned long flags;
+ u64 vsync_ticks;
+ int ret;
+
+ if (!mfd->ctl || !mfd->ctl->power_on)
+ return 0;
+
+ ret = wait_for_completion_interruptible_timeout(&mfd->vsync_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 5));
+ if (ret <= 0) {
+ pr_warn("vsync wait on fb%d interrupted (%d)\n",
+ mfd->index, ret);
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&mfd->vsync_lock, flags);
+ vsync_ticks = ktime_to_ns(mfd->vsync_time);
+ spin_unlock_irqrestore(&mfd->vsync_lock, flags);
+
+ pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
+
+ return ret;
+}
+
+static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL);
+
+static struct attribute *vsync_fs_attrs[] = {
+ &dev_attr_vsync_event.attr,
+ NULL,
+};
+
+static struct attribute_group vsync_fs_attr_group = {
+ .attrs = vsync_fs_attrs,
+};
+
static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
struct fb_cursor *cursor)
{
@@ -1154,6 +1203,9 @@
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
+ struct device *dev = mfd->fbi->dev;
+ int rc;
+
mfd->on_fnc = mdss_mdp_overlay_on;
mfd->off_fnc = mdss_mdp_overlay_off;
mfd->hw_refresh = true;
@@ -1168,7 +1220,18 @@
INIT_LIST_HEAD(&mfd->pipes_used);
INIT_LIST_HEAD(&mfd->pipes_cleanup);
+ init_completion(&mfd->vsync_comp);
+ spin_lock_init(&mfd->vsync_lock);
mutex_init(&mfd->ov_lock);
- return 0;
+ rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
+ if (rc) {
+ pr_err("vsync sysfs group creation failed, ret=%d\n", rc);
+ return rc;
+ }
+
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+ pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
+
+ return rc;
}
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index bdd4f3bb..505928e 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -71,6 +71,7 @@
__u32 hw_vsync_mode;
__u32 vsync_notifier_period;
__u32 blt_ctrl;
+ __u32 blt_mode;
__u32 rev;
};
@@ -204,6 +205,18 @@
int (*clk_func) (int enable);
};
+enum {
+ MDP4_OVERLAY_BLT_SWITCH_TG_OFF,
+ MDP4_OVERLAY_BLT_SWITCH_TG_ON,
+ MDP4_OVERLAY_BLT_SWITCH_POLL
+};
+
+enum {
+ MDP4_OVERLAY_MODE_BLT_CTRL,
+ MDP4_OVERLAY_MODE_BLT_ALWAYS_ON,
+ MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF
+};
+
/*===========================================================================
FUNCTIONS PROTOTYPES
============================================================================*/
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index c798cf9..6c43ec7 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -138,6 +138,8 @@
void (*enable_preset_value)(struct mmc_host *host, bool enable);
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
void (*hw_reset)(struct mmc_host *host);
+ unsigned long (*get_max_frequency)(struct mmc_host *host);
+ unsigned long (*get_min_frequency)(struct mmc_host *host);
};
struct mmc_card;
@@ -250,6 +252,7 @@
#define MMC_CAP2_SANITIZE (1 << 13) /* Support Sanitize */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
+#define MMC_CAP2_CLK_SCALE (1 << 16) /* Allow dynamic clk scaling */
mmc_pm_flag_t pm_caps; /* supported pm features */
int clk_requests; /* internal reference counter */
@@ -353,6 +356,19 @@
#endif
struct mmc_ios saved_ios;
+ struct {
+ unsigned long busy_time_us;
+ unsigned long window_time;
+ unsigned long curr_freq;
+ unsigned long polling_delay_ms;
+ unsigned int up_threshold;
+ unsigned int down_threshold;
+ ktime_t start_busy;
+ bool enable;
+ bool initialized;
+ bool in_progress;
+ struct delayed_work work;
+ } clk_scaling;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/msm_ipc.h b/include/linux/msm_ipc.h
index 44fa8eb..7b6bf41 100644
--- a/include/linux/msm_ipc.h
+++ b/include/linux/msm_ipc.h
@@ -45,6 +45,14 @@
unsigned char reserved;
};
+struct config_sec_rules_args {
+ int num_group_info;
+ uint32_t service_id;
+ uint32_t instance_id;
+ unsigned reserved;
+ gid_t group_id[0];
+};
+
#define IPC_ROUTER_IOCTL_MAGIC (0xC3)
#define IPC_ROUTER_IOCTL_GET_VERSION \
@@ -62,6 +70,9 @@
#define IPC_ROUTER_IOCTL_BIND_CONTROL_PORT \
_IOR(IPC_ROUTER_IOCTL_MAGIC, 4, unsigned int)
+#define IPC_ROUTER_IOCTL_CONFIG_SEC_RULES \
+ _IOR(IPC_ROUTER_IOCTL_MAGIC, 5, struct config_sec_rules_args)
+
struct msm_ipc_server_info {
uint32_t node_id;
uint32_t port_id;
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index d6fbc64..5b7820d 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -333,7 +333,6 @@
bool sm_work_pending;
atomic_t pm_suspended;
atomic_t in_lpm;
- atomic_t suspend_work_pending;
int async_int;
unsigned cur_power;
struct delayed_work chg_work;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 14ccf3e..3bf514f 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -247,6 +247,11 @@
bdaddr_t bdaddr;
} __packed;
+#define MGMT_OP_CANCEL_RESOLVE_NAME 0x0024
+struct mgmt_cp_cancel_resolve_name {
+ bdaddr_t bdaddr;
+} __packed;
+
#define MGMT_OP_LE_READ_WHITE_LIST_SIZE 0xE000
#define MGMT_OP_LE_CLEAR_WHITE_LIST 0xE001
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 28eb7ea..493801a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1956,6 +1956,38 @@
return err;
}
+static int cancel_resolve_name(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct mgmt_cp_cancel_resolve_name *mgmt_cp = (void *) data;
+ struct hci_cp_remote_name_req_cancel hci_cp;
+ struct hci_dev *hdev;
+ int err;
+
+ BT_DBG("");
+
+ if (len != sizeof(*mgmt_cp))
+ return cmd_status(sk, index, MGMT_OP_CANCEL_RESOLVE_NAME,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_CANCEL_RESOLVE_NAME,
+ ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ memset(&hci_cp, 0, sizeof(hci_cp));
+ bacpy(&hci_cp.bdaddr, &mgmt_cp->bdaddr);
+ err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(hci_cp),
+ &hci_cp);
+
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int set_connection_params(struct sock *sk, u16 index,
unsigned char *data, u16 len)
{
@@ -2189,10 +2221,11 @@
struct mgmt_mode cp = {0};
int err = -1;
- BT_DBG("");
-
hdev = hci_dev_get(index);
+ if (hdev)
+ BT_DBG("disco_state: %d", hdev->disco_state);
+
if (!hdev || !lmp_le_capable(hdev)) {
mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
@@ -2200,6 +2233,8 @@
mgmt_event(MGMT_EV_DISCOVERING, index, &cp, sizeof(cp), NULL);
+ hdev->disco_state = SCAN_IDLE;
+
if (hdev)
goto done;
else
@@ -2315,6 +2350,7 @@
if (!hdev)
return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV);
+ BT_DBG("disco_state: %d", hdev->disco_state);
hci_dev_lock_bh(hdev);
if (hdev->disco_state && timer_pending(&hdev->disco_timer)) {
@@ -2395,6 +2431,8 @@
if (!hdev)
return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
+ BT_DBG("disco_state: %d", hdev->disco_state);
+
hci_dev_lock_bh(hdev);
state = hdev->disco_state;
@@ -2659,6 +2697,9 @@
case MGMT_OP_RESOLVE_NAME:
err = resolve_name(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_CANCEL_RESOLVE_NAME:
+ err = cancel_resolve_name(sk, index, buf + sizeof(*hdr), len);
+ break;
case MGMT_OP_SET_CONNECTION_PARAMS:
err = set_connection_params(sk, index, buf + sizeof(*hdr), len);
break;
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 04afd89..b7a3656 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -256,6 +256,7 @@
static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
{
unsigned long flags;
+ bool prev, curr;
int err;
if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
@@ -270,6 +271,8 @@
rfkill->ops->query(rfkill, rfkill->data);
spin_lock_irqsave(&rfkill->lock, flags);
+ prev = rfkill->state & RFKILL_BLOCK_SW;
+
if (rfkill->state & RFKILL_BLOCK_SW)
rfkill->state |= RFKILL_BLOCK_SW_PREV;
else
@@ -299,10 +302,15 @@
}
rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
+ curr = rfkill->state & RFKILL_BLOCK_SW;
+
spin_unlock_irqrestore(&rfkill->lock, flags);
rfkill_led_trigger_event(rfkill);
- rfkill_event(rfkill);
+
+ if (prev != curr)
+ rfkill_event(rfkill);
+
}
#ifdef CONFIG_RFKILL_INPUT
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 58205e9..37a4234 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/qpnp/clkdiv.h>
+#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -30,9 +31,6 @@
#define DRV_NAME "msm8974-asoc-taiko"
-#define PM8921_GPIO_BASE NR_GPIO_IRQS
-#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
-
#define MSM8974_SPK_ON 1
#define MSM8974_SPK_OFF 0
@@ -42,10 +40,10 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
-#define BOTTOM_SPK_AMP_POS 0x1
-#define BOTTOM_SPK_AMP_NEG 0x2
-#define TOP_SPK_AMP_POS 0x4
-#define TOP_SPK_AMP_NEG 0x8
+#define LO_1_SPK_AMP 0x1
+#define LO_3_SPK_AMP 0x2
+#define LO_2_SPK_AMP 0x4
+#define LO_4_SPK_AMP 0x8
#define GPIO_AUX_PCM_DOUT 43
#define GPIO_AUX_PCM_DIN 44
@@ -56,6 +54,9 @@
#define WCD9XXX_MBHC_DEF_RLOADS 5
#define TAIKO_EXT_CLK_RATE 9600000
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+
void *def_taiko_mbhc_cal(void);
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm);
@@ -89,11 +90,11 @@
SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
};
-static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
-static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
-static int msm_spk_control;
-static int msm_ext_bottom_spk_pamp;
-static int msm_ext_top_spk_pamp;
+static struct platform_device *spdev;
+static struct regulator *ext_spk_amp_regulator;
+static int ext_spk_amp_gpio = -1;
+static int msm8974_spk_control = 1;
+static int msm8974_ext_spk_pamp;
static int msm_slim_0_rx_ch = 1;
static int msm_slim_0_tx_ch = 1;
@@ -105,103 +106,70 @@
static int clk_users;
static atomic_t auxpcm_rsc_ref;
-static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
+static int msm8974_liquid_ext_spk_power_amp_init(void)
{
int ret = 0;
- struct pm_gpio param = {
- .direction = PM_GPIO_DIR_OUT,
- .output_buffer = PM_GPIO_OUT_BUF_CMOS,
- .output_value = 1,
- .pull = PM_GPIO_PULL_NO,
- .vin_sel = PM_GPIO_VIN_S4,
- .out_strength = PM_GPIO_STRENGTH_MED,
- .
- function = PM_GPIO_FUNC_NORMAL,
- };
-
- if (spk_amp_gpio == bottom_spk_pamp_gpio) {
-
- ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
+ ext_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-spk-amp-gpio", 0);
+ if (ext_spk_amp_gpio >= 0) {
+ ret = gpio_request(ext_spk_amp_gpio, "ext_spk_amp_gpio");
if (ret) {
- pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
- __func__, bottom_spk_pamp_gpio);
- return;
+ pr_err("%s: gpio_request failed for ext_spk_amp_gpio.\n",
+ __func__);
+ return -EINVAL;
}
- ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, ¶m);
- if (ret)
- pr_err("%s: Failed to configure Bottom Spk Ampl gpio %u\n",
- __func__, bottom_spk_pamp_gpio);
- else {
- pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
- gpio_direction_output(bottom_spk_pamp_gpio, 1);
- }
+ gpio_direction_output(ext_spk_amp_gpio, 0);
- } else if (spk_amp_gpio == top_spk_pamp_gpio) {
+ if (ext_spk_amp_regulator == NULL) {
+ ext_spk_amp_regulator = regulator_get(&spdev->dev,
+ "qcom,ext-spk-amp");
- ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
- if (ret) {
- pr_err("%s: Error requesting GPIO %d\n", __func__,
- top_spk_pamp_gpio);
- return;
+ if (IS_ERR(ext_spk_amp_regulator)) {
+ pr_err("%s: Cannot get regulator %s.\n",
+ __func__, "qcom,ext-spk-amp");
+
+ gpio_free(ext_spk_amp_gpio);
+ return PTR_ERR(ext_spk_amp_regulator);
+ }
}
- ret = pm8xxx_gpio_config(top_spk_pamp_gpio, ¶m);
- if (ret)
- pr_err("%s: Failed to configure Top Spk Ampl gpio %u\n",
- __func__, top_spk_pamp_gpio);
- else {
- pr_debug("%s: enable Top spkr amp gpio\n", __func__);
- gpio_direction_output(top_spk_pamp_gpio, 1);
- }
- } else {
- pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO. gpio = %u\n",
- __func__, spk_amp_gpio);
- return;
}
+
+ return 0;
}
-static void msm_ext_spk_power_amp_on(u32 spk)
+static void msm8974_liquid_ext_spk_power_amp_enable(u32 on)
{
- if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+ if (on)
+ regulator_enable(ext_spk_amp_regulator);
+ else
+ regulator_disable(ext_spk_amp_regulator);
- if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
- (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ usleep_range(EXT_CLASS_D_EN_DELAY, EXT_CLASS_D_EN_DELAY);
+ pr_debug("%s: %s external speaker PAs.\n", __func__,
+ on ? "Enable" : "Disable");
+}
- pr_debug("%s() External Bottom Speaker Ampl already turned on. spk = 0x%08x\n",
+static void msm8974_ext_spk_power_amp_on(u32 spk)
+{
+ if (spk & (LO_1_SPK_AMP |
+ LO_3_SPK_AMP |
+ LO_2_SPK_AMP |
+ LO_4_SPK_AMP)) {
+
+ pr_debug("%s() External Left/Right Speakers already turned on. spk = 0x%08x\n",
__func__, spk);
- return;
- }
- msm_ext_bottom_spk_pamp |= spk;
+ msm8974_ext_spk_pamp |= spk;
- if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
- (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+ if ((msm8974_ext_spk_pamp & LO_1_SPK_AMP) &&
+ (msm8974_ext_spk_pamp & LO_3_SPK_AMP) &&
+ (msm8974_ext_spk_pamp & LO_2_SPK_AMP) &&
+ (msm8974_ext_spk_pamp & LO_4_SPK_AMP)) {
- msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
- pr_debug("%s: slepping 4 ms after turning on external Bottom Speaker Ampl\n",
- __func__);
- usleep_range(4000, 4000);
- }
-
- } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
-
- if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
- (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
-
- pr_debug("%s() External Top Speaker Ampl already turned on. spk = 0x%08x\n",
- __func__, spk);
- return;
- }
-
- msm_ext_top_spk_pamp |= spk;
-
- if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
- (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
-
- msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
- pr_debug("%s: sleeping 4 ms after turning on external Top Speaker Ampl\n",
- __func__);
- usleep_range(4000, 4000);
+ if (ext_spk_amp_gpio >= 0)
+ msm8974_liquid_ext_spk_power_amp_enable(1);
}
} else {
@@ -211,35 +179,22 @@
}
}
-static void msm_ext_spk_power_amp_off(u32 spk)
+static void msm8974_ext_spk_power_amp_off(u32 spk)
{
- if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+ if (spk & (LO_1_SPK_AMP |
+ LO_3_SPK_AMP |
+ LO_2_SPK_AMP |
+ LO_4_SPK_AMP)) {
- if (!msm_ext_bottom_spk_pamp)
- return;
+ pr_debug("%s Left and right speakers case spk = 0x%08x",
+ __func__, spk);
- gpio_direction_output(bottom_spk_pamp_gpio, 0);
- gpio_free(bottom_spk_pamp_gpio);
- msm_ext_bottom_spk_pamp = 0;
+ if (!msm8974_ext_spk_pamp) {
+ if (ext_spk_amp_gpio >= 0)
+ msm8974_liquid_ext_spk_power_amp_enable(0);
+ msm8974_ext_spk_pamp = 0;
+ }
- pr_debug("%s: sleeping 4 ms after turning off external Bottom Speaker Ampl\n",
- __func__);
-
- usleep_range(4000, 4000);
-
- } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
-
- if (!msm_ext_top_spk_pamp)
- return;
-
- gpio_direction_output(top_spk_pamp_gpio, 0);
- gpio_free(top_spk_pamp_gpio);
- msm_ext_top_spk_pamp = 0;
-
- pr_debug("%s: sleeping 4 ms after turning off external Top Spkaker Ampl\n",
- __func__);
-
- usleep_range(4000, 4000);
} else {
pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
@@ -248,87 +203,89 @@
}
}
-static void msm_ext_control(struct snd_soc_codec *codec)
+static void msm8974_ext_control(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
mutex_lock(&dapm->codec->mutex);
- pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
- if (msm_spk_control == MSM8974_SPK_ON) {
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+ pr_debug("%s: msm8974_spk_control = %d", __func__, msm8974_spk_control);
+ if (msm8974_spk_control == MSM8974_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
} else {
- snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
- snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
- snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
- snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_disable_pin(dapm, "Lineout_4 amp");
}
snd_soc_dapm_sync(dapm);
mutex_unlock(&dapm->codec->mutex);
}
-static int msm_get_spk(struct snd_kcontrol *kcontrol,
+static int msm8974_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
- ucontrol->value.integer.value[0] = msm_spk_control;
+ pr_debug("%s: msm8974_spk_control = %d", __func__, msm8974_spk_control);
+ ucontrol->value.integer.value[0] = msm8974_spk_control;
return 0;
}
-static int msm_set_spk(struct snd_kcontrol *kcontrol,
+static int msm8974_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
pr_debug("%s()\n", __func__);
- if (msm_spk_control == ucontrol->value.integer.value[0])
+ if (msm8974_spk_control == ucontrol->value.integer.value[0])
return 0;
- msm_spk_control = ucontrol->value.integer.value[0];
- msm_ext_control(codec);
+ msm8974_spk_control = ucontrol->value.integer.value[0];
+ msm8974_ext_control(codec);
return 1;
}
-static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
+
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
+ pr_debug("%s()\n", __func__);
if (SND_SOC_DAPM_EVENT_ON(event)) {
- if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
- msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
- else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
- msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
- else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
- msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
- else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
- msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ msm8974_ext_spk_power_amp_on(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_3 amp", 14))
+ msm8974_ext_spk_power_amp_on(LO_3_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ msm8974_ext_spk_power_amp_on(LO_2_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_4 amp", 14))
+ msm8974_ext_spk_power_amp_on(LO_4_SPK_AMP);
else {
pr_err("%s() Invalid Speaker Widget = %s\n",
__func__, w->name);
return -EINVAL;
}
-
} else {
- if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
- msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
- else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
- msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
- else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
- msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
- else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
- msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
+ if (!strncmp(w->name, "Lineout_1 amp", 14))
+ msm8974_ext_spk_power_amp_off(LO_1_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_3 amp", 14))
+ msm8974_ext_spk_power_amp_off(LO_3_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_2 amp", 14))
+ msm8974_ext_spk_power_amp_off(LO_2_SPK_AMP);
+ else if (!strncmp(w->name, "Lineout_4 amp", 14))
+ msm8974_ext_spk_power_amp_off(LO_4_SPK_AMP);
else {
pr_err("%s() Invalid Speaker Widget = %s\n",
__func__, w->name);
return -EINVAL;
}
}
+
return 0;
+
}
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
@@ -397,11 +354,11 @@
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
msm8974_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
- SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_1 amp", msm_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_3 amp", msm_ext_spkramp_event),
- SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
- SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_2 amp", msm_ext_spkramp_event),
+ SND_SOC_DAPM_SPK("Lineout_4 amp", msm_ext_spkramp_event),
SND_SOC_DAPM_MIC("Handset Mic", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -693,8 +650,8 @@
};
static const struct snd_kcontrol_new msm_snd_controls[] = {
- SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm_get_spk,
- msm_set_spk),
+ SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm8974_get_spk,
+ msm8974_set_spk),
SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1],
msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2],
@@ -723,11 +680,6 @@
pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
- if (machine_is_msm8960_liquid()) {
- top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
- bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
- }
-
rtd->pmdown_time = 0;
err = snd_soc_add_codec_controls(codec, msm_snd_controls,
@@ -735,13 +687,21 @@
if (err < 0)
return err;
+ err = msm8974_liquid_ext_spk_power_amp_init();
+ if (err) {
+ pr_err("%s: LiQUID 8974 CLASS_D PAs init failed (%d)\n",
+ __func__, err);
+ return err;
+ }
+
snd_soc_dapm_new_controls(dapm, msm8974_dapm_widgets,
ARRAY_SIZE(msm8974_dapm_widgets));
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
- snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
snd_soc_dapm_sync(dapm);
@@ -1458,9 +1418,13 @@
ret);
goto err;
}
+
mutex_init(&cdc_mclk_mutex);
atomic_set(&auxpcm_rsc_ref, 0);
+ spdev = pdev;
+ ext_spk_amp_regulator = NULL;
+
return 0;
err:
devm_kfree(&pdev->dev, pdata);
@@ -1472,7 +1436,13 @@
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ if (ext_spk_amp_regulator)
+ regulator_put(ext_spk_amp_regulator);
+
gpio_free(pdata->mclk_gpio);
+ if (ext_spk_amp_gpio >= 0)
+ gpio_free(ext_spk_amp_gpio);
+
snd_soc_unregister_card(card);
return 0;