Merge "power: qpnp-bms: clamp for SOC <= 0"
diff --git a/Documentation/devicetree/bindings/sound/voice-svc.txt b/Documentation/devicetree/bindings/sound/voice-svc.txt
new file mode 100644
index 0000000..deca7f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/voice-svc.txt
@@ -0,0 +1,11 @@
+* Voice Service binding
+
+Required properties:
+- compatible : "qcom,msm-voice-svc"
+
+Example:
+
+ qcom,msm-voice-svc {
+ compatible = "qcom,msm-voice-svc";
+ };
+
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index cf8e3b3..4117d9d 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -499,6 +499,10 @@
compatible = "qti,msm-pcm-loopback";
};
+ qcom,msm-voice-svc {
+ compatible = "qcom,msm-voice-svc";
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 6bd3efb..3d7638d 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -12,7 +12,7 @@
obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += lpa_if_hdmi.o
endif
obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_v1.o apr_tal.o q6core.o dsp_debug.o
-obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o
+obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o voice_svc.o
ifdef CONFIG_ARCH_MSM9615
obj-y += audio_acdb.o
obj-y += rtac.o
diff --git a/arch/arm/mach-msm/qdsp6v2/voice_svc.c b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
new file mode 100644
index 0000000..92b3003
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
@@ -0,0 +1,593 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <sound/voice_svc.h>
+#include <mach/qdsp6v2/apr_tal.h>
+#include <mach/qdsp6v2/apr.h>
+
+#define DRIVER_NAME "voice_svc"
+#define MINOR_NUMBER 1
+#define APR_MAX_RESPONSE 10
+
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+
+struct voice_svc_device {
+ struct cdev *cdev;
+ struct device *dev;
+ int major;
+};
+
+struct voice_svc_prvt {
+ void* apr_q6_mvm;
+ void* apr_q6_cvs;
+ uint16_t response_count;
+ struct list_head response_queue;
+ wait_queue_head_t response_wait;
+ spinlock_t response_lock;
+};
+
+struct apr_data {
+ struct apr_hdr hdr;
+ __u8 payload[0];
+} __packed;
+
+struct apr_response_list {
+ struct list_head list;
+ struct voice_svc_cmd_response resp;
+};
+
+static struct voice_svc_device *voice_svc_dev;
+static struct class *voice_svc_class;
+dev_t device_num;
+
+static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
+{
+ struct voice_svc_prvt *prtd;
+ struct apr_response_list *response_list;
+ unsigned long spin_flags;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ prtd = (struct voice_svc_prvt*)priv;
+
+ pr_debug("%s: data->opcode %x\n", __func__,
+ data->opcode);
+
+ if (data->opcode == RESET_EVENTS) {
+ if (data->reset_proc == APR_DEST_QDSP6) {
+ pr_debug("%s: Received reset event\n", __func__);
+
+ if (prtd->apr_q6_mvm != NULL) {
+ apr_reset(prtd->apr_q6_mvm);
+ prtd->apr_q6_mvm = NULL;
+ }
+
+ if (prtd->apr_q6_cvs != NULL) {
+ apr_reset(prtd->apr_q6_cvs);
+ prtd->apr_q6_cvs = NULL;
+ }
+ } else if (data->reset_proc ==APR_DEST_MODEM) {
+ pr_debug("%s: Received Modem reset event\n", __func__);
+ }
+ }
+
+ spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+ if (prtd->response_count < APR_MAX_RESPONSE) {
+ response_list = (struct apr_response_list *)kmalloc(
+ sizeof(struct apr_response_list) + data->payload_size,
+ GFP_ATOMIC);
+ if (response_list == NULL) {
+ pr_err("%s: kmalloc failed\n", __func__);
+
+ return -ENOMEM;
+ }
+
+ response_list->resp.src_port = data->src_port;
+ response_list->resp.dest_port = ((data->dest_port) >> 8);
+ response_list->resp.token = data->token;
+ response_list->resp.opcode = data->opcode;
+ response_list->resp.payload_size = data->payload_size;
+ if (data->payload != NULL && data->payload_size > 0) {
+ memcpy(response_list->resp.payload, data->payload,
+ data->payload_size);
+ }
+
+ list_add_tail(&response_list->list, &prtd->response_queue);
+ prtd->response_count++;
+
+ wake_up(&prtd->response_wait);
+ } else {
+ pr_err("%s: Response dropped since the queue is full\n", __func__);
+ }
+
+ spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+
+ return 0;
+}
+
+static void voice_svc_update_hdr(struct voice_svc_cmd_request* apr_req_data,
+ struct apr_data *aprdata,
+ struct voice_svc_prvt *prtd)
+{
+
+ aprdata->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ aprdata->hdr.src_port = ((apr_req_data->src_port) << 8 | 0x0001);
+ aprdata->hdr.dest_port = apr_req_data->dest_port;
+ aprdata->hdr.token = apr_req_data->token;
+ aprdata->hdr.opcode = apr_req_data->opcode;
+ aprdata->hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ apr_req_data->payload_size);
+ memcpy(aprdata->payload, apr_req_data->payload,
+ apr_req_data->payload_size);
+}
+
+static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request,
+ struct voice_svc_prvt *prtd)
+{
+ int ret = 0;
+ void *apr_handle = NULL;
+ struct apr_data *aprdata = NULL;
+ uint32_t user_payload_size = 0;
+
+ if (apr_request == NULL) {
+ pr_err("%s: apr_request is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ user_payload_size = apr_request->payload_size;
+
+ aprdata = kmalloc(sizeof(struct apr_data) + user_payload_size,
+ GFP_KERNEL);
+
+ if (aprdata == NULL) {
+ pr_err("%s: aprdata kmalloc failed.", __func__);
+
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ voice_svc_update_hdr(apr_request, aprdata, prtd);
+
+ if (!strncmp(apr_request->svc_name, VOICE_SVC_CVS_STR,
+ MAX(sizeof(apr_request->svc_name), sizeof(VOICE_SVC_CVS_STR)))) {
+ apr_handle = prtd->apr_q6_cvs;
+ } else if (!strncmp(apr_request->svc_name, VOICE_SVC_MVM_STR,
+ MAX(sizeof(apr_request->svc_name), sizeof(VOICE_SVC_MVM_STR)))) {
+ apr_handle = prtd->apr_q6_mvm;
+ } else {
+ pr_err("%s: Invalid service %s\n", __func__,
+ apr_request->svc_name);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = apr_send_pkt(apr_handle, (uint32_t *)aprdata);
+
+ if (ret < 0) {
+ pr_err("%s: Fail in sending SNDRV_VOICE_SVC_REQUEST\n",
+ __func__);
+ ret = -EINVAL;
+ } else {
+ pr_debug("%s: apr packet sent successfully %d\n",
+ __func__, ret);
+ ret = 0;
+ }
+
+done:
+ if (aprdata != NULL)
+ kfree(aprdata);
+
+ return ret;
+}
+static int voice_svc_reg(char *svc, uint32_t src_port,
+ struct voice_svc_prvt *prtd, void **handle)
+{
+ int ret = 0;
+
+ if (handle == NULL) {
+ pr_err("%s: handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*handle != NULL) {
+ pr_err("%s: svc handle not NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *handle = apr_register("ADSP",
+ svc, qdsp_apr_callback,
+ ((src_port) << 8 | 0x0001),
+ prtd);
+
+ if (*handle == NULL) {
+ pr_err("%s: Unable to register %s\n",
+ __func__, svc);
+
+ ret = -EFAULT;
+ goto done;
+ }
+ pr_debug("%s: register %s successful\n",
+ __func__, svc);
+done:
+ return ret;
+}
+
+static int voice_svc_dereg(char *svc, void **handle)
+{
+ int ret = 0;
+ if (handle == NULL) {
+ pr_err("%s: handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_deregister(*handle);
+ *handle = NULL;
+ pr_debug("%s: deregister %s successful\n",
+ __func__, svc);
+
+done:
+ return 0;
+}
+
+static int process_reg_cmd(struct voice_svc_register apr_reg_svc,
+ struct voice_svc_prvt *prtd)
+{
+ int ret = 0;
+ char *svc = NULL;
+ void **handle = NULL;
+
+ if (!strncmp(apr_reg_svc.svc_name, VOICE_SVC_MVM_STR,
+ MAX(sizeof(apr_reg_svc.svc_name), sizeof(VOICE_SVC_MVM_STR)))) {
+ svc = VOICE_SVC_MVM_STR;
+ handle = &prtd->apr_q6_mvm;
+ } else if (!strncmp(apr_reg_svc.svc_name, VOICE_SVC_CVS_STR,
+ MAX(sizeof(apr_reg_svc.svc_name), sizeof(VOICE_SVC_CVS_STR)))) {
+ svc = VOICE_SVC_CVS_STR;
+ handle = &prtd->apr_q6_cvs;
+ } else {
+ pr_err("%s: Invalid Service: %s\n", __func__,
+ apr_reg_svc.svc_name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*handle == NULL &&
+ apr_reg_svc.reg_flag) {
+ ret = voice_svc_reg(svc, apr_reg_svc.src_port, prtd,
+ handle);
+ } else if (handle != NULL &&
+ !apr_reg_svc.reg_flag) {
+ ret = voice_svc_dereg(svc, handle);
+ }
+
+done:
+ return ret;
+}
+
+static long voice_svc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long u_arg)
+{
+ int ret = 0;
+ struct voice_svc_prvt *prtd;
+ struct voice_svc_register apr_reg_svc;
+ struct voice_svc_cmd_request *apr_request = NULL;
+ struct voice_svc_cmd_response *apr_response = NULL;
+ struct apr_response_list *resp;
+ void __user *arg = (void __user *)u_arg;
+ uint32_t user_payload_size = 0;
+ unsigned long spin_flags;
+
+ pr_debug("%s: cmd: %u\n", __func__, cmd);
+
+ prtd = (struct voice_svc_prvt*)file->private_data;
+
+ switch (cmd) {
+ case SNDRV_VOICE_SVC_REGISTER_SVC:
+ pr_debug("%s: size of struct: %d\n", __func__,
+ sizeof(apr_reg_svc));
+ if (copy_from_user(&apr_reg_svc, arg, sizeof(apr_reg_svc))) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = process_reg_cmd(apr_reg_svc, prtd);
+
+ break;
+ case SNDRV_VOICE_SVC_CMD_REQUEST:
+ if (!access_ok(VERIFY_READ, arg,
+ sizeof(struct voice_svc_cmd_request))) {
+ pr_err("%s: Unable to read user data", __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ user_payload_size =
+ ((struct voice_svc_cmd_request*)arg)->payload_size;
+
+ apr_request = kmalloc(sizeof(struct voice_svc_cmd_request) +
+ user_payload_size, GFP_KERNEL);
+
+ if (apr_request == NULL) {
+ pr_err("%s: apr_request kmalloc failed.", __func__);
+
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(apr_request, arg,
+ sizeof(struct voice_svc_cmd_request) +
+ user_payload_size)) {
+ pr_err("%s: copy from user failed, size %d\n", __func__,
+ sizeof(struct voice_svc_cmd_request) +
+ user_payload_size);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = voice_svc_send_req(apr_request, prtd);
+
+ break;
+
+ case SNDRV_VOICE_SVC_CMD_RESPONSE:
+ do {
+ if (!access_ok(VERIFY_READ, arg,
+ sizeof(struct voice_svc_cmd_response))) {
+ pr_err("%s: Unable to read user data",
+ __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ user_payload_size =
+ ((struct voice_svc_cmd_response*)arg)->payload_size;
+ pr_debug("%s: RESPONSE: user payload size %d",
+ __func__, user_payload_size);
+
+ spin_lock_irqsave(&prtd->response_lock, spin_flags);
+ if (!list_empty(&prtd->response_queue)) {
+ resp = list_first_entry(&prtd->response_queue,
+ struct apr_response_list, list);
+
+ if (user_payload_size <
+ resp->resp.payload_size) {
+ pr_err("%s: Invalid payload size %d,%d",
+ __func__, user_payload_size,
+ resp->resp.payload_size);
+ ret = -ENOMEM;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ if (!access_ok(VERIFY_WRITE, arg,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size)) {
+ ret = -EFAULT;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ if (copy_to_user(arg, &resp->resp,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size)) {
+ pr_err("%s: copy to user failed, size \
+ %d\n", __func__,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size);
+
+ ret = -EFAULT;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ prtd->response_count--;
+
+ list_del(&resp->list);
+ kfree(resp);
+ spin_unlock_irqrestore(&prtd->response_lock,
+ spin_flags);
+ goto done;
+ } else {
+ spin_unlock_irqrestore(&prtd->response_lock,
+ spin_flags);
+ wait_event_interruptible(prtd->response_wait,
+ !list_empty(&prtd->response_queue));
+ pr_debug("%s: Interupt recieved for response",
+ __func__);
+ }
+ } while(!apr_response);
+ break;
+ default:
+ pr_debug("%s: cmd: %u\n", __func__, cmd);
+ ret = -EINVAL;
+ }
+
+done:
+ if (apr_request != NULL)
+ kfree(apr_request);
+
+ return ret;
+}
+
+static int voice_svc_open(struct inode *inode, struct file *file)
+{
+ struct voice_svc_prvt *prtd = NULL;
+
+ prtd = kmalloc(sizeof(struct voice_svc_prvt), GFP_KERNEL);
+
+ if (prtd == NULL) {
+ pr_err("%s: kmalloc failed", __func__);
+
+ return -ENOMEM;
+ }
+
+ memset(prtd, 0, sizeof(struct voice_svc_prvt));
+ prtd->apr_q6_cvs = NULL;
+ prtd->apr_q6_mvm = NULL;
+ prtd->response_count = 0;
+
+ INIT_LIST_HEAD(&prtd->response_queue);
+ init_waitqueue_head(&prtd->response_wait);
+ spin_lock_init(&prtd->response_lock);
+
+ file->private_data = (void*)prtd;
+
+ return 0;
+}
+
+static int voice_svc_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations voice_svc_fops = {
+ .owner = THIS_MODULE,
+ .open = voice_svc_open,
+ .unlocked_ioctl = voice_svc_ioctl,
+ .release = voice_svc_release,
+};
+
+
+static int voice_svc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ voice_svc_dev = devm_kzalloc(&pdev->dev, sizeof(struct voice_svc_device),
+ GFP_KERNEL);
+ if (!voice_svc_dev) {
+ pr_err("%s: kzalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = alloc_chrdev_region(&device_num, 0, MINOR_NUMBER, DRIVER_NAME);
+ if (ret) {
+ pr_err("%s: Failed to alloc chrdev\n", __func__);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ voice_svc_dev->major = MAJOR(device_num);
+ voice_svc_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR(voice_svc_class)) {
+ ret = PTR_ERR(voice_svc_class);
+ pr_err("%s: Failed to create class; err = %d\n", __func__,
+ ret);
+ goto class_err;
+ }
+
+ voice_svc_dev->dev = device_create(voice_svc_class, NULL, device_num,
+ NULL, DRIVER_NAME);
+ if (IS_ERR(voice_svc_dev->dev)) {
+ ret = PTR_ERR(voice_svc_dev->dev);
+ pr_err("%s: Failed to create device; err = %d\n", __func__,
+ ret);
+ goto dev_err;
+ }
+
+ voice_svc_dev->cdev = cdev_alloc();
+ cdev_init(voice_svc_dev->cdev, &voice_svc_fops);
+ ret = cdev_add(voice_svc_dev->cdev, device_num, MINOR_NUMBER);
+ if (ret) {
+ pr_err("%s: Failed to register chrdev; err = %d\n", __func__,
+ ret);
+ goto add_err;
+ }
+ pr_debug("%s: Device created\n", __func__);
+ goto done;
+
+add_err:
+ cdev_del(voice_svc_dev->cdev);
+ device_destroy(voice_svc_class, device_num);
+dev_err:
+ class_destroy(voice_svc_class);
+class_err:
+ unregister_chrdev_region(0, MINOR_NUMBER);
+done:
+ return ret;
+}
+
+static int voice_svc_remove(struct platform_device *pdev)
+{
+ cdev_del(voice_svc_dev->cdev);
+ kfree(voice_svc_dev->cdev);
+ device_destroy(voice_svc_class, device_num);
+ class_destroy(voice_svc_class);
+ unregister_chrdev_region(0, MINOR_NUMBER);
+ kfree(voice_svc_dev);
+
+ return 0;
+}
+
+static struct of_device_id voice_svc_of_match[] = {
+ {.compatible = "qcom,msm-voice-svc"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, voice_svc_of_match);
+
+static struct platform_driver voice_svc_driver = {
+ .probe = voice_svc_probe,
+ .remove = voice_svc_remove,
+ .driver = {
+ .name = "msm-voice-svc",
+ .owner = THIS_MODULE,
+ .of_match_table = voice_svc_of_match,
+ },
+};
+
+static int __init voice_svc_init(void)
+{
+ return platform_driver_register(&voice_svc_driver);
+}
+
+static void __exit voice_svc_exit(void)
+{
+ platform_driver_unregister(&voice_svc_driver);
+}
+
+module_init(voice_svc_init);
+module_exit(voice_svc_exit);
+
+MODULE_DESCRIPTION("Soc QDSP6v2 Audio APR driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 428d5b0..9cb26e1 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -551,7 +551,7 @@
static void smd_tty_close(struct tty_struct *tty, struct file *f)
{
- struct smd_tty_info *info = tty->driver_data;
+ struct smd_tty_info *info = smd_tty + tty->index;
tty_port_close(&info->port, tty, f);
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 606953d..99647a7 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -107,6 +107,10 @@
#define DIAG_STM_WCNSS 0x04
#define DIAG_STM_APPS 0x08
+#define DIAG_DIAG_STM 0x214
+
+#define BAD_PARAM_RESPONSE_MESSAGE 20
+
/*
* 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
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 8cc7515..0bbb012 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -56,6 +56,8 @@
#define STM_RSP_SMD_COMPLY_INDEX 9
#define STM_RSP_NUM_BYTES 10
+#define STM_COMMAND_VALID 1
+
#define SMD_DRAIN_BUF_SIZE 4096
int diag_debug_buf_idx;
@@ -1136,20 +1138,44 @@
}
}
-int diag_process_stm_cmd(unsigned char *buf)
+int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf)
{
- uint8_t version = *(buf+STM_CMD_VERSION_OFFSET);
- uint8_t mask = *(buf+STM_CMD_MASK_OFFSET);
- uint8_t cmd = *(buf+STM_CMD_DATA_OFFSET);
+ uint8_t version, mask, cmd;
uint8_t rsp_supported = 0;
uint8_t rsp_smd_comply = 0;
- int valid_command = 1;
int i;
- /* Check if command is valid */
- if ((version != 1) || (mask == 0) || (0 != (mask >> 4)) ||
- (cmd != ENABLE_STM && cmd != DISABLE_STM)) {
- valid_command = 0;
+ if (!buf || !dest_buf) {
+ pr_err("diag: Invalid pointers buf: %p, dest_buf %p in %s\n",
+ buf, dest_buf, __func__);
+ return -EIO;
+ }
+
+ version = *(buf + STM_CMD_VERSION_OFFSET);
+ mask = *(buf + STM_CMD_MASK_OFFSET);
+ cmd = *(buf + STM_CMD_DATA_OFFSET);
+
+ /*
+ * Check if command is valid. If the command is asking for
+ * status, then the processor mask field is to be ignored.
+ */
+ if ((version != 1) || (cmd > STATUS_STM) ||
+ ((cmd != STATUS_STM) && ((mask == 0) || (0 != (mask >> 4))))) {
+ /* Command is invalid. Send bad param message response */
+ dest_buf[0] = BAD_PARAM_RESPONSE_MESSAGE;
+ for (i = 0; i < STM_CMD_NUM_BYTES; i++)
+ dest_buf[i+1] = *(buf + i);
+ return STM_CMD_NUM_BYTES+1;
+ } else if (cmd == STATUS_STM) {
+ /*
+ * Only the status is being queried, so fill in whether diag
+ * over stm is supported or not
+ */
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ if (driver->peripheral_supports_stm[i])
+ rsp_supported |= 1 << i;
+
+ rsp_supported |= DIAG_STM_APPS;
} else {
if (mask & DIAG_STM_MODEM)
diag_process_stm_mask(cmd, DIAG_STM_MODEM, MODEM_DATA,
@@ -1169,15 +1195,13 @@
}
for (i = 0; i < STM_CMD_NUM_BYTES; i++)
- driver->apps_rsp_buf[i] = *(buf+i);
+ dest_buf[i] = *(buf + i);
- driver->apps_rsp_buf[STM_RSP_VALID_INDEX] = valid_command;
- driver->apps_rsp_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
- driver->apps_rsp_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
+ dest_buf[STM_RSP_VALID_INDEX] = STM_COMMAND_VALID;
+ dest_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
+ dest_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
- encode_rsp_and_send(STM_RSP_NUM_BYTES-1);
-
- return 0;
+ return STM_RSP_NUM_BYTES;
}
int diag_apps_responds()
@@ -1273,8 +1297,13 @@
encode_rsp_and_send(7);
return 0;
} else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
- (*(uint16_t *)(buf+2) == 0x020E)) {
- return diag_process_stm_cmd(buf);
+ (*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) {
+ len = diag_process_stm_cmd(buf, driver->apps_rsp_buf);
+ if (len > 0) {
+ encode_rsp_and_send(len - 1);
+ return 0;
+ }
+ return len;
}
/* Check for Apps Only & get event mask request */
else if (diag_apps_responds() && *buf == 0x81) {
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index d79195c..7f5ea03 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -54,8 +54,9 @@
#define ENABLE_SEPARATE_CMDRSP 1
#define DISABLE_SEPARATE_CMDRSP 0
-#define ENABLE_STM 1
#define DISABLE_STM 0
+#define ENABLE_STM 1
+#define STATUS_STM 2
#define UPDATE_PERIPHERAL_STM_STATE 1
#define CLEAR_PERIPHERAL_STM_STATE 2
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index d87520f..7b39645e 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -691,7 +691,7 @@
{
int rc;
u8 val;
- int duty_us;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
@@ -711,12 +711,23 @@
}
if (led->mpp_cfg->pwm_mode == PWM_MODE) {
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
- duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / LED_FULL;
/*config pwm for brightness scaling*/
- rc = pwm_config_us(led->mpp_cfg->pwm_cfg->pwm_dev,
+ period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ LED_FULL;
+ rc = pwm_config_us(
+ led->mpp_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->mpp_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ LED_FULL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->mpp_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "Failed to " \
"configure pwm for new values\n");
@@ -1219,8 +1230,8 @@
static int qpnp_kpdbl_set(struct qpnp_led_data *led)
{
- int duty_us;
int rc;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (!led->kpdbl_cfg->pwm_cfg->blinking)
@@ -1237,11 +1248,22 @@
}
if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
- duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / KPDBL_MAX_LEVEL;
- rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ KPDBL_MAX_LEVEL;
+ rc = pwm_config_us(
+ led->kpdbl_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ KPDBL_MAX_LEVEL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "pwm config failed\n");
return rc;
@@ -1262,7 +1284,7 @@
if (led->kpdbl_cfg->always_on) {
rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ led->kpdbl_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"pwm config failed\n");
@@ -1300,19 +1322,30 @@
static int qpnp_rgb_set(struct qpnp_led_data *led)
{
- int duty_us;
int rc;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (!led->rgb_cfg->pwm_cfg->blinking)
led->rgb_cfg->pwm_cfg->mode =
led->rgb_cfg->pwm_cfg->default_mode;
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
- duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / LED_FULL;
- rc = pwm_config_us(led->rgb_cfg->pwm_cfg->pwm_dev,
+ period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ LED_FULL;
+ rc = pwm_config_us(
+ led->rgb_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->rgb_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ LED_FULL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->rgb_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"pwm config failed\n");
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 2fa8c63..ebcec96 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, 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
@@ -47,7 +47,7 @@
/* control info */
struct list_head cpkt_resp_q;
- atomic_t notify_count;
+ unsigned long notify_count;
unsigned long cpkts_len;
};
@@ -605,7 +605,7 @@
list_del(&cpkt->list);
rmnet_free_ctrl_pkt(cpkt);
}
- atomic_set(&dev->notify_count, 0);
+ dev->notify_count = 0;
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -619,6 +619,7 @@
__func__, xport_to_str(dxport),
dev, dev->port_num);
+ usb_ep_fifo_flush(dev->notify);
frmnet_purge_responses(dev);
port_num = rmnet_ports[dev->port_num].data_xport_num;
@@ -754,7 +755,7 @@
return;
}
- if (atomic_inc_return(&dev->notify_count) != 1) {
+ if (++dev->notify_count != 1) {
spin_unlock_irqrestore(&dev->lock, flags);
return;
}
@@ -772,7 +773,14 @@
if (ret) {
spin_lock_irqsave(&dev->lock, flags);
if (!list_empty(&dev->cpkt_resp_q)) {
- atomic_dec(&dev->notify_count);
+ if (dev->notify_count > 0)
+ dev->notify_count--;
+ else {
+ pr_debug("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+ }
cpkt = list_first_entry(&dev->cpkt_resp_q,
struct rmnet_ctrl_pkt, list);
list_del(&cpkt->list);
@@ -911,7 +919,9 @@
case -ECONNRESET:
case -ESHUTDOWN:
/* connection gone */
- atomic_set(&dev->notify_count, 0);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->notify_count = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
break;
default:
pr_err("rmnet notify ep error %d\n", status);
@@ -920,14 +930,34 @@
if (!atomic_read(&dev->ctrl_online))
break;
- if (atomic_dec_and_test(&dev->notify_count))
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->notify_count > 0) {
+ dev->notify_count--;
+ if (dev->notify_count == 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ break;
+ }
+ } else {
+ pr_debug("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock, flags);
break;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
if (status) {
spin_lock_irqsave(&dev->lock, flags);
if (!list_empty(&dev->cpkt_resp_q)) {
- atomic_dec(&dev->notify_count);
+ if (dev->notify_count > 0)
+ dev->notify_count--;
+ else {
+ pr_err("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock,
+ flags);
+ break;
+ }
cpkt = list_first_entry(&dev->cpkt_resp_q,
struct rmnet_ctrl_pkt, list);
list_del(&cpkt->list);
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index 60847b0..aeccfed 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -13,3 +13,4 @@
header-y += compress_offload.h
header-y += lsm_params.h
header-y += voice_params.h
+header-y += voice_svc.h
diff --git a/include/sound/voice_svc.h b/include/sound/voice_svc.h
new file mode 100644
index 0000000..7045018
--- /dev/null
+++ b/include/sound/voice_svc.h
@@ -0,0 +1,46 @@
+#ifndef __VOICE_SVC_H__
+#define __VOICE_SVC_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define VOICE_SVC_DRIVER_NAME "voice_svc"
+
+#define VOICE_SVC_MVM_STR "MVM"
+#define VOICE_SVC_CVS_STR "CVS"
+#define MAX_APR_SERVICE_NAME_LEN 64
+
+struct voice_svc_register {
+ char svc_name[MAX_APR_SERVICE_NAME_LEN];
+ __u32 src_port;
+ __u8 reg_flag;
+};
+
+struct voice_svc_cmd_response {
+ __u32 src_port;
+ __u32 dest_port;
+ __u32 token;
+ __u32 opcode;
+ __u32 payload_size;
+ __u8 payload[0];
+};
+
+struct voice_svc_cmd_request {
+ char svc_name[MAX_APR_SERVICE_NAME_LEN];
+ __u32 src_port;
+ __u32 dest_port;
+ __u32 token;
+ __u32 opcode;
+ __u32 payload_size;
+ __u8 payload[0];
+};
+
+#define VOICE_SVC_MAGIC 'N'
+
+#define SNDRV_VOICE_SVC_REGISTER_SVC _IOWR(VOICE_SVC_MAGIC, \
+ 0x01, struct voice_svc_register)
+#define SNDRV_VOICE_SVC_CMD_RESPONSE _IOWR(VOICE_SVC_MAGIC, \
+ 0x02, struct voice_svc_cmd_response)
+#define SNDRV_VOICE_SVC_CMD_REQUEST _IOWR(VOICE_SVC_MAGIC, \
+ 0x03, struct voice_svc_cmd_request)
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 1434970..8b2c443 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -57,6 +57,7 @@
struct msm_dai_q6_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
+ DECLARE_BITMAP(hwfree_status, STATUS_MAX);
u32 rate;
u32 channels;
u32 bitwidth;
@@ -1510,6 +1511,11 @@
set_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: set hwfree_status to started\n",
+ __func__);
+ }
return rc;
}
@@ -1525,7 +1531,6 @@
struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s;
-
dai_data->channels = params_channels(params);
switch (dai_data->channels) {
case 8:
@@ -1602,10 +1607,14 @@
dai_data->port_config.i2s.i2s_cfg_minor_version =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.sample_rate = dai_data->rate;
- if (test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+ if ((test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) ||
+ (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) &&
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) {
if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate !=
mi2s_dai_data->rx_dai.mi2s_dai_data.rate) ||
(mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth !=
@@ -1669,6 +1678,23 @@
return 0;
}
+static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__);
+ }
+ return 0;
+}
+
static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -1696,12 +1722,15 @@
dev_err(dai->dev, "fail to close AFE port\n");
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status))
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
}
static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
.startup = msm_dai_q6_mi2s_startup,
.prepare = msm_dai_q6_mi2s_prepare,
.hw_params = msm_dai_q6_mi2s_hw_params,
+ .hw_free = msm_dai_q6_mi2s_hw_free,
.set_fmt = msm_dai_q6_mi2s_set_fmt,
.shutdown = msm_dai_q6_mi2s_shutdown,
};