aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2015-12-07 12:01:09 +0000
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2016-01-11 09:54:00 +0000
commit3063982df0fcda6ecaa5efaf9b058bfb3a8f8faf (patch)
tree42a5a9739d9a189799029cad261ee91daefced7e
parent75cfda907b41e23c6c46e867142f9dd6c873ca9e (diff)
sound: qcom: add dsp support to apq8064tracking-qcomlt-apq8064-audio
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-rw-r--r--include/linux/msm_audio.h367
-rw-r--r--include/linux/msm_audio_acdb.h81
-rw-r--r--include/sound/apr_audio.h1675
-rw-r--r--include/sound/msm-dai-q6.h45
-rw-r--r--include/sound/msm_hdmi_audio.h54
-rw-r--r--include/sound/q6adm.h50
-rw-r--r--include/sound/q6afe.h110
-rw-r--r--include/sound/q6asm.h334
-rw-r--r--include/sound/qdsp6v2/apr.h169
-rw-r--r--include/sound/qdsp6v2/apr_tal.h52
-rw-r--r--include/sound/qdsp6v2/audio_acdb.h61
-rw-r--r--include/sound/qdsp6v2/audio_def.h35
-rw-r--r--include/sound/qdsp6v2/audio_dev_ctl.h221
-rw-r--r--include/sound/qdsp6v2/dsp_debug.h22
-rw-r--r--include/sound/qdsp6v2/q6voice.h778
-rw-r--r--include/sound/qdsp6v2/rtac.h39
-rw-r--r--sound/soc/qcom/Kconfig62
-rw-r--r--sound/soc/qcom/Makefile9
-rw-r--r--sound/soc/qcom/apq8064.c170
-rw-r--r--sound/soc/qcom/msm-dai-fe.c101
-rw-r--r--sound/soc/qcom/msm-dai-q6-hdmi.c305
-rw-r--r--sound/soc/qcom/msm-pcm-q6.c851
-rw-r--r--sound/soc/qcom/msm-pcm-q6.h96
-rw-r--r--sound/soc/qcom/msm-pcm-routing.c894
-rw-r--r--sound/soc/qcom/msm-pcm-routing.h145
-rw-r--r--sound/soc/qcom/qdsp6/Makefile2
-rw-r--r--sound/soc/qcom/qdsp6/core/Makefile3
-rw-r--r--sound/soc/qcom/qdsp6/core/apr.c649
-rw-r--r--sound/soc/qcom/qdsp6/core/apr_tal.c177
-rw-r--r--sound/soc/qcom/qdsp6/core/apr_v1.c131
-rw-r--r--sound/soc/qcom/qdsp6/core/audio_acdb.c865
-rw-r--r--sound/soc/qcom/qdsp6/core/dsp_debug.c259
-rw-r--r--sound/soc/qcom/qdsp6/core/q6audio_common.h35
-rw-r--r--sound/soc/qcom/qdsp6/core/q6core.c406
-rw-r--r--sound/soc/qcom/qdsp6/core/q6core.h52
-rw-r--r--sound/soc/qcom/qdsp6/core/rtac.c1046
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c1241
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c1826
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c3841
39 files changed, 17259 insertions, 0 deletions
diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h
new file mode 100644
index 0000000000000..04d4e5b56b21b
--- /dev/null
+++ b/include/linux/msm_audio.h
@@ -0,0 +1,367 @@
+/* include/linux/msm_audio.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2012 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.
+ *
+ */
+
+#ifndef __LINUX_MSM_AUDIO_H
+#define __LINUX_MSM_AUDIO_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* PCM Audio */
+
+#define AUDIO_IOCTL_MAGIC 'a'
+
+#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned)
+#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned)
+#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned)
+#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned)
+#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned)
+#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned)
+#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned)
+#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned)
+#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned)
+#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned)
+#define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned)
+#define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned)
+#define AUDIO_PLAY_DTMF _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned)
+#define AUDIO_GET_EVENT _IOR(AUDIO_IOCTL_MAGIC, 13, unsigned)
+#define AUDIO_ABORT_GET_EVENT _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned)
+#define AUDIO_REGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned)
+#define AUDIO_DEREGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 16, unsigned)
+#define AUDIO_ASYNC_WRITE _IOW(AUDIO_IOCTL_MAGIC, 17, unsigned)
+#define AUDIO_ASYNC_READ _IOW(AUDIO_IOCTL_MAGIC, 18, unsigned)
+#define AUDIO_SET_INCALL _IOW(AUDIO_IOCTL_MAGIC, 19, struct msm_voicerec_mode)
+#define AUDIO_GET_NUM_SND_DEVICE _IOR(AUDIO_IOCTL_MAGIC, 20, unsigned)
+#define AUDIO_GET_SND_DEVICES _IOWR(AUDIO_IOCTL_MAGIC, 21, \
+ struct msm_snd_device_list)
+#define AUDIO_ENABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 22, unsigned)
+#define AUDIO_DISABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 23, unsigned)
+#define AUDIO_ROUTE_STREAM _IOW(AUDIO_IOCTL_MAGIC, 24, \
+ struct msm_audio_route_config)
+#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned)
+#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned)
+#define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned)
+#define AUDIO_SET_MUTE _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned)
+#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned)
+#define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned)
+#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned)
+#define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned)
+#define AUDIO_OUTPORT_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 40, unsigned short)
+#define AUDIO_SET_ERR_THRESHOLD_VALUE _IOW(AUDIO_IOCTL_MAGIC, 41, \
+ unsigned short)
+#define AUDIO_GET_BITSTREAM_ERROR_INFO _IOR(AUDIO_IOCTL_MAGIC, 42, \
+ struct msm_audio_bitstream_error_info)
+
+#define AUDIO_SET_SRS_TRUMEDIA_PARAM _IOW(AUDIO_IOCTL_MAGIC, 43, unsigned)
+
+/* Qualcomm extensions */
+#define AUDIO_SET_STREAM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 80, \
+ struct msm_audio_stream_config)
+#define AUDIO_GET_STREAM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 81, \
+ struct msm_audio_stream_config)
+#define AUDIO_GET_SESSION_ID _IOR(AUDIO_IOCTL_MAGIC, 82, unsigned short)
+#define AUDIO_GET_STREAM_INFO _IOR(AUDIO_IOCTL_MAGIC, 83, \
+ struct msm_audio_bitstream_info)
+#define AUDIO_SET_PAN _IOW(AUDIO_IOCTL_MAGIC, 84, unsigned)
+#define AUDIO_SET_QCONCERT_PLUS _IOW(AUDIO_IOCTL_MAGIC, 85, unsigned)
+#define AUDIO_SET_MBADRC _IOW(AUDIO_IOCTL_MAGIC, 86, unsigned)
+#define AUDIO_SET_VOLUME_PATH _IOW(AUDIO_IOCTL_MAGIC, 87, \
+ struct msm_vol_info)
+#define AUDIO_SET_MAX_VOL_ALL _IOW(AUDIO_IOCTL_MAGIC, 88, unsigned)
+#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 89, unsigned)
+#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 90, unsigned)
+#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 91, unsigned)
+#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 92, unsigned)
+#define AUDIO_GET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 93, \
+ struct msm_audio_buf_cfg)
+#define AUDIO_SET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 94, \
+ struct msm_audio_buf_cfg)
+#define AUDIO_SET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 95, \
+ struct msm_acdb_cmd_device)
+#define AUDIO_GET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 96, \
+ struct msm_acdb_cmd_device)
+
+#define AUDIO_REGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 97, unsigned)
+#define AUDIO_DEREGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 98, unsigned)
+
+#define AUDIO_MAX_COMMON_IOCTL_NUM 100
+
+
+#define HANDSET_MIC 0x01
+#define HANDSET_SPKR 0x02
+#define HEADSET_MIC 0x03
+#define HEADSET_SPKR_MONO 0x04
+#define HEADSET_SPKR_STEREO 0x05
+#define SPKR_PHONE_MIC 0x06
+#define SPKR_PHONE_MONO 0x07
+#define SPKR_PHONE_STEREO 0x08
+#define BT_SCO_MIC 0x09
+#define BT_SCO_SPKR 0x0A
+#define BT_A2DP_SPKR 0x0B
+#define TTY_HEADSET_MIC 0x0C
+#define TTY_HEADSET_SPKR 0x0D
+
+/* Default devices are not supported in a */
+/* device switching context. Only supported */
+/* for stream devices. */
+/* DO NOT USE */
+#define DEFAULT_TX 0x0E
+#define DEFAULT_RX 0x0F
+
+#define BT_A2DP_TX 0x10
+
+#define HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11
+#define HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12
+#define HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13
+#define HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14
+
+#define I2S_RX 0x20
+#define I2S_TX 0x21
+
+#define ADRC_ENABLE 0x0001
+#define EQ_ENABLE 0x0002
+#define IIR_ENABLE 0x0004
+#define QCONCERT_PLUS_ENABLE 0x0008
+#define MBADRC_ENABLE 0x0010
+#define SRS_ENABLE 0x0020
+#define SRS_DISABLE 0x0040
+
+#define AGC_ENABLE 0x0001
+#define NS_ENABLE 0x0002
+#define TX_IIR_ENABLE 0x0004
+#define FLUENCE_ENABLE 0x0008
+
+#define VOC_REC_UPLINK 0x00
+#define VOC_REC_DOWNLINK 0x01
+#define VOC_REC_BOTH 0x02
+
+struct msm_audio_config {
+ uint32_t buffer_size;
+ uint32_t buffer_count;
+ uint32_t channel_count;
+ uint32_t sample_rate;
+ uint32_t type;
+ uint32_t meta_field;
+ uint32_t bits;
+ uint32_t unused[3];
+};
+
+struct msm_audio_stream_config {
+ uint32_t buffer_size;
+ uint32_t buffer_count;
+};
+
+struct msm_audio_buf_cfg{
+ uint32_t meta_info_enable;
+ uint32_t frames_per_buf;
+};
+
+struct msm_audio_stats {
+ uint32_t byte_count;
+ uint32_t sample_count;
+ uint32_t unused[2];
+};
+
+struct msm_audio_ion_info {
+ int fd;
+ void *vaddr;
+};
+
+struct msm_audio_pmem_info {
+ int fd;
+ void *vaddr;
+};
+
+struct msm_audio_aio_buf {
+ void *buf_addr;
+ uint32_t buf_len;
+ uint32_t data_len;
+ void *private_data;
+ unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+/* Audio routing */
+
+#define SND_IOCTL_MAGIC 's'
+
+#define SND_MUTE_UNMUTED 0
+#define SND_MUTE_MUTED 1
+
+struct msm_mute_info {
+ uint32_t mute;
+ uint32_t path;
+};
+
+struct msm_vol_info {
+ uint32_t vol;
+ uint32_t path;
+};
+
+struct msm_voicerec_mode {
+ uint32_t rec_mode;
+};
+
+struct msm_snd_device_config {
+ uint32_t device;
+ uint32_t ear_mute;
+ uint32_t mic_mute;
+};
+
+#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *)
+
+#define SND_METHOD_VOICE 0
+
+struct msm_snd_volume_config {
+ uint32_t device;
+ uint32_t method;
+ uint32_t volume;
+};
+
+#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *)
+
+/* Returns the number of SND endpoints supported. */
+
+#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned *)
+
+struct msm_snd_endpoint {
+ int id; /* input and output */
+ char name[64]; /* output only */
+};
+
+/* Takes an index between 0 and one less than the number returned by
+ * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a
+ * SND endpoint. On input, the .id field contains the number of the
+ * endpoint, and on exit it contains the SND index, while .name contains
+ * the description of the endpoint.
+ */
+
+#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *)
+
+
+#define SND_AVC_CTL _IOW(SND_IOCTL_MAGIC, 6, unsigned *)
+#define SND_AGC_CTL _IOW(SND_IOCTL_MAGIC, 7, unsigned *)
+
+struct msm_audio_pcm_config {
+ uint32_t pcm_feedback; /* 0 - disable > 0 - enable */
+ uint32_t buffer_count; /* Number of buffers to allocate */
+ uint32_t buffer_size; /* Size of buffer for capturing of
+ PCM samples */
+};
+
+#define AUDIO_EVENT_SUSPEND 0
+#define AUDIO_EVENT_RESUME 1
+#define AUDIO_EVENT_WRITE_DONE 2
+#define AUDIO_EVENT_READ_DONE 3
+#define AUDIO_EVENT_STREAM_INFO 4
+#define AUDIO_EVENT_BITSTREAM_ERROR_INFO 5
+
+#define AUDIO_CODEC_TYPE_MP3 0
+#define AUDIO_CODEC_TYPE_AAC 1
+
+struct msm_audio_bitstream_info {
+ uint32_t codec_type;
+ uint32_t chan_info;
+ uint32_t sample_rate;
+ uint32_t bit_stream_info;
+ uint32_t bit_rate;
+ uint32_t unused[3];
+};
+
+struct msm_audio_bitstream_error_info {
+ uint32_t dec_id;
+ uint32_t err_msg_indicator;
+ uint32_t err_type;
+};
+
+union msm_audio_event_payload {
+ struct msm_audio_aio_buf aio_buf;
+ struct msm_audio_bitstream_info stream_info;
+ struct msm_audio_bitstream_error_info error_info;
+ int reserved;
+};
+
+struct msm_audio_event {
+ int event_type;
+ int timeout_ms;
+ union msm_audio_event_payload event_payload;
+};
+
+#define MSM_SNDDEV_CAP_RX 0x1
+#define MSM_SNDDEV_CAP_TX 0x2
+#define MSM_SNDDEV_CAP_VOICE 0x4
+
+struct msm_snd_device_info {
+ uint32_t dev_id;
+ uint32_t dev_cap; /* bitmask describe capability of device */
+ char dev_name[64];
+};
+
+struct msm_snd_device_list {
+ uint32_t num_dev; /* Indicate number of device info to be retrieved */
+ struct msm_snd_device_info *list;
+};
+
+struct msm_dtmf_config {
+ uint16_t path;
+ uint16_t dtmf_hi;
+ uint16_t dtmf_low;
+ uint16_t duration;
+ uint16_t tx_gain;
+ uint16_t rx_gain;
+ uint16_t mixing;
+};
+
+#define AUDIO_ROUTE_STREAM_VOICE_RX 0
+#define AUDIO_ROUTE_STREAM_VOICE_TX 1
+#define AUDIO_ROUTE_STREAM_PLAYBACK 2
+#define AUDIO_ROUTE_STREAM_REC 3
+
+struct msm_audio_route_config {
+ uint32_t stream_type;
+ uint32_t stream_id;
+ uint32_t dev_id;
+};
+
+#define AUDIO_MAX_EQ_BANDS 12
+
+struct msm_audio_eq_band {
+ uint16_t band_idx; /* The band index, 0 .. 11 */
+ uint32_t filter_type; /* Filter band type */
+ uint32_t center_freq_hz; /* Filter band center frequency */
+ uint32_t filter_gain; /* Filter band initial gain (dB) */
+ /* Range is +12 dB to -12 dB with 1dB increments. */
+ uint32_t q_factor;
+} __attribute__ ((packed));
+
+struct msm_audio_eq_stream_config {
+ uint32_t enable; /* Number of consequtive bands specified */
+ uint32_t num_bands;
+ struct msm_audio_eq_band eq_bands[AUDIO_MAX_EQ_BANDS];
+} __attribute__ ((packed));
+
+struct msm_acdb_cmd_device {
+ uint32_t command_id;
+ uint32_t device_id;
+ uint32_t network_id;
+ uint32_t sample_rate_id; /* Actual sample rate value */
+ uint32_t interface_id; /* See interface id's above */
+ uint32_t algorithm_block_id; /* See enumerations above */
+ uint32_t total_bytes; /* Length in bytes used by buffer */
+ uint32_t *phys_buf; /* Physical Address of data */
+};
+
+
+#endif
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
new file mode 100644
index 0000000000000..e7f06b53ac84b
--- /dev/null
+++ b/include/linux/msm_audio_acdb.h
@@ -0,0 +1,81 @@
+#ifndef __MSM_AUDIO_ACDB_H
+#define __MSM_AUDIO_ACDB_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_SET_VOCPROC_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned)
+#define AUDIO_SET_VOCPROC_STREAM_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned)
+#define AUDIO_SET_VOCPROC_VOL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+2), unsigned)
+#define AUDIO_SET_AUDPROC_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+3), unsigned)
+#define AUDIO_SET_AUDPROC_RX_STREAM_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+4), unsigned)
+#define AUDIO_SET_AUDPROC_RX_VOL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+5), unsigned)
+#define AUDIO_SET_AUDPROC_TX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+6), unsigned)
+#define AUDIO_SET_AUDPROC_TX_STREAM_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+7), unsigned)
+#define AUDIO_SET_AUDPROC_TX_VOL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+8), unsigned)
+#define AUDIO_SET_SIDETONE_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+9), unsigned)
+#define AUDIO_SET_ANC_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+10), unsigned)
+#define AUDIO_SET_VOICE_RX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+11), unsigned)
+#define AUDIO_SET_VOICE_TX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+12), unsigned)
+#define AUDIO_SET_ADM_RX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+13), unsigned)
+#define AUDIO_SET_ADM_TX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+14), unsigned)
+#define AUDIO_SET_ASM_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+15), unsigned)
+#define AUDIO_SET_AFE_TX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+16), unsigned)
+#define AUDIO_SET_AFE_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+17), unsigned)
+
+
+#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30)
+
+/* ACDB structures */
+struct cal_block {
+ uint32_t cal_size; /* Size of Cal Data */
+ uint32_t cal_offset; /* offset pointer to Cal Data */
+};
+
+struct sidetone_cal {
+ uint16_t enable;
+ uint16_t gain;
+};
+
+/* For Real-Time Audio Calibration */
+#define AUDIO_GET_RTAC_ADM_INFO _IOR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+1), unsigned)
+#define AUDIO_GET_RTAC_VOICE_INFO _IOR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+2), unsigned)
+#define AUDIO_GET_RTAC_ADM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+3), unsigned)
+#define AUDIO_SET_RTAC_ADM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+4), unsigned)
+#define AUDIO_GET_RTAC_ASM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+5), unsigned)
+#define AUDIO_SET_RTAC_ASM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+6), unsigned)
+#define AUDIO_GET_RTAC_CVS_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+7), unsigned)
+#define AUDIO_SET_RTAC_CVS_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+8), unsigned)
+#define AUDIO_GET_RTAC_CVP_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+9), unsigned)
+#define AUDIO_SET_RTAC_CVP_CAL _IOWR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_ACDB_IOCTL+10), unsigned)
+
+#define AUDIO_MAX_RTAC_IOCTL (AUDIO_MAX_ACDB_IOCTL+20)
+
+#endif /* __MSM_AUDIO_ACDB_H */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
new file mode 100644
index 0000000000000..d3c8a4f23dae8
--- /dev/null
+++ b/include/sound/apr_audio.h
@@ -0,0 +1,1675 @@
+/*
+ *
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 _APR_AUDIO_H_
+#define _APR_AUDIO_H_
+
+/* ASM opcodes without APR payloads*/
+#include <sound/qdsp6v2/apr.h>
+
+/*
+ * Audio Front End (AFE)
+ */
+
+/* Port ID. Update afe_get_port_index when a new port is added here. */
+#define PRIMARY_I2S_RX 0 /* index = 0 */
+#define PRIMARY_I2S_TX 1 /* index = 1 */
+#define PCM_RX 2 /* index = 2 */
+#define PCM_TX 3 /* index = 3 */
+#define SECONDARY_I2S_RX 4 /* index = 4 */
+#define SECONDARY_I2S_TX 5 /* index = 5 */
+#define MI2S_RX 6 /* index = 6 */
+#define MI2S_TX 7 /* index = 7 */
+#define HDMI_RX 8 /* index = 8 */
+#define RSVD_2 9 /* index = 9 */
+#define RSVD_3 10 /* index = 10 */
+#define DIGI_MIC_TX 11 /* index = 11 */
+#define VOICE_RECORD_RX 0x8003 /* index = 12 */
+#define VOICE_RECORD_TX 0x8004 /* index = 13 */
+#define VOICE_PLAYBACK_TX 0x8005 /* index = 14 */
+
+/* Slimbus Multi channel port id pool */
+#define SLIMBUS_0_RX 0x4000 /* index = 15 */
+#define SLIMBUS_0_TX 0x4001 /* index = 16 */
+#define SLIMBUS_1_RX 0x4002 /* index = 17 */
+#define SLIMBUS_1_TX 0x4003 /* index = 18 */
+#define SLIMBUS_2_RX 0x4004
+#define SLIMBUS_2_TX 0x4005
+#define SLIMBUS_3_RX 0x4006
+#define SLIMBUS_3_TX 0x4007
+#define SLIMBUS_4_RX 0x4008
+#define SLIMBUS_4_TX 0x4009 /* index = 24 */
+
+#define INT_BT_SCO_RX 0x3000 /* index = 25 */
+#define INT_BT_SCO_TX 0x3001 /* index = 26 */
+#define INT_BT_A2DP_RX 0x3002 /* index = 27 */
+#define INT_FM_RX 0x3004 /* index = 28 */
+#define INT_FM_TX 0x3005 /* index = 29 */
+#define RT_PROXY_PORT_001_RX 0x2000 /* index = 30 */
+#define RT_PROXY_PORT_001_TX 0x2001 /* index = 31 */
+#define SECONDARY_PCM_RX 12 /* index = 32 */
+#define SECONDARY_PCM_TX 13 /* index = 33 */
+
+
+#define AFE_PORT_INVALID 0xFFFF
+#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID
+
+#define AFE_PORT_CMD_START 0x000100ca
+
+#define AFE_EVENT_RTPORT_START 0
+#define AFE_EVENT_RTPORT_STOP 1
+#define AFE_EVENT_RTPORT_LOW_WM 2
+#define AFE_EVENT_RTPORT_HI_WM 3
+
+struct afe_port_start_command {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 gain; /* Q13 */
+ u32 sample_rate; /* 8 , 16, 48khz */
+} __attribute__ ((packed));
+
+#define AFE_PORT_CMD_STOP 0x000100cb
+struct afe_port_stop_command {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define AFE_PORT_CMD_APPLY_GAIN 0x000100cc
+struct afe_port_gain_command {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 gain;/* Q13 */
+} __attribute__ ((packed));
+
+#define AFE_PORT_CMD_SIDETONE_CTL 0x000100cd
+struct afe_port_sidetone_command {
+ struct apr_hdr hdr;
+ u16 rx_port_id; /* Primary i2s tx = 1 */
+ /* PCM tx = 3 */
+ /* Secondary i2s tx = 5 */
+ /* Mi2s tx = 7 */
+ /* Digital mic tx = 11 */
+ u16 tx_port_id; /* Primary i2s rx = 0 */
+ /* PCM rx = 2 */
+ /* Secondary i2s rx = 4 */
+ /* Mi2S rx = 6 */
+ /* HDMI rx = 8 */
+ u16 gain; /* Q13 */
+ u16 enable; /* 1 = enable, 0 = disable */
+} __attribute__ ((packed));
+
+#define AFE_PORT_CMD_LOOPBACK 0x000100ce
+struct afe_loopback_command {
+ struct apr_hdr hdr;
+ u16 tx_port_id; /* Primary i2s rx = 0 */
+ /* PCM rx = 2 */
+ /* Secondary i2s rx = 4 */
+ /* Mi2S rx = 6 */
+ /* HDMI rx = 8 */
+ u16 rx_port_id; /* Primary i2s tx = 1 */
+ /* PCM tx = 3 */
+ /* Secondary i2s tx = 5 */
+ /* Mi2s tx = 7 */
+ /* Digital mic tx = 11 */
+ u16 mode; /* Default -1, DSP will conver
+ the tx to rx format */
+ u16 enable; /* 1 = enable, 0 = disable */
+} __attribute__ ((packed));
+
+#define AFE_PSEUDOPORT_CMD_START 0x000100cf
+struct afe_pseudoport_start_command {
+ struct apr_hdr hdr;
+ u16 port_id; /* Pseudo Port 1 = 0x8000 */
+ /* Pseudo Port 2 = 0x8001 */
+ /* Pseudo Port 3 = 0x8002 */
+ u16 timing; /* FTRT = 0 , AVTimer = 1, */
+} __attribute__ ((packed));
+
+#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0
+struct afe_pseudoport_stop_command {
+ struct apr_hdr hdr;
+ u16 port_id; /* Pseudo Port 1 = 0x8000 */
+ /* Pseudo Port 2 = 0x8001 */
+ /* Pseudo Port 3 = 0x8002 */
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define AFE_CMD_GET_ACTIVE_PORTS 0x000100d1
+
+
+#define AFE_CMD_GET_ACTIVE_HANDLES_FOR_PORT 0x000100d2
+struct afe_get_active_handles_command {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define AFE_PCM_CFG_MODE_PCM 0x0
+#define AFE_PCM_CFG_MODE_AUX 0x1
+#define AFE_PCM_CFG_SYNC_EXT 0x0
+#define AFE_PCM_CFG_SYNC_INT 0x1
+#define AFE_PCM_CFG_FRM_8BPF 0x0
+#define AFE_PCM_CFG_FRM_16BPF 0x1
+#define AFE_PCM_CFG_FRM_32BPF 0x2
+#define AFE_PCM_CFG_FRM_64BPF 0x3
+#define AFE_PCM_CFG_FRM_128BPF 0x4
+#define AFE_PCM_CFG_FRM_256BPF 0x5
+#define AFE_PCM_CFG_QUANT_ALAW_NOPAD 0x0
+#define AFE_PCM_CFG_QUANT_MULAW_NOPAD 0x1
+#define AFE_PCM_CFG_QUANT_LINEAR_NOPAD 0x2
+#define AFE_PCM_CFG_QUANT_ALAW_PAD 0x3
+#define AFE_PCM_CFG_QUANT_MULAW_PAD 0x4
+#define AFE_PCM_CFG_QUANT_LINEAR_PAD 0x5
+#define AFE_PCM_CFG_CDATAOE_MASTER 0x0
+#define AFE_PCM_CFG_CDATAOE_SHARE 0x1
+
+struct afe_port_pcm_cfg {
+ u16 mode; /* PCM (short sync) = 0, AUXPCM (long sync) = 1 */
+ u16 sync; /* external = 0 , internal = 1 */
+ u16 frame; /* 8 bpf = 0 */
+ /* 16 bpf = 1 */
+ /* 32 bpf = 2 */
+ /* 64 bpf = 3 */
+ /* 128 bpf = 4 */
+ /* 256 bpf = 5 */
+ u16 quant;
+ u16 slot; /* Slot for PCM stream , 0 - 31 */
+ u16 data; /* 0, PCM block is the only master */
+ /* 1, PCM block is shares to driver data out signal */
+ /* other master */
+ u16 reserved;
+} __attribute__ ((packed));
+
+enum {
+ AFE_I2S_SD0 = 1,
+ AFE_I2S_SD1,
+ AFE_I2S_SD2,
+ AFE_I2S_SD3,
+ AFE_I2S_QUAD01,
+ AFE_I2S_QUAD23,
+ AFE_I2S_6CHS,
+ AFE_I2S_8CHS,
+};
+
+#define AFE_MI2S_MONO 0
+#define AFE_MI2S_STEREO 3
+#define AFE_MI2S_4CHANNELS 4
+#define AFE_MI2S_6CHANNELS 6
+#define AFE_MI2S_8CHANNELS 8
+
+struct afe_port_mi2s_cfg {
+ u16 bitwidth; /* 16,24,32 */
+ u16 line; /* Called ChannelMode in documentation */
+ /* i2s_sd0 = 1 */
+ /* i2s_sd1 = 2 */
+ /* i2s_sd2 = 3 */
+ /* i2s_sd3 = 4 */
+ /* i2s_quad01 = 5 */
+ /* i2s_quad23 = 6 */
+ /* i2s_6chs = 7 */
+ /* i2s_8chs = 8 */
+ u16 channel; /* Called MonoStereo in documentation */
+ /* i2s mono = 0 */
+ /* i2s mono right = 1 */
+ /* i2s mono left = 2 */
+ /* i2s stereo = 3 */
+ u16 ws; /* 0, word select signal from external source */
+ /* 1, word select signal from internal source */
+ u16 format; /* don't touch this field if it is not for */
+ /* AFE_PORT_CMD_I2S_CONFIG opcode */
+} __attribute__ ((packed));
+
+struct afe_port_hdmi_cfg {
+ u16 bitwidth; /* 16,24,32 */
+ u16 channel_mode; /* HDMI Stereo = 0 */
+ /* HDMI_3Point1 (4-ch) = 1 */
+ /* HDMI_5Point1 (6-ch) = 2 */
+ /* HDMI_6Point1 (8-ch) = 3 */
+ u16 data_type; /* HDMI_Linear = 0 */
+ /* HDMI_non_Linear = 1 */
+} __attribute__ ((packed));
+
+
+struct afe_port_hdmi_multi_ch_cfg {
+ u16 data_type; /* HDMI_Linear = 0 */
+ /* HDMI_non_Linear = 1 */
+ u16 channel_allocation; /* The default is 0 (Stereo) */
+ u16 reserved; /* must be set to 0 */
+} __packed;
+
+
+/* Slimbus Device Ids */
+#define AFE_SLIMBUS_DEVICE_1 0x0
+#define AFE_SLIMBUS_DEVICE_2 0x1
+#define AFE_PORT_MAX_AUDIO_CHAN_CNT 16
+
+struct afe_port_slimbus_cfg {
+ u16 slimbus_dev_id; /* SLIMBUS Device id.*/
+
+ u16 slave_dev_pgd_la; /* Slave ported generic device
+ * logical address.
+ */
+ u16 slave_dev_intfdev_la; /* Slave interface device logical
+ * address.
+ */
+ u16 bit_width; /** bit width of the samples, 16, 24.*/
+
+ u16 data_format; /** data format.*/
+
+ u16 num_channels; /** Number of channels.*/
+
+ /** Slave port mapping for respective channels.*/
+ u16 slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+
+ u16 reserved;
+} __packed;
+
+struct afe_port_slimbus_sch_cfg {
+ u16 slimbus_dev_id; /* SLIMBUS Device id.*/
+ u16 bit_width; /** bit width of the samples, 16, 24.*/
+ u16 data_format; /** data format.*/
+ u16 num_channels; /** Number of channels.*/
+ u16 reserved;
+ /** Slave channel mapping for respective channels.*/
+ u8 slave_ch_mapping[8];
+} __packed;
+
+struct afe_port_rtproxy_cfg {
+ u16 bitwidth; /* 16,24,32 */
+ u16 interleaved; /* interleaved = 1 */
+ /* Noninterleaved = 0 */
+ u16 frame_sz; /* 5ms buffers = 160bytes */
+ u16 jitter; /* 10ms of jitter = 320 */
+ u16 lw_mark; /* Low watermark in bytes for triggering event*/
+ u16 hw_mark; /* High watermark bytes for triggering event*/
+ u16 rsvd;
+ int num_ch; /* 1 to 8 */
+} __packed;
+
+#define AFE_PORT_AUDIO_IF_CONFIG 0x000100d3
+#define AFE_PORT_AUDIO_SLIM_SCH_CONFIG 0x000100e4
+#define AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG 0x000100D9
+#define AFE_PORT_CMD_I2S_CONFIG 0x000100E7
+
+union afe_port_config {
+ struct afe_port_pcm_cfg pcm;
+ struct afe_port_mi2s_cfg mi2s;
+ struct afe_port_hdmi_cfg hdmi;
+ struct afe_port_hdmi_multi_ch_cfg hdmi_multi_ch;
+ struct afe_port_slimbus_cfg slimbus;
+ struct afe_port_slimbus_sch_cfg slim_sch;
+ struct afe_port_rtproxy_cfg rtproxy;
+} __attribute__((packed));
+
+struct afe_audioif_config_command {
+ struct apr_hdr hdr;
+ u16 port_id;
+ union afe_port_config port;
+} __attribute__ ((packed));
+
+#define AFE_TEST_CODEC_LOOPBACK_CTL 0x000100d5
+struct afe_codec_loopback_command {
+ u16 port_inf; /* Primary i2s = 0 */
+ /* PCM = 2 */
+ /* Secondary i2s = 4 */
+ /* Mi2s = 6 */
+ u16 enable; /* 0, disable. 1, enable */
+} __attribute__ ((packed));
+
+
+#define AFE_PARAM_ID_SIDETONE_GAIN 0x00010300
+struct afe_param_sidetone_gain {
+ u16 gain;
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define AFE_PARAM_ID_SAMPLING_RATE 0x00010301
+struct afe_param_sampling_rate {
+ u32 sampling_rate;
+} __attribute__ ((packed));
+
+
+#define AFE_PARAM_ID_CHANNELS 0x00010302
+struct afe_param_channels {
+ u16 channels;
+ u16 reserved;
+} __attribute__ ((packed));
+
+
+#define AFE_PARAM_ID_LOOPBACK_GAIN 0x00010303
+struct afe_param_loopback_gain {
+ u16 gain;
+ u16 reserved;
+} __attribute__ ((packed));
+
+/* Parameter ID used to configure and enable/disable the loopback path. The
+ * difference with respect to the existing API, AFE_PORT_CMD_LOOPBACK, is that
+ * it allows Rx port to be configured as source port in loopback path. Port-id
+ * in AFE_PORT_CMD_SET_PARAM cmd is the source port whcih can be Tx or Rx port.
+ * In addition, we can configure the type of routing mode to handle different
+ * use cases.
+*/
+enum {
+ /* Regular loopback from source to destination port */
+ LB_MODE_DEFAULT = 1,
+ /* Sidetone feed from Tx source to Rx destination port */
+ LB_MODE_SIDETONE,
+ /* Echo canceller reference, voice + audio + DTMF */
+ LB_MODE_EC_REF_VOICE_AUDIO,
+ /* Echo canceller reference, voice alone */
+ LB_MODE_EC_REF_VOICE
+};
+
+#define AFE_PARAM_ID_LOOPBACK_CONFIG 0x0001020B
+#define AFE_API_VERSION_LOOPBACK_CONFIG 0x1
+struct afe_param_loopback_cfg {
+ /* Minor version used for tracking the version of the configuration
+ * interface.
+ */
+ uint32_t loopback_cfg_minor_version;
+
+ /* Destination Port Id. */
+ uint16_t dst_port_id;
+
+ /* Specifies data path type from src to dest port. Supported values:
+ * LB_MODE_DEFAULT
+ * LB_MODE_SIDETONE
+ * LB_MODE_EC_REF_VOICE_AUDIO
+ * LB_MODE_EC_REF_VOICE
+ */
+ uint16_t routing_mode;
+
+ /* Specifies whether to enable (1) or disable (0) an AFE loopback. */
+ uint16_t enable;
+
+ /* Reserved for 32-bit alignment. This field must be set to 0. */
+ uint16_t reserved;
+} __packed;
+
+#define AFE_MODULE_ID_PORT_INFO 0x00010200
+/* Module ID for the loopback-related parameters. */
+#define AFE_MODULE_LOOPBACK 0x00010205
+struct afe_param_payload_base {
+ u32 module_id;
+ u32 param_id;
+ u16 param_size;
+ u16 reserved;
+} __packed;
+
+struct afe_param_payload {
+ struct afe_param_payload_base base;
+ union {
+ struct afe_param_sidetone_gain sidetone_gain;
+ struct afe_param_sampling_rate sampling_rate;
+ struct afe_param_channels channels;
+ struct afe_param_loopback_gain loopback_gain;
+ struct afe_param_loopback_cfg loopback_cfg;
+ } __attribute__((packed)) param;
+} __attribute__ ((packed));
+
+#define AFE_PORT_CMD_SET_PARAM 0x000100dc
+
+struct afe_port_cmd_set_param {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 payload_size;
+ u32 payload_address;
+ struct afe_param_payload payload;
+} __attribute__ ((packed));
+
+struct afe_port_cmd_set_param_no_payload {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 payload_size;
+ u32 payload_address;
+} __packed;
+
+#define AFE_EVENT_GET_ACTIVE_PORTS 0x00010100
+struct afe_get_active_ports_rsp {
+ u16 num_ports;
+ u16 port_id;
+} __attribute__ ((packed));
+
+
+#define AFE_EVENT_GET_ACTIVE_HANDLES 0x00010102
+struct afe_get_active_handles_rsp {
+ u16 port_id;
+ u16 num_handles;
+ u16 mode; /* 0, voice rx */
+ /* 1, voice tx */
+ /* 2, audio rx */
+ /* 3, audio tx */
+ u16 handle;
+} __attribute__ ((packed));
+
+#define AFE_SERVICE_CMD_MEMORY_MAP 0x000100DE
+struct afe_cmd_memory_map {
+ struct apr_hdr hdr;
+ u32 phy_addr;
+ u32 mem_sz;
+ u16 mem_id;
+ u16 rsvd;
+} __packed;
+
+#define AFE_SERVICE_CMD_MEMORY_UNMAP 0x000100DF
+struct afe_cmd_memory_unmap {
+ struct apr_hdr hdr;
+ u32 phy_addr;
+} __packed;
+
+#define AFE_SERVICE_CMD_REG_RTPORT 0x000100E0
+struct afe_cmd_reg_rtport {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 rsvd;
+} __packed;
+
+#define AFE_SERVICE_CMD_UNREG_RTPORT 0x000100E1
+struct afe_cmd_unreg_rtport {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 rsvd;
+} __packed;
+
+#define AFE_SERVICE_CMD_RTPORT_WR 0x000100E2
+struct afe_cmd_rtport_wr {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 rsvd;
+ u32 buf_addr;
+ u32 bytes_avail;
+} __packed;
+
+#define AFE_SERVICE_CMD_RTPORT_RD 0x000100E3
+struct afe_cmd_rtport_rd {
+ struct apr_hdr hdr;
+ u16 port_id;
+ u16 rsvd;
+ u32 buf_addr;
+ u32 bytes_avail;
+} __packed;
+
+#define AFE_EVENT_RT_PROXY_PORT_STATUS 0x00010105
+
+#define ADM_MAX_COPPS 5
+
+#define ADM_SERVICE_CMD_GET_COPP_HANDLES 0x00010300
+struct adm_get_copp_handles_command {
+ struct apr_hdr hdr;
+} __attribute__ ((packed));
+
+#define ADM_CMD_MATRIX_MAP_ROUTINGS 0x00010301
+struct adm_routings_session {
+ u16 id;
+ u16 num_copps;
+ u16 copp_id[ADM_MAX_COPPS+1]; /*Padding if numCopps is odd */
+} __packed;
+
+struct adm_routings_command {
+ struct apr_hdr hdr;
+ u32 path; /* 0 = Rx, 1 Tx */
+ u32 num_sessions;
+ struct adm_routings_session session[8];
+} __attribute__ ((packed));
+
+
+#define ADM_CMD_MATRIX_RAMP_GAINS 0x00010302
+struct adm_ramp_gain {
+ struct apr_hdr hdr;
+ u16 session_id;
+ u16 copp_id;
+ u16 initial_gain;
+ u16 gain_increment;
+ u16 ramp_duration;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct adm_ramp_gains_command {
+ struct apr_hdr hdr;
+ u32 id;
+ u32 num_gains;
+ struct adm_ramp_gain gains[ADM_MAX_COPPS];
+} __attribute__ ((packed));
+
+
+#define ADM_CMD_COPP_OPEN 0x00010304
+struct adm_copp_open_command {
+ struct apr_hdr hdr;
+ u16 flags;
+ u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */
+ u16 endpoint_id1;
+ u16 endpoint_id2;
+ u32 topology_id;
+ u16 channel_config;
+ u16 reserved;
+ u32 rate;
+} __attribute__ ((packed));
+
+#define ADM_CMD_COPP_CLOSE 0x00010305
+
+#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN 0x00010310
+#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3 0x00010333
+struct adm_multi_ch_copp_open_command {
+ struct apr_hdr hdr;
+ u16 flags;
+ u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */
+ u16 endpoint_id1;
+ u16 endpoint_id2;
+ u32 topology_id;
+ u16 channel_config;
+ u16 reserved;
+ u32 rate;
+ u8 dev_channel_mapping[8];
+} __packed;
+#define ADM_CMD_MEMORY_MAP 0x00010C30
+struct adm_cmd_memory_map{
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u16 mempool_id;
+ u16 reserved;
+} __attribute__((packed));
+
+#define ADM_CMD_MEMORY_UNMAP 0x00010C31
+struct adm_cmd_memory_unmap{
+ struct apr_hdr hdr;
+ u32 buf_add;
+} __attribute__((packed));
+
+#define ADM_CMD_MEMORY_MAP_REGIONS 0x00010C47
+struct adm_memory_map_regions{
+ u32 phys;
+ u32 buf_size;
+} __attribute__((packed));
+
+struct adm_cmd_memory_map_regions{
+ struct apr_hdr hdr;
+ u16 mempool_id;
+ u16 nregions;
+} __attribute__((packed));
+
+#define ADM_CMD_MEMORY_UNMAP_REGIONS 0x00010C48
+struct adm_memory_unmap_regions{
+ u32 phys;
+} __attribute__((packed));
+
+struct adm_cmd_memory_unmap_regions{
+ struct apr_hdr hdr;
+ u16 nregions;
+ u16 reserved;
+} __attribute__((packed));
+
+#define DEFAULT_COPP_TOPOLOGY 0x00010be3
+#define DEFAULT_POPP_TOPOLOGY 0x00010be4
+#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71
+#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72
+#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75
+
+#define LOWLATENCY_POPP_TOPOLOGY 0x00010C68
+#define LOWLATENCY_COPP_TOPOLOGY 0x00010312
+#define PCM_BITS_PER_SAMPLE 16
+
+#define ASM_OPEN_WRITE_PERF_MODE_BIT (1<<28)
+#define ASM_OPEN_READ_PERF_MODE_BIT (1<<29)
+#define ADM_MULTI_CH_COPP_OPEN_PERF_MODE_BIT (1<<13)
+
+/* SRS TRUMEDIA GUIDS */
+/* topology */
+#define SRS_TRUMEDIA_TOPOLOGY_ID 0x00010D90
+/* module */
+#define SRS_TRUMEDIA_MODULE_ID 0x10005010
+/* parameters */
+#define SRS_TRUMEDIA_PARAMS 0x10005011
+#define SRS_TRUMEDIA_PARAMS_WOWHD 0x10005012
+#define SRS_TRUMEDIA_PARAMS_CSHP 0x10005013
+#define SRS_TRUMEDIA_PARAMS_HPF 0x10005014
+#define SRS_TRUMEDIA_PARAMS_PEQ 0x10005015
+#define SRS_TRUMEDIA_PARAMS_HL 0x10005016
+
+#define ASM_MAX_EQ_BANDS 12
+
+struct asm_eq_band {
+ u32 band_idx; /* The band index, 0 .. 11 */
+ u32 filter_type; /* Filter band type */
+ u32 center_freq_hz; /* Filter band center frequency */
+ u32 filter_gain; /* Filter band initial gain (dB) */
+ /* Range is +12 dB to -12 dB with 1dB increments. */
+ u32 q_factor;
+} __attribute__ ((packed));
+
+struct asm_equalizer_params {
+ u32 enable;
+ u32 num_bands;
+ struct asm_eq_band eq_bands[ASM_MAX_EQ_BANDS];
+} __attribute__ ((packed));
+
+struct asm_master_gain_params {
+ u16 master_gain;
+ u16 padding;
+} __attribute__ ((packed));
+
+struct asm_lrchannel_gain_params {
+ u16 left_gain;
+ u16 right_gain;
+} __attribute__ ((packed));
+
+struct asm_mute_params {
+ u32 muteflag;
+} __attribute__ ((packed));
+
+struct asm_softvolume_params {
+ u32 period;
+ u32 step;
+ u32 rampingcurve;
+} __attribute__ ((packed));
+
+struct asm_softpause_params {
+ u32 enable;
+ u32 period;
+ u32 step;
+ u32 rampingcurve;
+} __packed;
+
+struct asm_pp_param_data_hdr {
+ u32 module_id;
+ u32 param_id;
+ u16 param_size;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct asm_pp_params_command {
+ struct apr_hdr hdr;
+ u32 *payload;
+ u32 payload_size;
+ struct asm_pp_param_data_hdr params;
+} __attribute__ ((packed));
+
+#define EQUALIZER_MODULE_ID 0x00010c27
+#define EQUALIZER_PARAM_ID 0x00010c28
+
+#define VOLUME_CONTROL_MODULE_ID 0x00010bfe
+#define MASTER_GAIN_PARAM_ID 0x00010bff
+#define L_R_CHANNEL_GAIN_PARAM_ID 0x00010c00
+#define MUTE_CONFIG_PARAM_ID 0x00010c01
+#define SOFT_PAUSE_PARAM_ID 0x00010D6A
+#define SOFT_VOLUME_PARAM_ID 0x00010C29
+
+#define IIR_FILTER_ENABLE_PARAM_ID 0x00010c03
+#define IIR_FILTER_PREGAIN_PARAM_ID 0x00010c04
+#define IIR_FILTER_CONFIG_PARAM_ID 0x00010c05
+
+#define MBADRC_MODULE_ID 0x00010c06
+#define MBADRC_ENABLE_PARAM_ID 0x00010c07
+#define MBADRC_CONFIG_PARAM_ID 0x00010c08
+
+
+#define ADM_CMD_SET_PARAMS 0x00010306
+#define ADM_CMD_GET_PARAMS 0x0001030B
+#define ADM_CMDRSP_GET_PARAMS 0x0001030C
+struct adm_set_params_command {
+ struct apr_hdr hdr;
+ u32 payload;
+ u32 payload_size;
+} __attribute__ ((packed));
+
+
+#define ADM_CMD_TAP_COPP_PCM 0x00010307
+struct adm_tap_copp_pcm_command {
+ struct apr_hdr hdr;
+} __attribute__ ((packed));
+
+
+/* QDSP6 to Client messages
+*/
+#define ADM_SERVICE_CMDRSP_GET_COPP_HANDLES 0x00010308
+struct adm_get_copp_handles_respond {
+ struct apr_hdr hdr;
+ u32 handles;
+ u32 copp_id;
+} __attribute__ ((packed));
+
+#define ADM_CMDRSP_COPP_OPEN 0x0001030A
+struct adm_copp_open_respond {
+ u32 status;
+ u16 copp_id;
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN 0x00010311
+#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3 0x00010334
+
+
+#define ASM_STREAM_PRIORITY_NORMAL 0
+#define ASM_STREAM_PRIORITY_LOW 1
+#define ASM_STREAM_PRIORITY_HIGH 2
+#define ASM_STREAM_PRIORITY_RESERVED 3
+
+#define ASM_END_POINT_DEVICE_MATRIX 0
+#define ASM_END_POINT_STREAM 1
+
+#define AAC_ENC_MODE_AAC_LC 0x02
+#define AAC_ENC_MODE_AAC_P 0x05
+#define AAC_ENC_MODE_EAAC_P 0x1D
+
+#define ASM_STREAM_CMD_CLOSE 0x00010BCD
+#define ASM_STREAM_CMD_FLUSH 0x00010BCE
+#define ASM_STREAM_CMD_SET_PP_PARAMS 0x00010BCF
+#define ASM_STREAM_CMD_GET_PP_PARAMS 0x00010BD0
+#define ASM_STREAM_CMDRSP_GET_PP_PARAMS 0x00010BD1
+#define ASM_SESSION_CMD_PAUSE 0x00010BD3
+#define ASM_SESSION_CMD_GET_SESSION_TIME 0x00010BD4
+#define ASM_DATA_CMD_EOS 0x00010BDB
+#define ASM_DATA_EVENT_EOS 0x00010BDD
+
+#define ASM_SERVICE_CMD_GET_STREAM_HANDLES 0x00010C0B
+#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09
+
+#define ASM_SESSION_EVENT_RX_UNDERFLOW 0x00010C17
+#define ASM_SESSION_EVENT_TX_OVERFLOW 0x00010C18
+#define ASM_SERVICE_CMD_GET_WALLCLOCK_TIME 0x00010C19
+#define ASM_DATA_CMDRSP_EOS 0x00010C1C
+
+/* ASM Data structures */
+
+/* common declarations */
+struct asm_pcm_cfg {
+ u16 ch_cfg;
+ u16 bits_per_sample;
+ u32 sample_rate;
+ u16 is_signed;
+ u16 interleaved;
+};
+
+#define PCM_CHANNEL_NULL 0
+
+/* Front left channel. */
+#define PCM_CHANNEL_FL 1
+
+/* Front right channel. */
+#define PCM_CHANNEL_FR 2
+
+/* Front center channel. */
+#define PCM_CHANNEL_FC 3
+
+/* Left surround channel.*/
+#define PCM_CHANNEL_LS 4
+
+/* Right surround channel.*/
+#define PCM_CHANNEL_RS 5
+
+/* Low frequency effect channel. */
+#define PCM_CHANNEL_LFE 6
+
+/* Center surround channel; Rear center channel. */
+#define PCM_CHANNEL_CS 7
+
+/* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_LB 8
+
+/* Right back channel; Rear right channel. */
+#define PCM_CHANNEL_RB 9
+
+/* Top surround channel. */
+#define PCM_CHANNEL_TS 10
+
+/* Center vertical height channel.*/
+#define PCM_CHANNEL_CVH 11
+
+/* Mono surround channel.*/
+#define PCM_CHANNEL_MS 12
+
+/* Front left of center. */
+#define PCM_CHANNEL_FLC 13
+
+/* Front right of center. */
+#define PCM_CHANNEL_FRC 14
+
+/* Rear left of center. */
+#define PCM_CHANNEL_RLC 15
+
+/* Rear right of center. */
+#define PCM_CHANNEL_RRC 16
+
+#define PCM_FORMAT_MAX_NUM_CHANNEL 8
+
+/* Maximum number of channels supported
+ * in ASM_ENCDEC_DEC_CHAN_MAP command
+ */
+#define MAX_CHAN_MAP_CHANNELS 16
+/*
+ * Multiple-channel PCM decoder format block structure used in the
+ * #ASM_STREAM_CMD_OPEN_WRITE command.
+ * The data must be in little-endian format.
+ */
+struct asm_multi_channel_pcm_fmt_blk {
+
+ u16 num_channels; /*
+ * Number of channels.
+ * Supported values:1 to 8
+ */
+
+ u16 bits_per_sample; /*
+ * Number of bits per sample per channel.
+ * Supported values: 16, 24 When used for
+ * playback, the client must send 24-bit
+ * samples packed in 32-bit words. The
+ * 24-bit samples must be placed in the most
+ * significant 24 bits of the 32-bit word. When
+ * used for recording, the aDSP sends 24-bit
+ * samples packed in 32-bit words. The 24-bit
+ * samples are placed in the most significant
+ * 24 bits of the 32-bit word.
+ */
+
+ u32 sample_rate; /*
+ * Number of samples per second
+ * (in Hertz). Supported values:
+ * 2000 to 48000
+ */
+
+ u16 is_signed; /*
+ * Flag that indicates the samples
+ * are signed (1).
+ */
+
+ u16 is_interleaved; /*
+ * Flag that indicates whether the channels are
+ * de-interleaved (0) or interleaved (1).
+ * Interleaved format means corresponding
+ * samples from the left and right channels are
+ * interleaved within the buffer.
+ * De-interleaved format means samples from
+ * each channel are contiguous in the buffer.
+ * The samples from one channel immediately
+ * follow those of the previous channel.
+ */
+
+ u8 channel_mapping[8]; /*
+ * Supported values:
+ * PCM_CHANNEL_NULL, PCM_CHANNEL_FL,
+ * PCM_CHANNEL_FR, PCM_CHANNEL_FC,
+ * PCM_CHANNEL_LS, PCM_CHANNEL_RS,
+ * PCM_CHANNEL_LFE, PCM_CHANNEL_CS,
+ * PCM_CHANNEL_LB, PCM_CHANNEL_RB,
+ * PCM_CHANNEL_TS, PCM_CHANNEL_CVH,
+ * PCM_CHANNEL_MS, PCM_CHANNEL_FLC,
+ * PCM_CHANNEL_FRC, PCM_CHANNEL_RLC,
+ * PCM_CHANNEL_RRC.
+ * Channel[i] mapping describes channel I. Each
+ * element i of the array describes channel I
+ * inside the buffer where I < num_channels.
+ * An unused channel is set to zero.
+ */
+};
+
+struct asm_adpcm_cfg {
+ u16 ch_cfg;
+ u16 bits_per_sample;
+ u32 sample_rate;
+ u32 block_size;
+};
+
+struct asm_yadpcm_cfg {
+ u16 ch_cfg;
+ u16 bits_per_sample;
+ u32 sample_rate;
+};
+
+struct asm_midi_cfg {
+ u32 nMode;
+};
+
+struct asm_wma_cfg {
+ u16 format_tag;
+ u16 ch_cfg;
+ u32 sample_rate;
+ u32 avg_bytes_per_sec;
+ u16 block_align;
+ u16 valid_bits_per_sample;
+ u32 ch_mask;
+ u16 encode_opt;
+ u16 adv_encode_opt;
+ u32 adv_encode_opt2;
+ u32 drc_peak_ref;
+ u32 drc_peak_target;
+ u32 drc_ave_ref;
+ u32 drc_ave_target;
+};
+
+struct asm_wmapro_cfg {
+ u16 format_tag;
+ u16 ch_cfg;
+ u32 sample_rate;
+ u32 avg_bytes_per_sec;
+ u16 block_align;
+ u16 valid_bits_per_sample;
+ u32 ch_mask;
+ u16 encode_opt;
+ u16 adv_encode_opt;
+ u32 adv_encode_opt2;
+ u32 drc_peak_ref;
+ u32 drc_peak_target;
+ u32 drc_ave_ref;
+ u32 drc_ave_target;
+};
+
+struct asm_aac_cfg {
+ u16 format;
+ u16 aot;
+ u16 ep_config;
+ u16 section_data_resilience;
+ u16 scalefactor_data_resilience;
+ u16 spectral_data_resilience;
+ u16 ch_cfg;
+ u16 reserved;
+ u32 sample_rate;
+};
+
+struct asm_amrwbplus_cfg {
+ u32 size_bytes;
+ u32 version;
+ u32 num_channels;
+ u32 amr_band_mode;
+ u32 amr_dtx_mode;
+ u32 amr_frame_fmt;
+ u32 amr_lsf_idx;
+};
+
+struct asm_flac_cfg {
+ u16 stream_info_present;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 ch_cfg;
+ u16 sample_size;
+ u16 sample_rate;
+ u16 md5_sum;
+ u32 ext_sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+};
+
+struct asm_vorbis_cfg {
+ u32 ch_cfg;
+ u32 bit_rate;
+ u32 min_bit_rate;
+ u32 max_bit_rate;
+ u16 bit_depth_pcm_sample;
+ u16 bit_stream_format;
+};
+
+struct asm_aac_read_cfg {
+ u32 bitrate;
+ u32 enc_mode;
+ u16 format;
+ u16 ch_cfg;
+ u32 sample_rate;
+};
+
+struct asm_amrnb_read_cfg {
+ u16 mode;
+ u16 dtx_mode;
+};
+
+struct asm_amrwb_read_cfg {
+ u16 mode;
+ u16 dtx_mode;
+};
+
+struct asm_evrc_read_cfg {
+ u16 max_rate;
+ u16 min_rate;
+ u16 rate_modulation_cmd;
+ u16 reserved;
+};
+
+struct asm_qcelp13_read_cfg {
+ u16 max_rate;
+ u16 min_rate;
+ u16 reduced_rate_level;
+ u16 rate_modulation_cmd;
+};
+
+struct asm_sbc_read_cfg {
+ u32 subband;
+ u32 block_len;
+ u32 ch_mode;
+ u32 alloc_method;
+ u32 bit_rate;
+ u32 sample_rate;
+};
+
+struct asm_sbc_bitrate {
+ u32 bitrate;
+};
+
+struct asm_immed_decode {
+ u32 mode;
+};
+
+struct asm_sbr_ps {
+ u32 enable;
+};
+
+struct asm_dual_mono {
+ u16 sce_left;
+ u16 sce_right;
+};
+
+struct asm_dec_chan_map {
+ u32 num_channels; /* Number of decoder output
+ * channels. A value of 0
+ * indicates native channel
+ * mapping, which is valid
+ * only for NT mode. This
+ * means the output of the
+ * decoder is to be preserved
+ * as is.
+ */
+
+ u8 channel_mapping[MAX_CHAN_MAP_CHANNELS];/* Channel array of size
+ * num_channels. It can grow
+ * till MAX_CHAN_MAP_CHANNELS.
+ * Channel[i] mapping
+ * describes channel I inside
+ * the decoder output buffer.
+ * Valid channel mapping
+ * values are to be present at
+ * the beginning of the array.
+ * All remaining elements of
+ * the array are to be filled
+ * with PCM_CHANNEL_NULL.
+ */
+};
+
+struct asm_encode_cfg_blk {
+ u32 frames_per_buf;
+ u32 format_id;
+ u32 cfg_size;
+ union {
+ struct asm_pcm_cfg pcm;
+ struct asm_aac_read_cfg aac;
+ struct asm_amrnb_read_cfg amrnb;
+ struct asm_evrc_read_cfg evrc;
+ struct asm_qcelp13_read_cfg qcelp13;
+ struct asm_sbc_read_cfg sbc;
+ struct asm_amrwb_read_cfg amrwb;
+ struct asm_multi_channel_pcm_fmt_blk mpcm;
+ } __attribute__((packed)) cfg;
+};
+
+struct asm_frame_meta_info {
+ u32 offset_to_frame;
+ u32 frame_size;
+ u32 encoded_pcm_samples;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 nflags;
+};
+
+/* Stream level commands */
+#define ASM_STREAM_CMD_OPEN_READ 0x00010BCB
+#define ASM_STREAM_CMD_OPEN_READ_V2_1 0x00010DB2
+struct asm_stream_cmd_open_read {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u32 src_endpoint;
+ u32 pre_proc_top;
+ u32 format;
+} __attribute__((packed));
+
+struct asm_stream_cmd_open_read_v2_1 {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u32 src_endpoint;
+ u32 pre_proc_top;
+ u32 format;
+ u16 bits_per_sample;
+ u16 reserved;
+} __packed;
+
+/* Supported formats */
+#define LINEAR_PCM 0x00010BE5
+#define DTMF 0x00010BE6
+#define ADPCM 0x00010BE7
+#define YADPCM 0x00010BE8
+#define MP3 0x00010BE9
+#define MPEG4_AAC 0x00010BEA
+#define AMRNB_FS 0x00010BEB
+#define AMRWB_FS 0x00010BEC
+#define V13K_FS 0x00010BED
+#define EVRC_FS 0x00010BEE
+#define EVRCB_FS 0x00010BEF
+#define EVRCWB_FS 0x00010BF0
+#define MIDI 0x00010BF1
+#define SBC 0x00010BF2
+#define WMA_V10PRO 0x00010BF3
+#define WMA_V9 0x00010BF4
+#define AMR_WB_PLUS 0x00010BF5
+#define AC3_DECODER 0x00010BF6
+#define EAC3_DECODER 0x00010C3C
+#define DTS 0x00010D88
+#define DTS_LBR 0x00010DBB
+#define ATRAC 0x00010D89
+#define MAT 0x00010D8A
+#define G711_ALAW_FS 0x00010BF7
+#define G711_MLAW_FS 0x00010BF8
+#define G711_PCM_FS 0x00010BF9
+#define MPEG4_MULTI_AAC 0x00010D86
+#define US_POINT_EPOS_FORMAT 0x00012310
+#define US_RAW_FORMAT 0x0001127C
+#define MULTI_CHANNEL_PCM 0x00010C66
+
+#define ASM_ENCDEC_SBCRATE 0x00010C13
+#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14
+#define ASM_ENCDEC_CFG_BLK 0x00010C2C
+
+#define ASM_ENCDEC_SBCRATE 0x00010C13
+#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14
+#define ASM_ENCDEC_CFG_BLK 0x00010C2C
+
+#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED 0x00010D95
+struct asm_stream_cmd_open_read_compressed {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u32 frame_per_buf;
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_WRITE 0x00010BCA
+#define ASM_STREAM_CMD_OPEN_WRITE_V2_1 0x00010DB1
+struct asm_stream_cmd_open_write {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u16 sink_endpoint;
+ u16 stream_handle;
+ u32 post_proc_top;
+ u32 format;
+} __attribute__((packed));
+
+#define IEC_61937_MASK 0x00000001
+#define IEC_60958_MASK 0x00000002
+
+#define ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED 0x00010D84
+struct asm_stream_cmd_open_write_compressed {
+ struct apr_hdr hdr;
+ u32 flags;
+ u32 format;
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_READWRITE 0x00010BCC
+
+struct asm_stream_cmd_open_read_write {
+ struct apr_hdr hdr;
+ u32 uMode;
+ u32 post_proc_top;
+ u32 write_format;
+ u32 read_format;
+} __attribute__((packed));
+
+#define ASM_STREAM_CMD_OPEN_LOOPBACK 0x00010D6E
+struct asm_stream_cmd_open_loopback {
+ struct apr_hdr hdr;
+ u32 mode_flags;
+/* Mode flags.
+ * Bit 0-31: reserved; client should set these bits to 0
+ */
+ u16 src_endpointype;
+ /* Endpoint type. 0 = Tx Matrix */
+ u16 sink_endpointype;
+ /* Endpoint type. 0 = Rx Matrix */
+ u32 postprocopo_id;
+/* Postprocessor topology ID. Specifies the topology of
+ * postprocessing algorithms.
+ */
+} __packed;
+
+#define ADM_CMD_CONNECT_AFE_PORT 0x00010320
+#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321
+
+struct adm_cmd_connect_afe_port {
+ struct apr_hdr hdr;
+ u8 mode; /*mode represent the interface is for RX or TX*/
+ u8 session_id; /*ASM session ID*/
+ u16 afe_port_id;
+} __packed;
+
+#define ADM_CMD_CONNECT_AFE_PORT_V2 0x00010332
+
+struct adm_cmd_connect_afe_port_v2 {
+ struct apr_hdr hdr;
+ u8 mode; /*mode represent the interface is for RX or TX*/
+ u8 session_id; /*ASM session ID*/
+ u16 afe_port_id;
+ u32 num_channels;
+ u32 sampleing_rate;
+} __packed;
+
+#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10
+#define ASM_STREAM_CMD_GET_ENCDEC_PARAM 0x00010C11
+#define ASM_ENCDEC_CFG_BLK_ID 0x00010C2C
+#define ASM_ENABLE_SBR_PS 0x00010C63
+#define ASM_CONFIGURE_DUAL_MONO 0x00010C64
+struct asm_stream_cmd_encdec_cfg_blk{
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct asm_encode_cfg_blk enc_blk;
+} __attribute__((packed));
+
+struct asm_stream_cmd_encdec_sbc_bitrate{
+ struct apr_hdr hdr;
+ u32 param_id;
+ struct asm_sbc_bitrate sbc_bitrate;
+} __attribute__((packed));
+
+struct asm_stream_cmd_encdec_immed_decode{
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct asm_immed_decode dec;
+} __attribute__((packed));
+
+struct asm_stream_cmd_encdec_sbr{
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct asm_sbr_ps sbr_ps;
+} __attribute__((packed));
+
+struct asm_stream_cmd_encdec_dualmono {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct asm_dual_mono channel_map;
+} __packed;
+
+#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG 0x00010DD8
+
+/* Structure for AAC decoder stereo coefficient setting. */
+
+struct asm_aac_stereo_mix_coeff_selection_param {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ u32 aac_stereo_mix_coeff_flag;
+} __packed;
+
+#define ASM_ENCDEC_DEC_CHAN_MAP 0x00010D82
+struct asm_stream_cmd_encdec_channelmap {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct asm_dec_chan_map chan_map;
+} __packed;
+
+#define ASM_STREAM _CMD_ADJUST_SAMPLES 0x00010C0A
+struct asm_stream_cmd_adjust_samples{
+ struct apr_hdr hdr;
+ u16 nsamples;
+ u16 reserved;
+} __attribute__((packed));
+
+#define ASM_STREAM_CMD_TAP_POPP_PCM 0x00010BF9
+struct asm_stream_cmd_tap_popp_pcm{
+ struct apr_hdr hdr;
+ u16 enable;
+ u16 reserved;
+ u32 module_id;
+} __attribute__((packed));
+
+/* Session Level commands */
+#define ASM_SESSION_CMD_MEMORY_MAP 0x00010C32
+struct asm_stream_cmd_memory_map{
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u16 mempool_id;
+ u16 reserved;
+} __attribute__((packed));
+
+#define ASM_SESSION_CMD_MEMORY_UNMAP 0x00010C33
+struct asm_stream_cmd_memory_unmap{
+ struct apr_hdr hdr;
+ u32 buf_add;
+} __attribute__((packed));
+
+#define ASM_SESSION_CMD_MEMORY_MAP_REGIONS 0x00010C45
+struct asm_memory_map_regions{
+ u32 phys;
+ u32 buf_size;
+} __attribute__((packed));
+
+struct asm_stream_cmd_memory_map_regions{
+ struct apr_hdr hdr;
+ u16 mempool_id;
+ u16 nregions;
+} __attribute__((packed));
+
+#define ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS 0x00010C46
+struct asm_memory_unmap_regions{
+ u32 phys;
+} __attribute__((packed));
+
+struct asm_stream_cmd_memory_unmap_regions{
+ struct apr_hdr hdr;
+ u16 nregions;
+ u16 reserved;
+} __attribute__((packed));
+
+#define ASM_SESSION_CMD_RUN 0x00010BD2
+struct asm_stream_cmd_run{
+ struct apr_hdr hdr;
+ u32 flags;
+ u32 msw_ts;
+ u32 lsw_ts;
+} __attribute__((packed));
+
+/* Session level events */
+#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5
+struct asm_stream_cmd_reg_rx_underflow_event{
+ struct apr_hdr hdr;
+ u16 enable;
+ u16 reserved;
+} __attribute__((packed));
+
+#define ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS 0x00010BD6
+struct asm_stream_cmd_reg_tx_overflow_event{
+ struct apr_hdr hdr;
+ u16 enable;
+ u16 reserved;
+} __attribute__((packed));
+
+/* Data Path commands */
+#define ASM_DATA_CMD_WRITE 0x00010BD9
+struct asm_stream_cmd_write{
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 avail_bytes;
+ u32 uid;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 uflags;
+} __attribute__((packed));
+
+#define ASM_DATA_CMD_READ 0x00010BDA
+struct asm_stream_cmd_read{
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u32 uid;
+} __attribute__((packed));
+
+#define ASM_DATA_CMD_READ_COMPRESSED 0x00010DBF
+struct asm_stream_cmd_read_compressed {
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u32 uid;
+} __packed;
+
+#define ASM_DATA_CMD_MEDIA_FORMAT_UPDATE 0x00010BDC
+#define ASM_DATA_EVENT_ENC_SR_CM_NOTIFY 0x00010BDE
+struct asm_stream_media_format_update{
+ struct apr_hdr hdr;
+ u32 format;
+ u32 cfg_size;
+ union {
+ struct asm_pcm_cfg pcm_cfg;
+ struct asm_adpcm_cfg adpcm_cfg;
+ struct asm_yadpcm_cfg yadpcm_cfg;
+ struct asm_midi_cfg midi_cfg;
+ struct asm_wma_cfg wma_cfg;
+ struct asm_wmapro_cfg wmapro_cfg;
+ struct asm_aac_cfg aac_cfg;
+ struct asm_flac_cfg flac_cfg;
+ struct asm_vorbis_cfg vorbis_cfg;
+ struct asm_multi_channel_pcm_fmt_blk multi_ch_pcm_cfg;
+ struct asm_amrwbplus_cfg amrwbplus_cfg;
+ } __attribute__((packed)) write_cfg;
+} __attribute__((packed));
+
+
+/* Command Responses */
+#define ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM 0x00010C12
+struct asm_stream_cmdrsp_get_readwrite_param{
+ struct apr_hdr hdr;
+ u32 status;
+ u32 param_id;
+ u16 param_size;
+ u16 padding;
+ union {
+ struct asm_sbc_bitrate sbc_bitrate;
+ struct asm_immed_decode aac_dec;
+ } __attribute__((packed)) read_write_cfg;
+} __attribute__((packed));
+
+
+#define ASM_SESSION_CMDRSP_GET_SESSION_TIME 0x00010BD8
+struct asm_stream_cmdrsp_get_session_time{
+ struct apr_hdr hdr;
+ u32 status;
+ u32 msw_ts;
+ u32 lsw_ts;
+} __attribute__((packed));
+
+#define ASM_DATA_EVENT_WRITE_DONE 0x00010BDF
+struct asm_data_event_write_done{
+ u32 buf_add;
+ u32 status;
+} __attribute__((packed));
+
+#define ASM_DATA_EVENT_READ_DONE 0x00010BE0
+struct asm_data_event_read_done{
+ u32 status;
+ u32 buffer_add;
+ u32 enc_frame_size;
+ u32 offset;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 flags;
+ u32 num_frames;
+ u32 id;
+} __attribute__((packed));
+
+#define ASM_DATA_EVENT_READ_COMPRESSED_DONE 0x00010DC0
+struct asm_data_event_read_compressed_done {
+ u32 status;
+ u32 buffer_add;
+ u32 enc_frame_size;
+ u32 offset;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 flags;
+ u32 num_frames;
+ u32 id;
+} __packed;
+
+#define ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY 0x00010C65
+struct asm_data_event_sr_cm_change_notify {
+ u32 sample_rate;
+ u16 no_of_channels;
+ u16 reserved;
+ u8 channel_map[8];
+} __packed;
+
+/* service level events */
+
+#define ASM_SERVICE_CMDRSP_GET_STREAM_HANDLES 0x00010C1B
+struct asm_svc_cmdrsp_get_strm_handles{
+ struct apr_hdr hdr;
+ u32 num_handles;
+ u32 stream_handles;
+} __attribute__((packed));
+
+
+#define ASM_SERVICE_CMDRSP_GET_WALLCLOCK_TIME 0x00010C1A
+struct asm_svc_cmdrsp_get_wallclock_time{
+ struct apr_hdr hdr;
+ u32 status;
+ u32 msw_ts;
+ u32 lsw_ts;
+} __attribute__((packed));
+
+/*
+ * Error code
+*/
+#define ADSP_EOK 0x00000000 /* Success / completed / no errors. */
+#define ADSP_EFAILED 0x00000001 /* General failure. */
+#define ADSP_EBADPARAM 0x00000002 /* Bad operation parameter(s). */
+#define ADSP_EUNSUPPORTED 0x00000003 /* Unsupported routine/operation. */
+#define ADSP_EVERSION 0x00000004 /* Unsupported version. */
+#define ADSP_EUNEXPECTED 0x00000005 /* Unexpected problem encountered. */
+#define ADSP_EPANIC 0x00000006 /* Unhandled problem occurred. */
+#define ADSP_ENORESOURCE 0x00000007 /* Unable to allocate resource(s). */
+#define ADSP_EHANDLE 0x00000008 /* Invalid handle. */
+#define ADSP_EALREADY 0x00000009 /* Operation is already processed. */
+#define ADSP_ENOTREADY 0x0000000A /* Operation not ready to be processed*/
+#define ADSP_EPENDING 0x0000000B /* Operation is pending completion*/
+#define ADSP_EBUSY 0x0000000C /* Operation could not be accepted or
+ processed. */
+#define ADSP_EABORTED 0x0000000D /* Operation aborted due to an error. */
+#define ADSP_EPREEMPTED 0x0000000E /* Operation preempted by higher priority*/
+#define ADSP_ECONTINUE 0x0000000F /* Operation requests intervention
+ to complete. */
+#define ADSP_EIMMEDIATE 0x00000010 /* Operation requests immediate
+ intervention to complete. */
+#define ADSP_ENOTIMPL 0x00000011 /* Operation is not implemented. */
+#define ADSP_ENEEDMORE 0x00000012 /* Operation needs more data or resources*/
+
+/* SRS TRUMEDIA start */
+#define SRS_ID_GLOBAL 0x00000001
+#define SRS_ID_WOWHD 0x00000002
+#define SRS_ID_CSHP 0x00000003
+#define SRS_ID_HPF 0x00000004
+#define SRS_ID_PEQ 0x00000005
+#define SRS_ID_HL 0x00000006
+
+#define SRS_CMD_UPLOAD 0x7FFF0000
+#define SRS_PARAM_INDEX_MASK 0x80000000
+#define SRS_PARAM_OFFSET_MASK 0x3FFF0000
+#define SRS_PARAM_VALUE_MASK 0x0000FFFF
+
+struct srs_trumedia_params_GLOBAL {
+ uint8_t v1;
+ uint8_t v2;
+ uint8_t v3;
+ uint8_t v4;
+ uint8_t v5;
+ uint8_t v6;
+ uint8_t v7;
+ uint8_t v8;
+} __packed;
+
+struct srs_trumedia_params_WOWHD {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v5;
+ uint16_t v6;
+ uint16_t v7;
+ uint16_t v8;
+ uint16_t v____A1;
+ uint32_t v9;
+ uint16_t v10;
+ uint16_t v11;
+ uint32_t v12[16];
+} __packed;
+
+struct srs_trumedia_params_CSHP {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v5;
+ uint16_t v6;
+ uint16_t v____A1;
+ uint32_t v7;
+ uint16_t v8;
+ uint16_t v9;
+ uint32_t v10[16];
+} __packed;
+
+struct srs_trumedia_params_HPF {
+ uint32_t v1;
+ uint32_t v2[26];
+} __packed;
+
+struct srs_trumedia_params_PEQ {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v4;
+ uint16_t v____A1;
+ uint32_t v5[26];
+ uint32_t v6[26];
+} __packed;
+
+struct srs_trumedia_params_HL {
+ uint16_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint16_t v____A1;
+ int32_t v4;
+ uint32_t v5;
+ uint16_t v6;
+ uint16_t v____A2;
+ uint32_t v7;
+} __packed;
+
+struct srs_trumedia_params {
+ struct srs_trumedia_params_GLOBAL global;
+ struct srs_trumedia_params_WOWHD wowhd;
+ struct srs_trumedia_params_CSHP cshp;
+ struct srs_trumedia_params_HPF hpf;
+ struct srs_trumedia_params_PEQ peq;
+ struct srs_trumedia_params_HL hl;
+} __packed;
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params);
+/* SRS TruMedia end */
+
+/* SRS Studio Sound 3D start */
+#define SRS_ID_SS3D_GLOBAL 0x00000001
+#define SRS_ID_SS3D_CTRL 0x00000002
+#define SRS_ID_SS3D_FILTER 0x00000003
+
+struct srs_SS3D_params_GLOBAL {
+ uint8_t v1;
+ uint8_t v2;
+ uint8_t v3;
+ uint8_t v4;
+ uint8_t v5;
+ uint8_t v6;
+ uint8_t v7;
+ uint8_t v8;
+} __packed;
+
+struct srs_SS3D_ctrl_params {
+ uint8_t v[236];
+} __packed;
+
+struct srs_SS3D_filter_params {
+ uint8_t v[28 + 2752];
+} __packed;
+
+struct srs_SS3D_params {
+ struct srs_SS3D_params_GLOBAL global;
+ struct srs_SS3D_ctrl_params ss3d;
+ struct srs_SS3D_filter_params ss3d_f;
+} __packed;
+
+int srs_ss3d_open(int port_id, int srs_tech_id, void *srs_params);
+/* SRS Studio Sound 3D end */
+#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/msm-dai-q6.h b/include/sound/msm-dai-q6.h
new file mode 100644
index 0000000000000..a39d3dc08d008
--- /dev/null
+++ b/include/sound/msm-dai-q6.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_DAI_Q6_PDATA_H__
+
+#define __MSM_DAI_Q6_PDATA_H__
+
+#define MSM_MI2S_SD0 (1 << 0)
+#define MSM_MI2S_SD1 (1 << 1)
+#define MSM_MI2S_SD2 (1 << 2)
+#define MSM_MI2S_SD3 (1 << 3)
+#define MSM_MI2S_CAP_RX 0
+#define MSM_MI2S_CAP_TX 1
+
+struct msm_dai_auxpcm_config {
+ u16 mode;
+ u16 sync;
+ u16 frame;
+ u16 quant;
+ u16 slot;
+ u16 data;
+ int pcm_clk_rate;
+};
+
+struct msm_mi2s_pdata {
+ u16 rx_sd_lines;
+ u16 tx_sd_lines;
+};
+
+struct msm_dai_auxpcm_pdata {
+ const char *clk;
+ struct msm_dai_auxpcm_config mode_8k;
+ struct msm_dai_auxpcm_config mode_16k;
+};
+
+#endif
diff --git a/include/sound/msm_hdmi_audio.h b/include/sound/msm_hdmi_audio.h
new file mode 100644
index 0000000000000..8ada49f82234e
--- /dev/null
+++ b/include/sound/msm_hdmi_audio.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_HDMI_AUDIO_H
+#define __MSM_HDMI_AUDIO_H
+
+/* Supported HDMI Audio channels */
+#define MSM_HDMI_AUDIO_CHANNEL_2 0
+#define MSM_HDMI_AUDIO_CHANNEL_4 1
+#define MSM_HDMI_AUDIO_CHANNEL_6 2
+#define MSM_HDMI_AUDIO_CHANNEL_8 3
+
+#define TRUE 1
+#define FALSE 0
+
+enum hdmi_supported_sample_rates {
+ HDMI_SAMPLE_RATE_32KHZ,
+ HDMI_SAMPLE_RATE_44_1KHZ,
+ HDMI_SAMPLE_RATE_48KHZ,
+ HDMI_SAMPLE_RATE_88_2KHZ,
+ HDMI_SAMPLE_RATE_96KHZ,
+ HDMI_SAMPLE_RATE_176_4KHZ,
+ HDMI_SAMPLE_RATE_192KHZ
+};
+
+int hdmi_audio_enable(bool on , u32 fifo_water_mark);
+int hdmi_audio_packet_enable(bool on);
+int hdmi_msm_audio_get_sample_rate(void);
+
+#if defined(CONFIG_FB_MSM_HDMI_MSM_PANEL) || defined(CONFIG_DRM_MSM)
+int hdmi_msm_audio_info_setup(bool enabled, u32 num_of_channels,
+ u32 channel_allocation, u32 level_shift, bool down_mix);
+void hdmi_msm_audio_sample_rate_reset(int rate);
+#else
+static inline int hdmi_msm_audio_info_setup(bool enabled,
+ u32 num_of_channels, u32 channel_allocation, u32 level_shift,
+ bool down_mix)
+{
+ return 0;
+}
+static inline void hdmi_msm_audio_sample_rate_reset(int rate)
+{
+}
+#endif
+#endif /* __MSM_HDMI_AUDIO_H*/
diff --git a/include/sound/q6adm.h b/include/sound/q6adm.h
new file mode 100644
index 0000000000000..cb3273f5119b0
--- /dev/null
+++ b/include/sound/q6adm.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2010-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 __Q6_ADM_H__
+#define __Q6_ADM_H__
+#include <sound/q6afe.h>
+
+#define ADM_PATH_PLAYBACK 0x1
+#define ADM_PATH_LIVE_REC 0x2
+#define ADM_PATH_NONLIVE_REC 0x3
+
+/* multiple copp per stream. */
+struct route_payload {
+ unsigned int copp_ids[AFE_MAX_PORTS];
+ unsigned short num_copps;
+ unsigned int session_id;
+};
+
+int adm_open(int port, int path, int rate, int mode, int topology);
+
+int adm_multi_ch_copp_open(int port, int path, int rate, int mode,
+ int topology, int perfmode);
+
+int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id,
+ uint32_t *bufsz, uint32_t bufcnt);
+
+int adm_memory_unmap_regions(uint32_t *buf_add, uint32_t *bufsz,
+ uint32_t bufcnt);
+
+int adm_close(int port);
+
+int adm_matrix_map(int session_id, int path, int num_copps,
+ unsigned int *port_id, int copp_id);
+
+int adm_connect_afe_port(int mode, int session_id, int port_id);
+int adm_disconnect_afe_port(int mode, int session_id, int port_id);
+
+void adm_ec_ref_rx_id(int port_id);
+
+int adm_get_copp_id(int port_id);
+
+#endif /* __Q6_ADM_H__ */
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
new file mode 100644
index 0000000000000..1b7a79093c1b9
--- /dev/null
+++ b/include/sound/q6afe.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2010-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 __Q6AFE_H__
+#define __Q6AFE_H__
+#include <sound/apr_audio.h>
+
+#define MSM_AFE_MONO 0
+#define MSM_AFE_MONO_RIGHT 1
+#define MSM_AFE_MONO_LEFT 2
+#define MSM_AFE_STEREO 3
+#define MSM_AFE_4CHANNELS 4
+#define MSM_AFE_6CHANNELS 6
+#define MSM_AFE_8CHANNELS 8
+
+#define MSM_AFE_I2S_FORMAT_LPCM 0
+#define MSM_AFE_I2S_FORMAT_COMPR 1
+#define MSM_AFE_I2S_FORMAT_IEC60958_LPCM 2
+#define MSM_AFE_I2S_FORMAT_IEC60958_COMPR 3
+
+#define MSM_AFE_PORT_TYPE_RX 0
+#define MSM_AFE_PORT_TYPE_TX 1
+
+#define RT_PROXY_DAI_001_RX 0xE0
+#define RT_PROXY_DAI_001_TX 0xF0
+#define RT_PROXY_DAI_002_RX 0xF1
+#define RT_PROXY_DAI_002_TX 0xE1
+#define VIRTUAL_ID_TO_PORTID(val) ((val & 0xF) | 0x2000)
+
+enum {
+ IDX_PRIMARY_I2S_RX = 0,
+ IDX_PRIMARY_I2S_TX = 1,
+ IDX_PCM_RX = 2,
+ IDX_PCM_TX = 3,
+ IDX_SECONDARY_I2S_RX = 4,
+ IDX_SECONDARY_I2S_TX = 5,
+ IDX_MI2S_RX = 6,
+ IDX_MI2S_TX = 7,
+ IDX_HDMI_RX = 8,
+ IDX_RSVD_2 = 9,
+ IDX_RSVD_3 = 10,
+ IDX_DIGI_MIC_TX = 11,
+ IDX_VOICE_RECORD_RX = 12,
+ IDX_VOICE_RECORD_TX = 13,
+ IDX_VOICE_PLAYBACK_TX = 14,
+ IDX_SLIMBUS_0_RX = 15,
+ IDX_SLIMBUS_0_TX = 16,
+ IDX_SLIMBUS_1_RX = 17,
+ IDX_SLIMBUS_1_TX = 18,
+ IDX_SLIMBUS_2_RX = 19,
+ IDX_SLIMBUS_2_TX = 20,
+ IDX_SLIMBUS_3_RX = 21,
+ IDX_SLIMBUS_3_TX = 22,
+ IDX_SLIMBUS_4_RX = 23,
+ IDX_SLIMBUS_4_TX = 24,
+ IDX_INT_BT_SCO_RX = 25,
+ IDX_INT_BT_SCO_TX = 26,
+ IDX_INT_BT_A2DP_RX = 27,
+ IDX_INT_FM_RX = 28,
+ IDX_INT_FM_TX = 29,
+ IDX_RT_PROXY_PORT_001_RX = 30,
+ IDX_RT_PROXY_PORT_001_TX = 31,
+ IDX_SECONDARY_PCM_RX = 32,
+ IDX_SECONDARY_PCM_TX = 33,
+ AFE_MAX_PORTS
+};
+
+int afe_open(u16 port_id, union afe_port_config *afe_config, int rate);
+int afe_close(int port_id);
+int afe_loopback(u16 enable, u16 rx_port, u16 tx_port);
+int afe_loopback_cfg(u16 enable, u16 dst_port, u16 src_port, u16 mode);
+int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain);
+int afe_loopback_gain(u16 port_id, u16 volume);
+int afe_validate_port(u16 port_id);
+int afe_get_port_index(u16 port_id);
+int afe_start_pseudo_port(u16 port_id);
+int afe_stop_pseudo_port(u16 port_id);
+int afe_cmd_memory_map(u32 dma_addr_p, u32 dma_buf_sz);
+int afe_cmd_memory_map_nowait(u32 dma_addr_p, u32 dma_buf_sz);
+int afe_cmd_memory_unmap(u32 dma_addr_p);
+int afe_cmd_memory_unmap_nowait(u32 dma_addr_p);
+
+int afe_register_get_events(u16 port_id,
+ void (*cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv),
+ void *private_data);
+int afe_unregister_get_events(u16 port_id);
+int afe_rt_proxy_port_write(u32 buf_addr_p, int bytes);
+int afe_rt_proxy_port_read(u32 buf_addr_p, int bytes);
+int afe_port_start(u16 port_id, union afe_port_config *afe_config, u32 rate);
+int afe_port_stop_nowait(int port_id);
+int afe_apply_gain(u16 port_id, u16 gain);
+int afe_q6_interface_prepare(void);
+int afe_get_port_type(u16 port_id);
+/* if port_id is virtual, convert to physical..
+ * if port_id is already physical, return physical
+ */
+int afe_convert_virtual_to_portid(u16 port_id);
+
+int afe_pseudo_port_start_nowait(u16 port_id);
+int afe_pseudo_port_stop_nowait(u16 port_id);
+#endif /* __Q6AFE_H__ */
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
new file mode 100644
index 0000000000000..139b501a35688
--- /dev/null
+++ b/include/sound/q6asm.h
@@ -0,0 +1,334 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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_ASM_H__
+#define __Q6_ASM_H__
+
+#include <sound/qdsp6v2/apr.h>
+#include <sound/apr_audio.h>
+
+#define IN 0x000
+#define OUT 0x001
+#define CH_MODE_MONO 0x001
+#define CH_MODE_STEREO 0x002
+
+#define FORMAT_LINEAR_PCM 0x0000
+#define FORMAT_DTMF 0x0001
+#define FORMAT_ADPCM 0x0002
+#define FORMAT_YADPCM 0x0003
+#define FORMAT_MP3 0x0004
+#define FORMAT_MPEG4_AAC 0x0005
+#define FORMAT_AMRNB 0x0006
+#define FORMAT_AMRWB 0x0007
+#define FORMAT_V13K 0x0008
+#define FORMAT_EVRC 0x0009
+#define FORMAT_EVRCB 0x000a
+#define FORMAT_EVRCWB 0x000b
+#define FORMAT_MIDI 0x000c
+#define FORMAT_SBC 0x000d
+#define FORMAT_WMA_V10PRO 0x000e
+#define FORMAT_WMA_V9 0x000f
+#define FORMAT_AMR_WB_PLUS 0x0010
+#define FORMAT_MPEG4_MULTI_AAC 0x0011
+#define FORMAT_MULTI_CHANNEL_LINEAR_PCM 0x0012
+#define FORMAT_AC3 0x0013
+#define FORMAT_DTS 0x0014
+#define FORMAT_EAC3 0x0015
+#define FORMAT_ATRAC 0x0016
+#define FORMAT_MAT 0x0017
+#define FORMAT_AAC 0x0018
+#define FORMAT_DTS_LBR 0x0019
+
+#define ENCDEC_SBCBITRATE 0x0001
+#define ENCDEC_IMMEDIATE_DECODE 0x0002
+#define ENCDEC_CFG_BLK 0x0003
+
+#define CMD_PAUSE 0x0001
+#define CMD_FLUSH 0x0002
+#define CMD_EOS 0x0003
+#define CMD_CLOSE 0x0004
+#define CMD_OUT_FLUSH 0x0005
+
+/* 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
+
+/* Enable Sample_Rate/Channel_Mode notification event from Decoder */
+#define SR_CM_NOTIFY_ENABLE 0x0004
+
+#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */
+#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */
+#define ASYNC_IO_MODE 0x0002
+#define SYNC_IO_MODE 0x0001
+#define NO_TIMESTAMP 0xFF00
+#define SET_TIMESTAMP 0x0000
+
+#define SOFT_PAUSE_ENABLE 1
+#define SOFT_PAUSE_DISABLE 0
+
+#define SESSION_MAX 0x08
+
+#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */
+#define SOFT_PAUSE_STEP_LINEAR 0 /* Step value 0ms or 0us */
+#define SOFT_PAUSE_STEP 0 /* Step value 0ms or 0us */
+enum {
+ SOFT_PAUSE_CURVE_LINEAR = 0,
+ SOFT_PAUSE_CURVE_EXP,
+ SOFT_PAUSE_CURVE_LOG,
+};
+
+#define SOFT_VOLUME_PERIOD 30 /* ramp up/down for 30ms */
+#define SOFT_VOLUME_STEP_LINEAR 0 /* Step value 0ms or 0us */
+#define SOFT_VOLUME_STEP 0 /* Step value 0ms or 0us */
+enum {
+ SOFT_VOLUME_CURVE_LINEAR = 0,
+ SOFT_VOLUME_CURVE_EXP,
+ SOFT_VOLUME_CURVE_LOG,
+};
+
+typedef void (*app_cb)(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+struct audio_buffer {
+ dma_addr_t phys;
+ void *data;
+ uint32_t used;
+ uint32_t size;/* size of buffer */
+ uint32_t actual_size; /* actual number of bytes read by DSP */
+ void *mem_buffer;
+};
+
+struct audio_aio_write_param {
+ unsigned long paddr;
+ uint32_t uid;
+ uint32_t len;
+ uint32_t msw_ts;
+ uint32_t lsw_ts;
+ uint32_t flags;
+};
+
+struct audio_aio_read_param {
+ unsigned long paddr;
+ uint32_t len;
+ uint32_t uid;
+};
+
+struct audio_port_data {
+ struct audio_buffer *buf;
+ uint32_t max_buf_cnt;
+ uint32_t dsp_buf;
+ uint32_t cpu_buf;
+ /* read or write locks */
+ struct mutex lock;
+ spinlock_t dsp_lock;
+};
+
+struct audio_client {
+ int session;
+ /* idx:1 out port, 0: in port*/
+ struct audio_port_data port[2];
+
+ struct apr_svc *apr;
+ struct mutex cmd_lock;
+
+ atomic_t cmd_state;
+ atomic_t cmd_close_state;
+ atomic_t time_flag;
+ atomic_t nowait_cmd_cnt;
+ wait_queue_head_t cmd_wait;
+ wait_queue_head_t time_wait;
+
+ app_cb cb;
+ void *priv;
+ uint32_t io_mode;
+ uint64_t time_stamp;
+ atomic_t cmd_response;
+ bool perf_mode;
+};
+
+void q6asm_audio_client_free(struct audio_client *ac);
+
+struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv);
+
+struct audio_client *q6asm_get_audio_client(int session_id);
+
+int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt);
+int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir
+ /* 1:Out,0:In */,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt);
+
+int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
+ struct audio_client *ac);
+
+int q6asm_open_read(struct audio_client *ac, uint32_t format);
+int q6asm_open_read_v2_1(struct audio_client *ac, uint32_t format);
+
+int q6asm_open_read_compressed(struct audio_client *ac,
+ uint32_t frames_per_buffer, uint32_t meta_data_mode);
+
+int q6asm_open_write(struct audio_client *ac, uint32_t format);
+
+int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format);
+
+int q6asm_open_read_write(struct audio_client *ac,
+ uint32_t rd_format,
+ uint32_t wr_format);
+
+int q6asm_open_loopack(struct audio_client *ac);
+
+int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags);
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags);
+
+int q6asm_async_write(struct audio_client *ac,
+ struct audio_aio_write_param *param);
+
+int q6asm_async_read(struct audio_client *ac,
+ struct audio_aio_read_param *param);
+
+int q6asm_async_read_compressed(struct audio_client *ac,
+ struct audio_aio_read_param *param);
+
+int q6asm_read(struct audio_client *ac);
+int q6asm_read_nolock(struct audio_client *ac);
+
+int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add,
+ int dir, uint32_t bufsz, uint32_t bufcnt);
+
+int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add,
+ int dir);
+
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts);
+
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts);
+
+int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable);
+
+int q6asm_cmd(struct audio_client *ac, int cmd);
+
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd);
+
+void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac,
+ uint32_t *size, uint32_t *idx);
+
+void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
+ uint32_t *size, uint32_t *idx);
+
+int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac);
+
+/* File format specific configurations to be added below */
+
+int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
+ uint32_t frames_per_buf,
+ uint32_t sample_rate, uint32_t channels,
+ uint32_t bit_rate,
+ uint32_t mode, uint32_t format);
+
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels);
+
+int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
+ uint32_t rate, uint32_t channels);
+
+int q6asm_enc_cfg_blk_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels);
+
+int q6asm_enable_sbrps(struct audio_client *ac,
+ uint32_t sbr_ps);
+
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+ uint16_t sce_left, uint16_t sce_right);
+
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff);
+
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+ uint32_t num_channels);
+
+int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
+
+int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t rate_modulation_cmd);
+
+int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable);
+
+int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable);
+
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels);
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels);
+
+int q6asm_media_format_block_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg);
+
+int q6asm_media_format_block_amrwbplus(struct audio_client *ac,
+ struct asm_amrwbplus_cfg *cfg);
+
+int q6asm_media_format_block_multi_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg);
+
+int q6asm_media_format_block_wma(struct audio_client *ac,
+ void *cfg);
+
+int q6asm_media_format_block_wmapro(struct audio_client *ac,
+ void *cfg);
+
+/* PP specific */
+int q6asm_equalizer(struct audio_client *ac, void *eq);
+
+/* Send Volume Command */
+int q6asm_set_volume(struct audio_client *ac, int volume);
+
+/* Set SoftPause Params */
+int q6asm_set_softpause(struct audio_client *ac,
+ struct asm_softpause_params *param);
+
+/* Set Softvolume Params */
+int q6asm_set_softvolume(struct audio_client *ac,
+ struct asm_softvolume_params *param);
+
+/* Send left-right channel gain */
+int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain);
+
+/* Enable Mute/unmute flag */
+int q6asm_set_mute(struct audio_client *ac, int muteflag);
+
+int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp);
+
+/* Client can set the IO mode to either AIO/SIO mode */
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode);
+
+/* Get Service ID for APR communication */
+int q6asm_get_apr_service_id(int session_id);
+
+/* Common format block without any payload
+*/
+int q6asm_media_format_block(struct audio_client *ac, uint32_t format);
+
+#endif /* __Q6_ASM_H__ */
diff --git a/include/sound/qdsp6v2/apr.h b/include/sound/qdsp6v2/apr.h
new file mode 100644
index 0000000000000..9473989caff39
--- /dev/null
+++ b/include/sound/qdsp6v2/apr.h
@@ -0,0 +1,169 @@
+/* Copyright (c) 2010-2011, 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 __APR_H_
+#define __APR_H_
+
+#include <linux/mutex.h>
+
+enum apr_subsys_state {
+ APR_SUBSYS_DOWN,
+ APR_SUBSYS_UP,
+ APR_SUBSYS_LOADED,
+};
+
+struct apr_q6 {
+// void *pil;
+ struct rproc *rproc;
+ atomic_t q6_state;
+ atomic_t modem_state;
+ struct mutex lock;
+};
+
+struct apr_hdr {
+ uint16_t hdr_field;
+ uint16_t pkt_size;
+ uint8_t src_svc;
+ uint8_t src_domain;
+ uint16_t src_port;
+ uint8_t dest_svc;
+ uint8_t dest_domain;
+ uint16_t dest_port;
+ uint32_t token;
+ uint32_t opcode;
+};
+
+#define APR_HDR_LEN(hdr_len) ((hdr_len)/4)
+#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len))
+#define APR_HDR_FIELD(msg_type, hdr_len, ver)\
+ (((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF))
+
+#define APR_HDR_SIZE sizeof(struct apr_hdr)
+
+/* Version */
+#define APR_PKT_VER 0x0
+
+/* Command and Response Types */
+#define APR_MSG_TYPE_EVENT 0x0
+#define APR_MSG_TYPE_CMD_RSP 0x1
+#define APR_MSG_TYPE_SEQ_CMD 0x2
+#define APR_MSG_TYPE_NSEQ_CMD 0x3
+#define APR_MSG_TYPE_MAX 0x04
+
+/* APR Basic Response Message */
+#define APR_BASIC_RSP_RESULT 0x000110E8
+#define APR_RSP_ACCEPTED 0x000100BE
+
+/* Domain IDs */
+#define APR_DOMAIN_SIM 0x1
+#define APR_DOMAIN_PC 0x2
+#define APR_DOMAIN_MODEM 0x3
+#define APR_DOMAIN_ADSP 0x4
+#define APR_DOMAIN_APPS 0x5
+#define APR_DOMAIN_MAX 0x6
+
+/* ADSP service IDs */
+#define APR_SVC_TEST_CLIENT 0x2
+#define APR_SVC_ADSP_CORE 0x3
+#define APR_SVC_AFE 0x4
+#define APR_SVC_VSM 0x5
+#define APR_SVC_VPM 0x6
+#define APR_SVC_ASM 0x7
+#define APR_SVC_ADM 0x8
+#define APR_SVC_ADSP_MVM 0x09
+#define APR_SVC_ADSP_CVS 0x0A
+#define APR_SVC_ADSP_CVP 0x0B
+#define APR_SVC_USM 0x0C
+#define APR_SVC_MAX 0x0D
+
+/* Modem Service IDs */
+#define APR_SVC_MVS 0x3
+#define APR_SVC_MVM 0x4
+#define APR_SVC_CVS 0x5
+#define APR_SVC_CVP 0x6
+#define APR_SVC_SRD 0x7
+
+/* APR Port IDs */
+#define APR_MAX_PORTS 0x40
+
+#define APR_NAME_MAX 0x40
+
+#define RESET_EVENTS 0xFFFFFFFF
+
+#define LPASS_RESTART_EVENT 0x1000
+#define LPASS_RESTART_READY 0x1001
+
+struct apr_client_data {
+ uint16_t reset_event;
+ uint16_t reset_proc;
+ uint16_t payload_size;
+ uint16_t hdr_len;
+ uint16_t msg_type;
+ uint16_t src;
+ uint16_t dest_svc;
+ uint16_t src_port;
+ uint16_t dest_port;
+ uint32_t token;
+ uint32_t opcode;
+ void *payload;
+};
+
+typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv);
+
+struct apr_svc {
+ uint16_t id;
+ uint16_t dest_id;
+ uint16_t client_id;
+ uint8_t rvd;
+ uint8_t port_cnt;
+ uint8_t svc_cnt;
+ uint8_t need_reset;
+ apr_fn port_fn[APR_MAX_PORTS];
+ void *port_priv[APR_MAX_PORTS];
+ apr_fn fn;
+ void *priv;
+ struct mutex m_lock;
+ spinlock_t w_lock;
+};
+
+struct apr_client {
+ uint8_t id;
+ uint8_t svc_cnt;
+ uint8_t rvd;
+ struct mutex m_lock;
+ struct apr_svc_ch_dev *handle;
+ struct apr_svc svc[APR_SVC_MAX];
+};
+
+int apr_load_adsp_image(void);
+struct apr_client *apr_get_client(int dest_id, int client_id);
+int apr_wait_for_device_up(int dest_id);
+int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
+ int *svc_idx, int *svc_id);
+void apr_cb_func(void *buf, int len, void *priv);
+struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
+ uint32_t src_port, void *priv);
+inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port,
+ uint16_t msg_type, uint16_t dest_port,
+ uint32_t token, uint32_t opcode, uint16_t len);
+
+int apr_send_pkt(void *handle, uint32_t *buf);
+int apr_deregister(void *handle);
+void change_q6_state(int state);
+void q6audio_dsp_not_responding(void);
+void apr_reset(void *handle);
+enum apr_subsys_state apr_get_modem_state(void);
+void apr_set_modem_state(enum apr_subsys_state state);
+enum apr_subsys_state apr_get_q6_state(void);
+int apr_set_q6_state(enum apr_subsys_state state);
+void apr_set_subsys_state(void);
+#endif
diff --git a/include/sound/qdsp6v2/apr_tal.h b/include/sound/qdsp6v2/apr_tal.h
new file mode 100644
index 0000000000000..69170b98629ea
--- /dev/null
+++ b/include/sound/qdsp6v2/apr_tal.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2010-2011, 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 __APR_TAL_H_
+#define __APR_TAL_H_
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+
+/* APR Client IDs */
+#define APR_CLIENT_AUDIO 0x0
+#define APR_CLIENT_VOICE 0x1
+#define APR_CLIENT_MAX 0x2
+
+#define APR_DL_SMD 0
+#define APR_DL_MAX 1
+
+#define APR_DEST_MODEM 0
+#define APR_DEST_QDSP6 1
+#define APR_DEST_MAX 2
+
+#define APR_MAX_BUF 8192
+
+#define APR_OPEN_TIMEOUT_MS 5000
+
+typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv);
+struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest,
+ uint32_t dl, apr_svc_cb_fn func, void *priv);
+int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len);
+int apr_tal_close(struct apr_svc_ch_dev *apr_ch);
+struct apr_svc_ch_dev {
+ struct qcom_smd_channel *ch;
+ spinlock_t lock;
+ apr_svc_cb_fn func;
+ char data[APR_MAX_BUF];
+ wait_queue_head_t wait;
+ void *priv;
+ wait_queue_head_t dest;
+ uint32_t dest_state;
+};
+
+#endif
diff --git a/include/sound/qdsp6v2/audio_acdb.h b/include/sound/qdsp6v2/audio_acdb.h
new file mode 100644
index 0000000000000..9af653c76e6c8
--- /dev/null
+++ b/include/sound/qdsp6v2/audio_acdb.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2010-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 _AUDIO_ACDB_H
+#define _AUDIO_ACDB_H
+
+#include <linux/msm_audio_acdb.h>
+#include <sound/q6adm.h>
+enum {
+ RX_CAL,
+ TX_CAL,
+ MAX_AUDPROC_TYPES
+};
+
+struct acdb_cal_block {
+ uint32_t cal_size;
+ uint32_t cal_kvaddr;
+ uint32_t cal_paddr;
+};
+
+struct acdb_atomic_cal_block {
+ atomic_t cal_size;
+ atomic_t cal_kvaddr;
+ atomic_t cal_paddr;
+};
+
+struct acdb_cal_data {
+ uint32_t num_cal_blocks;
+ struct acdb_atomic_cal_block *cal_blocks;
+};
+
+uint32_t get_voice_rx_topology(void);
+uint32_t get_voice_tx_topology(void);
+uint32_t get_adm_rx_topology(void);
+uint32_t get_adm_tx_topology(void);
+uint32_t get_asm_topology(void);
+void get_all_voice_cal(struct acdb_cal_block *cal_block);
+void get_all_cvp_cal(struct acdb_cal_block *cal_block);
+void get_all_vocproc_cal(struct acdb_cal_block *cal_block);
+void get_all_vocstrm_cal(struct acdb_cal_block *cal_block);
+void get_all_vocvol_cal(struct acdb_cal_block *cal_block);
+void get_anc_cal(struct acdb_cal_block *cal_block);
+void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_vocproc_cal(struct acdb_cal_data *cal_data);
+void get_vocstrm_cal(struct acdb_cal_data *cal_data);
+void get_vocvol_cal(struct acdb_cal_data *cal_data);
+void get_sidetone_cal(struct sidetone_cal *cal_data);
+
+#endif
diff --git a/include/sound/qdsp6v2/audio_def.h b/include/sound/qdsp6v2/audio_def.h
new file mode 100644
index 0000000000000..35a4d5c2f7947
--- /dev/null
+++ b/include/sound/qdsp6v2/audio_def.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2009,2011, 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 _MACH_QDSP5_V2_AUDIO_DEF_H
+#define _MACH_QDSP5_V2_AUDIO_DEF_H
+
+/* Define sound device capability */
+#define SNDDEV_CAP_RX 0x1 /* RX direction */
+#define SNDDEV_CAP_TX 0x2 /* TX direction */
+#define SNDDEV_CAP_VOICE 0x4 /* Support voice call */
+#define SNDDEV_CAP_PLAYBACK 0x8 /* Support playback */
+#define SNDDEV_CAP_FM 0x10 /* Support FM radio */
+#define SNDDEV_CAP_TTY 0x20 /* Support TTY */
+#define SNDDEV_CAP_ANC 0x40 /* Support ANC */
+#define SNDDEV_CAP_LB 0x80 /* Loopback */
+#define VOC_NB_INDEX 0
+#define VOC_WB_INDEX 1
+#define VOC_RX_VOL_ARRAY_NUM 2
+
+/* Device volume types . In Current deisgn only one of these are supported. */
+#define SNDDEV_DEV_VOL_DIGITAL 0x1 /* Codec Digital volume control */
+#define SNDDEV_DEV_VOL_ANALOG 0x2 /* Codec Analog volume control */
+
+#define SIDE_TONE_MASK 0x01
+
+#endif /* _MACH_QDSP5_V2_AUDIO_DEF_H */
diff --git a/include/sound/qdsp6v2/audio_dev_ctl.h b/include/sound/qdsp6v2/audio_dev_ctl.h
new file mode 100644
index 0000000000000..403dc6eccdafd
--- /dev/null
+++ b/include/sound/qdsp6v2/audio_dev_ctl.h
@@ -0,0 +1,221 @@
+/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 __MACH_QDSP6_V2_SNDDEV_H
+#define __MACH_QDSP6_V2_SNDDEV_H
+#include <sound/qdsp6v2/audio_def.h>
+#include <sound/q6afe.h>
+
+#define AUDIO_DEV_CTL_MAX_DEV 64
+#define DIR_TX 2
+#define DIR_RX 1
+
+#define DEVICE_IGNORE 0xffff
+#define COPP_IGNORE 0xffffffff
+#define SESSION_IGNORE 0x0UL
+
+/* 8 concurrent sessions with Q6 possible, session:0
+ reserved in DSP */
+#define MAX_SESSIONS 0x09
+
+/* This represents Maximum bit needed for representing sessions
+ per clients, MAX_BIT_PER_CLIENT >= MAX_SESSIONS */
+#define MAX_BIT_PER_CLIENT 16
+
+#define VOICE_STATE_INVALID 0x0
+#define VOICE_STATE_INCALL 0x1
+#define VOICE_STATE_OFFCALL 0x2
+#define ONE_TO_MANY 1
+#define MANY_TO_ONE 2
+
+struct msm_snddev_info {
+ const char *name;
+ u32 capability;
+ u32 copp_id;
+ u32 acdb_id;
+ u32 dev_volume;
+ struct msm_snddev_ops {
+ int (*open)(struct msm_snddev_info *);
+ int (*close)(struct msm_snddev_info *);
+ int (*set_freq)(struct msm_snddev_info *, u32);
+ int (*enable_sidetone)(struct msm_snddev_info *, u32, uint16_t);
+ int (*set_device_volume)(struct msm_snddev_info *, u32);
+ int (*enable_anc)(struct msm_snddev_info *, u32);
+ } dev_ops;
+ u8 opened;
+ void *private_data;
+ bool state;
+ u32 sample_rate;
+ u32 channel_mode;
+ u32 set_sample_rate;
+ u64 sessions;
+ int usage_count;
+ s32 max_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB,[1] for WB */
+ s32 min_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM];
+};
+
+struct msm_volume {
+ int volume; /* Volume parameter, in % Scale */
+ int pan;
+};
+
+extern struct msm_volume msm_vol_ctl;
+
+void msm_snddev_register(struct msm_snddev_info *);
+void msm_snddev_unregister(struct msm_snddev_info *);
+int msm_snddev_devcount(void);
+int msm_snddev_query(int dev_id);
+unsigned short msm_snddev_route_dec(int popp_id);
+unsigned short msm_snddev_route_enc(int enc_id);
+
+int msm_snddev_set_dec(int popp_id, int copp_id, int set,
+ int rate, int channel_mode);
+int msm_snddev_set_enc(int popp_id, int copp_id, int set,
+ int rate, int channel_mode);
+
+int msm_snddev_is_set(int popp_id, int copp_id);
+int msm_get_voc_route(u32 *rx_id, u32 *tx_id);
+int msm_set_voc_route(struct msm_snddev_info *dev_info, int stream_type,
+ int dev_id);
+int msm_snddev_enable_sidetone(u32 dev_id, u32 enable, uint16_t gain);
+
+int msm_set_copp_id(int session_id, int copp_id);
+
+int msm_clear_copp_id(int session_id, int copp_id);
+
+int msm_clear_session_id(int session_id);
+
+int msm_reset_all_device(void);
+
+int reset_device(void);
+
+int msm_clear_all_session(void);
+
+struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id);
+
+void msm_release_voc_thread(void);
+
+int snddev_voice_set_volume(int vol, int path);
+
+struct auddev_evt_voc_devinfo {
+ u32 dev_type; /* Rx or Tx */
+ u32 acdb_dev_id; /* acdb id of device */
+ u32 dev_sample; /* Sample rate of device */
+ s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb (milibel),
+ [0] is for NB, other for WB */
+ s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb */
+ u32 dev_id; /* registered device id */
+ u32 dev_port_id;
+};
+
+struct auddev_evt_audcal_info {
+ u32 dev_id;
+ u32 acdb_id;
+ u32 sample_rate;
+ u32 dev_type;
+ u32 sessions;
+};
+
+union msm_vol_mute {
+ int vol;
+ bool mute;
+};
+
+struct auddev_evt_voc_mute_info {
+ u32 dev_type;
+ u32 acdb_dev_id;
+ u32 voice_session_id;
+ union msm_vol_mute dev_vm_val;
+};
+
+struct auddev_evt_freq_info {
+ u32 dev_type;
+ u32 acdb_dev_id;
+ u32 sample_rate;
+};
+
+union auddev_evt_data {
+ struct auddev_evt_voc_devinfo voc_devinfo;
+ struct auddev_evt_voc_mute_info voc_vm_info;
+ struct auddev_evt_freq_info freq_info;
+ u32 routing_id;
+ s32 session_vol;
+ s32 voice_state;
+ struct auddev_evt_audcal_info audcal_info;
+ u32 voice_session_id;
+};
+
+struct message_header {
+ uint32_t id;
+ uint32_t data_len;
+};
+
+#define AUDDEV_EVT_DEV_CHG_VOICE 0x01 /* device change event */
+#define AUDDEV_EVT_DEV_RDY 0x02 /* device ready event */
+#define AUDDEV_EVT_DEV_RLS 0x04 /* device released event */
+#define AUDDEV_EVT_REL_PENDING 0x08 /* device release pending */
+#define AUDDEV_EVT_DEVICE_VOL_MUTE_CHG 0x10 /* device volume changed */
+#define AUDDEV_EVT_START_VOICE 0x20 /* voice call start */
+#define AUDDEV_EVT_END_VOICE 0x40 /* voice call end */
+#define AUDDEV_EVT_STREAM_VOL_CHG 0x80 /* device volume changed */
+#define AUDDEV_EVT_FREQ_CHG 0x100 /* Change in freq */
+#define AUDDEV_EVT_VOICE_STATE_CHG 0x200 /* Change in voice state */
+
+#define AUDDEV_CLNT_VOC 0x1 /*Vocoder clients*/
+#define AUDDEV_CLNT_DEC 0x2 /*Decoder clients*/
+#define AUDDEV_CLNT_ENC 0x3 /* Encoder clients */
+#define AUDDEV_CLNT_AUDIOCAL 0x4 /* AudioCalibration client */
+
+#define AUDIO_DEV_CTL_MAX_LISTNER 20 /* Max Listeners Supported */
+
+struct msm_snd_evt_listner {
+ uint32_t evt_id;
+ uint32_t clnt_type;
+ uint32_t clnt_id;
+ void *private_data;
+ void (*auddev_evt_listener)(u32 evt_id,
+ union auddev_evt_data *evt_payload,
+ void *private_data);
+ struct msm_snd_evt_listner *cb_next;
+ struct msm_snd_evt_listner *cb_prev;
+};
+
+struct event_listner {
+ struct msm_snd_evt_listner *cb;
+ u32 num_listner;
+ int state; /* Call state */ /* TODO remove this if not req*/
+};
+
+extern struct event_listner event;
+int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id,
+ void (*listner)(u32 evt_id,
+ union auddev_evt_data *evt_payload,
+ void *private_data),
+ void *private_data);
+int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id);
+void mixer_post_event(u32 evt_id, u32 dev_id);
+void broadcast_event(u32 evt_id, u32 dev_id, u64 session_id);
+int auddev_cfg_tx_copp_topology(int session_id, int cfg);
+int msm_snddev_request_freq(int *freq, u32 session_id,
+ u32 capability, u32 clnt_type);
+int msm_snddev_withdraw_freq(u32 session_id,
+ u32 capability, u32 clnt_type);
+int msm_device_is_voice(int dev_id);
+int msm_get_voc_freq(int *tx_freq, int *rx_freq);
+int msm_snddev_get_enc_freq(int session_id);
+int msm_set_voice_vol(int dir, s32 volume, u32 session_id);
+int msm_set_voice_mute(int dir, int mute, u32 session_id);
+int msm_get_voice_state(void);
+int msm_enable_incall_recording(int popp_id, int rec_mode, int rate,
+ int channel_mode);
+int msm_disable_incall_recording(uint32_t popp_id, uint32_t rec_mode);
+#endif
diff --git a/include/sound/qdsp6v2/dsp_debug.h b/include/sound/qdsp6v2/dsp_debug.h
new file mode 100644
index 0000000000000..bc1cd9ec8743f
--- /dev/null
+++ b/include/sound/qdsp6v2/dsp_debug.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2010, 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 __DSP_DEBUG_H_
+#define __DSP_DEBUG_H_
+
+typedef int (*dsp_state_cb)(int state);
+int dsp_debug_register(dsp_state_cb ptr);
+
+#define DSP_STATE_CRASHED 0x0
+#define DSP_STATE_CRASH_DUMP_DONE 0x1
+
+#endif
diff --git a/include/sound/qdsp6v2/q6voice.h b/include/sound/qdsp6v2/q6voice.h
new file mode 100644
index 0000000000000..7165998e2efae
--- /dev/null
+++ b/include/sound/qdsp6v2/q6voice.h
@@ -0,0 +1,778 @@
+/* Copyright (c) 2010-2011, 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 __QDSP6VOICE_H__
+#define __QDSP6VOICE_H__
+
+#include <mach/qdsp6v2/apr.h>
+
+/* Device Event */
+#define DEV_CHANGE_READY 0x1
+
+#define VOICE_CALL_START 0x1
+#define VOICE_CALL_END 0
+
+#define VOICE_DEV_ENABLED 0x1
+#define VOICE_DEV_DISABLED 0
+
+#define MAX_VOC_PKT_SIZE 642
+
+#define SESSION_NAME_LEN 20
+
+struct voice_header {
+ uint32_t id;
+ uint32_t data_len;
+};
+
+struct voice_init {
+ struct voice_header hdr;
+ void *cb_handle;
+};
+
+
+/* Device information payload structure */
+
+struct device_data {
+ uint32_t dev_acdb_id;
+ uint32_t volume; /* in percentage */
+ uint32_t mute;
+ uint32_t sample;
+ uint32_t enabled;
+ uint32_t dev_id;
+ uint32_t dev_port_id;
+};
+
+enum {
+ VOC_INIT = 0,
+ VOC_RUN,
+ VOC_CHANGE,
+ VOC_RELEASE,
+};
+
+/* TO MVM commands */
+#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE
+/* Create a new full control MVM session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C
+/* Attach a stream to the MVM. */
+
+#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D
+/* Detach a stream from the MVM. */
+
+#define VSS_IMVM_CMD_ATTACH_VOCPROC 0x0001123E
+/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc
+ * to all the streams currently attached to it.
+ */
+
+#define VSS_IMVM_CMD_DETACH_VOCPROC 0x0001123F
+/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this
+ * vocproc from all the streams to which it is currently attached.
+ */
+
+#define VSS_IMVM_CMD_START_VOICE 0x00011190
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_STOP_VOICE 0x00011192
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+
+#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C
+/* Set the network type. */
+
+#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0
+/* Set the voice timing parameters. */
+
+struct vss_imvm_cmd_create_control_session_t {
+ char name[SESSION_NAME_LEN];
+ /*
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+struct vss_istream_cmd_set_tty_mode_t {
+ uint32_t mode;
+ /**<
+ * TTY mode.
+ *
+ * 0 : TTY disabled
+ * 1 : HCO
+ * 2 : VCO
+ * 3 : FULL
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_attach_vocproc_t {
+ uint16_t handle;
+ /**< Handle of vocproc being attached. */
+} __attribute__((packed));
+
+struct vss_istream_cmd_detach_vocproc_t {
+ uint16_t handle;
+ /**< Handle of vocproc being detached. */
+} __attribute__((packed));
+
+struct vss_imvm_cmd_attach_stream_t {
+ uint16_t handle;
+ /* The stream handle to attach. */
+} __attribute__((packed));
+
+struct vss_imvm_cmd_detach_stream_t {
+ uint16_t handle;
+ /* The stream handle to detach. */
+} __attribute__((packed));
+
+struct vss_icommon_cmd_set_network_t {
+ uint32_t network_id;
+ /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+} __attribute__((packed));
+
+struct vss_icommon_cmd_set_voice_timing_t {
+ uint16_t mode;
+ /*
+ * The vocoder frame synchronization mode.
+ *
+ * 0 : No frame sync.
+ * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt).
+ */
+ uint16_t enc_offset;
+ /*
+ * The offset in microseconds from the VFR to deliver a Tx vocoder
+ * packet. The offset should be less than 20000us.
+ */
+ uint16_t dec_req_offset;
+ /*
+ * The offset in microseconds from the VFR to request for an Rx vocoder
+ * packet. The offset should be less than 20000us.
+ */
+ uint16_t dec_offset;
+ /*
+ * The offset in microseconds from the VFR to indicate the deadline to
+ * receive an Rx vocoder packet. The offset should be less than 20000us.
+ * Rx vocoder packets received after this deadline are not guaranteed to
+ * be processed.
+ */
+} __attribute__((packed));
+
+struct mvm_attach_vocproc_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle;
+} __attribute__((packed));
+
+struct mvm_detach_vocproc_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle;
+} __attribute__((packed));
+
+struct mvm_create_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_create_control_session_t mvm_session;
+} __packed;
+
+struct mvm_set_tty_mode_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_tty_mode_t tty_mode;
+} __attribute__((packed));
+
+struct mvm_attach_stream_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_attach_stream_t attach_stream;
+} __attribute__((packed));
+
+struct mvm_detach_stream_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_detach_stream_t detach_stream;
+} __attribute__((packed));
+
+struct mvm_set_network_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_set_network_t network;
+} __attribute__((packed));
+
+struct mvm_set_voice_timing_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_set_voice_timing_t timing;
+} __attribute__((packed));
+
+/* TO CVS commands */
+#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7
+/* Create a new full control stream session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+
+#define VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA 0x000110FB
+
+#define VSS_ISTREAM_CMD_SET_MUTE 0x00011022
+
+#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186
+/* Set media type on the stream. */
+
+#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015
+/* Event sent by the stream to its client to provide an encoded packet. */
+
+#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017
+/* Event sent by the stream to its client requesting for a decoder packet.
+ * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event.
+ */
+
+#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016
+/* Event sent by the client to the stream in response to a
+ * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet.
+ */
+
+#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E
+/* Set AMR encoder rate. */
+
+#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F
+/* Set AMR-WB encoder rate. */
+
+#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019
+/* Set encoder minimum and maximum rate. */
+
+#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D
+/* Set encoder DTX mode. */
+
+#define VSS_ISTREAM_CMD_START_RECORD 0x00011236
+/* Start in-call conversation recording. */
+
+#define VSS_ISTREAM_CMD_STOP_RECORD 0x00011237
+/* Stop in-call conversation recording. */
+
+#define VSS_ISTREAM_CMD_START_PLAYBACK 0x00011238
+/* Start in-call music delivery on the Tx voice path. */
+
+#define VSS_ISTREAM_CMD_STOP_PLAYBACK 0x00011239
+/* Stop the in-call music delivery on the Tx voice path. */
+
+struct vss_istream_cmd_create_passive_control_session_t {
+ char name[SESSION_NAME_LEN];
+ /**<
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_set_mute_t {
+ uint16_t direction;
+ /**<
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and Rx
+ */
+ uint16_t mute_flag;
+ /**<
+ * Mute, un-mute.
+ *
+ * 0 : Silence disable
+ * 1 : Silence enable
+ * 2 : CNG enable. Applicable to TX only. If set on RX behavior
+ * will be the same as 1
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_create_full_control_session_t {
+ uint16_t direction;
+ /*
+ * Stream direction.
+ *
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and RX
+ * 3 : TX and RX loopback
+ */
+ uint32_t enc_media_type;
+ /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t dec_media_type;
+ /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t network_id;
+ /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+ char name[SESSION_NAME_LEN];
+ /*
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_set_media_type_t {
+ uint32_t rx_media_id;
+ /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t tx_media_id;
+ /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+} __attribute__((packed));
+
+struct vss_istream_evt_send_enc_buffer_t {
+ uint32_t media_id;
+ /* Media ID of the packet. */
+ uint8_t packet_data[MAX_VOC_PKT_SIZE];
+ /* Packet data buffer. */
+} __attribute__((packed));
+
+struct vss_istream_evt_send_dec_buffer_t {
+ uint32_t media_id;
+ /* Media ID of the packet. */
+ uint8_t packet_data[MAX_VOC_PKT_SIZE];
+ /* Packet data. */
+} __attribute__((packed));
+
+struct vss_istream_cmd_voc_amr_set_enc_rate_t {
+ uint32_t mode;
+ /* Set the AMR encoder rate.
+ *
+ * 0x00000000 : 4.75 kbps
+ * 0x00000001 : 5.15 kbps
+ * 0x00000002 : 5.90 kbps
+ * 0x00000003 : 6.70 kbps
+ * 0x00000004 : 7.40 kbps
+ * 0x00000005 : 7.95 kbps
+ * 0x00000006 : 10.2 kbps
+ * 0x00000007 : 12.2 kbps
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_voc_amrwb_set_enc_rate_t {
+ uint32_t mode;
+ /* Set the AMR-WB encoder rate.
+ *
+ * 0x00000000 : 6.60 kbps
+ * 0x00000001 : 8.85 kbps
+ * 0x00000002 : 12.65 kbps
+ * 0x00000003 : 14.25 kbps
+ * 0x00000004 : 15.85 kbps
+ * 0x00000005 : 18.25 kbps
+ * 0x00000006 : 19.85 kbps
+ * 0x00000007 : 23.05 kbps
+ * 0x00000008 : 23.85 kbps
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_cdma_set_enc_minmax_rate_t {
+ uint16_t min_rate;
+ /* Set the lower bound encoder rate.
+ *
+ * 0x0000 : Blank frame
+ * 0x0001 : Eighth rate
+ * 0x0002 : Quarter rate
+ * 0x0003 : Half rate
+ * 0x0004 : Full rate
+ */
+ uint16_t max_rate;
+ /* Set the upper bound encoder rate.
+ *
+ * 0x0000 : Blank frame
+ * 0x0001 : Eighth rate
+ * 0x0002 : Quarter rate
+ * 0x0003 : Half rate
+ * 0x0004 : Full rate
+ */
+} __attribute__((packed));
+
+struct vss_istream_cmd_set_enc_dtx_mode_t {
+ uint32_t enable;
+ /* Toggle DTX on or off.
+ *
+ * 0 : Disables DTX
+ * 1 : Enables DTX
+ */
+} __attribute__((packed));
+
+#define VSS_TAP_POINT_NONE 0x00010F78
+/* Indicates no tapping for specified path. */
+
+#define VSS_TAP_POINT_STREAM_END 0x00010F79
+/* Indicates that specified path should be tapped at the end of the stream. */
+
+struct vss_istream_cmd_start_record_t {
+ uint32_t rx_tap_point;
+ /* Tap point to use on the Rx path. Supported values are:
+ * VSS_TAP_POINT_NONE : Do not record Rx path.
+ * VSS_TAP_POINT_STREAM_END : Rx tap point is at the end of the stream.
+ */
+ uint32_t tx_tap_point;
+ /* Tap point to use on the Tx path. Supported values are:
+ * VSS_TAP_POINT_NONE : Do not record tx path.
+ * VSS_TAP_POINT_STREAM_END : Tx tap point is at the end of the stream.
+ */
+} __attribute__((packed));
+
+struct cvs_create_passive_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_create_passive_control_session_t cvs_session;
+} __attribute__((packed));
+
+struct cvs_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_create_full_control_session_t cvs_session;
+} __attribute__((packed));
+
+struct cvs_destroy_session_cmd {
+ struct apr_hdr hdr;
+} __attribute__((packed));
+
+struct cvs_cache_calibration_data_cmd {
+ struct apr_hdr hdr;
+} __attribute__ ((packed));
+
+struct cvs_set_mute_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_mute_t cvs_set_mute;
+} __attribute__((packed));
+
+struct cvs_set_media_type_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_media_type_t media_type;
+} __attribute__((packed));
+
+struct cvs_send_dec_buf_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_evt_send_dec_buffer_t dec_buf;
+} __attribute__((packed));
+
+struct cvs_set_amr_enc_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate;
+} __attribute__((packed));
+
+struct cvs_set_amrwb_enc_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate;
+} __attribute__((packed));
+
+struct cvs_set_cdma_enc_minmax_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate;
+} __attribute__((packed));
+
+struct cvs_set_enc_dtx_mode_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode;
+} __attribute__((packed));
+
+struct cvs_start_record_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_start_record_t rec_mode;
+} __attribute__((packed));
+
+/* TO CVP commands */
+
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+
+#define VSS_IVOCPROC_CMD_SET_DEVICE 0x000100C4
+
+#define VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA 0x000110E3
+
+#define VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE 0x000110E4
+
+#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB
+
+#define VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX 0x000110EE
+
+#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77
+
+/* Newtwork IDs */
+#define VSS_NETWORK_ID_DEFAULT 0x00010037
+#define VSS_NETWORK_ID_VOIP_NB 0x00011240
+#define VSS_NETWORK_ID_VOIP_WB 0x00011241
+#define VSS_NETWORK_ID_VOIP_WV 0x00011242
+
+/* Media types */
+#define VSS_MEDIA_ID_13K_MODEM 0x00010FC1
+/* Qcelp vocoder modem format */
+#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2
+/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */
+#define VSS_MEDIA_ID_4GV_NB_MODEM 0x00010FC3
+/* 4GV Narrowband modem format */
+#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4
+/* 4GV Wideband modem format */
+#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6
+/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7
+/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */
+#define VSS_MEDIA_ID_EFR_MODEM 0x00010FC8
+/*EFR modem format */
+#define VSS_MEDIA_ID_FR_MODEM 0x00010FC9
+/*FR modem format */
+#define VSS_MEDIA_ID_HR_MODEM 0x00010FCA
+/*HR modem format */
+#define VSS_MEDIA_ID_PCM_NB 0x00010FCB
+/* Linear PCM (16-bit, little-endian). */
+#define VSS_MEDIA_ID_PCM_WB 0x00010FCC
+/* Linear wideband PCM vocoder modem format (16 bits, little endian). */
+#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD
+/* G.711 a-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE
+/* G.711 mu-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G729 0x00010FD0
+/* G.729AB (contains two 10ms vocoder frames. */
+
+#define VOICE_CMD_SET_PARAM 0x00011006
+#define VOICE_CMD_GET_PARAM 0x00011007
+#define VOICE_EVT_GET_PARAM_ACK 0x00011008
+
+struct vss_ivocproc_cmd_create_full_control_session_t {
+ uint16_t direction;
+ /*
+ * stream direction.
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and RX
+ */
+ uint32_t tx_port_id;
+ /*
+ * TX device port ID which vocproc will connect to. If not supplying a
+ * port ID set to VSS_IVOCPROC_PORT_ID_NONE.
+ */
+ uint32_t tx_topology_id;
+ /*
+ * Tx leg topology ID. If not supplying a topology ID set to
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+ */
+ uint32_t rx_port_id;
+ /*
+ * RX device port ID which vocproc will connect to. If not supplying a
+ * port ID set to VSS_IVOCPROC_PORT_ID_NONE.
+ */
+ uint32_t rx_topology_id;
+ /*
+ * Rx leg topology ID. If not supplying a topology ID set to
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+ */
+ int32_t network_id;
+ /*
+ * Network ID. (Refer to VSS_NETWORK_ID_XXX). If not supplying a network
+ * ID set to VSS_NETWORK_ID_DEFAULT.
+ */
+} __attribute__((packed));
+
+struct vss_ivocproc_cmd_set_device_t {
+ uint32_t tx_port_id;
+ /**<
+ * TX device port ID which vocproc will connect to.
+ * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+ */
+ uint32_t tx_topology_id;
+ /**<
+ * TX leg topology ID.
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+ * pre/post-processing blocks and is pass-through.
+ */
+ int32_t rx_port_id;
+ /**<
+ * RX device port ID which vocproc will connect to.
+ * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+ */
+ uint32_t rx_topology_id;
+ /**<
+ * RX leg topology ID.
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+ * pre/post-processing blocks and is pass-through.
+ */
+} __attribute__((packed));
+
+struct vss_ivocproc_cmd_set_volume_index_t {
+ uint16_t vol_index;
+ /**<
+ * Volume index utilized by the vocproc to index into the volume table
+ * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set
+ * volume on the VDSP.
+ */
+} __attribute__((packed));
+
+struct cvp_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_create_full_control_session_t cvp_session;
+} __attribute__ ((packed));
+
+struct cvp_command {
+ struct apr_hdr hdr;
+} __attribute__((packed));
+
+struct cvp_set_device_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_set_device_t cvp_set_device;
+} __attribute__ ((packed));
+
+struct cvp_cache_calibration_data_cmd {
+ struct apr_hdr hdr;
+} __attribute__((packed));
+
+struct cvp_cache_volume_calibration_table_cmd {
+ struct apr_hdr hdr;
+} __attribute__((packed));
+
+struct cvp_set_vp3_data_cmd {
+ struct apr_hdr hdr;
+} __attribute__((packed));
+
+struct cvp_set_rx_volume_index_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
+} __attribute__((packed));
+
+/* CB for up-link packets. */
+typedef void (*ul_cb_fn)(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ void *private_data);
+
+/* CB for down-link packets. */
+typedef void (*dl_cb_fn)(uint8_t *voc_pkt,
+ uint32_t *pkt_len,
+ void *private_data);
+
+struct q_min_max_rate {
+ uint32_t min_rate;
+ uint32_t max_rate;
+};
+
+struct mvs_driver_info {
+ uint32_t media_type;
+ uint32_t rate;
+ uint32_t network_type;
+ uint32_t dtx_mode;
+ struct q_min_max_rate q_min_max_rate;
+ ul_cb_fn ul_cb;
+ dl_cb_fn dl_cb;
+ void *private_data;
+};
+
+struct incall_rec_info {
+ uint32_t pending;
+ uint32_t rec_mode;
+};
+
+struct incall_music_info {
+ uint32_t pending;
+ uint32_t playing;
+};
+
+struct voice_data {
+ int voc_state;/*INIT, CHANGE, RELEASE, RUN */
+
+ wait_queue_head_t mvm_wait;
+ wait_queue_head_t cvs_wait;
+ wait_queue_head_t cvp_wait;
+
+ /* cache the values related to Rx and Tx */
+ struct device_data dev_rx;
+ struct device_data dev_tx;
+
+ /* call status */
+ int v_call_status; /* Start or End */
+
+ u32 mvm_state;
+ u32 cvs_state;
+ u32 cvp_state;
+
+ /* Handle to MVM */
+ u16 mvm_handle;
+ /* Handle to CVS */
+ u16 cvs_handle;
+ /* Handle to CVP */
+ u16 cvp_handle;
+
+ struct mutex lock;
+
+ struct incall_rec_info rec_info;
+
+ struct incall_music_info music_info;
+
+ u16 session_id;
+};
+
+#define MAX_VOC_SESSIONS 2
+#define SESSION_ID_BASE 0xFFF0
+
+struct common_data {
+ uint32_t voc_path;
+ uint32_t adsp_version;
+ uint32_t device_events;
+
+ /* These default values are for all devices */
+ uint32_t default_mute_val;
+ uint32_t default_vol_val;
+ uint32_t default_sample_val;
+
+ /* APR to MVM in the modem */
+ void *apr_mvm;
+ /* APR to CVS in the modem */
+ void *apr_cvs;
+ /* APR to CVP in the modem */
+ void *apr_cvp;
+
+ /* APR to MVM in the Q6 */
+ void *apr_q6_mvm;
+ /* APR to CVS in the Q6 */
+ void *apr_q6_cvs;
+ /* APR to CVP in the Q6 */
+ void *apr_q6_cvp;
+
+ struct mutex common_lock;
+
+ struct mvs_driver_info mvs_info;
+
+ struct voice_data voice[MAX_VOC_SESSIONS];
+};
+
+int voice_set_voc_path_full(uint32_t set);
+
+void voice_register_mvs_cb(ul_cb_fn ul_cb,
+ dl_cb_fn dl_cb,
+ void *private_data);
+
+void voice_config_vocoder(uint32_t media_type,
+ uint32_t rate,
+ uint32_t network_type,
+ uint32_t dtx_mode,
+ struct q_min_max_rate q_min_max_rate);
+
+int voice_start_record(uint32_t rec_mode, uint32_t set);
+
+int voice_start_playback(uint32_t set);
+
+u16 voice_get_session_id(const char *name);
+#endif
diff --git a/include/sound/qdsp6v2/rtac.h b/include/sound/qdsp6v2/rtac.h
new file mode 100644
index 0000000000000..07be428f14d39
--- /dev/null
+++ b/include/sound/qdsp6v2/rtac.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2011, 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 __RTAC_H__
+#define __RTAC_H__
+
+/* Voice Modes */
+#define RTAC_CVP 0
+#define RTAC_CVS 1
+#define RTAC_VOICE_MODES 2
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id);
+void rtac_remove_adm_device(u32 port_id);
+void rtac_remove_popp_from_adm_devices(u32 popp_id);
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+ u32 tx_afe_port, u32 session_id);
+void rtac_remove_voice(u32 cvs_handle);
+void rtac_set_adm_handle(void *handle);
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size);
+void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size);
+void rtac_set_asm_handle(u32 session_id, void *handle);
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+ u32 payload_size);
+void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size);
+void rtac_set_voice_handle(u32 mode, void *handle);
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size);
+void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size);
+
+#endif
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 3cc252e55468e..c393530cbff4a 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -40,3 +40,65 @@ config SND_SOC_APQ8016_SBC
Support for Qualcomm Technologies LPASS audio block in
APQ8016 SOC-based systems.
Say Y if you want to use audio devices on MI2S.
+
+menu "QCOM QDSP Audio support"
+
+config SND_MSM_DAI_SOC
+ tristate
+
+config SND_MSM_SOC
+ tristate "SoC Audio for the MSM series chips"
+ select SND_MSM_DAI_SOC
+ select SND_MSM_SOC_MSM7K
+ default n
+ help
+ To add support for ALSA PCM driver for MSM board.
+
+config SND_SOC_MSM_QDSP6_HDMI_AUDIO
+ tristate "Soc QDSP6 HDMI Audio DAI driver"
+ default n
+ help
+ To support HDMI Audio on MSM8960 over QDSP6.
+
+config SND_SOC_MSM_QDSP6_INTF
+ bool "SoC Q6 audio driver for MSM8960"
+ default n
+ help
+ To add support for SoC audio on MSM8960.
+
+config SND_SOC_MSM_QDSP6V2_INTF
+ bool "SoC Q6 audio driver for MSM8974"
+ help
+ To add support for SoC audio on MSM8974.
+ This will enable all the platform specific
+ interactions towards DSP. It includes asm,
+ adm and afe interfaces on the DSP.
+
+
+config SND_SOC_QDSP6
+ tristate "SoC ALSA audio driver for QDSP6"
+ select SND_SOC_MSM_QDSP6_INTF
+ default n
+ help
+ To add support for MSM QDSP6 Soc Audio.
+
+config SND_SOC_QDSP6V2
+ tristate "SoC ALSA audio driver for QDSP6V2"
+ select SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for MSM QDSP6V2 Soc Audio.
+ This will enable sound soc platform specific
+ audio drivers. This includes q6asm, q6adm,
+ q6afe interfaces to DSP using apr.
+
+config SND_SOC_MSM8960
+ tristate "SoC Machine driver for MSM8960 and APQ8064 boards"
+ select SND_SOC_QDSP6
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_SOC_MSM_QDSP6_HDMI_AUDIO if FB_MSM_HDMI_MSM_PANEL
+ default n
+ help
+ To add support for SoC audio on MSM8960 and APQ8064 boards
+
+endmenu
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 79e5c50a8f71b..a313fdf8ce1c6 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -15,3 +15,12 @@ snd-soc-apq8016-sbc-objs := apq8016_sbc.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
+
+# for MSM 8960 sound card driver
+obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
+snd-soc-qdsp6-objs := msm-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o
+obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
+obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
+
+snd-soc-msm8960-objs := apq8064.o
+obj-$(CONFIG_SND_SOC_MSM8960) += snd-soc-msm8960.o
diff --git a/sound/soc/qcom/apq8064.c b/sound/soc/qcom/apq8064.c
new file mode 100644
index 0000000000000..31923ae68b8d7
--- /dev/null
+++ b/sound/soc/qcom/apq8064.c
@@ -0,0 +1,170 @@
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include "msm-pcm-routing.h"
+
+static int msm_hdmi_rx_ch = 2;
+static int hdmi_rate_variable;
+
+static const char *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
+
+static const struct soc_enum msm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
+};
+
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ return 1;
+}
+
+static int msm_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_rate_variable = ucontrol->value.integer.value[0];
+ pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+ return 0;
+}
+
+static int msm_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_rate_variable;
+ return 0;
+}
+
+static const struct snd_kcontrol_new tabla_msm_controls[] = {
+ SOC_ENUM_EXT("HDMI_RX Channels", msm_enum[0],
+ msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+ SOC_ENUM_EXT("HDMI RX Rate", msm_enum[1],
+ msm_hdmi_rate_get,
+ msm_hdmi_rate_put),
+};
+
+int msm_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ if (!hdmi_rate_variable)
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm_hdmi_rx_ch;
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_dai[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MultiMedia1 PCM",
+ .stream_name = "MultiMedia1 Playback",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "soc:msm_pcm",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1, /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1,
+ .be_hw_params_fixup = msm_hdmi_be_hw_params_fixup,
+
+ },
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "HDMI",
+ .platform_name = "soc:msm_pcm_routing",
+ .codec_name = "soc:codec_hdmi",
+ .codec_dai_name = "hdmi-hifi",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm_hdmi_be_hw_params_fixup,
+
+ },
+};
+
+static struct snd_soc_card snd_soc_card_msm = {
+ .name = "apq8064-tabla-snd-card",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+ .owner = THIS_MODULE,
+ .controls = tabla_msm_controls,
+ .num_controls = ARRAY_SIZE(tabla_msm_controls),
+};
+
+static int msm_snd_apq8064_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ snd_soc_card_msm.dev = &pdev->dev;
+ ret = snd_soc_register_card(&snd_soc_card_msm);
+ if (ret)
+ dev_err(&pdev->dev, "Error: snd_soc_register_card failed (%d)!\n", ret);
+
+ return ret;
+
+}
+
+static int msm_snd_apq8064_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id msm_snd_apq8064_dt_match[] = {
+ {.compatible = "qcom,snd-apq8064"},
+ {}
+};
+
+static struct platform_driver msm_snd_apq8064_driver = {
+ .probe = msm_snd_apq8064_probe,
+ .remove = msm_snd_apq8064_remove,
+ .driver = {
+ .name = "msm-snd-apq8064",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_snd_apq8064_dt_match,
+ },
+};
+module_platform_driver(msm_snd_apq8064_driver);
+/* Module information */
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/qcom/msm-dai-fe.c b/sound/soc/qcom/msm-dai-fe.c
new file mode 100644
index 0000000000000..e0f3bbe6731b0
--- /dev/null
+++ b/sound/soc/qcom/msm-dai-fe.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int multimedia_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_fe_Multimedia_dai_ops = {
+ .startup = multimedia_startup,
+};
+
+static struct snd_soc_dai_driver msm_fe_dais[] = {
+ {
+ .name = "MultiMedia1",
+ .id = 1,
+ .playback = {
+ .stream_name = "MultiMedia1 Playback",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 6,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_Multimedia_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver msm_dai_fe_component = {
+ .name = "msm-dai-fe",
+};
+
+static int msm_fe_dai_dev_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = snd_soc_register_component(&pdev->dev, &msm_dai_fe_component, msm_fe_dais, 1);
+
+ return ret;
+}
+
+static int msm_fe_dai_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_fe_dt_match[] = {
+ {.compatible = "qcom,msm-dai-fe"},
+ {}
+};
+
+static struct platform_driver msm_fe_dai_driver = {
+ .probe = msm_fe_dai_dev_probe,
+ .remove = msm_fe_dai_dev_remove,
+ .driver = {
+ .name = "msm-dai-fe",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_fe_dt_match,
+ },
+};
+module_platform_driver(msm_fe_dai_driver);
+/* Module information */
+MODULE_DESCRIPTION("MSM Frontend DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/msm-dai-q6-hdmi.c b/sound/soc/qcom/msm-dai-q6-hdmi.c
new file mode 100644
index 0000000000000..4f5b7da91493b
--- /dev/null
+++ b/sound/soc/qcom/msm-dai-q6-hdmi.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio.h>
+#include <sound/q6afe.h>
+#include <sound/q6adm.h>
+#include <sound/msm-dai-q6.h>
+#include <sound/msm_hdmi_audio.h>
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->port_config.hdmi_multi_ch.data_type = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+ return 0;
+}
+
+static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.hdmi_multi_ch.data_type;
+ return 0;
+}
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ */
+static const char *hdmi_format[] = {
+ "LPCM",
+ "Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+ SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+ msm_dai_q6_hdmi_format_get,
+ msm_dai_q6_hdmi_format_put),
+};
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ u32 channel_allocation = 0;
+ u32 level_shift = 0; /* 0dB */
+ bool down_mix = FALSE;
+ int sample_rate = 48000;
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.hdmi_multi_ch.reserved = 0;
+
+ dai_data->channels = 2;
+ dai_data->rate = 48000;
+ switch (dai_data->rate) {
+ case 48000:
+ sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+ break;
+ case 44100:
+ sample_rate = HDMI_SAMPLE_RATE_44_1KHZ;
+ break;
+ case 32000:
+ sample_rate = HDMI_SAMPLE_RATE_32KHZ;
+ break;
+ }
+ hdmi_msm_audio_sample_rate_reset(sample_rate);
+
+ switch (dai_data->channels) {
+ case 2:
+ channel_allocation = 0;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 8:
+ channel_allocation = 0x1F;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_8,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ default:
+ dev_err(dai->dev, "invalid Channels = %u\n",
+ dai_data->channels);
+ return -EINVAL;
+ }
+ dev_info(dai->dev, "%s() num_ch = %u rate =%u"
+ " channel_allocation = %u data type = %d\n", __func__,
+ dai_data->channels,
+ dai_data->rate,
+ dai_data->port_config.hdmi_multi_ch.channel_allocation,
+ dai_data->port_config.hdmi_multi_ch.data_type);
+
+ return 0;
+}
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_info("%s: afe port not started. dai_data->status_mask"
+ " = %ld\n", __func__, *dai_data->status_mask);
+ return;
+ }
+
+ rc = afe_close(HDMI_RX); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+ dai_data->rate = 48000;
+ dai_data->port_config.hdmi_multi_ch.channel_allocation = 0;
+ dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ dai_data->port_config.hdmi_multi_ch.data_type = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_port_start(HDMI_RX, &dai_data->port_config,
+ dai_data->rate);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ HDMI_RX);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ const struct snd_kcontrol_new *kcontrol;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ HDMI_RX);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ kcontrol = &hdmi_config_controls[0];
+
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(HDMI_RX); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+ snd_soc_unregister_component(dai->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+ .prepare = msm_dai_q6_hdmi_prepare,
+ .hw_params = msm_dai_q6_hdmi_hw_params,
+ .shutdown = msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+ .playback = {
+ .stream_name = "HDMI Playback",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 6,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+ .id = HDMI_RX,
+ .name = "HDMI",
+};
+
+static const struct snd_soc_component_driver msm_hdmi_q6_component = {
+ .name = "msm-dai-q6-hdmi",
+};
+/* To do: change to register DAIs as batch */
+static int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ dev_info(&pdev->dev, "dev name %s dev-id %d\n",
+ dev_name(&pdev->dev), pdev->id);
+
+ rc = snd_soc_register_component(&pdev->dev, &msm_hdmi_q6_component,
+ &msm_dai_q6_hdmi_hdmi_rx_dai, 1);
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_hdmi_dt_match[] = {
+ {.compatible = "qcom,msm-dai-q6-hdmi"},
+ {}
+};
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+ .probe = msm_dai_q6_hdmi_dev_probe,
+ .remove = msm_dai_q6_hdmi_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-hdmi",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_hdmi_dt_match,
+ },
+};
+
+module_platform_driver(msm_dai_q6_hdmi_driver);
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/msm-pcm-q6.c b/sound/soc/qcom/msm-pcm-q6.c
new file mode 100644
index 0000000000000..68eaf60de7d16
--- /dev/null
+++ b/sound/soc/qcom/msm-pcm-q6.c
@@ -0,0 +1,851 @@
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/q6afe.h>
+#include <sound/q6adm.h>
+#include <sound/msm-dai-q6.h>
+#include <sound/msm_hdmi_audio.h>
+#include "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+
+static struct audio_locks the_locks;
+
+extern int q6_hdmi_prepare(struct snd_pcm_substream *substream);
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+#define PLAYBACK_NUM_PERIODS 8
+#define PLAYBACK_PERIOD_SIZE 2048
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 16
+#define CAPTURE_MAX_PERIOD_SIZE 4096
+#define CAPTURE_MIN_PERIOD_SIZE 320
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
+ CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = CAPTURE_MAX_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = PLAYBACK_NUM_PERIODS * PLAYBACK_PERIOD_SIZE,
+ .period_bytes_min = PLAYBACK_PERIOD_SIZE,
+ .period_bytes_max = PLAYBACK_PERIOD_SIZE,
+ .periods_min = PLAYBACK_NUM_PERIODS,
+ .periods_max = PLAYBACK_NUM_PERIODS,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static uint32_t in_frame_info[CAPTURE_MAX_NUM_PERIODS][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+ void *priv_data)
+{
+ struct msm_audio *prtd = priv_data;
+
+ BUG_ON(!prtd);
+
+ pr_debug("%s: event %x\n", __func__, event);
+
+ switch (event) {
+ case MSM_PCM_RT_EVT_BUF_RECFG:
+ q6asm_cmd(prtd->audio_client, CMD_PAUSE);
+ q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ q6asm_run(prtd->audio_client, 0, 0, 0);
+ default:
+ break;
+ }
+}
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ int i = 0;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ if (q6asm_is_cpu_buf_avail_nolock(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_CMDRSP_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+ pr_debug("token = 0x%08x\n", token);
+ for (i = 0; i < 8; i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ in_frame_info[token][0] = payload[2];
+ in_frame_info[token][1] = payload[3];
+
+ /* assume data size = 0 during flushing */
+ if (in_frame_info[token][0]) {
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag &&
+ q6asm_is_cpu_buf_avail_nolock(OUT,
+ prtd->audio_client,
+ &size, &idx))
+ q6asm_read_nolock(prtd->audio_client);
+ } else {
+ pr_debug("%s: reclaim flushed buf in_count %x\n",
+ __func__, atomic_read(&prtd->in_count));
+ atomic_inc(&prtd->in_count);
+ if (atomic_read(&prtd->in_count) == prtd->periods) {
+ pr_info("%s: reclaimed all bufs\n", __func__);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ wake_up(&the_locks.read_wait);
+ }
+ }
+
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK) {
+ atomic_set(&prtd->start, 1);
+ break;
+ }
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ atomic_set(&prtd->start, 1);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ if (prtd->channel_mode > 2) {
+ ret = q6asm_enc_cfg_blk_multi_ch_pcm(prtd->audio_client,
+ prtd->samp_rate, prtd->channel_mode);
+ } else {
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client,
+ prtd->samp_rate, prtd->channel_mode);
+ }
+
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ prtd->audio_client->perf_mode = false;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw = msm_pcm_hardware_playback;
+ snd_soc_set_runtime_hwparams(substream, &msm_pcm_hardware_playback);
+ if(snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS) < 0)
+ pr_err("%s Failed to set hw periods\n", __func__);
+
+ if(snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE) < 0)
+ pr_err("%s Failed to set hw period size\n", __func__);
+
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+
+ pr_debug("%s: session ID %d\n", __func__,
+ prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream);
+ prtd->cmd_ack = 1;
+
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = msm_pcm_hardware_capture;
+ }
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+ if (snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ 2048) <0)
+ pr_err("snd_pcm_hw_constraint_integer period bytes failed\n");
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE,
+ CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE);
+ if (ret < 0) {
+ pr_err("constraint for buffer bytes min max ret = %d\n",
+ ret);
+ }
+ }
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ xfer = fbytes;
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d bufptr=%p buf=%p\n",
+ __func__, fbytes, xfer, size, bufptr, buf);
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ if (prtd->audio_client) {
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ q6asm_audio_client_free(prtd->audio_client);
+ }
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ //q6_hdmi_prepare(substream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ int result = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+
+ if (runtime->dma_addr && runtime->dma_bytes) {
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ result = remap_pfn_range(vma, vma->vm_start,
+ runtime->dma_addr >> PAGE_SHIFT,
+ runtime->dma_bytes,
+ vma->vm_page_prot);
+ } else {
+ pr_err("Physical address or size of buf is NULL");
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct audio_buffer *buf;
+ int dir, ret;
+ int format = FORMAT_LINEAR_PCM;
+ struct msm_pcm_routing_evt event;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ /*capture path*/
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (params_channels(params) > 2)
+ format = FORMAT_MULTI_CHANNEL_LINEAR_PCM;
+ pr_debug("%s format = :0x%x\n", __func__, format);
+
+ ret = q6asm_open_read(prtd->audio_client, format);
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_read failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ prtd->audio_client = NULL;
+ return -ENOMEM;
+ }
+
+ pr_debug("%s: session ID %d\n", __func__,
+ prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ event.event_func = msm_pcm_route_event_handler;
+ event.priv_data = (void *) prtd;
+ msm_pcm_routing_reg_phy_stream_v2(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id,
+ substream->stream, event);
+ }
+
+ if (dir == OUT) {
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ (params_buffer_bytes(params) / params_periods(params)),
+ params_periods(params));
+ pr_debug("buff bytes = %d, period size = %d,\
+ period count = %d\n", params_buffer_bytes(params),
+ params_periods(params),
+ params_buffer_bytes(params) / params_periods(params));
+ } else {
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ pr_debug("buff bytes = %d, period size = %d,\
+ period count = %d\n", params_buffer_bytes(params),
+ params_periods(params),
+ params_buffer_bytes(params) / params_periods(params));
+ }
+
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed \
+ rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+ if (buf == NULL || buf[0].data == NULL)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (dir == IN)
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ else
+ dma_buf->bytes = params_buffer_bytes(params);
+
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+ int ret;
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+ ret = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+ return ret;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_pcm_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-dsp"},
+ {}
+};
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dsp",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_pcm_dt_match,
+ },
+ .probe = msm_pcm_probe,
+ .remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("platform:msm-pcm-dsp");
diff --git a/sound/soc/qcom/msm-pcm-q6.h b/sound/soc/qcom/msm-pcm-q6.h
new file mode 100644
index 0000000000000..0522f549e8185
--- /dev/null
+++ b/sound/soc/qcom/msm-pcm-q6.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009,2011 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+
+
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+extern int copy_count;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ spinlock_t event_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+ wait_queue_head_t flush_wait;
+};
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct audio_client *audio_client;
+
+ uint16_t session_id;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t dsp_cnt;
+
+ int abort; /* set when error, like sample rate mismatch */
+
+ int enabled;
+ int close_ack;
+ int cmd_ack;
+ atomic_t start;
+ atomic_t stop;
+ atomic_t out_count;
+ atomic_t in_count;
+ atomic_t out_needed;
+ atomic_t eos;
+ int out_head;
+ int periods;
+ int mmap_flag;
+ atomic_t pending_buffer;
+ int cmd_interrupt;
+ bool meta_data_mode;
+};
+
+struct output_meta_data_st {
+ uint32_t meta_data_length;
+ uint32_t frame_size;
+ uint32_t timestamp_lsw;
+ uint32_t timestamp_msw;
+ uint32_t reserved[12];
+};
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/qcom/msm-pcm-routing.c b/sound/soc/qcom/msm-pcm-routing.c
new file mode 100644
index 0000000000000..27785805e7f86
--- /dev/null
+++ b/sound/soc/qcom/msm-pcm-routing.c
@@ -0,0 +1,894 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm.h>
+#include <sound/q6asm.h>
+#include <sound/q6afe.h>
+#include <sound/tlv.h>
+#include "msm-pcm-routing.h"
+
+struct msm_pcm_routing_bdai_data {
+ u16 port_id; /* AFE port ID */
+ u8 active; /* track if this backend is enabled */
+ unsigned long fe_sessions; /* Front-end sessions */
+ unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
+ unsigned int sample_rate;
+ unsigned int channel;
+ bool perf_mode;
+};
+
+struct msm_pcm_routing_fdai_data {
+ u16 be_srate; /* track prior backend sample rate for flushing purpose */
+ int strm_id; /* ASM stream ID */
+ struct msm_pcm_routing_evt event_info;
+};
+
+#define INVALID_SESSION -1
+#define SESSION_TYPE_RX 0
+#define SESSION_TYPE_TX 1
+
+static DEFINE_MUTEX(routing_lock);
+
+static int srs_alsa_ctrl_ever_called;
+
+#define INT_RX_VOL_MAX_STEPS 0x2000
+#define INT_RX_VOL_GAIN 0x2000
+#define INT_RX_LR_VOL_MAX_STEPS 0x20002000
+
+static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static const DECLARE_TLV_DB_LINEAR(lpa_rx_vol_gain, 0,
+ INT_RX_LR_VOL_MAX_STEPS);
+
+static const DECLARE_TLV_DB_LINEAR(multimedia2_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static const DECLARE_TLV_DB_LINEAR(multimedia5_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
+static const DECLARE_TLV_DB_LINEAR(compressed_rx_vol_gain, 0,
+ INT_RX_LR_VOL_MAX_STEPS);
+
+/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
+#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE
+
+enum {
+ EQ_BAND1 = 0,
+ EQ_BAND2,
+ EQ_BAND3,
+ EQ_BAND4,
+ EQ_BAND5,
+ EQ_BAND6,
+ EQ_BAND7,
+ EQ_BAND8,
+ EQ_BAND9,
+ EQ_BAND10,
+ EQ_BAND11,
+ EQ_BAND12,
+ EQ_BAND_MAX,
+};
+
+struct msm_audio_eq_band {
+ uint16_t band_idx; /* The band index, 0 .. 11 */
+ uint32_t filter_type; /* Filter band type */
+ uint32_t center_freq_hz; /* Filter band center frequency */
+ uint32_t filter_gain; /* Filter band initial gain (dB) */
+ /* Range is +12 dB to -12 dB with 1dB increments. */
+ uint32_t q_factor;
+} __packed;
+
+struct msm_audio_eq_stream_config {
+ uint32_t enable; /* Number of consequtive bands specified */
+ uint32_t num_bands;
+ struct msm_audio_eq_band eq_bands[EQ_BAND_MAX];
+} __packed;
+
+struct msm_audio_eq_stream_config eq_data[MAX_EQ_SESSIONS];
+
+static void msm_send_eq_values(int eq_idx);
+/* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
+ * If new back-end is defined, add new back-end DAI ID at the end of enum
+ */
+
+union srs_trumedia_params_u {
+ struct srs_trumedia_params srs_params;
+ unsigned short int raw_params[1];
+};
+static union srs_trumedia_params_u msm_srs_trumedia_params[2];
+static int srs_port_id = -1;
+
+static void srs_send_params(int port_id, unsigned int techs,
+ int param_block_idx) {
+ /* only send commands to dsp if srs alsa ctrl was used
+ at least one time */
+ if (!srs_alsa_ctrl_ever_called)
+ return;
+
+ pr_debug("SRS %s: called, port_id = %d, techs flags = %u,"
+ " paramblockidx %d", __func__, port_id, techs,
+ param_block_idx);
+ /* force all if techs is set to 1 */
+ if (techs == 1)
+ techs = 0xFFFFFFFF;
+
+ if (techs & (1 << SRS_ID_WOWHD))
+ srs_trumedia_open(port_id, SRS_ID_WOWHD,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.wowhd);
+ if (techs & (1 << SRS_ID_CSHP))
+ srs_trumedia_open(port_id, SRS_ID_CSHP,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.cshp);
+ if (techs & (1 << SRS_ID_HPF))
+ srs_trumedia_open(port_id, SRS_ID_HPF,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.hpf);
+ if (techs & (1 << SRS_ID_PEQ))
+ srs_trumedia_open(port_id, SRS_ID_PEQ,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.peq);
+ if (techs & (1 << SRS_ID_HL))
+ srs_trumedia_open(port_id, SRS_ID_HL,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.hl);
+ if (techs & (1 << SRS_ID_GLOBAL))
+ srs_trumedia_open(port_id, SRS_ID_GLOBAL,
+ (void *)&msm_srs_trumedia_params[param_block_idx].srs_params.global);
+}
+
+static struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
+ { PRIMARY_I2S_RX, 0, 0, 0, 0, 0},
+ { PRIMARY_I2S_TX, 0, 0, 0, 0, 0},
+ { SLIMBUS_0_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_0_TX, 0, 0, 0, 0, 0},
+ { HDMI_RX, 0, 0, 0, 0, 0},
+ { INT_BT_SCO_RX, 0, 0, 0, 0, 0},
+ { INT_BT_SCO_TX, 0, 0, 0, 0, 0},
+ { INT_FM_RX, 0, 0, 0, 0, 0},
+ { INT_FM_TX, 0, 0, 0, 0, 0},
+ { RT_PROXY_PORT_001_RX, 0, 0, 0, 0, 0},
+ { RT_PROXY_PORT_001_TX, 0, 0, 0, 0, 0},
+ { PCM_RX, 0, 0, 0, 0, 0},
+ { PCM_TX, 0, 0, 0, 0, 0},
+ { VOICE_PLAYBACK_TX, 0, 0, 0, 0, 0},
+ { VOICE_RECORD_RX, 0, 0, 0, 0, 0},
+ { VOICE_RECORD_TX, 0, 0, 0, 0, 0},
+ { MI2S_RX, 0, 0, 0, 0, 0},
+ { MI2S_TX, 0, 0, 0, 0},
+ { SECONDARY_I2S_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_1_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_1_TX, 0, 0, 0, 0, 0},
+ { SLIMBUS_4_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_4_TX, 0, 0, 0, 0, 0},
+ { SLIMBUS_3_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_3_TX, 0, 0, 0, 0, 0},
+ { SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
+ { SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
+ { SECONDARY_PCM_RX, 0, 0, 0, 0, 0},
+ { SECONDARY_PCM_TX, 0, 0, 0, 0, 0},
+};
+
+/* Track ASM playback & capture sessions of DAI */
+static struct msm_pcm_routing_fdai_data
+ fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
+ /* MULTIMEDIA1 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA2 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA3 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA4 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA5 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA6 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA7*/
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+ /* MULTIMEDIA8 */
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
+};
+
+static uint8_t is_be_dai_extproc(int be_dai)
+{
+ if (be_dai == MSM_BACKEND_DAI_EXTPROC_RX ||
+ be_dai == MSM_BACKEND_DAI_EXTPROC_TX ||
+ be_dai == MSM_BACKEND_DAI_EXTPROC_EC_TX)
+ return 1;
+ else
+ return 0;
+}
+
+static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
+ int path_type)
+{
+ int i, port_type;
+ struct route_payload payload;
+
+ payload.num_copps = 0;
+ port_type = (path_type == ADM_PATH_PLAYBACK ?
+ MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions)))
+ payload.copp_ids[payload.num_copps++] =
+ msm_bedais[i].port_id;
+ }
+
+ if (payload.num_copps)
+ adm_matrix_map(dspst_id, path_type,
+ payload.num_copps, payload.copp_ids, 0);
+}
+
+void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
+ int stream_type, int enable)
+{
+ int i, session_type, path_type, port_type;
+ u32 mode = 0;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+ mode = afe_get_port_type(msm_bedais[i].port_id);
+ if (enable)
+ adm_connect_afe_port(mode, dspst_id,
+ msm_bedais[i].port_id);
+ else
+ adm_disconnect_afe_port(mode, dspst_id,
+ msm_bedais[i].port_id);
+
+ break;
+ }
+ }
+ mutex_unlock(&routing_lock);
+}
+
+void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, int dspst_id,
+ int stream_type)
+{
+ int i, session_type, path_type, port_type;
+ struct route_payload payload;
+ u32 channels;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID %d\n", __func__, fedai_id);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ payload.num_copps = 0; /* only RX needs to use payload */
+ fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
+ /* re-enable EQ if active */
+ if (eq_data[fedai_id].enable)
+ msm_send_eq_values(fedai_id);
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
+ msm_bedais[i].perf_mode = perf_mode;
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+
+ channels = msm_bedais[i].channel;
+
+ if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
+ ((channels == 1) || (channels == 2)) &&
+ msm_bedais[i].perf_mode) {
+ pr_debug("%s configure COPP to lowlatency mode",
+ __func__);
+ adm_multi_ch_copp_open(msm_bedais[i].port_id,
+ path_type,
+ msm_bedais[i].sample_rate,
+ msm_bedais[i].channel,
+ DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode);
+ } else if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[i].port_id,
+ path_type,
+ msm_bedais[i].sample_rate,
+ msm_bedais[i].channel,
+ DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode);
+ else
+ adm_open(msm_bedais[i].port_id,
+ path_type,
+ msm_bedais[i].sample_rate,
+ msm_bedais[i].channel,
+ DEFAULT_COPP_TOPOLOGY);
+
+ payload.copp_ids[payload.num_copps++] =
+ msm_bedais[i].port_id;
+ srs_port_id = msm_bedais[i].port_id;
+ srs_send_params(srs_port_id, 1, 0);
+ }
+ }
+ if (payload.num_copps)
+ adm_matrix_map(dspst_id, path_type,
+ payload.num_copps, payload.copp_ids, 0);
+
+ mutex_unlock(&routing_lock);
+}
+void msm_pcm_routing_reg_phy_stream_v2(int fedai_id, bool perf_mode,
+ int dspst_id, int stream_type,
+ struct msm_pcm_routing_evt event_info)
+{
+ msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id,
+ stream_type);
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info;
+ else
+ fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info;
+
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+ int i, port_type, session_type;
+
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ port_type = MSM_AFE_PORT_TYPE_RX;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ port_type = MSM_AFE_PORT_TYPE_TX;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (!is_be_dai_extproc(i) &&
+ (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+ (msm_bedais[i].active) &&
+ (test_bit(fedai_id, &msm_bedais[i].fe_sessions)))
+ adm_close(msm_bedais[i].port_id);
+ }
+
+ fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION;
+ fe_dai_map[fedai_id][session_type].be_srate = 0;
+ mutex_unlock(&routing_lock);
+}
+
+/* Check if FE/BE route is set */
+static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id)
+{
+ bool rc = false;
+
+ if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return rc;
+ }
+
+ if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions))
+ rc = true;
+
+ return rc;
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+ int session_type, path_type;
+ u32 channels;
+ struct msm_pcm_routing_fdai_data *fdai;
+
+
+ if (val > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
+ if (afe_get_port_type(msm_bedais[reg].port_id) ==
+ MSM_AFE_PORT_TYPE_RX) {
+ session_type = SESSION_TYPE_RX;
+ path_type = ADM_PATH_PLAYBACK;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ path_type = ADM_PATH_LIVE_REC;
+ }
+
+ mutex_lock(&routing_lock);
+
+ if (set) {
+
+ set_bit(val, &msm_bedais[reg].fe_sessions);
+ fdai = &fe_dai_map[val][session_type];
+ if (msm_bedais[reg].active && fdai->strm_id !=
+ INVALID_SESSION) {
+ channels = msm_bedais[reg].channel;
+
+ if (session_type == SESSION_TYPE_TX && fdai->be_srate &&
+ (fdai->be_srate != msm_bedais[reg].sample_rate)) {
+ pr_debug("%s: flush strm %d due diff BE rates\n",
+ __func__, fdai->strm_id);
+
+ if (fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ fdai->event_info.priv_data);
+ fdai->be_srate = 0; /* might not need it */
+ }
+
+ if ((session_type == SESSION_TYPE_RX) &&
+ ((channels == 1) || (channels == 2))
+ && msm_bedais[reg].perf_mode) {
+ adm_multi_ch_copp_open(msm_bedais[reg].port_id,
+ path_type,
+ msm_bedais[reg].sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY,
+ msm_bedais[reg].perf_mode);
+ pr_debug("%s:configure COPP to lowlatency mode",
+ __func__);
+ } else if ((session_type == SESSION_TYPE_RX)
+ && (channels > 2))
+ adm_multi_ch_copp_open(msm_bedais[reg].port_id,
+ path_type,
+ msm_bedais[reg].sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY,
+ msm_bedais[reg].perf_mode);
+ else
+ adm_open(msm_bedais[reg].port_id,
+ path_type,
+ msm_bedais[reg].sample_rate, channels,
+ DEFAULT_COPP_TOPOLOGY);
+
+ if (session_type == SESSION_TYPE_RX &&
+ fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_DEVSWITCH,
+ fdai->event_info.priv_data);
+
+ msm_pcm_routing_build_matrix(val,
+ fdai->strm_id, path_type);
+ srs_port_id = msm_bedais[reg].port_id;
+ srs_send_params(srs_port_id, 1, 0);
+ }
+ } else {
+ clear_bit(val, &msm_bedais[reg].fe_sessions);
+ fdai = &fe_dai_map[val][session_type];
+ if (msm_bedais[reg].active && fdai->strm_id !=
+ INVALID_SESSION) {
+ fdai->be_srate = msm_bedais[reg].sample_rate;
+ adm_close(msm_bedais[reg].port_id);
+ msm_pcm_routing_build_matrix(val,
+ fdai->strm_id, path_type);
+ }
+ }
+
+ mutex_unlock(&routing_lock);
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mask = (1 << fls(mc->max)) - 1;
+ unsigned short val = (ucontrol->value.integer.value[0] & mask);
+ struct snd_soc_dapm_update update;
+ struct snd_soc_card *card;
+
+ card = dapm->card;
+ update.kcontrol = kcontrol;
+ update.reg = mc->reg;
+ update.mask = mask;
+ update.val = val;
+
+
+ if (ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->reg, mc->shift) == false) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, &update);
+ } else if (!ucontrol->value.integer.value[0] &&
+ msm_pcm_routing_route_is_set(mc->reg, mc->shift) == true) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, &update);
+ }
+
+ return 0;
+}
+
+static void msm_send_eq_values(int eq_idx)
+{
+ int result;
+ struct audio_client *ac = q6asm_get_audio_client(
+ fe_dai_map[eq_idx][SESSION_TYPE_RX].strm_id);
+
+ if (ac == NULL) {
+ pr_err("%s: Could not get audio client for session: %d\n",
+ __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX].strm_id);
+ goto done;
+ }
+
+ result = q6asm_equalizer(ac, &eq_data[eq_idx]);
+
+ if (result < 0)
+ pr_err("%s: Call to ASM equalizer failed, returned = %d\n",
+ __func__, result);
+done:
+ return;
+}
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Frontend AIF */
+ /* Widget name equals to Front-End DAI name<Need confirmation>,
+ * Stream name must contains substring of front-end dai name
+ */
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
+ /* Backend AIF */
+ /* Stream name equals to backend dai link stream name
+ */
+ SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+ /* incall */
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+ {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+ {"HDMI Mixer", "MultiMedia3", "MM_DL3"},
+ {"HDMI Mixer", "MultiMedia4", "MM_DL4"},
+ {"HDMI Mixer", "MultiMedia5", "MM_DL5"},
+ {"HDMI Mixer", "MultiMedia6", "MM_DL6"},
+ {"HDMI Mixer", "MultiMedia7", "MM_DL7"},
+ {"HDMI Mixer", "MultiMedia8", "MM_DL8"},
+ {"HDMI", NULL, "HDMI Mixer"},
+ {"BE_OUT", NULL, "HDMI Playback"},
+ {"HDMI Playback", NULL, "MultiMedia1 Playback"},
+};
+
+int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&routing_lock);
+ msm_bedais[be_id].sample_rate = params_rate(params);
+ msm_bedais[be_id].channel = params_channels(params);
+ mutex_unlock(&routing_lock);
+ return 0;
+}
+
+static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, session_type;
+ struct msm_pcm_routing_bdai_data *bedai;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ bedai = &msm_bedais[be_id];
+ session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ 0 : 1);
+
+ mutex_lock(&routing_lock);
+
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+ if (fe_dai_map[i][session_type].strm_id != INVALID_SESSION) {
+ fe_dai_map[i][session_type].be_srate =
+ bedai->sample_rate;
+ adm_close(bedai->port_id);
+ srs_port_id = -1;
+ }
+ }
+
+ bedai->active = 0;
+ bedai->sample_rate = 0;
+ bedai->channel = 0;
+ bedai->perf_mode = false;
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int be_id = rtd->dai_link->be_id;
+ int i, path_type, session_type;
+ struct msm_pcm_routing_bdai_data *bedai;
+ u32 channels;
+ bool playback, capture;
+ struct msm_pcm_routing_fdai_data *fdai;
+
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
+ bedai = &msm_bedais[be_id];
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ path_type = ADM_PATH_PLAYBACK;
+ session_type = SESSION_TYPE_RX;
+ } else {
+ path_type = ADM_PATH_LIVE_REC;
+ session_type = SESSION_TYPE_TX;
+ }
+
+ mutex_lock(&routing_lock);
+
+ if (bedai->active == 1)
+ goto done; /* Ignore prepare if back-end already active */
+
+ /* AFE port is not active at this point. However, still
+ * go ahead setting active flag under the notion that
+ * QDSP6 is able to handle ADM starting before AFE port
+ * is started.
+ */
+ bedai->active = 1;
+ playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ capture = substream->stream == SNDRV_PCM_STREAM_CAPTURE;
+
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+ fdai = &fe_dai_map[i][session_type];
+ if (fdai->strm_id != INVALID_SESSION) {
+ if (session_type == SESSION_TYPE_TX && fdai->be_srate &&
+ (fdai->be_srate != bedai->sample_rate)) {
+ pr_debug("%s: flush strm %d due diff BE rates\n",
+ __func__,
+ fdai->strm_id);
+
+ if (fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ fdai->event_info.priv_data);
+ fdai->be_srate = 0; /* might not need it */
+ }
+
+ channels = bedai->channel;
+ if ((playback || capture)
+ && ((channels == 2) || (channels == 1)) &&
+ bedai->perf_mode) {
+ adm_multi_ch_copp_open(bedai->port_id,
+ path_type,
+ bedai->sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY, bedai->perf_mode);
+ pr_debug("%s:configure COPP to lowlatency mode",
+ __func__);
+ } else if ((playback || capture)
+ && (channels > 2))
+ adm_multi_ch_copp_open(bedai->port_id,
+ path_type,
+ bedai->sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY, bedai->perf_mode);
+ else
+ adm_open(bedai->port_id,
+ path_type,
+ bedai->sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY);
+
+ msm_pcm_routing_build_matrix(i,
+ fdai->strm_id, path_type);
+ srs_port_id = bedai->port_id;
+ srs_send_params(srs_port_id, 1, 0);
+ }
+ }
+
+done:
+ mutex_unlock(&routing_lock);
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_routing_pcm_ops = {
+ .hw_params = msm_pcm_routing_hw_params,
+ .close = msm_pcm_routing_close,
+ .prepare = msm_pcm_routing_prepare,
+};
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+ return 0;
+}
+
+static int msm_asoc_routing_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+ .ops = &msm_routing_pcm_ops,
+ .probe = msm_routing_probe,
+ .pcm_new = msm_asoc_routing_pcm_new,
+ .component_driver = {
+ .name = "msm-qdsp6-routing-dai",
+ .dapm_widgets = msm_qdsp6_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ },
+};
+
+static int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+ int ret;
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+ ret = snd_soc_register_platform(&pdev->dev,
+ &msm_soc_routing_platform);
+ return ret;
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_routing_dt_match[] = {
+ {.compatible = "qcom,msm-pcm-routing"},
+ {}
+};
+
+static struct platform_driver msm_routing_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-routing",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_routing_dt_match,
+ },
+ .probe = msm_routing_pcm_probe,
+ .remove = (msm_routing_pcm_remove),
+};
+
+int msm_routing_check_backend_enabled(int fedai_id)
+{
+ int i;
+
+ if (fedai_id >= MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return 0;
+ }
+ for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
+ return msm_bedais[i].active;
+ }
+ return 0;
+}
+module_platform_driver(msm_routing_pcm_driver);
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/msm-pcm-routing.h b/sound/soc/qcom/msm-pcm-routing.h
new file mode 100644
index 0000000000000..ad63c120aad8b
--- /dev/null
+++ b/sound/soc/qcom/msm-pcm-routing.h
@@ -0,0 +1,145 @@
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_ROUTING_H
+#define _MSM_PCM_ROUTING_H
+#include <sound/apr_audio.h>
+
+#define LPASS_BE_PRI_I2S_RX "PRIMARY_I2S_RX"
+#define LPASS_BE_PRI_I2S_TX "PRIMARY_I2S_TX"
+#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX"
+#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX"
+#define LPASS_BE_HDMI "HDMI"
+#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX"
+#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX"
+#define LPASS_BE_INT_FM_RX "INT_FM_RX"
+#define LPASS_BE_INT_FM_TX "INT_FM_TX"
+#define LPASS_BE_AFE_PCM_RX "RT_PROXY_DAI_001_RX"
+#define LPASS_BE_AFE_PCM_TX "RT_PROXY_DAI_002_TX"
+#define LPASS_BE_AUXPCM_RX "AUX_PCM_RX"
+#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX"
+#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX"
+#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX"
+#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX"
+#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
+#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
+#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+
+#define LPASS_BE_MI2S_RX "(Backend) MI2S_RX"
+#define LPASS_BE_MI2S_TX "(Backend) MI2S_TX"
+#define LPASS_BE_STUB_RX "(Backend) STUB_RX"
+#define LPASS_BE_STUB_TX "(Backend) STUB_TX"
+#define LPASS_BE_SLIMBUS_1_RX "(Backend) SLIMBUS_1_RX"
+#define LPASS_BE_SLIMBUS_1_TX "(Backend) SLIMBUS_1_TX"
+#define LPASS_BE_STUB_1_TX "(Backend) STUB_1_TX"
+#define LPASS_BE_SLIMBUS_3_RX "(Backend) SLIMBUS_3_RX"
+#define LPASS_BE_SLIMBUS_3_TX "(Backend) SLIMBUS_3_TX"
+#define LPASS_BE_SLIMBUS_4_RX "(Backend) SLIMBUS_4_RX"
+#define LPASS_BE_SLIMBUS_4_TX "(Backend) SLIMBUS_4_TX"
+
+/* For multimedia front-ends, asm session is allocated dynamically.
+ * Hence, asm session/multimedia front-end mapping has to be maintained.
+ * Due to this reason, additional multimedia front-end must be placed before
+ * non-multimedia front-ends.
+ */
+
+enum {
+ MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2,
+ MSM_FRONTEND_DAI_MULTIMEDIA3,
+ MSM_FRONTEND_DAI_MULTIMEDIA4,
+ MSM_FRONTEND_DAI_MULTIMEDIA5,
+ MSM_FRONTEND_DAI_MULTIMEDIA6,
+ MSM_FRONTEND_DAI_MULTIMEDIA7,
+ MSM_FRONTEND_DAI_MULTIMEDIA8,
+ MSM_FRONTEND_DAI_CS_VOICE,
+ MSM_FRONTEND_DAI_VOIP,
+ MSM_FRONTEND_DAI_AFE_RX,
+ MSM_FRONTEND_DAI_AFE_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB,
+ MSM_FRONTEND_DAI_VOLTE,
+ MSM_FRONTEND_DAI_VOICE2,
+ MSM_FRONTEND_DAI_VOLTE_STUB,
+ MSM_FRONTEND_DAI_VOICE2_STUB,
+ MSM_FRONTEND_DAI_MAX,
+};
+
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA8 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA8
+
+enum {
+ MSM_BACKEND_DAI_PRI_I2S_RX = 0,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_MI2S_TX,
+ MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_BACKEND_DAI_EXTPROC_RX,
+ MSM_BACKEND_DAI_EXTPROC_TX,
+ MSM_BACKEND_DAI_EXTPROC_EC_TX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_BACKEND_DAI_MAX,
+};
+
+enum msm_pcm_routing_event {
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ MSM_PCM_RT_EVT_DEVSWITCH,
+ MSM_PCM_RT_EVT_MAX,
+};
+/* dai_id: front-end ID,
+ * dspst_id: DSP audio stream ID
+ * stream_type: playback or capture
+ */
+void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode,
+ int dspst_id, int stream_type);
+void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
+ int stream_type, int enable);
+
+struct msm_pcm_routing_evt {
+ void (*event_func)(enum msm_pcm_routing_event, void *);
+ void *priv_data;
+};
+
+void msm_pcm_routing_reg_phy_stream_v2(int fedai_id, bool perf_mode,
+ int dspst_id, int stream_type,
+ struct msm_pcm_routing_evt event_info);
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+int lpa_set_volume(unsigned volume);
+
+int msm_routing_check_backend_enabled(int fedai_id);
+
+int multi_ch_pcm_set_volume(unsigned volume);
+
+int compressed_set_volume(unsigned volume);
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
new file mode 100644
index 0000000000000..b636583a60284
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -0,0 +1,2 @@
+obj-y := q6asm.o q6adm.o q6afe.o
+obj-$(CONFIG_SND_SOC_QDSP6) += core/
diff --git a/sound/soc/qcom/qdsp6/core/Makefile b/sound/soc/qcom/qdsp6/core/Makefile
new file mode 100644
index 0000000000000..06089d9bced97
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/Makefile
@@ -0,0 +1,3 @@
+obj-y += apr.o apr_v1.o apr_tal.o q6core.o dsp_debug.o
+obj-y += audio_acdb.o
+obj-y += rtac.o
diff --git a/sound/soc/qcom/qdsp6/core/apr.c b/sound/soc/qcom/qdsp6/core/apr.c
new file mode 100644
index 0000000000000..ae4801e4f8369
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/apr.c
@@ -0,0 +1,649 @@
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/remoteproc.h>
+#include <sound/qdsp6v2/apr.h>
+#include <sound/qdsp6v2/apr_tal.h>
+#include <sound/qdsp6v2/dsp_debug.h>
+
+static struct apr_q6 q6;
+static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
+
+static wait_queue_head_t dsp_wait;
+static wait_queue_head_t modem_wait;
+/* Subsystem restart: QDSP6 data, functions */
+static struct workqueue_struct *apr_reset_workqueue;
+static void apr_reset_deregister(struct work_struct *work);
+struct apr_reset_work {
+ void *handle;
+ struct work_struct work;
+};
+
+struct apr_svc_table {
+ char name[64];
+ int idx;
+ int id;
+ int client_id;
+};
+
+static const struct apr_svc_table svc_tbl_audio[] = {
+ {
+ .name = "AFE",
+ .idx = 0,
+ .id = APR_SVC_AFE,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "ASM",
+ .idx = 1,
+ .id = APR_SVC_ASM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "ADM",
+ .idx = 2,
+ .id = APR_SVC_ADM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "CORE",
+ .idx = 3,
+ .id = APR_SVC_ADSP_CORE,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "TEST",
+ .idx = 4,
+ .id = APR_SVC_TEST_CLIENT,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "MVM",
+ .idx = 5,
+ .id = APR_SVC_ADSP_MVM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "CVS",
+ .idx = 6,
+ .id = APR_SVC_ADSP_CVS,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "CVP",
+ .idx = 7,
+ .id = APR_SVC_ADSP_CVP,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "USM",
+ .idx = 8,
+ .id = APR_SVC_USM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+};
+
+static struct apr_svc_table svc_tbl_voice[] = {
+ {
+ .name = "VSM",
+ .idx = 0,
+ .id = APR_SVC_VSM,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "VPM",
+ .idx = 1,
+ .id = APR_SVC_VPM,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "MVS",
+ .idx = 2,
+ .id = APR_SVC_MVS,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "MVM",
+ .idx = 3,
+ .id = APR_SVC_MVM,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "CVS",
+ .idx = 4,
+ .id = APR_SVC_CVS,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "CVP",
+ .idx = 5,
+ .id = APR_SVC_CVP,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "SRD",
+ .idx = 6,
+ .id = APR_SVC_SRD,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "TEST",
+ .idx = 7,
+ .id = APR_SVC_TEST_CLIENT,
+ .client_id = APR_CLIENT_VOICE,
+ },
+};
+
+enum apr_subsys_state apr_get_modem_state(void)
+{
+ return atomic_read(&q6.modem_state);
+}
+
+void apr_set_modem_state(enum apr_subsys_state state)
+{
+ atomic_set(&q6.modem_state, state);
+}
+
+enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
+ enum apr_subsys_state new)
+{
+ return atomic_cmpxchg(&q6.modem_state, prev, new);
+}
+
+enum apr_subsys_state apr_get_q6_state(void)
+{
+ return atomic_read(&q6.q6_state);
+}
+EXPORT_SYMBOL_GPL(apr_get_q6_state);
+
+int apr_set_q6_state(enum apr_subsys_state state)
+{
+ pr_debug("%s: setting adsp state %d\n", __func__, state);
+ if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
+ return -EINVAL;
+ atomic_set(&q6.q6_state, state);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apr_set_q6_state);
+
+enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
+ enum apr_subsys_state new)
+{
+ return atomic_cmpxchg(&q6.q6_state, prev, new);
+}
+
+int apr_wait_for_device_up(int dest_id)
+{
+ int rc = -1;
+ if (dest_id == APR_DEST_MODEM)
+ rc = wait_event_interruptible_timeout(modem_wait,
+ (apr_get_modem_state() == APR_SUBSYS_UP),
+ (1 * HZ));
+ else if (dest_id == APR_DEST_QDSP6)
+ rc = wait_event_interruptible_timeout(dsp_wait,
+ (apr_get_q6_state() == APR_SUBSYS_UP),
+ (1 * HZ));
+ else
+ pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
+ /* returns left time */
+ return rc;
+}
+
+int apr_load_adsp_image(void)
+{
+ int rc = 0;
+ struct device_node *np;
+ phandle phandle;
+ const __be32 *list;
+ int size;
+
+ mutex_lock(&q6.lock);
+ if (apr_get_q6_state() == APR_SUBSYS_UP) {
+ np = of_find_compatible_node(NULL, NULL, "qcom,apr");
+ list = of_get_property(np, "rproc", &size);
+ phandle = be32_to_cpup(list++);
+
+ q6.rproc = rproc_get_by_phandle(phandle);
+
+ if (!q6.rproc) {
+ rc = -ENODEV;
+ pr_err("APR: Unable to load q6 image, error:%d\n", rc);
+ } else {
+ rproc_boot(q6.rproc);
+ apr_set_q6_state(APR_SUBSYS_LOADED);
+ pr_debug("APR: Image is loaded, stated\n");
+ }
+ } else
+ pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
+ mutex_unlock(&q6.lock);
+ return rc;
+}
+
+struct apr_client *apr_get_client(int dest_id, int client_id)
+{
+ return &client[dest_id][client_id];
+}
+
+int apr_send_pkt(void *handle, uint32_t *buf)
+{
+ struct apr_svc *svc = handle;
+ struct apr_client *clnt;
+ struct apr_hdr *hdr;
+ uint16_t dest_id;
+ uint16_t client_id;
+ uint16_t w_len;
+ unsigned long flags;
+
+ if (!handle || !buf) {
+ pr_err("APR: Wrong parameters\n");
+ return -EINVAL;
+ }
+ if (svc->need_reset) {
+ pr_err("apr: send_pkt service need reset\n");
+ return -ENETRESET;
+ }
+
+ if ((svc->dest_id == APR_DEST_QDSP6) &&
+ (apr_get_q6_state() != APR_SUBSYS_LOADED)) {
+ pr_err("%s: Still dsp is not Up\n", __func__);
+ return -ENETRESET;
+ } else if ((svc->dest_id == APR_DEST_MODEM) &&
+ (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
+ pr_err("apr: Still Modem is not Up\n");
+ return -ENETRESET;
+ }
+
+ spin_lock_irqsave(&svc->w_lock, flags);
+ dest_id = svc->dest_id;
+ client_id = svc->client_id;
+ clnt = &client[dest_id][client_id];
+
+ if (!client[dest_id][client_id].handle) {
+ pr_err("APR: Still service is not yet opened\n");
+ spin_unlock_irqrestore(&svc->w_lock, flags);
+ return -EINVAL;
+ }
+ hdr = (struct apr_hdr *)buf;
+
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->src_svc = svc->id;
+ if (dest_id == APR_DEST_MODEM)
+ hdr->dest_domain = APR_DOMAIN_MODEM;
+ else if (dest_id == APR_DEST_QDSP6)
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+
+ hdr->dest_svc = svc->id;
+
+ w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size);
+ if (w_len != hdr->pkt_size)
+ pr_err("Unable to write APR pkt successfully: %d\n", w_len);
+ spin_unlock_irqrestore(&svc->w_lock, flags);
+
+ return w_len;
+}
+
+void apr_cb_func(void *buf, int len, void *priv)
+{
+ struct apr_client_data data;
+ struct apr_client *apr_client;
+ struct apr_svc *c_svc;
+ struct apr_hdr *hdr;
+ uint16_t hdr_size;
+ uint16_t msg_type;
+ uint16_t ver;
+ uint16_t src;
+ uint16_t svc;
+ uint16_t clnt;
+ int i;
+ int temp_port = 0;
+ uint32_t *ptr;
+
+ pr_debug("APR2: len = %d\n", len);
+ ptr = buf;
+ pr_debug("\n*****************\n");
+ for (i = 0; i < len/4; i++)
+ pr_debug("%x ", ptr[i]);
+ pr_debug("\n");
+ pr_debug("\n*****************\n");
+
+ if (!buf || len <= APR_HDR_SIZE) {
+ pr_err("APR: Improper apr pkt received:%p %d\n", buf, len);
+ return;
+ }
+ hdr = buf;
+
+ ver = hdr->hdr_field;
+ ver = (ver & 0x000F);
+ if (ver > APR_PKT_VER + 1) {
+ pr_err("APR: Wrong version: %d\n", ver);
+ return;
+ }
+
+ hdr_size = hdr->hdr_field;
+ hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4;
+ if (hdr_size < APR_HDR_SIZE) {
+ pr_err("APR: Wrong hdr size:%d\n", hdr_size);
+ return;
+ }
+
+ if (hdr->pkt_size < APR_HDR_SIZE) {
+ pr_err("APR: Wrong paket size\n");
+ return;
+ }
+ msg_type = hdr->hdr_field;
+ msg_type = (msg_type >> 0x08) & 0x0003;
+ if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
+ pr_err("APR: Wrong message type: %d\n", msg_type);
+ return;
+ }
+
+ if (hdr->src_domain >= APR_DOMAIN_MAX ||
+ hdr->dest_domain >= APR_DOMAIN_MAX ||
+ hdr->src_svc >= APR_SVC_MAX ||
+ hdr->dest_svc >= APR_SVC_MAX) {
+ pr_err("APR: Wrong APR header\n");
+ return;
+ }
+
+ svc = hdr->dest_svc;
+ if (hdr->src_domain == APR_DOMAIN_MODEM) {
+ src = APR_DEST_MODEM;
+ if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
+ svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
+ svc == APR_SVC_TEST_CLIENT)
+ clnt = APR_CLIENT_VOICE;
+ else {
+ pr_err("APR: Wrong svc :%d\n", svc);
+ return;
+ }
+ } else if (hdr->src_domain == APR_DOMAIN_ADSP) {
+ src = APR_DEST_QDSP6;
+ if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
+ svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
+ svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
+ svc == APR_SVC_USM ||
+ svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
+ svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP)
+ clnt = APR_CLIENT_AUDIO;
+ else {
+ pr_err("APR: Wrong svc :%d\n", svc);
+ return;
+ }
+ } else {
+ pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
+ return;
+ }
+
+ pr_debug("src =%d clnt = %d\n", src, clnt);
+ apr_client = &client[src][clnt];
+ for (i = 0; i < APR_SVC_MAX; i++)
+ if (apr_client->svc[i].id == svc) {
+ pr_debug("%d\n", apr_client->svc[i].id);
+ c_svc = &apr_client->svc[i];
+ break;
+ }
+
+ if (i == APR_SVC_MAX) {
+ pr_err("APR: service is not registered\n");
+ return;
+ }
+ pr_debug("svc_idx = %d\n", i);
+ pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id,
+ c_svc->client_id, c_svc->fn, c_svc->priv);
+ data.payload_size = hdr->pkt_size - hdr_size;
+ data.opcode = hdr->opcode;
+ data.src = src;
+ data.src_port = hdr->src_port;
+ data.dest_port = hdr->dest_port;
+ data.token = hdr->token;
+ data.msg_type = msg_type;
+ if (data.payload_size > 0)
+ data.payload = (char *)hdr + hdr_size;
+
+ temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF);
+ pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
+ if (c_svc->port_cnt && c_svc->port_fn[temp_port])
+ c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]);
+ else if (c_svc->fn)
+ c_svc->fn(&data, c_svc->priv);
+ else
+ pr_err("APR: Rxed a packet for NULL callback\n");
+}
+
+int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
+ int *svc_idx, int *svc_id)
+{
+ int i;
+ int size;
+ struct apr_svc_table *tbl;
+ int ret = 0;
+
+ if (dest_id == APR_DEST_QDSP6) {
+ tbl = (struct apr_svc_table *)&svc_tbl_audio;
+ size = ARRAY_SIZE(svc_tbl_audio);
+ } else {
+ tbl = (struct apr_svc_table *)&svc_tbl_voice;
+ size = ARRAY_SIZE(svc_tbl_voice);
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!strncmp(svc_name, tbl[i].name, strlen(tbl[i].name))) {
+ *client_id = tbl[i].client_id;
+ *svc_idx = tbl[i].idx;
+ *svc_id = tbl[i].id;
+ break;
+ }
+ }
+
+ pr_debug("%s: svc_name = %s c_id = %d dest_id = %d\n",
+ __func__, svc_name, *client_id, dest_id);
+ if (i == size) {
+ pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void apr_reset_deregister(struct work_struct *work)
+{
+ struct apr_svc *handle = NULL;
+ struct apr_reset_work *apr_reset =
+ container_of(work, struct apr_reset_work, work);
+
+ handle = apr_reset->handle;
+ pr_debug("%s:handle[%p]\n", __func__, handle);
+ apr_deregister(handle);
+ kfree(apr_reset);
+}
+
+int apr_deregister(void *handle)
+{
+ struct apr_svc *svc = handle;
+ struct apr_client *clnt;
+ uint16_t dest_id;
+ uint16_t client_id;
+
+ if (!handle)
+ return -EINVAL;
+
+ mutex_lock(&svc->m_lock);
+ dest_id = svc->dest_id;
+ client_id = svc->client_id;
+ clnt = &client[dest_id][client_id];
+
+ if (svc->port_cnt > 0 || svc->svc_cnt > 0) {
+ if (svc->port_cnt)
+ svc->port_cnt--;
+ else if (svc->svc_cnt)
+ svc->svc_cnt--;
+ if (!svc->port_cnt && !svc->svc_cnt) {
+ client[dest_id][client_id].svc_cnt--;
+ svc->need_reset = 0x0;
+ }
+ } else if (client[dest_id][client_id].svc_cnt > 0) {
+ client[dest_id][client_id].svc_cnt--;
+ if (!client[dest_id][client_id].svc_cnt) {
+ svc->need_reset = 0x0;
+ pr_debug("%s: service is reset %p\n", __func__, svc);
+ }
+ }
+
+ if (!svc->port_cnt && !svc->svc_cnt) {
+ svc->priv = NULL;
+ svc->id = 0;
+ svc->fn = NULL;
+ svc->dest_id = 0;
+ svc->client_id = 0;
+ svc->need_reset = 0x0;
+ }
+ if (client[dest_id][client_id].handle &&
+ !client[dest_id][client_id].svc_cnt) {
+ apr_tal_close(client[dest_id][client_id].handle);
+ client[dest_id][client_id].handle = NULL;
+ }
+ mutex_unlock(&svc->m_lock);
+
+ return 0;
+}
+
+void apr_reset(void *handle)
+{
+ struct apr_reset_work *apr_reset_worker = NULL;
+
+ if (!handle)
+ return;
+ pr_debug("%s: handle[%p]\n", __func__, handle);
+
+ if (apr_reset_workqueue == NULL) {
+ pr_err("%s: apr_reset_workqueue is NULL\n", __func__);
+ return;
+ }
+
+ apr_reset_worker = kzalloc(sizeof(struct apr_reset_work),
+ GFP_ATOMIC);
+
+ if (apr_reset_worker == NULL) {
+ pr_err("%s: mem failure\n", __func__);
+ return;
+ }
+
+ apr_reset_worker->handle = handle;
+ INIT_WORK(&apr_reset_worker->work, apr_reset_deregister);
+ queue_work(apr_reset_workqueue, &apr_reset_worker->work);
+}
+
+static int adsp_state(int state)
+{
+ pr_info("dsp state = %d\n", state);
+ return 0;
+}
+
+/* Dispatch the Reset events to Modem and audio clients */
+void dispatch_event(unsigned long code, unsigned short proc)
+{
+ struct apr_client *apr_client;
+ struct apr_client_data data;
+ struct apr_svc *svc;
+ uint16_t clnt;
+ int i, j;
+
+ data.opcode = RESET_EVENTS;
+ data.reset_event = code;
+ data.reset_proc = proc;
+
+ clnt = APR_CLIENT_AUDIO;
+ apr_client = &client[proc][clnt];
+ for (i = 0; i < APR_SVC_MAX; i++) {
+ mutex_lock(&apr_client->svc[i].m_lock);
+ if (apr_client->svc[i].fn) {
+ apr_client->svc[i].need_reset = 0x1;
+ apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
+ }
+ if (apr_client->svc[i].port_cnt) {
+ svc = &(apr_client->svc[i]);
+ svc->need_reset = 0x1;
+ for (j = 0; j < APR_MAX_PORTS; j++)
+ if (svc->port_fn[j])
+ svc->port_fn[j](&data,
+ svc->port_priv[j]);
+ }
+ mutex_unlock(&apr_client->svc[i].m_lock);
+ }
+
+ clnt = APR_CLIENT_VOICE;
+ apr_client = &client[proc][clnt];
+ for (i = 0; i < APR_SVC_MAX; i++) {
+ mutex_lock(&apr_client->svc[i].m_lock);
+ if (apr_client->svc[i].fn) {
+ apr_client->svc[i].need_reset = 0x1;
+ apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
+ }
+ if (apr_client->svc[i].port_cnt) {
+ svc = &(apr_client->svc[i]);
+ svc->need_reset = 0x1;
+ for (j = 0; j < APR_MAX_PORTS; j++)
+ if (svc->port_fn[j])
+ svc->port_fn[j](&data,
+ svc->port_priv[j]);
+ }
+ mutex_unlock(&apr_client->svc[i].m_lock);
+ }
+}
+
+static int __init apr_init(void)
+{
+ int i, j, k;
+
+ for (i = 0; i < APR_DEST_MAX; i++)
+ for (j = 0; j < APR_CLIENT_MAX; j++) {
+ mutex_init(&client[i][j].m_lock);
+ for (k = 0; k < APR_SVC_MAX; k++) {
+ mutex_init(&client[i][j].svc[k].m_lock);
+ spin_lock_init(&client[i][j].svc[k].w_lock);
+ }
+ }
+ apr_set_subsys_state();
+ mutex_init(&q6.lock);
+ dsp_debug_register(adsp_state);
+ apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
+ if (!apr_reset_workqueue)
+ return -ENOMEM;
+ return 0;
+}
+device_initcall(apr_init);
+
+static int __init apr_late_init(void)
+{
+ int ret = 0;
+ init_waitqueue_head(&dsp_wait);
+ init_waitqueue_head(&modem_wait);
+ return ret;
+}
+late_initcall(apr_late_init);
diff --git a/sound/soc/qcom/qdsp6/core/apr_tal.c b/sound/soc/qcom/qdsp6/core/apr_tal.c
new file mode 100644
index 0000000000000..cca94c69554f7
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/apr_tal.c
@@ -0,0 +1,177 @@
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <sound/qdsp6v2/apr_tal.h>
+#include <linux/soc/qcom/smd.h>
+#include <linux/io.h>
+
+struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
+
+int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len)
+{
+ int ret;
+ ret = qcom_smd_send(apr_ch->ch, data, len);
+ if (ret) {
+ pr_err("apr_tal: Error in write %d\n", ret);
+ return ret;;
+ }
+ return len;
+}
+
+struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest,
+ uint32_t dl, apr_svc_cb_fn func, void *priv)
+{
+ int rc;
+ pr_err("apr_tal:open\n");
+ if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) ||
+ (dl >= APR_DL_MAX)) {
+ pr_err("apr_tal: Invalid params\n");
+ return NULL;
+ }
+
+ if (apr_svc_ch[dl][dest][svc].ch) {
+ pr_err("apr_tal: This channel alreday openend\n");
+ return NULL;
+ }
+
+ if (!apr_svc_ch[dl][dest][svc].dest_state) {
+ rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest,
+ apr_svc_ch[dl][dest][svc].dest_state,
+ msecs_to_jiffies(APR_OPEN_TIMEOUT_MS));
+ if (rc == 0) {
+ pr_err("apr_tal:open timeout\n");
+ return NULL;
+ }
+ pr_info("apr_tal:Wakeup done\n");
+ apr_svc_ch[dl][dest][svc].dest_state = 0;
+ }
+
+ rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait,
+ (apr_svc_ch[dl][dest][svc].ch->state == SMD_CHANNEL_OPENED), 5 * HZ);
+ if (rc == 0) {
+ pr_err("apr_tal:TIMEOUT for OPEN event\n");
+ apr_tal_close(&apr_svc_ch[dl][dest][svc]);
+ return NULL;
+ }
+ if (!apr_svc_ch[dl][dest][svc].dest_state) {
+ apr_svc_ch[dl][dest][svc].dest_state = 1;
+ pr_info("apr_tal:Waiting for apr svc init\n");
+ msleep(200);
+ pr_info("apr_tal:apr svc init done\n");
+ }
+ apr_svc_ch[dl][dest][svc].func = func;
+ apr_svc_ch[dl][dest][svc].priv = priv;
+
+
+ return &apr_svc_ch[dl][dest][svc];
+}
+
+int apr_tal_close(struct apr_svc_ch_dev *apr_ch)
+{
+ if (!apr_ch->ch)
+ return -EINVAL;
+
+ apr_ch->ch = NULL;
+ apr_ch->func = NULL;
+ apr_ch->priv = NULL;
+ return 0;
+}
+
+
+static int qcom_smd_q6_callback(struct qcom_smd_device *sdev,
+ const void *data,
+ size_t count)
+{
+ struct apr_svc_ch_dev *apr_ch = dev_get_drvdata(&sdev->dev);
+
+ memcpy_fromio(apr_ch->data, data, count);
+
+ if (apr_ch->func)
+ apr_ch->func(apr_ch->data, count, apr_ch->priv);
+
+ return 0;
+}
+
+static int qcom_smd_q6_probe(struct qcom_smd_device *sdev)
+{
+ int dest = APR_DEST_QDSP6;
+ int clnt = APR_CLIENT_AUDIO;
+
+ apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO].ch = sdev->channel;
+
+ pr_info("apr_tal:Q6 Is Up\n");
+ apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1;
+ wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest);
+
+ dev_set_drvdata(&sdev->dev, &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]);
+
+ return 0;
+}
+
+static void qcom_smd_q6_remove(struct qcom_smd_device *sdev)
+{
+}
+
+
+static const struct of_device_id qcom_smd_q6_of_match[] = {
+ { .compatible = "qcom,apr" },
+ {}
+};
+
+static struct qcom_smd_driver qcom_smd_q6_driver = {
+ .probe = qcom_smd_q6_probe,
+ .remove = qcom_smd_q6_remove,
+ .callback = qcom_smd_q6_callback,
+ .driver = {
+ .name = "qcom_smd_q6",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_smd_q6_of_match,
+ },
+};
+
+static void __exit qcom_smd_q6_exit(void)
+{
+ qcom_smd_driver_unregister(&qcom_smd_q6_driver);
+}
+module_exit(qcom_smd_q6_exit);
+
+static int __init apr_tal_init(void)
+{
+
+ int i, j, k;
+
+ for (i = 0; i < APR_DL_MAX; i++)
+ for (j = 0; j < APR_DEST_MAX; j++)
+ for (k = 0; k < APR_CLIENT_MAX; k++) {
+ init_waitqueue_head(&apr_svc_ch[i][j][k].wait);
+ init_waitqueue_head(&apr_svc_ch[i][j][k].dest);
+ }
+ qcom_smd_driver_register(&qcom_smd_q6_driver);
+ return 0;
+}
+device_initcall(apr_tal_init);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("Qualcomm SMD backed apr driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/core/apr_v1.c b/sound/soc/qcom/qdsp6/core/apr_v1.c
new file mode 100644
index 0000000000000..1547c0c9a5e97
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/apr_v1.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/err.h>
+#include <sound/qdsp6v2/apr.h>
+#include <sound/qdsp6v2/apr_tal.h>
+
+struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
+ uint32_t src_port, void *priv)
+{
+ struct apr_client *client;
+ int client_id = 0;
+ int svc_idx = 0;
+ int svc_id = 0;
+ int dest_id = 0;
+ int temp_port = 0;
+ struct apr_svc *svc = NULL;
+ int rc = 0;
+
+ if (!dest || !svc_name || !svc_fn)
+ return NULL;
+
+ if (!strncmp(dest, "ADSP", 4))
+ dest_id = APR_DEST_QDSP6;
+ else if (!strncmp(dest, "MODEM", 5)) {
+ dest_id = APR_DEST_MODEM;
+ } else {
+ pr_err("APR: wrong destination\n");
+ goto done;
+ }
+
+ if (dest_id == APR_DEST_QDSP6 &&
+ apr_get_q6_state() == APR_SUBSYS_DOWN) {
+ pr_info("%s: Wait for Lpass to bootup\n", __func__);
+ rc = apr_wait_for_device_up(dest_id);
+ if (rc == 0) {
+ pr_err("%s: DSP is not Up\n", __func__);
+ return NULL;
+ }
+ pr_info("%s: Lpass Up\n", __func__);
+ } else if (dest_id == APR_DEST_MODEM &&
+ (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
+ pr_info("%s: Wait for modem to bootup\n", __func__);
+ rc = apr_wait_for_device_up(dest_id);
+ if (rc == 0) {
+ pr_err("%s: Modem is not Up\n", __func__);
+ return NULL;
+ }
+ pr_info("%s: modem Up\n", __func__);
+ }
+
+ if (apr_get_svc(svc_name, dest_id, &client_id, &svc_idx, &svc_id)) {
+ pr_err("%s: apr_get_svc failed\n", __func__);
+ goto done;
+ }
+
+ /* APRv1 loads ADSP image automatically */
+ apr_load_adsp_image();
+
+ client = apr_get_client(dest_id, client_id);
+ mutex_lock(&client->m_lock);
+ if (!client->handle) {
+ client->handle = apr_tal_open(client_id, dest_id, APR_DL_SMD,
+ apr_cb_func, NULL);
+ if (!client->handle) {
+ svc = NULL;
+ pr_err("APR: Unable to open handle\n");
+ mutex_unlock(&client->m_lock);
+ goto done;
+ }
+ }
+ mutex_unlock(&client->m_lock);
+ svc = &client->svc[svc_idx];
+ mutex_lock(&svc->m_lock);
+ client->id = client_id;
+ if (svc->need_reset) {
+ mutex_unlock(&svc->m_lock);
+ pr_err("APR: Service needs reset\n");
+ goto done;
+ }
+ svc->priv = priv;
+ svc->id = svc_id;
+ svc->dest_id = dest_id;
+ svc->client_id = client_id;
+ if (src_port != 0xFFFFFFFF) {
+ temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
+ pr_debug("port = %d t_port = %d\n", src_port, temp_port);
+ if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
+ pr_err("APR: temp_port out of bounds\n");
+ mutex_unlock(&svc->m_lock);
+ return NULL;
+ }
+ if (!svc->port_cnt && !svc->svc_cnt)
+ client->svc_cnt++;
+ svc->port_cnt++;
+ svc->port_fn[temp_port] = svc_fn;
+ svc->port_priv[temp_port] = priv;
+ } else {
+ if (!svc->fn) {
+ if (!svc->port_cnt && !svc->svc_cnt)
+ client->svc_cnt++;
+ svc->fn = svc_fn;
+ if (svc->port_cnt)
+ svc->svc_cnt++;
+ }
+ }
+
+ mutex_unlock(&svc->m_lock);
+done:
+ return svc;
+}
+
+void apr_set_subsys_state(void)
+{
+ apr_set_q6_state(APR_SUBSYS_UP);
+ apr_set_modem_state(APR_SUBSYS_UP);
+}
diff --git a/sound/soc/qcom/qdsp6/core/audio_acdb.c b/sound/soc/qcom/qdsp6/core/audio_acdb.c
new file mode 100644
index 0000000000000..88ca64977a1ab
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/audio_acdb.c
@@ -0,0 +1,865 @@
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+#include <sound/qdsp6v2/audio_acdb.h>
+
+
+#define MAX_NETWORKS 15
+
+struct sidetone_atomic_cal {
+ atomic_t enable;
+ atomic_t gain;
+};
+
+
+struct acdb_data {
+ struct mutex acdb_mutex;
+
+ /* ANC Cal */
+ struct acdb_atomic_cal_block anc_cal;
+
+ /* AudProc Cal */
+ atomic_t asm_topology;
+ atomic_t adm_topology[MAX_AUDPROC_TYPES];
+ struct acdb_atomic_cal_block audproc_cal[MAX_AUDPROC_TYPES];
+ struct acdb_atomic_cal_block audstrm_cal[MAX_AUDPROC_TYPES];
+ struct acdb_atomic_cal_block audvol_cal[MAX_AUDPROC_TYPES];
+
+ /* VocProc Cal */
+ atomic_t voice_rx_topology;
+ atomic_t voice_tx_topology;
+ struct acdb_atomic_cal_block vocproc_cal[MAX_NETWORKS];
+ struct acdb_atomic_cal_block vocstrm_cal[MAX_NETWORKS];
+ struct acdb_atomic_cal_block vocvol_cal[MAX_NETWORKS];
+ /* size of cal block tables above*/
+ atomic_t vocproc_cal_size;
+ atomic_t vocstrm_cal_size;
+ atomic_t vocvol_cal_size;
+ /* Total size of cal data for all networks */
+ atomic_t vocproc_total_cal_size;
+ atomic_t vocstrm_total_cal_size;
+ atomic_t vocvol_total_cal_size;
+
+ /* AFE cal */
+ struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES];
+
+ /* Sidetone Cal */
+ struct sidetone_atomic_cal sidetone_cal;
+
+
+ /* Allocation information */
+// struct ion_client *ion_client;
+// struct ion_handle *ion_handle;
+ atomic_t map_handle;
+ atomic64_t paddr;
+ atomic64_t kvaddr;
+ atomic64_t mem_len;
+};
+
+static struct acdb_data acdb_data;
+static atomic_t usage_count;
+
+uint32_t get_voice_rx_topology(void)
+{
+ return atomic_read(&acdb_data.voice_rx_topology);
+}
+
+void store_voice_rx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.voice_rx_topology, topology);
+}
+
+uint32_t get_voice_tx_topology(void)
+{
+ return atomic_read(&acdb_data.voice_tx_topology);
+}
+
+void store_voice_tx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.voice_tx_topology, topology);
+}
+
+uint32_t get_adm_rx_topology(void)
+{
+ return atomic_read(&acdb_data.adm_topology[RX_CAL]);
+}
+
+void store_adm_rx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.adm_topology[RX_CAL], topology);
+}
+
+uint32_t get_adm_tx_topology(void)
+{
+ return atomic_read(&acdb_data.adm_topology[TX_CAL]);
+}
+
+void store_adm_tx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.adm_topology[TX_CAL], topology);
+}
+
+uint32_t get_asm_topology(void)
+{
+ return atomic_read(&acdb_data.asm_topology);
+}
+
+void store_asm_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.asm_topology, topology);
+}
+
+void get_all_voice_cal(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_total_cal_size) +
+ atomic_read(&acdb_data.vocstrm_total_cal_size) +
+ atomic_read(&acdb_data.vocvol_total_cal_size);
+}
+
+void get_all_cvp_cal(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_total_cal_size) +
+ atomic_read(&acdb_data.vocvol_total_cal_size);
+}
+
+void get_all_vocproc_cal(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_total_cal_size);
+}
+
+void get_all_vocstrm_cal(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocstrm_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocstrm_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocstrm_total_cal_size);
+}
+
+void get_all_vocvol_cal(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocvol_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocvol_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocvol_total_cal_size);
+}
+
+void get_anc_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.anc_cal.cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.anc_cal.cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.anc_cal.cal_size);
+done:
+ return;
+}
+
+void store_anc_cal(struct cal_block *cal_block)
+{
+ pr_debug("%s,\n", __func__);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+
+ atomic_set(&acdb_data.anc_cal.cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+ atomic_set(&acdb_data.anc_cal.cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.anc_cal.cal_size,
+ cal_block->cal_size);
+done:
+ return;
+}
+
+void store_afe_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.afe_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+ atomic_set(&acdb_data.afe_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.afe_cal[path].cal_size,
+ cal_block->cal_size);
+done:
+ return;
+}
+
+void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.afe_cal[path].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.afe_cal[path].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.afe_cal[path].cal_size);
+done:
+ return;
+}
+
+void store_audproc_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.audproc_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+ atomic_set(&acdb_data.audproc_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.audproc_cal[path].cal_size,
+ cal_block->cal_size);
+done:
+ return;
+}
+
+void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.audproc_cal[path].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.audproc_cal[path].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.audproc_cal[path].cal_size);
+done:
+ return;
+}
+
+void store_audstrm_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.audstrm_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+ atomic_set(&acdb_data.audstrm_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.audstrm_cal[path].cal_size,
+ cal_block->cal_size);
+done:
+ return;
+}
+
+void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.audstrm_cal[path].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.audstrm_cal[path].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.audstrm_cal[path].cal_size);
+done:
+ return;
+}
+
+void store_audvol_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.audvol_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+ atomic_set(&acdb_data.audvol_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.audvol_cal[path].cal_size,
+ cal_block->cal_size);
+done:
+ return;
+}
+
+void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES || path < 0) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.audvol_cal[path].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.audvol_cal[path].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.audvol_cal[path].cal_size);
+done:
+ return;
+}
+
+
+void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (len > MAX_NETWORKS) {
+ pr_err("%s: Calibration sent for %d networks, only %d are "
+ "supported!\n", __func__, len, MAX_NETWORKS);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocproc_total_cal_size, 0);
+ for (i = 0; i < len; i++) {
+ if (cal_blocks[i].cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_blocks[i].cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocproc_cal[i].cal_size, 0);
+ } else {
+ atomic_add(cal_blocks[i].cal_size,
+ &acdb_data.vocproc_total_cal_size);
+ atomic_set(&acdb_data.vocproc_cal[i].cal_size,
+ cal_blocks[i].cal_size);
+ atomic_set(&acdb_data.vocproc_cal[i].cal_paddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocproc_cal[i].cal_kvaddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+ }
+ }
+ atomic_set(&acdb_data.vocproc_cal_size, len);
+done:
+ return;
+}
+
+void get_vocproc_cal(struct acdb_cal_data *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_data == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_data->num_cal_blocks = atomic_read(&acdb_data.vocproc_cal_size);
+ cal_data->cal_blocks = &acdb_data.vocproc_cal[0];
+done:
+ return;
+}
+
+void store_vocstrm_cal(int32_t len, struct cal_block *cal_blocks)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (len > MAX_NETWORKS) {
+ pr_err("%s: Calibration sent for %d networks, only %d are "
+ "supported!\n", __func__, len, MAX_NETWORKS);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocstrm_total_cal_size, 0);
+ for (i = 0; i < len; i++) {
+ if (cal_blocks[i].cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_blocks[i].cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocstrm_cal[i].cal_size, 0);
+ } else {
+ atomic_add(cal_blocks[i].cal_size,
+ &acdb_data.vocstrm_total_cal_size);
+ atomic_set(&acdb_data.vocstrm_cal[i].cal_size,
+ cal_blocks[i].cal_size);
+ atomic_set(&acdb_data.vocstrm_cal[i].cal_paddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocstrm_cal[i].cal_kvaddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+ }
+ }
+ atomic_set(&acdb_data.vocstrm_cal_size, len);
+done:
+ return;
+}
+
+void get_vocstrm_cal(struct acdb_cal_data *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_data == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_data->num_cal_blocks = atomic_read(&acdb_data.vocstrm_cal_size);
+ cal_data->cal_blocks = &acdb_data.vocstrm_cal[0];
+done:
+ return;
+}
+
+void store_vocvol_cal(int32_t len, struct cal_block *cal_blocks)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (len > MAX_NETWORKS) {
+ pr_err("%s: Calibration sent for %d networks, only %d are "
+ "supported!\n", __func__, len, MAX_NETWORKS);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocvol_total_cal_size, 0);
+ for (i = 0; i < len; i++) {
+ if (cal_blocks[i].cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_blocks[i].cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocvol_cal[i].cal_size, 0);
+ } else {
+ atomic_add(cal_blocks[i].cal_size,
+ &acdb_data.vocvol_total_cal_size);
+ atomic_set(&acdb_data.vocvol_cal[i].cal_size,
+ cal_blocks[i].cal_size);
+ atomic_set(&acdb_data.vocvol_cal[i].cal_paddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocvol_cal[i].cal_kvaddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+ }
+ }
+ atomic_set(&acdb_data.vocvol_cal_size, len);
+done:
+ return;
+}
+
+void get_vocvol_cal(struct acdb_cal_data *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_data == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_data->num_cal_blocks = atomic_read(&acdb_data.vocvol_cal_size);
+ cal_data->cal_blocks = &acdb_data.vocvol_cal[0];
+done:
+ return;
+}
+
+void store_sidetone_cal(struct sidetone_cal *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ atomic_set(&acdb_data.sidetone_cal.enable, cal_data->enable);
+ atomic_set(&acdb_data.sidetone_cal.gain, cal_data->gain);
+}
+
+
+void get_sidetone_cal(struct sidetone_cal *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_data == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_data->enable = atomic_read(&acdb_data.sidetone_cal.enable);
+ cal_data->gain = atomic_read(&acdb_data.sidetone_cal.gain);
+done:
+ return;
+}
+
+static int acdb_open(struct inode *inode, struct file *f)
+{
+ s32 result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (atomic64_read(&acdb_data.mem_len)) {
+ pr_debug("%s: ACDB opened but memory allocated, "
+ "using existing allocation!\n",
+ __func__);
+ }
+
+ atomic_inc(&usage_count);
+ return result;
+}
+
+static long acdb_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ int32_t result = 0;
+ int32_t size;
+ int32_t map_fd;
+ uint32_t topology;
+ struct cal_block data[MAX_NETWORKS];
+ pr_debug("%s\n", __func__);
+
+ switch (cmd) {
+
+ case AUDIO_REGISTER_PMEM:
+ pr_debug("AUDIO_REGISTER_PMEM\n");
+ if (atomic_read(&acdb_data.mem_len)) {
+ //deregister_memory();
+ pr_debug("Remove the existing memory\n");
+ }
+
+ if (copy_from_user(&map_fd, (void *)arg, sizeof(map_fd))) {
+ pr_err("%s: fail to copy memory handle!\n", __func__);
+ result = -EFAULT;
+ } else {
+ atomic_set(&acdb_data.map_handle, map_fd);
+ //result = register_memory();
+ }
+ goto done;
+
+ case AUDIO_DEREGISTER_PMEM:
+ pr_debug("AUDIO_DEREGISTER_PMEM\n");
+ //deregister_memory();
+ goto done;
+ case AUDIO_SET_VOICE_RX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_voice_rx_topology(topology);
+ goto done;
+ case AUDIO_SET_VOICE_TX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_voice_tx_topology(topology);
+ goto done;
+ case AUDIO_SET_ADM_RX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_adm_rx_topology(topology);
+ goto done;
+ case AUDIO_SET_ADM_TX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_adm_tx_topology(topology);
+ goto done;
+ case AUDIO_SET_ASM_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_asm_topology(topology);
+ goto done;
+ }
+
+ if (copy_from_user(&size, (void *) arg, sizeof(size))) {
+
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (size <= 0) {
+ pr_err("%s: Invalid size sent to driver: %d\n",
+ __func__, size);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) {
+
+ pr_err("%s: fail to copy table size %d\n", __func__, size);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (data == NULL) {
+ pr_err("%s: NULL pointer sent to driver!\n", __func__);
+ result = -EFAULT;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_SET_AUDPROC_TX_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More Audproc Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_audproc_cal(TX_CAL, data);
+ break;
+ case AUDIO_SET_AUDPROC_RX_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More Audproc Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_audproc_cal(RX_CAL, data);
+ break;
+ case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More Audproc Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_audstrm_cal(TX_CAL, data);
+ break;
+ case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More Audproc Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_audstrm_cal(RX_CAL, data);
+ break;
+ case AUDIO_SET_AUDPROC_TX_VOL_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More Audproc Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_audvol_cal(TX_CAL, data);
+ break;
+ case AUDIO_SET_AUDPROC_RX_VOL_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More Audproc Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_audvol_cal(RX_CAL, data);
+ break;
+ case AUDIO_SET_AFE_TX_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More AFE Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_afe_cal(TX_CAL, data);
+ break;
+ case AUDIO_SET_AFE_RX_CAL:
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More AFE Cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_afe_cal(RX_CAL, data);
+ break;
+ case AUDIO_SET_VOCPROC_CAL:
+ store_vocproc_cal(size / sizeof(struct cal_block), data);
+ break;
+ case AUDIO_SET_VOCPROC_STREAM_CAL:
+ store_vocstrm_cal(size / sizeof(struct cal_block), data);
+ break;
+ case AUDIO_SET_VOCPROC_VOL_CAL:
+ store_vocvol_cal(size / sizeof(struct cal_block), data);
+ break;
+ case AUDIO_SET_SIDETONE_CAL:
+ if (size > sizeof(struct sidetone_cal))
+ pr_err("%s: More sidetone cal then expected, "
+ "size received: %d\n", __func__, size);
+ store_sidetone_cal((struct sidetone_cal *)data);
+ break;
+ case AUDIO_SET_ANC_CAL:
+ store_anc_cal(data);
+ break;
+ default:
+ pr_err("ACDB=> ACDB ioctl not found!\n");
+ }
+
+done:
+ return result;
+}
+
+static int acdb_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int result = 0;
+ int size = vma->vm_end - vma->vm_start;
+
+ pr_debug("%s\n", __func__);
+
+ if (atomic64_read(&acdb_data.mem_len)) {
+ if (size <= atomic64_read(&acdb_data.mem_len)) {
+ vma->vm_page_prot = pgprot_noncached(
+ vma->vm_page_prot);
+ result = remap_pfn_range(vma,
+ vma->vm_start,
+ atomic64_read(&acdb_data.paddr) >> PAGE_SHIFT,
+ size,
+ vma->vm_page_prot);
+ } else {
+ pr_err("%s: Not enough memory!\n", __func__);
+ result = -ENOMEM;
+ }
+ } else {
+ pr_err("%s: memory is not allocated, yet!\n", __func__);
+ result = -ENODEV;
+ }
+
+ return result;
+}
+
+static int acdb_release(struct inode *inode, struct file *f)
+{
+ s32 result = 0;
+
+ atomic_dec(&usage_count);
+ atomic_read(&usage_count);
+
+ pr_debug("%s: ref count %d!\n", __func__,
+ atomic_read(&usage_count));
+
+ if (atomic_read(&usage_count) >= 1)
+ result = -EBUSY;
+
+ return result;
+}
+
+static const struct file_operations acdb_fops = {
+ .owner = THIS_MODULE,
+ .open = acdb_open,
+ .release = acdb_release,
+ .unlocked_ioctl = acdb_ioctl,
+ .mmap = acdb_mmap,
+};
+
+struct miscdevice acdb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_acdb",
+ .fops = &acdb_fops,
+};
+
+static int __init acdb_init(void)
+{
+ memset(&acdb_data, 0, sizeof(acdb_data));
+ mutex_init(&acdb_data.acdb_mutex);
+ atomic_set(&usage_count, 0);
+ return misc_register(&acdb_misc);
+}
+
+static void __exit acdb_exit(void)
+{
+}
+
+module_init(acdb_init);
+module_exit(acdb_exit);
+
+MODULE_DESCRIPTION("MSM 8x60 Audio ACDB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/core/dsp_debug.c b/sound/soc/qcom/qdsp6/core/dsp_debug.c
new file mode 100644
index 0000000000000..4e242e395b0c5
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/dsp_debug.c
@@ -0,0 +1,259 @@
+/* arch/arm/mach-msm/qdsp6/dsp_dump.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * 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/io.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <asm/atomic.h>
+
+#include <sound/qdsp6v2/dsp_debug.h>
+
+static wait_queue_head_t dsp_wait;
+static int dsp_has_crashed;
+static int dsp_wait_count;
+
+static atomic_t dsp_crash_count = ATOMIC_INIT(0);
+dsp_state_cb cb_ptr;
+
+void q6audio_dsp_not_responding(void)
+{
+ int i;
+
+ if (cb_ptr)
+ cb_ptr(DSP_STATE_CRASHED);
+ if (atomic_add_return(1, &dsp_crash_count) != 1) {
+ pr_err("q6audio_dsp_not_responding() \
+ - parking additional crasher...\n");
+ for (i = 0; i < 600; i++)
+ msleep(1000);
+ }
+ if (dsp_wait_count) {
+ dsp_has_crashed = 1;
+ wake_up(&dsp_wait);
+
+ while (dsp_has_crashed != 2)
+ wait_event(dsp_wait, dsp_has_crashed == 2);
+ } else {
+ pr_err("q6audio_dsp_not_responding() - no waiter?\n");
+ }
+ if (cb_ptr)
+ cb_ptr(DSP_STATE_CRASH_DUMP_DONE);
+}
+
+static int dsp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+#define DSP_NMI_ADDR 0x28800010
+
+static ssize_t dsp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+ void __iomem *ptr;
+ void *mem_buffer;
+
+ if (count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ if (!strcmp(cmd, "wait-for-crash")) {
+ while (!dsp_has_crashed) {
+ int res;
+ dsp_wait_count++;
+ res = wait_event_interruptible(dsp_wait,
+ dsp_has_crashed);
+ if (res < 0) {
+ dsp_wait_count--;
+ return res;
+ }
+ }
+ /* assert DSP NMI */
+ mem_buffer = ioremap(DSP_NMI_ADDR, 0x16);
+ if (IS_ERR((void *)mem_buffer)) {
+ pr_err("%s:map_buffer failed, error = %ld\n", __func__,
+ PTR_ERR((void *)mem_buffer));
+ return -ENOMEM;
+ }
+ ptr = mem_buffer;
+ if (!ptr) {
+ pr_err("Unable to map DSP NMI\n");
+ return -EFAULT;
+ }
+ writel(0x1, (void *)ptr);
+ iounmap(mem_buffer);
+ } else if (!strcmp(cmd, "boom")) {
+ q6audio_dsp_not_responding();
+ } else if (!strcmp(cmd, "continue-crash")) {
+ dsp_has_crashed = 2;
+ wake_up(&dsp_wait);
+ } else {
+ pr_err("[%s:%s] unknown dsp_debug command: %s\n", __FILE__,
+ __func__, cmd);
+ }
+
+ return count;
+}
+
+static unsigned copy_ok_count;
+static uint32_t dsp_ram_size;
+static uint32_t dsp_ram_base;
+
+static ssize_t dsp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ size_t actual = 0;
+ size_t mapsize = PAGE_SIZE;
+ unsigned addr;
+ void __iomem *ptr;
+ void *mem_buffer;
+
+ if ((dsp_ram_base == 0) || (dsp_ram_size == 0)) {
+ pr_err("[%s:%s] Memory Invalid or not initialized, Base = 0x%x,"
+ " size = 0x%x\n", __FILE__,
+ __func__, dsp_ram_base, dsp_ram_size);
+ return -EINVAL;
+ }
+
+ if (*pos >= dsp_ram_size)
+ return 0;
+
+ if (*pos & (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ addr = (*pos + dsp_ram_base);
+
+ /* don't blow up if we're unaligned */
+ if (addr & (PAGE_SIZE - 1))
+ mapsize *= 2;
+
+ while (count >= PAGE_SIZE) {
+ mem_buffer = ioremap(addr, mapsize);
+ if (IS_ERR((void *)mem_buffer)) {
+ pr_err("%s:map_buffer failed, error = %ld\n",
+ __func__, PTR_ERR((void *)mem_buffer));
+ return -ENOMEM;
+ }
+ ptr = mem_buffer;
+ if (!ptr) {
+ pr_err("[%s:%s] map error @ %x\n", __FILE__,
+ __func__, addr);
+ return -EFAULT;
+ }
+ if (copy_to_user(buf, ptr, PAGE_SIZE)) {
+ iounmap(mem_buffer);
+ pr_err("[%s:%s] copy error @ %p\n", __FILE__,
+ __func__, buf);
+ return -EFAULT;
+ }
+ copy_ok_count += PAGE_SIZE;
+ iounmap(mem_buffer);
+ addr += PAGE_SIZE;
+ buf += PAGE_SIZE;
+ actual += PAGE_SIZE;
+ count -= PAGE_SIZE;
+ }
+
+ *pos += actual;
+ return actual;
+}
+
+static int dsp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int dsp_debug_register(dsp_state_cb ptr)
+{
+ if (ptr == NULL)
+ return -EINVAL;
+ cb_ptr = ptr;
+
+ return 0;
+}
+
+static int dspcrashd_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *res;
+ int *pdata;
+
+ pdata = pdev->dev.platform_data;
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+ "msm_dspcrashd");
+ if (!res) {
+ pr_err("%s: failed to get resources for dspcrashd\n", __func__);
+ return -ENODEV;
+ }
+
+ dsp_ram_base = res->start;
+ dsp_ram_size = res->end - res->start;
+ pr_info("%s: Platform driver values: Base = 0x%x, Size = 0x%x,"
+ "pdata = 0x%x\n", __func__,
+ dsp_ram_base, dsp_ram_size, *pdata);
+ return rc;
+}
+
+static const struct file_operations dsp_fops = {
+ .owner = THIS_MODULE,
+ .open = dsp_open,
+ .read = dsp_read,
+ .write = dsp_write,
+ .release = dsp_release,
+};
+
+static struct miscdevice dsp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "dsp_debug",
+ .fops = &dsp_fops,
+};
+
+static struct platform_driver dspcrashd_driver = {
+ .probe = dspcrashd_probe,
+ .driver = { .name = "msm_dspcrashd"}
+};
+
+static int __init dsp_init(void)
+{
+ int rc = 0;
+ init_waitqueue_head(&dsp_wait);
+ rc = platform_driver_register(&dspcrashd_driver);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: platform_driver_register for dspcrashd failed\n",
+ __func__);
+ }
+ return misc_register(&dsp_misc);
+}
+
+static int __exit dsp_exit(void)
+{
+ platform_driver_unregister(&dspcrashd_driver);
+ return 0;
+}
+
+device_initcall(dsp_init);
diff --git a/sound/soc/qcom/qdsp6/core/q6audio_common.h b/sound/soc/qcom/qdsp6/core/q6audio_common.h
new file mode 100644
index 0000000000000..68efafd650368
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/q6audio_common.h
@@ -0,0 +1,35 @@
+/* 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.
+ *
+*/
+
+/* For Decoders */
+#ifndef __Q6_AUDIO_COMMON_H__
+#define __Q6_AUDIO_COMMON_H__
+
+#include <sound/apr_audio.h>
+#include <sound/q6asm.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/sound/soc/qcom/qdsp6/core/q6core.c b/sound/soc/qcom/qdsp6/core/q6core.c
new file mode 100644
index 0000000000000..e5f1d4ed7a4ce
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/q6core.c
@@ -0,0 +1,406 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <sound/qdsp6v2/apr.h>
+#include "q6core.h"
+
+#define TIMEOUT_MS 1000
+
+static struct apr_svc *apr_handle_q;
+static struct apr_svc *apr_handle_m;
+static struct apr_svc *core_handle_q;
+
+static int32_t query_adsp_ver;
+static wait_queue_head_t adsp_version_wait;
+static uint32_t adsp_version;
+
+static wait_queue_head_t bus_bw_req_wait;
+static u32 bus_bw_resp_received;
+
+static struct dentry *dentry;
+static char l_buf[4096];
+
+static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
+{
+ struct adsp_get_version *payload;
+ uint32_t *payload1;
+ struct adsp_service_info *svc_info;
+ int i;
+
+ pr_info("core msg: payload len = %u, apr resp opcode = 0x%X\n",
+ data->payload_size, data->opcode);
+
+ switch (data->opcode) {
+
+ case APR_BASIC_RSP_RESULT:{
+
+ if (data->payload_size == 0) {
+ pr_err("%s: APR_BASIC_RSP_RESULT No Payload ",
+ __func__);
+ return 0;
+ }
+
+ payload1 = data->payload;
+
+ switch (payload1[0]) {
+
+ case ADSP_CMD_SET_POWER_COLLAPSE_STATE:
+ pr_info("Cmd = ADSP_CMD_SET_POWER_COLLAPSE_STATE"
+ " status[0x%x]\n", payload1[1]);
+ break;
+ case ADSP_CMD_REMOTE_BUS_BW_REQUEST:
+ pr_info("%s: cmd = ADSP_CMD_REMOTE_BUS_BW_REQUEST"
+ " status = 0x%x\n", __func__, payload1[1]);
+
+ bus_bw_resp_received = 1;
+ wake_up(&bus_bw_req_wait);
+ break;
+ default:
+ pr_err("Invalid cmd rsp[0x%x][0x%x]\n",
+ payload1[0], payload1[1]);
+ break;
+ }
+ break;
+ }
+ case ADSP_GET_VERSION_RSP:{
+ if (data->payload_size) {
+ payload = data->payload;
+ if (query_adsp_ver == 1) {
+ query_adsp_ver = 0;
+ adsp_version = payload->build_id;
+ wake_up(&adsp_version_wait);
+ }
+ svc_info = (struct adsp_service_info *)
+ ((char *)payload + sizeof(struct adsp_get_version));
+ pr_info("----------------------------------------\n");
+ pr_info("Build id = %x\n", payload->build_id);
+ pr_info("Number of services= %x\n", payload->svc_cnt);
+ pr_info("----------------------------------------\n");
+ for (i = 0; i < payload->svc_cnt; i++) {
+ pr_info("svc-id[%d]\tver[%x.%x]\n",
+ svc_info[i].svc_id,
+ (svc_info[i].svc_ver & 0xFFFF0000)
+ >> 16,
+ (svc_info[i].svc_ver & 0xFFFF));
+ }
+ pr_info("-----------------------------------------\n");
+ } else
+ pr_info("zero payload for ADSP_GET_VERSION_RSP\n");
+ break;
+ }
+ case RESET_EVENTS:{
+ pr_debug("Reset event received in Core service");
+ apr_reset(core_handle_q);
+ core_handle_q = NULL;
+ break;
+ }
+
+ default:
+ pr_err("Message id from adsp core svc: %d\n", data->opcode);
+ break;
+ }
+
+ return 0;
+}
+
+static int32_t aprv2_debug_fn_q(struct apr_client_data *data, void *priv)
+{
+ pr_debug("Q6_Payload Length = %d\n", data->payload_size);
+ if (memcmp(data->payload, l_buf + 20, data->payload_size))
+ pr_info("FAIL: %d\n", data->payload_size);
+ else
+ pr_info("SUCCESS: %d\n", data->payload_size);
+ return 0;
+}
+
+static int32_t aprv2_debug_fn_m(struct apr_client_data *data, void *priv)
+{
+ pr_info("M_Payload Length = %d\n", data->payload_size);
+ return 0;
+}
+
+static ssize_t apr_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ pr_debug("apr debugfs opened\n");
+ return 0;
+}
+
+void core_open(void)
+{
+ if (core_handle_q == NULL) {
+ core_handle_q = apr_register("ADSP", "CORE",
+ aprv2_core_fn_q, 0xFFFFFFFF, NULL);
+ }
+ pr_info("Open_q %p\n", core_handle_q);
+ if (core_handle_q == NULL) {
+ pr_err("%s: Unable to register CORE\n", __func__);
+ }
+}
+
+int core_req_bus_bandwith(u16 bus_id, u32 ab_bps, u32 ib_bps)
+{
+ struct adsp_cmd_remote_bus_bw_request bus_bw_req;
+ int ret;
+
+ pr_debug("%s: bus_id %u ab_bps %u ib_bps %u\n",
+ __func__, bus_id, ab_bps, ib_bps);
+
+ core_open();
+ if (core_handle_q == NULL) {
+ pr_info("%s: apr registration for CORE failed\n", __func__);
+ return -ENODEV;
+ }
+
+ bus_bw_req.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ bus_bw_req.hdr.pkt_size = sizeof(struct adsp_cmd_remote_bus_bw_request);
+
+ bus_bw_req.hdr.src_port = 0;
+ bus_bw_req.hdr.dest_port = 0;
+ bus_bw_req.hdr.token = 0;
+ bus_bw_req.hdr.opcode = ADSP_CMD_REMOTE_BUS_BW_REQUEST;
+
+ bus_bw_req.bus_identifier = bus_id;
+ bus_bw_req.reserved = 0;
+ bus_bw_req.ab_bps = ab_bps;
+ bus_bw_req.ib_bps = ib_bps;
+
+ bus_bw_resp_received = 0;
+ ret = apr_send_pkt(core_handle_q, (uint32_t *) &bus_bw_req);
+ if (ret < 0) {
+ pr_err("%s: CORE bus bw request failed\n", __func__);
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(bus_bw_req_wait, (bus_bw_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -ETIME;
+ goto fail_cmd;
+ }
+
+ return 0;
+
+fail_cmd:
+ return ret;
+}
+
+uint32_t core_get_adsp_version(void)
+{
+ struct apr_hdr *hdr;
+ int32_t rc = 0, ret = 0;
+ core_open();
+ if (core_handle_q) {
+ hdr = (struct apr_hdr *)l_buf;
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ hdr->token = 0;
+ hdr->opcode = ADSP_GET_VERSION;
+
+ apr_send_pkt(core_handle_q, (uint32_t *)l_buf);
+ query_adsp_ver = 1;
+ pr_info("Write_q\n");
+ ret = wait_event_timeout(adsp_version_wait,
+ (query_adsp_ver == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ rc = adsp_version;
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ rc = -ENODEV;
+ }
+ } else
+ pr_info("apr registration failed\n");
+ return rc;
+}
+EXPORT_SYMBOL(core_get_adsp_version);
+
+static ssize_t apr_debug_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int len;
+ static int t_len;
+
+ len = count > 63 ? 63 : count;
+ if (copy_from_user(l_buf + 20 , buf, len)) {
+ pr_info("Unable to copy data from user space\n");
+ return -EFAULT;
+ }
+ l_buf[len + 20] = 0;
+ if (l_buf[len + 20 - 1] == '\n') {
+ l_buf[len + 20 - 1] = 0;
+ len--;
+ }
+ if (!strncmp(l_buf + 20, "open_q", 64)) {
+ apr_handle_q = apr_register("ADSP", "TEST", aprv2_debug_fn_q,
+ 0xFFFFFFFF, NULL);
+ pr_info("Open_q %p\n", apr_handle_q);
+ } else if (!strncmp(l_buf + 20, "open_m", 64)) {
+ apr_handle_m = apr_register("MODEM", "TEST", aprv2_debug_fn_m,
+ 0xFFFFFFFF, NULL);
+ pr_info("Open_m %p\n", apr_handle_m);
+ } else if (!strncmp(l_buf + 20, "write_q", 64)) {
+ struct apr_hdr *hdr;
+
+ t_len++;
+ t_len = t_len % 450;
+ if (!t_len % 99)
+ msleep(2000);
+ hdr = (struct apr_hdr *)l_buf;
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ hdr->pkt_size = APR_PKT_SIZE(20, t_len);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ hdr->token = 0;
+ hdr->opcode = 0x12345678;
+ memset(l_buf + 20, 9, 4060);
+
+ apr_send_pkt(apr_handle_q, (uint32_t *)l_buf);
+ pr_debug("Write_q\n");
+ } else if (!strncmp(l_buf + 20, "write_m", 64)) {
+ struct apr_hdr *hdr;
+
+ hdr = (struct apr_hdr *)l_buf;
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ hdr->pkt_size = APR_PKT_SIZE(20, 8);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ hdr->token = 0;
+ hdr->opcode = 0x12345678;
+ memset(l_buf + 30, 9, 4060);
+
+ apr_send_pkt(apr_handle_m, (uint32_t *)l_buf);
+ pr_info("Write_m\n");
+ } else if (!strncmp(l_buf + 20, "write_q4", 64)) {
+ struct apr_hdr *hdr;
+
+ hdr = (struct apr_hdr *)l_buf;
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ hdr->pkt_size = APR_PKT_SIZE(20, 4076);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ hdr->token = 0;
+ hdr->opcode = 0x12345678;
+ memset(l_buf + 30, 9, 4060);
+
+ apr_send_pkt(apr_handle_q, (uint32_t *)l_buf);
+ pr_info("Write_q\n");
+ } else if (!strncmp(l_buf + 20, "write_m4", 64)) {
+ struct apr_hdr *hdr;
+
+ hdr = (struct apr_hdr *)l_buf;
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ hdr->pkt_size = APR_PKT_SIZE(20, 4076);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ hdr->token = 0;
+ hdr->opcode = 0x12345678;
+ memset(l_buf + 30, 9, 4060);
+
+ apr_send_pkt(apr_handle_m, (uint32_t *)l_buf);
+ pr_info("Write_m\n");
+ } else if (!strncmp(l_buf + 20, "close", 64)) {
+ if (apr_handle_q)
+ apr_deregister(apr_handle_q);
+ } else if (!strncmp(l_buf + 20, "loaded", 64)) {
+ apr_set_q6_state(APR_SUBSYS_LOADED);
+ } else if (!strncmp(l_buf + 20, "boom", 64)) {
+ q6audio_dsp_not_responding();
+ } else if (!strncmp(l_buf + 20, "dsp_ver", 64)) {
+ core_get_adsp_version();
+ } else if (!strncmp(l_buf + 20, "en_pwr_col", 64)) {
+ struct adsp_power_collapse pc;
+
+ core_open();
+ if (core_handle_q) {
+ pc.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ pc.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(uint32_t));;
+ pc.hdr.src_port = 0;
+ pc.hdr.dest_port = 0;
+ pc.hdr.token = 0;
+ pc.hdr.opcode = ADSP_CMD_SET_POWER_COLLAPSE_STATE;
+ pc.power_collapse = 0x00000000;
+ apr_send_pkt(core_handle_q, (uint32_t *)&pc);
+ pr_info("Write_q :enable power collapse\n");
+ }
+ } else if (!strncmp(l_buf + 20, "dis_pwr_col", 64)) {
+ struct adsp_power_collapse pc;
+
+ core_open();
+ if (core_handle_q) {
+ pc.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ pc.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(uint32_t));
+ pc.hdr.src_port = 0;
+ pc.hdr.dest_port = 0;
+ pc.hdr.token = 0;
+ pc.hdr.opcode = ADSP_CMD_SET_POWER_COLLAPSE_STATE;
+ pc.power_collapse = 0x00000001;
+ apr_send_pkt(core_handle_q, (uint32_t *)&pc);
+ pr_info("Write_q:disable power collapse\n");
+ }
+ } else
+ pr_info("Unknown Command\n");
+
+ return count;
+}
+
+static const struct file_operations apr_debug_fops = {
+ .write = apr_debug_write,
+ .open = apr_debug_open,
+};
+
+static int __init core_init(void)
+{
+ init_waitqueue_head(&bus_bw_req_wait);
+ bus_bw_resp_received = 0;
+
+ query_adsp_ver = 0;
+ init_waitqueue_head(&adsp_version_wait);
+ adsp_version = 0;
+
+ core_handle_q = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ dentry = debugfs_create_file("apr", S_IFREG | S_IRUGO | S_IWUSR
+ | S_IWGRP, NULL, (void *) NULL, &apr_debug_fops);
+#endif /* CONFIG_DEBUG_FS */
+
+ return 0;
+}
+
+device_initcall(core_init);
diff --git a/sound/soc/qcom/qdsp6/core/q6core.h b/sound/soc/qcom/qdsp6/core/q6core.h
new file mode 100644
index 0000000000000..97a81cbe75f1c
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/q6core.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2011, 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 __Q6CORE_H__
+#define __Q6CORE_H__
+#include <sound/qdsp6v2/apr.h>
+
+
+#define ADSP_CMD_REMOTE_BUS_BW_REQUEST 0x0001115D
+#define AUDIO_IF_BUS_ID 1
+
+struct adsp_cmd_remote_bus_bw_request {
+ struct apr_hdr hdr;
+ u16 bus_identifier;
+ u16 reserved;
+ u32 ab_bps;
+ u32 ib_bps;
+} __packed;
+
+#define ADSP_GET_VERSION 0x00011152
+#define ADSP_GET_VERSION_RSP 0x00011153
+
+struct adsp_get_version {
+ uint32_t build_id;
+ uint32_t svc_cnt;
+};
+
+struct adsp_service_info {
+ uint32_t svc_id;
+ uint32_t svc_ver;
+};
+
+#define ADSP_CMD_SET_POWER_COLLAPSE_STATE 0x0001115C
+struct adsp_power_collapse {
+ struct apr_hdr hdr;
+ uint32_t power_collapse;
+};
+
+int core_req_bus_bandwith(u16 bus_id, u32 ab_bps, u32 ib_bps);
+
+uint32_t core_get_adsp_version(void);
+
+#endif /* __Q6CORE_H__ */
diff --git a/sound/soc/qcom/qdsp6/core/rtac.c b/sound/soc/qcom/qdsp6/core/rtac.c
new file mode 100644
index 0000000000000..175568fe53a2a
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/core/rtac.c
@@ -0,0 +1,1046 @@
+/* Copyright (c) 2011, 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <asm/atomic.h>
+#include <sound/qdsp6v2/audio_acdb.h>
+#include <sound/qdsp6v2/rtac.h>
+#include "q6audio_common.h"
+#include <sound/q6afe.h>
+
+#define CONFIG_RTAC
+#ifndef CONFIG_RTAC
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id) {}
+void rtac_remove_adm_device(u32 port_id) {}
+void rtac_remove_popp_from_adm_devices(u32 popp_id) {}
+void rtac_set_adm_handle(void *handle) {}
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size)
+ {return false; }
+void rtac_set_asm_handle(u32 session_id, void *handle) {}
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+ u32 payload_size) {return false; }
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+ u32 tx_afe_port, u32 session_id) {}
+void rtac_remove_voice(u32 cvs_handle) {}
+void rtac_set_voice_handle(u32 mode, void *handle) {}
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload,
+ u32 payload_size) {return false; }
+
+#else
+
+#define VOICE_CMD_SET_PARAM 0x00011006
+#define VOICE_CMD_GET_PARAM 0x00011007
+#define VOICE_EVT_GET_PARAM_ACK 0x00011008
+
+/* Max size of payload (buf size - apr header) */
+#define MAX_PAYLOAD_SIZE 4076
+#define RTAC_MAX_ACTIVE_DEVICES 4
+#define RTAC_MAX_ACTIVE_VOICE_COMBOS 2
+#define RTAC_MAX_ACTIVE_POPP 8
+#define RTAC_BUF_SIZE 4096
+
+#define TIMEOUT_MS 1000
+
+/* APR data */
+struct rtac_apr_data {
+ void *apr_handle;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+};
+
+static struct rtac_apr_data rtac_adm_apr_data;
+static struct rtac_apr_data rtac_asm_apr_data[SESSION_MAX+1];
+static struct rtac_apr_data rtac_voice_apr_data[RTAC_VOICE_MODES];
+
+
+/* ADM info & APR */
+struct rtac_adm_data {
+ uint32_t topology_id;
+ uint32_t afe_port;
+ uint32_t copp;
+ uint32_t num_of_popp;
+ uint32_t popp[RTAC_MAX_ACTIVE_POPP];
+};
+
+struct rtac_adm {
+ uint32_t num_of_dev;
+ struct rtac_adm_data device[RTAC_MAX_ACTIVE_DEVICES];
+};
+static struct rtac_adm rtac_adm_data;
+static u32 rtac_adm_payload_size;
+static u32 rtac_adm_user_buf_size;
+static u8 *rtac_adm_buffer;
+
+
+/* ASM APR */
+static u32 rtac_asm_payload_size;
+static u32 rtac_asm_user_buf_size;
+static u8 *rtac_asm_buffer;
+
+
+/* Voice info & APR */
+struct rtac_voice_data {
+ uint32_t tx_topology_id;
+ uint32_t rx_topology_id;
+ uint32_t tx_afe_port;
+ uint32_t rx_afe_port;
+ uint16_t cvs_handle;
+ uint16_t cvp_handle;
+};
+
+struct rtac_voice {
+ uint32_t num_of_voice_combos;
+ struct rtac_voice_data voice[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+};
+
+static struct rtac_voice rtac_voice_data;
+static u32 rtac_voice_payload_size;
+static u32 rtac_voice_user_buf_size;
+static u8 *rtac_voice_buffer;
+static u32 voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+
+
+struct mutex rtac_adm_mutex;
+struct mutex rtac_adm_apr_mutex;
+struct mutex rtac_asm_apr_mutex;
+struct mutex rtac_voice_mutex;
+struct mutex rtac_voice_apr_mutex;
+
+static int rtac_open(struct inode *inode, struct file *f)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int rtac_release(struct inode *inode, struct file *f)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+/* ADM Info */
+void add_popp(u32 dev_idx, u32 port_id, u32 popp_id)
+{
+ u32 i = 0;
+
+ for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++)
+ if (rtac_adm_data.device[dev_idx].popp[i] == popp_id)
+ goto done;
+
+ if (rtac_adm_data.device[dev_idx].num_of_popp ==
+ RTAC_MAX_ACTIVE_POPP) {
+ pr_err("%s, Max POPP!\n", __func__);
+ goto done;
+ }
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp++] = popp_id;
+done:
+ return;
+}
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id)
+{
+ u32 i = 0;
+ pr_debug("%s: port_id = %d, popp_id = %d\n", __func__, port_id,
+ popp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) {
+ pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+ goto done;
+ }
+
+ /* Check if device already added */
+ if (rtac_adm_data.num_of_dev != 0) {
+ for (; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id) {
+ add_popp(i, port_id, popp_id);
+ goto done;
+ }
+ if (rtac_adm_data.device[i].num_of_popp ==
+ RTAC_MAX_ACTIVE_POPP) {
+ pr_err("%s, Max POPP!\n", __func__);
+ goto done;
+ }
+ }
+ }
+
+ /* Add device */
+ rtac_adm_data.num_of_dev++;
+
+ if (path_id == ADM_PATH_PLAYBACK)
+ rtac_adm_data.device[i].topology_id =
+ get_adm_rx_topology();
+ else
+ rtac_adm_data.device[i].topology_id =
+ get_adm_tx_topology();
+ rtac_adm_data.device[i].afe_port = port_id;
+ rtac_adm_data.device[i].copp = copp_id;
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp++] = popp_id;
+done:
+ mutex_unlock(&rtac_adm_mutex);
+ return;
+}
+
+static void shift_adm_devices(u32 dev_idx)
+{
+ for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) {
+ memcpy(&rtac_adm_data.device[dev_idx],
+ &rtac_adm_data.device[dev_idx + 1],
+ sizeof(rtac_adm_data.device[dev_idx]));
+ memset(&rtac_adm_data.device[dev_idx + 1], 0,
+ sizeof(rtac_adm_data.device[dev_idx]));
+ }
+}
+
+static void shift_popp(u32 copp_idx, u32 popp_idx)
+{
+ for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp;
+ popp_idx++) {
+ memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx],
+ &rtac_adm_data.device[copp_idx].popp[popp_idx + 1],
+ sizeof(uint32_t));
+ memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1], 0,
+ sizeof(uint32_t));
+ }
+}
+
+void rtac_remove_adm_device(u32 port_id)
+{
+ s32 i;
+ pr_debug("%s: port_id = %d\n", __func__, port_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ /* look for device */
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id) {
+ memset(&rtac_adm_data.device[i], 0,
+ sizeof(rtac_adm_data.device[i]));
+ rtac_adm_data.num_of_dev--;
+
+ if (rtac_adm_data.num_of_dev >= 1) {
+ shift_adm_devices(i);
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&rtac_adm_mutex);
+ return;
+}
+
+void rtac_remove_popp_from_adm_devices(u32 popp_id)
+{
+ s32 i, j;
+ pr_debug("%s: popp_id = %d\n", __func__, popp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) {
+ if (rtac_adm_data.device[i].popp[j] == popp_id) {
+ rtac_adm_data.device[i].popp[j] = 0;
+ rtac_adm_data.device[i].num_of_popp--;
+ shift_popp(i, j);
+ }
+ }
+ }
+
+ mutex_unlock(&rtac_adm_mutex);
+}
+
+/* Voice Info */
+static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle,
+ u32 rx_afe_port, u32 tx_afe_port,
+ u32 session_id)
+{
+ rtac_voice_data.voice[idx].tx_topology_id = get_voice_tx_topology();
+ rtac_voice_data.voice[idx].rx_topology_id = get_voice_rx_topology();
+ rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port;
+ rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port;
+ rtac_voice_data.voice[idx].cvs_handle = cvs_handle;
+ rtac_voice_data.voice[idx].cvp_handle = cvp_handle;
+
+ /* Store session ID for voice RTAC */
+ voice_session_id[idx] = session_id;
+}
+
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+ u32 tx_afe_port, u32 session_id)
+{
+ u32 i = 0;
+ pr_debug("%s\n", __func__);
+ mutex_lock(&rtac_voice_mutex);
+
+ if (rtac_voice_data.num_of_voice_combos ==
+ RTAC_MAX_ACTIVE_VOICE_COMBOS) {
+ pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+ goto done;
+ }
+
+ /* Check if device already added */
+ if (rtac_voice_data.num_of_voice_combos != 0) {
+ for (; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle ==
+ cvs_handle) {
+ set_rtac_voice_data(i, cvs_handle, cvp_handle,
+ rx_afe_port, tx_afe_port,
+ session_id);
+ goto done;
+ }
+ }
+ }
+
+ /* Add device */
+ rtac_voice_data.num_of_voice_combos++;
+ set_rtac_voice_data(i, cvs_handle, cvp_handle,
+ rx_afe_port, tx_afe_port,
+ session_id);
+done:
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+}
+
+static void shift_voice_devices(u32 idx)
+{
+ for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) {
+ memcpy(&rtac_voice_data.voice[idx],
+ &rtac_voice_data.voice[idx + 1],
+ sizeof(rtac_voice_data.voice[idx]));
+ voice_session_id[idx] = voice_session_id[idx + 1];
+ }
+}
+
+void rtac_remove_voice(u32 cvs_handle)
+{
+ u32 i = 0;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_voice_mutex);
+ /* look for device */
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) {
+ shift_voice_devices(i);
+ rtac_voice_data.num_of_voice_combos--;
+ memset(&rtac_voice_data.voice[
+ rtac_voice_data.num_of_voice_combos], 0,
+ sizeof(rtac_voice_data.voice
+ [rtac_voice_data.num_of_voice_combos]));
+ voice_session_id[rtac_voice_data.num_of_voice_combos]
+ = 0;
+ break;
+ }
+ }
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+}
+
+static int get_voice_index_cvs(u32 cvs_handle)
+{
+ u32 i;
+
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle == cvs_handle)
+ return i;
+ }
+
+ pr_err("%s: No voice index for CVS handle %d found returning 0\n",
+ __func__, cvs_handle);
+ return 0;
+}
+
+static int get_voice_index_cvp(u32 cvp_handle)
+{
+ u32 i;
+
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvp_handle == cvp_handle)
+ return i;
+ }
+
+ pr_err("%s: No voice index for CVP handle %d found returning 0\n",
+ __func__, cvp_handle);
+ return 0;
+}
+
+static int get_voice_index(u32 mode, u32 handle)
+{
+ if (mode == RTAC_CVP)
+ return get_voice_index_cvp(handle);
+ if (mode == RTAC_CVS)
+ return get_voice_index_cvs(handle);
+
+ pr_err("%s: Invalid mode %d, returning 0\n",
+ __func__, mode);
+ return 0;
+}
+
+
+/* ADM APR */
+void rtac_set_adm_handle(void *handle)
+{
+ pr_debug("%s: handle = %d\n", __func__, (unsigned int)handle);
+
+ mutex_lock(&rtac_adm_apr_mutex);
+ rtac_adm_apr_data.apr_handle = handle;
+ mutex_unlock(&rtac_adm_apr_mutex);
+}
+
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size)
+{
+ pr_debug("%s:cmd_state = %d\n", __func__,
+ atomic_read(&rtac_adm_apr_data.cmd_state));
+ if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1)
+ return false;
+
+ /* Offset data for in-band payload */
+ rtac_copy_adm_payload_to_user(payload, payload_size);
+ atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+ wake_up(&rtac_adm_apr_data.cmd_wait);
+ return true;
+}
+
+void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size)
+{
+ pr_debug("%s\n", __func__);
+ rtac_adm_payload_size = payload_size;
+
+ memcpy(rtac_adm_buffer, &payload_size, sizeof(u32));
+ if (payload_size != 0) {
+ if (payload_size > rtac_adm_user_buf_size) {
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d,ret data = %d\n",
+ __func__, rtac_adm_user_buf_size, payload_size);
+ goto done;
+ }
+ memcpy(rtac_adm_buffer + sizeof(u32), payload, payload_size);
+ }
+done:
+ return;
+}
+
+u32 send_adm_apr(void *buf, u32 opcode)
+{
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 port_index = 0;
+ u32 copp_id;
+ u32 payload_size;
+ struct apr_hdr adm_params;
+ pr_debug("%s\n", __func__);
+
+ if (copy_from_user(&count, (void *)buf, sizeof(count))) {
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (count <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n", __func__, count);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+
+ if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy port id from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ for (port_index = 0; port_index < AFE_MAX_PORTS; port_index++) {
+ if (adm_get_copp_id(port_index) == copp_id)
+ break;
+ }
+ if (port_index >= AFE_MAX_PORTS) {
+ pr_err("%s: Could not find port index for copp = %d\n",
+ __func__, copp_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_adm_apr_mutex);
+ if (rtac_adm_apr_data.apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ goto err;
+ }
+
+ /* Set globals for copy of returned payload */
+ rtac_adm_user_buf_size = count;
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_adm_buffer + sizeof(adm_params),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ goto err;
+ }
+
+ /* Pack header */
+ adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ adm_params.src_svc = APR_SVC_ADM;
+ adm_params.src_domain = APR_DOMAIN_APPS;
+ adm_params.src_port = copp_id;
+ adm_params.dest_svc = APR_SVC_ADM;
+ adm_params.dest_domain = APR_DOMAIN_ADSP;
+ adm_params.dest_port = copp_id;
+ adm_params.token = copp_id;
+ adm_params.opcode = opcode;
+
+ memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params));
+ atomic_set(&rtac_adm_apr_data.cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command size = %d\n",
+ __func__, adm_params.pkt_size);
+
+ result = apr_send_pkt(rtac_adm_apr_data.apr_handle,
+ (uint32_t *)rtac_adm_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed port = %d, copp = %d\n",
+ __func__, port_index, copp_id);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_adm_apr_data.cmd_wait,
+ (atomic_read(&rtac_adm_apr_data.cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ mutex_unlock(&rtac_adm_apr_mutex);
+ if (!result) {
+ pr_err("%s: Set params timed out port = %d, copp = %d\n",
+ __func__, port_index, copp_id);
+ goto done;
+ }
+
+ if (rtac_adm_payload_size != 0) {
+ if (copy_to_user(buf, rtac_adm_buffer,
+ rtac_adm_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user, size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+ }
+
+ /* Return data written for SET & data read for GET */
+ if (opcode == ADM_CMD_GET_PARAMS)
+ bytes_returned = rtac_adm_payload_size;
+ else
+ bytes_returned = payload_size;
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_adm_apr_mutex);
+ return bytes_returned;
+}
+
+
+/* ASM APR */
+void rtac_set_asm_handle(u32 session_id, void *handle)
+{
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_asm_apr_mutex);
+ rtac_asm_apr_data[session_id].apr_handle = handle;
+ mutex_unlock(&rtac_asm_apr_mutex);
+}
+
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+ u32 payload_size)
+{
+ if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1)
+ return false;
+
+ pr_debug("%s\n", __func__);
+ /* Offset data for in-band payload */
+ rtac_copy_asm_payload_to_user(payload, payload_size);
+ atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0);
+ wake_up(&rtac_asm_apr_data[session_id].cmd_wait);
+ return true;
+}
+
+void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size)
+{
+ pr_debug("%s\n", __func__);
+ rtac_asm_payload_size = payload_size;
+
+ memcpy(rtac_asm_buffer, &payload_size, sizeof(u32));
+ if (payload_size) {
+ if (payload_size > rtac_asm_user_buf_size) {
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_asm_user_buf_size, payload_size);
+ goto done;
+ }
+ memcpy(rtac_asm_buffer + sizeof(u32), payload, payload_size);
+ }
+done:
+ return;
+}
+
+u32 send_rtac_asm_apr(void *buf, u32 opcode)
+{
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 session_id = 0;
+ u32 payload_size;
+ struct apr_hdr asm_params;
+ pr_debug("%s\n", __func__);
+
+ if (copy_from_user(&count, (void *)buf, sizeof(count))) {
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (count <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n", __func__, count);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+
+ if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy session id from user buffer\n",
+ __func__);
+ goto done;
+ }
+ if (session_id > (SESSION_MAX + 1)) {
+ pr_err("%s: Invalid Session = %d\n", __func__, session_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_asm_apr_mutex);
+ if (session_id < SESSION_MAX+1) {
+ if (rtac_asm_apr_data[session_id].apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ goto err;
+ }
+ }
+
+ /* Set globals for copy of returned payload */
+ rtac_asm_user_buf_size = count;
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_asm_buffer + sizeof(asm_params),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ goto err;
+ }
+
+ /* Pack header */
+ asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ asm_params.src_svc = q6asm_get_apr_service_id(session_id);
+ asm_params.src_domain = APR_DOMAIN_APPS;
+ asm_params.src_port = (session_id << 8) | 0x0001;
+ asm_params.dest_svc = APR_SVC_ASM;
+ asm_params.dest_domain = APR_DOMAIN_ADSP;
+ asm_params.dest_port = (session_id << 8) | 0x0001;
+ asm_params.token = session_id;
+ asm_params.opcode = opcode;
+
+ memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params));
+ if (session_id < SESSION_MAX+1)
+ atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command size = %d, session_id=%d\n",
+ __func__, asm_params.pkt_size, session_id);
+
+ result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle,
+ (uint32_t *)rtac_asm_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed session = %d\n",
+ __func__, session_id);
+ goto err;
+ }
+
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait,
+ (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0),
+ 5 * HZ);
+ mutex_unlock(&rtac_asm_apr_mutex);
+ if (!result) {
+ pr_err("%s: Set params timed out session = %d\n",
+ __func__, session_id);
+ goto done;
+ }
+
+ if (rtac_asm_payload_size != 0) {
+ if (copy_to_user(buf, rtac_asm_buffer,
+ rtac_asm_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+ }
+
+ /* Return data written for SET & data read for GET */
+ if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS)
+ bytes_returned = rtac_asm_payload_size;
+ else
+ bytes_returned = payload_size;
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_asm_apr_mutex);
+ return bytes_returned;
+}
+
+
+/* Voice APR */
+void rtac_set_voice_handle(u32 mode, void *handle)
+{
+ pr_debug("%s\n", __func__);
+ mutex_lock(&rtac_voice_apr_mutex);
+ rtac_voice_apr_data[mode].apr_handle = handle;
+ mutex_unlock(&rtac_voice_apr_mutex);
+}
+
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size)
+{
+ if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) ||
+ (mode >= RTAC_VOICE_MODES))
+ return false;
+
+ pr_debug("%s\n", __func__);
+ /* Offset data for in-band payload */
+ rtac_copy_voice_payload_to_user(payload, payload_size);
+ atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0);
+ wake_up(&rtac_voice_apr_data[mode].cmd_wait);
+ return true;
+}
+
+void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size)
+{
+ pr_debug("%s\n", __func__);
+ rtac_voice_payload_size = payload_size;
+
+ memcpy(rtac_voice_buffer, &payload_size, sizeof(u32));
+ if (payload_size) {
+ if (payload_size > rtac_voice_user_buf_size) {
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_voice_user_buf_size, payload_size);
+ goto done;
+ }
+ memcpy(rtac_voice_buffer + sizeof(u32), payload, payload_size);
+ }
+done:
+ return;
+}
+
+u32 send_voice_apr(u32 mode, void *buf, u32 opcode)
+{
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 payload_size;
+ u32 dest_port;
+ struct apr_hdr voice_params;
+ pr_debug("%s\n", __func__);
+
+ if (copy_from_user(&count, (void *)buf, sizeof(count))) {
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (count <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n", __func__, count);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(payload_size),
+ sizeof(payload_size))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+
+ if (copy_from_user(&dest_port, buf + 2 * sizeof(dest_port),
+ sizeof(dest_port))) {
+ pr_err("%s: Could not copy port id from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) {
+ pr_err("%s: Invalid Mode for APR, mode = %d\n",
+ __func__, mode);
+ goto done;
+ }
+
+ mutex_lock(&rtac_voice_apr_mutex);
+ if (rtac_voice_apr_data[mode].apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ goto err;
+ }
+
+ /* Set globals for copy of returned payload */
+ rtac_voice_user_buf_size = count;
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_voice_buffer + sizeof(voice_params),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ goto err;
+ }
+
+ /* Pack header */
+ voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ voice_params.src_svc = 0;
+ voice_params.src_domain = APR_DOMAIN_APPS;
+ voice_params.src_port = voice_session_id[
+ get_voice_index(mode, dest_port)];
+ voice_params.dest_svc = 0;
+ voice_params.dest_domain = APR_DOMAIN_MODEM;
+ voice_params.dest_port = (u16)dest_port;
+ voice_params.token = 0;
+ voice_params.opcode = opcode;
+
+ memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params));
+ atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command size = %d, opcode = %x\n",
+ __func__, voice_params.pkt_size, opcode);
+
+ result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle,
+ (uint32_t *)rtac_voice_buffer);
+ if (result < 0) {
+ pr_err("%s: apr_send_pkt failed opcode = %x\n",
+ __func__, opcode);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait,
+ (atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ mutex_unlock(&rtac_voice_apr_mutex);
+ if (!result) {
+ pr_err("%s: apr_send_pkt timed out opcode = %x\n",
+ __func__, opcode);
+ goto done;
+ }
+
+ if (rtac_voice_payload_size != 0) {
+ if (copy_to_user(buf, rtac_voice_buffer,
+ rtac_voice_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+ }
+
+ /* Return data written for SET & data read for GET */
+ if (opcode == VOICE_CMD_GET_PARAM)
+ bytes_returned = rtac_voice_payload_size;
+ else
+ bytes_returned = payload_size;
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_voice_apr_mutex);
+ return bytes_returned;
+}
+
+
+
+static long rtac_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ s32 result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (arg == 0) {
+ pr_err("%s: No data sent to driver!\n", __func__);
+ result = -EFAULT;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_GET_RTAC_ADM_INFO:
+ if (copy_to_user((void *)arg, &rtac_adm_data,
+ sizeof(rtac_adm_data)))
+ pr_err("%s: Could not copy to userspace!\n", __func__);
+ else
+ result = sizeof(rtac_adm_data);
+ break;
+ case AUDIO_GET_RTAC_VOICE_INFO:
+ if (copy_to_user((void *)arg, &rtac_voice_data,
+ sizeof(rtac_voice_data)))
+ pr_err("%s: Could not copy to userspace!\n", __func__);
+ else
+ result = sizeof(rtac_voice_data);
+ break;
+ case AUDIO_GET_RTAC_ADM_CAL:
+ result = send_adm_apr((void *)arg, ADM_CMD_GET_PARAMS);
+ break;
+ case AUDIO_SET_RTAC_ADM_CAL:
+ result = send_adm_apr((void *)arg, ADM_CMD_SET_PARAMS);
+ break;
+ case AUDIO_GET_RTAC_ASM_CAL:
+ result = send_rtac_asm_apr((void *)arg,
+ ASM_STREAM_CMD_GET_PP_PARAMS);
+ break;
+ case AUDIO_SET_RTAC_ASM_CAL:
+ result = send_rtac_asm_apr((void *)arg,
+ ASM_STREAM_CMD_SET_PP_PARAMS);
+ break;
+ case AUDIO_GET_RTAC_CVS_CAL:
+ result = send_voice_apr(RTAC_CVS, (void *)arg,
+ VOICE_CMD_GET_PARAM);
+ break;
+ case AUDIO_SET_RTAC_CVS_CAL:
+ result = send_voice_apr(RTAC_CVS, (void *)arg,
+ VOICE_CMD_SET_PARAM);
+ break;
+ case AUDIO_GET_RTAC_CVP_CAL:
+ result = send_voice_apr(RTAC_CVP, (void *)arg,
+ VOICE_CMD_GET_PARAM);
+ break;
+ case AUDIO_SET_RTAC_CVP_CAL:
+ result = send_voice_apr(RTAC_CVP, (void *)arg,
+ VOICE_CMD_SET_PARAM);
+ break;
+ default:
+ pr_err("%s: Invalid IOCTL, command = %d!\n",
+ __func__, cmd);
+ }
+done:
+ return result;
+}
+
+
+static const struct file_operations rtac_fops = {
+ .owner = THIS_MODULE,
+ .open = rtac_open,
+ .release = rtac_release,
+ .unlocked_ioctl = rtac_ioctl,
+};
+
+struct miscdevice rtac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_rtac",
+ .fops = &rtac_fops,
+};
+
+static int __init rtac_init(void)
+{
+ int i = 0;
+ pr_debug("%s\n", __func__);
+
+ /* ADM */
+ memset(&rtac_adm_data, 0, sizeof(rtac_adm_data));
+ rtac_adm_apr_data.apr_handle = NULL;
+ atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+ init_waitqueue_head(&rtac_adm_apr_data.cmd_wait);
+ mutex_init(&rtac_adm_mutex);
+ mutex_init(&rtac_adm_apr_mutex);
+
+ rtac_adm_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ if (rtac_adm_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, RTAC_BUF_SIZE);
+ goto nomem;
+ }
+
+ /* ASM */
+ for (i = 0; i < SESSION_MAX+1; i++) {
+ rtac_asm_apr_data[i].apr_handle = NULL;
+ atomic_set(&rtac_asm_apr_data[i].cmd_state, 0);
+ init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait);
+ }
+ mutex_init(&rtac_asm_apr_mutex);
+
+ rtac_asm_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ if (rtac_asm_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, RTAC_BUF_SIZE);
+ kzfree(rtac_adm_buffer);
+ goto nomem;
+ }
+
+ /* Voice */
+ memset(&rtac_voice_data, 0, sizeof(rtac_voice_data));
+ for (i = 0; i < RTAC_VOICE_MODES; i++) {
+ rtac_voice_apr_data[i].apr_handle = NULL;
+ atomic_set(&rtac_voice_apr_data[i].cmd_state, 0);
+ init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait);
+ }
+ mutex_init(&rtac_voice_mutex);
+ mutex_init(&rtac_voice_apr_mutex);
+
+ rtac_voice_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ if (rtac_voice_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, RTAC_BUF_SIZE);
+ kzfree(rtac_adm_buffer);
+ kzfree(rtac_asm_buffer);
+ goto nomem;
+ }
+
+ return misc_register(&rtac_misc);
+nomem:
+ return -ENOMEM;
+}
+
+module_init(rtac_init);
+
+MODULE_DESCRIPTION("MSM 8x60 Real-Time Audio Calibration driver");
+MODULE_LICENSE("GPL v2");
+
+#endif
diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c
new file mode 100644
index 0000000000000..b565411a5f7cd
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6adm.c
@@ -0,0 +1,1241 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/err.h>
+
+#include <sound/qdsp6v2/audio_dev_ctl.h>
+#include <sound/qdsp6v2/audio_acdb.h>
+#include <sound/qdsp6v2/rtac.h>
+
+#include <sound/apr_audio.h>
+#include <sound/q6afe.h>
+
+#define TIMEOUT_MS 1000
+#define AUDIO_RX 0x0
+#define AUDIO_TX 0x1
+
+#define ASM_MAX_SESSION 0x8 /* To do: define in a header */
+#define RESET_COPP_ID 99
+#define INVALID_COPP_ID 0xFF
+
+struct adm_ctl {
+ void *apr;
+ atomic_t copp_id[AFE_MAX_PORTS];
+ atomic_t copp_cnt[AFE_MAX_PORTS];
+ atomic_t copp_stat[AFE_MAX_PORTS];
+ wait_queue_head_t wait;
+ int ec_ref_rx;
+};
+
+static struct acdb_cal_block mem_addr_audproc[MAX_AUDPROC_TYPES];
+static struct acdb_cal_block mem_addr_audvol[MAX_AUDPROC_TYPES];
+
+static struct adm_ctl this_adm;
+
+
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params)
+{
+ struct asm_pp_params_command *open = NULL;
+ int ret = 0, sz = 0;
+ int index;
+
+ pr_debug("SRS - %s", __func__);
+
+ index = afe_get_port_index(port_id);
+
+ if (IS_ERR_VALUE(index)) {
+ pr_err("%s: invald port id\n", __func__);
+ return index;
+ }
+
+ switch (srs_tech_id) {
+ case SRS_ID_GLOBAL: {
+ struct srs_trumedia_params_GLOBAL *glb_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_GLOBAL) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_GLOBAL);
+ glb_params = (struct srs_trumedia_params_GLOBAL *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(glb_params, srs_params,
+ sizeof(struct srs_trumedia_params_GLOBAL));
+ pr_debug("SRS - %s: Global params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x\n",
+ __func__, (int)glb_params->v1,
+ (int)glb_params->v2, (int)glb_params->v3,
+ (int)glb_params->v4, (int)glb_params->v5,
+ (int)glb_params->v6, (int)glb_params->v7,
+ (int)glb_params->v8);
+ break;
+ }
+ case SRS_ID_WOWHD: {
+ struct srs_trumedia_params_WOWHD *whd_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_WOWHD);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_WOWHD) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_WOWHD;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_WOWHD);
+ whd_params = (struct srs_trumedia_params_WOWHD *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(whd_params, srs_params,
+ sizeof(struct srs_trumedia_params_WOWHD));
+ pr_debug("SRS - %s: WOWHD params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x, 9 = %x,"
+ " 10 = %x, 11 = %x\n", __func__, (int)whd_params->v1,
+ (int)whd_params->v2, (int)whd_params->v3,
+ (int)whd_params->v4, (int)whd_params->v5,
+ (int)whd_params->v6, (int)whd_params->v7,
+ (int)whd_params->v8, (int)whd_params->v9,
+ (int)whd_params->v10, (int)whd_params->v11);
+ break;
+ }
+ case SRS_ID_CSHP: {
+ struct srs_trumedia_params_CSHP *chp_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_CSHP);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_CSHP) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_CSHP;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_CSHP);
+ chp_params = (struct srs_trumedia_params_CSHP *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(chp_params, srs_params,
+ sizeof(struct srs_trumedia_params_CSHP));
+ pr_debug("SRS - %s: CSHP params - 1 = %x, 2 = %x, 3 = %x,"
+ " 4 = %x, 5 = %x, 6 = %x, 7 = %x, 8 = %x,"
+ " 9 = %x\n", __func__, (int)chp_params->v1,
+ (int)chp_params->v2, (int)chp_params->v3,
+ (int)chp_params->v4, (int)chp_params->v5,
+ (int)chp_params->v6, (int)chp_params->v7,
+ (int)chp_params->v8, (int)chp_params->v9);
+ break;
+ }
+ case SRS_ID_HPF: {
+ struct srs_trumedia_params_HPF *hpf_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_HPF);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_HPF) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_HPF;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_HPF);
+ hpf_params = (struct srs_trumedia_params_HPF *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(hpf_params, srs_params,
+ sizeof(struct srs_trumedia_params_HPF));
+ pr_debug("SRS - %s: HPF params - 1 = %x\n", __func__,
+ (int)hpf_params->v1);
+ break;
+ }
+ case SRS_ID_PEQ: {
+ struct srs_trumedia_params_PEQ *peq_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_PEQ);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_PEQ) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_PEQ;
+ open->params.param_size =
+ sizeof(struct srs_trumedia_params_PEQ);
+ peq_params = (struct srs_trumedia_params_PEQ *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(peq_params, srs_params,
+ sizeof(struct srs_trumedia_params_PEQ));
+ pr_debug("SRS - %s: PEQ params - 1 = %x 2 = %x, 3 = %x,"
+ " 4 = %x\n", __func__, (int)peq_params->v1,
+ (int)peq_params->v2, (int)peq_params->v3,
+ (int)peq_params->v4);
+ break;
+ }
+ case SRS_ID_HL: {
+ struct srs_trumedia_params_HL *hl_params = NULL;
+ sz = sizeof(struct asm_pp_params_command) +
+ sizeof(struct srs_trumedia_params_HL);
+ open = kzalloc(sz, GFP_KERNEL);
+ open->payload_size = sizeof(struct srs_trumedia_params_HL) +
+ sizeof(struct asm_pp_param_data_hdr);
+ open->params.param_id = SRS_TRUMEDIA_PARAMS_HL;
+ open->params.param_size = sizeof(struct srs_trumedia_params_HL);
+ hl_params = (struct srs_trumedia_params_HL *)((u8 *)open +
+ sizeof(struct asm_pp_params_command));
+ memcpy(hl_params, srs_params,
+ sizeof(struct srs_trumedia_params_HL));
+ pr_debug("SRS - %s: HL params - 1 = %x, 2 = %x, 3 = %x, 4 = %x,"
+ " 5 = %x, 6 = %x, 7 = %x\n", __func__,
+ (int)hl_params->v1, (int)hl_params->v2,
+ (int)hl_params->v3, (int)hl_params->v4,
+ (int)hl_params->v5, (int)hl_params->v6,
+ (int)hl_params->v7);
+ break;
+ }
+ default:
+ goto fail_cmd;
+ }
+
+ open->payload = NULL;
+ open->params.module_id = SRS_TRUMEDIA_MODULE_ID;
+ open->params.reserved = 0;
+ open->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ open->hdr.pkt_size = sz;
+ open->hdr.src_svc = APR_SVC_ADM;
+ open->hdr.src_domain = APR_DOMAIN_APPS;
+ open->hdr.src_port = port_id;
+ open->hdr.dest_svc = APR_SVC_ADM;
+ open->hdr.dest_domain = APR_DOMAIN_ADSP;
+ open->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ open->hdr.token = port_id;
+ open->hdr.opcode = ADM_CMD_SET_PARAMS;
+ pr_debug("SRS - %s: Command was sent now check Q6 - port id = %d,"
+ " size %d, module id %x, param id %x.\n", __func__,
+ open->hdr.dest_port, open->payload_size,
+ open->params.module_id, open->params.param_id);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)open);
+ if (ret < 0) {
+ pr_err("SRS - %s: ADM enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait, 1,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("SRS - %s: ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ kfree(open);
+ return ret;
+}
+
+static int32_t adm_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *payload;
+ int i, index;
+ payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("adm_callback: Reset event is received: %d %d apr[%p]\n",
+ data->reset_event, data->reset_proc,
+ this_adm.apr);
+ if (this_adm.apr) {
+ apr_reset(this_adm.apr);
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ atomic_set(&this_adm.copp_id[i],
+ RESET_COPP_ID);
+ atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_stat[i], 0);
+ }
+ this_adm.apr = NULL;
+ }
+ pr_debug("Resetting calibration blocks");
+ for (i = 0; i < MAX_AUDPROC_TYPES; i++) {
+ /* Device calibration */
+ mem_addr_audproc[i].cal_size = 0;
+ mem_addr_audproc[i].cal_kvaddr = 0;
+ mem_addr_audproc[i].cal_paddr = 0;
+
+ /* Volume calibration */
+ mem_addr_audvol[i].cal_size = 0;
+ mem_addr_audvol[i].cal_kvaddr = 0;
+ mem_addr_audvol[i].cal_paddr = 0;
+ }
+ return 0;
+ }
+
+ pr_debug("%s: code = 0x%x %x %x size = %d\n", __func__,
+ data->opcode, payload[0], payload[1],
+ data->payload_size);
+
+ if (data->payload_size) {
+ index = afe_get_port_index(data->token);
+ pr_debug("%s: Port ID %d, index %d\n", __func__,
+ data->token, index);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: invalid port idx %d token %d\n",
+ __func__, index, data->token);
+ return 0;
+ }
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ pr_debug("APR_BASIC_RSP_RESULT id %x\n", payload[0]);
+ switch (payload[0]) {
+ case ADM_CMD_SET_PARAMS:
+ if (rtac_make_adm_callback(payload,
+ data->payload_size))
+ break;
+ case ADM_CMD_COPP_CLOSE:
+ case ADM_CMD_MEMORY_MAP:
+ case ADM_CMD_MEMORY_UNMAP:
+ case ADM_CMD_MEMORY_MAP_REGIONS:
+ case ADM_CMD_MEMORY_UNMAP_REGIONS:
+ case ADM_CMD_MATRIX_MAP_ROUTINGS:
+ case ADM_CMD_CONNECT_AFE_PORT:
+ case ADM_CMD_DISCONNECT_AFE_PORT:
+ atomic_set(&this_adm.copp_stat[index], 1);
+ wake_up(&this_adm.wait);
+ break;
+ default:
+ pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
+ payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case ADM_CMDRSP_COPP_OPEN:
+ case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN:
+ case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3: {
+ struct adm_copp_open_respond *open = data->payload;
+ if (open->copp_id == INVALID_COPP_ID) {
+ pr_err("%s: invalid coppid rxed %d\n",
+ __func__, open->copp_id);
+ atomic_set(&this_adm.copp_stat[index], 1);
+ wake_up(&this_adm.wait);
+ break;
+ }
+ atomic_set(&this_adm.copp_id[index], open->copp_id);
+ atomic_set(&this_adm.copp_stat[index], 1);
+ pr_debug("%s: coppid rxed=%d\n", __func__,
+ open->copp_id);
+ wake_up(&this_adm.wait);
+ }
+ break;
+ case ADM_CMDRSP_GET_PARAMS:
+ pr_debug("%s: ADM_CMDRSP_GET_PARAMS\n", __func__);
+ rtac_make_adm_callback(payload,
+ data->payload_size);
+ break;
+ default:
+ pr_err("%s: Unknown cmd:0x%x\n", __func__,
+ data->opcode);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int send_adm_cal_block(int port_id, struct acdb_cal_block *aud_cal)
+{
+ s32 result = 0;
+ struct adm_set_params_command adm_params;
+ int index = afe_get_port_index(port_id);
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: invalid port idx %d portid %d\n",
+ __func__, index, port_id);
+ return 0;
+ }
+
+ pr_debug("%s: Port id %d, index %d\n", __func__, port_id, index);
+
+ if (!aud_cal || aud_cal->cal_size == 0) {
+ pr_debug("%s: No ADM cal to send for port_id = %d!\n",
+ __func__, port_id);
+ result = -EINVAL;
+ goto done;
+ }
+
+ adm_params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_params.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(adm_params));
+ adm_params.hdr.src_svc = APR_SVC_ADM;
+ adm_params.hdr.src_domain = APR_DOMAIN_APPS;
+ adm_params.hdr.src_port = port_id;
+ adm_params.hdr.dest_svc = APR_SVC_ADM;
+ adm_params.hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_params.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ adm_params.hdr.token = port_id;
+ adm_params.hdr.opcode = ADM_CMD_SET_PARAMS;
+ adm_params.payload = aud_cal->cal_paddr;
+ adm_params.payload_size = aud_cal->cal_size;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+ pr_debug("%s: Sending SET_PARAMS payload = 0x%x, size = %d\n",
+ __func__, adm_params.payload, adm_params.payload_size);
+ result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_params);
+ if (result < 0) {
+ pr_err("%s: Set params failed port = %d payload = 0x%x\n",
+ __func__, port_id, aud_cal->cal_paddr);
+ result = -EINVAL;
+ goto done;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: Set params timed out port = %d, payload = 0x%x\n",
+ __func__, port_id, aud_cal->cal_paddr);
+ result = -EINVAL;
+ goto done;
+ }
+
+ result = 0;
+done:
+ return result;
+}
+
+static void send_adm_cal(int port_id, int path)
+{
+ int result = 0;
+ s32 acdb_path;
+ struct acdb_cal_block aud_cal;
+
+ pr_debug("%s\n", __func__);
+
+ /* Maps audio_dev_ctrl path definition to ACDB definition */
+ acdb_path = path - 1;
+
+ pr_debug("%s: Sending audproc cal\n", __func__);
+ get_audproc_cal(acdb_path, &aud_cal);
+
+ /* map & cache buffers used */
+ if (((mem_addr_audproc[acdb_path].cal_paddr != aud_cal.cal_paddr) &&
+ (aud_cal.cal_size > 0)) ||
+ (aud_cal.cal_size > mem_addr_audproc[acdb_path].cal_size)) {
+
+ if (mem_addr_audproc[acdb_path].cal_paddr != 0)
+ adm_memory_unmap_regions(
+ &mem_addr_audproc[acdb_path].cal_paddr,
+ &mem_addr_audproc[acdb_path].cal_size, 1);
+
+ result = adm_memory_map_regions(&aud_cal.cal_paddr, 0,
+ &aud_cal.cal_size, 1);
+ if (result < 0)
+ pr_err("ADM audproc mmap did not work! path = %d, "
+ "addr = 0x%x, size = %d\n", acdb_path,
+ aud_cal.cal_paddr, aud_cal.cal_size);
+ else
+ mem_addr_audproc[acdb_path] = aud_cal;
+ }
+
+ if (!send_adm_cal_block(port_id, &aud_cal))
+ pr_debug("%s: Audproc cal sent for port id: %d, path %d\n",
+ __func__, port_id, acdb_path);
+ else
+ pr_debug("%s: Audproc cal not sent for port id: %d, path %d\n",
+ __func__, port_id, acdb_path);
+
+ pr_debug("%s: Sending audvol cal\n", __func__);
+ get_audvol_cal(acdb_path, &aud_cal);
+
+ /* map & cache buffers used */
+ if (((mem_addr_audvol[acdb_path].cal_paddr != aud_cal.cal_paddr) &&
+ (aud_cal.cal_size > 0)) ||
+ (aud_cal.cal_size > mem_addr_audvol[acdb_path].cal_size)) {
+ if (mem_addr_audvol[acdb_path].cal_paddr != 0)
+ adm_memory_unmap_regions(
+ &mem_addr_audvol[acdb_path].cal_paddr,
+ &mem_addr_audvol[acdb_path].cal_size, 1);
+
+ result = adm_memory_map_regions(&aud_cal.cal_paddr, 0,
+ &aud_cal.cal_size, 1);
+ if (result < 0)
+ pr_err("ADM audvol mmap did not work! path = %d, "
+ "addr = 0x%x, size = %d\n", acdb_path,
+ aud_cal.cal_paddr, aud_cal.cal_size);
+ else
+ mem_addr_audvol[acdb_path] = aud_cal;
+ }
+
+ if (!send_adm_cal_block(port_id, &aud_cal))
+ pr_debug("%s: Audvol cal sent for port id: %d, path %d\n",
+ __func__, port_id, acdb_path);
+ else
+ pr_debug("%s: Audvol cal not sent for port id: %d, path %d\n",
+ __func__, port_id, acdb_path);
+}
+
+int adm_connect_afe_port(int mode, int session_id, int port_id)
+{
+ struct adm_cmd_connect_afe_port cmd;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d session id:%d mode:%d\n", __func__,
+ port_id, session_id, mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd.hdr.pkt_size = sizeof(cmd);
+ cmd.hdr.src_svc = APR_SVC_ADM;
+ cmd.hdr.src_domain = APR_DOMAIN_APPS;
+ cmd.hdr.src_port = port_id;
+ cmd.hdr.dest_svc = APR_SVC_ADM;
+ cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
+ cmd.hdr.dest_port = port_id;
+ cmd.hdr.token = port_id;
+ cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT;
+
+ cmd.mode = mode;
+ cmd.session_id = session_id;
+ cmd.afe_port_id = port_id;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM connect AFE failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ atomic_inc(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_disconnect_afe_port(int mode, int session_id, int port_id)
+{
+ struct adm_cmd_connect_afe_port cmd;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d session id:%d mode:%d\n", __func__,
+ port_id, session_id, mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd.hdr.pkt_size = sizeof(cmd);
+ cmd.hdr.src_svc = APR_SVC_ADM;
+ cmd.hdr.src_domain = APR_DOMAIN_APPS;
+ cmd.hdr.src_port = port_id;
+ cmd.hdr.dest_svc = APR_SVC_ADM;
+ cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
+ cmd.hdr.dest_port = port_id;
+ cmd.hdr.token = port_id;
+ cmd.hdr.opcode = ADM_CMD_DISCONNECT_AFE_PORT;
+
+ cmd.mode = mode;
+ cmd.session_id = session_id;
+ cmd.afe_port_id = port_id;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM connect AFE failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
+{
+ struct adm_copp_open_command open;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
+ port_id, path, rate, channel_mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+
+
+ /* Create a COPP if port id are not enabled */
+ if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
+
+ open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ open.hdr.pkt_size = sizeof(open);
+ open.hdr.src_svc = APR_SVC_ADM;
+ open.hdr.src_domain = APR_DOMAIN_APPS;
+ open.hdr.src_port = port_id;
+ open.hdr.dest_svc = APR_SVC_ADM;
+ open.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open.hdr.dest_port = port_id;
+ open.hdr.token = port_id;
+ open.hdr.opcode = ADM_CMD_COPP_OPEN;
+
+ open.mode = path;
+ open.endpoint_id1 = port_id;
+
+ if (this_adm.ec_ref_rx == 0) {
+ open.endpoint_id2 = 0xFFFF;
+ } else if (this_adm.ec_ref_rx && (path != 1)) {
+ open.endpoint_id2 = this_adm.ec_ref_rx;
+ this_adm.ec_ref_rx = 0;
+ }
+
+ pr_debug("%s open.endpoint_id1:%d open.endpoint_id2:%d",
+ __func__, open.endpoint_id1, open.endpoint_id2);
+ /* convert path to acdb path */
+ if (path == ADM_PATH_PLAYBACK)
+ open.topology_id = get_adm_rx_topology();
+ else {
+ open.topology_id = get_adm_tx_topology();
+ if ((open.topology_id ==
+ VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
+ (open.topology_id ==
+ VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
+ rate = 16000;
+ }
+
+ if ((open.topology_id == 0) || (port_id == VOICE_RECORD_RX) || (port_id == VOICE_RECORD_TX))
+ open.topology_id = topology;
+
+ open.channel_config = channel_mode & 0x00FF;
+ open.rate = rate;
+
+ pr_debug("%s: channel_config=%d port_id=%d rate=%d"
+ "topology_id=0x%X\n", __func__, open.channel_config,\
+ open.endpoint_id1, open.rate,\
+ open.topology_id);
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+ atomic_inc(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+
+int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
+ int topology, int perfmode)
+{
+ struct adm_multi_ch_copp_open_command open;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d path:%d rate:%d channel :%d\n", __func__,
+ port_id, path, rate, channel_mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+
+ /* Create a COPP if port id are not enabled */
+ if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
+
+ open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+
+ open.hdr.pkt_size =
+ sizeof(struct adm_multi_ch_copp_open_command);
+
+ if (perfmode) {
+ pr_debug("%s Performance mode", __func__);
+ open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3;
+ open.flags = ADM_MULTI_CH_COPP_OPEN_PERF_MODE_BIT;
+ open.reserved = PCM_BITS_PER_SAMPLE;
+ } else {
+ open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN;
+ open.reserved = 0;
+ }
+
+ memset(open.dev_channel_mapping, 0, 8);
+
+ if (channel_mode == 1) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 4) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_RB;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+ } else if (channel_mode == 6) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ } else if (channel_mode == 8) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ open.dev_channel_mapping[6] = PCM_CHANNEL_FLC;
+ open.dev_channel_mapping[7] = PCM_CHANNEL_FRC;
+ } else {
+ pr_err("%s invalid num_chan %d\n", __func__,
+ channel_mode);
+ return -EINVAL;
+ }
+ open.hdr.src_svc = APR_SVC_ADM;
+ open.hdr.src_domain = APR_DOMAIN_APPS;
+ open.hdr.src_port = port_id;
+ open.hdr.dest_svc = APR_SVC_ADM;
+ open.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open.hdr.dest_port = port_id;
+ open.hdr.token = port_id;
+
+ open.mode = path;
+ open.endpoint_id1 = port_id;
+
+ if (this_adm.ec_ref_rx == 0) {
+ open.endpoint_id2 = 0xFFFF;
+ } else if (this_adm.ec_ref_rx && (path != 1)) {
+ open.endpoint_id2 = this_adm.ec_ref_rx;
+ this_adm.ec_ref_rx = 0;
+ }
+
+ pr_debug("%s open.endpoint_id1:%d open.endpoint_id2:%d",
+ __func__, open.endpoint_id1, open.endpoint_id2);
+ /* convert path to acdb path */
+ if (path == ADM_PATH_PLAYBACK)
+ open.topology_id = get_adm_rx_topology();
+ else {
+ open.topology_id = get_adm_tx_topology();
+ if ((open.topology_id ==
+ VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
+ (open.topology_id ==
+ VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
+ rate = 16000;
+ }
+
+ if ((open.topology_id == 0) || (port_id == VOICE_RECORD_RX) || (port_id == VOICE_RECORD_TX))
+ open.topology_id = topology;
+
+ open.channel_config = channel_mode & 0x00FF;
+ open.rate = rate;
+
+ pr_debug("%s: channel_config=%d port_id=%d rate=%d"
+ " topology_id=0x%X\n", __func__, open.channel_config,
+ open.endpoint_id1, open.rate,
+ open.topology_id);
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+ atomic_inc(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_matrix_map(int session_id, int path, int num_copps,
+ unsigned int *port_id, int copp_id)
+{
+ struct adm_routings_command route;
+ int ret = 0, i = 0;
+ /* Assumes port_ids have already been validated during adm_open */
+ int index = afe_get_port_index(copp_id);
+ int copp_cnt;
+
+ if (index < 0 || index >= AFE_MAX_PORTS) {
+ pr_err("%s: invalid port idx %d token %d\n",
+ __func__, index, copp_id);
+ return 0;
+ }
+
+ pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0]:%d\n",
+ __func__, session_id, path, num_copps, port_id[0]);
+
+ route.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ route.hdr.pkt_size = sizeof(route);
+ route.hdr.src_svc = 0;
+ route.hdr.src_domain = APR_DOMAIN_APPS;
+ route.hdr.src_port = copp_id;
+ route.hdr.dest_svc = APR_SVC_ADM;
+ route.hdr.dest_domain = APR_DOMAIN_ADSP;
+ route.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ route.hdr.token = copp_id;
+ route.hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS;
+ route.num_sessions = 1;
+ route.session[0].id = session_id;
+
+ if (num_copps < ADM_MAX_COPPS) {
+ copp_cnt = num_copps;
+ } else {
+ copp_cnt = ADM_MAX_COPPS;
+ /* print out warning for now as playback/capture to/from
+ * COPPs more than maximum allowed is extremely unlikely
+ */
+ pr_warn("%s: max out routable COPPs\n", __func__);
+ }
+
+ route.session[0].num_copps = copp_cnt;
+ for (i = 0; i < copp_cnt; i++) {
+ int tmp;
+ port_id[i] = afe_convert_virtual_to_portid(port_id[i]);
+
+ tmp = afe_get_port_index(port_id[i]);
+
+ pr_debug("%s: port_id[%d]: %d, index: %d\n", __func__, i,
+ port_id[i], tmp);
+
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS)
+ route.session[0].copp_id[i] =
+ atomic_read(&this_adm.copp_id[tmp]);
+ }
+
+ if (copp_cnt % 2)
+ route.session[0].copp_id[i] = 0;
+
+ switch (path) {
+ case 0x1:
+ route.path = AUDIO_RX;
+ break;
+ case 0x2:
+ case 0x3:
+ route.path = AUDIO_TX;
+ break;
+ default:
+ pr_err("%s: Wrong path set[%d]\n", __func__, path);
+ break;
+ }
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&route);
+ if (ret < 0) {
+ pr_err("%s: ADM routing for port %d failed\n",
+ __func__, port_id[0]);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM cmd Route failed for port %d\n",
+ __func__, port_id[0]);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ for (i = 0; i < num_copps; i++)
+ send_adm_cal(port_id[i], path);
+
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
+ tmp = afe_get_port_index(port_id[i]);
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS)
+ rtac_add_adm_device(port_id[i],
+ atomic_read(&this_adm.copp_id[tmp]),
+ path, session_id);
+ else
+ pr_debug("%s: Invalid port index %d",
+ __func__, tmp);
+ }
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id,
+ uint32_t *bufsz, uint32_t bufcnt)
+{
+ struct adm_cmd_memory_map_regions *mmap_regions = NULL;
+ struct adm_memory_map_regions *mregions = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int ret = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ pr_debug("%s\n", __func__);
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+
+ cmd_size = sizeof(struct adm_cmd_memory_map_regions)
+ + sizeof(struct adm_memory_map_regions) * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+ mmap_regions = (struct adm_cmd_memory_map_regions *)mmap_region_cmd;
+ mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mmap_regions->hdr.pkt_size = cmd_size;
+ mmap_regions->hdr.src_port = 0;
+ mmap_regions->hdr.dest_port = 0;
+ mmap_regions->hdr.token = 0;
+ mmap_regions->hdr.opcode = ADM_CMD_MEMORY_MAP_REGIONS;
+ mmap_regions->mempool_id = mempool_id & 0x00ff;
+ mmap_regions->nregions = bufcnt & 0x00ff;
+ pr_debug("%s: map_regions->nregions = %d\n", __func__,
+ mmap_regions->nregions);
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct adm_cmd_memory_map_regions));
+ mregions = (struct adm_memory_map_regions *)payload;
+
+ for (i = 0; i < bufcnt; i++) {
+ mregions->phys = buf_add[i];
+ mregions->buf_size = bufsz[i];
+ ++mregions;
+ }
+
+ atomic_set(&this_adm.copp_stat[0], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
+ if (ret < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ mmap_regions->hdr.opcode, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return ret;
+}
+
+int adm_memory_unmap_regions(uint32_t *buf_add, uint32_t *bufsz,
+ uint32_t bufcnt)
+{
+ struct adm_cmd_memory_unmap_regions *unmap_regions = NULL;
+ struct adm_memory_unmap_regions *mregions = NULL;
+ void *unmap_region_cmd = NULL;
+ void *payload = NULL;
+ int ret = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ pr_debug("%s\n", __func__);
+
+ if (this_adm.apr == NULL) {
+ pr_err("%s APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ cmd_size = sizeof(struct adm_cmd_memory_unmap_regions)
+ + sizeof(struct adm_memory_unmap_regions) * bufcnt;
+
+ unmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!unmap_region_cmd) {
+ pr_err("%s: allocate unmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+ unmap_regions = (struct adm_cmd_memory_unmap_regions *)
+ unmap_region_cmd;
+ unmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ unmap_regions->hdr.pkt_size = cmd_size;
+ unmap_regions->hdr.src_port = 0;
+ unmap_regions->hdr.dest_port = 0;
+ unmap_regions->hdr.token = 0;
+ unmap_regions->hdr.opcode = ADM_CMD_MEMORY_UNMAP_REGIONS;
+ unmap_regions->nregions = bufcnt & 0x00ff;
+ unmap_regions->reserved = 0;
+ pr_debug("%s: unmap_regions->nregions = %d\n", __func__,
+ unmap_regions->nregions);
+ payload = ((u8 *) unmap_region_cmd +
+ sizeof(struct adm_cmd_memory_unmap_regions));
+ mregions = (struct adm_memory_unmap_regions *)payload;
+
+ for (i = 0; i < bufcnt; i++) {
+ mregions->phys = buf_add[i];
+ ++mregions;
+ }
+ atomic_set(&this_adm.copp_stat[0], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) unmap_region_cmd);
+ if (ret < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ unmap_regions->hdr.opcode, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_unmap\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+fail_cmd:
+ kfree(unmap_region_cmd);
+ return ret;
+}
+
+int adm_get_copp_id(int port_index)
+{
+ pr_debug("%s\n", __func__);
+
+ if (port_index < 0) {
+ pr_err("%s: invalid port_id = %d\n", __func__, port_index);
+ return -EINVAL;
+ }
+
+ return atomic_read(&this_adm.copp_id[port_index]);
+}
+
+void adm_ec_ref_rx_id(int port_id)
+{
+ this_adm.ec_ref_rx = port_id;
+ pr_debug("%s ec_ref_rx:%d", __func__, this_adm.ec_ref_rx);
+}
+
+int adm_close(int port_id)
+{
+ struct apr_hdr close;
+
+ int ret = 0;
+ int index = 0;
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ index = afe_get_port_index(port_id);
+ if (afe_validate_port(port_id) < 0)
+ return -EINVAL;
+
+ pr_debug("%s port_id=%d index %d\n", __func__, port_id, index);
+
+ if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+ pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
+
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_cnt[index]);
+ if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+
+ close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ close.pkt_size = sizeof(close);
+ close.src_svc = APR_SVC_ADM;
+ close.src_domain = APR_DOMAIN_APPS;
+ close.src_port = port_id;
+ close.dest_svc = APR_SVC_ADM;
+ close.dest_domain = APR_DOMAIN_ADSP;
+ close.dest_port = atomic_read(&this_adm.copp_id[index]);
+ close.token = port_id;
+ close.opcode = ADM_CMD_COPP_CLOSE;
+
+ atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+
+ pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
+ __func__,
+ atomic_read(&this_adm.copp_id[index]),
+ port_id, index,
+ atomic_read(&this_adm.copp_cnt[index]));
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
+ if (ret < 0) {
+ pr_err("%s ADM close failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM cmd Route failed for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rtac_remove_adm_device(port_id);
+ }
+
+fail_cmd:
+ return ret;
+}
+
+static int __init adm_init(void)
+{
+ int i = 0;
+ init_waitqueue_head(&this_adm.wait);
+ this_adm.apr = NULL;
+
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
+ atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_stat[i], 0);
+ }
+ return 0;
+}
+
+device_initcall(adm_init);
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
new file mode 100644
index 0000000000000..5935ff3c90b71
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -0,0 +1,1826 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <sound/qdsp6v2/audio_acdb.h>
+#include <sound/apr_audio.h>
+#include <sound/q6afe.h>
+
+struct afe_ctl {
+ void *apr;
+ atomic_t state;
+ atomic_t status;
+ wait_queue_head_t wait;
+ struct task_struct *task;
+ void (*tx_cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv);
+ void (*rx_cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv);
+ void *tx_private_data;
+ void *rx_private_data;
+};
+
+static struct afe_ctl this_afe;
+
+static struct acdb_cal_block afe_cal_addr[MAX_AUDPROC_TYPES];
+static int pcm_afe_instance[2];
+static int proxy_afe_instance[2];
+bool afe_close_done[2] = {true, true};
+
+#define TIMEOUT_MS 1000
+#define Q6AFE_MAX_VOLUME 0x3FFF
+
+#define SIZEOF_CFG_CMD(y) \
+ (sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y)))
+
+static int32_t afe_callback(struct apr_client_data *data, void *priv)
+{
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("q6afe: reset event = %d %d apr[%p]\n",
+ data->reset_event, data->reset_proc, this_afe.apr);
+ if (this_afe.apr) {
+ apr_reset(this_afe.apr);
+ atomic_set(&this_afe.state, 0);
+ this_afe.apr = NULL;
+ }
+ /* send info to user */
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ send_sig(SIGUSR1, this_afe.task, 0);
+ return 0;
+ }
+ if (data->payload_size) {
+ uint32_t *payload;
+ uint16_t port_id = 0;
+ payload = data->payload;
+ pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x\n",
+ __func__, data->opcode,
+ payload[0], payload[1]);
+ /* payload[1] contains the error status for response */
+ if (payload[1] != 0) {
+ atomic_set(&this_afe.status, -1);
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ switch (payload[0]) {
+ case AFE_PORT_AUDIO_IF_CONFIG:
+ case AFE_PORT_CMD_I2S_CONFIG:
+ case AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG:
+ case AFE_PORT_AUDIO_SLIM_SCH_CONFIG:
+ case AFE_PORT_CMD_STOP:
+ case AFE_PORT_CMD_START:
+ case AFE_PORT_CMD_LOOPBACK:
+ case AFE_PORT_CMD_SIDETONE_CTL:
+ case AFE_PORT_CMD_SET_PARAM:
+ case AFE_PSEUDOPORT_CMD_START:
+ case AFE_PSEUDOPORT_CMD_STOP:
+ case AFE_PORT_CMD_APPLY_GAIN:
+ case AFE_SERVICE_CMD_MEMORY_MAP:
+ case AFE_SERVICE_CMD_MEMORY_UNMAP:
+ case AFE_SERVICE_CMD_UNREG_RTPORT:
+ atomic_set(&this_afe.state, 0);
+ wake_up(&this_afe.wait);
+ break;
+ case AFE_SERVICE_CMD_REG_RTPORT:
+ break;
+ case AFE_SERVICE_CMD_RTPORT_WR:
+ port_id = RT_PROXY_PORT_001_TX;
+ break;
+ case AFE_SERVICE_CMD_RTPORT_RD:
+ port_id = RT_PROXY_PORT_001_RX;
+ break;
+ default:
+ pr_err("Unknown cmd 0x%x\n",
+ payload[0]);
+ break;
+ }
+ } else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) {
+ port_id = (uint16_t)(0x0000FFFF & payload[0]);
+ }
+ pr_debug("%s:port_id = %x\n", __func__, port_id);
+ switch (port_id) {
+ case RT_PROXY_PORT_001_TX: {
+ if (this_afe.tx_cb) {
+ this_afe.tx_cb(data->opcode, data->token,
+ data->payload,
+ this_afe.tx_private_data);
+ }
+ break;
+ }
+ case RT_PROXY_PORT_001_RX: {
+ if (this_afe.rx_cb) {
+ this_afe.rx_cb(data->opcode, data->token,
+ data->payload,
+ this_afe.rx_private_data);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+int afe_get_port_type(u16 port_id)
+{
+ int ret;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PCM_RX:
+ case SECONDARY_PCM_RX:
+ case SECONDARY_I2S_RX:
+ case MI2S_RX:
+ case HDMI_RX:
+ case SLIMBUS_0_RX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_3_RX:
+ case INT_BT_SCO_RX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case VOICE_PLAYBACK_TX:
+ case RT_PROXY_PORT_001_RX:
+ case SLIMBUS_4_RX:
+ ret = MSM_AFE_PORT_TYPE_RX;
+ break;
+
+ case PRIMARY_I2S_TX:
+ case PCM_TX:
+ case SECONDARY_PCM_TX:
+ case SECONDARY_I2S_TX:
+ case MI2S_TX:
+ case DIGI_MIC_TX:
+ case VOICE_RECORD_TX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_TX:
+ case INT_FM_TX:
+ case VOICE_RECORD_RX:
+ case INT_BT_SCO_TX:
+ case RT_PROXY_PORT_001_TX:
+ case SLIMBUS_4_TX:
+ ret = MSM_AFE_PORT_TYPE_TX;
+ break;
+
+ default:
+ pr_err("%s: invalid port id %d\n", __func__, port_id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int afe_validate_port(u16 port_id)
+{
+ int ret;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case PCM_RX:
+ case PCM_TX:
+ case SECONDARY_PCM_RX:
+ case SECONDARY_PCM_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case HDMI_RX:
+ case RSVD_2:
+ case RSVD_3:
+ case DIGI_MIC_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ case VOICE_PLAYBACK_TX:
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_001_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ {
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int afe_convert_virtual_to_portid(u16 port_id)
+{
+ int ret;
+
+ /* if port_id is virtual, convert to physical..
+ * if port_id is already physical, return physical
+ */
+ if (afe_validate_port(port_id) < 0) {
+ if (port_id == RT_PROXY_DAI_001_RX ||
+ port_id == RT_PROXY_DAI_001_TX ||
+ port_id == RT_PROXY_DAI_002_RX ||
+ port_id == RT_PROXY_DAI_002_TX)
+ ret = VIRTUAL_ID_TO_PORTID(port_id);
+ else
+ ret = -EINVAL;
+ } else
+ ret = port_id;
+
+ return ret;
+}
+
+int afe_get_port_index(u16 port_id)
+{
+ switch (port_id) {
+ case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
+ case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
+ case PCM_RX: return IDX_PCM_RX;
+ case PCM_TX: return IDX_PCM_TX;
+ case SECONDARY_PCM_RX: return IDX_SECONDARY_PCM_RX;
+ case SECONDARY_PCM_TX: return IDX_SECONDARY_PCM_TX;
+ case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
+ case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
+ case MI2S_RX: return IDX_MI2S_RX;
+ case MI2S_TX: return IDX_MI2S_TX;
+ case HDMI_RX: return IDX_HDMI_RX;
+ case RSVD_2: return IDX_RSVD_2;
+ case RSVD_3: return IDX_RSVD_3;
+ case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
+ case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
+ case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
+ case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+ case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
+ case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
+ case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
+ case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
+ case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
+ case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
+ case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
+ case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
+ case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
+ case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
+ case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
+ case INT_FM_RX: return IDX_INT_FM_RX;
+ case INT_FM_TX: return IDX_INT_FM_TX;
+ case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
+ case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
+ case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
+ case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+
+ default: return -EINVAL;
+ }
+}
+
+int afe_sizeof_cfg_cmd(u16 port_id)
+{
+ int ret_size;
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_mi2s_cfg);
+ break;
+ case HDMI_RX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_hdmi_multi_ch_cfg);
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_slimbus_sch_cfg);
+ break;
+ case RT_PROXY_PORT_001_RX:
+ case RT_PROXY_PORT_001_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_rtproxy_cfg);
+ break;
+ case PCM_RX:
+ case PCM_TX:
+ case SECONDARY_PCM_RX:
+ case SECONDARY_PCM_TX:
+ default:
+ ret_size = SIZEOF_CFG_CMD(afe_port_pcm_cfg);
+ break;
+ }
+ return ret_size;
+}
+
+int afe_q6_interface_prepare(void)
+{
+ int ret = 0;
+
+ pr_debug("%s:", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ }
+ }
+ return ret;
+}
+
+static void afe_send_cal_block(int32_t path, u16 port_id)
+{
+ int result = 0;
+ struct acdb_cal_block cal_block;
+ struct afe_port_cmd_set_param_no_payload afe_cal;
+ pr_debug("%s: path %d\n", __func__, path);
+
+ get_afe_cal(path, &cal_block);
+ if (cal_block.cal_size <= 0) {
+ pr_debug("%s: No AFE cal to send!\n", __func__);
+ goto done;
+ }
+
+ if ((afe_cal_addr[path].cal_paddr != cal_block.cal_paddr) ||
+ (cal_block.cal_size > afe_cal_addr[path].cal_size)) {
+ if (afe_cal_addr[path].cal_paddr != 0)
+ afe_cmd_memory_unmap(
+ afe_cal_addr[path].cal_paddr);
+
+ afe_cmd_memory_map(cal_block.cal_paddr, cal_block.cal_size);
+ afe_cal_addr[path].cal_paddr = cal_block.cal_paddr;
+ afe_cal_addr[path].cal_size = cal_block.cal_size;
+ }
+
+ afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ afe_cal.hdr.pkt_size = sizeof(afe_cal);
+ afe_cal.hdr.src_port = 0;
+ afe_cal.hdr.dest_port = 0;
+ afe_cal.hdr.token = 0;
+ afe_cal.hdr.opcode = AFE_PORT_CMD_SET_PARAM;
+ afe_cal.port_id = port_id;
+ afe_cal.payload_size = cal_block.cal_size;
+ afe_cal.payload_address = cal_block.cal_paddr;
+
+ pr_debug("%s: AFE cal sent for device port = %d, path = %d, "
+ "cal size = %d, cal addr = 0x%x\n", __func__,
+ port_id, path, cal_block.cal_size, cal_block.cal_paddr);
+
+ atomic_set(&this_afe.state, 1);
+ result = apr_send_pkt(this_afe.apr, (uint32_t *) &afe_cal);
+ if (result < 0) {
+ pr_err("%s: AFE cal for port %d failed\n",
+ __func__, port_id);
+ }
+
+ result = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result) {
+ pr_err("%s: wait_event timeout SET AFE CAL\n", __func__);
+ goto done;
+ }
+
+ pr_debug("%s: AFE cal sent for path %d device!\n", __func__, path);
+done:
+ return;
+}
+
+void afe_send_cal(u16 port_id)
+{
+ pr_debug("%s\n", __func__);
+
+ if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX)
+ afe_send_cal_block(TX_CAL, port_id);
+ else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX)
+ afe_send_cal_block(RX_CAL, port_id);
+}
+
+/* This function sends multi-channel HDMI configuration command and AFE
+ * calibration which is only supported by QDSP6 on 8960 and onward.
+ */
+int afe_port_start(u16 port_id, union afe_port_config *afe_config,
+ u32 rate)
+{
+ struct afe_port_start_command start;
+ struct afe_audioif_config_command config;
+ int ret;
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+ pr_debug("%s: %d %d\n", __func__, port_id, rate);
+
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX)) {
+ pr_debug("%s: before incrementing pcm_afe_instance %d"\
+ " port_id %d\n", __func__,
+ pcm_afe_instance[port_id & 0x1], port_id);
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ pcm_afe_instance[port_id & 0x1]++;
+ return 0;
+ }
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX)) {
+ pr_debug("%s: before incrementing proxy_afe_instance %d"\
+ " port_id %d\n", __func__,
+ proxy_afe_instance[port_id & 0x1], port_id);
+ if (!afe_close_done[port_id & 0x1]) {
+ /*close pcm dai corresponding to the proxy dai*/
+ afe_close(port_id - 0x10);
+ pcm_afe_instance[port_id & 0x1]++;
+ pr_debug("%s: reconfigure afe port again\n", __func__);
+ }
+ proxy_afe_instance[port_id & 0x1]++;
+ afe_close_done[port_id & 0x1] = false;
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ }
+
+ ret = afe_q6_interface_prepare();
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ if (port_id == HDMI_RX) {
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ config.hdr.opcode = AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG;
+ } else {
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ switch (port_id) {
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ config.hdr.opcode = AFE_PORT_AUDIO_SLIM_SCH_CONFIG;
+ break;
+ case MI2S_TX:
+ case MI2S_RX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ /* AFE_PORT_CMD_I2S_CONFIG command is not supported
+ * in the LPASS EL 1.0. So we have to distiguish
+ * which AFE command, AFE_PORT_CMD_I2S_CONFIG or
+ * AFE_PORT_AUDIO_IF_CONFIG to use. If the format
+ * is L-PCM, the AFE_PORT_AUDIO_IF_CONFIG is used
+ * to make the backward compatible.
+ */
+ pr_debug("%s: afe_config->mi2s.format = %d\n", __func__,
+ afe_config->mi2s.format);
+ if (afe_config->mi2s.format == MSM_AFE_I2S_FORMAT_LPCM)
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+ else
+ config.hdr.opcode = AFE_PORT_CMD_I2S_CONFIG;
+ break;
+ default:
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+ break;
+ }
+ }
+
+ if (afe_validate_port(port_id) < 0) {
+
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ config.port_id = port_id;
+ config.port = *afe_config;
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout IF CONFIG\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ /* send AFE cal */
+ afe_send_cal(port_id);
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PORT_CMD_START;
+ start.port_id = port_id;
+ start.gain = 0x2000;
+ start.sample_rate = rate;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout PORT START\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (this_afe.task != current)
+ this_afe.task = current;
+
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ return 0;
+
+fail_cmd:
+ return ret;
+}
+
+/* This function should be used by 8660 exclusively */
+int afe_open(u16 port_id, union afe_port_config *afe_config, int rate)
+{
+ struct afe_port_start_command start;
+ struct afe_audioif_config_command config;
+ int ret = 0;
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ pr_debug("%s: %d %d\n", __func__, port_id, rate);
+
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX))
+ return 0;
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX))
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0)
+ return ret;
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ switch (port_id) {
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case SLIMBUS_1_RX:
+ case SLIMBUS_1_TX:
+ case SLIMBUS_2_RX:
+ case SLIMBUS_2_TX:
+ case SLIMBUS_3_RX:
+ case SLIMBUS_3_TX:
+ case SLIMBUS_4_RX:
+ case SLIMBUS_4_TX:
+ config.hdr.opcode = AFE_PORT_AUDIO_SLIM_SCH_CONFIG;
+ break;
+ case MI2S_TX:
+ case MI2S_RX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ /* AFE_PORT_CMD_I2S_CONFIG command is not supported
+ * in the LPASS EL 1.0. So we have to distiguish
+ * which AFE command, AFE_PORT_CMD_I2S_CONFIG or
+ * AFE_PORT_AUDIO_IF_CONFIG to use. If the format
+ * is L-PCM, the AFE_PORT_AUDIO_IF_CONFIG is used
+ * to make the backward compatible.
+ */
+ pr_debug("%s: afe_config->mi2s.format = %d\n", __func__,
+ afe_config->mi2s.format);
+ if (afe_config->mi2s.format == MSM_AFE_I2S_FORMAT_LPCM)
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+ else
+ config.hdr.opcode = AFE_PORT_CMD_I2S_CONFIG;
+ break;
+ default:
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+ break;
+ }
+
+ if (afe_validate_port(port_id) < 0) {
+
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ config.port_id = port_id;
+ config.port = *afe_config;
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PORT_CMD_START;
+ start.port_id = port_id;
+ start.gain = 0x2000;
+ start.sample_rate = rate;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (this_afe.task != current)
+ this_afe.task = current;
+
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_loopback(u16 enable, u16 dst_port, u16 src_port)
+{
+ struct afe_loopback_command lb_cmd;
+ int ret = 0;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0)
+ return ret;
+
+ if ((afe_get_port_type(dst_port) == MSM_AFE_PORT_TYPE_RX) &&
+ (afe_get_port_type(src_port) == MSM_AFE_PORT_TYPE_RX))
+ return afe_loopback_cfg(enable, dst_port, src_port,
+ LB_MODE_EC_REF_VOICE_AUDIO);
+
+ lb_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ lb_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(lb_cmd) - APR_HDR_SIZE);
+ lb_cmd.hdr.src_port = 0;
+ lb_cmd.hdr.dest_port = 0;
+ lb_cmd.hdr.token = 0;
+ lb_cmd.hdr.opcode = AFE_PORT_CMD_LOOPBACK;
+ lb_cmd.tx_port_id = src_port;
+ lb_cmd.rx_port_id = dst_port;
+ lb_cmd.mode = 0xFFFF;
+ lb_cmd.enable = (enable ? 1 : 0);
+ atomic_set(&this_afe.state, 1);
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &lb_cmd);
+ if (ret < 0) {
+ pr_err("%s: AFE loopback failed\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ }
+done:
+ return ret;
+}
+
+int afe_loopback_cfg(u16 enable, u16 dst_port, u16 src_port, u16 mode)
+{
+ struct afe_port_cmd_set_param lp_cfg;
+ int ret = 0;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0)
+ return ret;
+
+ pr_debug("%s: src_port %d, dst_port %d\n",
+ __func__, src_port, dst_port);
+
+ lp_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ lp_cfg.hdr.pkt_size = sizeof(lp_cfg);
+ lp_cfg.hdr.src_port = 0;
+ lp_cfg.hdr.dest_port = 0;
+ lp_cfg.hdr.token = 0;
+ lp_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM;
+
+ lp_cfg.port_id = src_port;
+ lp_cfg.payload_size = sizeof(struct afe_param_payload_base) +
+ sizeof(struct afe_param_loopback_cfg);
+ lp_cfg.payload_address = 0;
+
+ lp_cfg.payload.base.module_id = AFE_MODULE_LOOPBACK;
+ lp_cfg.payload.base.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG;
+ lp_cfg.payload.base.param_size = sizeof(struct afe_param_loopback_cfg);
+ lp_cfg.payload.base.reserved = 0;
+
+ lp_cfg.payload.param.loopback_cfg.loopback_cfg_minor_version =
+ AFE_API_VERSION_LOOPBACK_CONFIG;
+ lp_cfg.payload.param.loopback_cfg.dst_port_id = dst_port;
+ lp_cfg.payload.param.loopback_cfg.routing_mode = mode;
+ lp_cfg.payload.param.loopback_cfg.enable = enable;
+ lp_cfg.payload.param.loopback_cfg.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &lp_cfg);
+ if (ret < 0) {
+ pr_err("%s: AFE loopback config failed for src_port %d, dst_port %d\n",
+ __func__, src_port, dst_port);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_loopback_gain(u16 port_id, u16 volume)
+{
+ struct afe_port_cmd_set_param set_param;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ if (afe_validate_port(port_id) < 0) {
+
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ /* RX ports numbers are even .TX ports numbers are odd. */
+ if (port_id % 2 == 0) {
+ pr_err("%s: Failed : afe loopback gain only for TX ports."
+ " port_id %d\n", __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: %d %hX\n", __func__, port_id, volume);
+
+ set_param.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ set_param.hdr.pkt_size = sizeof(set_param);
+ set_param.hdr.src_port = 0;
+ set_param.hdr.dest_port = 0;
+ set_param.hdr.token = 0;
+ set_param.hdr.opcode = AFE_PORT_CMD_SET_PARAM;
+
+ set_param.port_id = port_id;
+ set_param.payload_size = sizeof(struct afe_param_payload_base) +
+ sizeof(struct afe_param_loopback_gain);
+ set_param.payload_address = 0;
+
+ set_param.payload.base.module_id = AFE_MODULE_ID_PORT_INFO;
+ set_param.payload.base.param_id = AFE_PARAM_ID_LOOPBACK_GAIN;
+ set_param.payload.base.param_size =
+ sizeof(struct afe_param_loopback_gain);
+ set_param.payload.base.reserved = 0;
+
+ set_param.payload.param.loopback_gain.gain = volume;
+ set_param.payload.param.loopback_gain.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &set_param);
+ if (ret < 0) {
+ pr_err("%s: AFE param set failed for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_apply_gain(u16 port_id, u16 gain)
+{
+ struct afe_port_gain_command set_gain;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is not opened\n", __func__);
+ ret = -EPERM;
+ goto fail_cmd;
+ }
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ /* RX ports numbers are even .TX ports numbers are odd. */
+ if (port_id % 2 == 0) {
+ pr_err("%s: Failed : afe apply gain only for TX ports."
+ " port_id %d\n", __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: %d %hX\n", __func__, port_id, gain);
+
+ set_gain.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ set_gain.hdr.pkt_size = sizeof(set_gain);
+ set_gain.hdr.src_port = 0;
+ set_gain.hdr.dest_port = 0;
+ set_gain.hdr.token = 0;
+ set_gain.hdr.opcode = AFE_PORT_CMD_APPLY_GAIN;
+
+ set_gain.port_id = port_id;
+ set_gain.gain = gain;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &set_gain);
+ if (ret < 0) {
+ pr_err("%s: AFE Gain set failed for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_pseudo_port_start_nowait(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_start_command start;
+
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE APR is not registered\n", __func__);
+ return -ENODEV;
+ }
+
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+ start.port_id = port_id;
+ start.timing = 1;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int afe_start_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_start_command start;
+
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0)
+ return ret;
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+ start.port_id = port_id;
+ start.timing = 1;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int afe_pseudo_port_stop_nowait(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_stop_command stop;
+
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ return -EINVAL;
+ }
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+ if (ret < 0) {
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+int afe_stop_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_stop_command stop;
+
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ return -EINVAL;
+ }
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+ if (ret < 0) {
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int afe_cmd_memory_map(u32 dma_addr_p, u32 dma_buf_sz)
+{
+ int ret = 0;
+ struct afe_cmd_memory_map mregion;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = 0;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_MEMORY_MAP;
+ mregion.phy_addr = dma_addr_p;
+ mregion.mem_sz = dma_buf_sz;
+ mregion.mem_id = 0;
+ mregion.rsvd = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &mregion);
+ if (ret < 0) {
+ pr_err("%s: AFE memory map cmd failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ return 0;
+}
+
+int afe_cmd_memory_map_nowait(u32 dma_addr_p, u32 dma_buf_sz)
+{
+ int ret = 0;
+ struct afe_cmd_memory_map mregion;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = 0;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_MEMORY_MAP;
+ mregion.phy_addr = dma_addr_p;
+ mregion.mem_sz = dma_buf_sz;
+ mregion.mem_id = 0;
+ mregion.rsvd = 0;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &mregion);
+ if (ret < 0) {
+ pr_err("%s: AFE memory map cmd failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ }
+ return 0;
+}
+
+int afe_cmd_memory_unmap(u32 dma_addr_p)
+{
+ int ret = 0;
+ struct afe_cmd_memory_unmap mregion;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = 0;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_MEMORY_UNMAP;
+ mregion.phy_addr = dma_addr_p;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &mregion);
+ if (ret < 0) {
+ pr_err("%s: AFE memory unmap cmd failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+ return 0;
+}
+
+int afe_cmd_memory_unmap_nowait(u32 dma_addr_p)
+{
+ int ret = 0;
+ struct afe_cmd_memory_unmap mregion;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mregion.hdr.pkt_size = sizeof(mregion);
+ mregion.hdr.src_port = 0;
+ mregion.hdr.dest_port = 0;
+ mregion.hdr.token = 0;
+ mregion.hdr.opcode = AFE_SERVICE_CMD_MEMORY_UNMAP;
+ mregion.phy_addr = dma_addr_p;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &mregion);
+ if (ret < 0) {
+ pr_err("%s: AFE memory unmap cmd failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ }
+ return 0;
+}
+
+int afe_register_get_events(u16 port_id,
+ void (*cb) (uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv),
+ void *private_data)
+{
+ int ret = 0;
+ struct afe_cmd_reg_rtport rtproxy;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX))
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ else
+ return -EINVAL;
+
+ if (port_id == RT_PROXY_PORT_001_TX) {
+ this_afe.tx_cb = cb;
+ this_afe.tx_private_data = private_data;
+ } else if (port_id == RT_PROXY_PORT_001_RX) {
+ this_afe.rx_cb = cb;
+ this_afe.rx_private_data = private_data;
+ }
+
+ rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ rtproxy.hdr.pkt_size = sizeof(rtproxy);
+ rtproxy.hdr.src_port = 1;
+ rtproxy.hdr.dest_port = 1;
+ rtproxy.hdr.token = 0;
+ rtproxy.hdr.opcode = AFE_SERVICE_CMD_REG_RTPORT;
+ rtproxy.port_id = port_id;
+ rtproxy.rsvd = 0;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &rtproxy);
+ if (ret < 0) {
+ pr_err("%s: AFE reg. rtproxy_event failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+ return 0;
+}
+
+int afe_unregister_get_events(u16 port_id)
+{
+ int ret = 0;
+ struct afe_cmd_unreg_rtport rtproxy;
+
+ pr_debug("%s:\n", __func__);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_debug("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX))
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ else
+ return -EINVAL;
+
+ rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ rtproxy.hdr.pkt_size = sizeof(rtproxy);
+ rtproxy.hdr.src_port = 0;
+ rtproxy.hdr.dest_port = 0;
+ rtproxy.hdr.token = 0;
+ rtproxy.hdr.opcode = AFE_SERVICE_CMD_UNREG_RTPORT;
+ rtproxy.port_id = port_id;
+ rtproxy.rsvd = 0;
+
+ if (port_id == RT_PROXY_PORT_001_TX) {
+ this_afe.tx_cb = NULL;
+ this_afe.tx_private_data = NULL;
+ } else if (port_id == RT_PROXY_PORT_001_RX) {
+ this_afe.rx_cb = NULL;
+ this_afe.rx_private_data = NULL;
+ }
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &rtproxy);
+ if (ret < 0) {
+ pr_err("%s: AFE enable Unreg. rtproxy_event failed %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+ return 0;
+}
+
+int afe_rt_proxy_port_write(u32 buf_addr_p, int bytes)
+{
+ int ret = 0;
+ struct afe_cmd_rtport_wr afecmd_wr;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s:register to AFE is not done\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ pr_debug("%s: buf_addr_p = 0x%08x bytes = %d\n", __func__,
+ buf_addr_p, bytes);
+
+ afecmd_wr.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ afecmd_wr.hdr.pkt_size = sizeof(afecmd_wr);
+ afecmd_wr.hdr.src_port = 0;
+ afecmd_wr.hdr.dest_port = 0;
+ afecmd_wr.hdr.token = 0;
+ afecmd_wr.hdr.opcode = AFE_SERVICE_CMD_RTPORT_WR;
+ afecmd_wr.buf_addr = (uint32_t)buf_addr_p;
+ afecmd_wr.port_id = RT_PROXY_PORT_001_TX;
+ afecmd_wr.bytes_avail = bytes;
+ afecmd_wr.rsvd = 0;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &afecmd_wr);
+ if (ret < 0) {
+ pr_err("%s: AFE rtproxy write to port 0x%x failed %d\n",
+ __func__, afecmd_wr.port_id, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+ return 0;
+
+}
+
+int afe_rt_proxy_port_read(u32 buf_addr_p, int bytes)
+{
+ int ret = 0;
+ struct afe_cmd_rtport_rd afecmd_rd;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: register to AFE is not done\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ pr_debug("%s: buf_addr_p = 0x%08x bytes = %d\n", __func__,
+ buf_addr_p, bytes);
+
+ afecmd_rd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ afecmd_rd.hdr.pkt_size = sizeof(afecmd_rd);
+ afecmd_rd.hdr.src_port = 0;
+ afecmd_rd.hdr.dest_port = 0;
+ afecmd_rd.hdr.token = 0;
+ afecmd_rd.hdr.opcode = AFE_SERVICE_CMD_RTPORT_RD;
+ afecmd_rd.buf_addr = (uint32_t)buf_addr_p;
+ afecmd_rd.port_id = RT_PROXY_PORT_001_RX;
+ afecmd_rd.bytes_avail = bytes;
+ afecmd_rd.rsvd = 0;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &afecmd_rd);
+ if (ret < 0) {
+ pr_err("%s: AFE rtproxy read cmd to port 0x%x failed %d\n",
+ __func__, afecmd_rd.port_id, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+static struct dentry *debugfs_afelb_gain;
+
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ pr_info("debug intf %s\n", (char *) file->private_data);
+ return 0;
+}
+
+static int afe_get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (kstrtoul(token, base, &param1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ } else
+ return -EINVAL;
+ }
+ return 0;
+}
+#define AFE_LOOPBACK_ON (1)
+#define AFE_LOOPBACK_OFF (0)
+static ssize_t afe_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char lbuf[32];
+ int rc;
+ unsigned long param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(lb_str, "afe_loopback")) {
+ rc = afe_get_parameters(lbuf, param, 3);
+ if (!rc) {
+ pr_info("%s %lu %lu %lu\n", lb_str, param[0], param[1],
+ param[2]);
+
+ if ((param[0] != AFE_LOOPBACK_ON) && (param[0] !=
+ AFE_LOOPBACK_OFF)) {
+ pr_err("%s: Error, parameter 0 incorrect\n",
+ __func__);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+ if ((afe_validate_port(param[1]) < 0) ||
+ (afe_validate_port(param[2])) < 0) {
+ pr_err("%s: Error, invalid afe port\n",
+ __func__);
+ }
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Error, AFE not opened\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = afe_loopback(param[0], param[1], param[2]);
+ }
+ } else {
+ pr_err("%s: Error, invalid parameters\n", __func__);
+ rc = -EINVAL;
+ }
+
+ } else if (!strcmp(lb_str, "afe_loopback_gain")) {
+ rc = afe_get_parameters(lbuf, param, 2);
+ if (!rc) {
+ pr_info("%s %lu %lu\n", lb_str, param[0], param[1]);
+
+ if (afe_validate_port(param[0]) < 0) {
+ pr_err("%s: Error, invalid afe port\n",
+ __func__);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+
+ if (param[1] > 100) {
+ pr_err("%s: Error, volume shoud be 0 to 100"
+ " percentage param = %lu\n",
+ __func__, param[1]);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+
+ param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Error, AFE not opened\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = afe_loopback_gain(param[0], param[1]);
+ }
+ } else {
+ pr_err("%s: Error, invalid parameters\n", __func__);
+ rc = -EINVAL;
+ }
+ }
+
+afe_error:
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations afe_debug_fops = {
+ .open = afe_debug_open,
+ .write = afe_debug_write
+};
+#endif
+int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain)
+{
+ struct afe_port_sidetone_command cmd_sidetone;
+ int ret = 0;
+
+ pr_info("%s: tx_port_id:%d rx_port_id:%d enable:%d gain:%d\n", __func__,
+ tx_port_id, rx_port_id, enable, gain);
+ cmd_sidetone.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd_sidetone.hdr.pkt_size = sizeof(cmd_sidetone);
+ cmd_sidetone.hdr.src_port = 0;
+ cmd_sidetone.hdr.dest_port = 0;
+ cmd_sidetone.hdr.token = 0;
+ cmd_sidetone.hdr.opcode = AFE_PORT_CMD_SIDETONE_CTL;
+ cmd_sidetone.tx_port_id = tx_port_id;
+ cmd_sidetone.rx_port_id = rx_port_id;
+ cmd_sidetone.gain = gain;
+ cmd_sidetone.enable = enable;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_sidetone);
+ if (ret < 0) {
+ pr_err("%s: AFE sidetone failed for tx_port:%d rx_port:%d\n",
+ __func__, tx_port_id, rx_port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_port_stop_nowait(int port_id)
+{
+ struct afe_port_stop_command stop;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("AFE is already closed\n");
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+
+ if (ret == -ENETRESET) {
+ pr_info("%s: Need to reset, calling APR deregister", __func__);
+ return apr_deregister(this_afe.apr);
+ } else if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: AFE close failed\n", __func__);
+ ret = -EINVAL;
+ }
+
+fail_cmd:
+ return ret;
+
+}
+
+int afe_close(int port_id)
+{
+ struct afe_port_stop_command stop;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("AFE is already closed\n");
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+
+ if ((port_id == RT_PROXY_DAI_001_RX) ||
+ (port_id == RT_PROXY_DAI_002_TX)) {
+ pr_debug("%s: before decrementing pcm_afe_instance %d\n",
+ __func__, pcm_afe_instance[port_id & 0x1]);
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ pcm_afe_instance[port_id & 0x1]--;
+ if (!(pcm_afe_instance[port_id & 0x1] == 0 &&
+ proxy_afe_instance[port_id & 0x1] == 0))
+ return 0;
+ else
+ afe_close_done[port_id & 0x1] = true;
+ }
+
+ if ((port_id == RT_PROXY_DAI_002_RX) ||
+ (port_id == RT_PROXY_DAI_001_TX)) {
+ pr_debug("%s: before decrementing proxy_afe_instance %d\n",
+ __func__, proxy_afe_instance[port_id & 0x1]);
+ port_id = VIRTUAL_ID_TO_PORTID(port_id);
+ proxy_afe_instance[port_id & 0x1]--;
+ if (!(pcm_afe_instance[port_id & 0x1] == 0 &&
+ proxy_afe_instance[port_id & 0x1] == 0))
+ return 0;
+ else
+ afe_close_done[port_id & 0x1] = true;
+ }
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+
+ if (ret == -ENETRESET) {
+ pr_info("%s: Need to reset, calling APR deregister", __func__);
+ return apr_deregister(this_afe.apr);
+ }
+
+ if (ret < 0) {
+ pr_err("%s: AFE close failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+fail_cmd:
+ return ret;
+}
+
+static int __init afe_init(void)
+{
+ init_waitqueue_head(&this_afe.wait);
+ atomic_set(&this_afe.state, 0);
+ atomic_set(&this_afe.status, 0);
+ this_afe.apr = NULL;
+#ifdef CONFIG_DEBUG_FS
+ debugfs_afelb = debugfs_create_file("afe_loopback",
+ 0220, NULL, (void *) "afe_loopback",
+ &afe_debug_fops);
+
+ debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain",
+ 0220, NULL, (void *) "afe_loopback_gain",
+ &afe_debug_fops);
+
+
+#endif
+ return 0;
+}
+
+static void __exit afe_exit(void)
+{
+ int i;
+#ifdef CONFIG_DEBUG_FS
+ if (debugfs_afelb)
+ debugfs_remove(debugfs_afelb);
+ if (debugfs_afelb_gain)
+ debugfs_remove(debugfs_afelb_gain);
+#endif
+ for (i = 0; i < MAX_AUDPROC_TYPES; i++) {
+ if (afe_cal_addr[i].cal_paddr != 0)
+ afe_cmd_memory_unmap_nowait(
+ afe_cal_addr[i].cal_paddr);
+ }
+}
+
+device_initcall(afe_init);
+__exitcall(afe_exit);
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
new file mode 100644
index 0000000000000..7fc06ae03af65
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -0,0 +1,3841 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * 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/kernel.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <linux/debugfs.h>
+#include <linux/time.h>
+#include <linux/atomic.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/ioctls.h>
+
+#include <sound/qdsp6v2/audio_acdb.h>
+#include <sound/qdsp6v2/rtac.h>
+
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+
+
+#define TRUE 0x01
+#define FALSE 0x00
+#define READDONE_IDX_STATUS 0
+#define READDONE_IDX_BUFFER 1
+#define READDONE_IDX_SIZE 2
+#define READDONE_IDX_OFFSET 3
+#define READDONE_IDX_MSW_TS 4
+#define READDONE_IDX_LSW_TS 5
+#define READDONE_IDX_FLAGS 6
+#define READDONE_IDX_NUMFRAMES 7
+#define READDONE_IDX_ID 8
+#ifdef CONFIG_DEBUG_FS
+#define OUT_BUFFER_SIZE 56
+#define IN_BUFFER_SIZE 24
+#endif
+static DEFINE_MUTEX(session_lock);
+
+/* session id: 0 reserved */
+static struct audio_client *session[SESSION_MAX+1];
+static int32_t q6asm_mmapcallback(struct apr_client_data *data, void *priv);
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv);
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg);
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg);
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt);
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt);
+
+static void q6asm_reset_buf_state(struct audio_client *ac);
+
+#ifdef CONFIG_DEBUG_FS
+static struct timeval out_cold_tv;
+static struct timeval out_warm_tv;
+static struct timeval out_cont_tv;
+static struct timeval in_cont_tv;
+static long out_enable_flag;
+static long in_enable_flag;
+static struct dentry *out_dentry;
+static struct dentry *in_dentry;
+static int in_cont_index;
+/*This var is used to keep track of first write done for cold output latency */
+static int out_cold_index;
+static char *out_buffer;
+static char *in_buffer;
+static int audio_output_latency_dbgfs_open(struct inode *inode,
+ struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+static ssize_t audio_output_latency_dbgfs_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,",\
+ out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec,\
+ out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec);
+ return simple_read_from_buffer(buf, OUT_BUFFER_SIZE, ppos,
+ out_buffer, OUT_BUFFER_SIZE);
+}
+static ssize_t audio_output_latency_dbgfs_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ char *temp;
+
+ if (count > 2*sizeof(char))
+ return -EINVAL;
+ else
+ temp = kmalloc(2*sizeof(char), GFP_KERNEL);
+
+ out_cold_index = 0;
+
+ if (temp) {
+ if (copy_from_user(temp, buf, 2*sizeof(char))) {
+ kfree(temp);
+ return -EFAULT;
+ }
+ if (!kstrtol(temp, 10, &out_enable_flag)) {
+ kfree(temp);
+ return count;
+ }
+ kfree(temp);
+ }
+ return -EINVAL;
+}
+static const struct file_operations audio_output_latency_debug_fops = {
+ .open = audio_output_latency_dbgfs_open,
+ .read = audio_output_latency_dbgfs_read,
+ .write = audio_output_latency_dbgfs_write
+};
+
+static int audio_input_latency_dbgfs_open(struct inode *inode,
+ struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+static ssize_t audio_input_latency_dbgfs_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,",\
+ in_cont_tv.tv_sec, in_cont_tv.tv_usec);
+ return simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos,
+ in_buffer, IN_BUFFER_SIZE);
+}
+static ssize_t audio_input_latency_dbgfs_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ char *temp;
+
+ if (count > 2*sizeof(char))
+ return -EINVAL;
+ else
+ temp = kmalloc(2*sizeof(char), GFP_KERNEL);
+ if (temp) {
+ if (copy_from_user(temp, buf, 2*sizeof(char))) {
+ kfree(temp);
+ return -EFAULT;
+ }
+ if (!kstrtol(temp, 10, &in_enable_flag)) {
+ kfree(temp);
+ return count;
+ }
+ kfree(temp);
+ }
+ return -EINVAL;
+}
+static const struct file_operations audio_input_latency_debug_fops = {
+ .open = audio_input_latency_dbgfs_open,
+ .read = audio_input_latency_dbgfs_read,
+ .write = audio_input_latency_dbgfs_write
+};
+#endif
+struct asm_mmap {
+ atomic_t ref_cnt;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+ void *apr;
+};
+
+static struct asm_mmap this_mmap;
+
+static int q6asm_session_alloc(struct audio_client *ac)
+{
+ int n;
+ mutex_lock(&session_lock);
+ for (n = 1; n <= SESSION_MAX; n++) {
+ if (!session[n]) {
+ session[n] = ac;
+ mutex_unlock(&session_lock);
+ return n;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void q6asm_session_free(struct audio_client *ac)
+{
+ pr_debug("%s: sessionid[%d]\n", __func__, ac->session);
+ rtac_remove_popp_from_adm_devices(ac->session);
+ mutex_lock(&session_lock);
+ session[ac->session] = 0;
+ mutex_unlock(&session_lock);
+ ac->session = 0;
+ ac->perf_mode = false;
+ return;
+}
+
+int q6asm_audio_client_buf_free(unsigned int dir,
+ struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+ if (!port->buf) {
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (cnt >= 0) {
+ rc = q6asm_memory_unmap_regions(ac, dir,
+ port->buf[0].size,
+ port->max_buf_cnt);
+ if (rc < 0)
+ pr_err("%s CMD Memory_unmap_regions failed\n",
+ __func__);
+ }
+
+ while (cnt >= 0) {
+ if (port->buf[cnt].data) {
+ pr_debug("%s:data[%p]phys[%p][%p] cnt[%d] mem_buffer[%p]\n",
+ __func__, (void *)port->buf[cnt].data,
+ (void *)port->buf[cnt].phys,
+ (void *)&port->buf[cnt].phys, cnt,
+ (void *)port->buf[cnt].mem_buffer);
+
+ dma_free_writecombine(NULL, port->buf[cnt].size, port->buf[cnt].data, port->buf[cnt].phys);
+
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ --(port->max_buf_cnt);
+ }
+ --cnt;
+ }
+ kfree(port->buf);
+ port->buf = NULL;
+ }
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
+ struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ port = &ac->port[dir];
+ if (!port->buf) {
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (cnt >= 0) {
+ rc = q6asm_memory_unmap(ac, port->buf[0].phys, dir);
+ if (rc < 0)
+ pr_err("%s CMD Memory_unmap_regions failed\n",
+ __func__);
+ }
+
+ if (port->buf[0].data) {
+ pr_debug("%s:data[%p]phys[%p][%p] mem_buffer[%p]\n",
+ __func__,
+ (void *)port->buf[0].data,
+ (void *)port->buf[0].phys,
+ (void *)&port->buf[0].phys,
+ (void *)port->buf[0].mem_buffer);
+
+
+ dma_free_writecombine(NULL, port->max_buf_cnt * port->buf[0].size,
+ port->buf[0].data, port->buf[0].phys);
+ }
+
+ while (cnt >= 0) {
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ cnt--;
+ }
+ port->max_buf_cnt = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+void q6asm_audio_client_free(struct audio_client *ac)
+{
+ int loopcnt;
+ struct audio_port_data *port;
+ if (!ac || !ac->session)
+ return;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ if (!port->buf)
+ continue;
+ pr_debug("%s:loopcnt = %d\n", __func__, loopcnt);
+ q6asm_audio_client_buf_free(loopcnt, ac);
+ }
+ }
+
+ apr_deregister(ac->apr);
+ q6asm_session_free(ac);
+
+ 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:
+ kfree(ac);
+ return;
+}
+
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode)
+{
+ if (ac == NULL) {
+ pr_err("%s APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mode == ASYNC_IO_MODE) {
+ ac->io_mode &= ~SYNC_IO_MODE;
+ ac->io_mode |= ASYNC_IO_MODE;
+ } else if (mode == SYNC_IO_MODE) {
+ ac->io_mode &= ~ASYNC_IO_MODE;
+ ac->io_mode |= SYNC_IO_MODE;
+ } else {
+ pr_err("%s:Not an valid IO Mode:%d\n", __func__, ac->io_mode);
+ return -EINVAL;
+ }
+
+ pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode);
+ return 0;
+}
+
+struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
+{
+ struct audio_client *ac;
+ int n;
+ int lcnt = 0;
+
+ ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL);
+ if (!ac)
+ return NULL;
+ n = q6asm_session_alloc(ac);
+ if (n <= 0)
+ goto fail_session;
+ ac->session = n;
+ ac->cb = cb;
+ ac->priv = priv;
+ ac->io_mode = SYNC_IO_MODE;
+ ac->perf_mode = false;
+ ac->apr = apr_register("ADSP", "ASM", \
+ (apr_fn)q6asm_callback,\
+ ((ac->session) << 8 | 0x0001),\
+ ac);
+
+ if (ac->apr == NULL) {
+ pr_err("%s Registration with APR failed\n", __func__);
+ goto fail;
+ }
+ rtac_set_asm_handle(n, ac->apr);
+
+ 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", "ASM", \
+ (apr_fn)q6asm_mmapcallback,\
+ 0x0FFFFFFFF, &this_mmap);
+ if (this_mmap.apr == NULL) {
+ pr_debug("%s Unable to register APR ASM common port\n",
+ __func__);
+ goto fail;
+ }
+ }
+
+ atomic_inc(&this_mmap.ref_cnt);
+ init_waitqueue_head(&ac->cmd_wait);
+ init_waitqueue_head(&ac->time_wait);
+ atomic_set(&ac->time_flag, 1);
+ mutex_init(&ac->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; lcnt++) {
+ mutex_init(&ac->port[lcnt].lock);
+ spin_lock_init(&ac->port[lcnt].dsp_lock);
+ }
+ atomic_set(&ac->cmd_state, 0);
+ atomic_set(&ac->cmd_response, 0);
+
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ return ac;
+fail:
+ q6asm_audio_client_free(ac);
+ return NULL;
+fail_session:
+ kfree(ac);
+ return NULL;
+}
+
+struct audio_client *q6asm_get_audio_client(int session_id)
+{
+ if ((session_id <= 0) || (session_id > SESSION_MAX)) {
+ pr_err("%s: invalid session: %d\n", __func__, session_id);
+ goto err;
+ }
+
+ if (!session[session_id]) {
+ pr_err("%s: session not active: %d\n", __func__, session_id);
+ goto err;
+ }
+
+ return session[session_id];
+err:
+ return NULL;
+}
+
+int q6asm_audio_client_buf_alloc(unsigned int dir,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct audio_buffer *buf;
+
+ if (!(ac) || ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session,
+ bufsz, bufcnt);
+
+ if (ac->session <= 0 || ac->session > 8)
+ goto fail;
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ if (ac->port[dir].buf) {
+ pr_debug("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+ mutex_lock(&ac->cmd_lock);
+ buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ if (!buf[cnt].data) {
+ buf[cnt].size = bufsz;
+ buf[cnt].data = dma_alloc_writecombine(NULL, bufsz,
+ &buf[cnt].phys, GFP_KERNEL);
+ if (WARN_ON(IS_ERR_OR_NULL(buf[0].data))) {
+ pr_err("%s: allocation failed\n", __func__);
+ goto fail;
+ }
+
+
+ buf[cnt].used = 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s data[%p]phys[%p][%p]\n",
+ __func__,
+ (void *)buf[cnt].data,
+ (void *)buf[cnt].phys,
+ (void *)&buf[cnt].phys);
+ cnt++;
+ }
+ }
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+
+ mutex_unlock(&ac->cmd_lock);
+ rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt);
+ if (rc < 0) {
+ pr_err("%s:CMD Memory_map_regions failed\n", __func__);
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ q6asm_audio_client_buf_free(dir, ac);
+ return -EINVAL;
+}
+
+int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct audio_buffer *buf;
+ if (!(ac) || ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n",
+ __func__, ac->session,
+ bufsz, bufcnt);
+
+ if (ac->session <= 0 || ac->session > 8)
+ goto fail;
+
+ if (ac->port[dir].buf) {
+ pr_debug("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+ mutex_lock(&ac->cmd_lock);
+ buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+
+ buf[0].size = bufsz * bufcnt;
+ buf[0].data = dma_alloc_writecombine(NULL, bufsz *bufcnt,
+ &buf[0].phys, GFP_KERNEL);
+ if (WARN_ON(IS_ERR_OR_NULL(buf[0].data))) {
+ pr_err("%s: allocation failed\n", __func__);
+ goto fail;
+ }
+
+ if (!buf[0].data) {
+ pr_err("%s:invalid vaddr, iomap failed\n", __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ buf[0].used = dir ^ 1;
+ buf[0].size = bufsz;
+ buf[0].actual_size = bufsz;
+ cnt = 1;
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ buf[cnt].data = buf[0].data + (cnt * bufsz);
+ buf[cnt].phys = buf[0].phys + (cnt * bufsz);
+ if (!buf[cnt].data) {
+ pr_err("%s Buf alloc failed\n",
+ __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ buf[cnt].used = dir ^ 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s data[%p]phys[%p][%p]\n", __func__,
+ (void *)buf[cnt].data,
+ (void *)buf[cnt].phys,
+ (void *)&buf[cnt].phys);
+ }
+ cnt++;
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+
+ pr_debug("%s ac->port[%d].max_buf_cnt[%d]\n", __func__, dir,
+ ac->port[dir].max_buf_cnt);
+ mutex_unlock(&ac->cmd_lock);
+ rc = q6asm_memory_map(ac, buf[0].phys, dir, bufsz, cnt);
+ if (rc < 0) {
+ pr_err("%s:CMD Memory_map_regions failed\n", __func__);
+ goto fail;
+ }
+ return 0;
+fail:
+ q6asm_audio_client_buf_free_contiguous(dir, ac);
+ return -EINVAL;
+}
+
+static int32_t q6asm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ uint32_t token;
+ uint32_t *payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event is received: %d %d apr[%p]\n",
+ __func__,
+ data->reset_event,
+ data->reset_proc,
+ this_mmap.apr);
+ apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+ atomic_set(&this_mmap.cmd_state, 0);
+ return 0;
+ }
+
+ pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]\n",
+ __func__, payload[0], payload[1], data->opcode, data->token,
+ data->payload_size, data->src_port, data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ token = data->token;
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_MEMORY_MAP:
+ case ASM_SESSION_CMD_MEMORY_UNMAP:
+ case ASM_SESSION_CMD_MEMORY_MAP_REGIONS:
+ case ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS:
+ pr_debug("%s:command[0x%x]success [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ break;
+ default:
+ pr_debug("%s:command[0x%x] not expecting rsp\n",
+ __func__, payload[0]);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int32_t is_no_wait_cmd_rsp(uint32_t opcode, uint32_t *cmd_type)
+{
+ if (opcode == APR_BASIC_RSP_RESULT) {
+ if (cmd_type != NULL) {
+ switch (cmd_type[0]) {
+ case ASM_SESSION_CMD_RUN:
+ case ASM_SESSION_CMD_PAUSE:
+ case ASM_DATA_CMD_EOS:
+ return 1;
+ default:
+ break;
+ }
+ } else
+ pr_err("%s: null pointer!", __func__);
+ } else if (opcode == ASM_DATA_CMDRSP_EOS)
+ return 1;
+
+ return 0;
+}
+
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
+{
+ int i = 0;
+ struct audio_client *ac = (struct audio_client *)priv;
+ uint32_t token;
+ unsigned long dsp_flags;
+ uint32_t *payload;
+ uint32_t wakeup_flag = 1;
+
+
+ if ((ac == NULL) || (data == NULL)) {
+ pr_err("ac or priv NULL\n");
+ return -EINVAL;
+ }
+ if (ac->session <= 0 || ac->session > 8) {
+ pr_err("%s:Session ID is invalid, session = %d\n", __func__,
+ ac->session);
+ return -EINVAL;
+ }
+
+ payload = data->payload;
+ if ((atomic_read(&ac->nowait_cmd_cnt) > 0) &&
+ is_no_wait_cmd_rsp(data->opcode, payload)) {
+ pr_debug("%s: nowait_cmd_cnt %d\n",
+ __func__,
+ atomic_read(&ac->nowait_cmd_cnt));
+ atomic_dec(&ac->nowait_cmd_cnt);
+ wakeup_flag = 0;
+ }
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("q6asm_callback: Reset event is received: %d %d apr[%p]\n",
+ data->reset_event, data->reset_proc, ac->apr);
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ (uint32_t *)data->payload, ac->priv);
+ apr_reset(ac->apr);
+ return 0;
+ }
+
+ pr_debug("%s: session[%d]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]\n",
+ __func__,
+ ac->session, data->opcode,
+ data->token, data->payload_size, data->src_port,
+ data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ token = data->token;
+ pr_debug("%s payload[0]:%x", __func__, payload[0]);
+ switch (payload[0]) {
+ case ASM_STREAM_CMD_SET_PP_PARAMS:
+ if (rtac_make_asm_callback(ac->session, payload,
+ data->payload_size))
+ break;
+ case ASM_SESSION_CMD_PAUSE:
+ case ASM_DATA_CMD_EOS:
+ case ASM_STREAM_CMD_CLOSE:
+ case ASM_STREAM_CMD_FLUSH:
+ case ASM_SESSION_CMD_RUN:
+ case ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS:
+ case ASM_STREAM_CMD_FLUSH_READBUFS:
+ pr_debug("%s:Payload = [0x%x]\n", __func__, payload[0]);
+ if (token != ac->session) {
+ pr_err("%s:Invalid session[%d] rxed expected[%d]",
+ __func__, token, ac->session);
+ return -EINVAL;
+ }
+ case ASM_STREAM_CMD_OPEN_READ:
+ case ASM_STREAM_CMD_OPEN_READ_V2_1:
+ case ASM_STREAM_CMD_OPEN_WRITE:
+ case ASM_STREAM_CMD_OPEN_WRITE_V2_1:
+ case ASM_STREAM_CMD_OPEN_READWRITE:
+ case ASM_STREAM_CMD_OPEN_LOOPBACK:
+ case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
+ case ASM_STREAM_CMD_OPEN_READ_COMPRESSED:
+ if (payload[0] == ASM_STREAM_CMD_CLOSE) {
+ atomic_set(&ac->cmd_close_state, 0);
+ wake_up(&ac->cmd_wait);
+ } else if (atomic_read(&ac->cmd_state) &&
+ wakeup_flag) {
+ atomic_set(&ac->cmd_state, 0);
+ if (payload[1] == ADSP_EUNSUPPORTED) {
+ pr_debug("paload[1]:%d unsupported",
+ payload[1]);
+ atomic_set(&ac->cmd_response, 1);
+ }
+ else
+ atomic_set(&ac->cmd_response, 0);
+ wake_up(&ac->cmd_wait);
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ (uint32_t *)data->payload, ac->priv);
+ break;
+ default:
+ pr_debug("%s:command[0x%x] not expecting rsp\n",
+ __func__, payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE:{
+ struct audio_port_data *port = &ac->port[IN];
+ pr_debug("%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
+ __func__, payload[0], payload[1],
+ data->token);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ if (port->buf == NULL) {
+ pr_err("%s: Unexpected Write Done\n",
+ __func__);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (port->buf[data->token].phys !=
+ payload[0]) {
+ pr_err("Buf expected[%p]rxed[%p]\n",\
+ (void *)port->buf[data->token].phys,\
+ (void *)payload[0]);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ return -EINVAL;
+ }
+ token = data->token;
+ port->buf[token].used = 1;
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+#ifdef CONFIG_DEBUG_FS
+ if (out_enable_flag) {
+ /* For first Write done log the time and reset
+ out_cold_index*/
+ if (out_cold_index != 1) {
+ do_gettimeofday(&out_cold_tv);
+ pr_debug("COLD: apr_send_pkt at %ld sec %ld microsec\n",
+ out_cold_tv.tv_sec,
+ out_cold_tv.tv_usec);
+ out_cold_index = 1;
+ }
+ pr_debug("out_enable_flag %ld",\
+ out_enable_flag);
+ }
+#endif
+ for (i = 0; i < port->max_buf_cnt; i++)
+ pr_debug("%d ", port->buf[i].used);
+
+ }
+ break;
+ }
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
+ rtac_make_asm_callback(ac->session, payload,
+ data->payload_size);
+ break;
+ case ASM_DATA_EVENT_READ_DONE:{
+
+ struct audio_port_data *port = &ac->port[OUT];
+#ifdef CONFIG_DEBUG_FS
+ if (in_enable_flag) {
+ /* when in_cont_index == 7, DSP would be
+ * writing into the 8th 512 byte buffer and this
+ * timestamp is tapped here.Once done it then writes
+ * to 9th 512 byte buffer.These two buffers(8th, 9th)
+ * reach the test application in 5th iteration and that
+ * timestamp is tapped at user level. The difference
+ * of these two timestamps gives us the time between
+ * the time at which dsp started filling the sample
+ * required and when it reached the test application.
+ * Hence continuous input latency
+ */
+ if (in_cont_index == 7) {
+ do_gettimeofday(&in_cont_tv);
+ pr_err("In_CONT:previous read buffer done at %ld sec %ld microsec\n",
+ in_cont_tv.tv_sec, in_cont_tv.tv_usec);
+ }
+ }
+#endif
+ pr_debug("%s:R-D: status=%d buff_add=%x act_size=%d offset=%d\n",
+ __func__, payload[READDONE_IDX_STATUS],
+ payload[READDONE_IDX_BUFFER],
+ payload[READDONE_IDX_SIZE],
+ payload[READDONE_IDX_OFFSET]);
+ pr_debug("%s:R-D:msw_ts=%d lsw_ts=%d flags=%d id=%d num=%d\n",
+ __func__, payload[READDONE_IDX_MSW_TS],
+ payload[READDONE_IDX_LSW_TS],
+ payload[READDONE_IDX_FLAGS],
+ payload[READDONE_IDX_ID],
+ payload[READDONE_IDX_NUMFRAMES]);
+#ifdef CONFIG_DEBUG_FS
+ if (in_enable_flag)
+ in_cont_index++;
+#endif
+ if (ac->io_mode & SYNC_IO_MODE) {
+ if (port->buf == NULL) {
+ pr_err("%s: Unexpected Write Done\n", __func__);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ token = data->token;
+ port->buf[token].used = 0;
+ if (port->buf[token].phys !=
+ payload[READDONE_IDX_BUFFER]) {
+ pr_err("Buf expected[%p]rxed[%p]\n",\
+ (void *)port->buf[token].phys,\
+ (void *)payload[READDONE_IDX_BUFFER]);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ }
+ port->buf[token].actual_size =
+ payload[READDONE_IDX_SIZE];
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_EOS:
+ case ASM_DATA_CMDRSP_EOS:
+ pr_debug("%s:EOS ACK received: rxed opcode[0x%x]\n",
+ __func__, data->opcode);
+ break;
+ case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
+ break;
+ case ASM_SESSION_EVENT_TX_OVERFLOW:
+ pr_err("ASM_SESSION_EVENT_TX_OVERFLOW\n");
+ break;
+ case ASM_SESSION_CMDRSP_GET_SESSION_TIME:
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSION_TIME, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
+ __func__,
+ payload[0], payload[1], payload[2]);
+ ac->time_stamp = (uint64_t)(((uint64_t)payload[1] << 32) |
+ payload[2]);
+ if (atomic_read(&ac->time_flag)) {
+ atomic_set(&ac->time_flag, 0);
+ wake_up(&ac->time_wait);
+ }
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
+ pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
+ __func__,
+ payload[0], payload[1], payload[2],
+ payload[3]);
+ break;
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ data->payload, ac->priv);
+
+ return 0;
+}
+
+void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size,
+ uint32_t *index)
+{
+ void *data;
+ unsigned char idx;
+ struct audio_port_data *port;
+
+ if (!ac || ((dir != IN) && (dir != OUT)))
+ return NULL;
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+
+ mutex_lock(&port->lock);
+ idx = port->cpu_buf;
+ if (port->buf == NULL) {
+ pr_debug("%s:Buffer pointer null\n", __func__);
+ mutex_unlock(&port->lock);
+ return NULL;
+ }
+ /* dir 0: used = 0 means buf in use
+ dir 1: used = 1 means buf in use */
+ if (port->buf[idx].used == dir) {
+ /* To make it more robust, we could loop and get the
+ next avail buf, its risky though */
+ pr_debug("%s:Next buf idx[0x%x] not available,dir[%d]\n",
+ __func__, idx, dir);
+ mutex_unlock(&port->lock);
+ return NULL;
+ }
+ *size = port->buf[idx].actual_size;
+ *index = port->cpu_buf;
+ data = port->buf[idx].data;
+ pr_debug("%s:session[%d]index[%d] data[%p]size[%d]\n",
+ __func__,
+ ac->session,
+ port->cpu_buf,
+ data, *size);
+ /* By default increase the cpu_buf cnt
+ user accesses this function,increase cpu
+ buf(to avoid another api)*/
+ port->buf[idx].used = dir;
+ port->cpu_buf = ((port->cpu_buf + 1) & (port->max_buf_cnt - 1));
+ mutex_unlock(&port->lock);
+ return data;
+ }
+ return NULL;
+}
+
+void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
+ uint32_t *size, uint32_t *index)
+{
+ void *data;
+ unsigned char idx;
+ struct audio_port_data *port;
+
+ if (!ac || ((dir != IN) && (dir != OUT)))
+ return NULL;
+
+ port = &ac->port[dir];
+
+ idx = port->cpu_buf;
+ if (port->buf == NULL) {
+ pr_debug("%s:Buffer pointer null\n", __func__);
+ return NULL;
+ }
+ /*
+ * dir 0: used = 0 means buf in use
+ * dir 1: used = 1 means buf in use
+ */
+ if (port->buf[idx].used == dir) {
+ /*
+ * To make it more robust, we could loop and get the
+ * next avail buf, its risky though
+ */
+ pr_debug("%s:Next buf idx[0x%x] not available, dir[%d]\n",
+ __func__, idx, dir);
+ return NULL;
+ }
+ *size = port->buf[idx].actual_size;
+ *index = port->cpu_buf;
+ data = port->buf[idx].data;
+ pr_debug("%s:session[%d]index[%d] data[%p]size[%d]\n",
+ __func__, ac->session, port->cpu_buf,
+ data, *size);
+ /*
+ * By default increase the cpu_buf cnt
+ * user accesses this function,increase cpu
+ * buf(to avoid another api)
+ */
+ port->buf[idx].used = dir;
+ port->cpu_buf = ((port->cpu_buf + 1) & (port->max_buf_cnt - 1));
+ return data;
+}
+
+int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac)
+{
+ int ret = -1;
+ struct audio_port_data *port;
+ uint32_t idx;
+
+ if (!ac || (dir != OUT))
+ return ret;
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+
+ mutex_lock(&port->lock);
+ idx = port->dsp_buf;
+
+ if (port->buf[idx].used == (dir ^ 1)) {
+ /* To make it more robust, we could loop and get the
+ next avail buf, its risky though */
+ pr_err("Next buf idx[0x%x] not available, dir[%d]\n",
+ idx, dir);
+ mutex_unlock(&port->lock);
+ return ret;
+ }
+ pr_debug("%s: session[%d]dsp_buf=%d cpu_buf=%d\n", __func__,
+ ac->session, port->dsp_buf, port->cpu_buf);
+ ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1);
+ mutex_unlock(&port->lock);
+ }
+ return ret;
+}
+
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg)
+{
+ pr_debug("%s:session=%d pkt size=%d cmd_flg=%d\n", __func__, pkt_size,
+ cmd_flg, ac->session);
+ mutex_lock(&ac->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 *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ if (cmd_flg) {
+ hdr->token = ac->session;
+ atomic_set(&ac->cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ mutex_unlock(&ac->cmd_lock);
+ return;
+}
+
+static void q6asm_add_mmaphdr(struct apr_hdr *hdr, uint32_t pkt_size,
+ uint32_t cmd_flg)
+{
+ pr_debug("%s:pkt size=%d cmd_flg=%d\n", __func__, pkt_size, cmd_flg);
+ 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 = 0;
+ atomic_set(&this_mmap.cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+int q6asm_open_read(struct audio_client *ac,
+ uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read open;
+#ifdef CONFIG_DEBUG_FS
+ in_cont_index = 0;
+#endif
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ;
+ /* Stream prio : High, provide meta info with encoded frames */
+ open.src_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+
+ open.pre_proc_top = get_asm_topology();
+ if (open.pre_proc_top == 0)
+ open.pre_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = LINEAR_PCM;
+ break;
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = MULTI_CHANNEL_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_V13K:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRWB_FS;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", format);
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+
+ ac->io_mode |= TUN_READ_IO_MODE;
+
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_read_v2_1(struct audio_client *ac,
+ uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_v2_1 open;
+#ifdef CONFIG_DEBUG_FS
+ in_cont_index = 0;
+#endif
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V2_1;
+ open.src_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.pre_proc_top = get_asm_topology();
+ if (open.pre_proc_top == 0)
+ open.pre_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = LINEAR_PCM;
+ break;
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = MULTI_CHANNEL_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_V13K:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRWB_FS;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", format);
+ goto fail_cmd;
+ }
+ open.uMode = ASM_OPEN_READ_PERF_MODE_BIT;
+ open.bits_per_sample = PCM_BITS_PER_SAMPLE;
+ open.reserved = 0;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+
+int q6asm_open_read_compressed(struct audio_client *ac,
+ uint32_t frames_per_buffer, uint32_t meta_data_mode)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_compressed open;
+#ifdef CONFIG_DEBUG_FS
+ in_cont_index = 0;
+#endif
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_COMPRESSED;
+ /* hardcoded as following*/
+ open.frame_per_buf = frames_per_buffer;
+ open.uMode = meta_data_mode;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_READ_COMPRESSED rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_write_compressed open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session,
+ format);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED;
+
+ switch (format) {
+ case FORMAT_AC3:
+ open.format = AC3_DECODER;
+ break;
+ case FORMAT_EAC3:
+ open.format = EAC3_DECODER;
+ break;
+ case FORMAT_MP3:
+ open.format = MP3;
+ break;
+ case FORMAT_DTS:
+ open.format = DTS;
+ break;
+ case FORMAT_DTS_LBR:
+ open.format = DTS_LBR;
+ break;
+ case FORMAT_AAC:
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_ATRAC:
+ open.format = ATRAC;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.format = WMA_V10PRO;
+ break;
+ case FORMAT_MAT:
+ open.format = MAT;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, format);
+ goto fail_cmd;
+ }
+ /*Below flag indicates the DSP that Compressed audio input
+ stream is not IEC 61937 or IEC 60958 packetizied*/
+ open.flags = 0x00000000;
+ rc = apr_send_pkt(ac->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(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_write(struct audio_client *ac, uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_write open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session,
+ format);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+
+ if (ac->perf_mode) {
+ pr_debug("%s In Performance/lowlatency mode", __func__);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V2_1;
+ open.uMode = ASM_OPEN_WRITE_PERF_MODE_BIT;
+ /* source endpoint : matrix */
+ open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.stream_handle = PCM_BITS_PER_SAMPLE;
+ } else {
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE;
+ open.uMode = STREAM_PRIORITY_HIGH;
+ /* source endpoint : matrix */
+ open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.stream_handle = 0x00;
+ }
+ open.post_proc_top = get_asm_topology();
+ if (open.post_proc_top == 0)
+ open.post_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.format = LINEAR_PCM;
+ break;
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.format = MULTI_CHANNEL_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_MPEG4_MULTI_AAC:
+ open.format = MPEG4_MULTI_AAC;
+ break;
+ case FORMAT_WMA_V9:
+ open.format = WMA_V9;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.format = WMA_V10PRO;
+ break;
+ case FORMAT_MP3:
+ open.format = MP3;
+ break;
+ case FORMAT_DTS:
+ open.format = DTS;
+ break;
+ case FORMAT_DTS_LBR:
+ open.format = DTS_LBR;
+ break;
+ case FORMAT_AMRWB:
+ open.format = AMRWB_FS;
+ pr_debug("q6asm_open_write FORMAT_AMRWB");
+ break;
+ case FORMAT_AMR_WB_PLUS:
+ open.format = AMR_WB_PLUS;
+ pr_debug("q6asm_open_write FORMAT_AMR_WB_PLUS");
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, format);
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->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(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_response)) {
+ pr_err("%s: format = %x not supported\n", __func__, format);
+ goto fail_cmd;
+ }
+
+ ac->io_mode |= TUN_WRITE_IO_MODE;
+
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_read_write(struct audio_client *ac,
+ uint32_t rd_format,
+ uint32_t wr_format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_write open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, ac->session);
+ pr_debug("wr_format[0x%x]rd_format[0x%x]",
+ wr_format, rd_format);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE;
+
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_NORMAL;
+ /* source endpoint : matrix */
+ open.post_proc_top = get_asm_topology();
+ if (open.post_proc_top == 0)
+ open.post_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (wr_format) {
+ case FORMAT_LINEAR_PCM:
+ open.write_format = LINEAR_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.write_format = MPEG4_AAC;
+ break;
+ case FORMAT_MPEG4_MULTI_AAC:
+ open.write_format = MPEG4_MULTI_AAC;
+ break;
+ case FORMAT_WMA_V9:
+ open.write_format = WMA_V9;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.write_format = WMA_V10PRO;
+ break;
+ case FORMAT_AMRNB:
+ open.write_format = AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.write_format = AMRWB_FS;
+ break;
+ case FORMAT_AMR_WB_PLUS:
+ open.write_format = AMR_WB_PLUS;
+ break;
+ case FORMAT_V13K:
+ open.write_format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.write_format = EVRC_FS;
+ break;
+ case FORMAT_EVRCB:
+ open.write_format = EVRCB_FS;
+ break;
+ case FORMAT_EVRCWB:
+ open.write_format = EVRCWB_FS;
+ break;
+ case FORMAT_MP3:
+ open.write_format = MP3;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", wr_format);
+ goto fail_cmd;
+ }
+
+ switch (rd_format) {
+ case FORMAT_LINEAR_PCM:
+ open.read_format = LINEAR_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.read_format = MPEG4_AAC;
+ break;
+ case FORMAT_V13K:
+ open.read_format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.read_format = EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.read_format = AMRNB_FS;
+ break;
+ case FORMAT_AMRWB:
+ open.read_format = AMRWB_FS;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", rd_format);
+ goto fail_cmd;
+ }
+ pr_debug("%s:rdformat[0x%x]wrformat[0x%x]\n", __func__,
+ open.read_format, open.write_format);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for OPEN_WRITE rc[%d]\n", rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_loopack(struct audio_client *ac)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_loopback open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK;
+
+ open.mode_flags = 0;
+ open.src_endpointype = 0;
+ open.sink_endpointype = 0;
+ /* source endpoint : matrix */
+ open.postprocopo_id = get_asm_topology();
+ if (open.postprocopo_id == 0)
+ open.postprocopo_id = DEFAULT_POPP_TOPOLOGY;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for OPEN_WRITE rc[%d]\n", rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct asm_stream_cmd_run run;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s session[%d]", __func__, ac->session);
+ q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE);
+
+ run.hdr.opcode = ASM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+#ifdef CONFIG_DEBUG_FS
+ if (out_enable_flag) {
+ do_gettimeofday(&out_cold_tv);
+ pr_debug("COLD: apr_send_pkt at %ld sec %ld microsec\n",\
+ out_cold_tv.tv_sec, out_cold_tv.tv_usec);
+ }
+#endif
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("Commmand run failed[%d]", rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for run success rc[%d]", rc);
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct asm_stream_cmd_run run;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s:APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("session[%d]", ac->session);
+ q6asm_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE);
+
+ run.hdr.opcode = ASM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s:Commmand run failed[%d]", __func__, rc);
+ return -EINVAL;
+ }
+ atomic_inc(&ac->nowait_cmd_cnt);
+ return 0;
+}
+
+
+int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
+ uint32_t frames_per_buf,
+ uint32_t sample_rate, uint32_t channels,
+ uint32_t bit_rate, uint32_t mode, uint32_t format)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d] format[%d]",
+ __func__, ac->session, frames_per_buf,
+ sample_rate, channels, bit_rate, mode, format);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = MPEG4_AAC;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_aac_read_cfg);
+ enc_cfg.enc_blk.cfg.aac.bitrate = bit_rate;
+ enc_cfg.enc_blk.cfg.aac.enc_mode = mode;
+ enc_cfg.enc_blk.cfg.aac.format = format;
+ enc_cfg.enc_blk.cfg.aac.ch_cfg = channels;
+ enc_cfg.enc_blk.cfg.aac.sample_rate = sample_rate;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+ ac->session, rate, channels);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+ enc_cfg.enc_blk.frames_per_buf = 1;
+ enc_cfg.enc_blk.format_id = LINEAR_PCM;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_pcm_cfg);
+ enc_cfg.enc_blk.cfg.pcm.ch_cfg = channels;
+ enc_cfg.enc_blk.cfg.pcm.bits_per_sample = 16;
+ enc_cfg.enc_blk.cfg.pcm.sample_rate = rate;
+ enc_cfg.enc_blk.cfg.pcm.is_signed = 1;
+ enc_cfg.enc_blk.cfg.pcm.interleaved = 1;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd open failed\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout opcode[0x%x] ", enc_cfg.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, rate = %d, channels = %d, setting the rate and channels to 0 for native\n",
+ __func__, ac->session, rate, channels);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+ enc_cfg.enc_blk.frames_per_buf = 1;
+ enc_cfg.enc_blk.format_id = LINEAR_PCM;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_pcm_cfg);
+ enc_cfg.enc_blk.cfg.pcm.ch_cfg = 0;/*channels;*/
+ enc_cfg.enc_blk.cfg.pcm.bits_per_sample = 16;
+ enc_cfg.enc_blk.cfg.pcm.sample_rate = 0;/*rate;*/
+ enc_cfg.enc_blk.cfg.pcm.is_signed = 1;
+ enc_cfg.enc_blk.cfg.pcm.interleaved = 1;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd open failed\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout opcode[0x%x] ", enc_cfg.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+ ac->session, rate, channels);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+ enc_cfg.enc_blk.frames_per_buf = 1;
+ enc_cfg.enc_blk.format_id = MULTI_CHANNEL_PCM;
+ enc_cfg.enc_blk.cfg_size =
+ sizeof(struct asm_multi_channel_pcm_fmt_blk);
+ enc_cfg.enc_blk.cfg.mpcm.num_channels = channels;
+ enc_cfg.enc_blk.cfg.mpcm.bits_per_sample = 16;
+ enc_cfg.enc_blk.cfg.mpcm.sample_rate = rate;
+ enc_cfg.enc_blk.cfg.mpcm.is_signed = 1;
+ enc_cfg.enc_blk.cfg.mpcm.is_interleaved = 1;
+ if (channels == 1) {
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[0] = PCM_CHANNEL_FL;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[1] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[2] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[3] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[4] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[5] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[6] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[7] = 0;
+ } else if (channels == 2) {
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[0] = PCM_CHANNEL_FL;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[1] = PCM_CHANNEL_FR;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[2] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[3] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[4] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[5] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[6] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[7] = 0;
+ } else if (channels == 4) {
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[0] = PCM_CHANNEL_FL;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[1] = PCM_CHANNEL_FR;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[2] = PCM_CHANNEL_RB;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[3] = PCM_CHANNEL_LB;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[4] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[5] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[6] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[7] = 0;
+ } else if (channels == 6) {
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[0] = PCM_CHANNEL_FL;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[1] = PCM_CHANNEL_FR;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[2] = PCM_CHANNEL_LFE;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[3] = PCM_CHANNEL_FC;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[4] = PCM_CHANNEL_LB;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[5] = PCM_CHANNEL_RB;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[6] = 0;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[7] = 0;
+ } else if (channels == 8) {
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[0] = PCM_CHANNEL_FL;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[1] = PCM_CHANNEL_FR;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[2] = PCM_CHANNEL_LFE;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[3] = PCM_CHANNEL_FC;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[4] = PCM_CHANNEL_LB;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[5] = PCM_CHANNEL_RB;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[6] = PCM_CHANNEL_FLC;
+ enc_cfg.enc_blk.cfg.mpcm.channel_mapping[7] = PCM_CHANNEL_FRC;
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd open failed\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout opcode[0x%x] ", enc_cfg.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enable_sbrps(struct audio_client *ac,
+ uint32_t sbr_ps_enable)
+{
+ struct asm_stream_cmd_encdec_sbr sbrps;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d\n", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE);
+
+ sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ sbrps.param_id = ASM_ENABLE_SBR_PS;
+ sbrps.param_size = sizeof(struct asm_sbr_ps);
+ sbrps.sbr_ps.enable = sbr_ps_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps);
+ if (rc < 0) {
+ pr_err("Command opcode[0x%x]paramid[0x%x] failed\n",
+ ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_ENABLE_SBR_PS);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout opcode[0x%x] ", sbrps.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+ uint16_t sce_left, uint16_t sce_right)
+{
+ struct asm_stream_cmd_encdec_dualmono dual_mono;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, sce_left = %d, sce_right = %d\n",
+ __func__, ac->session, sce_left, sce_right);
+
+ q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE);
+
+ dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ dual_mono.param_id = ASM_CONFIGURE_DUAL_MONO;
+ dual_mono.param_size = sizeof(struct asm_dual_mono);
+ dual_mono.channel_map.sce_left = sce_left;
+ dual_mono.channel_map.sce_right = sce_right;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &dual_mono);
+ if (rc < 0) {
+ pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_CONFIGURE_DUAL_MONO);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout opcode[0x%x]\n", __func__,
+ dual_mono.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff)
+{
+ struct asm_aac_stereo_mix_coeff_selection_param aac_mix_coeff;
+ int rc = 0;
+ q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE);
+ aac_mix_coeff.hdr.opcode =
+ ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ aac_mix_coeff.param_id =
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG;
+ aac_mix_coeff.param_size =
+ sizeof(struct asm_aac_stereo_mix_coeff_selection_param);
+ aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff;
+ pr_debug("%s, mix_coeff = %u", __func__, mix_coeff);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff);
+ if (rc < 0) {
+ pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout opcode[0x%x]\n", __func__,
+ aac_mix_coeff.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+ uint32_t num_channels)
+{
+ struct asm_stream_cmd_encdec_channelmap chan_map;
+ u8 *channel_mapping;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, num_channels = %d\n",
+ __func__, ac->session, num_channels);
+
+ q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE);
+
+ chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ chan_map.param_id = ASM_ENCDEC_DEC_CHAN_MAP;
+ chan_map.param_size = sizeof(struct asm_dec_chan_map);
+ chan_map.chan_map.num_channels = num_channels;
+
+ channel_mapping =
+ chan_map.chan_map.channel_mapping;
+
+ memset(channel_mapping, PCM_CHANNEL_NULL, MAX_CHAN_MAP_CHANNELS);
+ if (num_channels == 1) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ } else if (num_channels == 2) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (num_channels == 4) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ channel_mapping[1] = PCM_CHANNEL_FR;
+ channel_mapping[1] = PCM_CHANNEL_LB;
+ channel_mapping[1] = PCM_CHANNEL_RB;
+ } else if (num_channels == 6) {
+ channel_mapping[0] = PCM_CHANNEL_FC;
+ channel_mapping[1] = PCM_CHANNEL_FL;
+ channel_mapping[2] = PCM_CHANNEL_FR;
+ channel_mapping[3] = PCM_CHANNEL_LB;
+ channel_mapping[4] = PCM_CHANNEL_RB;
+ channel_mapping[5] = PCM_CHANNEL_LFE;
+ } else if (num_channels == 8) {
+ channel_mapping[0] = PCM_CHANNEL_FC;
+ channel_mapping[1] = PCM_CHANNEL_FL;
+ channel_mapping[2] = PCM_CHANNEL_FR;
+ channel_mapping[3] = PCM_CHANNEL_LB;
+ channel_mapping[4] = PCM_CHANNEL_RB;
+ channel_mapping[5] = PCM_CHANNEL_LFE;
+ channel_mapping[6] = PCM_CHANNEL_FLC;
+ channel_mapping[7] = PCM_CHANNEL_FRC;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+ num_channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &chan_map);
+ if (rc < 0) {
+ pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_ENCDEC_DEC_CHAN_MAP);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout opcode[0x%x]\n", __func__,
+ chan_map.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]",
+ __func__,
+ ac->session, frames_per_buf, min_rate, max_rate,
+ reduced_rate_level, rate_modulation_cmd);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = V13K_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_qcelp13_read_cfg);
+ enc_cfg.enc_blk.cfg.qcelp13.min_rate = min_rate;
+ enc_cfg.enc_blk.cfg.qcelp13.max_rate = max_rate;
+ enc_cfg.enc_blk.cfg.qcelp13.reduced_rate_level = reduced_rate_level;
+ enc_cfg.enc_blk.cfg.qcelp13.rate_modulation_cmd = rate_modulation_cmd;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t rate_modulation_cmd)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] rate_modulation_cmd[0x%4x]",
+ __func__, ac->session,
+ frames_per_buf, min_rate, max_rate, rate_modulation_cmd);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = EVRC_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_evrc_read_cfg);
+ enc_cfg.enc_blk.cfg.evrc.min_rate = min_rate;
+ enc_cfg.enc_blk.cfg.evrc.max_rate = max_rate;
+ enc_cfg.enc_blk.cfg.evrc.rate_modulation_cmd = rate_modulation_cmd;
+ enc_cfg.enc_blk.cfg.evrc.reserved = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]",
+ __func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = AMRNB_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_amrnb_read_cfg);
+ enc_cfg.enc_blk.cfg.amrnb.mode = band_mode;
+ enc_cfg.enc_blk.cfg.amrnb.dtx_mode = dtx_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]",
+ __func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = AMRWB_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_amrwb_read_cfg);
+ enc_cfg.enc_blk.cfg.amrwb.mode = band_mode;
+ enc_cfg.enc_blk.cfg.amrwb.dtx_mode = dtx_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = LINEAR_PCM;
+ fmt.cfg_size = sizeof(struct asm_pcm_cfg);
+ fmt.write_cfg.pcm_cfg.ch_cfg = channels;
+ fmt.write_cfg.pcm_cfg.bits_per_sample = 16;
+ fmt.write_cfg.pcm_cfg.sample_rate = rate;
+ fmt.write_cfg.pcm_cfg.is_signed = 1;
+ fmt.write_cfg.pcm_cfg.interleaved = 1;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_media_format_update fmt;
+ u8 *channel_mapping;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = MULTI_CHANNEL_PCM;
+ fmt.cfg_size = sizeof(struct asm_multi_channel_pcm_fmt_blk);
+ fmt.write_cfg.multi_ch_pcm_cfg.num_channels = channels;
+ fmt.write_cfg.multi_ch_pcm_cfg.bits_per_sample = 16;
+ fmt.write_cfg.multi_ch_pcm_cfg.sample_rate = rate;
+ fmt.write_cfg.multi_ch_pcm_cfg.is_signed = 1;
+ fmt.write_cfg.multi_ch_pcm_cfg.is_interleaved = 1;
+ channel_mapping =
+ fmt.write_cfg.multi_ch_pcm_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (channels == 1) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ } else if (channels == 2) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channels == 4) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ channel_mapping[1] = PCM_CHANNEL_FR;
+ channel_mapping[1] = PCM_CHANNEL_LB;
+ channel_mapping[1] = PCM_CHANNEL_RB;
+ } else if (channels == 6) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ channel_mapping[1] = PCM_CHANNEL_FR;
+ channel_mapping[2] = PCM_CHANNEL_FC;
+ channel_mapping[3] = PCM_CHANNEL_LFE;
+ channel_mapping[4] = PCM_CHANNEL_LB;
+ channel_mapping[5] = PCM_CHANNEL_RB;
+ } else if (channels == 8) {
+ channel_mapping[0] = PCM_CHANNEL_FC;
+ channel_mapping[1] = PCM_CHANNEL_FL;
+ channel_mapping[2] = PCM_CHANNEL_FR;
+ channel_mapping[3] = PCM_CHANNEL_LB;
+ channel_mapping[4] = PCM_CHANNEL_RB;
+ channel_mapping[5] = PCM_CHANNEL_LFE;
+ channel_mapping[6] = PCM_CHANNEL_FLC;
+ channel_mapping[7] = PCM_CHANNEL_FRC;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+ channels);
+ return -EINVAL;
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session,
+ cfg->sample_rate, cfg->ch_cfg);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = MPEG4_AAC;
+ fmt.cfg_size = sizeof(struct asm_aac_cfg);
+ fmt.write_cfg.aac_cfg.format = cfg->format;
+ fmt.write_cfg.aac_cfg.aot = cfg->aot;
+ fmt.write_cfg.aac_cfg.ep_config = cfg->ep_config;
+ fmt.write_cfg.aac_cfg.section_data_resilience =
+ cfg->section_data_resilience;
+ fmt.write_cfg.aac_cfg.scalefactor_data_resilience =
+ cfg->scalefactor_data_resilience;
+ fmt.write_cfg.aac_cfg.spectral_data_resilience =
+ cfg->spectral_data_resilience;
+ fmt.write_cfg.aac_cfg.ch_cfg = cfg->ch_cfg;
+ fmt.write_cfg.aac_cfg.sample_rate = cfg->sample_rate;
+ pr_info("%s:format=%x cfg_size=%d aac-cfg=%x aot=%d ch=%d sr=%d\n",
+ __func__, fmt.format, fmt.cfg_size,
+ fmt.write_cfg.aac_cfg.format,
+ fmt.write_cfg.aac_cfg.aot,
+ fmt.write_cfg.aac_cfg.ch_cfg,
+ fmt.write_cfg.aac_cfg.sample_rate);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_amrwbplus(struct audio_client *ac,
+ struct asm_amrwbplus_cfg *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+ pr_debug("q6asm_media_format_block_amrwbplus");
+
+ pr_debug("%s:session[%d]band-mode[%d]frame-fmt[%d]ch[%d]\n",
+ __func__,
+ ac->session,
+ cfg->amr_band_mode,
+ cfg->amr_frame_fmt,
+ cfg->num_channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = AMR_WB_PLUS;
+ fmt.cfg_size = cfg->size_bytes;
+
+ fmt.write_cfg.amrwbplus_cfg.size_bytes = cfg->size_bytes;
+ fmt.write_cfg.amrwbplus_cfg.version = cfg->version;
+ fmt.write_cfg.amrwbplus_cfg.num_channels = cfg->num_channels;
+ fmt.write_cfg.amrwbplus_cfg.amr_band_mode = cfg->amr_band_mode;
+ fmt.write_cfg.amrwbplus_cfg.amr_dtx_mode = cfg->amr_dtx_mode;
+ fmt.write_cfg.amrwbplus_cfg.amr_frame_fmt = cfg->amr_frame_fmt;
+ fmt.write_cfg.amrwbplus_cfg.amr_lsf_idx = cfg->amr_lsf_idx;
+
+ pr_debug("%s: num_channels=%x amr_band_mode=%d amr_frame_fmt=%d\n",
+ __func__,
+ cfg->num_channels,
+ cfg->amr_band_mode,
+ cfg->amr_frame_fmt);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd media format update failed..\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+int q6asm_media_format_block_multi_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session,
+ cfg->sample_rate, cfg->ch_cfg);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = MPEG4_MULTI_AAC;
+ fmt.cfg_size = sizeof(struct asm_aac_cfg);
+ fmt.write_cfg.aac_cfg.format = cfg->format;
+ fmt.write_cfg.aac_cfg.aot = cfg->aot;
+ fmt.write_cfg.aac_cfg.ep_config = cfg->ep_config;
+ fmt.write_cfg.aac_cfg.section_data_resilience =
+ cfg->section_data_resilience;
+ fmt.write_cfg.aac_cfg.scalefactor_data_resilience =
+ cfg->scalefactor_data_resilience;
+ fmt.write_cfg.aac_cfg.spectral_data_resilience =
+ cfg->spectral_data_resilience;
+ fmt.write_cfg.aac_cfg.ch_cfg = cfg->ch_cfg;
+ fmt.write_cfg.aac_cfg.sample_rate = cfg->sample_rate;
+ pr_info("%s:format=%x cfg_size=%d aac-cfg=%x aot=%d ch=%d sr=%d\n",
+ __func__, fmt.format, fmt.cfg_size,
+ fmt.write_cfg.aac_cfg.format,
+ fmt.write_cfg.aac_cfg.aot,
+ fmt.write_cfg.aac_cfg.ch_cfg,
+ fmt.write_cfg.aac_cfg.sample_rate);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+
+
+int q6asm_media_format_block(struct audio_client *ac, uint32_t format)
+{
+
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+
+ pr_debug("%s:session[%d] format[0x%x]\n", __func__,
+ ac->session, format);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+ switch (format) {
+ case FORMAT_V13K:
+ fmt.format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ fmt.format = EVRC_FS;
+ break;
+ case FORMAT_AMRWB:
+ fmt.format = AMRWB_FS;
+ break;
+ case FORMAT_AMR_WB_PLUS:
+ fmt.format = AMR_WB_PLUS;
+ break;
+ case FORMAT_AMRNB:
+ fmt.format = AMRNB_FS;
+ break;
+ case FORMAT_MP3:
+ fmt.format = MP3;
+ break;
+ case FORMAT_DTS:
+ fmt.format = DTS;
+ break;
+ case FORMAT_DTS_LBR:
+ fmt.format = DTS_LBR;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", format);
+ goto fail_cmd;
+ }
+ fmt.cfg_size = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_wma(struct audio_client *ac,
+ void *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg;
+ int rc = 0;
+
+ pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n",
+ ac->session, wma_cfg->format_tag, wma_cfg->sample_rate,
+ wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec,
+ wma_cfg->block_align, wma_cfg->valid_bits_per_sample,
+ wma_cfg->ch_mask, wma_cfg->encode_opt);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = WMA_V9;
+ fmt.cfg_size = sizeof(struct asm_wma_cfg);
+ fmt.write_cfg.wma_cfg.format_tag = wma_cfg->format_tag;
+ fmt.write_cfg.wma_cfg.ch_cfg = wma_cfg->ch_cfg;
+ fmt.write_cfg.wma_cfg.sample_rate = wma_cfg->sample_rate;
+ fmt.write_cfg.wma_cfg.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec;
+ fmt.write_cfg.wma_cfg.block_align = wma_cfg->block_align;
+ fmt.write_cfg.wma_cfg.valid_bits_per_sample =
+ wma_cfg->valid_bits_per_sample;
+ fmt.write_cfg.wma_cfg.ch_mask = wma_cfg->ch_mask;
+ fmt.write_cfg.wma_cfg.encode_opt = wma_cfg->encode_opt;
+ fmt.write_cfg.wma_cfg.adv_encode_opt = 0;
+ fmt.write_cfg.wma_cfg.adv_encode_opt2 = 0;
+ fmt.write_cfg.wma_cfg.drc_peak_ref = 0;
+ fmt.write_cfg.wma_cfg.drc_peak_target = 0;
+ fmt.write_cfg.wma_cfg.drc_ave_ref = 0;
+ fmt.write_cfg.wma_cfg.drc_ave_target = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_wmapro(struct audio_client *ac,
+ void *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg;
+ int rc = 0;
+
+ pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x], adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n",
+ ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate,
+ wmapro_cfg->ch_cfg, wmapro_cfg->avg_bytes_per_sec,
+ wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample,
+ wmapro_cfg->ch_mask, wmapro_cfg->encode_opt,
+ wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = WMA_V10PRO;
+ fmt.cfg_size = sizeof(struct asm_wmapro_cfg);
+ fmt.write_cfg.wmapro_cfg.format_tag = wmapro_cfg->format_tag;
+ fmt.write_cfg.wmapro_cfg.ch_cfg = wmapro_cfg->ch_cfg;
+ fmt.write_cfg.wmapro_cfg.sample_rate = wmapro_cfg->sample_rate;
+ fmt.write_cfg.wmapro_cfg.avg_bytes_per_sec =
+ wmapro_cfg->avg_bytes_per_sec;
+ fmt.write_cfg.wmapro_cfg.block_align = wmapro_cfg->block_align;
+ fmt.write_cfg.wmapro_cfg.valid_bits_per_sample =
+ wmapro_cfg->valid_bits_per_sample;
+ fmt.write_cfg.wmapro_cfg.ch_mask = wmapro_cfg->ch_mask;
+ fmt.write_cfg.wmapro_cfg.encode_opt = wmapro_cfg->encode_opt;
+ fmt.write_cfg.wmapro_cfg.adv_encode_opt = wmapro_cfg->adv_encode_opt;
+ fmt.write_cfg.wmapro_cfg.adv_encode_opt2 = wmapro_cfg->adv_encode_opt2;
+ fmt.write_cfg.wmapro_cfg.drc_peak_ref = 0;
+ fmt.write_cfg.wmapro_cfg.drc_peak_target = 0;
+ fmt.write_cfg.wmapro_cfg.drc_ave_ref = 0;
+ fmt.write_cfg.wmapro_cfg.drc_ave_target = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct asm_stream_cmd_memory_map mem_map;
+ int rc = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ mem_map.hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP;
+
+ mem_map.buf_add = buf_add;
+ mem_map.buf_size = bufsz * bufcnt;
+ mem_map.mempool_id = 0; /* EBI */
+ mem_map.reserved = 0;
+
+ q6asm_add_mmaphdr(&mem_map.hdr,
+ sizeof(struct asm_stream_cmd_memory_map), TRUE);
+
+ pr_debug("buf add[%x] buf_add_parameter[%x]\n",
+ mem_map.buf_add, buf_add);
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map);
+ if (rc < 0) {
+ pr_err("mem_map op[0x%x]rc[%d]\n",
+ mem_map.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_map\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add, int dir)
+{
+ struct asm_stream_cmd_memory_unmap mem_unmap;
+ int rc = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ q6asm_add_mmaphdr(&mem_unmap.hdr,
+ sizeof(struct asm_stream_cmd_memory_unmap), TRUE);
+ mem_unmap.hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP;
+ mem_unmap.buf_add = buf_add;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("mem_unmap op[0x%x]rc[%d]\n",
+ mem_unmap.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_map\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_lrchannel_gain_params *lrgain = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_lrchannel_gain_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_lrchannel_gain_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = L_R_CHANNEL_GAIN_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_lrchannel_gain_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ lrgain = (struct asm_lrchannel_gain_params *)payload;
+
+ lrgain->left_gain = left_gain;
+ lrgain->right_gain = right_gain;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct asm_stream_cmd_memory_map_regions *mmap_regions = NULL;
+ struct asm_memory_map_regions *mregions = NULL;
+ struct audio_port_data *port = NULL;
+ struct audio_buffer *ab = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int rc = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ cmd_size = sizeof(struct asm_stream_cmd_memory_map_regions)
+ + sizeof(struct asm_memory_map_regions) * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (mmap_region_cmd == NULL) {
+ pr_err("%s: Mem alloc failed\n", __func__);
+ rc = -EINVAL;
+ return rc;
+ }
+ mmap_regions = (struct asm_stream_cmd_memory_map_regions *)
+ mmap_region_cmd;
+ q6asm_add_mmaphdr(&mmap_regions->hdr, cmd_size, TRUE);
+ mmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP_REGIONS;
+ mmap_regions->mempool_id = 0;
+ mmap_regions->nregions = bufcnt & 0x00ff;
+ pr_debug("map_regions->nregions = %d\n", mmap_regions->nregions);
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct asm_stream_cmd_memory_map_regions));
+ mregions = (struct asm_memory_map_regions *)payload;
+
+ port = &ac->port[dir];
+ for (i = 0; i < bufcnt; i++) {
+ ab = &port->buf[i];
+ mregions->phys = ab->phys;
+ mregions->buf_size = ab->size;
+ ++mregions;
+ }
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) mmap_region_cmd);
+ if (rc < 0) {
+ pr_err("mmap_regions op[0x%x]rc[%d]\n",
+ mmap_regions->hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_map\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return rc;
+}
+
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct asm_stream_cmd_memory_unmap_regions *unmap_regions = NULL;
+ struct asm_memory_unmap_regions *mregions = NULL;
+ struct audio_port_data *port = NULL;
+ struct audio_buffer *ab = NULL;
+ void *unmap_region_cmd = NULL;
+ void *payload = NULL;
+ int rc = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ cmd_size = sizeof(struct asm_stream_cmd_memory_unmap_regions) +
+ sizeof(struct asm_memory_unmap_regions) * bufcnt;
+
+ unmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (unmap_region_cmd == NULL) {
+ pr_err("%s: Mem alloc failed\n", __func__);
+ rc = -EINVAL;
+ return rc;
+ }
+ unmap_regions = (struct asm_stream_cmd_memory_unmap_regions *)
+ unmap_region_cmd;
+ q6asm_add_mmaphdr(&unmap_regions->hdr, cmd_size, TRUE);
+ unmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS;
+ unmap_regions->nregions = bufcnt & 0x00ff;
+ pr_debug("unmap_regions->nregions = %d\n", unmap_regions->nregions);
+ payload = ((u8 *) unmap_region_cmd +
+ sizeof(struct asm_stream_cmd_memory_unmap_regions));
+ mregions = (struct asm_memory_unmap_regions *)payload;
+ port = &ac->port[dir];
+ for (i = 0; i < bufcnt; i++) {
+ ab = &port->buf[i];
+ mregions->phys = ab->phys;
+ ++mregions;
+ }
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) unmap_region_cmd);
+ if (rc < 0) {
+ pr_err("mmap_regions op[0x%x]rc[%d]\n",
+ unmap_regions->hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_unmap\n");
+ goto fail_cmd;
+ }
+ rc = 0;
+
+fail_cmd:
+ kfree(unmap_region_cmd);
+ return rc;
+}
+
+int q6asm_set_mute(struct audio_client *ac, int muteflag)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_mute_params *mute = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_mute_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_mute_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = MUTE_CONFIG_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_mute_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ mute = (struct asm_mute_params *)payload;
+
+ mute->muteflag = muteflag;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Mute Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending mute command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_set_volume(struct audio_client *ac, int volume)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_master_gain_params *mgain = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_master_gain_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_master_gain_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = MASTER_GAIN_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_master_gain_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ mgain = (struct asm_master_gain_params *)payload;
+
+ mgain->master_gain = volume;
+ mgain->padding = 0x00;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_set_softpause(struct audio_client *ac,
+ struct asm_softpause_params *pause_param)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_softpause_params *params = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_softpause_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_softpause_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = SOFT_PAUSE_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_softpause_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ params = (struct asm_softpause_params *)payload;
+
+ params->enable = pause_param->enable;
+ params->period = pause_param->period;
+ params->step = pause_param->step;
+ params->rampingcurve = pause_param->rampingcurve;
+ pr_debug("%s: soft Pause Command: enable = %d, period = %d, step = %d, curve = %d\n",
+ __func__, params->enable,
+ params->period, params->step, params->rampingcurve);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command(soft_pause) failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command(soft_pause) to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_set_softvolume(struct audio_client *ac,
+ struct asm_softvolume_params *softvol_param)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_softvolume_params *params = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_softvolume_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_softvolume_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = SOFT_VOLUME_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_softvolume_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ params = (struct asm_softvolume_params *)payload;
+
+ params->period = softvol_param->period;
+ params->step = softvol_param->step;
+ params->rampingcurve = softvol_param->rampingcurve;
+ pr_debug("%s: soft Volume:opcode = %d,payload_sz =%d,module_id =%d, param_id = %d, param_sz = %d\n",
+ __func__,
+ cmd->hdr.opcode, cmd->payload_size,
+ cmd->params.module_id, cmd->params.param_id,
+ cmd->params.param_size);
+ pr_debug("%s: soft Volume Command: period = %d, step = %d, curve = %d\n",
+ __func__, params->period,
+ params->step, params->rampingcurve);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command(soft_volume) failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command(soft_volume) to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_equalizer(struct audio_client *ac, void *eq)
+{
+ void *eq_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_equalizer_params *equalizer = NULL;
+ struct msm_audio_eq_stream_config *eq_params = NULL;
+ int i = 0;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_equalizer_params);
+ eq_cmd = kzalloc(sz, GFP_KERNEL);
+ if (eq_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ eq_params = (struct msm_audio_eq_stream_config *) eq;
+ cmd = (struct asm_pp_params_command *)eq_cmd;
+ q6asm_add_hdr(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_equalizer_params);
+ cmd->params.module_id = EQUALIZER_MODULE_ID;
+ cmd->params.param_id = EQUALIZER_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_equalizer_params);
+ cmd->params.reserved = 0;
+ payload = (u8 *)(eq_cmd + sizeof(struct asm_pp_params_command));
+ equalizer = (struct asm_equalizer_params *)payload;
+
+ equalizer->enable = eq_params->enable;
+ equalizer->num_bands = eq_params->num_bands;
+ pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable,
+ eq_params->num_bands);
+ for (i = 0; i < eq_params->num_bands; i++) {
+ equalizer->eq_bands[i].band_idx =
+ eq_params->eq_bands[i].band_idx;
+ equalizer->eq_bands[i].filter_type =
+ eq_params->eq_bands[i].filter_type;
+ equalizer->eq_bands[i].center_freq_hz =
+ eq_params->eq_bands[i].center_freq_hz;
+ equalizer->eq_bands[i].filter_gain =
+ eq_params->eq_bands[i].filter_gain;
+ equalizer->eq_bands[i].q_factor =
+ eq_params->eq_bands[i].q_factor;
+ pr_debug("%s: filter_type:%u bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].filter_type, i);
+ pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].center_freq_hz, i);
+ pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].filter_gain, i);
+ pr_debug("%s: q_factor:%d bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].q_factor, i);
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) eq_cmd);
+ if (rc < 0) {
+ pr_err("%s: Equalizer Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending equalizer command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(eq_cmd);
+ return rc;
+}
+
+int q6asm_read(struct audio_client *ac)
+{
+ struct asm_stream_cmd_read read;
+ struct audio_buffer *ab;
+ int dsp_buf;
+ struct audio_port_data *port;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[OUT];
+
+ q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE);
+
+ mutex_lock(&port->lock);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n",
+ __func__,
+ ac->session,
+ dsp_buf,
+ (void *)port->buf[dsp_buf].data,
+ port->cpu_buf,
+ (void *)port->buf[port->cpu_buf].phys);
+
+ read.hdr.opcode = ASM_DATA_CMD_READ;
+ read.buf_add = ab->phys;
+ read.buf_size = ab->size;
+ read.uid = port->dsp_buf;
+ read.hdr.token = port->dsp_buf;
+
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+ mutex_unlock(&port->lock);
+ pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__,
+ read.buf_add,
+ read.hdr.token,
+ read.uid);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("read op[0x%x]rc[%d]\n", read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_read_nolock(struct audio_client *ac)
+{
+ struct asm_stream_cmd_read read;
+ struct audio_buffer *ab;
+ int dsp_buf;
+ struct audio_port_data *port;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[OUT];
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n",
+ __func__,
+ ac->session,
+ dsp_buf,
+ (void *)port->buf[dsp_buf].data,
+ port->cpu_buf,
+ (void *)port->buf[port->cpu_buf].phys);
+
+ read.hdr.opcode = ASM_DATA_CMD_READ;
+ read.buf_add = ab->phys;
+ read.buf_size = ab->size;
+ read.uid = port->dsp_buf;
+ read.hdr.token = port->dsp_buf;
+
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+ pr_info("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__,
+ read.buf_add,
+ read.hdr.token,
+ read.uid);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("read op[0x%x]rc[%d]\n", read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg)
+{
+ pr_debug("session=%d pkt size=%d cmd_flg=%d\n", pkt_size, cmd_flg,
+ ac->session);
+ 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 *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ if (cmd_flg) {
+ hdr->token = ac->session;
+ atomic_set(&ac->cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+int q6asm_async_write(struct audio_client *ac,
+ struct audio_aio_write_param *param)
+{
+ int rc = 0;
+ struct asm_stream_cmd_write write;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), FALSE);
+
+ /* Pass physical address as token for AIO scheme */
+ write.hdr.token = param->uid;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE;
+ write.buf_add = param->paddr;
+ write.avail_bytes = param->len;
+ write.uid = param->uid;
+ write.msw_ts = param->msw_ts;
+ write.lsw_ts = param->lsw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (param->flags == 0xFF00)
+ write.uflags = (0x00000000 | (param->flags & 0x800000FF));
+ else
+ write.uflags = (0x80000000 | param->flags);
+
+ pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session,
+ write.buf_add, write.avail_bytes);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_debug("[%s] write op[0x%x]rc[%d]\n", __func__,
+ write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_async_read(struct audio_client *ac,
+ struct audio_aio_read_param *param)
+{
+ int rc = 0;
+ struct asm_stream_cmd_read read;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+ /* Pass physical address as token for AIO scheme */
+ read.hdr.token = param->paddr;
+ read.hdr.opcode = ASM_DATA_CMD_READ;
+ read.buf_add = param->paddr;
+ read.buf_size = param->len;
+ read.uid = param->uid;
+
+ pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session,
+ read.buf_add, read.buf_size);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_debug("[%s] read op[0x%x]rc[%d]\n", __func__,
+ read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_async_read_compressed(struct audio_client *ac,
+ struct audio_aio_read_param *param)
+{
+ int rc = 0;
+ struct asm_stream_cmd_read read;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+ /* Pass physical address as token for AIO scheme */
+ read.hdr.token = param->paddr;
+ read.hdr.opcode = ASM_DATA_CMD_READ_COMPRESSED;
+ read.buf_add = param->paddr;
+ read.buf_size = param->len;
+ read.uid = param->uid;
+
+ pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session,
+ read.buf_add, read.buf_size);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_debug("[%s] read op[0x%x]rc[%d]\n", __func__,
+ read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags)
+{
+ int rc = 0;
+ struct asm_stream_cmd_write write;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ int dsp_buf = 0;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] len=%d", __func__, ac->session, len);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[IN];
+
+ q6asm_add_hdr(ac, &write.hdr, sizeof(write),
+ FALSE);
+ mutex_lock(&port->lock);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ write.hdr.token = port->dsp_buf;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE;
+ write.buf_add = ab->phys;
+ write.avail_bytes = len;
+ write.uid = port->dsp_buf;
+ write.msw_ts = msw_ts;
+ write.lsw_ts = lsw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (flags == 0xFF00)
+ write.uflags = (0x00000000 | (flags & 0x800000FF));
+ else
+ write.uflags = (0x80000000 | flags);
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+
+ pr_debug("%s:ab->phys[0x%x]bufadd[0x%x]token[0x%x]buf_id[0x%x]"
+ , __func__,
+ ab->phys,
+ write.buf_add,
+ write.hdr.token,
+ write.uid);
+ mutex_unlock(&port->lock);
+#ifdef CONFIG_DEBUG_FS
+ if (out_enable_flag) {
+ char zero_pattern[2] = {0x00, 0x00};
+ /* If First two byte is non zero and last two byte
+ is zero then it is warm output pattern */
+ if ((strncmp(((char *)ab->data), zero_pattern, 2)) &&
+ (!strncmp(((char *)ab->data + 2), zero_pattern, 2))) {
+ do_gettimeofday(&out_warm_tv);
+ pr_debug("WARM:apr_send_pkt at %ld sec %ld microsec\n",
+ out_warm_tv.tv_sec,\
+ out_warm_tv.tv_usec);
+ pr_debug("Warm Pattern Matched");
+ }
+ /* If First two byte is zero and last two byte is
+ non zero then it is cont ouput pattern */
+ else if ((!strncmp(((char *)ab->data), zero_pattern, 2))
+ && (strncmp(((char *)ab->data + 2), zero_pattern, 2))) {
+ do_gettimeofday(&out_cont_tv);
+ pr_debug("CONT:apr_send_pkt at %ld sec %ld microsec\n",
+ out_cont_tv.tv_sec,\
+ out_cont_tv.tv_usec);
+ pr_debug("Cont Pattern Matched");
+ }
+ }
+#endif
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("write op[0x%x]rc[%d]\n", write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ pr_debug("%s: WRITE SUCCESS\n", __func__);
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags)
+{
+ int rc = 0;
+ struct asm_stream_cmd_write write;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ int dsp_buf = 0;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] len=%d", __func__, ac->session, len);
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[IN];
+
+ q6asm_add_hdr_async(ac, &write.hdr, sizeof(write),
+ FALSE);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ write.hdr.token = port->dsp_buf;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE;
+ write.buf_add = ab->phys;
+ write.avail_bytes = len;
+ write.uid = port->dsp_buf;
+ write.msw_ts = msw_ts;
+ write.lsw_ts = lsw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (flags == 0xFF00)
+ write.uflags = (0x00000000 | (flags & 0x800000FF));
+ else
+ write.uflags = (0x80000000 | flags);
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+
+ pr_debug("%s:ab->phys[0x%x]bufadd[0x%x]token[0x%x]buf_id[0x%x]"
+ , __func__,
+ ab->phys,
+ write.buf_add,
+ write.hdr.token,
+ write.uid);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("write op[0x%x]rc[%d]\n", write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ pr_debug("%s: WRITE SUCCESS\n", __func__);
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ if (!ac || ac->apr == NULL || tstamp == NULL) {
+ pr_err("APR handle or tstamp NULL\n");
+ return -EINVAL;
+ }
+ q6asm_add_hdr(ac, &hdr, sizeof(hdr), FALSE);
+ hdr.opcode = ASM_SESSION_CMD_GET_SESSION_TIME;
+ atomic_set(&ac->time_flag, 1);
+
+ pr_debug("%s: session[%d]opcode[0x%x]\n", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("Commmand 0x%x failed\n", hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->time_wait,
+ (atomic_read(&ac->time_flag) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in getting session time from DSP\n",
+ __func__);
+ goto fail_cmd;
+ }
+
+ *tstamp = ac->time_stamp;
+ return 0;
+
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cmd(struct audio_client *ac, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc;
+ atomic_t *state;
+ int cnt = 0;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+ switch (cmd) {
+ case CMD_PAUSE:
+ pr_debug("%s:CMD_PAUSE\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ state = &ac->cmd_state;
+ break;
+ case CMD_FLUSH:
+ pr_debug("%s:CMD_FLUSH\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_FLUSH;
+ state = &ac->cmd_state;
+ break;
+ case CMD_OUT_FLUSH:
+ pr_debug("%s:CMD_OUT_FLUSH\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
+ state = &ac->cmd_state;
+ break;
+ case CMD_EOS:
+ pr_debug("%s:CMD_EOS\n", __func__);
+ hdr.opcode = ASM_DATA_CMD_EOS;
+ atomic_set(&ac->cmd_state, 0);
+ state = &ac->cmd_state;
+ break;
+ case CMD_CLOSE:
+ pr_debug("%s:CMD_CLOSE\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_CLOSE;
+ atomic_set(&ac->cmd_close_state, 1);
+ state = &ac->cmd_close_state;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", cmd);
+ goto fail_cmd;
+ }
+ pr_debug("%s:session[%d]opcode[0x%x] ", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("Commmand 0x%x failed\n", hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for response opcode[0x%x]\n",
+ hdr.opcode);
+ goto fail_cmd;
+ }
+ if (cmd == CMD_FLUSH)
+ q6asm_reset_buf_state(ac);
+ if (cmd == CMD_CLOSE) {
+ /* check if DSP return all buffers */
+ if (ac->port[IN].buf) {
+ for (cnt = 0; cnt < ac->port[IN].max_buf_cnt;
+ cnt++) {
+ if (ac->port[IN].buf[cnt].used == IN) {
+ pr_debug("Write Buf[%d] not returned\n",
+ cnt);
+ }
+ }
+ }
+ if (ac->port[OUT].buf) {
+ for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) {
+ if (ac->port[OUT].buf[cnt].used == OUT) {
+ pr_debug("Read Buf[%d] not returned\n",
+ cnt);
+ }
+ }
+ }
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s:APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6asm_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE);
+ switch (cmd) {
+ case CMD_PAUSE:
+ pr_debug("%s:CMD_PAUSE\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ break;
+ case CMD_EOS:
+ pr_debug("%s:CMD_EOS\n", __func__);
+ hdr.opcode = ASM_DATA_CMD_EOS;
+ break;
+ default:
+ pr_err("%s:Invalid format[%d]\n", __func__, cmd);
+ goto fail_cmd;
+ }
+ pr_debug("%s:session[%d]opcode[0x%x] ", __func__,
+ ac->session,
+ hdr.opcode);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s:Commmand 0x%x failed\n", __func__, hdr.opcode);
+ goto fail_cmd;
+ }
+ atomic_inc(&ac->nowait_cmd_cnt);
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+static void q6asm_reset_buf_state(struct audio_client *ac)
+{
+ int cnt = 0;
+ int loopcnt = 0;
+ int used;
+ struct audio_port_data *port = NULL;
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0);
+ mutex_lock(&ac->cmd_lock);
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ cnt = port->max_buf_cnt - 1;
+ port->dsp_buf = 0;
+ port->cpu_buf = 0;
+ while (cnt >= 0) {
+ if (!port->buf)
+ continue;
+ port->buf[cnt].used = used;
+ cnt--;
+ }
+ }
+ mutex_unlock(&ac->cmd_lock);
+ }
+}
+
+int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable)
+{
+ struct asm_stream_cmd_reg_tx_overflow_event tx_overflow;
+ int rc;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]enable[%d]\n", __func__,
+ ac->session, enable);
+ q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE);
+
+ tx_overflow.hdr.opcode = \
+ ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS;
+ /* tx overflow event: enable */
+ tx_overflow.enable = enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow);
+ if (rc < 0) {
+ pr_err("tx overflow op[0x%x]rc[%d]\n", \
+ tx_overflow.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for tx overflow\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_get_apr_service_id(int session_id)
+{
+ pr_debug("%s\n", __func__);
+
+ if (session_id < 0 || session_id > SESSION_MAX) {
+ pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ return ((struct apr_svc *)session[session_id]->apr)->id;
+}
+
+
+static int __init q6asm_init(void)
+{
+ pr_debug("%s\n", __func__);
+ init_waitqueue_head(&this_mmap.cmd_wait);
+ memset(session, 0, sizeof(session));
+#ifdef CONFIG_DEBUG_FS
+ out_buffer = kmalloc(OUT_BUFFER_SIZE, GFP_KERNEL);
+ out_dentry = debugfs_create_file("audio_out_latency_measurement_node",\
+ 0664,\
+ NULL, NULL, &audio_output_latency_debug_fops);
+ if (IS_ERR(out_dentry))
+ pr_err("debugfs_create_file failed\n");
+ in_buffer = kmalloc(IN_BUFFER_SIZE, GFP_KERNEL);
+ in_dentry = debugfs_create_file("audio_in_latency_measurement_node",\
+ 0664,\
+ NULL, NULL, &audio_input_latency_debug_fops);
+ if (IS_ERR(in_dentry))
+ pr_err("debugfs_create_file failed\n");
+#endif
+ return 0;
+}
+
+device_initcall(q6asm_init);