summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorSudheer Papothi <spapothi@codeaurora.org>2016-03-02 01:56:43 +0530
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:11:25 -0700
commitbe1a516dcb8571becec57f8965ca5abfdf7da092 (patch)
tree4f4a5fe235b9e5bef10b5aa764f31f6049a88da2 /drivers/misc
parent26c32e7dad6124d7d726ad17e8c661376cf10d4c (diff)
ASoC: msm: Add Audio drivers for MSM targets
Add snapshot for audio drivers for MSM targets. The code is migrated from msm-3.18 kernel at the below commit/AU level - AU_LINUX_ANDROID_LA.HB.1.3.1.06.00.00.187.056 (e70ad0cd5efdd9dc91a77dcdac31d6132e1315c1) (Promotion of kernel.lnx.3.18-151201.) Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/qcom/Kconfig18
-rw-r--r--drivers/misc/qcom/Makefile1
-rw-r--r--drivers/misc/qcom/qdsp6v2/Makefile5
-rw-r--r--drivers/misc/qcom/qdsp6v2/aac_in.c698
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrnb_in.c401
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrwb_in.c396
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_aac.c470
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_alac.c436
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrnb.c178
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwb.c182
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c395
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_ape.c359
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_evrc.c186
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c781
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_mp3.c188
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_multi_aac.c519
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_qcelp.c193
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.c915
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.h113
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c2086
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.h232
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wma.c345
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wmapro.c418
-rw-r--r--drivers/misc/qcom/qdsp6v2/evrc_in.c408
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_common.h37
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_v2.c99
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c219
-rw-r--r--drivers/misc/qcom/qdsp6v2/qcelp_in.c408
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/Makefile2
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c1464
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h130
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/usf.c2365
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c424
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h28
36 files changed, 15101 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index e59904d8f7b6..3d07d940a326 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -551,6 +551,7 @@ source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/altera-stapl/Kconfig"
source "drivers/misc/mei/Kconfig"
source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/qcom/Kconfig"
source "drivers/misc/mic/Kconfig"
source "drivers/misc/genwqe/Kconfig"
source "drivers/misc/echo/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 43d0131dc146..136582b22b6a 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
+obj-y += qcom/
diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig
new file mode 100644
index 000000000000..a62297e913d2
--- /dev/null
+++ b/drivers/misc/qcom/Kconfig
@@ -0,0 +1,18 @@
+config MSM_QDSP6V2_CODECS
+ bool "Audio QDSP6V2 APR support"
+ depends on MSM_SMD
+ help
+ Enable Audio codecs with APR IPC protocol support between
+ application processor and QDSP6 for B-family. APR is
+ used by audio driver to configure QDSP6's
+ ASM, ADM and AFE.
+
+config MSM_ULTRASOUND
+ bool "QDSP6V2 HW Ultrasound support"
+ help
+ Enable HW Ultrasound support in QDSP6V2.
+ QDSP6V2 can support HW encoder & decoder and
+ ultrasound processing. It will enable
+ ultrasound data paths between
+ HW and services, calculating input events
+ upon the ultrasound data.
diff --git a/drivers/misc/qcom/Makefile b/drivers/misc/qcom/Makefile
new file mode 100644
index 000000000000..120bdddcbc84
--- /dev/null
+++ b/drivers/misc/qcom/Makefile
@@ -0,0 +1 @@
+obj-y += qdsp6v2/
diff --git a/drivers/misc/qcom/qdsp6v2/Makefile b/drivers/misc/qcom/qdsp6v2/Makefile
new file mode 100644
index 000000000000..7006ff4a272f
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_alac.o audio_ape.o audio_utils_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o audio_hwacc_effects.o
+obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
diff --git a/drivers/misc/qcom/qdsp6v2/aac_in.c b/drivers/misc/qcom/qdsp6v2/aac_in.c
new file mode 100644
index 000000000000..c9d5dbb0b313
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/aac_in.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 2010-2015, 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/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_aac.h>
+#include <linux/compat.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 5 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5))
+
+#define AAC_FORMAT_ADTS 65535
+
+static long aac_in_ioctl_shared(struct file *file, unsigned int cmd, void *arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_aac_enc_config *enc_cfg;
+ struct msm_audio_aac_config *aac_config;
+ uint32_t aac_mode = AAC_ENC_MODE_AAC_LC;
+
+ enc_cfg = audio->enc_cfg;
+ aac_config = audio->codec_cfg;
+ /* ENCODE CFG (after new set of API's are published )bharath*/
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+
+ if (audio->opened) {
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ } else {
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ pr_debug("%s: starting in non_tunnel mode",
+ __func__);
+ rc = q6asm_open_read_write(audio->ac,
+ FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:open read write failed\n",
+ __func__);
+ break;
+ }
+ }
+ if (audio->feedback == TUNNEL_MODE) {
+ pr_debug("%s: starting in tunnel mode",
+ __func__);
+ rc = q6asm_open_read(audio->ac,
+ FORMAT_MPEG4_AAC);
+
+ if (rc < 0) {
+ pr_err("%s:open read failed\n",
+ __func__);
+ break;
+ }
+ }
+ audio->stopped = 0;
+ }
+
+ pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__,
+ aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag);
+ if (aac_config->sbr_ps_on_flag)
+ aac_mode = AAC_ENC_MODE_EAAC_P;
+ else if (aac_config->sbr_on_flag)
+ aac_mode = AAC_ENC_MODE_AAC_P;
+ else
+ aac_mode = AAC_ENC_MODE_AAC_LC;
+
+ rc = q6asm_enc_cfg_blk_aac(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->sample_rate,
+ enc_cfg->channels,
+ enc_cfg->bit_rate,
+ aac_mode,
+ enc_cfg->stream_format);
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ }
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure"
+ "failed rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac);
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__,
+ audio->ac->session);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_GET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config *cfg;
+ struct msm_audio_aac_enc_config *enc_cfg;
+
+ cfg = (struct msm_audio_aac_enc_config *)arg;
+ if (cfg == NULL) {
+ pr_err("%s: NULL config pointer for %s\n",
+ __func__, "AUDIO_GET_AAC_CONFIG");
+ rc = -EINVAL;
+ break;
+ }
+ memset(cfg, 0, sizeof(*cfg));
+ enc_cfg = audio->enc_cfg;
+ if (enc_cfg->channels == CH_MODE_MONO)
+ cfg->channels = 1;
+ else
+ cfg->channels = 2;
+
+ cfg->sample_rate = enc_cfg->sample_rate;
+ cfg->bit_rate = enc_cfg->bit_rate;
+ switch (enc_cfg->stream_format) {
+ case 0x00:
+ cfg->stream_format = AUDIO_AAC_FORMAT_ADTS;
+ break;
+ case 0x01:
+ cfg->stream_format = AUDIO_AAC_FORMAT_LOAS;
+ break;
+ case 0x02:
+ cfg->stream_format = AUDIO_AAC_FORMAT_ADIF;
+ break;
+ default:
+ case 0x03:
+ cfg->stream_format = AUDIO_AAC_FORMAT_RAW;
+ }
+ pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d"
+ "bitrate=%d\n", __func__, audio->ac->session,
+ cfg->stream_format, cfg->sample_rate, cfg->bit_rate);
+ break;
+ }
+ case AUDIO_SET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config *cfg;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ uint32_t min_bitrate, max_bitrate;
+
+ cfg = (struct msm_audio_aac_enc_config *)arg;
+ if (cfg == NULL) {
+ pr_err("%s: NULL config pointer for %s\n",
+ "AUDIO_SET_AAC_ENC_CONFIG", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__,
+ audio->ac->session, cfg->stream_format);
+
+ switch (cfg->stream_format) {
+ case AUDIO_AAC_FORMAT_ADTS:
+ enc_cfg->stream_format = 0x00;
+ break;
+ case AUDIO_AAC_FORMAT_LOAS:
+ enc_cfg->stream_format = 0x01;
+ break;
+ case AUDIO_AAC_FORMAT_ADIF:
+ enc_cfg->stream_format = 0x02;
+ break;
+ case AUDIO_AAC_FORMAT_RAW:
+ enc_cfg->stream_format = 0x03;
+ break;
+ default:
+ pr_err("%s:session id %d: unsupported AAC format %d\n",
+ __func__, audio->ac->session,
+ cfg->stream_format);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (cfg->channels == 1) {
+ cfg->channels = CH_MODE_MONO;
+ } else if (cfg->channels == 2) {
+ cfg->channels = CH_MODE_STEREO;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+
+ min_bitrate = ((cfg->sample_rate)*(cfg->channels))/2;
+ /* This calculation should be based on AAC mode. But we cannot
+ * get AAC mode in this setconfig. min_bitrate's logical max
+ * value is 24000. So if min_bitrate is higher than 24000,
+ * choose 24000.
+ */
+ if (min_bitrate > 24000)
+ min_bitrate = 24000;
+ max_bitrate = 6*(cfg->sample_rate)*(cfg->channels);
+ if (max_bitrate > 192000)
+ max_bitrate = 192000;
+ if ((cfg->bit_rate < min_bitrate) ||
+ (cfg->bit_rate > max_bitrate)) {
+ pr_err("%s: bitrate permissible: max=%d, min=%d\n",
+ __func__, max_bitrate, min_bitrate);
+ pr_err("%s: ERROR in setting bitrate = %d\n",
+ __func__, cfg->bit_rate);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg->sample_rate = cfg->sample_rate;
+ enc_cfg->channels = cfg->channels;
+ enc_cfg->bit_rate = cfg->bit_rate;
+ pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x"
+ "bitrate=0x%x, format(adts/raw) = %d\n",
+ __func__, audio->ac->session, enc_cfg->sample_rate,
+ enc_cfg->channels, enc_cfg->bit_rate,
+ enc_cfg->stream_format);
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config *aac_cfg;
+ struct msm_audio_aac_config *audio_aac_cfg;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ audio_aac_cfg = audio->codec_cfg;
+ aac_cfg = (struct msm_audio_aac_config *)arg;
+
+ if (aac_cfg == NULL) {
+ pr_err("%s: NULL config pointer %s\n",
+ __func__, "AUDIO_SET_AAC_CONFIG");
+ rc = -EINVAL;
+ break;
+ }
+ pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d sbr_ps_flag = %d\n",
+ __func__, audio->ac->session, aac_cfg->sbr_on_flag,
+ aac_cfg->sbr_ps_on_flag);
+ audio_aac_cfg->sbr_on_flag = aac_cfg->sbr_on_flag;
+ audio_aac_cfg->sbr_ps_on_flag = aac_cfg->sbr_ps_on_flag;
+ if ((audio_aac_cfg->sbr_on_flag == 1) ||
+ (audio_aac_cfg->sbr_ps_on_flag == 1)) {
+ if (enc_cfg->sample_rate < 24000) {
+ pr_err("%s: ERROR in setting samplerate = %d"
+ "\n", __func__, enc_cfg->sample_rate);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static long aac_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = aac_in_ioctl_shared(file, cmd, NULL);
+ break;
+ }
+ case AUDIO_GET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config cfg;
+ rc = aac_in_ioctl_shared(file, cmd, &cfg);
+ if (rc) {
+ pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_ENC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = aac_in_ioctl_shared(file, cmd, &cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG: {
+ if (copy_to_user((void *)arg, &audio->codec_cfg,
+ sizeof(struct msm_audio_aac_config))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config aac_cfg;
+ if (copy_from_user(&aac_cfg, (void *)arg,
+ sizeof(struct msm_audio_aac_config))) {
+ pr_err("%s: copy_to_user for AUDIO_SET_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = aac_in_ioctl_shared(file, cmd, &aac_cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd=%d\n", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_aac_enc_config32 {
+ u32 channels;
+ u32 sample_rate;
+ u32 bit_rate;
+ u32 stream_format;
+};
+
+struct msm_audio_aac_config32 {
+ s16 format;
+ u16 audio_object;
+ u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */
+ u16 aac_section_data_resilience_flag;
+ u16 aac_scalefactor_data_resilience_flag;
+ u16 aac_spectral_data_resilience_flag;
+ u16 sbr_on_flag;
+ u16 sbr_ps_on_flag;
+ u16 dual_mono_mode;
+ u16 channel_configuration;
+ u16 sample_rate;
+};
+
+enum {
+ AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
+ AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32),
+ AUDIO_SET_AAC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config32),
+ AUDIO_GET_AAC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config32)
+};
+
+static long aac_in_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = aac_in_ioctl_shared(file, cmd, NULL);
+ break;
+ }
+ case AUDIO_GET_AAC_ENC_CONFIG_32: {
+ struct msm_audio_aac_enc_config cfg;
+ struct msm_audio_aac_enc_config32 cfg_32;
+
+ cmd = AUDIO_GET_AAC_ENC_CONFIG;
+ rc = aac_in_ioctl_shared(file, cmd, &cfg);
+ if (rc) {
+ pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG_32 failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ cfg_32.channels = cfg.channels;
+ cfg_32.sample_rate = cfg.sample_rate;
+ cfg_32.bit_rate = cfg.bit_rate;
+ cfg_32.stream_format = cfg.stream_format;
+ if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_ENC_CONFIG_32: {
+ struct msm_audio_aac_enc_config cfg;
+ struct msm_audio_aac_enc_config32 cfg_32;
+ if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cfg.channels = cfg_32.channels;
+ cfg.sample_rate = cfg_32.sample_rate;
+ cfg.bit_rate = cfg_32.bit_rate;
+ cfg.stream_format = cfg_32.stream_format;
+ /* The command should be converted from 32 bit to normal
+ * before the shared ioctl is called as shared ioctl
+ * can process only normal commands */
+ cmd = AUDIO_SET_AAC_ENC_CONFIG;
+ rc = aac_in_ioctl_shared(file, cmd, &cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG_32 failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG_32: {
+ struct msm_audio_aac_config *aac_config;
+ struct msm_audio_aac_config32 aac_config_32;
+
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ aac_config_32.format = aac_config->format;
+ aac_config_32.audio_object = aac_config->audio_object;
+ aac_config_32.ep_config = aac_config->ep_config;
+ aac_config_32.aac_section_data_resilience_flag =
+ aac_config->aac_section_data_resilience_flag;
+ aac_config_32.aac_scalefactor_data_resilience_flag =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_config_32.aac_spectral_data_resilience_flag =
+ aac_config->aac_spectral_data_resilience_flag;
+ aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
+ aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
+ aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
+ aac_config_32.channel_configuration =
+ aac_config->channel_configuration;
+ aac_config_32.sample_rate = aac_config->sample_rate;
+
+ if (copy_to_user((void *)arg, &aac_config_32,
+ sizeof(aac_config_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG_32: {
+ struct msm_audio_aac_config aac_cfg;
+ struct msm_audio_aac_config32 aac_cfg_32;
+ if (copy_from_user(&aac_cfg_32, (void *)arg,
+ sizeof(aac_cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ aac_cfg.format = aac_cfg_32.format;
+ aac_cfg.audio_object = aac_cfg_32.audio_object;
+ aac_cfg.ep_config = aac_cfg_32.ep_config;
+ aac_cfg.aac_section_data_resilience_flag =
+ aac_cfg_32.aac_section_data_resilience_flag;
+ aac_cfg.aac_scalefactor_data_resilience_flag =
+ aac_cfg_32.aac_scalefactor_data_resilience_flag;
+ aac_cfg.aac_spectral_data_resilience_flag =
+ aac_cfg_32.aac_spectral_data_resilience_flag;
+ aac_cfg.sbr_on_flag = aac_cfg_32.sbr_on_flag;
+ aac_cfg.sbr_ps_on_flag = aac_cfg_32.sbr_ps_on_flag;
+ aac_cfg.dual_mono_mode = aac_cfg_32.dual_mono_mode;
+ aac_cfg.channel_configuration =
+ aac_cfg_32.channel_configuration;
+ aac_cfg.sample_rate = aac_cfg_32.sample_rate;
+
+ cmd = AUDIO_SET_AAC_CONFIG;
+ rc = aac_in_ioctl_shared(file, cmd, &aac_cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d\n", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+#else
+#define aac_in_compat_ioctl NULL
+#endif
+
+static int aac_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ struct msm_audio_aac_config *aac_config;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for aac"
+ "driver\n", __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config\n", __func__, audio->ac->session);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ aac_config = audio->codec_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 1536;
+ audio->max_frames_per_buf = 5;
+ enc_cfg->sample_rate = 8000;
+ enc_cfg->channels = 1;
+ enc_cfg->bit_rate = 16000;
+ enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ aac_config->format = AUDIO_AAC_FORMAT_ADTS;
+ aac_config->audio_object = AUDIO_AAC_OBJECT_LC;
+ aac_config->sbr_on_flag = 0;
+ aac_config->sbr_ps_on_flag = 0;
+ aac_config->channel_configuration = 1;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for"
+ "audio client\n", __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ /* open aac encoder in tunnel mode */
+ audio->buf_cfg.frames_per_buf = 0x01;
+
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC,
+ FORMAT_LINEAR_PCM);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->buf_cfg.meta_info_enable = 0x01;
+ pr_info("%s:session id %d: NT mode encoder success\n", __func__,
+ audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: Tunnel Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__,
+ audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->buf_cfg.meta_info_enable = 0x00;
+ pr_info("%s:session id %d: T mode encoder success\n", __func__,
+ audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+ audio->opened = 1;
+ audio->reset_event = false;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_compat_ioctl = aac_in_compat_ioctl;
+ audio->enc_ioctl = aac_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = aac_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+ .compat_ioctl = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_aac_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init aac_in_init(void)
+{
+ return misc_register(&audio_aac_in_misc);
+}
+device_initcall(aac_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
new file mode 100644
index 000000000000..eb92137f0671
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
@@ -0,0 +1,401 @@
+/* Copyright (c) 2010-2012, 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_amrnb.h>
+#include <linux/compat.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10))
+
+static long amrnb_in_ioctl_shared(struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ rc = q6asm_enc_cfg_blk_amrnb(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->band_mode,
+ enc_cfg->dtx_enable);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd amrnb media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+ __func__, audio->ac->session,
+ audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__,
+ audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:AUDIO_STOP\n", __func__);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__,
+ audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+ struct msm_audio_amrnb_enc_config_v2 *cfg;
+ struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+ cfg = (struct msm_audio_amrnb_enc_config_v2 *)arg;
+ if (cfg == NULL) {
+ pr_err("%s: NULL config pointer for %s\n",
+ __func__,
+ "AUDIO_SET_AMRNB_ENC_CONFIG_V2");
+ rc = -EINVAL;
+ break;
+ }
+
+ enc_cfg = audio->enc_cfg;
+ if (cfg->band_mode > 8 ||
+ cfg->band_mode < 1) {
+ pr_err("%s:session id %d: invalid band mode\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ /* AMR NB encoder accepts values between 0-7
+ while openmax provides value between 1-8
+ as per spec */
+ enc_cfg->band_mode = (cfg->band_mode - 1);
+ enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0);
+ enc_cfg->frame_format = 0;
+ pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
+ __func__, audio->ac->session,
+ enc_cfg->band_mode, enc_cfg->dtx_enable);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static long amrnb_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = amrnb_in_ioctl_shared(file, cmd, NULL);
+ break;
+ }
+ case AUDIO_GET_AMRNB_ENC_CONFIG_V2: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+ struct msm_audio_amrnb_enc_config_v2 cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(cfg))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = amrnb_in_ioctl_shared(file, cmd, &cfg);
+ if (rc)
+ pr_err("%s: AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd=%d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_amrnb_enc_config_v2_32 {
+ u32 band_mode;
+ u32 dtx_enable;
+ u32 frame_format;
+};
+
+enum {
+ AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+2),
+ struct msm_audio_amrnb_enc_config_v2_32),
+ AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+3),
+ struct msm_audio_amrnb_enc_config_v2_32)
+};
+
+static long amrnb_in_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = amrnb_in_ioctl_shared(file, cmd, NULL);
+ break;
+ }
+ case AUDIO_GET_AMRNB_ENC_CONFIG_V2_32: {
+ struct msm_audio_amrnb_enc_config_v2 *amrnb_config;
+ struct msm_audio_amrnb_enc_config_v2_32 amrnb_config_32;
+
+ amrnb_config =
+ (struct msm_audio_amrnb_enc_config_v2 *)audio->enc_cfg;
+ amrnb_config_32.band_mode = amrnb_config->band_mode;
+ amrnb_config_32.dtx_enable = amrnb_config->dtx_enable;
+ amrnb_config_32.frame_format = amrnb_config->frame_format;
+
+ if (copy_to_user((void *)arg, &amrnb_config_32,
+ sizeof(amrnb_config_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 failed",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRNB_ENC_CONFIG_V2_32: {
+ struct msm_audio_amrnb_enc_config_v2_32 cfg_32;
+ if (copy_from_user(&cfg_32, (void *) arg,
+ sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cmd = AUDIO_SET_AMRNB_ENC_CONFIG_V2;
+ rc = amrnb_in_ioctl_shared(file, cmd, &cfg_32);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+#else
+#define amrnb_in_compat_ioctl NULL
+#endif
+
+static int amrnb_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s Could not allocate memory for amrnb"
+ "driver\n", __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 32;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->band_mode = 7;
+ enc_cfg->dtx_enable = 0;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for audio"
+ "client\n", __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open amrnb encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n",
+ __func__, audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_AMRNB);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__, audio->ac->session,
+ rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n",
+ __func__, audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_compat_ioctl = amrnb_in_compat_ioctl;
+ audio->enc_ioctl = amrnb_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = amrnb_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+ .compat_ioctl = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_amrnb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init amrnb_in_init(void)
+{
+ return misc_register(&audio_amrnb_in_misc);
+}
+
+device_initcall(amrnb_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
new file mode 100644
index 000000000000..4cea3dc63389
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
@@ -0,0 +1,396 @@
+/* Copyright (c) 2011-2012, 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/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/msm_audio_amrwb.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/compat.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10))
+
+static long amrwb_in_ioctl_shared(struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_amrwb_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ rc = q6asm_enc_cfg_blk_amrwb(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->band_mode,
+ enc_cfg->dtx_enable);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd amrwb media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+ __func__, audio->ac->session,
+ audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:AUDIO_STOP\n", __func__);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRWB_ENC_CONFIG: {
+ struct msm_audio_amrwb_enc_config *cfg;
+ struct msm_audio_amrwb_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ cfg = (struct msm_audio_amrwb_enc_config *)arg;
+ if (cfg == NULL) {
+ pr_err("%s: NULL config pointer for %s\n",
+ __func__, "AUDIO_SET_AMRWB_ENC_CONFIG");
+ rc = -EINVAL;
+ break;
+ }
+
+ if (cfg->band_mode > 8) {
+ pr_err("%s:session id %d: invalid band mode\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ /* ToDo: AMR WB encoder accepts values between 0-8
+ while openmax provides value between 9-17
+ as per spec */
+ enc_cfg->band_mode = cfg->band_mode;
+ enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0);
+ /* Currently DSP does not support different frameformat */
+ enc_cfg->frame_format = 0;
+ pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
+ __func__, audio->ac->session,
+ enc_cfg->band_mode, enc_cfg->dtx_enable);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static long amrwb_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = amrwb_in_ioctl_shared(file, cmd, NULL);
+ break;
+ }
+ case AUDIO_GET_AMRWB_ENC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_amrwb_enc_config)))
+ pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_AMRWB_ENC_CONFIG: {
+ struct msm_audio_amrwb_enc_config cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(cfg))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = amrwb_in_ioctl_shared(file, cmd, &cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_amrwb_enc_config_32 {
+ u32 band_mode;
+ u32 dtx_enable;
+ u32 frame_format;
+};
+
+enum {
+ AUDIO_GET_AMRWB_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0),
+ struct msm_audio_amrwb_enc_config_32),
+ AUDIO_SET_AMRWB_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1),
+ struct msm_audio_amrwb_enc_config_32)
+};
+
+static long amrwb_in_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = amrwb_in_ioctl_shared(file, cmd, NULL);
+ break;
+ }
+ case AUDIO_GET_AMRWB_ENC_CONFIG_32: {
+ struct msm_audio_amrwb_enc_config *amrwb_config;
+ struct msm_audio_amrwb_enc_config_32 amrwb_config_32;
+
+ amrwb_config =
+ (struct msm_audio_amrwb_enc_config *)audio->enc_cfg;
+ amrwb_config_32.band_mode = amrwb_config->band_mode;
+ amrwb_config_32.dtx_enable = amrwb_config->dtx_enable;
+ amrwb_config_32.frame_format = amrwb_config->frame_format;
+
+ if (copy_to_user((void *)arg, &amrwb_config_32,
+ sizeof(struct msm_audio_amrwb_enc_config_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRWB_ENC_CONFIG_32: {
+ struct msm_audio_amrwb_enc_config cfg_32;
+ if (copy_from_user(&cfg_32, (void *) arg,
+ sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cmd = AUDIO_SET_AMRWB_ENC_CONFIG;
+ rc = amrwb_in_ioctl_shared(file, cmd, &cfg_32);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+#else
+#define amrwb_in_compat_ioctl NULL
+#endif
+
+static int amrwb_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_amrwb_enc_config *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for amrwb driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for amrwb"
+ "config param\n", __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 32;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->band_mode = 8;
+ enc_cfg->dtx_enable = 0;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 16000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s:audio[%p]: Could not allocate memory for audio"
+ "client\n", __func__, audio);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open amrwb encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n",
+ __func__, audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_AMRWB);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__, audio->ac->session,
+ rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n",
+ __func__, audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_compat_ioctl = amrwb_in_compat_ioctl;
+ audio->enc_ioctl = amrwb_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = amrwb_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+ .compat_ioctl = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_amrwb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwb_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init amrwb_in_init(void)
+{
+ return misc_register(&audio_amrwb_in_misc);
+}
+
+device_initcall(amrwb_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/drivers/misc/qcom/qdsp6v2/audio_aac.c
new file mode 100644
index 000000000000..e49d91d74514
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_aac.c
@@ -0,0 +1,470 @@
+/* aac audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_audio_aac.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
+#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out))
+
+static struct miscdevice audio_aac_misc;
+static struct ws_mgr audio_aac_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_aac_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_aac_cfg aac_cfg;
+ struct msm_audio_aac_config *aac_config;
+ uint32_t sbr_ps = 0x00;
+ pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+ audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ /* turn on both sbr and ps */
+ rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
+ if (rc < 0)
+ pr_err("sbr-ps enable failed\n");
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ if (aac_config->sbr_ps_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ else if (aac_config->sbr_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_AAC_P;
+ else
+ aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
+
+ switch (aac_config->format) {
+ case AUDIO_AAC_FORMAT_ADTS:
+ aac_cfg.format = 0x00;
+ break;
+ case AUDIO_AAC_FORMAT_LOAS:
+ aac_cfg.format = 0x01;
+ break;
+ case AUDIO_AAC_FORMAT_ADIF:
+ aac_cfg.format = 0x02;
+ break;
+ default:
+ case AUDIO_AAC_FORMAT_RAW:
+ aac_cfg.format = 0x03;
+ }
+ aac_cfg.ep_config = aac_config->ep_config;
+ aac_cfg.section_data_resilience =
+ aac_config->aac_section_data_resilience_flag;
+ aac_cfg.scalefactor_data_resilience =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_cfg.spectral_data_resilience =
+ aac_config->aac_spectral_data_resilience_flag;
+ aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+ if (audio->feedback == TUNNEL_MODE) {
+ aac_cfg.sample_rate = aac_config->sample_rate;
+ aac_cfg.ch_cfg = aac_config->channel_configuration;
+ } else {
+ aac_cfg.sample_rate = audio->pcm_cfg.sample_rate;
+ aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+ }
+
+ pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n",
+ __func__, aac_cfg.format,
+ aac_cfg.aot, aac_cfg.ch_cfg,
+ aac_cfg.sample_rate);
+
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ rc = enable_volume_ramp(audio);
+ if (rc < 0) {
+ pr_err("%s: Failed to enable volume ramp\n",
+ __func__);
+ }
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config *aac_config;
+ uint16_t sce_left = 1, sce_right = 2;
+
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+ aac_config = (struct msm_audio_aac_config *)arg;
+ if (aac_config == NULL) {
+ pr_err("%s: Invalid config pointer\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ memcpy(audio->codec_cfg, aac_config,
+ sizeof(struct msm_audio_aac_config));
+ /* PL_PR is 0 only need to check PL_SR */
+ if (aac_config->dual_mono_mode >
+ AUDIO_AAC_DUAL_MONO_PL_SR) {
+ pr_err("%s:Invalid dual_mono mode =%d\n", __func__,
+ aac_config->dual_mono_mode);
+ } else {
+ /* convert the data from user into sce_left
+ * and sce_right based on the definitions
+ */
+ pr_debug("%s: modify dual_mono mode =%d\n", __func__,
+ aac_config->dual_mono_mode);
+ switch (aac_config->dual_mono_mode) {
+ case AUDIO_AAC_DUAL_MONO_PL_PR:
+ sce_left = 1;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_SR:
+ sce_left = 2;
+ sce_right = 2;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_PR:
+ sce_left = 2;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_PL_SR:
+ default:
+ sce_left = 1;
+ sce_right = 2;
+ break;
+ }
+ rc = q6asm_cfg_dual_mono_aac(audio->ac,
+ sce_left, sce_right);
+ if (rc < 0)
+ pr_err("%s:asm cmd dualmono failed rc=%d\n",
+ __func__, rc);
+ }
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_aac_config))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config aac_config;
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+ if (copy_from_user(&aac_config, (void *)arg,
+ sizeof(aac_config))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = audio_ioctl_shared(file, cmd, &aac_config);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("%s[%p]:Failed in utils_ioctl: %d\n",
+ __func__, audio, rc);
+ }
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_aac_config32 {
+ s16 format;
+ u16 audio_object;
+ u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */
+ u16 aac_section_data_resilience_flag;
+ u16 aac_scalefactor_data_resilience_flag;
+ u16 aac_spectral_data_resilience_flag;
+ u16 sbr_on_flag;
+ u16 sbr_ps_on_flag;
+ u16 dual_mono_mode;
+ u16 channel_configuration;
+ u16 sample_rate;
+};
+
+enum {
+ AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
+ AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG_32: {
+ struct msm_audio_aac_config *aac_config;
+ struct msm_audio_aac_config32 aac_config_32;
+
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ aac_config_32.format = aac_config->format;
+ aac_config_32.audio_object = aac_config->audio_object;
+ aac_config_32.ep_config = aac_config->ep_config;
+ aac_config_32.aac_section_data_resilience_flag =
+ aac_config->aac_section_data_resilience_flag;
+ aac_config_32.aac_scalefactor_data_resilience_flag =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_config_32.aac_spectral_data_resilience_flag =
+ aac_config->aac_spectral_data_resilience_flag;
+ aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
+ aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
+ aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
+ aac_config_32.channel_configuration =
+ aac_config->channel_configuration;
+ aac_config_32.sample_rate = aac_config->sample_rate;
+
+ if (copy_to_user((void *)arg, &aac_config_32,
+ sizeof(aac_config_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG_32: {
+ struct msm_audio_aac_config aac_config;
+ struct msm_audio_aac_config32 aac_config_32;
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+ if (copy_from_user(&aac_config_32, (void *)arg,
+ sizeof(aac_config_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ aac_config.format = aac_config_32.format;
+ aac_config.audio_object = aac_config_32.audio_object;
+ aac_config.ep_config = aac_config_32.ep_config;
+ aac_config.aac_section_data_resilience_flag =
+ aac_config_32.aac_section_data_resilience_flag;
+ aac_config.aac_scalefactor_data_resilience_flag =
+ aac_config_32.aac_scalefactor_data_resilience_flag;
+ aac_config.aac_spectral_data_resilience_flag =
+ aac_config_32.aac_spectral_data_resilience_flag;
+ aac_config.sbr_on_flag = aac_config_32.sbr_on_flag;
+ aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag;
+ aac_config.dual_mono_mode = aac_config_32.dual_mono_mode;
+ aac_config.channel_configuration =
+ aac_config_32.channel_configuration;
+ aac_config.sample_rate = aac_config_32.sample_rate;
+
+ cmd = AUDIO_SET_AAC_CONFIG;
+ rc = audio_ioctl_shared(file, cmd, &aac_config);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("%s[%p]:Failed in utils_ioctl: %d\n",
+ __func__, audio, rc);
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+ struct msm_audio_aac_config *aac_config = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_aac_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:Could not allocate memory for aac"
+ "config\n", __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ aac_config = audio->codec_cfg;
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC;
+ audio->miscdevice = &audio_aac_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_aac_ws_mgr;
+ aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_MPEG4_AAC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open AAC decoder, expected frames is always 1
+ audio->buf_cfg.frames_per_buf = 0x01;*/
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_aac_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_aac_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_aac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_aac_init(void)
+{
+ int ret = misc_register(&audio_aac_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_aac_misc.this_device, true);
+ audio_aac_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_aac_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_aac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_alac.c b/drivers/misc/qcom/qdsp6v2/audio_alac.c
new file mode 100644
index 000000000000..9748db30fac3
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_alac.c
@@ -0,0 +1,436 @@
+/* Copyright (c) 2015, 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/types.h>
+#include <linux/msm_audio_alac.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_alac_misc;
+static struct ws_mgr audio_alac_ws_mgr;
+
+static const struct file_operations audio_alac_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+
+static struct dentry *config_debugfs_create_file(const char *name, void *data)
+{
+ return debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)data, &audio_alac_debug_fops);
+}
+
+static int alac_channel_map(u8 *channel_mapping, uint32_t channels);
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_alac_cfg alac_cfg;
+ struct msm_audio_alac_config *alac_config;
+ u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (alac_channel_map(channel_mapping,
+ audio->pcm_cfg.channel_count)) {
+ pr_err("%s: setting channel map failed %d\n",
+ __func__, audio->pcm_cfg.channel_count);
+ }
+
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count,
+ 16, /*bits per sample*/
+ false, false, channel_mapping);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
+ alac_cfg.frame_length = alac_config->frameLength;
+ alac_cfg.compatible_version = alac_config->compatVersion;
+ alac_cfg.bit_depth = alac_config->bitDepth;
+ alac_cfg.pb = alac_config->pb;
+ alac_cfg.mb = alac_config->mb;
+ alac_cfg.kb = alac_config->kb;
+ alac_cfg.num_channels = alac_config->channelCount;
+ alac_cfg.max_run = alac_config->maxRun;
+ alac_cfg.max_frame_bytes = alac_config->maxSize;
+ alac_cfg.avg_bit_rate = alac_config->averageBitRate;
+ alac_cfg.sample_rate = alac_config->sampleRate;
+ alac_cfg.channel_layout_tag = alac_config->channelLayout;
+ pr_debug("%s: frame_length %d compatible_version %d bit_depth %d pb %d mb %d kb %d num_channels %d max_run %d max_frame_bytes %d avg_bit_rate %d sample_rate %d channel_layout_tag %d\n",
+ __func__, alac_config->frameLength,
+ alac_config->compatVersion,
+ alac_config->bitDepth, alac_config->pb,
+ alac_config->mb, alac_config->kb,
+ alac_config->channelCount, alac_config->maxRun,
+ alac_config->maxSize,
+ alac_config->averageBitRate,
+ alac_config->sampleRate,
+ alac_config->channelLayout);
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_alac(audio->ac, &alac_cfg,
+ audio->ac->stream_id);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_ALAC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_alac_config))) {
+ pr_err("%s:copy_to_user for AUDIO_GET_ALAC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_ALAC_CONFIG: {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_alac_config))) {
+ pr_err("%s:copy_from_user for AUDIO_SET_ALAC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default: {
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_alac_config_32 {
+ u32 frameLength;
+ u8 compatVersion;
+ u8 bitDepth;
+ u8 pb;
+ u8 mb;
+ u8 kb;
+ u8 channelCount;
+ u16 maxRun;
+ u32 maxSize;
+ u32 averageBitRate;
+ u32 sampleRate;
+ u32 channelLayout;
+};
+
+enum {
+ AUDIO_GET_ALAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config_32),
+ AUDIO_SET_ALAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_ALAC_CONFIG_32: {
+ struct msm_audio_alac_config *alac_config;
+ struct msm_audio_alac_config_32 alac_config_32;
+
+ alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
+ alac_config_32.frameLength = alac_config->frameLength;
+ alac_config_32.compatVersion =
+ alac_config->compatVersion;
+ alac_config_32.bitDepth = alac_config->bitDepth;
+ alac_config_32.pb = alac_config->pb;
+ alac_config_32.mb = alac_config->mb;
+ alac_config_32.kb = alac_config->kb;
+ alac_config_32.channelCount = alac_config->channelCount;
+ alac_config_32.maxRun = alac_config->maxRun;
+ alac_config_32.maxSize = alac_config->maxSize;
+ alac_config_32.averageBitRate = alac_config->averageBitRate;
+ alac_config_32.sampleRate = alac_config->sampleRate;
+ alac_config_32.channelLayout = alac_config->channelLayout;
+
+ if (copy_to_user((void *)arg, &alac_config_32,
+ sizeof(alac_config_32))) {
+ pr_err("%s: copy_to_user for GET_ALAC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_ALAC_CONFIG_32: {
+ struct msm_audio_alac_config *alac_config;
+ struct msm_audio_alac_config_32 alac_config_32;
+
+ if (copy_from_user(&alac_config_32, (void *)arg,
+ sizeof(alac_config_32))) {
+ pr_err("%s: copy_from_user for SET_ALAC_CONFIG_32 failed\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
+ alac_config->frameLength = alac_config_32.frameLength;
+ alac_config->compatVersion =
+ alac_config_32.compatVersion;
+ alac_config->bitDepth = alac_config_32.bitDepth;
+ alac_config->pb = alac_config_32.pb;
+ alac_config->mb = alac_config_32.mb;
+ alac_config->kb = alac_config_32.kb;
+ alac_config->channelCount = alac_config_32.channelCount;
+ alac_config->maxRun = alac_config_32.maxRun;
+ alac_config->maxSize = alac_config_32.maxSize;
+ alac_config->averageBitRate = alac_config_32.averageBitRate;
+ alac_config->sampleRate = alac_config_32.sampleRate;
+ alac_config->channelLayout = alac_config_32.channelLayout;
+
+ break;
+ }
+ default: {
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_alac_" + 5];
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (!audio) {
+ pr_err("Could not allocate memory for alac decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_alac_config),
+ GFP_KERNEL);
+ if (!audio->codec_cfg) {
+ pr_err("%s:Could not allocate memory for alac config\n",
+ __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_alac_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_alac_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_ALAC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open ALAC decoder, expected frames is always 1*/
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_ALAC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+ snprintf(name, sizeof(name), "msm_alac_%04x", audio->ac->session);
+ audio->dentry = config_debugfs_create_file(name, (void *)audio);
+
+ if (IS_ERR_OR_NULL(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+ pr_debug("%s:alacdec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static int alac_channel_map(u8 *channel_mapping, uint32_t channels)
+{
+ u8 *lchannel_mapping;
+
+ lchannel_mapping = channel_mapping;
+ pr_debug("%s: channels passed: %d\n", __func__, channels);
+ if (channels == 1) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channels == 2) {
+ lchannel_mapping[0] = PCM_CHANNEL_FL;
+ lchannel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channels == 3) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FL;
+ lchannel_mapping[2] = PCM_CHANNEL_FR;
+ } else if (channels == 4) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FL;
+ lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[3] = PCM_CHANNEL_CS;
+ } else if (channels == 5) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FL;
+ lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[3] = PCM_CHANNEL_LS;
+ lchannel_mapping[4] = PCM_CHANNEL_RS;
+ } else if (channels == 6) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FL;
+ lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[3] = PCM_CHANNEL_LS;
+ lchannel_mapping[4] = PCM_CHANNEL_RS;
+ lchannel_mapping[5] = PCM_CHANNEL_LFE;
+ } else if (channels == 7) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FL;
+ lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[3] = PCM_CHANNEL_LS;
+ lchannel_mapping[4] = PCM_CHANNEL_RS;
+ lchannel_mapping[5] = PCM_CHANNEL_CS;
+ lchannel_mapping[6] = PCM_CHANNEL_LFE;
+ } else if (channels == 8) {
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FLC;
+ lchannel_mapping[2] = PCM_CHANNEL_FRC;
+ lchannel_mapping[3] = PCM_CHANNEL_FL;
+ lchannel_mapping[4] = PCM_CHANNEL_FR;
+ lchannel_mapping[5] = PCM_CHANNEL_LS;
+ lchannel_mapping[6] = PCM_CHANNEL_RS;
+ lchannel_mapping[7] = PCM_CHANNEL_LFE;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct file_operations audio_alac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_alac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_alac",
+ .fops = &audio_alac_fops,
+};
+
+static int __init audio_alac_init(void)
+{
+ int ret = misc_register(&audio_alac_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_alac_misc.this_device, true);
+ audio_alac_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_alac_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_alac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
new file mode 100644
index 000000000000..1625adb82be9
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
@@ -0,0 +1,178 @@
+/* amrnb audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_amrnb_misc;
+static struct ws_mgr audio_amrnb_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrnb_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_amrnb_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for wma decode driver\n");
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_amrnb_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_amrnb_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_AMRNB);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_AMRNB);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_amrnb_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_amrnb_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrnb_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_amrnb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb",
+ .fops = &audio_amrnb_fops,
+};
+
+static int __init audio_amrnb_init(void)
+{
+ int ret = misc_register(&audio_amrnb_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_amrnb_misc.this_device, true);
+ audio_amrnb_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_amrnb_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_amrnb_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
new file mode 100644
index 000000000000..c7ff607414a5
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
@@ -0,0 +1,182 @@
+/* amrwb audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_amrwb_misc;
+static struct ws_mgr audio_amrwb_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwb_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_amrwb_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_amrwb_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_amrwb_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_AMRWB);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_AMRWB);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_amrwb_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_amrwb_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrwb_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_amrwb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwb",
+ .fops = &audio_amrwb_fops,
+};
+
+static int __init audio_amrwb_init(void)
+{
+ int ret = misc_register(&audio_amrwb_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_amrwb_misc.this_device, true);
+ audio_amrwb_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_amrwb_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_amrwb_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
new file mode 100644
index 000000000000..ee5991177687
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
@@ -0,0 +1,395 @@
+/* amr-wbplus audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/msm_audio_amrwbplus.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_amrwbplus_misc;
+static struct ws_mgr audio_amrwbplus_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwbplus_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+ if (audio != NULL) {
+ char name[sizeof("msm_amrwbplus_") + 5];
+ snprintf(name, sizeof(name), "msm_amrwbplus_%04x",
+ audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_amrwbplus_debug_fops);
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+ }
+}
+#else
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+}
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct asm_amrwbplus_cfg q6_amrwbplus_cfg;
+ struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config;
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_err("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ amrwbplus_drv_config =
+ (struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg;
+
+ q6_amrwbplus_cfg.size_bytes =
+ amrwbplus_drv_config->size_bytes;
+ q6_amrwbplus_cfg.version =
+ amrwbplus_drv_config->version;
+ q6_amrwbplus_cfg.num_channels =
+ amrwbplus_drv_config->num_channels;
+ q6_amrwbplus_cfg.amr_band_mode =
+ amrwbplus_drv_config->amr_band_mode;
+ q6_amrwbplus_cfg.amr_dtx_mode =
+ amrwbplus_drv_config->amr_dtx_mode;
+ q6_amrwbplus_cfg.amr_frame_fmt =
+ amrwbplus_drv_config->amr_frame_fmt;
+ q6_amrwbplus_cfg.amr_lsf_idx =
+ amrwbplus_drv_config->amr_lsf_idx;
+
+ rc = q6asm_media_format_block_amrwbplus(audio->ac,
+ &q6_amrwbplus_cfg);
+ if (rc < 0) {
+ pr_err("q6asm_media_format_block_amrwb+ failed...\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_AMRWBPLUS_CONFIG_V2: {
+ if ((audio) && (arg) && (audio->codec_cfg)) {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_amrwbplus_config_v2))) {
+ rc = -EFAULT;
+ pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2 failed\n",
+ __func__);
+ break;
+ }
+ } else {
+ pr_err("%s: wb+ config v2 invalid parameters\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRWBPLUS_CONFIG_V2: {
+ if ((audio) && (arg) && (audio->codec_cfg)) {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_amrwbplus_config_v2))) {
+ rc = -EFAULT;
+ pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2 failed\n",
+ __func__);
+ break;
+ }
+ } else {
+ pr_err("%s: wb+ config invalid parameters\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ break;
+ }
+ }
+ return rc;
+}
+#ifdef CONFIG_COMPAT
+struct msm_audio_amrwbplus_config_v2_32 {
+ u32 size_bytes;
+ u32 version;
+ u32 num_channels;
+ u32 amr_band_mode;
+ u32 amr_dtx_mode;
+ u32 amr_frame_fmt;
+ u32 amr_lsf_idx;
+};
+
+enum {
+ AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+2),
+ struct msm_audio_amrwbplus_config_v2_32),
+ AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+3),
+ struct msm_audio_amrwbplus_config_v2_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_AMRWBPLUS_CONFIG_V2_32: {
+ if (audio && arg && (audio->codec_cfg)) {
+ struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
+ struct msm_audio_amrwbplus_config_v2_32
+ amrwbplus_config_32;
+ amrwbplus_config =
+ (struct msm_audio_amrwbplus_config_v2 *)
+ audio->codec_cfg;
+ amrwbplus_config_32.size_bytes =
+ amrwbplus_config->size_bytes;
+ amrwbplus_config_32.version =
+ amrwbplus_config->version;
+ amrwbplus_config_32.num_channels =
+ amrwbplus_config->num_channels;
+ amrwbplus_config_32.amr_band_mode =
+ amrwbplus_config->amr_band_mode;
+ amrwbplus_config_32.amr_dtx_mode =
+ amrwbplus_config->amr_dtx_mode;
+ amrwbplus_config_32.amr_frame_fmt =
+ amrwbplus_config->amr_frame_fmt;
+ amrwbplus_config_32.amr_lsf_idx =
+ amrwbplus_config->amr_lsf_idx;
+
+ if (copy_to_user((void *)arg, &amrwbplus_config_32,
+ sizeof(amrwbplus_config_32))) {
+ rc = -EFAULT;
+ pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 failed\n"
+ , __func__);
+ }
+ } else {
+ pr_err("%s: wb+ Get config v2 invalid parameters\n"
+ , __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRWBPLUS_CONFIG_V2_32: {
+ if ((audio) && (arg) && (audio->codec_cfg)) {
+ struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
+ struct msm_audio_amrwbplus_config_v2_32
+ amrwbplus_config_32;
+
+ if (copy_from_user(&amrwbplus_config_32, (void *)arg,
+ sizeof(struct msm_audio_amrwbplus_config_v2_32))) {
+ rc = -EFAULT;
+ pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 failed\n"
+ , __func__);
+ break;
+ }
+ amrwbplus_config =
+ (struct msm_audio_amrwbplus_config_v2 *)
+ audio->codec_cfg;
+ amrwbplus_config->size_bytes =
+ amrwbplus_config_32.size_bytes;
+ amrwbplus_config->version =
+ amrwbplus_config_32.version;
+ amrwbplus_config->num_channels =
+ amrwbplus_config_32.num_channels;
+ amrwbplus_config->amr_band_mode =
+ amrwbplus_config_32.amr_band_mode;
+ amrwbplus_config->amr_dtx_mode =
+ amrwbplus_config_32.amr_dtx_mode;
+ amrwbplus_config->amr_frame_fmt =
+ amrwbplus_config_32.amr_frame_fmt;
+ amrwbplus_config->amr_lsf_idx =
+ amrwbplus_config_32.amr_lsf_idx;
+ } else {
+ pr_err("%s: wb+ config invalid parameters\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ break;
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("kzalloc failed for amrwb+ decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg =
+ kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:failed kzalloc for amrwb+ config structure",
+ __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_amrwbplus_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_amrwbplus_ws_mgr;
+
+ audio->ac =
+ q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_AMR_WB_PLUS);
+ if (rc < 0) {
+ pr_err("amrwbplus NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS);
+ if (rc < 0) {
+ pr_err("wb+ T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("audio_amrwbplus Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+ config_debug_fs(audio);
+ pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrwbplus_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_amrwbplus_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwbplus",
+ .fops = &audio_amrwbplus_fops,
+};
+
+static int __init audio_amrwbplus_init(void)
+{
+ int ret = misc_register(&audio_amrwbplus_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_amrwbplus_misc.this_device, true);
+ audio_amrwbplus_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_amrwbplus_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_amrwbplus_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_ape.c b/drivers/misc/qcom/qdsp6v2/audio_ape.c
new file mode 100644
index 000000000000..b4c2ddb947de
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_ape.c
@@ -0,0 +1,359 @@
+/* Copyright (c) 2015, 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/types.h>
+#include <linux/msm_audio_ape.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_ape_misc;
+static struct ws_mgr audio_ape_ws_mgr;
+
+static const struct file_operations audio_ape_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+static struct dentry *config_debugfs_create_file(const char *name, void *data)
+{
+ return debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)data, &audio_ape_debug_fops);
+}
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_ape_cfg ape_cfg;
+ struct msm_audio_ape_config *ape_config;
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
+ ape_cfg.compatible_version = ape_config->compatibleVersion;
+ ape_cfg.compression_level = ape_config->compressionLevel;
+ ape_cfg.format_flags = ape_config->formatFlags;
+ ape_cfg.blocks_per_frame = ape_config->blocksPerFrame;
+ ape_cfg.final_frame_blocks = ape_config->finalFrameBlocks;
+ ape_cfg.total_frames = ape_config->totalFrames;
+ ape_cfg.bits_per_sample = ape_config->bitsPerSample;
+ ape_cfg.num_channels = ape_config->numChannels;
+ ape_cfg.sample_rate = ape_config->sampleRate;
+ ape_cfg.seek_table_present = ape_config->seekTablePresent;
+ pr_debug("%s: compatibleVersion %d compressionLevel %d formatFlags %d blocksPerFrame %d finalFrameBlocks %d totalFrames %d bitsPerSample %d numChannels %d sampleRate %d seekTablePresent %d\n",
+ __func__, ape_config->compatibleVersion,
+ ape_config->compressionLevel,
+ ape_config->formatFlags,
+ ape_config->blocksPerFrame,
+ ape_config->finalFrameBlocks,
+ ape_config->totalFrames,
+ ape_config->bitsPerSample,
+ ape_config->numChannels,
+ ape_config->sampleRate,
+ ape_config->seekTablePresent);
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_ape(audio->ac, &ape_cfg,
+ audio->ac->stream_id);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_APE_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_ape_config))) {
+ pr_err("%s:copy_to_user for AUDIO_GET_APE_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_APE_CONFIG: {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_ape_config))) {
+ pr_err("%s:copy_from_user for AUDIO_SET_APE_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_ape_config_32 {
+ u16 compatibleVersion;
+ u16 compressionLevel;
+ u32 formatFlags;
+ u32 blocksPerFrame;
+ u32 finalFrameBlocks;
+ u32 totalFrames;
+ u16 bitsPerSample;
+ u16 numChannels;
+ u32 sampleRate;
+ u32 seekTablePresent;
+
+};
+
+enum {
+ AUDIO_GET_APE_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config_32),
+ AUDIO_SET_APE_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_APE_CONFIG_32: {
+ struct msm_audio_ape_config *ape_config;
+ struct msm_audio_ape_config_32 ape_config_32;
+
+ ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
+ ape_config_32.compatibleVersion = ape_config->compatibleVersion;
+ ape_config_32.compressionLevel =
+ ape_config->compressionLevel;
+ ape_config_32.formatFlags = ape_config->formatFlags;
+ ape_config_32.blocksPerFrame = ape_config->blocksPerFrame;
+ ape_config_32.finalFrameBlocks = ape_config->finalFrameBlocks;
+ ape_config_32.totalFrames = ape_config->totalFrames;
+ ape_config_32.bitsPerSample = ape_config->bitsPerSample;
+ ape_config_32.numChannels = ape_config->numChannels;
+ ape_config_32.sampleRate = ape_config->sampleRate;
+ ape_config_32.seekTablePresent = ape_config->seekTablePresent;
+
+ if (copy_to_user((void *)arg, &ape_config_32,
+ sizeof(ape_config_32))) {
+ pr_err("%s: copy_to_user for GET_APE_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_APE_CONFIG_32: {
+ struct msm_audio_ape_config *ape_config;
+ struct msm_audio_ape_config_32 ape_config_32;
+
+ if (copy_from_user(&ape_config_32, (void *)arg,
+ sizeof(ape_config_32))) {
+ pr_err("%s: copy_from_user for SET_APE_CONFIG_32 failed\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
+ ape_config->compatibleVersion = ape_config_32.compatibleVersion;
+ ape_config->compressionLevel =
+ ape_config_32.compressionLevel;
+ ape_config->formatFlags = ape_config_32.formatFlags;
+ ape_config->blocksPerFrame = ape_config_32.blocksPerFrame;
+ ape_config->finalFrameBlocks = ape_config_32.finalFrameBlocks;
+ ape_config->totalFrames = ape_config_32.totalFrames;
+ ape_config->bitsPerSample = ape_config_32.bitsPerSample;
+ ape_config->numChannels = ape_config_32.numChannels;
+ ape_config->sampleRate = ape_config_32.sampleRate;
+ ape_config->seekTablePresent = ape_config_32.seekTablePresent;
+
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_ape_" + 5];
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (!audio) {
+ pr_err("Could not allocate memory for ape decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_ape_config),
+ GFP_KERNEL);
+ if (!audio->codec_cfg) {
+ pr_err("%s:Could not allocate memory for ape config\n",
+ __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_ape_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_ape_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_APE);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open APE decoder, expected frames is always 1*/
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_APE);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+ snprintf(name, sizeof(name), "msm_ape_%04x", audio->ac->session);
+ audio->dentry = config_debugfs_create_file(name, (void *)audio);
+
+ if (IS_ERR_OR_NULL(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+ pr_debug("%s:apedec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_ape_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_ape_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_ape",
+ .fops = &audio_ape_fops,
+};
+
+static int __init audio_ape_init(void)
+{
+ int ret = misc_register(&audio_ape_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_ape_misc.this_device, true);
+ audio_ape_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_ape_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_ape_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
new file mode 100644
index 000000000000..08ca94e62059
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
@@ -0,0 +1,186 @@
+/* evrc audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_evrc_misc;
+static struct ws_mgr audio_evrc_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_evrc_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_evrc_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_evrc_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_evrc_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_EVRC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_EVRC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_evrc_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_evrc_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_evrc_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_evrc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc",
+ .fops = &audio_evrc_fops,
+};
+
+static int __init audio_evrc_init(void)
+{
+ int ret = misc_register(&audio_evrc_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_evrc_misc.this_device, true);
+ audio_evrc_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_evrc_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_evrc_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
new file mode 100644
index 000000000000..c100c47d7641
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_audio.h>
+#include <linux/compat.h>
+#include "q6audio_common.h"
+#include "audio_utils_aio.h"
+#include <sound/msm-audio-effects-q6-v2.h>
+#include <sound/msm-dts-eagle.h>
+
+#define MAX_CHANNELS_SUPPORTED 8
+#define MAX_PP_PARAMS_SZ 128
+#define WAIT_TIMEDOUT_DURATION_SECS 1
+
+struct q6audio_effects {
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ struct audio_client *ac;
+ struct msm_hwacc_effects_config config;
+
+ atomic_t in_count;
+ atomic_t out_count;
+
+ int opened;
+ int started;
+ int buf_alloc;
+ struct msm_nt_eff_all_config audio_effects;
+};
+
+static void audio_effects_init_pp(struct audio_client *ac)
+{
+ int ret = 0;
+ struct asm_softvolume_params softvol = {
+ .period = SOFT_VOLUME_PERIOD,
+ .step = SOFT_VOLUME_STEP,
+ .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+ };
+
+ if (!ac) {
+ pr_err("%s: audio client null to init pp\n", __func__);
+ return;
+ }
+ switch (ac->topology) {
+ case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+
+ ret = q6asm_set_softvolume_v2(ac, &softvol,
+ SOFT_VOLUME_INSTANCE_1);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume1 Param failed ret=%d\n",
+ __func__, ret);
+ ret = q6asm_set_softvolume_v2(ac, &softvol,
+ SOFT_VOLUME_INSTANCE_2);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume2 Param failed ret=%d\n",
+ __func__, ret);
+
+ msm_dts_eagle_init_master_module(ac);
+
+ break;
+ default:
+ ret = q6asm_set_softvolume_v2(ac, &softvol,
+ SOFT_VOLUME_INSTANCE_1);
+ if (ret < 0)
+ pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+ __func__, ret);
+ break;
+ }
+}
+
+static void audio_effects_deinit_pp(struct audio_client *ac)
+{
+ if (!ac) {
+ pr_err("%s: audio client null to deinit pp\n", __func__);
+ return;
+ }
+ switch (ac->topology) {
+ case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+ msm_dts_eagle_deinit_master_module(ac);
+ break;
+ default:
+ break;
+ }
+}
+
+static void audio_effects_event_handler(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_effects *effects;
+
+ if (!payload || !priv) {
+ pr_err("%s: invalid data to handle events, payload: %p, priv: %p\n",
+ __func__, payload, priv);
+ return;
+ }
+
+ effects = (struct q6audio_effects *)priv;
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2: {
+ atomic_inc(&effects->out_count);
+ wake_up(&effects->write_wait);
+ break;
+ }
+ case ASM_DATA_EVENT_READ_DONE_V2: {
+ atomic_inc(&effects->in_count);
+ wake_up(&effects->read_wait);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ pr_debug("%s: APR_BASIC_RSP_RESULT Cmd[0x%x] Status[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN_V2:
+ pr_debug("ASM_SESSION_CMD_RUN_V2\n");
+ break;
+ default:
+ pr_debug("%s: Payload = [0x%x] stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("%s: Unhandled Event 0x%x token = 0x%x\n",
+ __func__, opcode, token);
+ break;
+ }
+}
+
+static int audio_effects_shared_ioctl(struct file *file, unsigned cmd,
+ unsigned long arg)
+{
+ struct q6audio_effects *effects = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s: AUDIO_START\n", __func__);
+
+ rc = q6asm_open_read_write_v2(effects->ac,
+ FORMAT_LINEAR_PCM,
+ FORMAT_MULTI_CHANNEL_LINEAR_PCM,
+ effects->config.meta_mode_enabled,
+ effects->config.output.bits_per_sample,
+ true /*overwrite topology*/,
+ ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER);
+ if (rc < 0) {
+ pr_err("%s: Open failed for hw accelerated effects:rc=%d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto ioctl_fail;
+ }
+ effects->opened = 1;
+
+ pr_debug("%s: dec buf size: %d, num_buf: %d, enc buf size: %d, num_buf: %d\n",
+ __func__, effects->config.output.buf_size,
+ effects->config.output.buf_size,
+ effects->config.input.buf_size,
+ effects->config.input.num_buf);
+ rc = q6asm_audio_client_buf_alloc_contiguous(IN, effects->ac,
+ effects->config.output.buf_size,
+ effects->config.output.num_buf);
+ if (rc < 0) {
+ pr_err("%s: Write buffer Allocation failed rc = %d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto ioctl_fail;
+ }
+ atomic_set(&effects->in_count, effects->config.input.num_buf);
+ rc = q6asm_audio_client_buf_alloc_contiguous(OUT, effects->ac,
+ effects->config.input.buf_size,
+ effects->config.input.num_buf);
+ if (rc < 0) {
+ pr_err("%s: Read buffer Allocation failed rc = %d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto readbuf_fail;
+ }
+ atomic_set(&effects->out_count, effects->config.output.num_buf);
+ effects->buf_alloc = 1;
+
+ pr_debug("%s: enc: sample_rate: %d, num_channels: %d\n",
+ __func__, effects->config.input.sample_rate,
+ effects->config.input.num_channels);
+ rc = q6asm_enc_cfg_blk_pcm(effects->ac,
+ effects->config.input.sample_rate,
+ effects->config.input.num_channels);
+ if (rc < 0) {
+ pr_err("%s: pcm read block config failed\n", __func__);
+ rc = -EINVAL;
+ goto cfg_fail;
+ }
+ pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n",
+ __func__, effects->config.output.sample_rate,
+ effects->config.output.num_channels,
+ effects->config.output.bits_per_sample);
+ rc = q6asm_media_format_block_pcm_format_support(
+ effects->ac, effects->config.output.sample_rate,
+ effects->config.output.num_channels,
+ effects->config.output.bits_per_sample);
+ if (rc < 0) {
+ pr_err("%s: pcm write format block config failed\n",
+ __func__);
+ rc = -EINVAL;
+ goto cfg_fail;
+ }
+
+ audio_effects_init_pp(effects->ac);
+
+ rc = q6asm_run(effects->ac, 0x00, 0x00, 0x00);
+ if (!rc)
+ effects->started = 1;
+ else {
+ effects->started = 0;
+ pr_err("%s: ASM run state failed\n", __func__);
+ }
+ break;
+ }
+ case AUDIO_EFFECTS_WRITE: {
+ char *bufptr = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ if (!effects->started) {
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+
+ rc = wait_event_timeout(effects->write_wait,
+ atomic_read(&effects->out_count),
+ WAIT_TIMEDOUT_DURATION_SECS * HZ);
+ if (!rc) {
+ pr_err("%s: write wait_event_timeout\n", __func__);
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+ if (!atomic_read(&effects->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+
+ bufptr = q6asm_is_cpu_buf_avail(IN, effects->ac, &size, &idx);
+ if (bufptr) {
+ if (copy_from_user(bufptr, (void *)arg,
+ effects->config.buf_cfg.output_len)) {
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+ rc = q6asm_write(effects->ac,
+ effects->config.buf_cfg.output_len,
+ 0, 0, NO_TIMESTAMP);
+ if (rc < 0) {
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+ atomic_dec(&effects->out_count);
+ } else {
+ pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n",
+ __func__);
+ }
+ break;
+ }
+ case AUDIO_EFFECTS_READ: {
+ char *bufptr = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ if (!effects->started) {
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+
+ atomic_set(&effects->in_count, 0);
+
+ q6asm_read_v2(effects->ac, effects->config.buf_cfg.input_len);
+ /* Read might fail initially, don't error out */
+ if (rc < 0)
+ pr_err("%s: read failed\n", __func__);
+
+ rc = wait_event_timeout(effects->read_wait,
+ atomic_read(&effects->in_count),
+ WAIT_TIMEDOUT_DURATION_SECS * HZ);
+ if (!rc) {
+ pr_err("%s: read wait_event_timeout\n", __func__);
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+ if (!atomic_read(&effects->in_count)) {
+ pr_err("%s: pcm stopped in_count 0\n", __func__);
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+
+ bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx);
+ if (bufptr) {
+ if (!((void *)arg)) {
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+ if (copy_to_user((void *)arg, bufptr,
+ effects->config.buf_cfg.input_len)) {
+ rc = -EFAULT;
+ goto ioctl_fail;
+ }
+ }
+ break;
+ }
+ default:
+ pr_err("%s: Invalid effects config module\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ioctl_fail:
+ return rc;
+readbuf_fail:
+ q6asm_audio_client_buf_free_contiguous(IN,
+ effects->ac);
+ return rc;
+cfg_fail:
+ q6asm_audio_client_buf_free_contiguous(IN,
+ effects->ac);
+ q6asm_audio_client_buf_free_contiguous(OUT,
+ effects->ac);
+ effects->buf_alloc = 0;
+ return rc;
+}
+
+static long audio_effects_set_pp_param(struct q6audio_effects *effects,
+ long *values)
+{
+ int rc = 0;
+ int effects_module = values[0];
+ switch (effects_module) {
+ case VIRTUALIZER_MODULE:
+ pr_debug("%s: VIRTUALIZER_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology))
+ msm_audio_effects_virtualizer_handler(
+ effects->ac,
+ &(effects->audio_effects.virtualizer),
+ (long *)&values[1]);
+ break;
+ case REVERB_MODULE:
+ pr_debug("%s: REVERB_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology))
+ msm_audio_effects_reverb_handler(effects->ac,
+ &(effects->audio_effects.reverb),
+ (long *)&values[1]);
+ break;
+ case BASS_BOOST_MODULE:
+ pr_debug("%s: BASS_BOOST_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology))
+ msm_audio_effects_bass_boost_handler(
+ effects->ac,
+ &(effects->audio_effects.bass_boost),
+ (long *)&values[1]);
+ break;
+ case PBE_MODULE:
+ pr_debug("%s: PBE_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology))
+ msm_audio_effects_pbe_handler(
+ effects->ac,
+ &(effects->audio_effects.pbe),
+ (long *)&values[1]);
+ break;
+ case EQ_MODULE:
+ pr_debug("%s: EQ_MODULE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology))
+ msm_audio_effects_popless_eq_handler(
+ effects->ac,
+ &(effects->audio_effects.equalizer),
+ (long *)&values[1]);
+ break;
+ case SOFT_VOLUME_MODULE:
+ pr_debug("%s: SA PLUS VOLUME_MODULE\n", __func__);
+ msm_audio_effects_volume_handler_v2(effects->ac,
+ &(effects->audio_effects.saplus_vol),
+ (long *)&values[1], SOFT_VOLUME_INSTANCE_1);
+ break;
+ case SOFT_VOLUME2_MODULE:
+ pr_debug("%s: TOPOLOGY SWITCH VOLUME MODULE\n",
+ __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology))
+ msm_audio_effects_volume_handler_v2(effects->ac,
+ &(effects->audio_effects.topo_switch_vol),
+ (long *)&values[1], SOFT_VOLUME_INSTANCE_2);
+ break;
+ case DTS_EAGLE_MODULE_ENABLE:
+ pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__);
+ if (msm_audio_effects_is_effmodule_supp_in_top(
+ effects_module, effects->ac->topology)) {
+ /*
+ * HPX->OFF: first disable HPX and then
+ * enable SA+
+ * HPX->ON: first disable SA+ and then
+ * enable HPX
+ */
+ bool hpx_state = (bool)values[1];
+ if (hpx_state)
+ msm_audio_effects_enable_extn(effects->ac,
+ &(effects->audio_effects),
+ false);
+ msm_dts_eagle_enable_asm(effects->ac,
+ hpx_state,
+ AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
+ msm_dts_eagle_enable_asm(effects->ac,
+ hpx_state,
+ AUDPROC_MODULE_ID_DTS_HPX_POSTMIX);
+ if (!hpx_state)
+ msm_audio_effects_enable_extn(effects->ac,
+ &(effects->audio_effects),
+ true);
+ }
+ break;
+ default:
+ pr_err("%s: Invalid effects config module\n", __func__);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static long audio_effects_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_effects *effects = file->private_data;
+ int rc = 0;
+ long argvalues[MAX_PP_PARAMS_SZ] = {0};
+
+ switch (cmd) {
+ case AUDIO_SET_EFFECTS_CONFIG: {
+ pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__);
+ memset(&effects->config, 0, sizeof(effects->config));
+ if (copy_from_user(&effects->config, (void *)arg,
+ sizeof(effects->config))) {
+ pr_err("%s: copy from user for AUDIO_SET_EFFECTS_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
+ __func__, effects->config.output.buf_size,
+ effects->config.output.num_buf,
+ effects->config.output.sample_rate,
+ effects->config.output.num_channels);
+ pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
+ __func__, effects->config.input.buf_size,
+ effects->config.input.num_buf,
+ effects->config.input.sample_rate,
+ effects->config.input.num_channels);
+ break;
+ }
+ case AUDIO_EFFECTS_SET_BUF_LEN: {
+ if (copy_from_user(&effects->config.buf_cfg, (void *)arg,
+ sizeof(effects->config.buf_cfg))) {
+ pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ pr_debug("%s: write buf len: %d, read buf len: %d\n",
+ __func__, effects->config.buf_cfg.output_len,
+ effects->config.buf_cfg.input_len);
+ break;
+ }
+ case AUDIO_EFFECTS_GET_BUF_AVAIL: {
+ struct msm_hwacc_buf_avail buf_avail;
+
+ buf_avail.input_num_avail = atomic_read(&effects->in_count);
+ buf_avail.output_num_avail = atomic_read(&effects->out_count);
+ pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
+ __func__, buf_avail.output_num_avail,
+ buf_avail.input_num_avail);
+ if (copy_to_user((void *)arg, &buf_avail,
+ sizeof(buf_avail))) {
+ pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_EFFECTS_SET_PP_PARAMS: {
+ if (copy_from_user(argvalues, (void *)arg,
+ MAX_PP_PARAMS_SZ*sizeof(long))) {
+ pr_err("%s: copy from user for pp params failed\n",
+ __func__);
+ return -EFAULT;
+ }
+ rc = audio_effects_set_pp_param(effects, argvalues);
+ break;
+ }
+ default:
+ pr_debug("%s: Calling shared ioctl\n", __func__);
+ rc = audio_effects_shared_ioctl(file, cmd, arg);
+ break;
+ }
+ if (rc)
+ pr_err("%s: cmd 0x%x failed\n", __func__, cmd);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_hwacc_data_config32 {
+ __u32 buf_size;
+ __u32 num_buf;
+ __u32 num_channels;
+ __u8 channel_map[MAX_CHANNELS_SUPPORTED];
+ __u32 sample_rate;
+ __u32 bits_per_sample;
+};
+
+struct msm_hwacc_buf_cfg32 {
+ __u32 input_len;
+ __u32 output_len;
+};
+
+struct msm_hwacc_buf_avail32 {
+ __u32 input_num_avail;
+ __u32 output_num_avail;
+};
+
+struct msm_hwacc_effects_config32 {
+ struct msm_hwacc_data_config32 input;
+ struct msm_hwacc_data_config32 output;
+ struct msm_hwacc_buf_cfg32 buf_cfg;
+ __u32 meta_mode_enabled;
+ __u32 overwrite_topology;
+ __s32 topology;
+};
+
+enum {
+ AUDIO_SET_EFFECTS_CONFIG32 = _IOW(AUDIO_IOCTL_MAGIC, 99,
+ struct msm_hwacc_effects_config32),
+ AUDIO_EFFECTS_SET_BUF_LEN32 = _IOW(AUDIO_IOCTL_MAGIC, 100,
+ struct msm_hwacc_buf_cfg32),
+ AUDIO_EFFECTS_GET_BUF_AVAIL32 = _IOW(AUDIO_IOCTL_MAGIC, 101,
+ struct msm_hwacc_buf_avail32),
+ AUDIO_EFFECTS_WRITE32 = _IOW(AUDIO_IOCTL_MAGIC, 102, compat_uptr_t),
+ AUDIO_EFFECTS_READ32 = _IOWR(AUDIO_IOCTL_MAGIC, 103, compat_uptr_t),
+ AUDIO_EFFECTS_SET_PP_PARAMS32 = _IOW(AUDIO_IOCTL_MAGIC, 104,
+ compat_uptr_t),
+ AUDIO_START32 = _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned),
+};
+
+static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_effects *effects = file->private_data;
+ int rc = 0, i;
+
+ switch (cmd) {
+ case AUDIO_SET_EFFECTS_CONFIG32: {
+ struct msm_hwacc_effects_config32 config32;
+ struct msm_hwacc_effects_config *config = &effects->config;
+ memset(&effects->config, 0, sizeof(effects->config));
+ if (copy_from_user(&config32, (void *)arg,
+ sizeof(config32))) {
+ pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ config->input.buf_size = config32.input.buf_size;
+ config->input.num_buf = config32.input.num_buf;
+ config->input.num_channels = config32.input.num_channels;
+ config->input.sample_rate = config32.input.sample_rate;
+ config->input.bits_per_sample = config32.input.bits_per_sample;
+ config->input.buf_size = config32.input.buf_size;
+ for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
+ config->input.channel_map[i] =
+ config32.input.channel_map[i];
+ config->output.buf_size = config32.output.buf_size;
+ config->output.num_buf = config32.output.num_buf;
+ config->output.num_channels = config32.output.num_channels;
+ config->output.sample_rate = config32.output.sample_rate;
+ config->output.bits_per_sample =
+ config32.output.bits_per_sample;
+ config->output.buf_size = config32.output.buf_size;
+ for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
+ config->output.channel_map[i] =
+ config32.output.channel_map[i];
+ config->buf_cfg.input_len = config32.buf_cfg.input_len;
+ config->buf_cfg.output_len = config32.buf_cfg.output_len;
+ config->meta_mode_enabled = config32.meta_mode_enabled;
+ config->overwrite_topology = config32.overwrite_topology;
+ config->topology = config32.topology;
+ pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
+ __func__, effects->config.output.buf_size,
+ effects->config.output.num_buf,
+ effects->config.output.sample_rate,
+ effects->config.output.num_channels);
+ pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
+ __func__, effects->config.input.buf_size,
+ effects->config.input.num_buf,
+ effects->config.input.sample_rate,
+ effects->config.input.num_channels);
+ break;
+ }
+ case AUDIO_EFFECTS_SET_BUF_LEN32: {
+ struct msm_hwacc_buf_cfg32 buf_cfg32;
+ struct msm_hwacc_effects_config *config = &effects->config;
+ if (copy_from_user(&buf_cfg32, (void *)arg,
+ sizeof(buf_cfg32))) {
+ pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ config->buf_cfg.input_len = buf_cfg32.input_len;
+ config->buf_cfg.output_len = buf_cfg32.output_len;
+ pr_debug("%s: write buf len: %d, read buf len: %d\n",
+ __func__, effects->config.buf_cfg.output_len,
+ effects->config.buf_cfg.input_len);
+ break;
+ }
+ case AUDIO_EFFECTS_GET_BUF_AVAIL32: {
+ struct msm_hwacc_buf_avail32 buf_avail;
+
+ buf_avail.input_num_avail = atomic_read(&effects->in_count);
+ buf_avail.output_num_avail = atomic_read(&effects->out_count);
+ pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
+ __func__, buf_avail.output_num_avail,
+ buf_avail.input_num_avail);
+ if (copy_to_user((void *)arg, &buf_avail,
+ sizeof(buf_avail))) {
+ pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_EFFECTS_SET_PP_PARAMS32: {
+ long argvalues[MAX_PP_PARAMS_SZ] = {0};
+ int argvalues32[MAX_PP_PARAMS_SZ] = {0};
+
+ if (copy_from_user(argvalues32, (void *)arg,
+ MAX_PP_PARAMS_SZ*sizeof(int))) {
+ pr_err("%s: copy from user failed for pp params\n",
+ __func__);
+ return -EFAULT;
+ }
+ for (i = 0; i < MAX_PP_PARAMS_SZ; i++)
+ argvalues[i] = argvalues32[i];
+
+ rc = audio_effects_set_pp_param(effects, argvalues);
+ break;
+ }
+ case AUDIO_START32: {
+ rc = audio_effects_shared_ioctl(file, AUDIO_START, arg);
+ break;
+ }
+ case AUDIO_EFFECTS_WRITE32: {
+ rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_WRITE, arg);
+ break;
+ }
+ case AUDIO_EFFECTS_READ32: {
+ rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_READ, arg);
+ break;
+ }
+ default:
+ pr_debug("%s: unhandled ioctl\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+#endif
+
+static int audio_effects_release(struct inode *inode, struct file *file)
+{
+ struct q6audio_effects *effects = file->private_data;
+ int rc = 0;
+ if (!effects) {
+ pr_err("%s: effect is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (effects->opened) {
+ rc = wait_event_timeout(effects->write_wait,
+ atomic_read(&effects->out_count),
+ WAIT_TIMEDOUT_DURATION_SECS * HZ);
+ if (!rc)
+ pr_err("%s: write wait_event_timeout failed\n",
+ __func__);
+ rc = wait_event_timeout(effects->read_wait,
+ atomic_read(&effects->in_count),
+ WAIT_TIMEDOUT_DURATION_SECS * HZ);
+ if (!rc)
+ pr_err("%s: read wait_event_timeout failed\n",
+ __func__);
+ rc = q6asm_cmd(effects->ac, CMD_CLOSE);
+ if (rc < 0)
+ pr_err("%s[%p]:Failed to close the session rc=%d\n",
+ __func__, effects, rc);
+ effects->opened = 0;
+ effects->started = 0;
+
+ audio_effects_deinit_pp(effects->ac);
+ }
+
+ if (effects->buf_alloc) {
+ q6asm_audio_client_buf_free_contiguous(IN, effects->ac);
+ q6asm_audio_client_buf_free_contiguous(OUT, effects->ac);
+ }
+ q6asm_audio_client_free(effects->ac);
+
+ kfree(effects);
+
+ pr_debug("%s: close session success\n", __func__);
+ return rc;
+}
+
+static int audio_effects_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_effects *effects;
+ int rc = 0;
+
+ effects = kzalloc(sizeof(struct q6audio_effects), GFP_KERNEL);
+ if (!effects) {
+ pr_err("%s: Could not allocate memory for hw acc effects driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ effects->ac = q6asm_audio_client_alloc(
+ (app_cb)audio_effects_event_handler,
+ (void *)effects);
+ if (!effects->ac) {
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
+ kfree(effects);
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&effects->read_wait);
+ init_waitqueue_head(&effects->write_wait);
+
+ effects->opened = 0;
+ effects->started = 0;
+ effects->buf_alloc = 0;
+ file->private_data = effects;
+ pr_debug("%s: open session success\n", __func__);
+ return rc;
+}
+
+static const struct file_operations audio_effects_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_effects_open,
+ .release = audio_effects_release,
+ .unlocked_ioctl = audio_effects_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = audio_effects_compat_ioctl,
+#endif
+};
+
+struct miscdevice audio_effects_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_hweffects",
+ .fops = &audio_effects_fops,
+};
+
+static int __init audio_effects_init(void)
+{
+ return misc_register(&audio_effects_misc);
+}
+
+device_initcall(audio_effects_init);
+MODULE_DESCRIPTION("Audio hardware accelerated effects driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
new file mode 100644
index 000000000000..83e300721c8e
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
@@ -0,0 +1,188 @@
+/* mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_mp3_misc;
+static struct ws_mgr audio_mp3_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_mp3_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ rc = enable_volume_ramp(audio);
+ if (rc < 0) {
+ pr_err("%s: Failed to enable volume ramp\n",
+ __func__);
+ }
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_mp3_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for mp3 decode driver\n");
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_mp3_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_mp3_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_MP3);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open MP3 decoder, expected frames is always 1
+ audio->buf_cfg.frames_per_buf = 0x01;*/
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_MP3);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_mp3_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_mp3_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &audio_mp3_fops,
+};
+
+static int __init audio_mp3_init(void)
+{
+ int ret = misc_register(&audio_mp3_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_mp3_misc.this_device, true);
+ audio_mp3_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_mp3_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_mp3_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
new file mode 100644
index 000000000000..a15fd87c7be8
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
@@ -0,0 +1,519 @@
+/* aac audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_audio_aac.h>
+#include <linux/compat.h>
+#include <soc/qcom/socinfo.h>
+#include "audio_utils_aio.h"
+
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
+
+
+/* Default number of pre-allocated event packets */
+#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out))
+static struct miscdevice audio_multiaac_misc;
+static struct ws_mgr audio_multiaac_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_aac_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_aac_cfg aac_cfg;
+ struct msm_audio_aac_config *aac_config;
+ uint32_t sbr_ps = 0x00;
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ if (audio->feedback == TUNNEL_MODE) {
+ aac_cfg.sample_rate = aac_config->sample_rate;
+ aac_cfg.ch_cfg = aac_config->channel_configuration;
+ } else {
+ aac_cfg.sample_rate = audio->pcm_cfg.sample_rate;
+ aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+ }
+ pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+ audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm_native(audio->ac,
+ aac_cfg.sample_rate,
+ aac_cfg.ch_cfg);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ /* turn on both sbr and ps */
+ rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
+ if (rc < 0)
+ pr_err("sbr-ps enable failed\n");
+ if (aac_config->sbr_ps_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ else if (aac_config->sbr_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_AAC_P;
+ else
+ aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
+
+ switch (aac_config->format) {
+ case AUDIO_AAC_FORMAT_ADTS:
+ aac_cfg.format = 0x00;
+ break;
+ case AUDIO_AAC_FORMAT_LOAS:
+ aac_cfg.format = 0x01;
+ break;
+ case AUDIO_AAC_FORMAT_ADIF:
+ aac_cfg.format = 0x02;
+ break;
+ default:
+ case AUDIO_AAC_FORMAT_RAW:
+ aac_cfg.format = 0x03;
+ }
+ aac_cfg.ep_config = aac_config->ep_config;
+ aac_cfg.section_data_resilience =
+ aac_config->aac_section_data_resilience_flag;
+ aac_cfg.scalefactor_data_resilience =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_cfg.spectral_data_resilience =
+ aac_config->aac_spectral_data_resilience_flag;
+
+ pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n",
+ __func__, aac_cfg.format,
+ aac_cfg.aot, aac_cfg.ch_cfg,
+ aac_cfg.sample_rate);
+
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = q6asm_set_encdec_chan_map(audio->ac, 2);
+ if (rc < 0) {
+ pr_err("%s: cmd set encdec_chan_map failed\n",
+ __func__);
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config *aac_config;
+ uint16_t sce_left = 1, sce_right = 2;
+
+ if (arg == NULL) {
+ pr_err("%s: NULL config pointer\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ memcpy(audio->codec_cfg, arg,
+ sizeof(struct msm_audio_aac_config));
+ aac_config = audio->codec_cfg;
+ if (aac_config->dual_mono_mode >
+ AUDIO_AAC_DUAL_MONO_PL_SR) {
+ pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n",
+ __func__, aac_config->dual_mono_mode);
+ } else {
+ /* convert the data from user into sce_left
+ * and sce_right based on the definitions
+ */
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n",
+ __func__, aac_config->dual_mono_mode);
+ switch (aac_config->dual_mono_mode) {
+ case AUDIO_AAC_DUAL_MONO_PL_PR:
+ sce_left = 1;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_SR:
+ sce_left = 2;
+ sce_right = 2;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_PR:
+ sce_left = 2;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_PL_SR:
+ default:
+ sce_left = 1;
+ sce_right = 2;
+ break;
+ }
+ rc = q6asm_cfg_dual_mono_aac(audio->ac,
+ sce_left, sce_right);
+ if (rc < 0)
+ pr_err("%s: asm cmd dualmono failed rc=%d\n",
+ __func__, rc);
+ } break;
+ break;
+ }
+ case AUDIO_SET_AAC_MIX_CONFIG: {
+ u32 *mix_coeff = (u32 *)arg;
+ if (!arg) {
+ pr_err("%s: Invalid param for %s\n",
+ __func__, "AUDIO_SET_AAC_MIX_CONFIG");
+ rc = -EINVAL;
+ break;
+ }
+ pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
+ pr_debug("%s, value of coeff = %d",
+ __func__, *mix_coeff);
+ q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff);
+ if (rc < 0)
+ pr_err("%s asm aac_sel_mix_coef failed rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_aac_config))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config aac_config;
+ if (copy_from_user(&aac_config, (void *)arg,
+ sizeof(aac_config))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n"
+ , __func__);
+ rc = -EFAULT;
+ }
+ rc = audio_ioctl_shared(file, cmd, &aac_config);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ case AUDIO_SET_AAC_MIX_CONFIG: {
+ u32 mix_config;
+ pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
+ if (copy_from_user(&mix_config, (void *)arg,
+ sizeof(u32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = audio_ioctl_shared(file, cmd, &mix_config);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default: {
+ pr_debug("Calling utils ioctl\n");
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_aac_config32 {
+ s16 format;
+ u16 audio_object;
+ u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */
+ u16 aac_section_data_resilience_flag;
+ u16 aac_scalefactor_data_resilience_flag;
+ u16 aac_spectral_data_resilience_flag;
+ u16 sbr_on_flag;
+ u16 sbr_ps_on_flag;
+ u16 dual_mono_mode;
+ u16 channel_configuration;
+ u16 sample_rate;
+};
+
+enum {
+ AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
+ AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32),
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG_32: {
+ struct msm_audio_aac_config *aac_config;
+ struct msm_audio_aac_config32 aac_config_32;
+
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ aac_config_32.format = aac_config->format;
+ aac_config_32.audio_object = aac_config->audio_object;
+ aac_config_32.ep_config = aac_config->ep_config;
+ aac_config_32.aac_section_data_resilience_flag =
+ aac_config->aac_section_data_resilience_flag;
+ aac_config_32.aac_scalefactor_data_resilience_flag =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_config_32.aac_spectral_data_resilience_flag =
+ aac_config->aac_spectral_data_resilience_flag;
+ aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
+ aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
+ aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
+ aac_config_32.channel_configuration =
+ aac_config->channel_configuration;
+ aac_config_32.sample_rate = aac_config->sample_rate;
+
+ if (copy_to_user((void *)arg, &aac_config_32,
+ sizeof(aac_config_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG_32: {
+ struct msm_audio_aac_config aac_config;
+ struct msm_audio_aac_config32 aac_config_32;
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+
+ if (copy_from_user(&aac_config_32, (void *)arg,
+ sizeof(aac_config_32))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ aac_config.format = aac_config_32.format;
+ aac_config.audio_object = aac_config_32.audio_object;
+ aac_config.ep_config = aac_config_32.ep_config;
+ aac_config.aac_section_data_resilience_flag =
+ aac_config_32.aac_section_data_resilience_flag;
+ aac_config.aac_scalefactor_data_resilience_flag =
+ aac_config_32.aac_scalefactor_data_resilience_flag;
+ aac_config.aac_spectral_data_resilience_flag =
+ aac_config_32.aac_spectral_data_resilience_flag;
+ aac_config.sbr_on_flag = aac_config_32.sbr_on_flag;
+ aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag;
+ aac_config.dual_mono_mode = aac_config_32.dual_mono_mode;
+ aac_config.channel_configuration =
+ aac_config_32.channel_configuration;
+ aac_config.sample_rate = aac_config_32.sample_rate;
+
+ cmd = AUDIO_SET_AAC_CONFIG;
+ rc = audio_ioctl_shared(file, cmd, &aac_config);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ case AUDIO_SET_AAC_MIX_CONFIG: {
+ u32 mix_config;
+ pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG\n", __func__);
+ if (copy_from_user(&mix_config, (void *)arg,
+ sizeof(u32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = audio_ioctl_shared(file, cmd, &mix_config);
+ if (rc)
+ pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default: {
+ pr_debug("Calling utils ioctl\n");
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+ struct msm_audio_aac_config *aac_config = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_multi_aac_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s: Could not allocate memory for aac config\n",
+ __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ aac_config = audio->codec_cfg;
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM;
+ audio->miscdevice = &audio_multiaac_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_multiaac_ws_mgr;
+ aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_MPEG4_MULTI_AAC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open AAC decoder, expected frames is always 1
+ audio->buf_cfg.frames_per_buf = 0x01;*/
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_multi_aac_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_aac_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n",
+ __func__, audio->feedback, audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_multiaac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_multi_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_aac_init(void)
+{
+ int ret = misc_register(&audio_multiaac_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_multiaac_misc.this_device, true);
+ audio_multiaac_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_multiaac_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_aac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
new file mode 100644
index 000000000000..653aee9c8eff
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
@@ -0,0 +1,193 @@
+/* qcelp(v13k) audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in))
+
+static struct miscdevice audio_qcelp_misc;
+static struct ws_mgr audio_qcelp_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_qcelp_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_qcelp_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->pcm_cfg.channel_count = 1;
+ audio->miscdevice = &audio_qcelp_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_qcelp_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_V13K);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_V13K);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_qcelp_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_qcelp_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_qcelp_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_qcelp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp",
+ .fops = &audio_qcelp_fops,
+};
+
+static int __init audio_qcelp_init(void)
+{
+ int ret = misc_register(&audio_qcelp_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_qcelp_misc.this_device, true);
+ audio_qcelp_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_qcelp_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_qcelp_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c
new file mode 100644
index 000000000000..cad0220a4960
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c
@@ -0,0 +1,915 @@
+/* Copyright (c) 2010-2015, 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/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+static int audio_in_pause(struct q6audio_in *audio)
+{
+ int rc;
+
+ rc = q6asm_cmd(audio->ac, CMD_PAUSE);
+ if (rc < 0)
+ pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
+ audio->ac->session, rc);
+
+ return rc;
+}
+
+static int audio_in_flush(struct q6audio_in *audio)
+{
+ int rc;
+
+ pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session);
+ /* Flush if session running */
+ if (audio->enabled) {
+ /* Implicitly issue a pause to the encoder before flushing */
+ rc = audio_in_pause(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: pause cmd failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ return rc;
+ }
+
+ rc = q6asm_cmd(audio->ac, CMD_FLUSH);
+ if (rc < 0) {
+ pr_err("%s:session id %d: flush cmd failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ return rc;
+ }
+ /* 2nd arg: 0 -> run immediately
+ 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
+ q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+ pr_debug("Rerun the session\n");
+ }
+ audio->rflush = 1;
+ audio->wflush = 1;
+ memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info));
+ wake_up(&audio->read_wait);
+ /* get read_lock to ensure no more waiting read thread */
+ mutex_lock(&audio->read_lock);
+ audio->rflush = 0;
+ mutex_unlock(&audio->read_lock);
+ wake_up(&audio->write_wait);
+ /* get write_lock to ensure no more waiting write thread */
+ mutex_lock(&audio->write_lock);
+ audio->wflush = 0;
+ mutex_unlock(&audio->write_lock);
+ pr_debug("%s:session id %d: in_bytes %d\n", __func__,
+ audio->ac->session, atomic_read(&audio->in_bytes));
+ pr_debug("%s:session id %d: in_samples %d\n", __func__,
+ audio->ac->session, atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+ atomic_set(&audio->out_count, 0);
+ return 0;
+}
+
+/* must be called with audio->lock held */
+int audio_in_enable(struct q6audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ /* 2nd arg: 0 -> run immediately
+ 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
+ return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+}
+
+/* must be called with audio->lock held */
+int audio_in_disable(struct q6audio_in *audio)
+{
+ int rc = 0;
+ if (!audio->stopped) {
+ audio->enabled = 0;
+ audio->opened = 0;
+ pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n",
+ __func__, audio->ac->session,
+ atomic_read(&audio->in_bytes),
+ atomic_read(&audio->in_samples));
+
+ rc = q6asm_cmd(audio->ac, CMD_CLOSE);
+ if (rc < 0)
+ pr_err("%s:session id %d: Failed to close the session rc=%d\n",
+ __func__, audio->ac->session,
+ rc);
+ audio->stopped = 1;
+ memset(audio->out_frame_info, 0,
+ sizeof(audio->out_frame_info));
+ wake_up(&audio->read_wait);
+ wake_up(&audio->write_wait);
+ }
+ pr_debug("%s:session id %d: enabled[%d]\n", __func__,
+ audio->ac->session, audio->enabled);
+ return rc;
+}
+
+int audio_in_buf_alloc(struct q6audio_in *audio)
+{
+ int rc = 0;
+
+ switch (audio->buf_alloc) {
+ case NO_BUF_ALLOC:
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_audio_client_buf_alloc(IN,
+ audio->ac,
+ ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+ audio->pcm_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__,
+ audio->ac->session);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_IN;
+ }
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ break;
+ case BUF_ALLOC_IN:
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ break;
+ case BUF_ALLOC_OUT:
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
+ ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+ audio->pcm_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__,
+ audio->ac->session);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_IN;
+ }
+ break;
+ default:
+ pr_debug("%s:session id %d: buf[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ }
+
+ return rc;
+}
+
+int audio_in_set_config(struct file *file,
+ struct msm_audio_config *cfg)
+{
+ int rc = 0;
+ struct q6audio_in *audio = file->private_data;
+
+ if (audio->feedback != NON_TUNNEL_MODE) {
+ pr_err("%s:session id %d: Not sufficient permission to change the record mode\n",
+ __func__, audio->ac->session);
+ rc = -EACCES;
+ goto ret;
+ }
+ if ((cfg->buffer_count > PCM_BUF_COUNT) ||
+ (cfg->buffer_count == 1))
+ cfg->buffer_count = PCM_BUF_COUNT;
+
+ audio->pcm_cfg.buffer_count = cfg->buffer_count;
+ audio->pcm_cfg.buffer_size = cfg->buffer_size;
+ audio->pcm_cfg.channel_count = cfg->channel_count;
+ audio->pcm_cfg.sample_rate = cfg->sample_rate;
+ if (audio->opened && audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
+ ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+ audio->pcm_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__, audio->ac->session);
+ rc = -ENOMEM;
+ goto ret;
+ }
+ }
+ audio->buf_alloc |= BUF_ALLOC_IN;
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__,
+ audio->ac->session, audio->pcm_cfg.buffer_count,
+ audio->pcm_cfg.buffer_size);
+ret:
+ return rc;
+}
+/* ------------------- device --------------------- */
+static long audio_in_ioctl_shared(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_FLUSH: {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ rc = audio_in_flush(audio);
+ if (rc < 0)
+ pr_err("%s:session id %d: Flush Fail rc=%d\n",
+ __func__, audio->ac->session, rc);
+ else { /* Register back the flushed read buffer with DSP */
+ int cnt = 0;
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ pr_debug("register the read buffer\n");
+ }
+ break;
+ }
+ case AUDIO_PAUSE: {
+ pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__,
+ audio->ac->session);
+ if (audio->enabled)
+ audio_in_pause(audio);
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->ac->session,
+ sizeof(u16))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+long audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_FLUSH:
+ case AUDIO_PAUSE:
+ case AUDIO_GET_SESSION_ID:
+ rc = audio_in_ioctl_shared(file, cmd, arg);
+ break;
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->str_cfg.buffer_size;
+ cfg.buffer_count = audio->str_cfg.buffer_count;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
+ __func__, audio->ac->session, cfg.buffer_size,
+ cfg.buffer_count);
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Minimum single frame size,
+ but with in maximum frames number */
+ if ((cfg.buffer_size < (audio->min_frame_size+ \
+ sizeof(struct meta_out_dsp))) ||
+ (cfg.buffer_count < FRAME_NUM)) {
+ rc = -EINVAL;
+ break;
+ }
+ audio->str_cfg.buffer_size = cfg.buffer_size;
+ audio->str_cfg.buffer_count = cfg.buffer_count;
+ if (audio->opened) {
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
+ __func__, audio->ac->session,
+ audio->str_cfg.buffer_size,
+ audio->str_cfg.buffer_count);
+ break;
+ }
+ case AUDIO_SET_BUF_CFG: {
+ struct msm_audio_buf_cfg cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((audio->feedback == NON_TUNNEL_MODE) &&
+ !cfg.meta_info_enable) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* Restrict the num of frames per buf to coincide with
+ * default buf size */
+ if (cfg.frames_per_buf > audio->max_frames_per_buf) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+ audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
+ pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
+ audio->ac->session, cfg.meta_info_enable,
+ cfg.frames_per_buf);
+ break;
+ }
+ case AUDIO_GET_BUF_CFG: {
+ pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
+ audio->ac->session, audio->buf_cfg.meta_info_enable,
+ audio->buf_cfg.frames_per_buf);
+
+ if (copy_to_user((void *)arg, &audio->buf_cfg,
+ sizeof(struct msm_audio_buf_cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ if (copy_to_user((void *)arg, &audio->pcm_cfg,
+ sizeof(struct msm_audio_config)))
+ rc = -EFAULT;
+ break;
+
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = audio_in_set_config(file, &cfg);
+ break;
+ }
+ default:
+ /* call codec specific ioctl */
+ rc = audio->enc_ioctl(file, cmd, arg);
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_stats32 {
+ u32 byte_count;
+ u32 sample_count;
+ u32 unused[2];
+};
+
+struct msm_audio_stream_config32 {
+ u32 buffer_size;
+ u32 buffer_count;
+};
+
+struct msm_audio_config32 {
+ u32 buffer_size;
+ u32 buffer_count;
+ u32 channel_count;
+ u32 sample_rate;
+ u32 type;
+ u32 meta_field;
+ u32 bits;
+ u32 unused[3];
+};
+
+struct msm_audio_buf_cfg32 {
+ u32 meta_info_enable;
+ u32 frames_per_buf;
+};
+
+enum {
+ AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
+ struct msm_audio_config32),
+ AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
+ struct msm_audio_config32),
+ AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
+ struct msm_audio_stats32),
+ AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
+ struct msm_audio_stream_config32),
+ AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
+ struct msm_audio_stream_config32),
+ AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
+ struct msm_audio_buf_cfg32),
+ AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
+ struct msm_audio_buf_cfg32),
+};
+
+long audio_in_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS_32) {
+ struct msm_audio_stats32 stats_32;
+ memset(&stats_32, 0, sizeof(stats_32));
+ stats_32.byte_count = atomic_read(&audio->in_bytes);
+ stats_32.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats_32, sizeof(stats_32))) {
+ pr_err("%s: copy_to_user failed for AUDIO_GET_STATS_32\n",
+ __func__);
+ return -EFAULT;
+ }
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_FLUSH:
+ case AUDIO_PAUSE:
+ case AUDIO_GET_SESSION_ID:
+ rc = audio_in_ioctl_shared(file, cmd, arg);
+ break;
+ case AUDIO_GET_STREAM_CONFIG_32: {
+ struct msm_audio_stream_config32 cfg_32;
+ memset(&cfg_32, 0, sizeof(cfg_32));
+ cfg_32.buffer_size = audio->str_cfg.buffer_size;
+ cfg_32.buffer_count = audio->str_cfg.buffer_count;
+ if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
+ pr_err("%s: Copy to user failed\n", __func__);
+ rc = -EFAULT;
+ }
+ pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
+ __func__, audio->ac->session,
+ cfg_32.buffer_size,
+ cfg_32.buffer_count);
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG_32: {
+ struct msm_audio_stream_config32 cfg_32;
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cfg.buffer_size = cfg_32.buffer_size;
+ cfg.buffer_count = cfg_32.buffer_count;
+ /* Minimum single frame size,
+ * but with in maximum frames number */
+ if ((cfg.buffer_size < (audio->min_frame_size +
+ sizeof(struct meta_out_dsp))) ||
+ (cfg.buffer_count < FRAME_NUM)) {
+ rc = -EINVAL;
+ break;
+ }
+ audio->str_cfg.buffer_size = cfg.buffer_size;
+ audio->str_cfg.buffer_count = cfg.buffer_count;
+ if (audio->opened) {
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s: session id %d:\n",
+ __func__, audio->ac->session);
+ pr_err("Buffer Alloc failed rc=%d\n", rc);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
+ __func__, audio->ac->session,
+ audio->str_cfg.buffer_size,
+ audio->str_cfg.buffer_count);
+ break;
+ }
+ case AUDIO_SET_BUF_CFG_32: {
+ struct msm_audio_buf_cfg32 cfg_32;
+ struct msm_audio_buf_cfg cfg;
+ if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_BUG_CFG_32 failed",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cfg.meta_info_enable = cfg_32.meta_info_enable;
+ cfg.frames_per_buf = cfg_32.frames_per_buf;
+
+ if ((audio->feedback == NON_TUNNEL_MODE) &&
+ !cfg.meta_info_enable) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* Restrict the num of frames per buf to coincide with
+ * default buf size */
+ if (cfg.frames_per_buf > audio->max_frames_per_buf) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+ audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
+ pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__, audio->ac->session, cfg.meta_info_enable,
+ cfg.frames_per_buf);
+ break;
+ }
+ case AUDIO_GET_BUF_CFG_32: {
+ struct msm_audio_buf_cfg32 cfg_32;
+ pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
+ audio->ac->session, audio->buf_cfg.meta_info_enable,
+ audio->buf_cfg.frames_per_buf);
+ cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
+ cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
+
+ if (copy_to_user((void *)arg, &cfg_32,
+ sizeof(struct msm_audio_buf_cfg32))) {
+ pr_err("%s: Copy to user failed\n", __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_GET_CONFIG_32: {
+ struct msm_audio_config32 cfg_32;
+ cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
+ cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
+ cfg_32.channel_count = audio->pcm_cfg.channel_count;
+ cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
+ cfg_32.type = audio->pcm_cfg.type;
+ cfg_32.meta_field = audio->pcm_cfg.meta_field;
+ cfg_32.bits = audio->pcm_cfg.bits;
+
+ if (copy_to_user((void *)arg, &cfg_32,
+ sizeof(struct msm_audio_config32))) {
+ pr_err("%s: Copy to user failed\n", __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_CONFIG_32: {
+ struct msm_audio_config32 cfg_32;
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cfg.buffer_size = cfg_32.buffer_size;
+ cfg.buffer_count = cfg_32.buffer_count;
+ cfg.channel_count = cfg_32.channel_count;
+ cfg.sample_rate = cfg_32.sample_rate;
+ cfg.type = cfg_32.type;
+ cfg.meta_field = cfg_32.meta_field;
+ cfg.bits = cfg_32.bits;
+ rc = audio_in_set_config(file, &cfg);
+ break;
+ }
+ default:
+ /* call codec specific ioctl */
+ rc = audio->enc_compat_ioctl(file, cmd, arg);
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+#endif
+
+ssize_t audio_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct q6audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ unsigned char *data;
+ uint32_t offset = 0;
+ uint32_t size = 0;
+ int rc = 0;
+ uint32_t idx;
+ struct meta_out_dsp meta;
+ uint32_t bytes_to_copy = 0;
+ uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
+ (sizeof(unsigned char) +
+ (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf)));
+
+ memset(&meta, 0, sizeof(meta));
+ pr_debug("%s:session id %d: read - %zd\n", __func__, audio->ac->session,
+ count);
+ if (audio->reset_event)
+ return -ENETRESET;
+
+ if (!audio->enabled)
+ return -EFAULT;
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->read_wait,
+ ((atomic_read(&audio->out_count) > 0) ||
+ (audio->stopped) ||
+ audio->rflush || audio->eos_rsp ||
+ audio->event_abort));
+
+ if (audio->event_abort) {
+ rc = -EIO;
+ break;
+ }
+
+
+ if (rc < 0)
+ break;
+
+ if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
+ audio->rflush) {
+ pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read",
+ __func__,
+ audio->ac->session);
+ rc = 0;/* End of File */
+ break;
+ }
+ if (!(atomic_read(&audio->out_count)) &&
+ (audio->eos_rsp == 1) &&
+ (count >= (sizeof(unsigned char) +
+ sizeof(struct meta_out_dsp)))) {
+ unsigned char num_of_frames;
+ pr_info("%s:session id %d: eos %d at output\n",
+ __func__, audio->ac->session, audio->eos_rsp);
+ if (buf != start)
+ break;
+ num_of_frames = 0xFF;
+ if (copy_to_user(buf, &num_of_frames,
+ sizeof(unsigned char))) {
+ rc = -EFAULT;
+ break;
+ }
+ buf += sizeof(unsigned char);
+ meta.frame_size = 0xFFFF;
+ meta.encoded_pcm_samples = 0xFFFF;
+ meta.msw_ts = 0x00;
+ meta.lsw_ts = 0x00;
+ meta.nflags = AUD_EOS_SET;
+ audio->eos_rsp = 0;
+ if (copy_to_user(buf, &meta, sizeof(meta))) {
+ rc = -EFAULT;
+ break;
+ }
+ buf += sizeof(meta);
+ break;
+ }
+ data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac,
+ &size, &idx);
+ if ((count >= (size + mfield_size)) && data) {
+ if (audio->buf_cfg.meta_info_enable) {
+ if (copy_to_user(buf,
+ &audio->out_frame_info[idx][0],
+ sizeof(unsigned char))) {
+ rc = -EFAULT;
+ break;
+ }
+ bytes_to_copy =
+ (size + audio->out_frame_info[idx][1]);
+ /* Number of frames information copied */
+ buf += sizeof(unsigned char);
+ count -= sizeof(unsigned char);
+ } else {
+ offset = audio->out_frame_info[idx][1];
+ bytes_to_copy = size;
+ }
+
+ pr_debug("%s:session id %d: offset=%d nr of frames= %d\n",
+ __func__, audio->ac->session,
+ audio->out_frame_info[idx][1],
+ audio->out_frame_info[idx][0]);
+
+ if (copy_to_user(buf, &data[offset], bytes_to_copy)) {
+ rc = -EFAULT;
+ break;
+ }
+ count -= bytes_to_copy;
+ buf += bytes_to_copy;
+ } else {
+ pr_err("%s:session id %d: short read data[%p] bytesavail[%d]bytesrequest[%zd]\n",
+ __func__,
+ audio->ac->session,
+ data, size, count);
+ }
+ atomic_dec(&audio->out_count);
+ q6asm_read(audio->ac);
+ break;
+ }
+ mutex_unlock(&audio->read_lock);
+
+ pr_debug("%s:session id %d: read: %zd bytes\n", __func__,
+ audio->ac->session, (buf-start));
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int extract_meta_info(char *buf, unsigned long *msw_ts,
+ unsigned long *lsw_ts, unsigned int *flags)
+{
+ struct meta_in *meta = (struct meta_in *)buf;
+ *msw_ts = meta->ntimestamp.highpart;
+ *lsw_ts = meta->ntimestamp.lowpart;
+ *flags = meta->nflags;
+ return 0;
+}
+
+ssize_t audio_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct q6audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ size_t xfer = 0;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned char *data;
+ uint32_t size = 0;
+ uint32_t idx = 0;
+ uint32_t nflags = 0;
+ unsigned long msw_ts = 0;
+ unsigned long lsw_ts = 0;
+ uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
+ sizeof(struct meta_in);
+
+ pr_debug("%s:session id %d: to write[%zd]\n", __func__,
+ audio->ac->session, count);
+ if (audio->reset_event)
+ return -ENETRESET;
+
+ if (!audio->enabled)
+ return -EFAULT;
+ mutex_lock(&audio->write_lock);
+
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->write_wait,
+ ((atomic_read(&audio->in_count) > 0) ||
+ (audio->stopped) ||
+ (audio->wflush) || (audio->event_abort)));
+
+ if (audio->event_abort) {
+ rc = -EIO;
+ break;
+ }
+
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ pr_debug("%s: session id %d: stop or flush\n", __func__,
+ audio->ac->session);
+ rc = -EBUSY;
+ break;
+ }
+ /* if no PCM data, might have only eos buffer
+ such case do not hold cpu buffer */
+ if ((buf == start) && (count == mfield_size)) {
+ char eos_buf[sizeof(struct meta_in)];
+ /* Processing begining of user buffer */
+ if (copy_from_user(eos_buf, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ extract_meta_info(eos_buf, &msw_ts, &lsw_ts,
+ &nflags);
+ buf += mfield_size;
+ /* send the EOS and return */
+ pr_debug("%s:session id %d: send EOS 0x%8x\n",
+ __func__,
+ audio->ac->session, nflags);
+ break;
+ }
+ data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac,
+ &size, &idx);
+ if (!data) {
+ pr_debug("%s:session id %d: No buf available\n",
+ __func__, audio->ac->session);
+ continue;
+ }
+ cpy_ptr = data;
+ if (audio->buf_cfg.meta_info_enable) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts,
+ &nflags);
+ buf += mfield_size;
+ count -= mfield_size;
+ } else {
+ pr_debug("%s:session id %d: continuous buffer\n",
+ __func__, audio->ac->session);
+ }
+ }
+ xfer = (count > (audio->pcm_cfg.buffer_size)) ?
+ (audio->pcm_cfg.buffer_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00);
+ if (rc < 0) {
+ rc = -EFAULT;
+ break;
+ }
+ atomic_dec(&audio->in_count);
+ count -= xfer;
+ buf += xfer;
+ }
+ mutex_unlock(&audio->write_lock);
+ pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%p] start[0x%p]\n",
+ __func__, audio->ac->session,
+ nflags, buf, start);
+ if (nflags & AUD_EOS_SET) {
+ rc = q6asm_cmd(audio->ac, CMD_EOS);
+ pr_info("%s:session id %d: eos %d at input\n", __func__,
+ audio->ac->session, audio->eos_rsp);
+ }
+ pr_debug("%s:session id %d: Written %zd Avail Buf[%d]", __func__,
+ audio->ac->session, (buf - start - mfield_size),
+ atomic_read(&audio->in_count));
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+int audio_in_release(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = file->private_data;
+ pr_info("%s: session id %d\n", __func__, audio->ac->session);
+ mutex_lock(&audio->lock);
+ audio_in_disable(audio);
+ q6asm_audio_client_free(audio->ac);
+ mutex_unlock(&audio->lock);
+ kfree(audio->enc_cfg);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return 0;
+}
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.h b/drivers/misc/qcom/qdsp6v2/audio_utils.h
new file mode 100644
index 000000000000..c757207b42e6
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2010-2015, 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/msm_audio.h>
+#include <linux/compat.h>
+#include "q6audio_common.h"
+
+#define FRAME_NUM (8)
+
+#define PCM_BUF_COUNT (2)
+
+#define AUD_EOS_SET 0x01
+#define TUNNEL_MODE 0x0000
+#define NON_TUNNEL_MODE 0x0001
+
+#define NO_BUF_ALLOC 0x00
+#define BUF_ALLOC_IN 0x01
+#define BUF_ALLOC_OUT 0x02
+#define BUF_ALLOC_INOUT 0x03
+#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095))
+
+struct timestamp {
+ u32 lowpart;
+ u32 highpart;
+} __packed;
+
+struct meta_in {
+ unsigned short offset;
+ struct timestamp ntimestamp;
+ unsigned int nflags;
+} __packed;
+
+struct meta_out_dsp {
+ u32 offset_to_frame;
+ u32 frame_size;
+ u32 encoded_pcm_samples;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 nflags;
+} __packed;
+
+struct meta_out {
+ unsigned char num_of_frames;
+ struct meta_out_dsp meta_out_dsp[];
+} __packed;
+
+struct q6audio_in {
+ spinlock_t dsp_lock;
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ struct mutex write_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ struct audio_client *ac;
+ struct msm_audio_stream_config str_cfg;
+ void *enc_cfg;
+ struct msm_audio_buf_cfg buf_cfg;
+ struct msm_audio_config pcm_cfg;
+ void *codec_cfg;
+
+ /* number of buffers available to read/write */
+ atomic_t in_count;
+ atomic_t out_count;
+
+ /* first idx: num of frames per buf, second idx: offset to frame */
+ uint32_t out_frame_info[FRAME_NUM][2];
+ int eos_rsp;
+ int opened;
+ int enabled;
+ int stopped;
+ int event_abort;
+ int feedback; /* Flag indicates whether used
+ in Non Tunnel mode */
+ int rflush;
+ int wflush;
+ int buf_alloc;
+ uint16_t min_frame_size;
+ uint16_t max_frames_per_buf;
+ bool reset_event;
+ long (*enc_ioctl)(struct file *, unsigned int, unsigned long);
+ long (*enc_compat_ioctl)(struct file *, unsigned int, unsigned long);
+};
+
+int audio_in_enable(struct q6audio_in *audio);
+int audio_in_disable(struct q6audio_in *audio);
+int audio_in_buf_alloc(struct q6audio_in *audio);
+long audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+long audio_in_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg);
+#else
+#define audio_in_compat_ioctl NULL
+#endif
+ssize_t audio_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos);
+ssize_t audio_in_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos);
+int audio_in_release(struct inode *inode, struct file *file);
+int audio_in_set_config(struct file *file, struct msm_audio_config *cfg);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
new file mode 100644
index 000000000000..884fbbb828b2
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -0,0 +1,2086 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include <linux/debugfs.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+#ifdef CONFIG_USE_DEV_CTRL_VOLUME
+#include <linux/qdsp6v2/audio_dev_ctl.h>
+#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+#ifdef CONFIG_DEBUG_FS
+int audio_aio_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct q6audio_aio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "feedback %d\n", audio->feedback);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "inqueue empty %d\n", list_empty(&audio->in_queue));
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "outqueue empty %d\n", list_empty(&audio->out_queue));
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+#endif
+
+static long audio_aio_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+#ifdef CONFIG_COMPAT
+static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+#else
+#define audio_aio_compat_ioctl NULL
+#endif
+int insert_eos_buf(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ struct dec_meta_out *eos_buf = buf_node->kvaddr;
+ pr_debug("%s[%p]:insert_eos_buf\n", __func__, audio);
+ eos_buf->num_of_frames = 0xFFFFFFFF;
+ eos_buf->meta_out_dsp[0].offset_to_frame = 0x0;
+ eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET;
+ return sizeof(struct dec_meta_out) +
+ sizeof(eos_buf->meta_out_dsp[0]);
+}
+
+/* Routine which updates read buffers of driver/dsp,
+ for flush operation as DSP output might not have proper
+ value set */
+static int insert_meta_data_flush(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ struct dec_meta_out *meta_data = buf_node->kvaddr;
+ meta_data->num_of_frames = 0x0;
+ meta_data->meta_out_dsp[0].offset_to_frame = 0x0;
+ meta_data->meta_out_dsp[0].nflags = 0x0;
+ return sizeof(struct dec_meta_out) +
+ sizeof(meta_data->meta_out_dsp[0]);
+}
+
+static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr,
+ unsigned long len,
+ struct audio_aio_ion_region **region)
+{
+ struct audio_aio_ion_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * ion buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+ __func__, audio, addr, len);
+ list_for_each_entry(region_elt, &audio->ion_region_queue,
+ list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len)
+ pr_err("\t%s[%p]:%p, %ld --> %pa\n",
+ __func__, audio,
+ region_elt->vaddr,
+ region_elt->len,
+ &region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr,
+ unsigned long len, int ref_up, void **kvaddr)
+{
+ struct audio_aio_ion_region *region;
+ phys_addr_t paddr;
+ int ret;
+
+ ret = audio_aio_ion_lookup_vaddr(audio, addr, len, &region);
+ if (ret) {
+ pr_err("%s[%p]:lookup (%p, %ld) failed\n",
+ __func__, audio, addr, len);
+ return 0;
+ }
+ if (ref_up)
+ region->ref_cnt++;
+ else
+ region->ref_cnt--;
+ pr_debug("%s[%p]:found region %p ref_cnt %d\n",
+ __func__, audio, region, region->ref_cnt);
+ paddr = region->paddr + (addr - region->vaddr);
+ /* provide kernel virtual address for accessing meta information */
+ if (kvaddr)
+ *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr));
+ return paddr;
+}
+
+static int audio_aio_pause(struct q6audio_aio *audio)
+{
+ int rc = -EINVAL;
+
+ pr_debug("%s[%p], enabled = %d\n", __func__, audio,
+ audio->enabled);
+ if (audio->enabled) {
+ rc = q6asm_cmd(audio->ac, CMD_PAUSE);
+ if (rc < 0)
+ pr_err("%s[%p]: pause cmd failed rc=%d\n",
+ __func__, audio, rc);
+
+ if (rc == 0) {
+ /* Send suspend only if pause was successful */
+ rc = q6asm_cmd(audio->ac, CMD_SUSPEND);
+ if (rc < 0)
+ pr_err("%s[%p]: suspend cmd failed rc=%d\n",
+ __func__, audio, rc);
+ } else
+ pr_err("%s[%p]: not sending suspend since pause failed\n",
+ __func__, audio);
+
+ } else
+ pr_err("%s[%p]: Driver not enabled\n", __func__, audio);
+ return rc;
+}
+
+static int audio_aio_flush(struct q6audio_aio *audio)
+{
+ int rc;
+
+ if (audio->enabled) {
+ /* Implicitly issue a pause to the decoder before flushing if
+ it is not in pause state */
+ if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
+ rc = audio_aio_pause(audio);
+ if (rc < 0)
+ pr_err("%s[%p}: pause cmd failed rc=%d\n",
+ __func__, audio,
+ rc);
+ else
+ audio->drv_status |= ADRV_STATUS_PAUSE;
+ }
+ rc = q6asm_cmd(audio->ac, CMD_FLUSH);
+ if (rc < 0)
+ pr_err("%s[%p]: flush cmd failed rc=%d\n",
+ __func__, audio, rc);
+ /* Not in stop state, reenable the stream */
+ if (audio->stopped == 0) {
+ rc = audio_aio_enable(audio);
+ if (rc)
+ pr_err("%s[%p]:audio re-enable failed\n",
+ __func__, audio);
+ else {
+ audio->enabled = 1;
+ if (audio->drv_status & ADRV_STATUS_PAUSE)
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ }
+ }
+ }
+ pr_debug("%s[%p]:in_bytes %d\n",
+ __func__, audio, atomic_read(&audio->in_bytes));
+ pr_debug("%s[%p]:in_samples %d\n",
+ __func__, audio, atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+ return 0;
+}
+
+static int audio_aio_outport_flush(struct q6audio_aio *audio)
+{
+ int rc;
+
+ rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH);
+ if (rc < 0)
+ pr_err("%s[%p}: output port flush cmd failed rc=%d\n",
+ __func__, audio, rc);
+ return rc;
+}
+
+/* Write buffer to DSP / Handle Ack from DSP */
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload event_payload;
+ struct audio_aio_buffer_node *used_buf;
+
+ /* No active flush in progress */
+ if (audio->wflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (list_empty(&audio->out_queue)) {
+ pr_warning("%s: ingore unexpected event from dsp\n", __func__);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return;
+ }
+ used_buf = list_first_entry(&audio->out_queue,
+ struct audio_aio_buffer_node, list);
+ if (token == used_buf->token) {
+ list_del(&used_buf->list);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ pr_debug("%s[%p]:consumed buffer\n", __func__, audio);
+ event_payload.aio_buf = used_buf->buf;
+ audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ event_payload);
+ kfree(used_buf);
+ if (list_empty(&audio->out_queue) &&
+ (audio->drv_status & ADRV_STATUS_FSYNC)) {
+ pr_debug("%s[%p]: list is empty, reached EOS in Tunnel\n",
+ __func__, audio);
+ wake_up(&audio->write_wait);
+ }
+ } else {
+ pr_err("%s[%p]:expected=%x ret=%x\n",
+ __func__, audio, used_buf->token, token);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+}
+
+/* ------------------- device --------------------- */
+void audio_aio_async_out_flush(struct q6audio_aio *audio)
+{
+ struct audio_aio_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+ unsigned long flags;
+
+ pr_debug("%s[%p}\n", __func__, audio);
+ /* EOS followed by flush, EOS response not guranteed, free EOS i/p
+ buffer */
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) {
+ pr_debug("%s[%p]: EOS followed by flush received,acknowledge"\
+ " eos i/p buffer immediately\n", __func__, audio);
+ audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ audio->eos_write_payload);
+ memset(&audio->eos_write_payload , 0,
+ sizeof(union msm_audio_event_payload));
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ list_for_each_safe(ptr, next, &audio->out_queue) {
+ buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
+ list_del(&buf_node->list);
+ payload.aio_buf = buf_node->buf;
+ audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload);
+ kfree(buf_node);
+ pr_debug("%s[%p]: Propagate WRITE_DONE during flush\n",
+ __func__, audio);
+ }
+}
+
+void audio_aio_async_in_flush(struct q6audio_aio *audio)
+{
+ struct audio_aio_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+
+ pr_debug("%s[%p]\n", __func__, audio);
+ list_for_each_safe(ptr, next, &audio->in_queue) {
+ buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
+ list_del(&buf_node->list);
+ /* Forcefull send o/p eos buffer after flush, if no eos response
+ * received by dsp even after sending eos command */
+ if ((audio->eos_rsp != 1) && audio->eos_flag) {
+ pr_debug("%s[%p]: send eos on o/p buffer during flush\n",
+ __func__, audio);
+ payload.aio_buf = buf_node->buf;
+ payload.aio_buf.data_len =
+ insert_eos_buf(audio, buf_node);
+ audio->eos_flag = 0;
+ } else {
+ payload.aio_buf = buf_node->buf;
+ payload.aio_buf.data_len =
+ insert_meta_data_flush(audio, buf_node);
+ }
+ audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload);
+ kfree(buf_node);
+ pr_debug("%s[%p]: Propagate READ_DONE during flush\n",
+ __func__, audio);
+ }
+}
+
+int audio_aio_enable(struct q6audio_aio *audio)
+{
+ /* 2nd arg: 0 -> run immediately
+ 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
+ return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+}
+
+int audio_aio_disable(struct q6audio_aio *audio)
+{
+ int rc = 0;
+ if (audio->opened) {
+ audio->enabled = 0;
+ audio->opened = 0;
+ pr_debug("%s[%p]: inbytes[%d] insamples[%d]\n", __func__,
+ audio, atomic_read(&audio->in_bytes),
+ atomic_read(&audio->in_samples));
+ /* Close the session */
+ rc = q6asm_cmd(audio->ac, CMD_CLOSE);
+ if (rc < 0)
+ pr_err("%s[%p]:Failed to close the session rc=%d\n",
+ __func__, audio, rc);
+ audio->stopped = 1;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->cmd_wait);
+ }
+ pr_debug("%s[%p]:enabled[%d]\n", __func__, audio, audio->enabled);
+ return rc;
+}
+
+void audio_aio_reset_ion_region(struct q6audio_aio *audio)
+{
+ struct audio_aio_ion_region *region;
+ struct list_head *ptr, *next;
+
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audio_aio_ion_region, list);
+ list_del(&region->list);
+ msm_audio_ion_free_legacy(audio->client, region->handle);
+ kfree(region);
+ }
+
+ return;
+}
+
+void audio_aio_reset_event_queue(struct q6audio_aio *audio)
+{
+ unsigned long flags;
+ struct audio_aio_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audio_aio_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audio_aio_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
+{
+ struct audio_aio_ion_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ pr_debug("%s[%p]:\n", __func__, audio);
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audio_aio_ion_region, list);
+ if (region != NULL) {
+ pr_debug("%s[%p]: phy_address = 0x%pa\n",
+ __func__, audio, &region->paddr);
+ rc = q6asm_memory_unmap(audio->ac,
+ region->paddr, IN);
+ if (rc < 0)
+ pr_err("%s[%p]: memory unmap failed\n",
+ __func__, audio);
+ }
+ }
+}
+
+#ifdef CONFIG_USE_DEV_CTRL_VOLUME
+
+static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *) private_data;
+ int rc = 0;
+
+ switch (evt_id) {
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->volume = evt_payload->session_vol;
+ pr_debug("%s[%p]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n",
+ __func__, audio, audio->volume, audio->enabled);
+ if (audio->enabled == 1) {
+ if (audio->ac) {
+ rc = q6asm_set_volume(audio->ac, audio->volume);
+ if (rc < 0) {
+ pr_err("%s[%p]: Send Volume command failed rc=%d\n",
+ __func__, audio, rc);
+ }
+ }
+ }
+ break;
+ default:
+ pr_err("%s[%p]:ERROR:wrong event\n", __func__, audio);
+ break;
+ }
+}
+
+int register_volume_listener(struct q6audio_aio *audio)
+{
+ int rc = 0;
+ audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG;
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->ac->session,
+ audio_aio_listner,
+ (void *)audio);
+ if (rc < 0) {
+ pr_err("%s[%p]: Event listener failed\n", __func__, audio);
+ rc = -EACCES;
+ }
+ return rc;
+}
+void unregister_volume_listener(struct q6audio_aio *audio)
+{
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
+}
+
+int enable_volume_ramp(struct q6audio_aio *audio)
+{
+ int rc = 0;
+ struct asm_softpause_params softpause;
+ struct asm_softvolume_params softvol;
+
+ if (audio->ac == NULL)
+ return -EINVAL;
+ pr_debug("%s[%p]\n", __func__, audio);
+ softpause.enable = SOFT_PAUSE_ENABLE;
+ softpause.period = SOFT_PAUSE_PERIOD;
+ softpause.step = SOFT_PAUSE_STEP;
+ softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR;
+
+ softvol.period = SOFT_VOLUME_PERIOD;
+ softvol.step = SOFT_VOLUME_STEP;
+ softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR;
+
+ if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR)
+ softpause.step = SOFT_PAUSE_STEP_LINEAR;
+ if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR)
+ softvol.step = SOFT_VOLUME_STEP_LINEAR;
+ rc = q6asm_set_volume(audio->ac, audio->volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ rc = q6asm_set_softpause(audio->ac, &softpause);
+ if (rc < 0) {
+ pr_err("%s: Send SoftPause Param failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ rc = q6asm_set_softvolume(audio->ac, &softvol);
+ if (rc < 0) {
+ pr_err("%s: Send SoftVolume Param failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ /* disable mute by default */
+ rc = q6asm_set_mute(audio->ac, 0);
+ if (rc < 0) {
+ pr_err("%s: Send mute command failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+#else /*CONFIG_USE_DEV_CTRL_VOLUME*/
+int register_volume_listener(struct q6audio_aio *audio)
+{
+ return 0;/* do nothing */
+}
+void unregister_volume_listener(struct q6audio_aio *audio)
+{
+ return;/* do nothing */
+}
+int enable_volume_ramp(struct q6audio_aio *audio)
+{
+ return 0; /* do nothing */
+}
+#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+
+int audio_aio_release(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = file->private_data;
+ pr_debug("%s[%p]\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ audio->wflush = 1;
+ if (audio->wakelock_voted &&
+ (audio->audio_ws_mgr != NULL) &&
+ (audio->miscdevice != NULL)) {
+ audio->wakelock_voted = false;
+ mutex_lock(&audio->audio_ws_mgr->ws_lock);
+ if ((audio->audio_ws_mgr->ref_cnt > 0) &&
+ (--audio->audio_ws_mgr->ref_cnt == 0)) {
+ pm_relax(audio->miscdevice->this_device);
+ }
+ mutex_unlock(&audio->audio_ws_mgr->ws_lock);
+ }
+ if (audio->enabled)
+ audio_aio_flush(audio);
+ audio->wflush = 0;
+ audio->drv_ops.out_flush(audio);
+ audio->drv_ops.in_flush(audio);
+ audio_aio_disable(audio);
+ audio_aio_unmap_ion_region(audio);
+ audio_aio_reset_ion_region(audio);
+ msm_audio_ion_client_destroy(audio->client);
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audio_aio_reset_event_queue(audio);
+ q6asm_audio_client_free(audio->ac);
+ mutex_unlock(&audio->lock);
+ mutex_destroy(&audio->lock);
+ mutex_destroy(&audio->read_lock);
+ mutex_destroy(&audio->write_lock);
+ mutex_destroy(&audio->get_event_lock);
+ unregister_volume_listener(audio);
+
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return 0;
+}
+
+int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ int rc = 0;
+ struct q6audio_aio *audio = file->private_data;
+
+ if (!audio->enabled || audio->feedback)
+ return -EINVAL;
+
+ /* Blocking client sends more data */
+ mutex_lock(&audio->lock);
+ audio->drv_status |= ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ pr_debug("%s[%p]:\n", __func__, audio);
+
+ audio->eos_rsp = 0;
+
+ pr_debug("%s[%p]Wait for write done from DSP\n", __func__, audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ (list_empty(&audio->out_queue)) ||
+ audio->wflush || audio->stopped);
+
+ if (audio->stopped || audio->wflush) {
+ pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n"
+ , __func__, audio);
+ audio->wflush = 0;
+ rc = -EBUSY;
+ }
+
+ if (rc < 0) {
+ pr_err("%s[%p]: wait event for list_empty failed, rc = %d\n",
+ __func__, audio, rc);
+ goto done;
+ }
+
+ rc = q6asm_cmd(audio->ac, CMD_EOS);
+ pr_debug("%s[%p]: EOS cmd sent to DSP\n", __func__, audio);
+
+ if (rc < 0)
+ pr_err("%s[%p]: q6asm_cmd failed, rc = %d",
+ __func__, audio, rc);
+
+ pr_debug("%s[%p]: wait for RENDERED_EOS from DSP\n"
+ , __func__, audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->eos_rsp || audio->wflush ||
+ audio->stopped));
+
+ if (rc < 0) {
+ pr_err("%s[%p]: wait event for eos_rsp failed, rc = %d\n",
+ __func__, audio, rc);
+ goto done;
+ }
+
+ if (audio->stopped || audio->wflush) {
+ audio->wflush = 0;
+ pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n"
+ , __func__, audio);
+ rc = -EBUSY;
+ }
+
+ if (audio->eos_rsp == 1)
+ pr_debug("%s[%p]: EOS\n", __func__, audio);
+
+
+done:
+ mutex_lock(&audio->lock);
+ audio->drv_status &= ~ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ return rc;
+}
+
+static int audio_aio_events_pending(struct q6audio_aio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
+ struct msm_audio_event *usr_evt)
+{
+ long rc;
+ struct audio_aio_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ timeout = usr_evt->timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(audio->event_wait,
+ audio_aio_events_pending
+ (audio),
+ msecs_to_jiffies
+ (timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(audio->event_wait,
+ audio_aio_events_pending(audio));
+ }
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audio_aio_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt->event_type = drv_evt->event_type;
+ usr_evt->event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else {
+ pr_err("%s[%p]:Unexpected path\n", __func__, audio);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return -EPERM;
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
+ pr_debug("%s[%p]:posted AUDIO_EVENT_WRITE_DONE to user\n",
+ __func__, audio);
+ mutex_lock(&audio->write_lock);
+ audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0, 0);
+ mutex_unlock(&audio->write_lock);
+ } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
+ pr_debug("%s[%p]:posted AUDIO_EVENT_READ_DONE to user\n",
+ __func__, audio);
+ mutex_lock(&audio->read_lock);
+ audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0, 0);
+ mutex_unlock(&audio->read_lock);
+ }
+
+ /* Some read buffer might be held up in DSP,release all
+ * Once EOS indicated
+ */
+ if (audio->eos_rsp && !list_empty(&audio->in_queue)) {
+ pr_debug("%s[%p]:Send flush command to release read buffers"\
+ " held up in DSP\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ audio_aio_flush(audio);
+ mutex_unlock(&audio->lock);
+ }
+
+ return rc;
+}
+
+static long audio_aio_process_event_req(struct q6audio_aio *audio,
+ void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return -EFAULT;
+ }
+
+ rc = audio_aio_process_event_req_common(audio, &usr_evt);
+
+ if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) {
+ pr_err("%s: copy_to_user failed\n", __func__);
+ rc = -EFAULT;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+
+struct msm_audio_aio_buf32 {
+ compat_uptr_t buf_addr;
+ u32 buf_len;
+ u32 data_len;
+ compat_uptr_t private_data;
+ u16 mfield_sz; /*only useful for data has meta field */
+};
+
+struct msm_audio_bitstream_info32 {
+ u32 codec_type;
+ u32 chan_info;
+ u32 sample_rate;
+ u32 bit_stream_info;
+ u32 bit_rate;
+ u32 unused[3];
+};
+
+struct msm_audio_bitstream_error_info32 {
+ u32 dec_id;
+ u32 err_msg_indicator;
+ u32 err_type;
+};
+
+union msm_audio_event_payload32 {
+ struct msm_audio_aio_buf32 aio_buf;
+ struct msm_audio_bitstream_info32 stream_info;
+ struct msm_audio_bitstream_error_info32 error_info;
+ s32 reserved;
+};
+
+struct msm_audio_event32 {
+ s32 event_type;
+ s32 timeout_ms;
+ union msm_audio_event_payload32 event_payload;
+};
+
+static long audio_aio_process_event_req_compat(struct q6audio_aio *audio,
+ void __user *arg)
+{
+ long rc;
+ struct msm_audio_event32 usr_evt_32;
+ struct msm_audio_event usr_evt;
+
+ if (copy_from_user(&usr_evt_32, arg,
+ sizeof(struct msm_audio_event32))) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return -EFAULT;
+ }
+ usr_evt.timeout_ms = usr_evt_32.timeout_ms;
+
+ rc = audio_aio_process_event_req_common(audio, &usr_evt);
+
+ usr_evt_32.event_type = usr_evt.event_type;
+ switch (usr_evt_32.event_type) {
+ case AUDIO_EVENT_SUSPEND:
+ case AUDIO_EVENT_RESUME:
+ case AUDIO_EVENT_WRITE_DONE:
+ case AUDIO_EVENT_READ_DONE:
+ usr_evt_32.event_payload.aio_buf.buf_addr =
+ ptr_to_compat(usr_evt.event_payload.aio_buf.buf_addr);
+ usr_evt_32.event_payload.aio_buf.buf_len =
+ usr_evt.event_payload.aio_buf.buf_len;
+ usr_evt_32.event_payload.aio_buf.data_len =
+ usr_evt.event_payload.aio_buf.data_len;
+ usr_evt_32.event_payload.aio_buf.private_data =
+ ptr_to_compat(usr_evt.event_payload.aio_buf.private_data);
+ usr_evt_32.event_payload.aio_buf.mfield_sz =
+ usr_evt.event_payload.aio_buf.mfield_sz;
+ break;
+ case AUDIO_EVENT_STREAM_INFO:
+ usr_evt_32.event_payload.stream_info.codec_type =
+ usr_evt.event_payload.stream_info.codec_type;
+ usr_evt_32.event_payload.stream_info.chan_info =
+ usr_evt.event_payload.stream_info.chan_info;
+ usr_evt_32.event_payload.stream_info.sample_rate =
+ usr_evt.event_payload.stream_info.sample_rate;
+ usr_evt_32.event_payload.stream_info.bit_stream_info =
+ usr_evt.event_payload.stream_info.bit_stream_info;
+ usr_evt_32.event_payload.stream_info.bit_rate =
+ usr_evt.event_payload.stream_info.bit_rate;
+ break;
+ case AUDIO_EVENT_BITSTREAM_ERROR_INFO:
+ usr_evt_32.event_payload.error_info.dec_id =
+ usr_evt.event_payload.error_info.dec_id;
+ usr_evt_32.event_payload.error_info.err_msg_indicator =
+ usr_evt.event_payload.error_info.err_msg_indicator;
+ usr_evt_32.event_payload.error_info.err_type =
+ usr_evt.event_payload.error_info.err_type;
+ break;
+ default:
+ pr_debug("%s: unknown audio event type = %d rc = %ld",
+ __func__, usr_evt_32.event_type, rc);
+ return rc;
+ }
+ if (copy_to_user(arg, &usr_evt_32, sizeof(usr_evt_32))) {
+ pr_err("%s: copy_to_user failed\n", __func__);
+ rc = -EFAULT;
+ }
+ return rc;
+}
+#endif
+
+static int audio_aio_ion_check(struct q6audio_aio *audio,
+ void *vaddr, unsigned long len)
+{
+ struct audio_aio_ion_region *region_elt;
+ struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len };
+
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ pr_err("%s[%p]:region (vaddr %p len %ld) clashes with registered region (vaddr %p paddr %pa len %ld)\n",
+ __func__, audio, vaddr, len,
+ region_elt->vaddr,
+ &region_elt->paddr, region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int audio_aio_ion_add(struct q6audio_aio *audio,
+ struct msm_audio_ion_info *info)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t len = 0;
+ struct audio_aio_ion_region *region;
+ int rc = -EINVAL;
+ struct ion_handle *handle = NULL;
+ unsigned long ionflag;
+ void *kvaddr = NULL;
+
+ pr_debug("%s[%p]:\n", __func__, audio);
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = msm_audio_ion_import_legacy("Audio_Dec_Client", audio->client,
+ &handle, info->fd, &ionflag,
+ 0, &paddr, &len, &kvaddr);
+ if (rc) {
+ pr_err("%s: msm audio ion alloc failed\n", __func__);
+ goto import_error;
+ }
+
+ rc = audio_aio_ion_check(audio, info->vaddr, len);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_ion_check failed\n", __func__);
+ goto ion_error;
+ }
+
+ region->handle = handle;
+ region->vaddr = info->vaddr;
+ region->fd = info->fd;
+ region->paddr = paddr;
+ region->kvaddr = kvaddr;
+ region->len = len;
+ region->ref_cnt = 0;
+ pr_debug("%s[%p]:add region paddr %pa vaddr %p, len %lu kvaddr %p\n",
+ __func__, audio,
+ &region->paddr, region->vaddr, region->len,
+ region->kvaddr);
+ list_add_tail(&region->list, &audio->ion_region_queue);
+ rc = q6asm_memory_map(audio->ac, paddr, IN, len, 1);
+ if (rc < 0) {
+ pr_err("%s[%p]: memory map failed\n", __func__, audio);
+ goto mmap_error;
+ } else {
+ goto end;
+ }
+mmap_error:
+ list_del(&region->list);
+ion_error:
+ msm_audio_ion_free_legacy(audio->client, handle);
+import_error:
+ kfree(region);
+end:
+ return rc;
+}
+
+static int audio_aio_ion_remove(struct q6audio_aio *audio,
+ struct msm_audio_ion_info *info)
+{
+ struct audio_aio_ion_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ pr_debug("%s[%p]:info fd %d vaddr %p\n",
+ __func__, audio, info->fd, info->vaddr);
+
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audio_aio_ion_region, list);
+
+ if ((region->fd == info->fd) &&
+ (region->vaddr == info->vaddr)) {
+ if (region->ref_cnt) {
+ pr_debug("%s[%p]:region %p in use ref_cnt %d\n",
+ __func__, audio, region,
+ region->ref_cnt);
+ break;
+ }
+ pr_debug("%s[%p]:remove region fd %d vaddr %p\n",
+ __func__, audio, info->fd, info->vaddr);
+ rc = q6asm_memory_unmap(audio->ac,
+ region->paddr, IN);
+ if (rc < 0)
+ pr_err("%s[%p]: memory unmap failed\n",
+ __func__, audio);
+
+ list_del(&region->list);
+ msm_audio_ion_free_legacy(audio->client,
+ region->handle);
+ kfree(region);
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int audio_aio_async_write(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ int rc;
+ struct audio_client *ac;
+ struct audio_aio_write_param param;
+
+ if (!audio || !buf_node) {
+ pr_err("%s NULL pointer audio=[0x%p], buf_node=[0x%p]\n",
+ __func__, audio, buf_node);
+ return -EINVAL;
+ }
+ pr_debug("%s[%p]: Send write buff %p phy %pa len %d meta_enable = %d\n",
+ __func__, audio, buf_node, &buf_node->paddr,
+ buf_node->buf.data_len,
+ audio->buf_cfg.meta_info_enable);
+ pr_debug("%s[%p]: flags = 0x%x\n", __func__, audio,
+ buf_node->meta_info.meta_in.nflags);
+
+ ac = audio->ac;
+ /* Offset with appropriate meta */
+ if (audio->feedback) {
+ /* Non Tunnel mode */
+ param.paddr = buf_node->paddr + sizeof(struct dec_meta_in);
+ param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in);
+ } else {
+ /* Tunnel mode */
+ param.paddr = buf_node->paddr;
+ param.len = buf_node->buf.data_len;
+ }
+ param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart;
+ param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart;
+ param.flags = buf_node->meta_info.meta_in.nflags;
+ /* If no meta_info enaled, indicate no time stamp valid */
+ if (!audio->buf_cfg.meta_info_enable)
+ param.flags = 0xFF00;
+
+ if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET)
+ param.flags |= AUDIO_DEC_EOF_SET;
+
+ param.uid = ac->session;
+ /* Read command will populate paddr as token */
+ buf_node->token = ac->session;
+ rc = q6asm_async_write(ac, &param);
+ if (rc < 0)
+ pr_err("%s[%p]:failed\n", __func__, audio);
+ return rc;
+}
+
+void audio_aio_post_event(struct q6audio_aio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audio_aio_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audio_aio_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC);
+ if (!e_node) {
+ pr_err("%s[%p]:No mem to post event %d\n",
+ __func__, audio, type);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+static int audio_aio_async_read(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ struct audio_client *ac;
+ struct audio_aio_read_param param;
+ int rc;
+
+ pr_debug("%s[%p]: Send read buff %p phy %pa len %d\n",
+ __func__, audio, buf_node,
+ &buf_node->paddr, buf_node->buf.buf_len);
+ ac = audio->ac;
+ /* Provide address so driver can append nr frames information */
+ param.paddr = buf_node->paddr +
+ sizeof(struct dec_meta_out);
+ param.len = buf_node->buf.buf_len -
+ sizeof(struct dec_meta_out);
+ param.uid = param.paddr;
+ /* Write command will populate paddr as token */
+ buf_node->token = param.paddr;
+ rc = q6asm_async_read(ac, &param);
+ if (rc < 0)
+ pr_err("%s[%p]:failed\n", __func__, audio);
+ return rc;
+}
+
+static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir,
+ struct audio_aio_buffer_node *buf_node)
+{
+ unsigned long flags;
+ int ret = 0;
+ pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len %d\n",
+ __func__, audio, buf_node, dir, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, buf_node->buf.data_len);
+ buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, 1,
+ &buf_node->kvaddr);
+ if (dir) {
+ /* write */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (!audio->feedback && !buf_node->buf.data_len)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ extract_meta_out_info(audio, buf_node, 1);
+ /* Not a EOS buffer */
+ if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) {
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ ret = audio_aio_async_write(audio, buf_node);
+ /* EOS buffer handled in driver */
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ } else if (buf_node->meta_info.meta_in.nflags
+ & AUDIO_DEC_EOS_SET) {
+ if (!audio->wflush) {
+ pr_debug("%s[%p]:Send EOS cmd at i/p\n",
+ __func__, audio);
+ /* Driver will forcefully post writedone event
+ * once eos ack recived from DSP
+ */
+ audio->eos_write_payload.aio_buf =\
+ buf_node->buf;
+ audio->eos_flag = 1;
+ audio->eos_rsp = 0;
+ q6asm_cmd(audio->ac, CMD_EOS);
+ kfree(buf_node);
+ } else { /* Flush in progress, send back i/p
+ * EOS buffer as is
+ */
+ union msm_audio_event_payload event_payload;
+ event_payload.aio_buf = buf_node->buf;
+ audio_aio_post_event(audio,
+ AUDIO_EVENT_WRITE_DONE,
+ event_payload);
+ kfree(buf_node);
+ }
+ }
+ } else {
+ /* read */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ /* No EOS reached */
+ if (!audio->eos_rsp) {
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ ret = audio_aio_async_read(audio, buf_node);
+ /* EOS buffer handled in driver */
+ list_add_tail(&buf_node->list, &audio->in_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+ /* EOS reached at input side fake all upcoming read buffer to
+ * indicate the same
+ */
+ else {
+ union msm_audio_event_payload event_payload;
+ event_payload.aio_buf = buf_node->buf;
+ event_payload.aio_buf.data_len =
+ insert_eos_buf(audio, buf_node);
+ pr_debug("%s[%p]: propagate READ_DONE as EOS done\n",\
+ __func__, audio);
+ audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
+ event_payload);
+ kfree(buf_node);
+ }
+ }
+ return ret;
+}
+#ifdef CONFIG_COMPAT
+static int audio_aio_buf_add_compat(struct q6audio_aio *audio, u32 dir,
+ void __user *arg)
+{
+ struct audio_aio_buffer_node *buf_node;
+ struct msm_audio_aio_buf32 aio_buf_32;
+
+ buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL);
+
+ if (!buf_node) {
+ pr_err("%s: Buffer node alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+
+ if (copy_from_user(&aio_buf_32, arg, sizeof(aio_buf_32))) {
+ kfree(buf_node);
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return -EFAULT;
+ }
+
+ buf_node->buf.buf_addr = compat_ptr(aio_buf_32.buf_addr);
+ buf_node->buf.buf_len = aio_buf_32.buf_len;
+ buf_node->buf.data_len = aio_buf_32.data_len;
+ buf_node->buf.private_data = compat_ptr(aio_buf_32.private_data);
+ buf_node->buf.mfield_sz = aio_buf_32.mfield_sz;
+
+ return audio_aio_buf_add_shared(audio, dir, buf_node);
+}
+#endif
+
+static int audio_aio_buf_add(struct q6audio_aio *audio, u32 dir,
+ void __user *arg)
+{
+ struct audio_aio_buffer_node *buf_node;
+
+ buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL);
+
+ if (!buf_node) {
+ pr_err("%s: Buffer node alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+ kfree(buf_node);
+ pr_err("%s: copy_from_user failed\n", __func__);
+ return -EFAULT;
+ }
+
+ return audio_aio_buf_add_shared(audio, dir, buf_node);
+}
+
+void audio_aio_ioport_reset(struct q6audio_aio *audio)
+{
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+ /* If fsync is in progress, make sure
+ * return value of fsync indicates
+ * abort due to flush
+ */
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ pr_debug("%s[%p]:fsync in progress\n", __func__, audio);
+ audio->drv_ops.out_flush(audio);
+ } else
+ audio->drv_ops.out_flush(audio);
+ if (audio->feedback == NON_TUNNEL_MODE)
+ audio->drv_ops.in_flush(audio);
+ }
+}
+
+int audio_aio_open(struct q6audio_aio *audio, struct file *file)
+{
+ int rc = 0;
+ int i;
+ struct audio_aio_event *e_node = NULL;
+ struct list_head *ptr, *next;
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ audio->pcm_cfg.sample_rate = 48000;
+ audio->pcm_cfg.channel_count = 2;
+
+ /* Only AIO interface */
+ if (file->f_flags & O_NONBLOCK) {
+ pr_debug("%s[%p]:set to aio interface\n", __func__, audio);
+ audio->drv_status |= ADRV_STATUS_AIO_INTF;
+ audio->drv_ops.out_flush = audio_aio_async_out_flush;
+ audio->drv_ops.in_flush = audio_aio_async_in_flush;
+ q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE);
+ } else {
+ pr_err("%s[%p]:SIO interface not supported\n",
+ __func__, audio);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->cmd_wait);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->event_wait);
+ INIT_LIST_HEAD(&audio->out_queue);
+ INIT_LIST_HEAD(&audio->in_queue);
+ INIT_LIST_HEAD(&audio->ion_region_queue);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+
+ audio->drv_ops.out_flush(audio);
+ audio->opened = 1;
+ file->private_data = audio;
+ audio->codec_ioctl = audio_aio_ioctl;
+ audio->codec_compat_ioctl = audio_aio_compat_ioctl;
+ for (i = 0; i < AUDIO_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ pr_err("%s[%p]:event pkt alloc failed\n",
+ __func__, audio);
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ }
+ audio->client = msm_audio_ion_client_create("Audio_Dec_Client");
+ if (IS_ERR_OR_NULL(audio->client)) {
+ pr_err("Unable to create ION client\n");
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ pr_debug("Ion client create in audio_aio_open %p", audio->client);
+
+ rc = register_volume_listener(audio);
+ if (rc < 0)
+ goto ion_cleanup;
+
+ return 0;
+ion_cleanup:
+ msm_audio_ion_client_destroy(audio->client);
+ audio->client = NULL;
+cleanup:
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audio_aio_event, list);
+ list_del(&e_node->list);
+ kfree(e_node);
+ }
+fail:
+ return rc;
+}
+
+static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_ABORT_GET_EVENT: {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ break;
+ }
+ case AUDIO_OUTPORT_FLUSH: {
+ pr_debug("%s[%p]:AUDIO_OUTPORT_FLUSH\n", __func__, audio);
+ mutex_lock(&audio->read_lock);
+ rc = audio_aio_outport_flush(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]: AUDIO_OUTPORT_FLUSH failed\n",
+ __func__, audio);
+ rc = -EINTR;
+ }
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s[%p]: AUDIO_STOP session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ mutex_lock(&audio->lock);
+ audio->stopped = 1;
+ rc = audio_aio_flush(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]:Audio Stop procedure failed rc=%d\n",
+ __func__, audio, rc);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ audio->enabled = 0;
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ 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;
+ }
+ case AUDIO_PAUSE: {
+ pr_debug("%s[%p]:AUDIO_PAUSE %ld\n", __func__, audio, arg);
+ mutex_lock(&audio->lock);
+ if (arg == 1) {
+ rc = audio_aio_pause(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]: pause FAILED rc=%d\n",
+ __func__, audio, rc);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ audio->drv_status |= ADRV_STATUS_PAUSE;
+ } else if (arg == 0) {
+ if (audio->drv_status & ADRV_STATUS_PAUSE) {
+ rc = audio_aio_enable(audio);
+ if (rc)
+ pr_err("%s[%p]: audio enable failed\n",
+ __func__, audio);
+ else {
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ audio->enabled = 1;
+ }
+ }
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_FLUSH: {
+ pr_debug("%s[%p]: AUDIO_FLUSH sessionid[%d]\n", __func__,
+ audio, audio->ac->session);
+ 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*/
+ audio_aio_ioport_reset(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]:AUDIO_FLUSH interrupted\n",
+ __func__, audio);
+ rc = -EINTR;
+ } else {
+ audio->rflush = 0;
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ wake_up(&audio->write_wait);
+ else
+ audio->wflush = 0;
+
+ }
+ audio->eos_flag = 0;
+ audio->eos_rsp = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ mutex_lock(&audio->lock);
+ if (copy_to_user((void *)arg, &audio->ac->session,
+ sizeof(u16))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_PM_AWAKE: {
+ if ((audio->audio_ws_mgr == NULL) ||
+ (audio->miscdevice == NULL)) {
+ pr_err("%s[%p]: invalid ws_mgr or miscdevice",
+ __func__, audio);
+ rc = -EACCES;
+ break;
+ }
+ pr_debug("%s[%p]:AUDIO_PM_AWAKE\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (!audio->wakelock_voted) {
+ audio->wakelock_voted = true;
+ mutex_lock(&audio->audio_ws_mgr->ws_lock);
+ if (audio->audio_ws_mgr->ref_cnt++ == 0)
+ pm_stay_awake(audio->miscdevice->this_device);
+ mutex_unlock(&audio->audio_ws_mgr->ws_lock);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_PM_RELAX: {
+ if ((audio->audio_ws_mgr == NULL) ||
+ (audio->miscdevice == NULL)) {
+ pr_err("%s[%p]: invalid ws_mgr or miscdevice",
+ __func__, audio);
+ rc = -EACCES;
+ break;
+ }
+ pr_debug("%s[%p]:AUDIO_PM_RELAX\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (audio->wakelock_voted) {
+ audio->wakelock_voted = false;
+ mutex_lock(&audio->audio_ws_mgr->ws_lock);
+ if ((audio->audio_ws_mgr->ref_cnt > 0) &&
+ (--audio->audio_ws_mgr->ref_cnt == 0)) {
+ pm_relax(audio->miscdevice->this_device);
+ }
+ mutex_unlock(&audio->audio_ws_mgr->ws_lock);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+
+
+}
+
+static long audio_aio_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_ABORT_GET_EVENT:
+ case AUDIO_OUTPORT_FLUSH:
+ case AUDIO_STOP:
+ case AUDIO_PAUSE:
+ case AUDIO_FLUSH:
+ case AUDIO_GET_SESSION_ID:
+ case AUDIO_PM_AWAKE:
+ case AUDIO_PM_RELAX:
+ rc = audio_aio_shared_ioctl(file, cmd, arg);
+ break;
+ case AUDIO_GET_STATS: {
+ struct msm_audio_stats stats;
+ uint64_t timestamp;
+ memset(&stats, 0, sizeof(struct msm_audio_stats));
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ rc = q6asm_get_session_time(audio->ac, &timestamp);
+ if (rc >= 0)
+ memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
+ else
+ pr_debug("Error while getting timestamp\n");
+ if (copy_to_user((void *)arg, &stats, sizeof(stats))) {
+ pr_err("%s: copy_frm_user for AUDIO_GET_STATS failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_GET_EVENT: {
+ pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio);
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audio_aio_process_event_req(audio,
+ (void __user *)arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ break;
+ }
+ case AUDIO_ASYNC_WRITE: {
+ mutex_lock(&audio->write_lock);
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ rc = -EBUSY;
+ else {
+ if (audio->enabled)
+ rc = audio_aio_buf_add(audio, 1,
+ (void __user *)arg);
+ else
+ rc = -EPERM;
+ }
+ mutex_unlock(&audio->write_lock);
+ break;
+ }
+ case AUDIO_ASYNC_READ: {
+ mutex_lock(&audio->read_lock);
+ if (audio->feedback)
+ rc = audio_aio_buf_add(audio, 0,
+ (void __user *)arg);
+ else
+ rc = -EPERM;
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ mutex_lock(&audio->lock);
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->str_cfg.buffer_size;
+ cfg.buffer_count = audio->str_cfg.buffer_count;
+ pr_debug("%s[%p]:GET STREAM CFG %d %d\n",
+ __func__, audio, cfg.buffer_size, cfg.buffer_count);
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
+ pr_err(
+ "%s: copy_to_user for AUDIO_GET_STREAM_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ rc = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ mutex_lock(&audio->lock);
+ if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) {
+ pr_err(
+ "%s: copy_to_user for AUDIO_GET_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&config, (void *)arg, sizeof(config))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ if (audio->feedback != NON_TUNNEL_MODE) {
+ pr_err("%s[%p]:Not sufficient permission to change the playback mode\n",
+ __func__, audio);
+ rc = -EACCES;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->pcm_cfg.buffer_count = config.buffer_count;
+ audio->pcm_cfg.buffer_size = config.buffer_size;
+ audio->pcm_cfg.channel_count = config.channel_count;
+ audio->pcm_cfg.sample_rate = config.sample_rate;
+ rc = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_BUF_CFG: {
+ struct msm_audio_buf_cfg cfg;
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_GET_BUF CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ if ((audio->feedback == NON_TUNNEL_MODE) &&
+ !cfg.meta_info_enable) {
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+
+ audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+ pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]",
+ __func__, audio,
+ audio->ac->session, cfg.meta_info_enable);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_BUF_CFG: {
+ pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__, audio,
+ audio->ac->session, audio->buf_cfg.meta_info_enable,
+ audio->buf_cfg.frames_per_buf);
+
+ mutex_lock(&audio->lock);
+ if (copy_to_user((void *)arg, &audio->buf_cfg,
+ sizeof(struct msm_audio_buf_cfg))) {
+ pr_err(
+ "%s: copy_to_user for AUDIO_GET_BUF_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_REGISTER_ION: {
+ struct msm_audio_ion_info info;
+ pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&info, (void *)arg, sizeof(info))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_REGISTER_ION failed\n",
+ __func__);
+ rc = -EFAULT;
+ } else {
+ rc = audio_aio_ion_add(audio, &info);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_DEREGISTER_ION: {
+ struct msm_audio_ion_info info;
+ mutex_lock(&audio->lock);
+ pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+ if (copy_from_user(&info, (void *)arg, sizeof(info))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_DEREGISTER_ION failed\n",
+ __func__);
+ rc = -EFAULT;
+ } else {
+ rc = audio_aio_ion_remove(audio, &info);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_stream_config32 {
+ u32 buffer_size;
+ u32 buffer_count;
+};
+
+struct msm_audio_stats32 {
+ u32 byte_count;
+ u32 sample_count;
+ u32 unused[2];
+};
+
+struct msm_audio_config32 {
+ u32 buffer_size;
+ u32 buffer_count;
+ u32 channel_count;
+ u32 sample_rate;
+ u32 type;
+ u32 meta_field;
+ u32 bits;
+ u32 unused[3];
+};
+
+struct msm_audio_buf_cfg32 {
+ u32 meta_info_enable;
+ u32 frames_per_buf;
+};
+
+struct msm_audio_ion_info32 {
+ int fd;
+ compat_uptr_t vaddr;
+};
+
+enum {
+ AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
+ struct msm_audio_config32),
+ AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
+ struct msm_audio_config32),
+ AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
+ struct msm_audio_stats32),
+ AUDIO_GET_EVENT_32 = _IOR(AUDIO_IOCTL_MAGIC, 13,
+ struct msm_audio_event32),
+ AUDIO_ASYNC_WRITE_32 = _IOW(AUDIO_IOCTL_MAGIC, 17,
+ struct msm_audio_aio_buf32),
+ AUDIO_ASYNC_READ_32 = _IOW(AUDIO_IOCTL_MAGIC, 18,
+ struct msm_audio_aio_buf32),
+ AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
+ struct msm_audio_stream_config32),
+ AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
+ struct msm_audio_stream_config32),
+ AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
+ struct msm_audio_buf_cfg32),
+ AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
+ struct msm_audio_buf_cfg32),
+ AUDIO_REGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 97,
+ struct msm_audio_ion_info32),
+ AUDIO_DEREGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 98,
+ struct msm_audio_ion_info32),
+};
+
+static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_ABORT_GET_EVENT:
+ case AUDIO_OUTPORT_FLUSH:
+ case AUDIO_STOP:
+ case AUDIO_PAUSE:
+ case AUDIO_FLUSH:
+ case AUDIO_GET_SESSION_ID:
+ case AUDIO_PM_AWAKE:
+ case AUDIO_PM_RELAX:
+ rc = audio_aio_shared_ioctl(file, cmd, arg);
+ break;
+ case AUDIO_GET_STATS_32: {
+ struct msm_audio_stats32 stats;
+ uint64_t timestamp;
+ memset(&stats, 0, sizeof(struct msm_audio_stats32));
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ rc = q6asm_get_session_time(audio->ac, &timestamp);
+ if (rc >= 0)
+ memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
+ else
+ pr_debug("Error while getting timestamp\n");
+ if (copy_to_user((void *)arg, &stats, sizeof(stats))) {
+ pr_err(
+ "%s: copy_to_user for AUDIO_GET_STATS_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_GET_EVENT_32: {
+ pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio);
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audio_aio_process_event_req_compat(audio,
+ (void __user *)arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ break;
+ }
+ case AUDIO_ASYNC_WRITE_32: {
+ mutex_lock(&audio->write_lock);
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ rc = -EBUSY;
+ else {
+ if (audio->enabled)
+ rc = audio_aio_buf_add_compat(audio, 1,
+ (void __user *)arg);
+ else
+ rc = -EPERM;
+ }
+ mutex_unlock(&audio->write_lock);
+ break;
+ }
+ case AUDIO_ASYNC_READ_32: {
+ mutex_lock(&audio->read_lock);
+ if (audio->feedback)
+ rc = audio_aio_buf_add_compat(audio, 0,
+ (void __user *)arg);
+ else
+ rc = -EPERM;
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+
+ case AUDIO_GET_STREAM_CONFIG_32: {
+ struct msm_audio_stream_config32 cfg;
+ mutex_lock(&audio->lock);
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->str_cfg.buffer_size;
+ cfg.buffer_count = audio->str_cfg.buffer_count;
+ pr_debug("%s[%p]:GET STREAM CFG %d %d\n",
+ __func__, audio, cfg.buffer_size, cfg.buffer_count);
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_STREAM_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG_32: {
+ struct msm_audio_stream_config32 cfg_32;
+ struct msm_audio_stream_config cfg;
+ pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ cfg.buffer_size = cfg_32.buffer_size;
+ cfg.buffer_count = cfg_32.buffer_count;
+
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ rc = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_CONFIG_32: {
+ struct msm_audio_config32 cfg_32;
+ mutex_lock(&audio->lock);
+ cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
+ cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
+ cfg_32.channel_count = audio->pcm_cfg.channel_count;
+ cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
+ cfg_32.type = audio->pcm_cfg.type;
+ cfg_32.meta_field = audio->pcm_cfg.meta_field;
+ cfg_32.bits = audio->pcm_cfg.bits;
+
+ if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_CONFIG_32: {
+ struct msm_audio_config config;
+ struct msm_audio_config32 config_32;
+ mutex_lock(&audio->lock);
+
+ if (audio->feedback != NON_TUNNEL_MODE) {
+ pr_err("%s[%p]:Not sufficient permission to change the playback mode\n",
+ __func__, audio);
+ rc = -EACCES;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio);
+ if (copy_from_user(&config_32, (void *)arg,
+ sizeof(config_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ config.buffer_size = config_32.buffer_size;
+ config.buffer_count = config_32.buffer_count;
+ config.channel_count = config_32.channel_count;
+ config.sample_rate = config_32.sample_rate;
+ config.type = config_32.type;
+ config.meta_field = config_32.meta_field;
+ config.bits = config_32.bits;
+
+ if ((config.buffer_count > PCM_BUF_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->pcm_cfg.buffer_count = config.buffer_count;
+ audio->pcm_cfg.buffer_size = config.buffer_size;
+ audio->pcm_cfg.channel_count = config.channel_count;
+ audio->pcm_cfg.sample_rate = config.sample_rate;
+ rc = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_BUF_CFG_32: {
+ struct msm_audio_buf_cfg cfg;
+ struct msm_audio_buf_cfg32 cfg_32;
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ cfg.meta_info_enable = cfg_32.meta_info_enable;
+ cfg.frames_per_buf = cfg_32.frames_per_buf;
+
+ if ((audio->feedback == NON_TUNNEL_MODE) &&
+ !cfg.meta_info_enable) {
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+
+ audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+ pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]",
+ __func__, audio,
+ audio->ac->session, cfg.meta_info_enable);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_BUF_CFG_32: {
+ struct msm_audio_buf_cfg32 cfg_32;
+ pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__, audio,
+ audio->ac->session, audio->buf_cfg.meta_info_enable,
+ audio->buf_cfg.frames_per_buf);
+
+ mutex_lock(&audio->lock);
+ cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
+ cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
+ if (copy_to_user((void *)arg, &cfg_32,
+ sizeof(struct msm_audio_buf_cfg32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_BUF_CFG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_REGISTER_ION_32: {
+ struct msm_audio_ion_info32 info_32;
+ struct msm_audio_ion_info info;
+ pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
+ pr_err("%s: copy_from_user for AUDIO_REGISTER_ION_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ } else {
+ info.fd = info_32.fd;
+ info.vaddr = compat_ptr(info_32.vaddr);
+ rc = audio_aio_ion_add(audio, &info);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_DEREGISTER_ION_32: {
+ struct msm_audio_ion_info32 info_32;
+ struct msm_audio_ion_info info;
+ mutex_lock(&audio->lock);
+ pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+ if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
+ pr_err("%s: copy_from_user for AUDIO_DEREGISTER_ION_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ } else {
+ info.fd = info_32.fd;
+ info.vaddr = compat_ptr(info_32.vaddr);
+ rc = audio_aio_ion_remove(audio, &info);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+#endif
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
new file mode 100644
index 000000000000..d4a7c7e5483b
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
@@ -0,0 +1,232 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/msm_audio.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/msm_ion.h>
+#include <asm/ioctls.h>
+#include <asm/atomic.h>
+#include "q6audio_common.h"
+
+#define TUNNEL_MODE 0x0000
+#define NON_TUNNEL_MODE 0x0001
+
+#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */
+#define ADRV_STATUS_FSYNC 0x00000008
+#define ADRV_STATUS_PAUSE 0x00000010
+#define AUDIO_DEC_EOS_SET 0x00000001
+#define AUDIO_DEC_EOF_SET 0x00000010
+#define AUDIO_EVENT_NUM 10
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = ((__v >= __r->vaddr) && \
+ (__e <= __r->vaddr + __r->len)); \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+struct timestamp {
+ u32 lowpart;
+ u32 highpart;
+} __packed;
+
+struct meta_out_dsp {
+ u32 offset_to_frame;
+ u32 frame_size;
+ u32 encoded_pcm_samples;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 nflags;
+} __packed;
+
+struct dec_meta_in {
+ unsigned char reserved[18];
+ unsigned short offset;
+ struct timestamp ntimestamp;
+ unsigned int nflags;
+} __packed;
+
+struct dec_meta_out {
+ unsigned int reserved[7];
+ unsigned int num_of_frames;
+ struct meta_out_dsp meta_out_dsp[];
+} __packed;
+
+/* General meta field to store meta info
+locally */
+union meta_data {
+ struct dec_meta_out meta_out;
+ struct dec_meta_in meta_in;
+} __packed;
+
+/* per device wakeup source manager */
+struct ws_mgr {
+ struct mutex ws_lock;
+ uint32_t ref_cnt;
+};
+
+#define PCM_BUF_COUNT (2)
+/* Buffer with meta */
+#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out))
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (2)
+#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in))
+
+struct audio_aio_ion_region {
+ struct list_head list;
+ struct ion_handle *handle;
+ int fd;
+ void *vaddr;
+ phys_addr_t paddr;
+ void *kvaddr;
+ unsigned long len;
+ unsigned ref_cnt;
+};
+
+struct audio_aio_event {
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio_aio_buffer_node {
+ struct list_head list;
+ struct msm_audio_aio_buf buf;
+ unsigned long paddr;
+ uint32_t token;
+ void *kvaddr;
+ union meta_data meta_info;
+};
+
+struct q6audio_aio;
+struct audio_aio_drv_operations {
+ void (*out_flush) (struct q6audio_aio *);
+ void (*in_flush) (struct q6audio_aio *);
+};
+
+struct q6audio_aio {
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct msm_audio_stream_config str_cfg;
+ struct msm_audio_buf_cfg buf_cfg;
+ struct msm_audio_config pcm_cfg;
+ void *codec_cfg;
+
+ struct audio_client *ac;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ struct mutex write_lock;
+ struct mutex get_event_lock;
+ wait_queue_head_t cmd_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t event_wait;
+ spinlock_t dsp_lock;
+ spinlock_t event_queue_lock;
+
+ struct miscdevice *miscdevice;
+ uint32_t wakelock_voted;
+ struct ws_mgr *audio_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+ struct list_head out_queue; /* queue to retain output buffers */
+ struct list_head in_queue; /* queue to retain input buffers */
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ struct list_head ion_region_queue; /* protected by lock */
+ struct ion_client *client;
+ struct audio_aio_drv_operations drv_ops;
+ union msm_audio_event_payload eos_write_payload;
+ uint32_t device_events;
+ uint16_t volume;
+ uint32_t drv_status;
+ int event_abort;
+ int eos_rsp;
+ int eos_flag;
+ int opened;
+ int enabled;
+ int stopped;
+ int feedback;
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ long (*codec_ioctl)(struct file *, unsigned int, unsigned long);
+ long (*codec_compat_ioctl)(struct file *, unsigned int, unsigned long);
+};
+
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload);
+
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload);
+
+int insert_eos_buf(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node);
+
+void extract_meta_out_info(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node, int dir);
+
+int audio_aio_open(struct q6audio_aio *audio, struct file *file);
+int audio_aio_enable(struct q6audio_aio *audio);
+void audio_aio_post_event(struct q6audio_aio *audio, int type,
+ union msm_audio_event_payload payload);
+int audio_aio_release(struct inode *inode, struct file *file);
+int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+void audio_aio_async_out_flush(struct q6audio_aio *audio);
+void audio_aio_async_in_flush(struct q6audio_aio *audio);
+void audio_aio_ioport_reset(struct q6audio_aio *audio);
+int enable_volume_ramp(struct q6audio_aio *audio);
+#ifdef CONFIG_DEBUG_FS
+int audio_aio_debug_open(struct inode *inode, struct file *file);
+ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+#endif
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/drivers/misc/qcom/qdsp6v2/audio_wma.c
new file mode 100644
index 000000000000..3d57d38d0fd1
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_wma.c
@@ -0,0 +1,345 @@
+/* wma audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_wma.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_wma_misc;
+static struct ws_mgr audio_wma_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_wma_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_wma_cfg wma_cfg;
+ struct msm_audio_wma_config_v2 *wma_config;
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+ wma_cfg.format_tag = wma_config->format_tag;
+ wma_cfg.ch_cfg = wma_config->numchannels;
+ wma_cfg.sample_rate = wma_config->samplingrate;
+ wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond;
+ wma_cfg.block_align = wma_config->block_align;
+ wma_cfg.valid_bits_per_sample =
+ wma_config->validbitspersample;
+ wma_cfg.ch_mask = wma_config->channelmask;
+ wma_cfg.encode_opt = wma_config->encodeopt;
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg,
+ audio->ac->stream_id);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_WMA_CONFIG_V2: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_wma_config_v2))) {
+ pr_err("%s:copy_to_user for AUDIO_SET_WMA_CONFIG_V2 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_WMA_CONFIG_V2: {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_wma_config_v2))) {
+ pr_err("%s:copy_from_user for AUDIO_SET_WMA_CONFIG_V2 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_wma_config_v2_32 {
+ u16 format_tag;
+ u16 numchannels;
+ u32 samplingrate;
+ u32 avgbytespersecond;
+ u16 block_align;
+ u16 validbitspersample;
+ u32 channelmask;
+ u16 encodeopt;
+};
+
+enum {
+ AUDIO_GET_WMA_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2_32),
+ AUDIO_SET_WMA_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ case AUDIO_GET_WMA_CONFIG_V2_32: {
+ struct msm_audio_wma_config_v2 *wma_config;
+ struct msm_audio_wma_config_v2_32 wma_config_32;
+
+ wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+ wma_config_32.format_tag = wma_config->format_tag;
+ wma_config_32.numchannels = wma_config->numchannels;
+ wma_config_32.samplingrate = wma_config->samplingrate;
+ wma_config_32.avgbytespersecond = wma_config->avgbytespersecond;
+ wma_config_32.block_align = wma_config->block_align;
+ wma_config_32.validbitspersample =
+ wma_config->validbitspersample;
+ wma_config_32.channelmask = wma_config->channelmask;
+ wma_config_32.encodeopt = wma_config->encodeopt;
+ if (copy_to_user((void *)arg, &wma_config_32,
+ sizeof(wma_config_32))) {
+ pr_err("%s: copy_to_user for GET_WMA_CONFIG_V2_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_WMA_CONFIG_V2_32: {
+ struct msm_audio_wma_config_v2 *wma_config;
+ struct msm_audio_wma_config_v2_32 wma_config_32;
+
+ if (copy_from_user(&wma_config_32, (void *)arg,
+ sizeof(wma_config_32))) {
+ pr_err("%s: copy_from_user for SET_WMA_CONFIG_V2_32 failed\n"
+ , __func__);
+ rc = -EFAULT;
+ break;
+ }
+ wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+ wma_config->format_tag = wma_config_32.format_tag;
+ wma_config->numchannels = wma_config_32.numchannels;
+ wma_config->samplingrate = wma_config_32.samplingrate;
+ wma_config->avgbytespersecond = wma_config_32.avgbytespersecond;
+ wma_config->block_align = wma_config_32.block_align;
+ wma_config->validbitspersample =
+ wma_config_32.validbitspersample;
+ wma_config->channelmask = wma_config_32.channelmask;
+ wma_config->encodeopt = wma_config_32.encodeopt;
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_wma_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for wma decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:Could not allocate memory for wma"
+ "config\n", __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_wma_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_wma_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_WMA_V9);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open WMA decoder, expected frames is always 1*/
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_wma_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_wma_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_wma_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_wma_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_wma",
+ .fops = &audio_wma_fops,
+};
+
+static int __init audio_wma_init(void)
+{
+ int ret = misc_register(&audio_wma_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_wma_misc.this_device, true);
+ audio_wma_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_wma_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_wma_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
new file mode 100644
index 000000000000..8d96b99d8f84
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
@@ -0,0 +1,418 @@
+/* wmapro audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_wmapro.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_wmapro_misc;
+static struct ws_mgr audio_wmapro_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_wmapro_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_wmapro_cfg wmapro_cfg;
+ struct msm_audio_wmapro_config *wmapro_config;
+ pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+ audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count,
+ 16, /* bits per sample */
+ true, /* use default channel map */
+ true, /* use back channel map flavor */
+ NULL);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ wmapro_config = (struct msm_audio_wmapro_config *)
+ audio->codec_cfg;
+ if ((wmapro_config->formattag == 0x162) ||
+ (wmapro_config->formattag == 0x163) ||
+ (wmapro_config->formattag == 0x166) ||
+ (wmapro_config->formattag == 0x167)) {
+ wmapro_cfg.format_tag = wmapro_config->formattag;
+ } else {
+ pr_err("%s:AUDIO_START failed: formattag = %d\n",
+ __func__, wmapro_config->formattag);
+ rc = -EINVAL;
+ break;
+ }
+ if (wmapro_config->numchannels > 0) {
+ wmapro_cfg.ch_cfg = wmapro_config->numchannels;
+ } else {
+ pr_err("%s:AUDIO_START failed: channels = %d\n",
+ __func__, wmapro_config->numchannels);
+ rc = -EINVAL;
+ break;
+ }
+ if (wmapro_config->samplingrate > 0) {
+ wmapro_cfg.sample_rate = wmapro_config->samplingrate;
+ } else {
+ pr_err("%s:AUDIO_START failed: sample_rate = %d\n",
+ __func__, wmapro_config->samplingrate);
+ rc = -EINVAL;
+ break;
+ }
+ wmapro_cfg.avg_bytes_per_sec =
+ wmapro_config->avgbytespersecond;
+ if ((wmapro_config->asfpacketlength <= 13376) ||
+ (wmapro_config->asfpacketlength > 0)) {
+ wmapro_cfg.block_align =
+ wmapro_config->asfpacketlength;
+ } else {
+ pr_err("%s:AUDIO_START failed: block_align = %d\n",
+ __func__, wmapro_config->asfpacketlength);
+ rc = -EINVAL;
+ break;
+ }
+ if ((wmapro_config->validbitspersample == 16) ||
+ (wmapro_config->validbitspersample == 24)) {
+ wmapro_cfg.valid_bits_per_sample =
+ wmapro_config->validbitspersample;
+ } else {
+ pr_err("%s:AUDIO_START failed: bitspersample = %d\n",
+ __func__, wmapro_config->validbitspersample);
+ rc = -EINVAL;
+ break;
+ }
+ wmapro_cfg.ch_mask = wmapro_config->channelmask;
+ wmapro_cfg.encode_opt = wmapro_config->encodeopt;
+ wmapro_cfg.adv_encode_opt =
+ wmapro_config->advancedencodeopt;
+ wmapro_cfg.adv_encode_opt2 =
+ wmapro_config->advancedencodeopt2;
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg,
+ audio->ac->stream_id);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_err("%s: Unkown ioctl cmd %d\n", __func__, cmd);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_GET_WMAPRO_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_wmapro_config))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_WMAPRO_CONFIG: {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_wmapro_config))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_WMAPRO_CONFIG_V2 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+
+struct msm_audio_wmapro_config32 {
+ u16 armdatareqthr;
+ u8 validbitspersample;
+ u8 numchannels;
+ u16 formattag;
+ u32 samplingrate;
+ u32 avgbytespersecond;
+ u16 asfpacketlength;
+ u32 channelmask;
+ u16 encodeopt;
+ u16 advancedencodeopt;
+ u32 advancedencodeopt2;
+};
+
+enum {
+ AUDIO_GET_WMAPRO_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config32),
+ AUDIO_SET_WMAPRO_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_GET_WMAPRO_CONFIG_32: {
+ struct msm_audio_wmapro_config *wmapro_config;
+ struct msm_audio_wmapro_config32 wmapro_config_32;
+
+ wmapro_config =
+ (struct msm_audio_wmapro_config *)audio->codec_cfg;
+ wmapro_config_32.armdatareqthr = wmapro_config->armdatareqthr;
+ wmapro_config_32.validbitspersample =
+ wmapro_config->validbitspersample;
+ wmapro_config_32.numchannels = wmapro_config->numchannels;
+ wmapro_config_32.formattag = wmapro_config->formattag;
+ wmapro_config_32.samplingrate = wmapro_config->samplingrate;
+ wmapro_config_32.avgbytespersecond =
+ wmapro_config->avgbytespersecond;
+ wmapro_config_32.asfpacketlength =
+ wmapro_config->asfpacketlength;
+ wmapro_config_32.channelmask = wmapro_config->channelmask;
+ wmapro_config_32.encodeopt = wmapro_config->encodeopt;
+ wmapro_config_32.advancedencodeopt =
+ wmapro_config->advancedencodeopt;
+ wmapro_config_32.advancedencodeopt2 =
+ wmapro_config->advancedencodeopt2;
+
+ if (copy_to_user((void *)arg, &wmapro_config_32,
+ sizeof(struct msm_audio_wmapro_config32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG_V2_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_WMAPRO_CONFIG_32: {
+ struct msm_audio_wmapro_config *wmapro_config;
+ struct msm_audio_wmapro_config32 wmapro_config_32;
+
+ if (copy_from_user(&wmapro_config_32, (void *)arg,
+ sizeof(struct msm_audio_wmapro_config32))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_SET_WMAPRO_CONFG_V2_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ wmapro_config =
+ (struct msm_audio_wmapro_config *)audio->codec_cfg;
+ wmapro_config->armdatareqthr = wmapro_config_32.armdatareqthr;
+ wmapro_config->validbitspersample =
+ wmapro_config_32.validbitspersample;
+ wmapro_config->numchannels = wmapro_config_32.numchannels;
+ wmapro_config->formattag = wmapro_config_32.formattag;
+ wmapro_config->samplingrate = wmapro_config_32.samplingrate;
+ wmapro_config->avgbytespersecond =
+ wmapro_config_32.avgbytespersecond;
+ wmapro_config->asfpacketlength =
+ wmapro_config_32.asfpacketlength;
+ wmapro_config->channelmask = wmapro_config_32.channelmask;
+ wmapro_config->encodeopt = wmapro_config_32.encodeopt;
+ wmapro_config->advancedencodeopt =
+ wmapro_config_32.advancedencodeopt;
+ wmapro_config->advancedencodeopt2 =
+ wmapro_config_32.advancedencodeopt2;
+ break;
+ }
+ case AUDIO_START: {
+ rc = audio_ioctl_shared(file, cmd, (void *)arg);
+ break;
+ }
+ default: {
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_compat_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_wmapro_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for wma decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s: Could not allocate memory for wmapro"
+ "config\n", __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->miscdevice = &audio_wmapro_misc;
+ audio->wakelock_voted = false;
+ audio->audio_ws_mgr = &audio_wmapro_ws_mgr;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_open rc=%d\n",
+ __func__, rc);
+ goto fail;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_WMA_V10PRO);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open WMA decoder, expected frames is always 1*/
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_wmapro_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_wmapro_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_wmapro_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+ .compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_wmapro_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_wmapro",
+ .fops = &audio_wmapro_fops,
+};
+
+static int __init audio_wmapro_init(void)
+{
+ int ret = misc_register(&audio_wmapro_misc);
+
+ if (ret == 0)
+ device_init_wakeup(audio_wmapro_misc.this_device, true);
+ audio_wmapro_ws_mgr.ref_cnt = 0;
+ mutex_init(&audio_wmapro_ws_mgr.ws_lock);
+
+ return ret;
+}
+
+device_initcall(audio_wmapro_init);
diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/drivers/misc/qcom/qdsp6v2/evrc_in.c
new file mode 100644
index 000000000000..2f931be226c6
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/evrc_in.c
@@ -0,0 +1,408 @@
+/* Copyright (c) 2010-2015, 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/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10))
+
+static long evrc_in_ioctl_shared(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_evrc_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ /* rate_modulation_cmd set to zero
+ currently not configurable from user space */
+ rc = q6asm_enc_cfg_blk_evrc(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate, 0);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd evrc media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+ audio->ac->session);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_EVRC_ENC_CONFIG: {
+ struct msm_audio_evrc_enc_config *cfg;
+ struct msm_audio_evrc_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+
+ cfg = (struct msm_audio_evrc_enc_config *)arg;
+ if (cfg == NULL) {
+ pr_err("%s: NULL config pointer for %s\n",
+ __func__, "AUDIO_SET_EVRC_ENC_CONFIG");
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg->min_bit_rate > 4 ||
+ cfg->min_bit_rate < 1 ||
+ (cfg->min_bit_rate == 2)) {
+ pr_err("%s:session id %d: invalid min bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg->max_bit_rate > 4 ||
+ cfg->max_bit_rate < 1 ||
+ (cfg->max_bit_rate == 2)) {
+ pr_err("%s:session id %d: invalid max bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg->min_bit_rate = cfg->min_bit_rate;
+ enc_cfg->max_bit_rate = cfg->max_bit_rate;
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+ __func__,
+ audio->ac->session, enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static long evrc_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = evrc_in_ioctl_shared(file, cmd, arg);
+ break;
+ }
+ case AUDIO_GET_EVRC_ENC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_evrc_enc_config))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_EVRC_ENC_CONFIG: {
+ struct msm_audio_evrc_enc_config cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(struct msm_audio_evrc_enc_config))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_evrc_enc_config32 {
+ u32 cdma_rate;
+ u32 min_bit_rate;
+ u32 max_bit_rate;
+};
+
+enum {
+ AUDIO_SET_EVRC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ 2, struct msm_audio_evrc_enc_config32),
+ AUDIO_GET_EVRC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ 3, struct msm_audio_evrc_enc_config32)
+};
+
+static long evrc_in_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = evrc_in_ioctl_shared(file, cmd, arg);
+ break;
+ }
+ case AUDIO_GET_EVRC_ENC_CONFIG_32: {
+ struct msm_audio_evrc_enc_config32 cfg_32;
+ struct msm_audio_evrc_enc_config *enc_cfg;
+
+ enc_cfg = audio->enc_cfg;
+ cfg_32.cdma_rate = enc_cfg->cdma_rate;
+ cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
+ cfg_32.max_bit_rate = enc_cfg->max_bit_rate;
+
+ if (copy_to_user((void *)arg, &cfg_32,
+ sizeof(cfg_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_EVRC_ENC_CONFIG_32: {
+ struct msm_audio_evrc_enc_config cfg;
+ struct msm_audio_evrc_enc_config32 cfg_32;
+ if (copy_from_user(&cfg_32, (void *) arg,
+ sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cfg.cdma_rate = cfg_32.cdma_rate;
+ cfg.min_bit_rate = cfg_32.min_bit_rate;
+ cfg.max_bit_rate = cfg_32.max_bit_rate;
+ cmd = AUDIO_SET_EVRC_ENC_CONFIG;
+ rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+#else
+#define evrc_in_compat_ioctl NULL
+#endif
+
+static int evrc_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_evrc_enc_config *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for evrc driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac config param\n",
+ __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 23;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->min_bit_rate = 4;
+ enc_cfg->max_bit_rate = 4;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->event_abort = 0;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open evrc encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n",
+ __func__, audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_EVRC);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+ __func__,
+ audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n", __func__,
+ audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ audio->reset_event = false;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_compat_ioctl = evrc_in_compat_ioctl;
+ audio->enc_ioctl = evrc_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = evrc_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+ .compat_ioctl = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_evrc_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init evrc_in_init(void)
+{
+ return misc_register(&audio_evrc_in_misc);
+}
+
+device_initcall(evrc_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_common.h b/drivers/misc/qcom/qdsp6v2/q6audio_common.h
new file mode 100644
index 000000000000..c41d0b651656
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_common.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012-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.
+ *
+*/
+
+
+/* For Decoders */
+#ifndef __Q6_AUDIO_COMMON_H__
+#define __Q6_AUDIO_COMMON_H__
+
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *audio);
+
+
+/* For Encoders */
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+void audio_in_get_dsp_frames(void *audio,
+ uint32_t token, uint32_t *payload);
+
+#endif /*__Q6_AUDIO_COMMON_H__*/
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
new file mode 100644
index 000000000000..df0065c3f77e
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2012-2013, 2015, 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/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_in *audio = (struct q6audio_in *)priv;
+ unsigned long flags;
+
+ pr_debug("%s:session id %d: opcode[0x%x]\n", __func__,
+ audio->ac->session, opcode);
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ switch (opcode) {
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ audio_in_get_dsp_frames(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ atomic_inc(&audio->in_count);
+ wake_up(&audio->write_wait);
+ break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ audio->eos_rsp = 1;
+ wake_up(&audio->read_wait);
+ break;
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
+ break;
+ case ASM_SESSION_EVENTX_OVERFLOW:
+ pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
+ __func__, audio->ac->session);
+ break;
+ case RESET_EVENTS:
+ pr_debug("%s:received RESET EVENTS\n", __func__);
+ audio->enabled = 0;
+ audio->stopped = 1;
+ audio->event_abort = 1;
+ audio->reset_event = true;
+ wake_up(&audio->read_wait);
+ wake_up(&audio->write_wait);
+ break;
+ default:
+ pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
+ audio->ac->session, opcode);
+ break;
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+void audio_in_get_dsp_frames(void *priv,
+ uint32_t token, uint32_t *payload)
+{
+ struct q6audio_in *audio = (struct q6audio_in *)priv;
+ uint32_t index;
+
+ index = token;
+ pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
+ __func__, audio->ac->session, token, payload[9],
+ payload[5]);
+ pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
+ audio->ac->session, payload[7], payload[6]);
+ pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
+ audio->ac->session, payload[8], payload[10]);
+ pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__,
+ audio->ac->session, payload[4]);
+
+ audio->out_frame_info[index][0] = payload[9];
+ audio->out_frame_info[index][1] = payload[5];
+
+ /* statistics of read */
+ atomic_add(payload[4], &audio->in_bytes);
+ atomic_add(payload[9], &audio->in_samples);
+
+ if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
+ atomic_inc(&audio->out_count);
+ wake_up(&audio->read_wait);
+ }
+}
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
new file mode 100644
index 000000000000..f9534b2bfa7f
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2012-2015, 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/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils_aio.h"
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+
+ pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+ case RESET_EVENTS:
+ audio_aio_cb(opcode, token, payload, audio);
+ break;
+ default:
+ pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
+ break;
+ }
+}
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv/*struct q6audio_aio *audio*/)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+ union msm_audio_event_payload e_payload;
+
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
+ __func__, audio, token);
+ audio_aio_async_write_ack(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
+ __func__, audio, token);
+ audio_aio_async_read_ack(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ /* EOS Handle */
+ pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
+ if (audio->feedback) { /* Non-Tunnel mode */
+ audio->eos_rsp = 1;
+ /* propagate input EOS i/p buffer,
+ after receiving DSP acknowledgement */
+ if (audio->eos_flag &&
+ (audio->eos_write_payload.aio_buf.buf_addr)) {
+ audio_aio_post_event(audio,
+ AUDIO_EVENT_WRITE_DONE,
+ audio->eos_write_payload);
+ memset(&audio->eos_write_payload , 0,
+ sizeof(union msm_audio_event_payload));
+ audio->eos_flag = 0;
+ }
+ } else { /* Tunnel mode */
+ audio->eos_rsp = 1;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->cmd_wait);
+ }
+ break;
+ case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
+ __func__, audio, payload[0], payload[1], opcode);
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+ pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n",
+ __func__, audio, payload[0],
+ payload[1], payload[2], payload[3]);
+
+ pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,",
+ __func__, audio, audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ audio->pcm_cfg.sample_rate = payload[0];
+ audio->pcm_cfg.channel_count = payload[1] & 0xFFFF;
+ e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count;
+ e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
+ audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+ break;
+ case RESET_EVENTS:
+ pr_debug("%s: Received opcode:0x%x\n", __func__, opcode);
+ audio->stopped = 1;
+ wake_up(&audio->event_wait);
+ break;
+ default:
+ break;
+ }
+}
+
+void extract_meta_out_info(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node, int dir)
+{
+ struct dec_meta_out *meta_data = buf_node->kvaddr;
+ uint32_t temp;
+
+ if (dir) { /* input buffer - Write */
+ if (audio->buf_cfg.meta_info_enable)
+ memcpy(&buf_node->meta_info.meta_in,
+ (char *)buf_node->kvaddr, sizeof(struct dec_meta_in));
+ else
+ memset(&buf_node->meta_info.meta_in,
+ 0, sizeof(struct dec_meta_in));
+ pr_debug("%s[%p]:i/p: msw_ts 0x%d lsw_ts 0x%d nflags 0x%8x\n",
+ __func__, audio,
+ buf_node->meta_info.meta_in.ntimestamp.highpart,
+ buf_node->meta_info.meta_in.ntimestamp.lowpart,
+ buf_node->meta_info.meta_in.nflags);
+ } else { /* output buffer - Read */
+ memcpy((char *)buf_node->kvaddr,
+ &buf_node->meta_info.meta_out,
+ sizeof(struct dec_meta_out));
+ meta_data->meta_out_dsp[0].nflags = 0x00000000;
+ temp = meta_data->meta_out_dsp[0].msw_ts;
+ meta_data->meta_out_dsp[0].msw_ts =
+ meta_data->meta_out_dsp[0].lsw_ts;
+ meta_data->meta_out_dsp[0].lsw_ts = temp;
+
+ pr_debug("%s[%p]:o/p: msw_ts 0x%d lsw_ts 0x%d nflags 0x%8x, num_frames = %d\n",
+ __func__, audio,
+ ((struct dec_meta_out *)buf_node->kvaddr)->\
+ meta_out_dsp[0].msw_ts,
+ ((struct dec_meta_out *)buf_node->kvaddr)->\
+ meta_out_dsp[0].lsw_ts,
+ ((struct dec_meta_out *)buf_node->kvaddr)->\
+ meta_out_dsp[0].nflags,
+ ((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames);
+ }
+}
+
+/* Read buffer from DSP / Handle Ack from DSP */
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload event_payload;
+ struct audio_aio_buffer_node *filled_buf;
+ pr_debug("%s\n", __func__);
+
+ /* No active flush in progress */
+ if (audio->rflush)
+ return;
+
+ /* Statistics of read */
+ atomic_add(payload[4], &audio->in_bytes);
+ atomic_add(payload[9], &audio->in_samples);
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (list_empty(&audio->in_queue)) {
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ pr_warning("%s unexpected ack from dsp\n", __func__);
+ return;
+ }
+ filled_buf = list_first_entry(&audio->in_queue,
+ struct audio_aio_buffer_node, list);
+
+ pr_debug("%s token: 0x[%x], filled_buf->token: 0x[%x]",
+ __func__, token, filled_buf->token);
+ if (token == (filled_buf->token)) {
+ list_del(&filled_buf->list);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ event_payload.aio_buf = filled_buf->buf;
+ /* Read done Buffer due to flush/normal condition
+ after EOS event, so append EOS buffer */
+ if (audio->eos_rsp == 0x1) {
+ event_payload.aio_buf.data_len =
+ insert_eos_buf(audio, filled_buf);
+ /* Reset flag back to indicate eos intimated */
+ audio->eos_rsp = 0;
+ } else {
+ filled_buf->meta_info.meta_out.num_of_frames\
+ = payload[9];
+ event_payload.aio_buf.data_len = payload[4]\
+ + payload[5] + sizeof(struct dec_meta_out);
+ pr_debug("%s[%p]:nr of frames 0x%8x len=%d\n",
+ __func__, audio,
+ filled_buf->meta_info.meta_out.num_of_frames,
+ event_payload.aio_buf.data_len);
+ extract_meta_out_info(audio, filled_buf, 0);
+ audio->eos_rsp = 0;
+ }
+ pr_debug("%s, posting read done to the app here\n", __func__);
+ audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
+ event_payload);
+ kfree(filled_buf);
+ } else {
+ pr_err("%s[%p]:expected=%x ret=%x\n",
+ __func__, audio, filled_buf->token, token);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+}
diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
new file mode 100644
index 000000000000..b5d5ad113722
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
@@ -0,0 +1,408 @@
+/* Copyright (c) 2010-2015, 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/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10))
+
+static long qcelp_in_ioctl_shared(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ /* reduced_rate_level, rate_modulation_cmd set to zero
+ currently not configurable from user space */
+ rc = q6asm_enc_cfg_blk_qcelp(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate, 0, 0);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd qcelp media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
+ audio->ac->session, audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+ audio->ac->session);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+ __func__, audio->ac->session,
+ rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_QCELP_ENC_CONFIG: {
+ struct msm_audio_qcelp_enc_config *cfg;
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+
+ cfg = (struct msm_audio_qcelp_enc_config *)arg;
+ if (cfg == NULL) {
+ pr_err("%s: NULL config pointer\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg->min_bit_rate > 4 ||
+ cfg->min_bit_rate < 1) {
+ pr_err("%s:session id %d: invalid min bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg->max_bit_rate > 4 ||
+ cfg->max_bit_rate < 1) {
+ pr_err("%s:session id %d: invalid max bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg->cdma_rate = cfg->cdma_rate;
+ enc_cfg->min_bit_rate = cfg->min_bit_rate;
+ enc_cfg->max_bit_rate = cfg->max_bit_rate;
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+ __func__,
+ audio->ac->session, enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static long qcelp_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = qcelp_in_ioctl_shared(file, cmd, arg);
+ break;
+ }
+ case AUDIO_GET_QCELP_ENC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_qcelp_enc_config))) {
+ pr_err(
+ "%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG failed",
+ __func__);
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_QCELP_ENC_CONFIG: {
+ struct msm_audio_qcelp_enc_config cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(cfg))) {
+ pr_err(
+ "%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG failed",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. Rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_qcelp_enc_config32 {
+ u32 cdma_rate;
+ u32 min_bit_rate;
+ u32 max_bit_rate;
+};
+
+enum {
+ AUDIO_SET_QCELP_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+ 0, struct msm_audio_qcelp_enc_config32),
+ AUDIO_GET_QCELP_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+ 1, struct msm_audio_qcelp_enc_config32)
+};
+
+static long qcelp_in_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START:
+ case AUDIO_STOP: {
+ rc = qcelp_in_ioctl_shared(file, cmd, arg);
+ break;
+ }
+ case AUDIO_GET_QCELP_ENC_CONFIG_32: {
+ struct msm_audio_qcelp_enc_config32 cfg_32;
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+
+ enc_cfg = (struct msm_audio_qcelp_enc_config *)audio->enc_cfg;
+ cfg_32.cdma_rate = enc_cfg->cdma_rate;
+ cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
+ cfg_32.max_bit_rate = enc_cfg->max_bit_rate;
+ if (copy_to_user((void *)arg, &cfg_32,
+ sizeof(cfg_32))) {
+ pr_err("%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+}
+ break;
+ }
+ case AUDIO_SET_QCELP_ENC_CONFIG_32: {
+ struct msm_audio_qcelp_enc_config32 cfg_32;
+ struct msm_audio_qcelp_enc_config cfg;
+ if (copy_from_user(&cfg_32, (void *) arg,
+ sizeof(cfg_32))) {
+ pr_err("%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG_32 failed\n",
+ __func__);
+ rc = -EFAULT;
+ break;
+ }
+ cfg.cdma_rate = cfg_32.cdma_rate;
+ cfg.min_bit_rate = cfg_32.min_bit_rate;
+ cfg.max_bit_rate = cfg_32.max_bit_rate;
+ cmd = AUDIO_SET_QCELP_ENC_CONFIG;
+ rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+ if (rc)
+ pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. rc= %d\n",
+ __func__, rc);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+#else
+#define qcelp_in_compat_ioctl NULL
+#endif
+
+static int qcelp_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for qcelp driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac config param\n",
+ __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 35;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->min_bit_rate = 4;
+ enc_cfg->max_bit_rate = 4;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->event_abort = 0;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open qcelp encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_V13K,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n", __func__,
+ audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_V13K);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n", __func__,
+ audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ audio->reset_event = false;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_compat_ioctl = qcelp_in_compat_ioctl;
+ audio->enc_ioctl = qcelp_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = qcelp_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+ .compat_ioctl = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_qcelp_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init qcelp_in_init(void)
+{
+ return misc_register(&audio_qcelp_in_misc);
+}
+
+device_initcall(qcelp_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile
new file mode 100644
index 000000000000..41f614aa4eb3
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile
@@ -0,0 +1,2 @@
+ccflags-y := -I$(src)/..
+obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
new file mode 100644
index 000000000000..42dc2b69407f
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
@@ -0,0 +1,1464 @@
+/* Copyright (c) 2012-2015, 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/mutex.h>
+#include <linux/wait.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/qdsp6v2/apr_us.h>
+#include "q6usm.h"
+
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+#define MEM_4K_OFFSET 4095
+#define MEM_4K_MASK 0xfffff000
+
+#define USM_SESSION_MAX 0x02 /* aDSP:USM limit */
+
+#define READDONE_IDX_STATUS 0
+
+#define WRITEDONE_IDX_STATUS 0
+
+/* Standard timeout in the asynchronous ops */
+#define Q6USM_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
+
+static DEFINE_MUTEX(session_lock);
+
+static struct us_client *session[USM_SESSION_MAX];
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv);
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv);
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg);
+
+struct usm_mmap {
+ atomic_t ref_cnt;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+ void *apr;
+ int mem_handle;
+};
+
+static struct usm_mmap this_mmap;
+
+static void q6usm_add_mmaphdr(struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg, u32 token)
+{
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ if (cmd_flg) {
+ hdr->token = token;
+ atomic_set(&this_mmap.cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static int q6usm_memory_map(phys_addr_t buf_add, int dir, uint32_t bufsz,
+ uint32_t bufcnt, uint32_t session, uint32_t *mem_handle)
+{
+ struct usm_cmd_memory_map_region mem_region_map;
+ int rc = 0;
+
+ if (this_mmap.apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_mmaphdr(&mem_region_map.hdr,
+ sizeof(struct usm_cmd_memory_map_region), true,
+ ((session << 8) | dir));
+
+ mem_region_map.hdr.opcode = USM_CMD_SHARED_MEM_MAP_REGION;
+ mem_region_map.mempool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+
+ mem_region_map.num_regions = 1;
+ mem_region_map.flags = 0;
+
+ mem_region_map.shm_addr_lsw = lower_32_bits(buf_add);
+ mem_region_map.shm_addr_msw = populate_upper_32_bits(buf_add);
+ mem_region_map.mem_size_bytes = bufsz * bufcnt;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_region_map);
+ if (rc < 0) {
+ pr_err("%s: mem_map op[0x%x]rc[%d]\n",
+ __func__, mem_region_map.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ } else {
+ *mem_handle = this_mmap.mem_handle;
+ rc = 0;
+ }
+fail_cmd:
+ return rc;
+}
+
+int q6usm_memory_unmap(phys_addr_t buf_add, int dir, uint32_t session,
+ uint32_t mem_handle)
+{
+ struct usm_cmd_memory_unmap_region mem_unmap;
+ int rc = 0;
+
+ if (this_mmap.apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_mmaphdr(&mem_unmap.hdr,
+ sizeof(struct usm_cmd_memory_unmap_region), true,
+ ((session << 8) | dir));
+ mem_unmap.hdr.opcode = USM_CMD_SHARED_MEM_UNMAP_REGION;
+ mem_unmap.mem_map_handle = mem_handle;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("%s: mem_unmap op[0x%x] rc[%d]\n",
+ __func__, mem_unmap.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_unmap\n", __func__);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+static int q6usm_session_alloc(struct us_client *usc)
+{
+ int ind = 0;
+
+ mutex_lock(&session_lock);
+ for (ind = 0; ind < USM_SESSION_MAX; ++ind) {
+ if (!session[ind]) {
+ session[ind] = usc;
+ mutex_unlock(&session_lock);
+ ++ind; /* session id: 0 reserved */
+ pr_debug("%s: session[%d] was allocated\n",
+ __func__, ind);
+ return ind;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void q6usm_session_free(struct us_client *usc)
+{
+ /* Session index was incremented during allocation */
+ uint16_t ind = (uint16_t)usc->session - 1;
+
+ pr_debug("%s: to free session[%d]\n", __func__, ind);
+ if (ind < USM_SESSION_MAX) {
+ mutex_lock(&session_lock);
+ session[ind] = NULL;
+ mutex_unlock(&session_lock);
+ }
+}
+
+static int q6usm_us_client_buf_free(unsigned int dir,
+ struct us_client *usc)
+{
+ struct us_port_data *port;
+ int rc = 0;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ mutex_lock(&usc->cmd_lock);
+ port = &usc->port[dir];
+ if (port == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return -EINVAL;
+ }
+
+ if (port->data == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return 0;
+ }
+
+ rc = q6usm_memory_unmap(port->phys, dir, usc->session,
+ *((uint32_t *)port->ext));
+ pr_debug("%s: data[%p]phys[%llx][%p]\n", __func__,
+ (void *)port->data, (u64)port->phys, (void *)&port->phys);
+
+ msm_audio_ion_free(port->client, port->handle);
+
+ port->data = NULL;
+ port->phys = 0;
+ port->buf_size = 0;
+ port->buf_cnt = 0;
+ port->client = NULL;
+ port->handle = NULL;
+
+ mutex_unlock(&usc->cmd_lock);
+ return rc;
+}
+
+int q6usm_us_param_buf_free(unsigned int dir,
+ struct us_client *usc)
+{
+ struct us_port_data *port;
+ int rc = 0;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ mutex_lock(&usc->cmd_lock);
+ port = &usc->port[dir];
+ if (port == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return -EINVAL;
+ }
+
+ if (port->param_buf == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return 0;
+ }
+
+ rc = q6usm_memory_unmap(port->param_phys, dir, usc->session,
+ *((uint32_t *)port->param_buf_mem_handle));
+ pr_debug("%s: data[%p]phys[%llx][%p]\n", __func__,
+ (void *)port->param_buf, (u64)port->param_phys,
+ (void *)&port->param_phys);
+
+ msm_audio_ion_free(port->param_client, port->param_handle);
+
+ port->param_buf = NULL;
+ port->param_phys = 0;
+ port->param_buf_size = 0;
+ port->param_client = NULL;
+ port->param_handle = NULL;
+
+ mutex_unlock(&usc->cmd_lock);
+ return rc;
+}
+
+void q6usm_us_client_free(struct us_client *usc)
+{
+ int loopcnt = 0;
+ struct us_port_data *port;
+ uint32_t *p_mem_handle = NULL;
+
+ if ((usc == NULL) ||
+ !(usc->session))
+ return;
+
+ for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) {
+ port = &usc->port[loopcnt];
+ if (port->data == NULL)
+ continue;
+ pr_debug("%s: loopcnt = %d\n", __func__, loopcnt);
+ q6usm_us_client_buf_free(loopcnt, usc);
+ q6usm_us_param_buf_free(loopcnt, usc);
+ }
+ q6usm_session_free(usc);
+ apr_deregister(usc->apr);
+
+ pr_debug("%s: APR De-Register\n", __func__);
+
+ if (atomic_read(&this_mmap.ref_cnt) <= 0) {
+ pr_err("%s: APR Common Port Already Closed\n", __func__);
+ goto done;
+ }
+
+ atomic_dec(&this_mmap.ref_cnt);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ apr_deregister(this_mmap.apr);
+ pr_debug("%s: APR De-Register common port\n", __func__);
+ }
+
+done:
+ p_mem_handle = (uint32_t *)usc->port[IN].ext;
+ kfree(p_mem_handle);
+ kfree(usc);
+ pr_debug("%s:\n", __func__);
+ return;
+}
+
+struct us_client *q6usm_us_client_alloc(
+ void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
+ void *priv)
+{
+ struct us_client *usc;
+ uint32_t *p_mem_handle = NULL;
+ int n;
+ int lcnt = 0;
+
+ usc = kzalloc(sizeof(struct us_client), GFP_KERNEL);
+ if (usc == NULL) {
+ pr_err("%s: us_client allocation failed\n", __func__);
+ return NULL;
+ }
+ p_mem_handle = kzalloc(sizeof(uint32_t) * 4, GFP_KERNEL);
+ if (p_mem_handle == NULL) {
+ pr_err("%s: p_mem_handle allocation failed\n", __func__);
+ kfree(usc);
+ return NULL;
+ }
+
+ n = q6usm_session_alloc(usc);
+ if (n <= 0)
+ goto fail_session;
+ usc->session = n;
+ usc->cb = cb;
+ usc->priv = priv;
+ usc->apr = apr_register("ADSP", "USM", \
+ (apr_fn)q6usm_callback,\
+ ((usc->session) << 8 | 0x0001),\
+ usc);
+
+ if (usc->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail;
+ }
+ pr_debug("%s: Registering the common port with APR\n", __func__);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ this_mmap.apr = apr_register("ADSP", "USM",
+ (apr_fn)q6usm_mmapcallback,
+ 0x0FFFFFFFF, &this_mmap);
+ if (this_mmap.apr == NULL) {
+ pr_err("%s: USM port registration failed\n",
+ __func__);
+ goto fail;
+ }
+ }
+
+ atomic_inc(&this_mmap.ref_cnt);
+ init_waitqueue_head(&usc->cmd_wait);
+ mutex_init(&usc->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; ++lcnt) {
+ mutex_init(&usc->port[lcnt].lock);
+ spin_lock_init(&usc->port[lcnt].dsp_lock);
+ usc->port[lcnt].ext = (void *)p_mem_handle++;
+ usc->port[lcnt].param_buf_mem_handle = (void *)p_mem_handle++;
+ pr_err("%s: usc->port[%d].ext=%p;\n",
+ __func__, lcnt, usc->port[lcnt].ext);
+ }
+ atomic_set(&usc->cmd_state, 0);
+
+ return usc;
+fail:
+ kfree(p_mem_handle);
+ q6usm_us_client_free(usc);
+ return NULL;
+fail_session:
+ kfree(p_mem_handle);
+ kfree(usc);
+ return NULL;
+}
+
+int q6usm_us_client_buf_alloc(unsigned int dir,
+ struct us_client *usc,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int rc = 0;
+ struct us_port_data *port = NULL;
+ unsigned int size = bufsz*bufcnt;
+ size_t len;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)) || (size == 0) ||
+ (usc->session <= 0 || usc->session > USM_SESSION_MAX)) {
+ pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n",
+ __func__, size, bufcnt);
+ return -EINVAL;
+ }
+
+ mutex_lock(&usc->cmd_lock);
+
+ port = &usc->port[dir];
+
+ /* The size to allocate should be multiple of 4K bytes */
+ size = PAGE_ALIGN(size);
+
+ rc = msm_audio_ion_alloc("ultrasound_client",
+ &port->client, &port->handle,
+ size, &port->phys,
+ &len, &port->data);
+
+ if (rc) {
+ pr_err("%s: US ION allocation failed, rc = %d\n",
+ __func__, rc);
+ mutex_unlock(&usc->cmd_lock);
+ return -ENOMEM;
+ }
+
+ port->buf_cnt = bufcnt;
+ port->buf_size = bufsz;
+ pr_debug("%s: data[%p]; phys[%llx]; [%p]\n", __func__,
+ (void *)port->data,
+ (u64)port->phys,
+ (void *)&port->phys);
+
+ rc = q6usm_memory_map(port->phys, dir, size, 1, usc->session,
+ (uint32_t *)port->ext);
+ if (rc < 0) {
+ pr_err("%s: CMD Memory_map failed\n", __func__);
+ mutex_unlock(&usc->cmd_lock);
+ q6usm_us_client_buf_free(dir, usc);
+ q6usm_us_param_buf_free(dir, usc);
+ } else {
+ mutex_unlock(&usc->cmd_lock);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int q6usm_us_param_buf_alloc(unsigned int dir,
+ struct us_client *usc,
+ unsigned int bufsz)
+{
+ int rc = 0;
+ struct us_port_data *port = NULL;
+ unsigned int size = bufsz;
+ size_t len;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)) ||
+ (usc->session <= 0 || usc->session > USM_SESSION_MAX)) {
+ pr_err("%s: wrong parameters: direction=%d, bufsz=%d\n",
+ __func__, dir, bufsz);
+ return -EINVAL;
+ }
+
+ mutex_lock(&usc->cmd_lock);
+
+ port = &usc->port[dir];
+
+ if (bufsz == 0) {
+ pr_debug("%s: bufsz=0, get/set param commands are forbidden\n",
+ __func__);
+ port->param_buf = NULL;
+ mutex_unlock(&usc->cmd_lock);
+ return rc;
+ }
+
+ /* The size to allocate should be multiple of 4K bytes */
+ size = PAGE_ALIGN(size);
+
+ rc = msm_audio_ion_alloc("ultrasound_client",
+ &port->param_client, &port->param_handle,
+ size, &port->param_phys,
+ &len, &port->param_buf);
+
+ if (rc) {
+ pr_err("%s: US ION allocation failed, rc = %d\n",
+ __func__, rc);
+ mutex_unlock(&usc->cmd_lock);
+ return -ENOMEM;
+ }
+
+ port->param_buf_size = bufsz;
+ pr_debug("%s: param_buf[%p]; param_phys[%llx]; [%p]\n", __func__,
+ (void *)port->param_buf,
+ (u64)port->param_phys,
+ (void *)&port->param_phys);
+
+ rc = q6usm_memory_map(port->param_phys, (IN | OUT), size, 1,
+ usc->session, (uint32_t *)port->param_buf_mem_handle);
+ if (rc < 0) {
+ pr_err("%s: CMD Memory_map failed\n", __func__);
+ mutex_unlock(&usc->cmd_lock);
+ q6usm_us_client_buf_free(dir, usc);
+ q6usm_us_param_buf_free(dir, usc);
+ } else {
+ mutex_unlock(&usc->cmd_lock);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ uint32_t token;
+ uint32_t *payload = data->payload;
+
+ pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
+ __func__, payload[0], payload[1], data->opcode);
+ pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
+ __func__, data->token, data->payload_size,
+ data->src_port, data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ /* status field check */
+ if (payload[1]) {
+ pr_err("%s: wrong response[%d] on cmd [%d]\n",
+ __func__, payload[1], payload[0]);
+ } else {
+ token = data->token;
+ switch (payload[0]) {
+ case USM_CMD_SHARED_MEM_UNMAP_REGION:
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ case USM_CMD_SHARED_MEM_MAP_REGION:
+ /* For MEM_MAP, additional answer is waited, */
+ /* therfore, no wake-up here */
+ pr_debug("%s: cmd[0x%x]; result[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ default:
+ pr_debug("%s: wrong command[0x%x]\n",
+ __func__, payload[0]);
+ break;
+ }
+ }
+ } else {
+ if (data->opcode == USM_CMDRSP_SHARED_MEM_MAP_REGION) {
+ this_mmap.mem_handle = payload[0];
+ pr_debug("%s: memory map handle = 0x%x",
+ __func__, payload[0]);
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv)
+{
+ struct us_client *usc = (struct us_client *)priv;
+ unsigned long dsp_flags;
+ uint32_t *payload = data->payload;
+ uint32_t token = data->token;
+ uint32_t opcode = Q6USM_EVENT_UNDEF;
+
+ if (usc == NULL) {
+ pr_err("%s: client info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ /* status field check */
+ if (payload[1]) {
+ pr_err("%s: wrong response[%d] on cmd [%d]\n",
+ __func__, payload[1], payload[0]);
+ if (usc->cb)
+ usc->cb(data->opcode, token,
+ (uint32_t *)data->payload, usc->priv);
+ } else {
+ switch (payload[0]) {
+ case USM_SESSION_CMD_RUN:
+ case USM_STREAM_CMD_CLOSE:
+ if (token != usc->session) {
+ pr_err("%s: wrong token[%d]",
+ __func__, token);
+ break;
+ }
+ case USM_STREAM_CMD_OPEN_READ:
+ case USM_STREAM_CMD_OPEN_WRITE:
+ case USM_STREAM_CMD_SET_ENC_PARAM:
+ case USM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case USM_SESSION_CMD_SIGNAL_DETECT_MODE:
+ case USM_STREAM_CMD_SET_PARAM:
+ case USM_STREAM_CMD_GET_PARAM:
+ if (atomic_read(&usc->cmd_state)) {
+ atomic_set(&usc->cmd_state, 0);
+ wake_up(&usc->cmd_wait);
+ }
+ if (usc->cb)
+ usc->cb(data->opcode, token,
+ (uint32_t *)data->payload,
+ usc->priv);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case RESET_EVENTS: {
+ pr_err("%s: Reset event is received: %d %d\n",
+ __func__,
+ data->reset_event,
+ data->reset_proc);
+
+ opcode = RESET_EVENTS;
+
+ apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+
+ apr_reset(usc->apr);
+ usc->apr = NULL;
+
+ break;
+ }
+
+
+ case USM_DATA_EVENT_READ_DONE: {
+ struct us_port_data *port = &usc->port[OUT];
+
+ opcode = Q6USM_EVENT_READ_DONE;
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (payload[READDONE_IDX_STATUS]) {
+ pr_err("%s: wrong READDONE[%d]; token[%d]\n",
+ __func__,
+ payload[READDONE_IDX_STATUS],
+ token);
+ token = USM_WRONG_TOKEN;
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ }
+
+ if (port->expected_token != token) {
+ u32 cpu_buf = port->cpu_buf;
+ pr_err("%s: expected[%d] != token[%d]\n",
+ __func__, port->expected_token, token);
+ pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n",
+ __func__, port->dsp_buf, cpu_buf);
+
+ token = USM_WRONG_TOKEN;
+ /* To prevent data handle continiue */
+ port->expected_token = USM_WRONG_TOKEN;
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ } /* port->expected_token != data->token */
+
+ port->expected_token = token + 1;
+ if (port->expected_token == port->buf_cnt)
+ port->expected_token = 0;
+
+ /* gap support */
+ if (port->expected_token != port->cpu_buf) {
+ port->dsp_buf = port->expected_token;
+ token = port->dsp_buf; /* for callback */
+ } else
+ port->dsp_buf = token;
+
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ break;
+ } /* case USM_DATA_EVENT_READ_DONE */
+
+ case USM_DATA_EVENT_WRITE_DONE: {
+ struct us_port_data *port = &usc->port[IN];
+
+ opcode = Q6USM_EVENT_WRITE_DONE;
+ if (payload[WRITEDONE_IDX_STATUS]) {
+ pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
+ __func__,
+ payload[WRITEDONE_IDX_STATUS]);
+ break;
+ }
+
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ port->dsp_buf = token + 1;
+ if (port->dsp_buf == port->buf_cnt)
+ port->dsp_buf = 0;
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+ break;
+ } /* case USM_DATA_EVENT_WRITE_DONE */
+
+ case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: {
+ pr_debug("%s: US detect result: result=%d",
+ __func__,
+ payload[0]);
+ opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT;
+
+ break;
+ } /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */
+
+ default:
+ return 0;
+
+ } /* switch */
+
+ if (usc->cb)
+ usc->cb(opcode, token,
+ data->payload, usc->priv);
+
+ return 0;
+}
+
+uint32_t q6usm_get_virtual_address(int dir,
+ struct us_client *usc,
+ struct vm_area_struct *vms)
+{
+ uint32_t ret = 0xffffffff;
+
+ if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) {
+ struct us_port_data *port = &usc->port[dir];
+ int size = PAGE_ALIGN(port->buf_size * port->buf_cnt);
+ struct audio_buffer ab;
+
+ ab.phys = port->phys;
+ ab.data = port->data;
+ ab.used = 1;
+ ab.size = size;
+ ab.actual_size = size;
+ ab.handle = port->handle;
+ ab.client = port->client;
+
+ ret = msm_audio_ion_mmap(&ab, vms);
+
+ }
+ return ret;
+}
+
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg)
+{
+ mutex_lock(&usc->cmd_lock);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)usc->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_USM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = (usc->session << 8) | 0x0001;
+ hdr->dest_port = (usc->session << 8) | 0x0001;
+ if (cmd_flg) {
+ hdr->token = usc->session;
+ atomic_set(&usc->cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ mutex_unlock(&usc->cmd_lock);
+ return;
+}
+
+static uint32_t q6usm_ext2int_format(uint32_t ext_format)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ switch (ext_format) {
+ case FORMAT_USPS_EPOS:
+ int_format = US_POINT_EPOS_FORMAT_V2;
+ break;
+ case FORMAT_USRAW:
+ int_format = US_RAW_FORMAT_V2;
+ break;
+ case FORMAT_USPROX:
+ int_format = US_PROX_FORMAT_V4;
+ break;
+ case FORMAT_USGES_SYNC:
+ int_format = US_GES_SYNC_FORMAT;
+ break;
+ case FORMAT_USRAW_SYNC:
+ int_format = US_RAW_SYNC_FORMAT;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
+ break;
+ }
+
+ return int_format;
+}
+
+int q6usm_open_read(struct us_client *usc,
+ uint32_t format)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ int rc = 0x00;
+ struct usm_stream_cmd_open_read open;
+
+ pr_debug("%s: session[%d]", __func__, usc->session);
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: client or its apr is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+ open.hdr.opcode = USM_STREAM_CMD_OPEN_READ;
+ open.src_endpoint = 0; /* AFE */
+ open.pre_proc_top = 0; /* No preprocessing required */
+
+ int_format = q6usm_ext2int_format(format);
+ if (int_format == INVALID_FORMAT)
+ return -EINVAL;
+
+ open.uMode = STREAM_PRIORITY_NORMAL;
+ open.format = int_format;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj;
+ struct usm_stream_cmd_encdec_cfg_blk *enc_cfg = &enc_cfg_obj;
+ int rc = 0;
+ uint32_t total_cfg_size =
+ sizeof(struct usm_stream_cmd_encdec_cfg_blk);
+ uint32_t round_params_size = 0;
+ uint8_t is_allocated = 0;
+
+
+ if ((usc == NULL) || (us_cfg == NULL)) {
+ pr_err("%s: wrong input", __func__);
+ return -EINVAL;
+ }
+
+ int_format = q6usm_ext2int_format(us_cfg->format_id);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong input format[%d]",
+ __func__, us_cfg->format_id);
+ return -EINVAL;
+ }
+
+ /* Transparent configuration data is after enc_cfg */
+ /* Integer number of u32s is requred */
+ round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+ if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+ /* Dynamic allocated encdec_cfg_blk is required */
+ /* static part use */
+ round_params_size -= USM_MAX_CFG_DATA_SIZE;
+ total_cfg_size += round_params_size;
+ enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+ if (enc_cfg == NULL) {
+ pr_err("%s: enc_cfg[%d] allocation failed\n",
+ __func__, total_cfg_size);
+ return -ENOMEM;
+ }
+ is_allocated = 1;
+ } else
+ round_params_size = 0;
+
+ q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size, true);
+
+ enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM;
+ enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK;
+ enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+
+ round_params_size;
+ enc_cfg->enc_blk.frames_per_buf = 1;
+ enc_cfg->enc_blk.format_id = int_format;
+ enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+
+ USM_MAX_CFG_DATA_SIZE +
+ round_params_size;
+ memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common),
+ sizeof(struct usm_cfg_common));
+
+ /* Transparent data copy */
+ memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params,
+ us_cfg->params_size);
+ pr_debug("%s: cfg_size[%d], params_size[%d]\n",
+ __func__,
+ enc_cfg->enc_blk.cfg_size,
+ us_cfg->params_size);
+ pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n",
+ __func__,
+ enc_cfg->enc_blk.transp_data[0],
+ enc_cfg->enc_blk.transp_data[1],
+ enc_cfg->enc_blk.transp_data[2],
+ enc_cfg->enc_blk.transp_data[3],
+ enc_cfg->enc_blk.transp_data[4],
+ enc_cfg->enc_blk.transp_data[5],
+ enc_cfg->enc_blk.transp_data[6],
+ enc_cfg->enc_blk.transp_data[7]
+ );
+ pr_debug("%s: srate:%d, ch=%d, bps= %d;\n",
+ __func__, enc_cfg->enc_blk.cfg_common.sample_rate,
+ enc_cfg->enc_blk.cfg_common.ch_cfg,
+ enc_cfg->enc_blk.cfg_common.bits_per_sample);
+ pr_debug("dmap:[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]; dev_id=0x%x\n",
+ enc_cfg->enc_blk.cfg_common.data_map[0],
+ enc_cfg->enc_blk.cfg_common.data_map[1],
+ enc_cfg->enc_blk.cfg_common.data_map[2],
+ enc_cfg->enc_blk.cfg_common.data_map[3],
+ enc_cfg->enc_blk.cfg_common.data_map[4],
+ enc_cfg->enc_blk.cfg_common.data_map[5],
+ enc_cfg->enc_blk.cfg_common.data_map[6],
+ enc_cfg->enc_blk.cfg_common.data_map[7],
+ enc_cfg->enc_blk.cfg_common.dev_id);
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg->hdr.opcode);
+ } else
+ rc = 0;
+
+fail_cmd:
+ if (is_allocated == 1)
+ kfree(enc_cfg);
+
+ return rc;
+}
+
+int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_media_format_update dec_cfg_obj;
+ struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj;
+
+ int rc = 0;
+ uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update);
+ uint32_t round_params_size = 0;
+ uint8_t is_allocated = 0;
+
+
+ if ((usc == NULL) || (us_cfg == NULL)) {
+ pr_err("%s: wrong input", __func__);
+ return -EINVAL;
+ }
+
+ int_format = q6usm_ext2int_format(us_cfg->format_id);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong input format[%d]",
+ __func__, us_cfg->format_id);
+ return -EINVAL;
+ }
+
+ /* Transparent configuration data is after enc_cfg */
+ /* Integer number of u32s is requred */
+ round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+ if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+ /* Dynamic allocated encdec_cfg_blk is required */
+ /* static part use */
+ round_params_size -= USM_MAX_CFG_DATA_SIZE;
+ total_cfg_size += round_params_size;
+ dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+ if (dec_cfg == NULL) {
+ pr_err("%s:dec_cfg[%d] allocation failed\n",
+ __func__, total_cfg_size);
+ return -ENOMEM;
+ }
+ is_allocated = 1;
+ } else { /* static transp_data is enough */
+ round_params_size = 0;
+ }
+
+ q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size, true);
+
+ dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+ dec_cfg->format_id = int_format;
+ dec_cfg->cfg_size = sizeof(struct usm_cfg_common) +
+ USM_MAX_CFG_DATA_SIZE +
+ round_params_size;
+ memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common),
+ sizeof(struct usm_cfg_common));
+ /* Transparent data copy */
+ memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size);
+ pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n",
+ __func__,
+ dec_cfg->cfg_size,
+ us_cfg->params_size,
+ dec_cfg->transp_data[0],
+ dec_cfg->transp_data[1],
+ dec_cfg->transp_data[2],
+ dec_cfg->transp_data[3]
+ );
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, dec_cfg->hdr.opcode);
+ } else
+ rc = 0;
+
+fail_cmd:
+ if (is_allocated == 1)
+ kfree(dec_cfg);
+
+ return rc;
+}
+
+int q6usm_open_write(struct us_client *usc,
+ uint32_t format)
+{
+ int rc = 0;
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_cmd_open_write open;
+
+ pr_debug("%s: session[%d]", __func__, usc->session);
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+ open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE;
+
+ int_format = q6usm_ext2int_format(format);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong format[%d]", __func__, format);
+ return -EINVAL;
+ }
+
+ open.format = int_format;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s:open failed op[0x%x]rc[%d]\n", \
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ } else
+ rc = 0;
+
+fail_cmd:
+ return rc;
+}
+
+int q6usm_run(struct us_client *usc, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct usm_stream_cmd_run run;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6usm_add_hdr(usc, &run.hdr, sizeof(run), true);
+
+ run.hdr.opcode = USM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s: Commmand run failed[%d]\n", __func__, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for run success rc[%d]\n",
+ __func__, rc);
+ } else
+ rc = 0;
+
+fail_cmd:
+ return rc;
+}
+
+
+
+int q6usm_read(struct us_client *usc, uint32_t read_ind)
+{
+ struct usm_stream_cmd_read read;
+ struct us_port_data *port = NULL;
+ int rc = 0;
+ u32 read_counter = 0;
+ u32 loop_ind = 0;
+ u64 buf_addr = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[OUT];
+
+ if (read_ind > port->buf_cnt) {
+ pr_err("%s: wrong read_ind[%d]\n",
+ __func__, read_ind);
+ return -EINVAL;
+ }
+ if (read_ind == port->cpu_buf) {
+ pr_err("%s: no free region\n", __func__);
+ return 0;
+ }
+
+ if (read_ind > port->cpu_buf) { /* 1 range */
+ read_counter = read_ind - port->cpu_buf;
+ } else { /* 2 ranges */
+ read_counter = (port->buf_cnt - port->cpu_buf) + read_ind;
+ }
+
+ q6usm_add_hdr(usc, &read.hdr, sizeof(read), false);
+
+ read.hdr.opcode = USM_DATA_CMD_READ;
+ read.buf_size = port->buf_size;
+ buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf);
+ read.buf_addr_lsw = lower_32_bits(buf_addr);
+ read.buf_addr_msw = populate_upper_32_bits(buf_addr);
+ read.mem_map_handle = *((uint32_t *)(port->ext));
+
+ for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) {
+ u32 temp_cpu_buf = port->cpu_buf;
+
+ buf_addr = (u64)(port->phys) +
+ port->buf_size * (port->cpu_buf);
+ read.buf_addr_lsw = lower_32_bits(buf_addr);
+ read.buf_addr_msw = populate_upper_32_bits(buf_addr);
+ read.seq_id = port->cpu_buf;
+ read.hdr.token = port->cpu_buf;
+ read.counter = 1;
+
+ ++(port->cpu_buf);
+ if (port->cpu_buf == port->buf_cnt)
+ port->cpu_buf = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &read);
+
+ if (rc < 0) {
+ port->cpu_buf = temp_cpu_buf;
+
+ pr_err("%s:read op[0x%x]rc[%d]\n",
+ __func__, read.hdr.opcode, rc);
+ break;
+ } else
+ rc = 0;
+ } /* bufs loop */
+
+ return rc;
+}
+
+int q6usm_write(struct us_client *usc, uint32_t write_ind)
+{
+ int rc = 0;
+ struct usm_stream_cmd_write cmd_write;
+ struct us_port_data *port = NULL;
+ u32 current_dsp_buf = 0;
+ u64 buf_addr = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[IN];
+
+ current_dsp_buf = port->dsp_buf;
+ /* free region, caused by new dsp_buf report from DSP, */
+ /* can be only extended */
+ if (port->cpu_buf >= current_dsp_buf) {
+ /* 2 -part free region, including empty buffer */
+ if ((write_ind <= port->cpu_buf) &&
+ (write_ind > current_dsp_buf)) {
+ pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+ __func__, write_ind,
+ current_dsp_buf, port->cpu_buf);
+ return -EINVAL;
+ }
+ } else {
+ /* 1 -part free region */
+ if ((write_ind <= port->cpu_buf) ||
+ (write_ind > current_dsp_buf)) {
+ pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+ __func__, write_ind,
+ current_dsp_buf, port->cpu_buf);
+ return -EINVAL;
+ }
+ }
+
+ q6usm_add_hdr(usc, &cmd_write.hdr, sizeof(cmd_write), false);
+
+ cmd_write.hdr.opcode = USM_DATA_CMD_WRITE;
+ cmd_write.buf_size = port->buf_size;
+ buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf);
+ cmd_write.buf_addr_lsw = lower_32_bits(buf_addr);
+ cmd_write.buf_addr_msw = populate_upper_32_bits(buf_addr);
+ cmd_write.mem_map_handle = *((uint32_t *)(port->ext));
+ cmd_write.res0 = 0;
+ cmd_write.res1 = 0;
+ cmd_write.res2 = 0;
+
+ while (port->cpu_buf != write_ind) {
+ u32 temp_cpu_buf = port->cpu_buf;
+
+ buf_addr = (u64)(port->phys) +
+ port->buf_size * (port->cpu_buf);
+ cmd_write.buf_addr_lsw = lower_32_bits(buf_addr);
+ cmd_write.buf_addr_msw = populate_upper_32_bits(buf_addr);
+ cmd_write.seq_id = port->cpu_buf;
+ cmd_write.hdr.token = port->cpu_buf;
+
+ ++(port->cpu_buf);
+ if (port->cpu_buf == port->buf_cnt)
+ port->cpu_buf = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write);
+
+ if (rc < 0) {
+ port->cpu_buf = temp_cpu_buf;
+ pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n",
+ __func__, cmd_write.hdr.opcode,
+ rc, port->cpu_buf);
+ break;
+ }
+
+ rc = 0;
+ }
+
+ return rc;
+}
+
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region)
+{
+ struct us_port_data *port = NULL;
+ u32 cpu_buf = 0;
+
+ if ((usc == NULL) || !free_region) {
+ pr_err("%s: input data wrong\n", __func__);
+ return false;
+ }
+ port = &usc->port[IN];
+ cpu_buf = port->cpu_buf + 1;
+ if (cpu_buf == port->buf_cnt)
+ cpu_buf = 0;
+
+ *free_region = port->dsp_buf;
+
+ return cpu_buf == *free_region;
+}
+
+int q6usm_cmd(struct us_client *usc, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc = 0;
+ atomic_t *state;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6usm_add_hdr(usc, &hdr, sizeof(hdr), true);
+ switch (cmd) {
+ case CMD_CLOSE:
+ hdr.opcode = USM_STREAM_CMD_CLOSE;
+ state = &usc->cmd_state;
+ break;
+
+ default:
+ pr_err("%s:Invalid format[%d]\n", __func__, cmd);
+ goto fail_cmd;
+ }
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. waited for response opcode[0x%x]\n",
+ __func__, hdr.opcode);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6usm_set_us_detection(struct us_client *usc,
+ struct usm_session_cmd_detect_info *detect_info,
+ uint16_t detect_info_size)
+{
+ int rc = 0;
+
+ if ((usc == NULL) ||
+ (detect_info_size == 0) ||
+ (detect_info == NULL)) {
+ pr_err("%s: wrong input: usc=0x%p, inf_size=%d; info=0x%p",
+ __func__,
+ usc,
+ detect_info_size,
+ detect_info);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &detect_info->hdr, detect_info_size, true);
+
+ detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info);
+ if (rc < 0) {
+ pr_err("%s:Comamnd signal detect failed\n", __func__);
+ return -EINVAL;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n",
+ __func__, Q6USM_TIMEOUT_JIFFIES);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+int q6usm_set_us_stream_param(int dir, struct us_client *usc,
+ uint32_t module_id, uint32_t param_id, uint32_t buf_size)
+{
+ int rc = 0;
+ struct usm_stream_cmd_set_param cmd_set_param;
+ struct us_port_data *port = NULL;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[dir];
+
+ q6usm_add_hdr(usc, &cmd_set_param.hdr, sizeof(cmd_set_param), true);
+
+ cmd_set_param.hdr.opcode = USM_STREAM_CMD_SET_PARAM;
+ cmd_set_param.buf_size = buf_size;
+ cmd_set_param.buf_addr_msw = populate_upper_32_bits(port->param_phys);
+ cmd_set_param.buf_addr_lsw = lower_32_bits(port->param_phys);
+ cmd_set_param.mem_map_handle =
+ *((uint32_t *)(port->param_buf_mem_handle));
+ cmd_set_param.module_id = module_id;
+ cmd_set_param.param_id = param_id;
+ cmd_set_param.hdr.token = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_set_param);
+
+ if (rc < 0) {
+ pr_err("%s:write op[0x%x];rc[%d]\n",
+ __func__, cmd_set_param.hdr.opcode, rc);
+ }
+
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: CMD_SET_PARAM: timeout=%d\n",
+ __func__, Q6USM_TIMEOUT_JIFFIES);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+int q6usm_get_us_stream_param(int dir, struct us_client *usc,
+ uint32_t module_id, uint32_t param_id, uint32_t buf_size)
+{
+ int rc = 0;
+ struct usm_stream_cmd_get_param cmd_get_param;
+ struct us_port_data *port = NULL;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[dir];
+
+ q6usm_add_hdr(usc, &cmd_get_param.hdr, sizeof(cmd_get_param), true);
+
+ cmd_get_param.hdr.opcode = USM_STREAM_CMD_GET_PARAM;
+ cmd_get_param.buf_size = buf_size;
+ cmd_get_param.buf_addr_msw = populate_upper_32_bits(port->param_phys);
+ cmd_get_param.buf_addr_lsw = lower_32_bits(port->param_phys);
+ cmd_get_param.mem_map_handle =
+ *((uint32_t *)(port->param_buf_mem_handle));
+ cmd_get_param.module_id = module_id;
+ cmd_get_param.param_id = param_id;
+ cmd_get_param.hdr.token = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_get_param);
+
+ if (rc < 0) {
+ pr_err("%s:write op[0x%x];rc[%d]\n",
+ __func__, cmd_get_param.hdr.opcode, rc);
+ }
+
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: CMD_GET_PARAM: timeout=%d\n",
+ __func__, Q6USM_TIMEOUT_JIFFIES);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+static int __init q6usm_init(void)
+{
+ pr_debug("%s\n", __func__);
+ init_waitqueue_head(&this_mmap.cmd_wait);
+ memset(session, 0, sizeof(session));
+ return 0;
+}
+
+device_initcall(q6usm_init);
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h
new file mode 100644
index 000000000000..d45d1657c924
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h
@@ -0,0 +1,130 @@
+/* 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
+ * 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 __Q6_USM_H__
+#define __Q6_USM_H__
+
+#include <linux/qdsp6v2/apr_us.h>
+
+#define Q6USM_EVENT_UNDEF 0
+#define Q6USM_EVENT_READ_DONE 1
+#define Q6USM_EVENT_WRITE_DONE 2
+#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3
+
+/* cyclic buffer with 1 gap support */
+#define USM_MIN_BUF_CNT 3
+
+#define FORMAT_USPS_EPOS 0x00000000
+#define FORMAT_USRAW 0x00000001
+#define FORMAT_USPROX 0x00000002
+#define FORMAT_USGES_SYNC 0x00000003
+#define FORMAT_USRAW_SYNC 0x00000004
+#define INVALID_FORMAT 0xffffffff
+
+#define IN 0x000
+#define OUT 0x001
+
+#define USM_WRONG_TOKEN 0xffffffff
+#define USM_UNDEF_TOKEN 0xfffffffe
+
+#define CMD_CLOSE 0x0004
+
+/* bit 0:1 represents priority of stream */
+#define STREAM_PRIORITY_NORMAL 0x0000
+#define STREAM_PRIORITY_LOW 0x0001
+#define STREAM_PRIORITY_HIGH 0x0002
+
+/* bit 4 represents META enable of encoded data buffer */
+#define BUFFER_META_ENABLE 0x0010
+
+struct us_port_data {
+ dma_addr_t phys;
+ /* cyclic region of buffers with 1 gap */
+ void *data;
+ /* number of buffers in the region */
+ uint32_t buf_cnt;
+ /* size of buffer */
+ size_t buf_size;
+ /* write index */
+ uint32_t dsp_buf;
+ /* read index */
+ uint32_t cpu_buf;
+ /* expected token from dsp */
+ uint32_t expected_token;
+ /* read or write locks */
+ struct mutex lock;
+ spinlock_t dsp_lock;
+ /* ION memory handle */
+ struct ion_handle *handle;
+ /* ION memory client */
+ struct ion_client *client;
+ /* extended parameters, related to q6 variants */
+ void *ext;
+ /* physical address of parameter buffer */
+ dma_addr_t param_phys;
+ /* buffer which stores the parameter data */
+ void *param_buf;
+ /* size of parameter buffer */
+ uint32_t param_buf_size;
+ /* parameter buffer memory handle */
+ void *param_buf_mem_handle;
+ /* ION memory handle for parameter buffer */
+ struct ion_handle *param_handle;
+ /* ION memory client for parameter buffer */
+ struct ion_client *param_client;
+};
+
+struct us_client {
+ int session;
+ /* idx:1 out port, 0: in port*/
+ struct us_port_data port[2];
+
+ struct apr_svc *apr;
+ struct mutex cmd_lock;
+
+ atomic_t cmd_state;
+ atomic_t eos_state;
+ wait_queue_head_t cmd_wait;
+
+ void (*cb)(uint32_t, uint32_t, uint32_t *, void *);
+ void *priv;
+};
+
+int q6usm_run(struct us_client *usc, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts);
+int q6usm_cmd(struct us_client *usc, int cmd);
+int q6usm_us_client_buf_alloc(unsigned int dir, struct us_client *usc,
+ unsigned int bufsz, unsigned int bufcnt);
+int q6usm_us_param_buf_alloc(unsigned int dir, struct us_client *usc,
+ unsigned int bufsz);
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg);
+int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg);
+int q6usm_read(struct us_client *usc, uint32_t read_ind);
+struct us_client *q6usm_us_client_alloc(
+ void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
+ void *priv);
+int q6usm_open_read(struct us_client *usc, uint32_t format);
+void q6usm_us_client_free(struct us_client *usc);
+uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc,
+ struct vm_area_struct *vms);
+int q6usm_open_write(struct us_client *usc, uint32_t format);
+int q6usm_write(struct us_client *usc, uint32_t write_ind);
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region);
+int q6usm_set_us_detection(struct us_client *usc,
+ struct usm_session_cmd_detect_info *detect_info,
+ uint16_t detect_info_size);
+int q6usm_set_us_stream_param(int dir, struct us_client *usc,
+ uint32_t module_id, uint32_t param_id, uint32_t buf_size);
+int q6usm_get_us_stream_param(int dir, struct us_client *usc,
+ uint32_t module_id, uint32_t param_id, uint32_t buf_size);
+
+#endif /* __Q6_USM_H__ */
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
new file mode 100644
index 000000000000..7ac0add368fb
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
@@ -0,0 +1,2365 @@
+/* Copyright (c) 2011-2015, 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/compat.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/time.h>
+#include <linux/kmemleak.h>
+#include <sound/apr_audio.h>
+#include <linux/qdsp6v2/usf.h>
+#include "q6usm.h"
+#include "usfcdev.h"
+
+/* The driver version*/
+#define DRV_VERSION "1.7.1"
+#define USF_VERSION_ID 0x0171
+
+/* Standard timeout in the asynchronous ops */
+#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
+
+/* Undefined USF device */
+#define USF_UNDEF_DEV_ID 0xffff
+
+/* TX memory mapping flag */
+#define USF_VM_READ 1
+/* RX memory mapping flag */
+#define USF_VM_WRITE 2
+
+/* Number of events, copied from the user space to kernel one */
+#define USF_EVENTS_PORTION_SIZE 20
+
+/* Indexes in range definitions */
+#define MIN_IND 0
+#define MAX_IND 1
+
+/* The coordinates indexes */
+#define X_IND 0
+#define Y_IND 1
+#define Z_IND 2
+
+/* Shared memory limits */
+/* max_buf_size = (port_size(65535*2) * port_num(8) * group_size(3) */
+#define USF_MAX_BUF_SIZE 3145680
+#define USF_MAX_BUF_NUM 32
+
+/* Place for opreation result, received from QDSP6 */
+#define APR_RESULT_IND 1
+
+/* Place for US detection result, received from QDSP6 */
+#define APR_US_DETECT_RESULT_IND 0
+
+#define BITS_IN_BYTE 8
+
+/* The driver states */
+enum usf_state_type {
+ USF_IDLE_STATE,
+ USF_OPENED_STATE,
+ USF_CONFIGURED_STATE,
+ USF_WORK_STATE,
+ USF_ADSP_RESTART_STATE,
+ USF_ERROR_STATE
+};
+
+/* The US detection status upon FW/HW based US detection results */
+enum usf_us_detect_type {
+ USF_US_DETECT_UNDEF,
+ USF_US_DETECT_YES,
+ USF_US_DETECT_NO
+};
+
+struct usf_xx_type {
+ /* Name of the client - event calculator */
+ char client_name[USF_MAX_CLIENT_NAME_SIZE];
+ /* The driver state in TX or RX direction */
+ enum usf_state_type usf_state;
+ /* wait for q6 events mechanism */
+ wait_queue_head_t wait;
+ /* IF with q6usm info */
+ struct us_client *usc;
+ /* Q6:USM' Encoder/decoder configuration */
+ struct us_encdec_cfg encdec_cfg;
+ /* Shared buffer (with Q6:USM) size */
+ uint32_t buffer_size;
+ /* Number of the shared buffers (with Q6:USM) */
+ uint32_t buffer_count;
+ /* Shared memory (Cyclic buffer with 1 gap) control */
+ uint32_t new_region;
+ uint32_t prev_region;
+ /* Q6:USM's events handler */
+ void (*cb)(uint32_t, uint32_t, uint32_t *, void *);
+ /* US detection result */
+ enum usf_us_detect_type us_detect_type;
+ /* User's update info isn't acceptable */
+ u8 user_upd_info_na;
+};
+
+struct usf_type {
+ /* TX device component configuration & control */
+ struct usf_xx_type usf_tx;
+ /* RX device component configuration & control */
+ struct usf_xx_type usf_rx;
+ /* Index into the opened device container */
+ /* To prevent mutual usage of the same device */
+ uint16_t dev_ind;
+ /* Event types, supported by device */
+ uint16_t event_types;
+ /* The input devices are "input" module registered clients */
+ struct input_dev *input_ifs[USF_MAX_EVENT_IND];
+ /* Bitmap of types of events, conflicting to USF's ones */
+ uint16_t conflicting_event_types;
+ /* Bitmap of types of events from devs, conflicting with USF */
+ uint16_t conflicting_event_filters;
+ /* The requested buttons bitmap */
+ uint16_t req_buttons_bitmap;
+};
+
+struct usf_input_dev_type {
+ /* Input event type, supported by the input device */
+ uint16_t event_type;
+ /* Input device name */
+ const char *input_dev_name;
+ /* Input device registration function */
+ int (*prepare_dev)(uint16_t, struct usf_type *,
+ struct us_input_info_type *,
+ const char *);
+ /* Input event notification function */
+ void (*notify_event)(struct usf_type *,
+ uint16_t,
+ struct usf_event_type *
+ );
+};
+
+
+/* The MAX number of the supported devices */
+#define MAX_DEVS_NUMBER 1
+
+/*
+ * code for a special button that is used to show/hide a
+ * hovering cursor in the input framework. Must be in
+ * sync with the button code definition in the framework
+ * (EventHub.h)
+ */
+#define BTN_USF_HOVERING_CURSOR 0x230
+
+/* Supported buttons container */
+static const int s_button_map[] = {
+ BTN_STYLUS,
+ BTN_STYLUS2,
+ BTN_TOOL_PEN,
+ BTN_TOOL_RUBBER,
+ BTN_TOOL_FINGER,
+ BTN_USF_HOVERING_CURSOR
+};
+
+/* The opened devices container */
+static int s_opened_devs[MAX_DEVS_NUMBER];
+
+#define USF_NAME_PREFIX "usf_"
+#define USF_NAME_PREFIX_SIZE 4
+
+
+static struct input_dev *allocate_dev(uint16_t ind, const char *name)
+{
+ struct input_dev *in_dev = input_allocate_device();
+
+ if (in_dev == NULL) {
+ pr_err("%s: input_allocate_device() failed\n", __func__);
+ } else {
+ /* Common part configuration */
+ in_dev->name = name;
+ in_dev->phys = NULL;
+ in_dev->id.bustype = BUS_HOST;
+ in_dev->id.vendor = 0x0001;
+ in_dev->id.product = 0x0001;
+ in_dev->id.version = USF_VERSION_ID;
+ }
+ return in_dev;
+}
+
+static int prepare_tsc_input_device(uint16_t ind,
+ struct usf_type *usf_info,
+ struct us_input_info_type *input_info,
+ const char *name)
+{
+ int i = 0;
+
+ int num_buttons = min(ARRAY_SIZE(s_button_map),
+ sizeof(input_info->req_buttons_bitmap) *
+ BITS_IN_BYTE);
+ uint16_t max_buttons_bitmap = ((1 << ARRAY_SIZE(s_button_map)) - 1);
+
+ struct input_dev *in_dev = allocate_dev(ind, name);
+ if (in_dev == NULL)
+ return -ENOMEM;
+
+ if (input_info->req_buttons_bitmap > max_buttons_bitmap) {
+ pr_err("%s: Requested buttons[%d] exceeds max buttons available[%d]\n",
+ __func__,
+ input_info->req_buttons_bitmap,
+ max_buttons_bitmap);
+ input_free_device(in_dev);
+ return -EINVAL;
+ }
+
+ usf_info->input_ifs[ind] = in_dev;
+ usf_info->req_buttons_bitmap =
+ input_info->req_buttons_bitmap;
+ in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ for (i = 0; i < num_buttons; i++)
+ if (input_info->req_buttons_bitmap & (1 << i))
+ in_dev->keybit[BIT_WORD(s_button_map[i])] |=
+ BIT_MASK(s_button_map[i]);
+
+ input_set_abs_params(in_dev, ABS_X,
+ input_info->tsc_x_dim[MIN_IND],
+ input_info->tsc_x_dim[MAX_IND],
+ 0, 0);
+ input_set_abs_params(in_dev, ABS_Y,
+ input_info->tsc_y_dim[MIN_IND],
+ input_info->tsc_y_dim[MAX_IND],
+ 0, 0);
+ input_set_abs_params(in_dev, ABS_DISTANCE,
+ input_info->tsc_z_dim[MIN_IND],
+ input_info->tsc_z_dim[MAX_IND],
+ 0, 0);
+
+ input_set_abs_params(in_dev, ABS_PRESSURE,
+ input_info->tsc_pressure[MIN_IND],
+ input_info->tsc_pressure[MAX_IND],
+ 0, 0);
+
+ input_set_abs_params(in_dev, ABS_TILT_X,
+ input_info->tsc_x_tilt[MIN_IND],
+ input_info->tsc_x_tilt[MAX_IND],
+ 0, 0);
+ input_set_abs_params(in_dev, ABS_TILT_Y,
+ input_info->tsc_y_tilt[MIN_IND],
+ input_info->tsc_y_tilt[MAX_IND],
+ 0, 0);
+
+ return 0;
+}
+
+static int prepare_mouse_input_device(uint16_t ind, struct usf_type *usf_info,
+ struct us_input_info_type *input_info,
+ const char *name)
+{
+ struct input_dev *in_dev = allocate_dev(ind, name);
+
+ if (in_dev == NULL)
+ return -ENOMEM;
+
+ usf_info->input_ifs[ind] = in_dev;
+ in_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+
+ in_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) |
+ BIT_MASK(BTN_MIDDLE);
+ in_dev->relbit[0] = BIT_MASK(REL_X) |
+ BIT_MASK(REL_Y) |
+ BIT_MASK(REL_Z);
+
+ return 0;
+}
+
+static int prepare_keyboard_input_device(
+ uint16_t ind,
+ struct usf_type *usf_info,
+ struct us_input_info_type *input_info,
+ const char *name)
+{
+ struct input_dev *in_dev = allocate_dev(ind, name);
+
+ if (in_dev == NULL)
+ return -ENOMEM;
+
+ usf_info->input_ifs[ind] = in_dev;
+ in_dev->evbit[0] |= BIT_MASK(EV_KEY);
+ /* All keys are permitted */
+ memset(in_dev->keybit, 0xff, sizeof(in_dev->keybit));
+
+ return 0;
+}
+
+static void notify_tsc_event(struct usf_type *usf_info,
+ uint16_t if_ind,
+ struct usf_event_type *event)
+
+{
+ int i = 0;
+ int num_buttons = min(ARRAY_SIZE(s_button_map),
+ sizeof(usf_info->req_buttons_bitmap) *
+ BITS_IN_BYTE);
+
+ struct input_dev *input_if = usf_info->input_ifs[if_ind];
+ struct point_event_type *pe = &(event->event_data.point_event);
+
+ input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]);
+ input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]);
+ input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]);
+
+ input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]);
+ input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]);
+
+ input_report_abs(input_if, ABS_PRESSURE, pe->pressure);
+ input_report_key(input_if, BTN_TOUCH, !!(pe->pressure));
+
+ for (i = 0; i < num_buttons; i++) {
+ uint16_t mask = (1 << i),
+ btn_state = !!(pe->buttons_state_bitmap & mask);
+ if (usf_info->req_buttons_bitmap & mask)
+ input_report_key(input_if, s_button_map[i], btn_state);
+ }
+
+ input_sync(input_if);
+
+ pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d], buttons[%d]\n",
+ __func__,
+ pe->coordinates[X_IND],
+ pe->coordinates[Y_IND],
+ pe->coordinates[Z_IND],
+ pe->inclinations[X_IND],
+ pe->inclinations[Y_IND],
+ pe->pressure,
+ pe->buttons_state_bitmap);
+}
+
+static void notify_mouse_event(struct usf_type *usf_info,
+ uint16_t if_ind,
+ struct usf_event_type *event)
+{
+ struct input_dev *input_if = usf_info->input_ifs[if_ind];
+ struct mouse_event_type *me = &(event->event_data.mouse_event);
+
+ input_report_rel(input_if, REL_X, me->rels[X_IND]);
+ input_report_rel(input_if, REL_Y, me->rels[Y_IND]);
+ input_report_rel(input_if, REL_Z, me->rels[Z_IND]);
+
+ input_report_key(input_if, BTN_LEFT,
+ me->buttons_states & USF_BUTTON_LEFT_MASK);
+ input_report_key(input_if, BTN_MIDDLE,
+ me->buttons_states & USF_BUTTON_MIDDLE_MASK);
+ input_report_key(input_if, BTN_RIGHT,
+ me->buttons_states & USF_BUTTON_RIGHT_MASK);
+
+ input_sync(input_if);
+
+ pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n",
+ __func__, me->rels[X_IND],
+ me->rels[Y_IND], me->buttons_states);
+}
+
+static void notify_key_event(struct usf_type *usf_info,
+ uint16_t if_ind,
+ struct usf_event_type *event)
+{
+ struct input_dev *input_if = usf_info->input_ifs[if_ind];
+ struct key_event_type *ke = &(event->event_data.key_event);
+
+ input_report_key(input_if, ke->key, ke->key_state);
+ input_sync(input_if);
+ pr_debug("%s: key event: key[%d], state[%d]\n",
+ __func__,
+ ke->key,
+ ke->key_state);
+
+}
+
+static struct usf_input_dev_type s_usf_input_devs[] = {
+ {USF_TSC_EVENT, "usf_tsc",
+ prepare_tsc_input_device, notify_tsc_event},
+ {USF_TSC_PTR_EVENT, "usf_tsc_ptr",
+ prepare_tsc_input_device, notify_tsc_event},
+ {USF_MOUSE_EVENT, "usf_mouse",
+ prepare_mouse_input_device, notify_mouse_event},
+ {USF_KEYBOARD_EVENT, "usf_kb",
+ prepare_keyboard_input_device, notify_key_event},
+ {USF_TSC_EXT_EVENT, "usf_tsc_ext",
+ prepare_tsc_input_device, notify_tsc_event},
+};
+
+static void usf_rx_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv;
+
+ if (usf_xx == NULL) {
+ pr_err("%s: the private data is NULL\n", __func__);
+ return;
+ }
+
+ switch (opcode) {
+ case Q6USM_EVENT_WRITE_DONE:
+ wake_up(&usf_xx->wait);
+ break;
+
+ case RESET_EVENTS:
+ pr_err("%s: received RESET_EVENTS\n", __func__);
+ usf_xx->usf_state = USF_ADSP_RESTART_STATE;
+ wake_up(&usf_xx->wait);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void usf_tx_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv;
+
+ if (usf_xx == NULL) {
+ pr_err("%s: the private data is NULL\n", __func__);
+ return;
+ }
+
+ switch (opcode) {
+ case Q6USM_EVENT_READ_DONE:
+ if (token == USM_WRONG_TOKEN)
+ usf_xx->usf_state = USF_ERROR_STATE;
+ usf_xx->new_region = token;
+ wake_up(&usf_xx->wait);
+ break;
+
+ case Q6USM_EVENT_SIGNAL_DETECT_RESULT:
+ usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ?
+ USF_US_DETECT_YES :
+ USF_US_DETECT_NO;
+
+ wake_up(&usf_xx->wait);
+ break;
+
+ case APR_BASIC_RSP_RESULT:
+ if (payload[APR_RESULT_IND]) {
+ usf_xx->usf_state = USF_ERROR_STATE;
+ usf_xx->new_region = USM_WRONG_TOKEN;
+ wake_up(&usf_xx->wait);
+ }
+ break;
+
+ case RESET_EVENTS:
+ pr_err("%s: received RESET_EVENTS\n", __func__);
+ usf_xx->usf_state = USF_ADSP_RESTART_STATE;
+ wake_up(&usf_xx->wait);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void release_xx(struct usf_xx_type *usf_xx)
+{
+ if (usf_xx != NULL) {
+ if (usf_xx->usc) {
+ q6usm_us_client_free(usf_xx->usc);
+ usf_xx->usc = NULL;
+ }
+
+ if (usf_xx->encdec_cfg.params != NULL) {
+ kfree(usf_xx->encdec_cfg.params);
+ usf_xx->encdec_cfg.params = NULL;
+ }
+ }
+}
+
+static void usf_disable(struct usf_xx_type *usf_xx)
+{
+ if (usf_xx != NULL) {
+ if ((usf_xx->usf_state != USF_IDLE_STATE) &&
+ (usf_xx->usf_state != USF_OPENED_STATE)) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ usf_xx->usf_state = USF_OPENED_STATE;
+ wake_up(&usf_xx->wait);
+ }
+ release_xx(usf_xx);
+ }
+}
+
+static int config_xx(struct usf_xx_type *usf_xx, struct us_xx_info_type *config)
+{
+ int rc = 0;
+ uint16_t data_map_size = 0;
+ uint16_t min_map_size = 0;
+
+ if ((usf_xx == NULL) ||
+ (config == NULL))
+ return -EINVAL;
+
+ if ((config->buf_size == 0) ||
+ (config->buf_size > USF_MAX_BUF_SIZE) ||
+ (config->buf_num == 0) ||
+ (config->buf_num > USF_MAX_BUF_NUM)) {
+ pr_err("%s: wrong params: buf_size=%d; buf_num=%d\n",
+ __func__, config->buf_size, config->buf_num);
+ return -EINVAL;
+ }
+
+ data_map_size = sizeof(usf_xx->encdec_cfg.cfg_common.data_map);
+ min_map_size = min(data_map_size, config->port_cnt);
+
+ if (config->client_name != NULL) {
+ if (strncpy_from_user(usf_xx->client_name,
+ (char __user *)config->client_name,
+ sizeof(usf_xx->client_name) - 1) < 0) {
+ pr_err("%s: get client name failed\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("%s: name=%s; buf_size:%d; dev_id:0x%x; sample_rate:%d\n",
+ __func__, usf_xx->client_name, config->buf_size,
+ config->dev_id, config->sample_rate);
+
+ pr_debug("%s: buf_num:%d; format:%d; port_cnt:%d; data_size=%d\n",
+ __func__, config->buf_num, config->stream_format,
+ config->port_cnt, config->params_data_size);
+
+ pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d,\n",
+ __func__,
+ config->port_id[0],
+ config->port_id[1],
+ config->port_id[2],
+ config->port_id[3],
+ config->port_id[4]);
+
+ pr_debug("id[5]=%d, id[6]=%d, id[7]=%d\n",
+ config->port_id[5],
+ config->port_id[6],
+ config->port_id[7]);
+
+ /* q6usm allocation & configuration */
+ usf_xx->buffer_size = config->buf_size;
+ usf_xx->buffer_count = config->buf_num;
+ usf_xx->encdec_cfg.cfg_common.bits_per_sample =
+ config->bits_per_sample;
+ usf_xx->encdec_cfg.cfg_common.sample_rate = config->sample_rate;
+ /* AFE port e.g. AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX */
+ usf_xx->encdec_cfg.cfg_common.dev_id = config->dev_id;
+
+ usf_xx->encdec_cfg.cfg_common.ch_cfg = config->port_cnt;
+ memcpy((void *)&usf_xx->encdec_cfg.cfg_common.data_map,
+ (void *)config->port_id,
+ min_map_size);
+
+ if (rc) {
+ pr_err("%s: ports offsets copy failure\n", __func__);
+ return -EINVAL;
+ }
+
+ usf_xx->encdec_cfg.format_id = config->stream_format;
+ usf_xx->encdec_cfg.params_size = config->params_data_size;
+ usf_xx->user_upd_info_na = 1; /* it's used in US_GET_TX_UPDATE */
+
+ if (config->params_data_size > 0) { /* transparent data copy */
+ usf_xx->encdec_cfg.params = kzalloc(config->params_data_size,
+ GFP_KERNEL);
+ /* False memory leak here - pointer in packed struct *
+ * is undetected by kmemleak tool */
+ kmemleak_ignore(usf_xx->encdec_cfg.params);
+ if (usf_xx->encdec_cfg.params == NULL) {
+ pr_err("%s: params memory alloc[%d] failure\n",
+ __func__,
+ config->params_data_size);
+ return -ENOMEM;
+ }
+ rc = copy_from_user(usf_xx->encdec_cfg.params,
+ (uint8_t __user *)config->params_data,
+ config->params_data_size);
+ if (rc) {
+ pr_err("%s: transparent data copy failure\n",
+ __func__);
+ kfree(usf_xx->encdec_cfg.params);
+ usf_xx->encdec_cfg.params = NULL;
+ return -EFAULT;
+ }
+ pr_debug("%s: params_size[%d]; params[%d,%d,%d,%d, %d]\n",
+ __func__,
+ config->params_data_size,
+ usf_xx->encdec_cfg.params[0],
+ usf_xx->encdec_cfg.params[1],
+ usf_xx->encdec_cfg.params[2],
+ usf_xx->encdec_cfg.params[3],
+ usf_xx->encdec_cfg.params[4]
+ );
+ }
+
+ usf_xx->usc = q6usm_us_client_alloc(usf_xx->cb, (void *)usf_xx);
+ if (!usf_xx->usc) {
+ pr_err("%s: Could not allocate q6usm client\n", __func__);
+ rc = -EFAULT;
+ }
+
+ return rc;
+}
+
+static bool usf_match(uint16_t event_type_ind, struct input_dev *dev)
+{
+ bool rc = false;
+
+ rc = (event_type_ind < MAX_EVENT_TYPE_NUM) &&
+ ((dev->name == NULL) ||
+ strncmp(dev->name, USF_NAME_PREFIX, USF_NAME_PREFIX_SIZE));
+ pr_debug("%s: name=[%s]; rc=%d\n",
+ __func__, dev->name, rc);
+
+ return rc;
+}
+
+static bool usf_register_conflicting_events(uint16_t event_types)
+{
+ bool rc = true;
+ uint16_t ind = 0;
+ uint16_t mask = 1;
+
+ for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) {
+ if (event_types & mask) {
+ rc = usfcdev_register(ind, usf_match);
+ if (!rc)
+ break;
+ }
+ mask = mask << 1;
+ }
+
+ return rc;
+}
+
+static void usf_unregister_conflicting_events(uint16_t event_types)
+{
+ uint16_t ind = 0;
+ uint16_t mask = 1;
+
+ for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) {
+ if (event_types & mask)
+ usfcdev_unregister(ind);
+ mask = mask << 1;
+ }
+}
+
+static void usf_set_event_filters(struct usf_type *usf, uint16_t event_filters)
+{
+ uint16_t ind = 0;
+ uint16_t mask = 1;
+
+ if (usf->conflicting_event_filters != event_filters) {
+ for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) {
+ if (usf->conflicting_event_types & mask)
+ usfcdev_set_filter(ind, event_filters&mask);
+ mask = mask << 1;
+ }
+ usf->conflicting_event_filters = event_filters;
+ }
+}
+
+static int register_input_device(struct usf_type *usf_info,
+ struct us_input_info_type *input_info)
+{
+ int rc = 0;
+ bool ret = true;
+ uint16_t ind = 0;
+
+ if ((usf_info == NULL) ||
+ (input_info == NULL) ||
+ !(input_info->event_types & USF_ALL_EVENTS)) {
+ pr_err("%s: wrong input parameter(s)\n", __func__);
+ return -EINVAL;
+ }
+
+ for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) {
+ if (usf_info->input_ifs[ind] != NULL) {
+ pr_err("%s: input_if[%d] is already allocated\n",
+ __func__, ind);
+ return -EFAULT;
+ }
+ if ((input_info->event_types &
+ s_usf_input_devs[ind].event_type) &&
+ s_usf_input_devs[ind].prepare_dev) {
+ rc = (*s_usf_input_devs[ind].prepare_dev)(
+ ind,
+ usf_info,
+ input_info,
+ s_usf_input_devs[ind].input_dev_name);
+ if (rc)
+ return rc;
+
+ rc = input_register_device(usf_info->input_ifs[ind]);
+ if (rc) {
+ pr_err("%s: input_reg_dev() failed; rc=%d\n",
+ __func__, rc);
+ input_free_device(usf_info->input_ifs[ind]);
+ usf_info->input_ifs[ind] = NULL;
+ } else {
+ usf_info->event_types |=
+ s_usf_input_devs[ind].event_type;
+ pr_debug("%s: input device[%s] was registered\n",
+ __func__,
+ s_usf_input_devs[ind].input_dev_name);
+ }
+ } /* supported event */
+ } /* event types loop */
+
+ ret = usf_register_conflicting_events(
+ input_info->conflicting_event_types);
+ if (ret)
+ usf_info->conflicting_event_types =
+ input_info->conflicting_event_types;
+
+ return 0;
+}
+
+
+static void handle_input_event(struct usf_type *usf_info,
+ uint16_t event_counter,
+ struct usf_event_type __user *event)
+{
+ uint16_t ind = 0;
+ uint16_t events_num = 0;
+ struct usf_event_type usf_events[USF_EVENTS_PORTION_SIZE];
+ int rc = 0;
+
+ if ((usf_info == NULL) ||
+ (event == NULL) || (!event_counter)) {
+ return;
+ }
+
+ while (event_counter > 0) {
+ if (event_counter > USF_EVENTS_PORTION_SIZE) {
+ events_num = USF_EVENTS_PORTION_SIZE;
+ event_counter -= USF_EVENTS_PORTION_SIZE;
+ } else {
+ events_num = event_counter;
+ event_counter = 0;
+ }
+ rc = copy_from_user(usf_events,
+ (struct usf_event_type __user *)event,
+ events_num * sizeof(struct usf_event_type));
+ if (rc) {
+ pr_err("%s: copy upd_rx_info from user; rc=%d\n",
+ __func__, rc);
+ return;
+ }
+ for (ind = 0; ind < events_num; ++ind) {
+ struct usf_event_type *p_event = &usf_events[ind];
+ uint16_t if_ind = p_event->event_type_ind;
+
+ if ((if_ind >= USF_MAX_EVENT_IND) ||
+ (usf_info->input_ifs[if_ind] == NULL))
+ continue; /* event isn't supported */
+
+ if (s_usf_input_devs[if_ind].notify_event)
+ (*s_usf_input_devs[if_ind].notify_event)(
+ usf_info,
+ if_ind,
+ p_event);
+ } /* loop in the portion */
+ } /* all events loop */
+}
+
+static int usf_start_tx(struct usf_xx_type *usf_xx)
+{
+ int rc = q6usm_run(usf_xx->usc, 0, 0, 0);
+
+ pr_debug("%s: tx: q6usm_run; rc=%d\n", __func__, rc);
+ if (!rc) {
+ if (usf_xx->buffer_count >= USM_MIN_BUF_CNT) {
+ /* supply all buffers */
+ rc = q6usm_read(usf_xx->usc,
+ usf_xx->buffer_count);
+ pr_debug("%s: q6usm_read[%d]\n",
+ __func__, rc);
+
+ if (rc)
+ pr_err("%s: buf read failed",
+ __func__);
+ else
+ usf_xx->usf_state =
+ USF_WORK_STATE;
+ } else
+ usf_xx->usf_state =
+ USF_WORK_STATE;
+ }
+
+ return rc;
+} /* usf_start_tx */
+
+static int usf_start_rx(struct usf_xx_type *usf_xx)
+{
+ int rc = q6usm_run(usf_xx->usc, 0, 0, 0);
+
+ pr_debug("%s: rx: q6usm_run; rc=%d\n",
+ __func__, rc);
+ if (!rc)
+ usf_xx->usf_state = USF_WORK_STATE;
+
+ return rc;
+} /* usf_start_rx */
+
+static int __usf_set_us_detection(struct usf_type *usf,
+ struct us_detect_info_type *detect_info)
+{
+ uint32_t timeout = 0;
+ struct usm_session_cmd_detect_info *p_allocated_memory = NULL;
+ struct usm_session_cmd_detect_info usm_detect_info;
+ struct usm_session_cmd_detect_info *p_usm_detect_info =
+ &usm_detect_info;
+ uint32_t detect_info_size = sizeof(struct usm_session_cmd_detect_info);
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ int rc = 0;
+
+ if (detect_info->us_detector != US_DETECT_FW) {
+ pr_err("%s: unsupported detector: %d\n",
+ __func__, detect_info->us_detector);
+ return -EINVAL;
+ }
+
+ if ((detect_info->params_data_size != 0) &&
+ (detect_info->params_data != NULL)) {
+ uint8_t *p_data = NULL;
+
+ detect_info_size += detect_info->params_data_size;
+ p_allocated_memory = kzalloc(detect_info_size, GFP_KERNEL);
+ if (p_allocated_memory == NULL) {
+ pr_err("%s: detect_info[%d] allocation failed\n",
+ __func__, detect_info_size);
+ return -ENOMEM;
+ }
+ p_usm_detect_info = p_allocated_memory;
+ p_data = (uint8_t *)p_usm_detect_info +
+ sizeof(struct usm_session_cmd_detect_info);
+
+ rc = copy_from_user(p_data,
+ (uint8_t __user *)(detect_info->params_data),
+ detect_info->params_data_size);
+ if (rc) {
+ pr_err("%s: copy params from user; rc=%d\n",
+ __func__, rc);
+ kfree(p_allocated_memory);
+ return -EFAULT;
+ }
+ p_usm_detect_info->algorithm_cfg_size =
+ detect_info->params_data_size;
+ } else
+ usm_detect_info.algorithm_cfg_size = 0;
+
+ p_usm_detect_info->detect_mode = detect_info->us_detect_mode;
+ p_usm_detect_info->skip_interval = detect_info->skip_time;
+
+ usf_xx->us_detect_type = USF_US_DETECT_UNDEF;
+
+ rc = q6usm_set_us_detection(usf_xx->usc,
+ p_usm_detect_info,
+ detect_info_size);
+ if (rc || (detect_info->detect_timeout == USF_NO_WAIT_TIMEOUT)) {
+ kfree(p_allocated_memory);
+ return rc;
+ }
+
+ /* Get US detection result */
+ if (detect_info->detect_timeout == USF_INFINITIVE_TIMEOUT) {
+ rc = wait_event_interruptible(usf_xx->wait,
+ (usf_xx->us_detect_type !=
+ USF_US_DETECT_UNDEF) ||
+ (usf_xx->usf_state ==
+ USF_ADSP_RESTART_STATE));
+ } else {
+ if (detect_info->detect_timeout == USF_DEFAULT_TIMEOUT)
+ timeout = USF_TIMEOUT_JIFFIES;
+ else
+ timeout = detect_info->detect_timeout * HZ;
+ }
+ rc = wait_event_interruptible_timeout(usf_xx->wait,
+ (usf_xx->us_detect_type !=
+ USF_US_DETECT_UNDEF) ||
+ (usf_xx->usf_state ==
+ USF_ADSP_RESTART_STATE), timeout);
+
+ /* In the case of aDSP restart, "no US" is assumed */
+ if (usf_xx->usf_state == USF_ADSP_RESTART_STATE) {
+ rc = -EFAULT;
+ }
+ /* In the case of timeout, "no US" is assumed */
+ if (rc < 0)
+ pr_err("%s: Getting US detection failed rc[%d]\n",
+ __func__, rc);
+ else {
+ usf->usf_rx.us_detect_type = usf->usf_tx.us_detect_type;
+ detect_info->is_us =
+ (usf_xx->us_detect_type == USF_US_DETECT_YES);
+ }
+
+ kfree(p_allocated_memory);
+
+ return rc;
+} /* __usf_set_us_detection */
+
+static int usf_set_us_detection(struct usf_type *usf, unsigned long arg)
+{
+ struct us_detect_info_type detect_info;
+
+ int rc = copy_from_user(&detect_info,
+ (struct us_detect_info_type __user *) arg,
+ sizeof(detect_info));
+
+ if (rc) {
+ pr_err("%s: copy detect_info from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ rc = __usf_set_us_detection(usf, &detect_info);
+ if (rc < 0) {
+ pr_err("%s: set us detection failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = copy_to_user((void __user *)arg,
+ &detect_info,
+ sizeof(detect_info));
+ if (rc) {
+ pr_err("%s: copy detect_info to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_set_us_detection */
+
+static int __usf_set_tx_info(struct usf_type *usf,
+ struct us_tx_info_type *config_tx)
+{
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ int rc = 0;
+
+ usf_xx->new_region = USM_UNDEF_TOKEN;
+ usf_xx->prev_region = USM_UNDEF_TOKEN;
+ usf_xx->cb = usf_tx_cb;
+
+ init_waitqueue_head(&usf_xx->wait);
+
+ if (config_tx->us_xx_info.client_name != NULL) {
+ int res = strncpy_from_user(
+ usf_xx->client_name,
+ (char __user *)(config_tx->us_xx_info.client_name),
+ sizeof(usf_xx->client_name)-1);
+ if (res < 0) {
+ pr_err("%s: get client name failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ rc = config_xx(usf_xx, &(config_tx->us_xx_info));
+ if (rc)
+ return rc;
+
+ rc = q6usm_open_read(usf_xx->usc,
+ usf_xx->encdec_cfg.format_id);
+ if (rc)
+ return rc;
+
+ rc = q6usm_us_client_buf_alloc(OUT, usf_xx->usc,
+ usf_xx->buffer_size,
+ usf_xx->buffer_count);
+ if (rc) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ return rc;
+ }
+
+ rc = q6usm_us_param_buf_alloc(OUT, usf_xx->usc,
+ config_tx->us_xx_info.max_get_set_param_buf_size);
+ if (rc) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ return rc;
+ }
+
+ rc = q6usm_enc_cfg_blk(usf_xx->usc,
+ &usf_xx->encdec_cfg);
+ if (!rc &&
+ (config_tx->input_info.event_types != USF_NO_EVENT)) {
+ rc = register_input_device(usf,
+ &(config_tx->input_info));
+ }
+
+ if (rc)
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ else
+ usf_xx->usf_state = USF_CONFIGURED_STATE;
+
+ return rc;
+} /* __usf_set_tx_info */
+
+static int usf_set_tx_info(struct usf_type *usf, unsigned long arg)
+{
+ struct us_tx_info_type config_tx;
+
+ int rc = copy_from_user(&config_tx,
+ (struct us_tx_info_type __user *) arg,
+ sizeof(config_tx));
+
+ if (rc) {
+ pr_err("%s: copy config_tx from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ return __usf_set_tx_info(usf, &config_tx);
+} /* usf_set_tx_info */
+
+static int __usf_set_rx_info(struct usf_type *usf,
+ struct us_rx_info_type *config_rx)
+{
+ struct usf_xx_type *usf_xx = &usf->usf_rx;
+ int rc = 0;
+
+ usf_xx->new_region = USM_UNDEF_TOKEN;
+ usf_xx->prev_region = USM_UNDEF_TOKEN;
+
+ usf_xx->cb = usf_rx_cb;
+
+ rc = config_xx(usf_xx, &(config_rx->us_xx_info));
+ if (rc)
+ return rc;
+
+ rc = q6usm_open_write(usf_xx->usc,
+ usf_xx->encdec_cfg.format_id);
+ if (rc)
+ return rc;
+
+ rc = q6usm_us_client_buf_alloc(
+ IN,
+ usf_xx->usc,
+ usf_xx->buffer_size,
+ usf_xx->buffer_count);
+ if (rc) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ return rc;
+ }
+
+ rc = q6usm_us_param_buf_alloc(IN, usf_xx->usc,
+ config_rx->us_xx_info.max_get_set_param_buf_size);
+ if (rc) {
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ return rc;
+ }
+
+ rc = q6usm_dec_cfg_blk(usf_xx->usc,
+ &usf_xx->encdec_cfg);
+ if (rc)
+ (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+ else {
+ init_waitqueue_head(&usf_xx->wait);
+ usf_xx->usf_state = USF_CONFIGURED_STATE;
+ }
+
+ return rc;
+} /* __usf_set_rx_info */
+
+static int usf_set_rx_info(struct usf_type *usf, unsigned long arg)
+{
+ struct us_rx_info_type config_rx;
+
+ int rc = copy_from_user(&config_rx,
+ (struct us_rx_info_type __user *) arg,
+ sizeof(config_rx));
+
+ if (rc) {
+ pr_err("%s: copy config_rx from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ return __usf_set_rx_info(usf, &config_rx);
+} /* usf_set_rx_info */
+
+static int __usf_get_tx_update(struct usf_type *usf,
+ struct us_tx_update_info_type *upd_tx_info)
+{
+ unsigned long prev_jiffies = 0;
+ uint32_t timeout = 0;
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ int rc = 0;
+
+ if (!usf_xx->user_upd_info_na) {
+ usf_set_event_filters(usf, upd_tx_info->event_filters);
+ handle_input_event(usf,
+ upd_tx_info->event_counter,
+ upd_tx_info->event);
+
+ /* Release available regions */
+ rc = q6usm_read(usf_xx->usc,
+ upd_tx_info->free_region);
+ if (rc)
+ return rc;
+ } else
+ usf_xx->user_upd_info_na = 0;
+
+ /* Get data ready regions */
+ if (upd_tx_info->timeout == USF_INFINITIVE_TIMEOUT) {
+ rc = wait_event_interruptible(usf_xx->wait,
+ (usf_xx->prev_region !=
+ usf_xx->new_region) ||
+ (usf_xx->usf_state !=
+ USF_WORK_STATE));
+ } else {
+ if (upd_tx_info->timeout == USF_NO_WAIT_TIMEOUT)
+ rc = (usf_xx->prev_region != usf_xx->new_region);
+ else {
+ prev_jiffies = jiffies;
+ if (upd_tx_info->timeout == USF_DEFAULT_TIMEOUT) {
+ timeout = USF_TIMEOUT_JIFFIES;
+ rc = wait_event_timeout(
+ usf_xx->wait,
+ (usf_xx->prev_region !=
+ usf_xx->new_region) ||
+ (usf_xx->usf_state !=
+ USF_WORK_STATE),
+ timeout);
+ } else {
+ timeout = upd_tx_info->timeout * HZ;
+ rc = wait_event_interruptible_timeout(
+ usf_xx->wait,
+ (usf_xx->prev_region !=
+ usf_xx->new_region) ||
+ (usf_xx->usf_state !=
+ USF_WORK_STATE),
+ timeout);
+ }
+ }
+ if (!rc) {
+ pr_debug("%s: timeout. prev_j=%lu; j=%lu\n",
+ __func__, prev_jiffies, jiffies);
+ pr_debug("%s: timeout. prev=%d; new=%d\n",
+ __func__, usf_xx->prev_region,
+ usf_xx->new_region);
+ pr_debug("%s: timeout. free_region=%d;\n",
+ __func__, upd_tx_info->free_region);
+ if (usf_xx->prev_region ==
+ usf_xx->new_region) {
+ pr_err("%s:read data: timeout\n",
+ __func__);
+ return -ETIME;
+ }
+ }
+ }
+
+ if ((usf_xx->usf_state != USF_WORK_STATE) ||
+ (rc == -ERESTARTSYS)) {
+ pr_err("%s: Get ready region failure; state[%d]; rc[%d]\n",
+ __func__, usf_xx->usf_state, rc);
+ return -EINTR;
+ }
+
+ upd_tx_info->ready_region = usf_xx->new_region;
+ usf_xx->prev_region = upd_tx_info->ready_region;
+
+ if (upd_tx_info->ready_region == USM_WRONG_TOKEN) {
+ pr_err("%s: TX path corrupted; prev=%d\n",
+ __func__, usf_xx->prev_region);
+ return -EIO;
+ }
+
+ return rc;
+} /* __usf_get_tx_update */
+
+static int usf_get_tx_update(struct usf_type *usf, unsigned long arg)
+{
+ struct us_tx_update_info_type upd_tx_info;
+
+ int rc = copy_from_user(&upd_tx_info,
+ (struct us_tx_update_info_type __user *) arg,
+ sizeof(upd_tx_info));
+
+ if (rc < 0) {
+ pr_err("%s: copy upd_tx_info from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ rc = __usf_get_tx_update(usf, &upd_tx_info);
+ if (rc < 0) {
+ pr_err("%s: get tx update failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = copy_to_user((void __user *)arg,
+ &upd_tx_info,
+ sizeof(upd_tx_info));
+ if (rc) {
+ pr_err("%s: copy upd_tx_info to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_get_tx_update */
+
+static int __usf_set_rx_update(struct usf_xx_type *usf_xx,
+ struct us_rx_update_info_type *upd_rx_info)
+{
+ int rc = 0;
+
+ /* Send available data regions */
+ if (upd_rx_info->ready_region !=
+ usf_xx->buffer_count) {
+ rc = q6usm_write(
+ usf_xx->usc,
+ upd_rx_info->ready_region);
+ if (rc)
+ return rc;
+ }
+
+ /* Get free regions */
+ rc = wait_event_timeout(
+ usf_xx->wait,
+ !q6usm_is_write_buf_full(
+ usf_xx->usc,
+ &(upd_rx_info->free_region)) ||
+ (usf_xx->usf_state == USF_IDLE_STATE),
+ USF_TIMEOUT_JIFFIES);
+
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. wait for write buf not full\n",
+ __func__);
+ } else {
+ if (usf_xx->usf_state !=
+ USF_WORK_STATE) {
+ pr_err("%s: RX: state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ rc = -EINTR;
+ }
+ }
+
+ return rc;
+} /* __usf_set_rx_update */
+
+static int usf_set_rx_update(struct usf_xx_type *usf_xx, unsigned long arg)
+{
+ struct us_rx_update_info_type upd_rx_info;
+
+ int rc = copy_from_user(&upd_rx_info,
+ (struct us_rx_update_info_type __user *) arg,
+ sizeof(upd_rx_info));
+
+ if (rc) {
+ pr_err("%s: copy upd_rx_info from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ rc = __usf_set_rx_update(usf_xx, &upd_rx_info);
+ if (rc < 0) {
+ pr_err("%s: set rx update failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = copy_to_user((void __user *)arg,
+ &upd_rx_info,
+ sizeof(upd_rx_info));
+ if (rc) {
+ pr_err("%s: copy rx_info to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_set_rx_update */
+
+static void usf_release_input(struct usf_type *usf)
+{
+ uint16_t ind = 0;
+
+ usf_unregister_conflicting_events(
+ usf->conflicting_event_types);
+ usf->conflicting_event_types = 0;
+ for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) {
+ if (usf->input_ifs[ind] == NULL)
+ continue;
+ input_unregister_device(usf->input_ifs[ind]);
+ usf->input_ifs[ind] = NULL;
+ pr_debug("%s input_unregister_device[%s]\n",
+ __func__,
+ s_usf_input_devs[ind].input_dev_name);
+ }
+} /* usf_release_input */
+
+static int usf_stop_tx(struct usf_type *usf)
+{
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+
+ usf_release_input(usf);
+ usf_disable(usf_xx);
+
+ return 0;
+} /* usf_stop_tx */
+
+static int __usf_get_version(struct us_version_info_type *version_info)
+{
+ int rc = 0;
+
+ if (version_info->buf_size < sizeof(DRV_VERSION)) {
+ pr_err("%s: buf_size (%d) < version string size (%zu)\n",
+ __func__, version_info->buf_size, sizeof(DRV_VERSION));
+ return -EINVAL;
+ }
+
+ rc = copy_to_user((void __user *)(version_info->pbuf),
+ DRV_VERSION,
+ sizeof(DRV_VERSION));
+ if (rc) {
+ pr_err("%s: copy to version_info.pbuf; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* __usf_get_version */
+
+static int usf_get_version(unsigned long arg)
+{
+ struct us_version_info_type version_info;
+
+ int rc = copy_from_user(&version_info,
+ (struct us_version_info_type __user *) arg,
+ sizeof(version_info));
+
+ if (rc) {
+ pr_err("%s: copy version_info from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ rc = __usf_get_version(&version_info);
+ if (rc < 0) {
+ pr_err("%s: get version failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = copy_to_user((void __user *)arg,
+ &version_info,
+ sizeof(version_info));
+ if (rc) {
+ pr_err("%s: copy version_info to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_get_version */
+
+static int __usf_set_stream_param(struct usf_xx_type *usf_xx,
+ struct us_stream_param_type *set_stream_param,
+ int dir)
+{
+ struct us_client *usc = usf_xx->usc;
+ struct us_port_data *port = &usc->port[dir];
+ int rc = 0;
+
+ if (port->param_buf == NULL) {
+ pr_err("%s: parameter buffer is null\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (set_stream_param->buf_size > port->param_buf_size) {
+ pr_err("%s: buf_size (%d) > maximum buf size (%d)\n",
+ __func__, set_stream_param->buf_size,
+ port->param_buf_size);
+ return -EINVAL;
+ }
+
+ if (set_stream_param->buf_size == 0) {
+ pr_err("%s: buf_size is 0\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(port->param_buf,
+ (uint8_t __user *) set_stream_param->pbuf,
+ set_stream_param->buf_size);
+ if (rc) {
+ pr_err("%s: copy param buf from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ rc = q6usm_set_us_stream_param(dir, usc, set_stream_param->module_id,
+ set_stream_param->param_id,
+ set_stream_param->buf_size);
+ if (rc) {
+ pr_err("%s: q6usm_set_us_stream_param failed; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static int usf_set_stream_param(struct usf_xx_type *usf_xx,
+ unsigned long arg, int dir)
+{
+ struct us_stream_param_type set_stream_param;
+ int rc = 0;
+
+ rc = copy_from_user(&set_stream_param,
+ (struct us_stream_param_type __user *) arg,
+ sizeof(set_stream_param));
+
+ if (rc) {
+ pr_err("%s: copy set_stream_param from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ return __usf_set_stream_param(usf_xx, &set_stream_param, dir);
+} /* usf_set_stream_param */
+
+static int __usf_get_stream_param(struct usf_xx_type *usf_xx,
+ struct us_stream_param_type *get_stream_param,
+ int dir)
+{
+ struct us_client *usc = usf_xx->usc;
+ struct us_port_data *port = &usc->port[dir];
+ int rc = 0;
+
+ if (port->param_buf == NULL) {
+ pr_err("%s: parameter buffer is null\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (get_stream_param->buf_size > port->param_buf_size) {
+ pr_err("%s: buf_size (%d) > maximum buf size (%d)\n",
+ __func__, get_stream_param->buf_size,
+ port->param_buf_size);
+ return -EINVAL;
+ }
+
+ if (get_stream_param->buf_size == 0) {
+ pr_err("%s: buf_size is 0\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = q6usm_get_us_stream_param(dir, usc, get_stream_param->module_id,
+ get_stream_param->param_id,
+ get_stream_param->buf_size);
+ if (rc) {
+ pr_err("%s: q6usm_get_us_stream_param failed; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ rc = copy_to_user((uint8_t __user *) get_stream_param->pbuf,
+ port->param_buf,
+ get_stream_param->buf_size);
+ if (rc) {
+ pr_err("%s: copy param buf to user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static int usf_get_stream_param(struct usf_xx_type *usf_xx,
+ unsigned long arg, int dir)
+{
+ struct us_stream_param_type get_stream_param;
+ int rc = 0;
+
+ rc = copy_from_user(&get_stream_param,
+ (struct us_stream_param_type __user *) arg,
+ sizeof(get_stream_param));
+
+ if (rc) {
+ pr_err("%s: copy get_stream_param from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ return __usf_get_stream_param(usf_xx, &get_stream_param, dir);
+} /* usf_get_stream_param */
+
+static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ struct usf_type *usf = file->private_data;
+ struct usf_xx_type *usf_xx = NULL;
+
+ switch (cmd) {
+ case US_START_TX: {
+ usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_CONFIGURED_STATE)
+ rc = usf_start_tx(usf_xx);
+ else {
+ pr_err("%s: start_tx: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+ break;
+ }
+
+ case US_START_RX: {
+ usf_xx = &usf->usf_rx;
+ if (usf_xx->usf_state == USF_CONFIGURED_STATE)
+ rc = usf_start_rx(usf_xx);
+ else {
+ pr_err("%s: start_rx: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+ break;
+ }
+
+ case US_SET_TX_INFO: {
+ usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_OPENED_STATE)
+ rc = usf_set_tx_info(usf, arg);
+ else {
+ pr_err("%s: set_tx_info: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+
+ break;
+ } /* US_SET_TX_INFO */
+
+ case US_SET_RX_INFO: {
+ usf_xx = &usf->usf_rx;
+ if (usf_xx->usf_state == USF_OPENED_STATE)
+ rc = usf_set_rx_info(usf, arg);
+ else {
+ pr_err("%s: set_rx_info: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+
+ break;
+ } /* US_SET_RX_INFO */
+
+ case US_GET_TX_UPDATE: {
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_WORK_STATE)
+ rc = usf_get_tx_update(usf, arg);
+ else {
+ pr_err("%s: get_tx_update: wrong state[%d]\n", __func__,
+ usf_xx->usf_state);
+ rc = -EBADFD;
+ }
+ break;
+ } /* US_GET_TX_UPDATE */
+
+ case US_SET_RX_UPDATE: {
+ struct usf_xx_type *usf_xx = &usf->usf_rx;
+ if (usf_xx->usf_state == USF_WORK_STATE)
+ rc = usf_set_rx_update(usf_xx, arg);
+ else {
+ pr_err("%s: set_rx_update: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ rc = -EBADFD;
+ }
+ break;
+ } /* US_SET_RX_UPDATE */
+
+ case US_STOP_TX: {
+ usf_xx = &usf->usf_tx;
+ if ((usf_xx->usf_state == USF_WORK_STATE)
+ || (usf_xx->usf_state == USF_ADSP_RESTART_STATE))
+ rc = usf_stop_tx(usf);
+ else {
+ pr_err("%s: stop_tx: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+ break;
+ } /* US_STOP_TX */
+
+ case US_STOP_RX: {
+ usf_xx = &usf->usf_rx;
+ if ((usf_xx->usf_state == USF_WORK_STATE)
+ || (usf_xx->usf_state == USF_ADSP_RESTART_STATE))
+ usf_disable(usf_xx);
+ else {
+ pr_err("%s: stop_rx: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+ break;
+ } /* US_STOP_RX */
+
+ case US_SET_DETECTION: {
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_WORK_STATE)
+ rc = usf_set_us_detection(usf, arg);
+ else {
+ pr_err("%s: set us detection: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ rc = -EBADFD;
+ }
+ break;
+ } /* US_SET_DETECTION */
+
+ case US_GET_VERSION: {
+ rc = usf_get_version(arg);
+ break;
+ } /* US_GET_VERSION */
+
+ case US_SET_TX_STREAM_PARAM: {
+ rc = usf_set_stream_param(&usf->usf_tx, arg, OUT);
+ break;
+ } /* US_SET_TX_STREAM_PARAM */
+
+ case US_GET_TX_STREAM_PARAM: {
+ rc = usf_get_stream_param(&usf->usf_tx, arg, OUT);
+ break;
+ } /* US_GET_TX_STREAM_PARAM */
+
+ case US_SET_RX_STREAM_PARAM: {
+ rc = usf_set_stream_param(&usf->usf_rx, arg, IN);
+ break;
+ } /* US_SET_RX_STREAM_PARAM */
+
+ case US_GET_RX_STREAM_PARAM: {
+ rc = usf_get_stream_param(&usf->usf_rx, arg, IN);
+ break;
+ } /* US_GET_RX_STREAM_PARAM */
+
+ default:
+ pr_err("%s: unsupported IOCTL command [%d]\n",
+ __func__,
+ cmd);
+ rc = -ENOTTY;
+ break;
+ }
+
+ if (rc &&
+ ((cmd == US_SET_TX_INFO) ||
+ (cmd == US_SET_RX_INFO)))
+ release_xx(usf_xx);
+
+ return rc;
+} /* usf_ioctl */
+
+#ifdef CONFIG_COMPAT
+
+#define US_SET_TX_INFO32 _IOW(USF_IOCTL_MAGIC, 0, \
+ struct us_tx_info_type32)
+#define US_GET_TX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 2, \
+ struct us_tx_update_info_type32)
+#define US_SET_RX_INFO32 _IOW(USF_IOCTL_MAGIC, 3, \
+ struct us_rx_info_type32)
+#define US_SET_RX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 4, \
+ struct us_rx_update_info_type32)
+#define US_SET_DETECTION32 _IOWR(USF_IOCTL_MAGIC, 8, \
+ struct us_detect_info_type32)
+#define US_GET_VERSION32 _IOWR(USF_IOCTL_MAGIC, 9, \
+ struct us_version_info_type32)
+#define US_SET_TX_STREAM_PARAM32 _IOW(USF_IOCTL_MAGIC, 10, \
+ struct us_stream_param_type32)
+#define US_GET_TX_STREAM_PARAM32 _IOWR(USF_IOCTL_MAGIC, 11, \
+ struct us_stream_param_type32)
+#define US_SET_RX_STREAM_PARAM32 _IOW(USF_IOCTL_MAGIC, 12, \
+ struct us_stream_param_type32)
+#define US_GET_RX_STREAM_PARAM32 _IOWR(USF_IOCTL_MAGIC, 13, \
+ struct us_stream_param_type32)
+
+/* Info structure common for TX and RX */
+struct us_xx_info_type32 {
+/* Input: general info */
+/* Name of the client - event calculator, ptr to char */
+ const compat_uptr_t client_name;
+/* Selected device identification, accepted in the kernel's CAD */
+ uint32_t dev_id;
+/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */
+ uint32_t stream_format;
+/* Required sample rate in Hz */
+ uint32_t sample_rate;
+/* Size of a buffer (bytes) for US data transfer between the module and USF */
+ uint32_t buf_size;
+/* Number of the buffers for the US data transfer */
+ uint16_t buf_num;
+/* Number of the microphones (TX) or speakers(RX) */
+ uint16_t port_cnt;
+/* Microphones(TX) or speakers(RX) indexes in their enumeration */
+ uint8_t port_id[USF_MAX_PORT_NUM];
+/* Bits per sample 16 or 32 */
+ uint16_t bits_per_sample;
+/* Input: Transparent info for encoder in the LPASS */
+/* Parameters data size in bytes */
+ uint16_t params_data_size;
+/* Pointer to the parameters, ptr to uint8_t */
+ compat_uptr_t params_data;
+/* Max size of buffer for get and set parameter */
+ uint32_t max_get_set_param_buf_size;
+};
+
+struct us_tx_info_type32 {
+/* Common info. This struct includes ptr and therefore the 32 version */
+ struct us_xx_info_type32 us_xx_info;
+/* Info specific for TX. This struct doesn't include long or ptr
+ and therefore no 32 version */
+ struct us_input_info_type input_info;
+};
+
+struct us_tx_update_info_type32 {
+/* Input general: */
+/* Number of calculated events */
+ uint16_t event_counter;
+/* Calculated events or NULL, ptr to struct usf_event_type */
+ compat_uptr_t event;
+/* Pointer (read index) to the end of available region */
+/* in the shared US data memory */
+ uint32_t free_region;
+/* Time (sec) to wait for data or special values: */
+/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */
+ uint32_t timeout;
+/* Events (from conflicting devs) to be disabled/enabled */
+ uint16_t event_filters;
+
+/* Input transparent data: */
+/* Parameters size */
+ uint16_t params_data_size;
+/* Pointer to the parameters, ptr to uint8_t */
+ compat_uptr_t params_data;
+/* Output parameters: */
+/* Pointer (write index) to the end of ready US data region */
+/* in the shared memory */
+ uint32_t ready_region;
+};
+
+struct us_rx_info_type32 {
+ /* Common info */
+ struct us_xx_info_type32 us_xx_info;
+ /* Info specific for RX*/
+};
+
+struct us_rx_update_info_type32 {
+/* Input general: */
+/* Pointer (write index) to the end of ready US data region */
+/* in the shared memory */
+ uint32_t ready_region;
+/* Input transparent data: */
+/* Parameters size */
+ uint16_t params_data_size;
+/* pPointer to the parameters, ptr to uint8_t */
+ compat_uptr_t params_data;
+/* Output parameters: */
+/* Pointer (read index) to the end of available region */
+/* in the shared US data memory */
+ uint32_t free_region;
+};
+
+struct us_detect_info_type32 {
+/* US detection place (HW|FW) */
+/* NA in the Active and OFF states */
+ enum us_detect_place_enum us_detector;
+/* US detection mode */
+ enum us_detect_mode_enum us_detect_mode;
+/* US data dropped during this time (msec) */
+ uint32_t skip_time;
+/* Transparent data size */
+ uint16_t params_data_size;
+/* Pointer to the transparent data, ptr to uint8_t */
+ compat_uptr_t params_data;
+/* Time (sec) to wait for US presence event */
+ uint32_t detect_timeout;
+/* Out parameter: US presence */
+ bool is_us;
+};
+
+struct us_version_info_type32 {
+/* Size of memory for the version string */
+ uint16_t buf_size;
+/* Pointer to the memory for the version string, ptr to char */
+ compat_uptr_t pbuf;
+};
+
+struct us_stream_param_type32 {
+/* Id of module */
+ uint32_t module_id;
+/* Id of parameter */
+ uint32_t param_id;
+/* Size of memory of the parameter buffer */
+ uint32_t buf_size;
+/* Pointer to the memory of the parameter buffer */
+ compat_uptr_t pbuf;
+};
+
+static void usf_compat_xx_info_type(struct us_xx_info_type32 *us_xx_info32,
+ struct us_xx_info_type *us_xx_info)
+{
+ int i = 0;
+ us_xx_info->client_name = compat_ptr(us_xx_info32->client_name);
+ us_xx_info->dev_id = us_xx_info32->dev_id;
+ us_xx_info->stream_format = us_xx_info32->stream_format;
+ us_xx_info->sample_rate = us_xx_info32->sample_rate;
+ us_xx_info->buf_size = us_xx_info32->buf_size;
+ us_xx_info->buf_num = us_xx_info32->buf_num;
+ us_xx_info->port_cnt = us_xx_info32->port_cnt;
+ for (i = 0; i < USF_MAX_PORT_NUM; i++)
+ us_xx_info->port_id[i] = us_xx_info32->port_id[i];
+ us_xx_info->bits_per_sample = us_xx_info32->bits_per_sample;
+ us_xx_info->params_data_size = us_xx_info32->params_data_size;
+ us_xx_info->params_data = compat_ptr(us_xx_info32->params_data);
+ us_xx_info->max_get_set_param_buf_size =
+ us_xx_info32->max_get_set_param_buf_size;
+}
+
+static int usf_set_tx_info32(struct usf_type *usf, unsigned long arg)
+{
+ struct us_tx_info_type32 config_tx32;
+ struct us_tx_info_type config_tx;
+
+ int rc = copy_from_user(&config_tx32,
+ (struct us_tx_info_type32 __user *) arg,
+ sizeof(config_tx32));
+
+ if (rc) {
+ pr_err("%s: copy config_tx from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+ memset(&config_tx, 0, sizeof(config_tx));
+ usf_compat_xx_info_type(&(config_tx32.us_xx_info),
+ &(config_tx.us_xx_info));
+ config_tx.input_info = config_tx32.input_info;
+
+ return __usf_set_tx_info(usf, &config_tx);
+} /* usf_set_tx_info 32*/
+
+static int usf_set_rx_info32(struct usf_type *usf, unsigned long arg)
+{
+ struct us_rx_info_type32 config_rx32;
+ struct us_rx_info_type config_rx;
+
+ int rc = copy_from_user(&config_rx32,
+ (struct us_rx_info_type32 __user *) arg,
+ sizeof(config_rx32));
+
+ if (rc) {
+ pr_err("%s: copy config_rx from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+ memset(&config_rx, 0, sizeof(config_rx));
+ usf_compat_xx_info_type(&(config_rx32.us_xx_info),
+ &(config_rx.us_xx_info));
+
+ return __usf_set_rx_info(usf, &config_rx);
+} /* usf_set_rx_info32 */
+
+static int usf_get_tx_update32(struct usf_type *usf, unsigned long arg)
+{
+ struct us_tx_update_info_type32 upd_tx_info32;
+ struct us_tx_update_info_type upd_tx_info;
+
+ int rc = copy_from_user(&upd_tx_info32,
+ (struct us_tx_update_info_type32 __user *) arg,
+ sizeof(upd_tx_info32));
+
+ if (rc) {
+ pr_err("%s: copy upd_tx_info32 from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ memset(&upd_tx_info, 0, sizeof(upd_tx_info));
+ upd_tx_info.event_counter = upd_tx_info32.event_counter;
+ upd_tx_info.event = compat_ptr(upd_tx_info32.event);
+ upd_tx_info.free_region = upd_tx_info32.free_region;
+ upd_tx_info.timeout = upd_tx_info32.timeout;
+ upd_tx_info.event_filters = upd_tx_info32.event_filters;
+ upd_tx_info.params_data_size = upd_tx_info32.params_data_size;
+ upd_tx_info.params_data = compat_ptr(upd_tx_info32.params_data);
+ upd_tx_info.ready_region = upd_tx_info32.ready_region;
+
+ rc = __usf_get_tx_update(usf, &upd_tx_info);
+ if (rc < 0) {
+ pr_err("%s: get tx update failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Update only the fields that were changed */
+ upd_tx_info32.ready_region = upd_tx_info.ready_region;
+
+ rc = copy_to_user((void __user *)arg, &upd_tx_info32,
+ sizeof(upd_tx_info32));
+ if (rc) {
+ pr_err("%s: copy upd_tx_info32 to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_get_tx_update */
+
+static int usf_set_rx_update32(struct usf_xx_type *usf_xx, unsigned long arg)
+{
+ struct us_rx_update_info_type32 upd_rx_info32;
+ struct us_rx_update_info_type upd_rx_info;
+
+ int rc = copy_from_user(&upd_rx_info32,
+ (struct us_rx_update_info_type32 __user *) arg,
+ sizeof(upd_rx_info32));
+
+ if (rc) {
+ pr_err("%s: copy upd_rx_info32 from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ memset(&upd_rx_info, 0, sizeof(upd_rx_info));
+ upd_rx_info.ready_region = upd_rx_info32.ready_region;
+ upd_rx_info.params_data_size = upd_rx_info32.params_data_size;
+ upd_rx_info.params_data = compat_ptr(upd_rx_info32.params_data);
+ upd_rx_info.free_region = upd_rx_info32.free_region;
+
+ rc = __usf_set_rx_update(usf_xx, &upd_rx_info);
+ if (rc < 0) {
+ pr_err("%s: set rx update failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Update only the fields that were changed */
+ upd_rx_info32.free_region = upd_rx_info.free_region;
+
+ rc = copy_to_user((void __user *)arg,
+ &upd_rx_info32,
+ sizeof(upd_rx_info32));
+ if (rc) {
+ pr_err("%s: copy rx_info32 to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_set_rx_update32 */
+
+static int usf_set_us_detection32(struct usf_type *usf, unsigned long arg)
+{
+ struct us_detect_info_type32 detect_info32;
+ struct us_detect_info_type detect_info;
+
+ int rc = copy_from_user(&detect_info32,
+ (struct us_detect_info_type32 __user *) arg,
+ sizeof(detect_info32));
+
+ if (rc) {
+ pr_err("%s: copy detect_info32 from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ memset(&detect_info, 0, sizeof(detect_info));
+ detect_info.us_detector = detect_info32.us_detector;
+ detect_info.us_detect_mode = detect_info32.us_detect_mode;
+ detect_info.skip_time = detect_info32.skip_time;
+ detect_info.params_data_size = detect_info32.params_data_size;
+ detect_info.params_data = compat_ptr(detect_info32.params_data);
+ detect_info.detect_timeout = detect_info32.detect_timeout;
+ detect_info.is_us = detect_info32.is_us;
+
+ rc = __usf_set_us_detection(usf, &detect_info);
+ if (rc < 0) {
+ pr_err("%s: set us detection failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* Update only the fields that were changed */
+ detect_info32.is_us = detect_info.is_us;
+
+ rc = copy_to_user((void __user *)arg,
+ &detect_info32,
+ sizeof(detect_info32));
+ if (rc) {
+ pr_err("%s: copy detect_info32 to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_set_us_detection32 */
+
+static int usf_get_version32(unsigned long arg)
+{
+ struct us_version_info_type32 version_info32;
+ struct us_version_info_type version_info;
+
+ int rc = copy_from_user(&version_info32,
+ (struct us_version_info_type32 __user *) arg,
+ sizeof(version_info32));
+
+ if (rc) {
+ pr_err("%s: copy version_info32 from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ memset(&version_info, 0, sizeof(version_info));
+ version_info.buf_size = version_info32.buf_size;
+ version_info.pbuf = compat_ptr(version_info32.pbuf);
+
+ rc = __usf_get_version(&version_info);
+ if (rc < 0) {
+ pr_err("%s: get version failed; rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ /* None of the fields were changed */
+
+ rc = copy_to_user((void __user *)arg,
+ &version_info32,
+ sizeof(version_info32));
+ if (rc) {
+ pr_err("%s: copy version_info32 to user; rc=%d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+
+ return rc;
+} /* usf_get_version32 */
+
+static int usf_set_stream_param32(struct usf_xx_type *usf_xx,
+ unsigned long arg, int dir)
+{
+ struct us_stream_param_type32 set_stream_param32;
+ struct us_stream_param_type set_stream_param;
+ int rc = 0;
+
+ rc = copy_from_user(&set_stream_param32,
+ (struct us_stream_param_type32 __user *) arg,
+ sizeof(set_stream_param32));
+
+ if (rc) {
+ pr_err("%s: copy set_stream_param from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ memset(&set_stream_param, 0, sizeof(set_stream_param));
+ set_stream_param.module_id = set_stream_param32.module_id;
+ set_stream_param.param_id = set_stream_param32.param_id;
+ set_stream_param.buf_size = set_stream_param32.buf_size;
+ set_stream_param.pbuf = compat_ptr(set_stream_param32.pbuf);
+
+ return __usf_set_stream_param(usf_xx, &set_stream_param, dir);
+} /* usf_set_stream_param32 */
+
+static int usf_get_stream_param32(struct usf_xx_type *usf_xx,
+ unsigned long arg, int dir)
+{
+ struct us_stream_param_type32 get_stream_param32;
+ struct us_stream_param_type get_stream_param;
+ int rc = 0;
+
+ rc = copy_from_user(&get_stream_param32,
+ (struct us_stream_param_type32 __user *) arg,
+ sizeof(get_stream_param32));
+
+ if (rc) {
+ pr_err("%s: copy get_stream_param from user; rc=%d\n",
+ __func__, rc);
+ return -EFAULT;
+ }
+
+ memset(&get_stream_param, 0, sizeof(get_stream_param));
+ get_stream_param.module_id = get_stream_param32.module_id;
+ get_stream_param.param_id = get_stream_param32.param_id;
+ get_stream_param.buf_size = get_stream_param32.buf_size;
+ get_stream_param.pbuf = compat_ptr(get_stream_param32.pbuf);
+
+ return __usf_get_stream_param(usf_xx, &get_stream_param, dir);
+} /* usf_get_stream_param32 */
+
+static long usf_compat_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+ struct usf_type *usf = file->private_data;
+ struct usf_xx_type *usf_xx = NULL;
+
+ switch (cmd) {
+ case US_START_TX:
+ case US_START_RX:
+ case US_STOP_TX:
+ case US_STOP_RX: {
+ return usf_ioctl(file, cmd, arg);
+ }
+
+ case US_SET_TX_INFO32: {
+ usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_OPENED_STATE)
+ rc = usf_set_tx_info32(usf, arg);
+ else {
+ pr_err("%s: set_tx_info32: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+
+ break;
+ } /* US_SET_TX_INFO32 */
+
+ case US_SET_RX_INFO32: {
+ usf_xx = &usf->usf_rx;
+ if (usf_xx->usf_state == USF_OPENED_STATE)
+ rc = usf_set_rx_info32(usf, arg);
+ else {
+ pr_err("%s: set_rx_info32: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ return -EBADFD;
+ }
+
+ break;
+ } /* US_SET_RX_INFO32 */
+
+ case US_GET_TX_UPDATE32: {
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_WORK_STATE)
+ rc = usf_get_tx_update32(usf, arg);
+ else {
+ pr_err("%s: get_tx_update32: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ rc = -EBADFD;
+ }
+ break;
+ } /* US_GET_TX_UPDATE32 */
+
+ case US_SET_RX_UPDATE32: {
+ struct usf_xx_type *usf_xx = &usf->usf_rx;
+ if (usf_xx->usf_state == USF_WORK_STATE)
+ rc = usf_set_rx_update32(usf_xx, arg);
+ else {
+ pr_err("%s: set_rx_update: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ rc = -EBADFD;
+ }
+ break;
+ } /* US_SET_RX_UPDATE32 */
+
+ case US_SET_DETECTION32: {
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+ if (usf_xx->usf_state == USF_WORK_STATE)
+ rc = usf_set_us_detection32(usf, arg);
+ else {
+ pr_err("%s: set us detection: wrong state[%d]\n",
+ __func__,
+ usf_xx->usf_state);
+ rc = -EBADFD;
+ }
+ break;
+ } /* US_SET_DETECTION32 */
+
+ case US_GET_VERSION32: {
+ rc = usf_get_version32(arg);
+ break;
+ } /* US_GET_VERSION32 */
+
+ case US_SET_TX_STREAM_PARAM32: {
+ rc = usf_set_stream_param32(&usf->usf_tx, arg, OUT);
+ break;
+ } /* US_SET_TX_STREAM_PARAM32 */
+
+ case US_GET_TX_STREAM_PARAM32: {
+ rc = usf_get_stream_param32(&usf->usf_tx, arg, OUT);
+ break;
+ } /* US_GET_TX_STREAM_PARAM32 */
+
+ case US_SET_RX_STREAM_PARAM32: {
+ rc = usf_set_stream_param32(&usf->usf_rx, arg, IN);
+ break;
+ } /* US_SET_RX_STREAM_PARAM32 */
+
+ case US_GET_RX_STREAM_PARAM32: {
+ rc = usf_get_stream_param32(&usf->usf_rx, arg, IN);
+ break;
+ } /* US_GET_RX_STREAM_PARAM32 */
+
+ default:
+ pr_err("%s: unsupported IOCTL command [%d]\n",
+ __func__,
+ cmd);
+ rc = -ENOTTY;
+ break;
+ }
+
+ if (rc &&
+ ((cmd == US_SET_TX_INFO) ||
+ (cmd == US_SET_RX_INFO)))
+ release_xx(usf_xx);
+
+ return rc;
+} /* usf_compat_ioctl */
+#endif /* CONFIG_COMPAT */
+
+static int usf_mmap(struct file *file, struct vm_area_struct *vms)
+{
+ struct usf_type *usf = file->private_data;
+ int dir = OUT;
+ struct usf_xx_type *usf_xx = &usf->usf_tx;
+
+ if (vms->vm_flags & USF_VM_WRITE) { /* RX buf mapping */
+ dir = IN;
+ usf_xx = &usf->usf_rx;
+ }
+
+ return q6usm_get_virtual_address(dir, usf_xx->usc, vms);
+}
+
+static uint16_t add_opened_dev(int minor)
+{
+ uint16_t ind = 0;
+
+ for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) {
+ if (minor == s_opened_devs[ind]) {
+ pr_err("%s: device %d is already opened\n",
+ __func__, minor);
+ return USF_UNDEF_DEV_ID;
+ }
+
+ if (s_opened_devs[ind] == 0) {
+ s_opened_devs[ind] = minor;
+ pr_debug("%s: device %d is added; ind=%d\n",
+ __func__, minor, ind);
+ return ind;
+ }
+ }
+
+ pr_err("%s: there is no place for device %d\n",
+ __func__, minor);
+ return USF_UNDEF_DEV_ID;
+}
+
+static int usf_open(struct inode *inode, struct file *file)
+{
+ struct usf_type *usf = NULL;
+ uint16_t dev_ind = 0;
+ int minor = MINOR(inode->i_rdev);
+
+ dev_ind = add_opened_dev(minor);
+ if (dev_ind == USF_UNDEF_DEV_ID)
+ return -EBUSY;
+
+ usf = kzalloc(sizeof(struct usf_type), GFP_KERNEL);
+ if (usf == NULL) {
+ pr_err("%s:usf allocation failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ file->private_data = usf;
+ usf->dev_ind = dev_ind;
+
+ usf->usf_tx.usf_state = USF_OPENED_STATE;
+ usf->usf_rx.usf_state = USF_OPENED_STATE;
+
+ usf->usf_tx.us_detect_type = USF_US_DETECT_UNDEF;
+ usf->usf_rx.us_detect_type = USF_US_DETECT_UNDEF;
+
+ pr_debug("%s:usf in open\n", __func__);
+ return 0;
+}
+
+static int usf_release(struct inode *inode, struct file *file)
+{
+ struct usf_type *usf = file->private_data;
+
+ pr_debug("%s: release entry\n", __func__);
+
+ usf_release_input(usf);
+
+ usf_disable(&usf->usf_tx);
+ usf_disable(&usf->usf_rx);
+
+ s_opened_devs[usf->dev_ind] = 0;
+
+ kfree(usf);
+ pr_debug("%s: release exit\n", __func__);
+ return 0;
+}
+
+extern long usf_compat_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations usf_fops = {
+ .owner = THIS_MODULE,
+ .open = usf_open,
+ .release = usf_release,
+ .unlocked_ioctl = usf_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = usf_compat_ioctl,
+#endif /* CONFIG_COMPAT */
+ .mmap = usf_mmap,
+};
+
+static struct miscdevice usf_misc[MAX_DEVS_NUMBER] = {
+ {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "usf1",
+ .fops = &usf_fops,
+ },
+};
+
+static int __init usf_init(void)
+{
+ int rc = 0;
+ uint16_t ind = 0;
+
+ pr_debug("%s: USF SW version %s.\n", __func__, DRV_VERSION);
+ pr_debug("%s: Max %d devs registration\n", __func__, MAX_DEVS_NUMBER);
+
+ for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) {
+ rc = misc_register(&usf_misc[ind]);
+ if (rc) {
+ pr_err("%s: misc_register() failed ind=%d; rc = %d\n",
+ __func__, ind, rc);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+device_initcall(usf_init);
+
+MODULE_DESCRIPTION("Ultrasound framework driver");
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
new file mode 100644
index 000000000000..76bcc83e1c5e
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
@@ -0,0 +1,424 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/syscalls.h>
+#include "usfcdev.h"
+
+#define UNDEF_ID 0xffffffff
+#define SLOT_CMD_ID 0
+#define MAX_RETRIES 10
+
+enum usdev_event_status {
+ USFCDEV_EVENT_ENABLED,
+ USFCDEV_EVENT_DISABLING,
+ USFCDEV_EVENT_DISABLED,
+};
+
+struct usfcdev_event {
+ bool (*match_cb)(uint16_t, struct input_dev *dev);
+ bool registered_event;
+ bool interleaved;
+ enum usdev_event_status event_status;
+};
+static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM];
+
+struct usfcdev_input_command {
+ unsigned int type;
+ unsigned int code;
+ unsigned int value;
+};
+
+static long s_usf_pid;
+
+static bool usfcdev_filter(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value);
+static bool usfcdev_match(struct input_handler *handler,
+ struct input_dev *dev);
+static int usfcdev_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id);
+static void usfcdev_disconnect(struct input_handle *handle);
+
+static const struct input_device_id usfc_tsc_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_KEYBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+ /* assumption: ABS_X & ABS_Y are in the same long */
+ .absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) |
+ BIT_MASK(ABS_Y) },
+ },
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_KEYBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+ /* assumption: MT_.._X & MT_.._Y are in the same long */
+ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+ BIT_MASK(ABS_MT_POSITION_X) |
+ BIT_MASK(ABS_MT_POSITION_Y) },
+ },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, usfc_tsc_ids);
+
+static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = {
+ { /* TSC handler */
+ .filter = usfcdev_filter,
+ .match = usfcdev_match,
+ .connect = usfcdev_connect,
+ .disconnect = usfcdev_disconnect,
+ /* .minor can be used as index in the container, */
+ /* because .fops isn't supported */
+ .minor = TSC_EVENT_TYPE_IND,
+ .name = "usfc_tsc_handler",
+ .id_table = usfc_tsc_ids,
+ },
+};
+
+/*
+ * For each event type, there are a number conflicting devices (handles)
+ * The first registered device (primary) is real TSC device; it's mandatory
+ * Optionally, later registered devices are simulated ones.
+ * They are dynamically managed
+ * The primary device's handles are stored in the below static array
+ */
+static struct input_handle s_usfc_primary_handles[MAX_EVENT_TYPE_NUM] = {
+ { /* TSC handle */
+ .handler = &s_usfc_handlers[TSC_EVENT_TYPE_IND],
+ .name = "usfc_tsc_handle",
+ },
+};
+
+static struct usfcdev_input_command initial_clear_cmds[] = {
+ {EV_ABS, ABS_PRESSURE, 0},
+ {EV_KEY, BTN_TOUCH, 0},
+};
+
+static struct usfcdev_input_command slot_clear_cmds[] = {
+ {EV_ABS, ABS_MT_SLOT, 0},
+ {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
+};
+
+static struct usfcdev_input_command no_filter_cmds[] = {
+ {EV_ABS, ABS_MT_SLOT, 0},
+ {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
+ {EV_SYN, SYN_REPORT, 0},
+};
+
+static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev)
+{
+ bool rc = false;
+ int ind = handler->minor;
+
+ pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind);
+
+ if (s_usfcdev_events[ind].registered_event &&
+ s_usfcdev_events[ind].match_cb) {
+ rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev);
+ pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc);
+ }
+ return rc;
+}
+
+static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int ret = 0;
+ uint16_t ind = handler->minor;
+ struct input_handle *usfc_handle = NULL;
+
+ if (s_usfc_primary_handles[ind].dev == NULL) {
+ pr_debug("%s: primary device; ind=%d\n",
+ __func__,
+ ind);
+ usfc_handle = &s_usfc_primary_handles[ind];
+ } else {
+ pr_debug("%s: secondary device; ind=%d\n",
+ __func__,
+ ind);
+ usfc_handle = kzalloc(sizeof(struct input_handle),
+ GFP_KERNEL);
+ if (!usfc_handle) {
+ pr_err("%s: memory allocation failed; ind=%d\n",
+ __func__,
+ ind);
+ return -ENOMEM;
+ }
+ usfc_handle->handler = &s_usfc_handlers[ind];
+ usfc_handle->name = s_usfc_primary_handles[ind].name;
+ }
+ usfc_handle->dev = dev;
+ ret = input_register_handle(usfc_handle);
+ pr_debug("%s: name=[%s]; ind=%d; dev=0x%p\n",
+ __func__,
+ dev->name,
+ ind,
+ usfc_handle->dev);
+ if (ret)
+ pr_err("%s: input_register_handle[%d] failed: ret=%d\n",
+ __func__,
+ ind,
+ ret);
+ else {
+ ret = input_open_device(usfc_handle);
+ if (ret) {
+ pr_err("%s: input_open_device[%d] failed: ret=%d\n",
+ __func__,
+ ind,
+ ret);
+ input_unregister_handle(usfc_handle);
+ } else
+ pr_debug("%s: device[%d] is opened\n",
+ __func__,
+ ind);
+ }
+
+ return ret;
+}
+
+static void usfcdev_disconnect(struct input_handle *handle)
+{
+ int ind = handle->handler->minor;
+
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ pr_debug("%s: handle[%d], name=[%s] is disconnected\n",
+ __func__,
+ ind,
+ handle->dev->name);
+ if (s_usfc_primary_handles[ind].dev == handle->dev)
+ s_usfc_primary_handles[ind].dev = NULL;
+ else
+ kfree(handle);
+}
+
+static bool usfcdev_filter(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value)
+{
+ uint16_t i = 0;
+ uint16_t ind = (uint16_t)handle->handler->minor;
+ bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED);
+
+ if (s_usf_pid == sys_getpid()) {
+ /* Pass events from usfcdev driver */
+ rc = false;
+ pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d",
+ __func__,
+ ind,
+ type,
+ code,
+ value);
+ } else if (s_usfcdev_events[ind].event_status ==
+ USFCDEV_EVENT_DISABLING) {
+ uint32_t u_value = value;
+ s_usfcdev_events[ind].interleaved = true;
+ /* Pass events for freeing slots from TSC driver */
+ for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) {
+ if ((no_filter_cmds[i].type == type) &&
+ (no_filter_cmds[i].code == code) &&
+ (no_filter_cmds[i].value <= u_value)) {
+ rc = false;
+ pr_debug("%s: no_filter_cmds[%d]; %d",
+ __func__,
+ i,
+ no_filter_cmds[i].value);
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+bool usfcdev_register(
+ uint16_t event_type_ind,
+ bool (*match_cb)(uint16_t, struct input_dev *dev))
+{
+ int ret = 0;
+ bool rc = false;
+
+ if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) {
+ pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%p\n",
+ __func__,
+ event_type_ind,
+ match_cb);
+ return false;
+ }
+
+ if (s_usfcdev_events[event_type_ind].registered_event) {
+ pr_info("%s: handler[%d] was already registered\n",
+ __func__,
+ event_type_ind);
+ return true;
+ }
+
+ s_usfcdev_events[event_type_ind].registered_event = true;
+ s_usfcdev_events[event_type_ind].match_cb = match_cb;
+ s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED;
+ ret = input_register_handler(&s_usfc_handlers[event_type_ind]);
+ if (!ret) {
+ rc = true;
+ pr_debug("%s: handler[%d] was registered\n",
+ __func__,
+ event_type_ind);
+ } else {
+ s_usfcdev_events[event_type_ind].registered_event = false;
+ s_usfcdev_events[event_type_ind].match_cb = NULL;
+ pr_err("%s: handler[%d] registration failed: ret=%d\n",
+ __func__,
+ event_type_ind,
+ ret);
+ }
+
+ return rc;
+}
+
+void usfcdev_unregister(uint16_t event_type_ind)
+{
+ if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+ pr_err("%s: wrong input: event_type_ind=%d\n",
+ __func__,
+ event_type_ind);
+ return;
+ }
+ if (s_usfcdev_events[event_type_ind].registered_event) {
+ input_unregister_handler(&s_usfc_handlers[event_type_ind]);
+ pr_debug("%s: handler[%d] was unregistered\n",
+ __func__,
+ event_type_ind);
+ s_usfcdev_events[event_type_ind].registered_event = false;
+ s_usfcdev_events[event_type_ind].match_cb = NULL;
+ s_usfcdev_events[event_type_ind].event_status =
+ USFCDEV_EVENT_ENABLED;
+
+ }
+}
+
+static inline void usfcdev_send_cmd(
+ struct input_dev *dev,
+ struct usfcdev_input_command cmd)
+{
+ input_event(dev, cmd.type, cmd.code, cmd.value);
+}
+
+static void usfcdev_clean_dev(uint16_t event_type_ind)
+{
+ struct input_dev *dev = NULL;
+ int i;
+ int j;
+ int retries = 0;
+
+ if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+ pr_err("%s: wrong input: event_type_ind=%d\n",
+ __func__,
+ event_type_ind);
+ return;
+ }
+ /* Only primary device must exist */
+ dev = s_usfc_primary_handles[event_type_ind].dev;
+ if (dev == NULL) {
+ pr_err("%s: NULL primary device\n",
+ __func__);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++)
+ usfcdev_send_cmd(dev, initial_clear_cmds[i]);
+ input_sync(dev);
+
+ /* Send commands to free all slots */
+ for (i = 0; i < dev->mt->num_slots; i++) {
+ s_usfcdev_events[event_type_ind].interleaved = false;
+ if (input_mt_get_value(&dev->mt->slots[i],
+ ABS_MT_TRACKING_ID) < 0) {
+ pr_debug("%s: skipping slot %d",
+ __func__, i);
+ continue;
+ }
+ slot_clear_cmds[SLOT_CMD_ID].value = i;
+ for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++)
+ usfcdev_send_cmd(dev, slot_clear_cmds[j]);
+
+ if (s_usfcdev_events[event_type_ind].interleaved) {
+ pr_debug("%s: interleaved(%d): slot(%d)",
+ __func__, i, dev->mt->slot);
+ if (retries++ < MAX_RETRIES) {
+ --i;
+ continue;
+ }
+ pr_warn("%s: index(%d) reached max retires",
+ __func__, i);
+ }
+
+ retries = 0;
+ input_sync(dev);
+ }
+}
+
+bool usfcdev_set_filter(uint16_t event_type_ind, bool filter)
+{
+ bool rc = true;
+
+ if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+ pr_err("%s: wrong input: event_type_ind=%d\n",
+ __func__,
+ event_type_ind);
+ return false;
+ }
+
+ if (s_usfcdev_events[event_type_ind].registered_event) {
+
+ pr_debug("%s: event_type[%d]; filter=%d\n",
+ __func__,
+ event_type_ind,
+ filter
+ );
+ if (filter) {
+ s_usfcdev_events[event_type_ind].event_status =
+ USFCDEV_EVENT_DISABLING;
+ s_usf_pid = sys_getpid();
+ usfcdev_clean_dev(event_type_ind);
+ s_usfcdev_events[event_type_ind].event_status =
+ USFCDEV_EVENT_DISABLED;
+ } else
+ s_usfcdev_events[event_type_ind].event_status =
+ USFCDEV_EVENT_ENABLED;
+ } else {
+ pr_err("%s: event_type[%d] isn't registered\n",
+ __func__,
+ event_type_ind);
+ rc = false;
+ }
+
+ return rc;
+}
+
+static int __init usfcdev_init(void)
+{
+ return 0;
+}
+
+device_initcall(usfcdev_init);
+
+MODULE_DESCRIPTION("Handle of events from devices, conflicting with USF");
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h
new file mode 100644
index 000000000000..03b62c5ec83c
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h
@@ -0,0 +1,28 @@
+/* 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 __USFCDEV_H__
+#define __USFCDEV_H__
+
+#include <linux/input.h>
+
+/* TSC event type index in the containers of the handlers & handles */
+#define TSC_EVENT_TYPE_IND 0
+/* Number of supported event types to be filtered */
+#define MAX_EVENT_TYPE_NUM 1
+
+bool usfcdev_register(
+ uint16_t event_type_ind,
+ bool (*match_cb)(uint16_t, struct input_dev *dev));
+void usfcdev_unregister(uint16_t event_type_ind);
+bool usfcdev_set_filter(uint16_t event_type_ind, bool filter);
+#endif /* __USFCDEV_H__ */