hal: Add support for compressed capture

- Add support for AMR-WB compressed capture.

Change-Id: Ief591c5d1ccc6f8afb408abfff447787ee7bd7aa
diff --git a/hal/Android.mk b/hal/Android.mk
index 21dc8a0..abafb85 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -82,6 +82,11 @@
   LOCAL_SRC_FILES += $(AUDIO_PLATFORM)/hw_info.c
 endif
 
+ifneq ($(strip $(AUDIO_FEATURE_DISABLED_COMPRESS_CAPTURE)),true)
+    LOCAL_CFLAGS += -DCOMPRESS_CAPTURE_ENABLED
+    LOCAL_SRC_FILES += audio_extn/compress_capture.c
+endif
+
 LOCAL_SHARED_LIBRARIES := \
 	liblog \
 	libcutils \
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index a54e5e7..7d04c6d 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -137,4 +137,24 @@
 bool audio_extn_spkr_prot_is_enabled();
 #endif
 
+#ifndef COMPRESS_CAPTURE_ENABLED
+#define audio_extn_compr_cap_init(adev,in)                (0)
+#define audio_extn_compr_cap_enabled()                    (0)
+#define audio_extn_compr_cap_format_supported(format)     (0)
+#define audio_extn_compr_cap_usecase_supported(usecase)   (0)
+#define audio_extn_compr_cap_get_buffer_size(format)      (0)
+#define audio_extn_compr_cap_read(in, buffer, bytes)      (0)
+#define audio_extn_compr_cap_deinit()                     (0)
+#else
+void audio_extn_compr_cap_init(struct audio_device *adev,
+                                    struct stream_in *in);
+bool audio_extn_compr_cap_enabled();
+bool audio_extn_compr_cap_format_supported(audio_format_t format);
+bool audio_extn_compr_cap_usecase_supported(audio_usecase_t usecase);
+size_t audio_extn_compr_cap_get_buffer_size(audio_format_t format);
+size_t audio_extn_compr_cap_read(struct stream_in *in,
+                                        void *buffer, size_t bytes);
+void audio_extn_compr_cap_deinit();
+#endif
+
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/compress_capture.c b/hal/audio_extn/compress_capture.c
new file mode 100644
index 0000000..f3db419
--- /dev/null
+++ b/hal/audio_extn/compress_capture.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_compress"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+
+#include "sound/compress_params.h"
+#include "sound/compress_offload.h"
+
+#ifdef COMPRESS_CAPTURE_ENABLED
+
+#define COMPRESS_IN_CONFIG_CHANNELS 1
+#define COMPRESS_IN_CONFIG_PERIOD_SIZE 2048
+#define COMPRESS_IN_CONFIG_PERIOD_COUNT 16
+
+
+struct compress_in_module {
+    uint8_t             *in_buf;
+};
+
+static struct compress_in_module c_in_mod = {
+    .in_buf = NULL,
+};
+
+
+void audio_extn_compr_cap_init(struct audio_device *adev,
+                            struct stream_in *in)
+{
+    in->usecase = USECASE_AUDIO_RECORD_COMPRESS;
+    in->config.channels = COMPRESS_IN_CONFIG_CHANNELS;
+    in->config.period_size = COMPRESS_IN_CONFIG_PERIOD_SIZE;
+    in->config.period_count= COMPRESS_IN_CONFIG_PERIOD_COUNT;
+    in->config.format = AUDIO_FORMAT_AMR_WB;
+    c_in_mod.in_buf = (uint8_t*)calloc(1, in->config.period_size*2);
+}
+
+void audio_extn_compr_cap_deinit()
+{
+    if (c_in_mod.in_buf) {
+        free(c_in_mod.in_buf);
+        c_in_mod.in_buf = NULL;
+    }
+}
+
+bool audio_extn_compr_cap_enabled()
+{
+    char prop_value[PROPERTY_VALUE_MAX] = {0};
+    bool tunnel_encode = false;
+
+    property_get("tunnel.audio.encode",prop_value,"0");
+    if (!strncmp("true", prop_value, sizeof("true")))
+        return true;
+    else
+        return false;
+}
+
+bool audio_extn_compr_cap_format_supported(audio_format_t format)
+{
+    if (format == AUDIO_FORMAT_AMR_WB)
+        return true;
+    else
+        return false;
+}
+
+
+bool audio_extn_compr_cap_usecase_supported(audio_usecase_t usecase)
+{
+    if (usecase == USECASE_AUDIO_RECORD_COMPRESS)
+        return true;
+    else
+        return false;
+}
+
+
+size_t audio_extn_compr_cap_get_buffer_size(audio_format_t format)
+{
+    if (format == AUDIO_FORMAT_AMR_WB)
+        /*One AMR WB frame is 61 bytes. Return that to the caller.
+        The buffer size is not altered, that is still period size.*/
+        return AMR_WB_FRAMESIZE;
+    else
+        return 0;
+}
+
+size_t audio_extn_compr_cap_read(struct stream_in * in,
+    void *buffer, size_t bytes)
+{
+    int ret;
+    struct snd_compr_audio_info *header;
+    uint32_t c_in_header;
+    uint32_t c_in_buf_size;
+
+    c_in_buf_size = in->config.period_size*2;
+
+    if (in->pcm) {
+        ret = pcm_read(in->pcm, c_in_mod.in_buf, c_in_buf_size);
+        if (ret < 0) {
+            ALOGE("pcm_read() returned failure: %d", ret);
+            return ret;
+        } else {
+            header = (struct snd_compr_audio_info *) c_in_mod.in_buf;
+            c_in_header = sizeof(*header) + header->reserved[0];
+            if (header->frame_size > 0) {
+                if (c_in_header  + header->frame_size > c_in_buf_size) {
+                    ALOGW("AMR WB read buffer overflow.");
+                    header->frame_size =
+                        bytes - sizeof(*header) - header->reserved[0];
+                }
+                ALOGV("c_in_buf: %p, data offset: %p, header size: %u,"
+                    "reserved[0]: %u frame_size: %d", c_in_mod.in_buf,
+                        c_in_mod.in_buf + c_in_header,
+                        sizeof(*header), header->reserved[0],
+                        header->frame_size);
+                memcpy(buffer, c_in_mod.in_buf + c_in_header, header->frame_size);
+            } else {
+                ALOGE("pcm_read() with zero frame size");
+                ret = -EINVAL;
+            }
+        }
+    }
+
+    return 0;
+}
+
+#endif /* COMPRESS_CAPTURE_ENABLED end */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 9b59e4a..873e5e2 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -105,6 +105,7 @@
     [USECASE_AUDIO_PLAYBACK_MULTI_CH] = "multi-channel-playback",
     [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
     [USECASE_AUDIO_RECORD] = "audio-record",
+    [USECASE_AUDIO_RECORD_COMPRESS] = "audio-record-compress",
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
     [USECASE_AUDIO_RECORD_FM_VIRTUAL] = "fm-virtual-record",
     [USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
@@ -741,7 +742,7 @@
     struct audio_usecase *uc_info;
     struct audio_device *adev = in->dev;
 
-    in->usecase = platform_get_usecase_from_source(in->source);
+    in->usecase = platform_update_usecase_from_source(in->source,in->usecase);
     ALOGV("%s: enter: usecase(%d)", __func__, in->usecase);
 
     /* Check if source matches incall recording usecase criteria */
@@ -1126,7 +1127,8 @@
     int ret = 0;
 
     if ((format != AUDIO_FORMAT_PCM_16_BIT) &&
-        !voice_extn_compress_voip_is_format_supported(format)) ret = -EINVAL;
+        !voice_extn_compress_voip_is_format_supported(format) &&
+            !audio_extn_compr_cap_format_supported(format))  ret = -EINVAL;
 
     switch (channel_count) {
     case 1:
@@ -1714,6 +1716,8 @@
 
     if(in->usecase == USECASE_COMPRESS_VOIP_CALL)
         return voice_extn_compress_voip_in_get_buffer_size(in);
+    else if(audio_extn_compr_cap_usecase_supported(in->usecase))
+        return audio_extn_compr_cap_get_buffer_size(in->config.format);
 
     return in->config.period_size * audio_stream_frame_size(stream);
 }
@@ -1865,6 +1869,8 @@
     if (in->pcm) {
         if (audio_extn_ssr_get_enabled() && popcount(in->channel_mask) == 6)
             ret = audio_extn_ssr_read(stream, buffer, bytes);
+        else if (audio_extn_compr_cap_usecase_supported(in->usecase))
+            ret = audio_extn_compr_cap_read(in, buffer, bytes);
         else
             ret = pcm_read(in->pcm, buffer, bytes);
     }
@@ -2383,8 +2389,7 @@
                   __func__, ret);
             goto err_open;
         }
-    }
-    if (channel_count == 6) {
+    } else if (channel_count == 6) {
         if(audio_extn_ssr_get_enabled()) {
             if(audio_extn_ssr_init(adev, in)) {
                 ALOGE("%s: audio_extn_ssr_init failed", __func__);
@@ -2395,6 +2400,9 @@
             ret = -EINVAL;
             goto err_open;
         }
+    } else if (audio_extn_compr_cap_enabled() &&
+            audio_extn_compr_cap_format_supported(config->format)) {
+        audio_extn_compr_cap_init(adev, in);
     } else {
         in->config.channels = channel_count;
         frame_size = audio_stream_frame_size((struct audio_stream *)in);
@@ -2434,6 +2442,9 @@
     }
     free(stream);
 
+    if(audio_extn_compr_cap_enabled() &&
+            audio_extn_compr_cap_format_supported(in->config.format))
+        audio_extn_compr_cap_deinit();
     return;
 }
 
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 9dca5c1..6ff6f40 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -64,6 +64,7 @@
 
     /* Capture usecases */
     USECASE_AUDIO_RECORD,
+    USECASE_AUDIO_RECORD_COMPRESS,
     USECASE_AUDIO_RECORD_LOW_LATENCY,
     USECASE_AUDIO_RECORD_FM_VIRTUAL,
 
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 089b2ec..43bef81 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -897,3 +897,9 @@
             return 0;
     }
 }
+
+int platform_update_usecase_from_source(int source, int usecase)
+{
+    ALOGV("%s: input source :%d", __func__, source);
+    return usecase;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 4a5bbc6..352c4d8 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -103,6 +103,7 @@
     [USECASE_AUDIO_PLAYBACK_OFFLOAD] =
                      {PLAYBACK_OFFLOAD_DEVICE, PLAYBACK_OFFLOAD_DEVICE},
     [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE, AUDIO_RECORD_PCM_DEVICE},
+    [USECASE_AUDIO_RECORD_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, COMPRESS_CAPTURE_DEVICE},
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
                                           LOWLATENCY_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_FM_VIRTUAL] = {MULTIMEDIA2_PCM_DEVICE,
@@ -1381,11 +1382,10 @@
     }
 }
 
-int platform_get_usecase_from_source(int source)
+int platform_update_usecase_from_source(int source, int usecase)
 {
     ALOGV("%s: input source :%d", __func__, source);
     if(source == AUDIO_SOURCE_FM_RX_A2DP)
-        return USECASE_AUDIO_RECORD_FM_VIRTUAL;
-    else
-        return USECASE_AUDIO_RECORD;
+        usecase = USECASE_AUDIO_RECORD_FM_VIRTUAL;
+    return usecase;
 }
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 9d4378d..7d80906 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -182,6 +182,7 @@
 #else
 #define LOWLATENCY_PCM_DEVICE 15
 #endif
+#define COMPRESS_CAPTURE_DEVICE 19
 
 #ifdef PLATFORM_MSM8x26
 #define VOICE_CALL_PCM_DEVICE 2
diff --git a/hal/platform_api.h b/hal/platform_api.h
index b9919ae..dac3fd9 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -47,6 +47,6 @@
 
 /* returns the latency for a usecase in Us */
 int64_t platform_render_latency(audio_usecase_t usecase);
-int platform_get_usecase_from_source(int source);
+int platform_update_usecase_from_source(int source, audio_usecase_t usecase);
 
 #endif // QCOM_AUDIO_PLATFORM_API_H