Merge "Revert "Add sensorhub based on Freescale sensor framework"" into mnc-brillo-dev
diff --git a/peripheral/alsa/Android.mk b/peripheral/alsa/Android.mk
new file mode 100755
index 0000000..b46bfcc
--- /dev/null
+++ b/peripheral/alsa/Android.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2008 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.
+
+# This is the Freescale ALSA module for i.MX.
+
+ifeq ($(findstring imx, $(soc_name)), imx)
+
+ifeq ($(strip $(BOARD_USES_ALSA_AUDIO)),true)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_SRC_FILES := tinyalsa_hal.c
+LOCAL_C_INCLUDES += \
+	external/tinyalsa/include \
+	system/media/audio_utils/include \
+	system/media/audio_effects/include
+LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libdl
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
+
+endif
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio.primary.$(soc_name)
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := tinyalsa_hal.c control.c
+LOCAL_C_INCLUDES += \
+	external/tinyalsa/include \
+	system/media/audio_utils/include \
+	system/media/audio_effects/include
+LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libdl
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -DBRILLO
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/peripheral/alsa/audio_hardware.h b/peripheral/alsa/audio_hardware.h
new file mode 100644
index 0000000..d3d109f
--- /dev/null
+++ b/peripheral/alsa/audio_hardware.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_AUDIO_HARDWARE_H
+#define ANDROID_INCLUDE_IMX_AUDIO_HARDWARE_H
+
+#include <stdlib.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <hardware/hardware.h>
+
+
+#define MIN(x, y) ((x) > (y) ? (y) : (x))
+
+enum tty_modes {
+    TTY_MODE_OFF,
+    TTY_MODE_VCO,
+    TTY_MODE_HCO,
+    TTY_MODE_FULL
+};
+
+enum pcm_type {
+    PCM_NORMAL = 0,
+    PCM_HDMI,
+    PCM_ESAI,
+    PCM_TOTAL,
+};
+
+enum output_type {
+    OUTPUT_DEEP_BUF,      // deep PCM buffers output stream
+    OUTPUT_PRIMARY,   // low latency output stream
+    OUTPUT_HDMI,
+    OUTPUT_ESAI,
+    OUTPUT_TOTAL
+};
+
+struct route_setting
+{
+    char *ctl_name;
+    int intval;
+    char *strval;
+};
+
+
+struct audio_card{
+    char * name;
+    char * driver_name;
+    int  supported_out_devices;
+    int  supported_in_devices;
+    struct route_setting *defaults;
+    struct route_setting *bt_output;
+    struct route_setting *speaker_output;
+    struct route_setting *hs_output;
+    struct route_setting *earpiece_output;
+    struct route_setting *vx_hs_mic_input;
+    struct route_setting *mm_main_mic_input;
+    struct route_setting *vx_main_mic_input;
+    struct route_setting *mm_hs_mic_input;
+    struct route_setting *vx_bt_mic_input;
+    struct route_setting *mm_bt_mic_input;
+    int  card;
+    unsigned int  out_rate;
+    int  out_channels;
+    int  out_format;
+    unsigned int  in_rate;
+    int  in_channels;
+    int  in_format;
+};
+
+#define MAX_AUDIO_CARD_NUM  4
+#define MAX_AUDIO_CARD_SCAN 4
+
+#define MAX_SUP_CHANNEL_NUM  20
+#define MAX_SUP_RATE_NUM     20
+
+struct imx_audio_device {
+    struct audio_hw_device hw_device;
+
+    pthread_mutex_t lock;       /* see note below on mutex acquisition order */
+    int mode;
+    int in_device;
+    int in_card_idx; /* the index for array card_list and mixer */
+    int out_device;
+    struct pcm *pcm_modem_dl;
+    struct pcm *pcm_modem_ul;
+    int in_call;
+    float voice_volume;
+    struct imx_stream_in  *active_input;                /*1 input stream, 2 outout stream*/
+    struct imx_stream_out *active_output[OUTPUT_TOTAL];
+    bool mic_mute;
+    int tty_mode;
+    struct echo_reference_itfe *echo_reference;
+    bool bluetooth_nrec;
+    bool device_is_auto;
+    int  wb_amr;
+    bool low_power;
+    struct audio_card *card_list[MAX_AUDIO_CARD_NUM];
+    struct mixer *mixer[MAX_AUDIO_CARD_NUM];
+    int audio_card_num;
+    unsigned int default_rate;               /*HAL input samplerate*/
+    unsigned int mm_rate;                    /*HAL hardware output samplerate*/
+    char usb_card_name[128];
+};
+
+struct imx_stream_out {
+    struct audio_stream_out stream;
+
+    pthread_mutex_t lock;       /* see note below on mutex acquisition order */
+    struct pcm_config config[PCM_TOTAL];
+    struct pcm *pcm[PCM_TOTAL];
+    int writeContiFailCount[PCM_TOTAL];
+    struct resampler_itfe *resampler[PCM_TOTAL];
+    char *buffer;
+    int standby;
+    struct echo_reference_itfe *echo_reference;
+    struct imx_audio_device *dev;
+    int write_threshold[PCM_TOTAL];
+    bool low_power;
+    int write_flags[PCM_TOTAL];
+    int device;
+    size_t buffer_frames;
+    uint64_t written;
+    audio_channel_mask_t channel_mask;
+    audio_channel_mask_t sup_channel_masks[3];
+    int sup_rates[MAX_SUP_RATE_NUM];
+};
+
+#define MAX_PREPROCESSORS 3 /* maximum one AGC + one NS + one AEC per input stream */
+struct effect_info_s {
+    effect_handle_t effect_itfe;
+    size_t num_channel_configs;
+    channel_config_t* channel_configs;
+};
+
+#define NUM_IN_AUX_CNL_CONFIGS 2
+channel_config_t in_aux_cnl_configs[NUM_IN_AUX_CNL_CONFIGS] = {
+    { AUDIO_CHANNEL_IN_FRONT , AUDIO_CHANNEL_IN_BACK},
+    { AUDIO_CHANNEL_IN_STEREO , AUDIO_CHANNEL_IN_RIGHT}
+};
+
+#define MAX_NUM_CHANNEL_CONFIGS 10
+
+struct imx_stream_in {
+    struct audio_stream_in stream;
+
+    pthread_mutex_t lock;       /* see note below on mutex acquisition order */
+    struct pcm_config config;
+    struct pcm *pcm;
+    int device;
+    struct resampler_itfe *resampler;
+    struct resampler_buffer_provider buf_provider;
+    int16_t *read_buf;
+    size_t read_buf_size;
+    size_t read_buf_frames;
+    int32_t *read_tmp_buf;
+    size_t read_tmp_buf_size;
+    size_t read_tmp_buf_frames;
+
+    unsigned int requested_rate;
+    unsigned int requested_format;
+    unsigned int requested_channel;
+    int standby;
+    int source;
+    struct echo_reference_itfe *echo_reference;
+    bool need_echo_reference;
+    struct effect_info_s preprocessors[MAX_PREPROCESSORS];
+    int num_preprocessors;
+
+    int16_t *proc_buf_in;
+    int16_t *proc_buf_out;
+    size_t proc_buf_size;
+    size_t proc_buf_frames;
+
+    int16_t *ref_buf;
+    size_t ref_buf_size;
+    size_t ref_frames_in;
+    int read_status;
+    size_t mute_500ms;
+    struct imx_audio_device *dev;
+    int last_time_of_xrun;
+    bool aux_channels_changed;
+    uint32_t main_channels;
+    uint32_t aux_channels;
+};
+#define STRING_TO_ENUM(string) { #string, string }
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+struct string_to_enum {
+    const char *name;
+    uint32_t value;
+};
+
+#define SUPPORTED_DEVICE_OUT_MODULE               \
+          ( AUDIO_DEVICE_OUT_EARPIECE |          \
+            AUDIO_DEVICE_OUT_SPEAKER |           \
+            AUDIO_DEVICE_OUT_WIRED_HEADSET |     \
+            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |   \
+            AUDIO_DEVICE_OUT_ALL_SCO |           \
+            AUDIO_DEVICE_OUT_AUX_DIGITAL |       \
+            AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | \
+            AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | \
+            AUDIO_DEVICE_OUT_REMOTE_SUBMIX |     \
+            AUDIO_DEVICE_OUT_DEFAULT)
+
+#define SUPPORTED_DEVICE_IN_MODULE               \
+          ( AUDIO_DEVICE_IN_COMMUNICATION |      \
+            AUDIO_DEVICE_IN_AMBIENT |            \
+            AUDIO_DEVICE_IN_BUILTIN_MIC |        \
+            AUDIO_DEVICE_IN_ALL_SCO |            \
+            AUDIO_DEVICE_IN_WIRED_HEADSET |      \
+            AUDIO_DEVICE_IN_AUX_DIGITAL |        \
+            AUDIO_DEVICE_IN_VOICE_CALL   |       \
+            AUDIO_DEVICE_IN_BACK_MIC   |         \
+            AUDIO_DEVICE_IN_REMOTE_SUBMIX |      \
+            AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET |  \
+            AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET |  \
+            AUDIO_DEVICE_IN_USB_ACCESSORY |      \
+            AUDIO_DEVICE_IN_USB_DEVICE  |        \
+            AUDIO_DEVICE_IN_DEFAULT )
+
+#endif  /* ANDROID_INCLUDE_IMX_AUDIO_HARDWARE_H */
diff --git a/peripheral/alsa/config_cs42888.h b/peripheral/alsa/config_cs42888.h
new file mode 100644
index 0000000..162ccf1
--- /dev/null
+++ b/peripheral/alsa/config_cs42888.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012-2014 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_CS42888_H
+#define ANDROID_INCLUDE_IMX_CONFIG_CS42888_H
+
+#include "audio_hardware.h"
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  cs42888_card = {
+    .name = "cs42888-audio",
+    .driver_name = "cs42888-audio",
+    .supported_out_devices = (AUDIO_DEVICE_OUT_EARPIECE |
+            AUDIO_DEVICE_OUT_SPEAKER |
+            AUDIO_DEVICE_OUT_WIRED_HEADSET |
+            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+            AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+            AUDIO_DEVICE_OUT_ALL_SCO |
+            AUDIO_DEVICE_OUT_DEFAULT ),
+    .supported_in_devices = (
+            AUDIO_DEVICE_IN_COMMUNICATION |
+            AUDIO_DEVICE_IN_AMBIENT |
+            AUDIO_DEVICE_IN_BUILTIN_MIC |
+            AUDIO_DEVICE_IN_WIRED_HEADSET |
+            AUDIO_DEVICE_IN_BACK_MIC |
+            AUDIO_DEVICE_IN_ALL_SCO |
+            AUDIO_DEVICE_IN_DEFAULT),
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = NULL,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = NULL,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_CS42888_H */
diff --git a/peripheral/alsa/config_hdmi.h b/peripheral/alsa/config_hdmi.h
new file mode 100644
index 0000000..83dd242
--- /dev/null
+++ b/peripheral/alsa/config_hdmi.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_HDMI_H
+#define ANDROID_INCLUDE_IMX_CONFIG_HDMI_H
+
+#include "audio_hardware.h"
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  hdmi_card = {
+    .name = "imx-hdmi-soc",
+    .driver_name = "imx-hdmi-soc",
+    .supported_out_devices  = AUDIO_DEVICE_OUT_AUX_DIGITAL,
+    .supported_in_devices   = 0,
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = NULL,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = NULL,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_HDMI_H */
diff --git a/peripheral/alsa/config_nullcard.h b/peripheral/alsa/config_nullcard.h
new file mode 100644
index 0000000..9cd3a0a
--- /dev/null
+++ b/peripheral/alsa/config_nullcard.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_NULLCARD_H
+#define ANDROID_INCLUDE_IMX_CONFIG_NULLCARD_H
+
+#include "audio_hardware.h"
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  null_card = {
+    .name = "null_card",
+    .driver_name = "null_card",
+    .supported_out_devices  = 0,
+    .supported_in_devices   = 0,
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = NULL,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = NULL,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = -1,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_NULLCARD_H */
diff --git a/peripheral/alsa/config_sii902x.h b/peripheral/alsa/config_sii902x.h
new file mode 100644
index 0000000..f05351b
--- /dev/null
+++ b/peripheral/alsa/config_sii902x.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2015 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_SII902X_H
+#define ANDROID_INCLUDE_IMX_CONFIG_SII902X_H
+
+#include "audio_hardware.h"
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  sii902x_card = {
+    .name = "sii902x-audio",
+    .driver_name = "sii902x-audio",
+    .supported_out_devices  = AUDIO_DEVICE_OUT_AUX_DIGITAL,
+    .supported_in_devices   = 0,
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = NULL,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = NULL,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_SII902X_H */
diff --git a/peripheral/alsa/config_spdif.h b/peripheral/alsa/config_spdif.h
new file mode 100644
index 0000000..ea75d28
--- /dev/null
+++ b/peripheral/alsa/config_spdif.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_SPDIF_H
+#define ANDROID_INCLUDE_IMX_CONFIG_SPDIF_H
+
+#include "audio_hardware.h"
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  spdif_card = {
+    .name = "imx-spdif",
+    .driver_name = "imx-spdif",
+    .supported_out_devices   = 0,
+    .supported_in_devices    = AUDIO_DEVICE_IN_AUX_DIGITAL,
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = NULL,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = NULL,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_SPDIF_H */
diff --git a/peripheral/alsa/config_usbaudio.h b/peripheral/alsa/config_usbaudio.h
new file mode 100644
index 0000000..6419b59
--- /dev/null
+++ b/peripheral/alsa/config_usbaudio.h
@@ -0,0 +1,50 @@
+
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012-2014 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_USBAUDIO_H
+#define ANDROID_INCLUDE_IMX_CONFIG_USBAUDIO_H
+
+#include "audio_hardware.h"
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  usbaudio_card = {
+    .name = "USB Device",
+    .driver_name = "USB-Audio",
+    .supported_out_devices  = AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
+    .supported_in_devices   = AUDIO_DEVICE_IN_USB_DEVICE|AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET,
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = NULL,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = NULL,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_USBAUDIO_H */
diff --git a/peripheral/alsa/config_wm8958.h b/peripheral/alsa/config_wm8958.h
new file mode 100644
index 0000000..6d9533f
--- /dev/null
+++ b/peripheral/alsa/config_wm8958.h
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012-2014 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_WM8958_H
+#define ANDROID_INCLUDE_IMX_CONFIG_WM8958_H
+
+#include "audio_hardware.h"
+
+#define MIXER_WM8958_AIF1DAC_MUX                    "AIF1DAC Mux"
+#define MIXER_WM8958_AIF2DAC_MUX                    "AIF2DAC Mux"
+#define MIXER_WM8958_AIF2ADC_MUX                    "AIF2ADC Mux"
+#define MIXER_WM8958_AIF3ADC_MUX                    "AIF3ADC Mux"
+
+#define MIXER_WM8958_AIF1ADC1L_MIXER_AIF2_SWITCH           "AIF1ADC1L Mixer AIF2 Switch"
+#define MIXER_WM8958_AIF1ADC1R_MIXER_AIF2_SWITCH           "AIF1ADC1R Mixer AIF2 Switch"
+
+#define MIXER_WM8958_DAC1L_MIXER_AIF1_1_SWITCH      "DAC1L Mixer AIF1.1 Switch"
+#define MIXER_WM8958_DAC1L_MIXER_AIF1_2_SWITCH      "DAC1L Mixer AIF1.2 Switch"
+#define MIXER_WM8958_DAC1L_MIXER_AIF2_SWITCH        "DAC1L Mixer AIF2 Switch"
+#define MIXER_WM8958_DAC1L_MIXER_LEFT_SIDETONE_SWITCH   "DAC1L Mixer Left Sidetone Switch"
+#define MIXER_WM8958_DAC1L_MIXER_RIGHT_SIDETONE_SWITCH  "DAC1L Mixer Right Sidetone Switch"
+
+#define MIXER_WM8958_DAC1R_MIXER_AIF1_1_SWITCH      "DAC1R Mixer AIF1.1 Switch"
+#define MIXER_WM8958_DAC1R_MIXER_AIF1_2_SWITCH      "DAC1R Mixer AIF1.2 Switch"
+#define MIXER_WM8958_DAC1R_MIXER_AIF2_SWITCH        "DAC1R Mixer AIF2 Switch"
+#define MIXER_WM8958_DAC1R_MIXER_LEFT_SIDETONE_SWITCH   "DAC1R Mixer Left Sidetone Switch"
+#define MIXER_WM8958_DAC1R_MIXER_RIGHT_SIDETONE_SWITCH  "DAC1R Mixer Right Sidetone Switch"
+
+#define MIXER_WM8958_SPEAKER_SWITCH                 "Speaker Switch"
+#define MIXER_WM8958_SPEAKER_VOLUME                 "Speaker Volume"
+#define MIXER_WM8958_EARPIECE_SWITCH                "Earpiece Switch"
+#define MIXER_WM8958_EARPIECE_VOLUME                "Earpiece Volume"
+#define MIXER_WM8958_HEADPHONE_SWITCH               "Headphone Switch"
+#define MIXER_WM8958_HEADPHONE_VOLUME               "Headphone Volume"
+
+#define MIXER_WM8958_LEFT_HEADPHONE_MUX             "Left Headphone Mux"
+#define MIXER_WM8958_RIGHT_HEADPHONE_MUX            "Right Headphone Mux"
+
+#define MIXER_WM8958_DAC1_SWITCH                    "DAC1 Switch"
+#define MIXER_WM8958_DAC2_SWITCH                    "DAC2 Switch"
+
+#define MIXER_WM8958_SPKL_DAC1_SWITCH               "SPKL DAC1 Switch"
+#define MIXER_WM8958_SPKL_DAC1_VOLUME               "SPKL DAC1 Volume"
+#define MIXER_WM8958_SPKR_DAC1_SWITCH               "SPKR DAC1 Switch"
+#define MIXER_WM8958_SPKR_DAC1_VOLUME               "SPKR DAC1 Volume"
+
+#define MIXER_WM8958_SPKL_BOOST_SPKL_SWITCH         "SPKL Boost SPKL Switch"
+#define MIXER_WM8958_SPKR_BOOST_SPKR_SWITCH         "SPKR Boost SPKR Switch"
+#define MIXER_WM8958_SPEAKER_MIXER_VOLUME           "Speaker Mixer Volume"
+
+#define MIXER_WM8958_IN1R_SWITCH                    "IN1R Switch"
+#define MIXER_WM8958_IN1R_VOLUME                    "IN1R Volume"
+#define MIXER_WM8958_IN1L_SWITCH                    "IN1L Switch"
+#define MIXER_WM8958_IN1L_VOLUME                    "IN1L Volume"
+
+#define MIXER_WM8958_MIXINL_IN1L_VOLUME             "MIXINL IN1L Volume"
+#define MIXER_WM8958_MIXINL_IN1L_SWITCH             "MIXINL IN1L Switch"
+#define MIXER_WM8958_MIXINR_IN1R_VOLUME             "MIXINR IN1R Volume"
+#define MIXER_WM8958_MIXINR_IN1R_SWITCH             "MIXINR IN1R Switch"
+
+#define MIXER_WM8958_IN1L_PGA_IN1LN_SWITCH          "IN1L PGA IN1LN Switch"
+#define MIXER_WM8958_IN1L_PGA_IN1LP_SWITCH          "IN1L PGA IN1LP Switch"
+#define MIXER_WM8958_IN1R_PGA_IN1RN_SWITCH          "IN1R PGA IN1RN Switch"
+#define MIXER_WM8958_IN1R_PGA_IN1RP_SWITCH          "IN1R PGA IN1RP Switch"
+
+#define MIXER_WM8958_ADCL_MUX                       "ADCL Mux"
+#define MIXER_WM8958_ADCR_MUX                       "ADCR Mux"
+
+#define MIXER_WM8958_ADC_OSR                        "ADC OSR"
+
+#define MIXER_WM8958_DAC2_LEFT_SIDETONE_VOLUME      "DAC2 Left Sidetone Volume"
+#define MIXER_WM8958_DAC2_RIGHT_SIDETONE_VOLUME     "DAC2 Right Sidetone Volume"
+
+#define MIXER_WM8958_AIF2DAC2L_MIXER_LEFT_SIDETONE_SWITCH   "AIF2DAC2L Mixer Left Sidetone Switch"
+#define MIXER_WM8958_AIF2DAC2R_MIXER_RIGHT_SIDETONE_SWITCH  "AIF2DAC2R Mixer Right Sidetone Switch"
+
+#define MIXER_WM8958_AIF1ADC1L_MIXER_ADCDMIC_SWITCH     "AIF1ADC1L Mixer ADC/DMIC Switch"
+#define MIXER_WM8958_AIF1ADC1R_MIXER_ADCDMIC_SWITCH     "AIF1ADC1R Mixer ADC/DMIC Switch"
+
+#define MIXER_WM8958_AIF1ADC1L_MIXER_AIF2_SWITCH        "AIF1ADC1L Mixer AIF2 Switch"
+#define MIXER_WM8958_AIF1ADC1R_MIXER_AIF2_SWITCH        "AIF1ADC1R Mixer AIF2 Switch"
+
+#define MIXER_WM8958_LEFT_SIDETONE                      "Left Sidetone"
+#define MIXER_WM8958_RIGHT_SIDETONE                     "Right Sidetone"
+
+
+
+/* These are values that never change */
+static struct route_setting defaults_wm8958[] = {
+    /* general */
+    {
+        .ctl_name = MIXER_WM8958_AIF1DAC_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF2DAC_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1L_MIXER_AIF1_1_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1L_MIXER_AIF1_2_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1L_MIXER_AIF2_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1L_MIXER_LEFT_SIDETONE_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1L_MIXER_RIGHT_SIDETONE_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1R_MIXER_AIF1_1_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1R_MIXER_AIF1_2_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1R_MIXER_AIF2_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1R_MIXER_LEFT_SIDETONE_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1R_MIXER_RIGHT_SIDETONE_SWITCH,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC1_SWITCH,
+        .intval = 1,
+    },
+    /* headset */
+
+    /* bt */
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting bt_output_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_AIF2ADC_MUX,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF3ADC_MUX,
+        .intval = 2,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting speaker_output_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_SPKL_DAC1_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPKL_DAC1_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPKR_DAC1_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPKR_DAC1_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPKL_BOOST_SPKL_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPKR_BOOST_SPKR_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPEAKER_MIXER_VOLUME,
+        .intval = 3,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPEAKER_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_SPEAKER_VOLUME,
+        .intval = 60,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting hs_output_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_LEFT_HEADPHONE_MUX,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_RIGHT_HEADPHONE_MUX,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_HEADPHONE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_HEADPHONE_VOLUME,
+        .intval = 57,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting earpiece_output_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_EARPIECE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_EARPIECE_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting vx_hs_mic_input_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_IN1L_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1L_VOLUME,
+        .intval = 27,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINL_IN1L_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINL_IN1L_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1L_PGA_IN1LN_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1L_PGA_IN1LP_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_ADCL_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_ADC_OSR,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC2_LEFT_SIDETONE_VOLUME,
+        .intval = 12,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF2ADC_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF2DAC2L_MIXER_LEFT_SIDETONE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+
+static struct route_setting mm_main_mic_input_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_IN1L_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1L_VOLUME,
+        .intval = 27,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINL_IN1L_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINL_IN1L_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1L_PGA_IN1LN_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1L_PGA_IN1LP_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_ADCL_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF1ADC1L_MIXER_ADCDMIC_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+
+static struct route_setting vx_main_mic_input_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_IN1R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1R_VOLUME,
+        .intval = 27,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINR_IN1R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINR_IN1R_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1R_PGA_IN1RN_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1R_PGA_IN1RP_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_ADCR_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_ADC_OSR,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC2_RIGHT_SIDETONE_VOLUME,
+        .intval = 12,
+    },
+    {
+        .ctl_name = MIXER_WM8958_DAC2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF2ADC_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF2DAC2R_MIXER_RIGHT_SIDETONE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+/*hs_mic exchanged with main mic for sabresd, because the the main is no implemented*/
+static struct route_setting mm_hs_mic_input_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_IN1R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1R_VOLUME,
+        .intval = 27,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINR_IN1R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_MIXINR_IN1R_VOLUME,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1R_PGA_IN1RN_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_IN1R_PGA_IN1RP_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_ADCR_MUX,
+        .intval = 0,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF1ADC1L_MIXER_ADCDMIC_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting vx_bt_mic_input_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_AIF1ADC1L_MIXER_AIF2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF1ADC1R_MIXER_AIF2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+
+static struct route_setting mm_bt_mic_input_wm8958[] = {
+    {
+        .ctl_name = MIXER_WM8958_AIF1ADC1L_MIXER_AIF2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8958_AIF1ADC1R_MIXER_AIF2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  wm8958_card = {
+    .name = "wm8958-audio",
+    .driver_name = "wm8958-audio",
+    .supported_out_devices = (AUDIO_DEVICE_OUT_EARPIECE |
+            AUDIO_DEVICE_OUT_SPEAKER |
+            AUDIO_DEVICE_OUT_WIRED_HEADSET |
+            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+            AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+            AUDIO_DEVICE_OUT_ALL_SCO |
+            AUDIO_DEVICE_OUT_DEFAULT),
+    .supported_in_devices = (
+            AUDIO_DEVICE_IN_COMMUNICATION |
+            AUDIO_DEVICE_IN_AMBIENT |
+            AUDIO_DEVICE_IN_BUILTIN_MIC |
+            AUDIO_DEVICE_IN_WIRED_HEADSET |
+            AUDIO_DEVICE_IN_BACK_MIC |
+            AUDIO_DEVICE_IN_ALL_SCO |
+            AUDIO_DEVICE_IN_DEFAULT),
+    .defaults            = defaults_wm8958,
+    .bt_output           = bt_output_wm8958,
+    .speaker_output      = speaker_output_wm8958,
+    .hs_output           = hs_output_wm8958,
+    .earpiece_output     = earpiece_output_wm8958,
+    .vx_hs_mic_input     = vx_hs_mic_input_wm8958,
+    .mm_main_mic_input   = mm_main_mic_input_wm8958,
+    .vx_main_mic_input   = vx_main_mic_input_wm8958,
+    .mm_hs_mic_input     = mm_hs_mic_input_wm8958,
+    .vx_bt_mic_input     = vx_bt_mic_input_wm8958,
+    .mm_bt_mic_input     = mm_bt_mic_input_wm8958,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_WM8958_H */
diff --git a/peripheral/alsa/config_wm8960.h b/peripheral/alsa/config_wm8960.h
new file mode 100755
index 0000000..a250cc0
--- /dev/null
+++ b/peripheral/alsa/config_wm8960.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2015 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_WM8960_H
+#define ANDROID_INCLUDE_IMX_CONFIG_WM8960_H
+
+#include "audio_hardware.h"
+
+
+#define MIXER_WM8960_SPEAKER_VOLUME                 "Speaker Playback Volume"
+#define MIXER_WM8960_HEADPHONE_VOLUME               "Headphone Playback Volume"
+#define MIXER_WM8960_PLAYBACK_VOLUME                "Playback Volume"
+
+#define MIXER_WM8960_LEFT_OUTPUT_SWITCH             "Left Output Mixer PCM Playback Switch"
+#define MIXER_WM8960_RIGHT_OUTPUT_SWITCH            "Right Output Mixer PCM Playback Switch"
+
+#define MIXER_WM8960_CAPTURE_SWITCH                 "Capture Switch"
+#define MIXER_WM8960_CAPTURE_VOLUME                 "Capture Volume"
+
+#define MIXER_WM8960_ALC_FUNCTION                   "ALC Function"
+#define MIXER_WM8960_LEFT_INPUT_SWITCH              "Left Input Mixer Boost Switch"
+#define MIXER_WM8960_ADC_PCM_CAPTURE_VOLUME         "ADC PCM Capture Volume"
+
+#ifdef BRILLO
+#define MIXER_WM8960_LEFT_INPUT1_SWITCH             "Left Boost Mixer LINPUT1 Switch"
+#define MIXER_WM8960_LEFT_INPUT2_SWITCH             "Left Boost Mixer LINPUT2 Switch"
+#define MIXER_WM8960_LEFT_INPUT3_SWITCH             "Left Boost Mixer LINPUT3 Switch"
+#define MIXER_WM8960_RIGHT_INPUT_SWITCH             "Right Input Mixer Boost Switch"
+#define MIXER_WM8960_RIGHT_INPUT1_SWITCH            "Right Boost Mixer LINPUT1 Switch"
+#define MIXER_WM8960_RIGHT_INPUT2_SWITCH            "Right Boost Mixer LINPUT2 Switch"
+#endif
+
+static struct route_setting speaker_output_wm8960[] = {
+    {
+        .ctl_name = MIXER_WM8960_LEFT_OUTPUT_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_RIGHT_OUTPUT_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_PLAYBACK_VOLUME,
+        .intval = 230,
+    },
+    {
+        .ctl_name = MIXER_WM8960_SPEAKER_VOLUME,
+        .intval = 120,
+    },
+    {
+        .ctl_name = MIXER_WM8960_HEADPHONE_VOLUME,
+        .intval = 120,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting mm_main_mic_input_wm8960[] = {
+    {
+        .ctl_name = MIXER_WM8960_ALC_FUNCTION,
+        .intval = 3,
+    },
+    {
+        .ctl_name = MIXER_WM8960_LEFT_INPUT_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_ADC_PCM_CAPTURE_VOLUME,
+        .intval = 230,
+    },
+    {
+        .ctl_name = MIXER_WM8960_CAPTURE_VOLUME,
+        .intval = 60,
+    },
+#ifdef BRILLO
+    {
+        .ctl_name = MIXER_WM8960_LEFT_INPUT1_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_LEFT_INPUT2_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_LEFT_INPUT3_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_RIGHT_INPUT_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_RIGHT_INPUT1_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8960_RIGHT_INPUT2_SWITCH,
+        .intval = 1,
+    },
+#endif
+    {
+        .ctl_name = NULL,
+    },
+};
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  wm8960_card = {
+    .name = "wm8960-audio",
+    .driver_name = "wm8960-audio",
+    .supported_out_devices = (AUDIO_DEVICE_OUT_EARPIECE |
+            AUDIO_DEVICE_OUT_SPEAKER |
+            AUDIO_DEVICE_OUT_WIRED_HEADSET |
+            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+            AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+            AUDIO_DEVICE_OUT_ALL_SCO |
+            AUDIO_DEVICE_OUT_DEFAULT ),
+    .supported_in_devices = (
+            AUDIO_DEVICE_IN_COMMUNICATION |
+            AUDIO_DEVICE_IN_AMBIENT |
+            AUDIO_DEVICE_IN_BUILTIN_MIC |
+            AUDIO_DEVICE_IN_WIRED_HEADSET |
+            AUDIO_DEVICE_IN_BACK_MIC |
+            AUDIO_DEVICE_IN_ALL_SCO |
+            AUDIO_DEVICE_IN_DEFAULT),
+    .defaults            = NULL,
+    .bt_output           = NULL,
+    .speaker_output      = speaker_output_wm8960,
+    .hs_output           = NULL,
+    .earpiece_output     = NULL,
+    .vx_hs_mic_input     = NULL,
+    .mm_main_mic_input   = mm_main_mic_input_wm8960,
+    .vx_main_mic_input   = NULL,
+    .mm_hs_mic_input     = NULL,
+    .vx_bt_mic_input     = NULL,
+    .mm_bt_mic_input     = NULL,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_WM8960_H */
diff --git a/peripheral/alsa/config_wm8962.h b/peripheral/alsa/config_wm8962.h
new file mode 100644
index 0000000..582be53
--- /dev/null
+++ b/peripheral/alsa/config_wm8962.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012-2014 Freescale Semiconductor, Inc. */
+
+#ifndef ANDROID_INCLUDE_IMX_CONFIG_WM8962_H
+#define ANDROID_INCLUDE_IMX_CONFIG_WM8962_H
+
+#include "audio_hardware.h"
+
+
+#define MIXER_WM8962_SPEAKER_VOLUME                 "Speaker Volume"
+#define MIXER_WM8962_SPEAKER_SWITCH                 "Speaker Switch"
+#define MIXER_WM8962_HEADPHONE_VOLUME               "Headphone Volume"
+#define MIXER_WM8962_HEADPHONE_SWITCH               "Headphone Switch"
+
+#define MIXER_WM8962_CAPTURE_SWITCH                 "Capture Switch"
+#define MIXER_WM8962_CAPTURE_VOLUME                 "Capture Volume"
+
+#define MIXER_WM8962_INPGAR_IN3R_SWITCH             "INPGAR IN3R Switch"
+#define MIXER_WM8962_MIXINR_IN3R_SWITCH             "MIXINR IN3R Switch"
+#define MIXER_WM8962_MIXINR_IN3R_VOLUME             "MIXINR IN3R Volume"
+
+#define MIXER_WM8962_MIXINR_PGA_SWITCH              "MIXINR PGA Switch"
+#define MIXER_WM8962_MIXINR_PGA_VOLUME              "MIXINR PGA Volume"
+
+#define MIXER_WM8962_DIGITAL_CAPTURE_VOLUME         "Digital Capture Volume"
+
+#define MIXER_WM8962_DIGITAL_PLAYBACK_VOLUME        "Digital Playback Volume"
+
+
+/* These are values that never change */
+static struct route_setting defaults_wm8962[] = {
+    /* general */
+    {
+        .ctl_name = MIXER_WM8962_DIGITAL_PLAYBACK_VOLUME,
+        .intval = 96,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting bt_output_wm8962[] = {
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting speaker_output_wm8962[] = {
+    {
+        .ctl_name = MIXER_WM8962_SPEAKER_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_SPEAKER_VOLUME,
+        .intval = 121,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting hs_output_wm8962[] = {
+    {
+        .ctl_name = MIXER_WM8962_HEADPHONE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_HEADPHONE_VOLUME,
+        .intval = 121,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting earpiece_output_wm8962[] = {
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting vx_hs_mic_input_wm8962[] = {
+    {
+        .ctl_name = NULL,
+    },
+};
+
+
+static struct route_setting mm_main_mic_input_wm8962[] = {
+    {
+        .ctl_name = MIXER_WM8962_CAPTURE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_CAPTURE_VOLUME,
+        .intval = 63,
+    },
+    {
+        .ctl_name = MIXER_WM8962_DIGITAL_CAPTURE_VOLUME,
+        .intval = 127,
+    },/*
+    {
+        .ctl_name = MIXER_WM8962_INPGAR_IN3R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_PGA_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_PGA_VOLUME,
+        .intval = 7,
+    },*/
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_IN3R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_IN3R_VOLUME,
+        .intval = 7,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+
+static struct route_setting vx_main_mic_input_wm8962[] = {
+    {
+        .ctl_name = NULL,
+    },
+};
+
+/*hs_mic exchanged with main mic for sabresd, because the the main is no implemented*/
+static struct route_setting mm_hs_mic_input_wm8962[] = {
+    {
+        .ctl_name = MIXER_WM8962_CAPTURE_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_CAPTURE_VOLUME,
+        .intval = 63,
+    },
+    {
+        .ctl_name = MIXER_WM8962_DIGITAL_CAPTURE_VOLUME,
+        .intval = 127,
+    },/*
+    {
+        .ctl_name = MIXER_WM8962_INPGAR_IN3R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_PGA_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_PGA_VOLUME,
+        .intval = 7,
+    },*/
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_IN3R_SWITCH,
+        .intval = 1,
+    },
+    {
+        .ctl_name = MIXER_WM8962_MIXINR_IN3R_VOLUME,
+        .intval = 7,
+    },
+    {
+        .ctl_name = NULL,
+    },
+};
+
+static struct route_setting vx_bt_mic_input_wm8962[] = {
+    {
+        .ctl_name = NULL,
+    },
+};
+
+
+static struct route_setting mm_bt_mic_input_wm8962[] = {
+    {
+        .ctl_name = NULL,
+    },
+};
+
+/* ALSA cards for IMX, these must be defined according different board / kernel config*/
+static struct audio_card  wm8962_card = {
+    .name = "wm8962-audio",
+    .driver_name = "wm8962-audio",
+    .supported_out_devices = (AUDIO_DEVICE_OUT_EARPIECE |
+            AUDIO_DEVICE_OUT_SPEAKER |
+            AUDIO_DEVICE_OUT_WIRED_HEADSET |
+            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+            AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+            AUDIO_DEVICE_OUT_ALL_SCO |
+            AUDIO_DEVICE_OUT_DEFAULT ),
+    .supported_in_devices = (
+            AUDIO_DEVICE_IN_COMMUNICATION |
+            AUDIO_DEVICE_IN_AMBIENT |
+            AUDIO_DEVICE_IN_BUILTIN_MIC |
+            AUDIO_DEVICE_IN_WIRED_HEADSET |
+            AUDIO_DEVICE_IN_BACK_MIC |
+            AUDIO_DEVICE_IN_ALL_SCO |
+            AUDIO_DEVICE_IN_DEFAULT),
+    .defaults            = defaults_wm8962,
+    .bt_output           = bt_output_wm8962,
+    .speaker_output      = speaker_output_wm8962,
+    .hs_output           = hs_output_wm8962,
+    .earpiece_output     = earpiece_output_wm8962,
+    .vx_hs_mic_input     = vx_hs_mic_input_wm8962,
+    .mm_main_mic_input   = mm_main_mic_input_wm8962,
+    .vx_main_mic_input   = vx_main_mic_input_wm8962,
+    .mm_hs_mic_input     = mm_hs_mic_input_wm8962,
+    .vx_bt_mic_input     = vx_bt_mic_input_wm8962,
+    .mm_bt_mic_input     = mm_bt_mic_input_wm8962,
+    .card                = 0,
+    .out_rate            = 0,
+    .out_channels        = 0,
+    .out_format          = 0,
+    .in_rate             = 0,
+    .in_channels         = 0,
+    .in_format           = 0,
+};
+
+#endif  /* ANDROID_INCLUDE_IMX_CONFIG_WM8962_H */
diff --git a/peripheral/alsa/control.c b/peripheral/alsa/control.c
new file mode 100755
index 0000000..69fe7c7
--- /dev/null
+++ b/peripheral/alsa/control.c
@@ -0,0 +1,254 @@
+/*
+** Copyright 2011, The Android Open Source Project
+** Copyright (C) 2012-2015 Freescale Semiconductor, Inc.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <linux/ioctl.h>
+#include <sound/asound.h>
+#include <tinyalsa/asoundlib.h>
+#include "control.h"
+
+struct control *control_open(unsigned int card)
+{
+    struct snd_ctl_card_info tmp;
+    struct control *control = NULL;
+    unsigned int n, m;
+    int fd;
+    char fn[256];
+    int device = -1;
+    struct ctl_pcm_info      *current = NULL;
+
+    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
+    fd = open(fn, O_RDWR);
+    if (fd < 0)
+        return 0;
+
+    control = calloc(1, sizeof(*control));
+    if (!control)
+        goto fail;
+
+    control->count_p   = 0;
+    control->count_c   = 0;
+    control->fd        = fd;
+    control->card_info = calloc(1, sizeof(struct snd_ctl_card_info));
+    if (!control->card_info)
+        goto fail;
+
+    if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, control->card_info) < 0)
+        goto fail;
+
+    control->pcm_info_p = calloc(1, sizeof(struct ctl_pcm_info));
+    if (!control->pcm_info_p)
+        goto fail;
+
+    current = control->pcm_info_p;
+    device = -1;
+    while(1)
+    {
+        if (ioctl(fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, &device) < 0)
+            break;
+        if(device < 0)
+            break;
+
+        control->count_p           += 1;
+        current->info = calloc(1, sizeof(struct snd_pcm_info));
+        if (!current->info)
+            goto fail;
+
+        current->info->device       = device;
+        current->info->subdevice    = 0;
+        current->info->stream       = SNDRV_PCM_STREAM_PLAYBACK;
+
+        if (ioctl(fd, SNDRV_CTL_IOCTL_PCM_INFO, current->info) < 0)
+            break;
+
+        current->next = calloc(1, sizeof(struct ctl_pcm_info));
+        if (!current->next)
+            goto fail;
+        current = current->next;
+    }
+
+    control->pcm_info_c = calloc(1, sizeof(struct ctl_pcm_info));
+    if (!control->pcm_info_c)
+        goto fail;
+
+    current = control->pcm_info_c;
+    device = -1;
+    while(1)
+    {
+        if (ioctl(fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, &device) < 0)
+            break;
+        if(device < 0)
+            break;
+
+        control->count_c           += 1;
+        current->info = calloc(1, sizeof(struct snd_pcm_info));
+        if (!current->info)
+            goto fail;
+
+        current->info->device       = device;
+        current->info->subdevice    = 0;
+        current->info->stream       = SNDRV_PCM_STREAM_CAPTURE;
+
+        if (ioctl(fd, SNDRV_CTL_IOCTL_PCM_INFO, current->info) < 0)
+            break;
+
+        current->next = calloc(1, sizeof(struct ctl_pcm_info));
+        if (!current->next)
+            goto fail;
+        current = current->next;
+    }
+    return control;
+
+fail:
+    if (control)
+        control_close(control);
+    else if (fd >= 0)
+        close(fd);
+    return 0;
+}
+
+void control_close(struct control *control)
+{
+    unsigned int n,m;
+    struct ctl_pcm_info      *current = NULL;
+    struct ctl_pcm_info      *p = NULL;
+
+    if (!control)
+        return;
+
+    if (control->fd >= 0)
+        close(control->fd);
+
+    if (control->card_info)
+        free(control->card_info);
+
+    current = control->pcm_info_p;
+    while(!current)
+    {
+        if(!current->info)
+            free(current->info);
+        p = current;
+        current = current->next;
+        free(p);
+    }
+
+    current = control->pcm_info_c;
+    while(!current)
+    {
+        if(!current->info)
+            free(current->info);
+        p = current;
+        current = current->next;
+        free(p);
+    }
+    free(control);
+}
+
+const char *control_card_info_get_id(struct control *control)
+{
+    if (!control)
+        return "";
+
+    return (const char *)control->card_info->id;
+}
+
+const char *control_card_info_get_driver(struct control *control)
+{
+    if (!control)
+        return "";
+
+    return (const char *)control->card_info->driver;
+}
+
+const char *control_card_info_get_name(struct control *control)
+{
+    if (!control)
+        return "";
+
+    return (const char *)control->card_info->name;
+}
+
+
+int control_pcm_next_device(struct control *control, int *device, int stream)
+{
+    struct ctl_pcm_info      *current = NULL;
+    if (!control)
+        return -EINVAL;
+
+    if(stream == SNDRV_PCM_STREAM_PLAYBACK)   current = control->pcm_info_p;
+    else                                      current = control->pcm_info_c;
+
+    while(!current->info)
+        if((int)current->info->device > *device)
+        {
+            *device = current->info->device;
+            return 0;
+        }
+    return -1;
+}
+
+
+const char *control_pcm_info_get_id(struct control *control, unsigned int device, int stream)
+{
+    struct ctl_pcm_info      *current = NULL;
+    if (!control)
+        return "";
+
+    if(stream == SNDRV_PCM_STREAM_PLAYBACK)   current = control->pcm_info_p;
+    else                                    current = control->pcm_info_c;
+
+    while(!current->info)
+        if(current->info->device == device)
+            return (const char *)current->info->id;
+
+    return "";
+}
+
+const char *control_pcm_info_get_name(struct control *control, unsigned int device, int stream)
+{
+    struct ctl_pcm_info      *current = NULL;
+    if (!control)
+        return "";
+
+    if(stream == SNDRV_PCM_STREAM_PLAYBACK)   current = control->pcm_info_p;
+    else                                    current = control->pcm_info_c;
+
+    while(!current->info)
+        if(current->info->device == device)
+            return (const char *)current->info->name;
+
+    return "";
+}
+
+
diff --git a/peripheral/alsa/control.h b/peripheral/alsa/control.h
new file mode 100755
index 0000000..916f06a
--- /dev/null
+++ b/peripheral/alsa/control.h
@@ -0,0 +1,52 @@
+/*
+** Copyright 2015, The Android Open Source Project
+** Copyright (C) 2015 Freescale Semiconductor, Inc.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#ifndef _CONTROL_H
+#define _CONTROL_H
+
+struct ctl_pcm_info {
+    struct snd_pcm_info      *info;
+    struct ctl_pcm_info      *next;
+};
+
+struct control {
+    int fd;
+    struct snd_ctl_card_info *card_info;
+    struct ctl_pcm_info      *pcm_info_p;
+    unsigned int count_p;
+    struct ctl_pcm_info      *pcm_info_c;
+    unsigned int count_c;
+};
+
+struct control *control_open(unsigned int card);
+void control_close(struct control *control);
+const char *control_card_info_get_id(struct control *control);
+const char *control_card_info_get_driver(struct control *control);
+const char *control_card_info_get_name(struct control *control);
+
+#endif //_CONTROL_H
diff --git a/peripheral/alsa/tinyalsa_hal.c b/peripheral/alsa/tinyalsa_hal.c
new file mode 100755
index 0000000..6ab494e
--- /dev/null
+++ b/peripheral/alsa/tinyalsa_hal.c
@@ -0,0 +1,3489 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/* Copyright (C) 2012-2015 Freescale Semiconductor, Inc. */
+
+#define LOG_TAG "audio_hw_primary"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <cutils/properties.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#include <tinyalsa/asoundlib.h>
+#include <audio_utils/resampler.h>
+#include <audio_utils/echo_reference.h>
+#include <hardware/audio_effect.h>
+#include <audio_effects/effect_aec.h>
+
+#include "audio_hardware.h"
+#include "config_wm8962.h"
+#include "config_wm8958.h"
+#include "config_hdmi.h"
+#include "config_usbaudio.h"
+#include "config_nullcard.h"
+#include "config_spdif.h"
+#include "config_cs42888.h"
+#include "config_wm8960.h"
+#include "config_sii902x.h"
+
+#ifdef BRILLO
+#define PCM_HW_PARAM_ACCESS 0
+#define PCM_HW_PARAM_FORMAT 1
+#define PCM_HW_PARAM_SUBFORMAT 2
+#define PCM_HW_PARAM_FIRST_MASK PCM_HW_PARAM_ACCESS
+#define PCM_HW_PARAM_LAST_MASK PCM_HW_PARAM_SUBFORMAT
+#define PCM_HW_PARAM_SAMPLE_BITS 8
+#define PCM_HW_PARAM_FRAME_BITS 9
+#define PCM_HW_PARAM_CHANNELS 10
+#define PCM_HW_PARAM_RATE 11
+#define PCM_HW_PARAM_PERIOD_TIME 12
+#define PCM_HW_PARAM_PERIOD_SIZE 13
+#define PCM_HW_PARAM_PERIOD_BYTES 14
+#define PCM_HW_PARAM_PERIODS 15
+#define PCM_HW_PARAM_BUFFER_TIME 16
+#define PCM_HW_PARAM_BUFFER_SIZE 17
+#define PCM_HW_PARAM_BUFFER_BYTES 18
+#define PCM_HW_PARAM_TICK_TIME 19
+#define PCM_HW_PARAM_FIRST_INTERVAL PCM_HW_PARAM_SAMPLE_BITS
+#define PCM_HW_PARAM_LAST_INTERVAL PCM_HW_PARAM_TICK_TIME
+#define PCM_HW_PARAMS_NORESAMPLE (1<<0)
+#endif
+
+
+/* ALSA ports for IMX */
+#define PORT_MM     0
+#define PORT_MM2_UL 0
+#define PORT_SPDIF  6 /*not used*/
+#define PORT_HDMI   0
+
+/*align the definition in kernel for hdmi audio*/
+#define HDMI_PERIOD_SIZE       192
+#define PLAYBACK_HDMI_PERIOD_COUNT      8
+
+#define ESAI_PERIOD_SIZE       192
+#define PLAYBACK_ESAI_PERIOD_COUNT      8
+
+/* number of frames per short period (low latency) */
+/* align other card with hdmi, same latency*/
+#define SHORT_PERIOD_SIZE       384
+/* number of short periods in a long period (low power) */
+#define LONG_PERIOD_MULTIPLIER  2
+/* number of frames per long period (low power) */
+#define LONG_PERIOD_SIZE        192
+/* number of periods for low power playback */
+#define PLAYBACK_LONG_PERIOD_COUNT  8
+/* number of periods for capture */
+#define CAPTURE_PERIOD_SIZE  1024
+/* number of periods for capture */
+#define CAPTURE_PERIOD_COUNT 4
+/* minimum sleep time in out_write() when write threshold is not reached */
+#define MIN_WRITE_SLEEP_US 5000
+
+#define DEFAULT_OUT_SAMPLING_RATE 44100
+
+/* sampling rate when using MM low power port */
+#define MM_LOW_POWER_SAMPLING_RATE  44100
+/* sampling rate when using MM full power port */
+#define MM_FULL_POWER_SAMPLING_RATE 44100
+
+#define MM_USB_AUDIO_IN_RATE   16000
+
+/* product-specific defines */
+#define PRODUCT_DEVICE_PROPERTY "ro.product.device"
+#define PRODUCT_NAME_PROPERTY   "ro.product.name"
+#define PRODUCT_DEVICE_IMX      "imx"
+#define PRODUCT_DEVICE_AUTO     "sabreauto"
+#define SUPPORT_CARD_NUM        9
+
+/*"null_card" must be in the end of this array*/
+struct audio_card *audio_card_list[SUPPORT_CARD_NUM] = {
+    &wm8958_card,
+    &wm8962_card,
+    &hdmi_card,
+    &usbaudio_card,
+    &spdif_card,
+    &cs42888_card,
+    &wm8960_card,
+    &sii902x_card,
+    &null_card,
+};
+
+struct pcm_config pcm_config_mm_out = {
+    .channels = 2,
+    .rate = MM_FULL_POWER_SAMPLING_RATE,
+    .period_size = LONG_PERIOD_SIZE,
+    .period_count = PLAYBACK_LONG_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .avail_min = 0,
+};
+
+struct pcm_config pcm_config_hdmi_multi = {
+    .channels = 8, /* changed when the stream is opened */
+    .rate = MM_FULL_POWER_SAMPLING_RATE, /* changed when the stream is opened */
+    .period_size = HDMI_PERIOD_SIZE,
+    .period_count = PLAYBACK_HDMI_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .avail_min = 0,
+};
+struct pcm_config pcm_config_esai_multi = {
+    .channels = 8, /* changed when the stream is opened */
+    .rate = MM_FULL_POWER_SAMPLING_RATE, /* changed when the stream is opened */
+    .period_size = ESAI_PERIOD_SIZE,
+    .period_count = PLAYBACK_ESAI_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .avail_min = 0,
+};
+
+struct pcm_config pcm_config_mm_in = {
+    .channels = 2,
+    .rate = MM_FULL_POWER_SAMPLING_RATE,
+    .period_size = CAPTURE_PERIOD_SIZE,
+    .period_count = CAPTURE_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .avail_min = 0,
+};
+
+const struct string_to_enum out_channels_name_to_enum_table[] = {
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
+};
+
+
+/**
+ * NOTE: when multiple mutexes have to be acquired, always respect the following order:
+ *        hw device > in stream > out stream
+ */
+static void select_output_device(struct imx_audio_device *adev);
+static void select_input_device(struct imx_audio_device *adev);
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume);
+static int do_input_standby(struct imx_stream_in *in);
+static int do_output_standby(struct imx_stream_out *out);
+static int scan_available_device(struct imx_audio_device *adev, bool rescanusb, bool queryInput, bool queryOutput);
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+                                   struct resampler_buffer* buffer);
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+                                  struct resampler_buffer* buffer);
+static int adev_get_rate_for_device(struct imx_audio_device *adev, uint32_t devices, unsigned int flag);
+static int adev_get_channels_for_device(struct imx_audio_device *adev, uint32_t devices, unsigned int flag);
+static int adev_get_format_for_device(struct imx_audio_device *adev, uint32_t devices, unsigned int flag);
+static void in_update_aux_channels(struct imx_stream_in *in, effect_handle_t effect);
+static int pcm_read_wrapper(struct pcm *pcm, const void * buffer, size_t bytes);
+
+/* Returns true on devices that are sabreauto, false otherwise */
+static int is_device_auto(void)
+{
+    char property[PROPERTY_VALUE_MAX];
+
+    property_get(PRODUCT_DEVICE_PROPERTY, property, "");
+
+    /* return true if the property matches the given value */
+    return strstr(property, PRODUCT_DEVICE_AUTO) != NULL;
+}
+
+static int convert_record_data(void *src, void *dst, unsigned int frames, bool bit_24b_2_16b, bool mono2stereo, bool stereo2mono)
+{
+     unsigned int i;
+     short *dst_t = (short *)dst;
+     if (bit_24b_2_16b && mono2stereo && !stereo2mono) {
+        int data;
+        int *src_t = (int *)src;
+        for(i = 0; i < frames; i++)
+        {
+            data   = *src_t++;
+            *dst_t++ = (short)(data >> 8);
+            *dst_t++ = (short)(data >> 8);
+        }
+     }
+
+     if (bit_24b_2_16b && !mono2stereo && stereo2mono) {
+        int data1=0, data2=0;
+        int *src_t = (int *)src;
+        for(i = 0; i < frames; i++)
+        {
+            data1   = *src_t++;
+            data2   = *src_t++;
+            *dst_t++ = (short)(((data1 << 8) >> 17) + ((data2 << 8) >> 17));
+        }
+     }
+
+     if (bit_24b_2_16b && !mono2stereo && !stereo2mono) {
+        int data1, data2;
+        int *src_t = (int *)src;
+        for(i = 0; i < frames; i++)
+        {
+            data1   = *src_t++;
+            data2   = *src_t++;
+            *dst_t++ = (short)(data1 >> 8);
+            *dst_t++ = (short)(data2 >> 8);
+        }
+     }
+
+     if (!bit_24b_2_16b && mono2stereo && !stereo2mono ) {
+        short data;
+        short *src_t = (short *)src;
+        for(i = 0; i < frames; i++)
+        {
+            data   = *src_t++;
+            *dst_t++ = data;
+            *dst_t++ = data;
+        }
+     }
+
+     if (!bit_24b_2_16b && !mono2stereo && stereo2mono) {
+        short data1, data2;
+        short *src_t = (short *)src;
+        for(i = 0; i < frames; i++)
+        {
+            data1   = *src_t++;
+            data2   = *src_t++;
+            *dst_t++ = (data1 >> 1) + (data2 >> 1);
+        }
+     }
+
+     return 0;
+}
+
+/* The enable flag when 0 makes the assumption that enums are disabled by
+ * "Off" and integers/booleans by 0 */
+static int set_route_by_array(struct mixer *mixer, struct route_setting *route,
+                              int enable)
+{
+    struct mixer_ctl *ctl = NULL;
+    unsigned int i, j;
+
+    if(!mixer) return 0;
+    if(!route) return 0;
+    /* Go through the route array and set each value */
+    i = 0;
+    while (route[i].ctl_name) {
+        ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name);
+        if (!ctl)
+            return -EINVAL;
+
+        if (route[i].strval) {
+            if (enable)
+                mixer_ctl_set_enum_by_string(ctl, route[i].strval);
+            else
+                mixer_ctl_set_enum_by_string(ctl, "Off");
+        } else {
+            /* This ensures multiple (i.e. stereo) values are set jointly */
+            for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) {
+                if (enable)
+                    mixer_ctl_set_value(ctl, j, route[i].intval);
+                else
+                    mixer_ctl_set_value(ctl, j, 0);
+            }
+        }
+        i++;
+    }
+
+    return 0;
+}
+
+
+
+static void force_all_standby(struct imx_audio_device *adev)
+{
+    struct imx_stream_in *in;
+    struct imx_stream_out *out;
+    int i;
+
+    for(i = 0; i < OUTPUT_TOTAL; i++)
+        if (adev->active_output[i]) {
+            out = adev->active_output[i];
+            pthread_mutex_lock(&out->lock);
+            do_output_standby(out);
+            pthread_mutex_unlock(&out->lock);
+        }
+
+    if (adev->active_input) {
+        in = adev->active_input;
+        pthread_mutex_lock(&in->lock);
+        do_input_standby(in);
+        pthread_mutex_unlock(&in->lock);
+    }
+}
+
+static void select_mode(struct imx_audio_device *adev)
+{
+    if (adev->mode == AUDIO_MODE_IN_CALL) {
+        ALOGW("Entering IN_CALL state, in_call=%d", adev->in_call);
+        if (!adev->in_call) {
+            force_all_standby(adev);
+            /* force earpiece route for in call state if speaker is the
+            only currently selected route. This prevents having to tear
+            down the modem PCMs to change route from speaker to earpiece
+            after the ringtone is played, but doesn't cause a route
+            change if a headset or bt device is already connected. If
+            speaker is not the only thing active, just remove it from
+            the route. We'll assume it'll never be used initally during
+            a call. This works because we're sure that the audio policy
+            manager will update the output device after the audio mode
+            change, even if the device selection did not change. */
+            if (adev->out_device == AUDIO_DEVICE_OUT_SPEAKER) {
+                adev->out_device = AUDIO_DEVICE_OUT_EARPIECE;
+                adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN;
+            } else
+                adev->out_device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+            select_output_device(adev);
+
+            adev_set_voice_volume(&adev->hw_device, adev->voice_volume);
+            adev->in_call = 1;
+        }
+    } else {
+        ALOGW("Leaving IN_CALL state, in_call=%d, mode=%d",
+             adev->in_call, adev->mode);
+        if (adev->in_call) {
+            adev->in_call = 0;
+            force_all_standby(adev);
+            select_output_device(adev);
+            select_input_device(adev);
+        }
+    }
+}
+
+static void select_output_device(struct imx_audio_device *adev)
+{
+    int headset_on;
+    int headphone_on;
+    int speaker_on;
+    int earpiece_on;
+    int bt_on;
+    bool tty_volume = false;
+    unsigned int channel;
+    int i;
+
+    headset_on      = adev->out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET;
+    headphone_on    = adev->out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+    speaker_on      = adev->out_device & AUDIO_DEVICE_OUT_SPEAKER;
+    earpiece_on     = adev->out_device & AUDIO_DEVICE_OUT_EARPIECE;
+    bt_on           = adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO;
+
+    /* force rx path according to TTY mode when in call */
+    if (adev->mode == AUDIO_MODE_IN_CALL && !bt_on) {
+        switch(adev->tty_mode) {
+            case TTY_MODE_FULL:
+            case TTY_MODE_VCO:
+                /* rx path to headphones */
+                headphone_on = 1;
+                headset_on = 0;
+                speaker_on = 0;
+                earpiece_on = 0;
+                tty_volume = true;
+                break;
+            case TTY_MODE_HCO:
+                /* rx path to device speaker */
+                headphone_on = 0;
+                headset_on = 0;
+                speaker_on = 1;
+                earpiece_on = 0;
+                break;
+            case TTY_MODE_OFF:
+            default:
+                /* force speaker on when in call and HDMI is selected as voice DL audio
+                 * cannot be routed to HDMI by ABE */
+                if (adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL)
+                    speaker_on = 1;
+                break;
+        }
+    }
+    /*if mode = AUDIO_MODE_IN_CALL*/
+    ALOGV("headphone %d ,headset %d ,speaker %d, earpiece %d, \n", headphone_on, headset_on, speaker_on, earpiece_on);
+    /* select output stage */
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+        set_route_by_array(adev->mixer[i], adev->card_list[i]->bt_output, bt_on);
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+        set_route_by_array(adev->mixer[i], adev->card_list[i]->hs_output, headset_on | headphone_on);
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+        set_route_by_array(adev->mixer[i], adev->card_list[i]->speaker_output, speaker_on);
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+        set_route_by_array(adev->mixer[i], adev->card_list[i]->earpiece_output, earpiece_on);
+
+    /* Special case: select input path if in a call, otherwise
+       in_set_parameters is used to update the input route
+       todo: use sub mic for handsfree case */
+    if (adev->mode == AUDIO_MODE_IN_CALL) {
+        if (bt_on)
+            for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                set_route_by_array(adev->mixer[i], adev->card_list[i]->vx_bt_mic_input, bt_on);
+        else {
+            /* force tx path according to TTY mode when in call */
+            switch(adev->tty_mode) {
+                case TTY_MODE_FULL:
+                case TTY_MODE_HCO:
+                    /* tx path from headset mic */
+                    headphone_on = 0;
+                    headset_on = 1;
+                    speaker_on = 0;
+                    earpiece_on = 0;
+                    break;
+                case TTY_MODE_VCO:
+                    /* tx path from device sub mic */
+                    headphone_on = 0;
+                    headset_on = 0;
+                    speaker_on = 1;
+                    earpiece_on = 0;
+                    break;
+                case TTY_MODE_OFF:
+                default:
+                    break;
+            }
+
+            if (headset_on)
+                for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                    set_route_by_array(adev->mixer[i], adev->card_list[i]->vx_hs_mic_input, 1);
+            else if (headphone_on || earpiece_on || speaker_on)
+                for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                    set_route_by_array(adev->mixer[i], adev->card_list[i]->vx_main_mic_input, 1);
+            else
+                for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                    set_route_by_array(adev->mixer[i], adev->card_list[i]->vx_main_mic_input, 0);
+        }
+    }
+}
+
+static void select_input_device(struct imx_audio_device *adev)
+{
+    int i;
+    int headset_on = 0;
+    int main_mic_on = 0;
+    int sub_mic_on = 0;
+    int bt_on = adev->in_device & AUDIO_DEVICE_IN_ALL_SCO;
+
+    if (!bt_on) {
+        if ((adev->mode != AUDIO_MODE_IN_CALL) && (adev->active_input != 0)) {
+            /* sub mic is used for camcorder or VoIP on speaker phone */
+            sub_mic_on = (adev->active_input->source == AUDIO_SOURCE_CAMCORDER) ||
+                         ((adev->out_device & AUDIO_DEVICE_OUT_SPEAKER) &&
+                          (adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION));
+        }
+
+        headset_on = adev->in_device & AUDIO_DEVICE_IN_WIRED_HEADSET;
+        main_mic_on = adev->in_device & AUDIO_DEVICE_IN_BUILTIN_MIC;
+    }
+
+   /* TODO: check how capture is possible during voice calls or if
+    * both use cases are mutually exclusive.
+    */
+    if (bt_on)
+        for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+            set_route_by_array(adev->mixer[i], adev->card_list[i]->mm_bt_mic_input, 1);
+    else {
+        /* Select front end */
+        if (headset_on)
+            for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                set_route_by_array(adev->mixer[i], adev->card_list[i]->mm_hs_mic_input, 1);
+        else if (main_mic_on || sub_mic_on)
+            for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                set_route_by_array(adev->mixer[i], adev->card_list[i]->mm_main_mic_input, 1);
+        else
+            for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+                set_route_by_array(adev->mixer[i], adev->card_list[i]->mm_main_mic_input, 0);
+    }
+}
+
+static int get_card_for_device(struct imx_audio_device *adev, int device, unsigned int flag)
+{
+    int i;
+    int card = -1;
+
+    if (flag == PCM_OUT ) {
+        for(i = 0; i < MAX_AUDIO_CARD_NUM; i++) {
+            if(adev->card_list[i]->supported_out_devices & device) {
+                  card = adev->card_list[i]->card;
+                  break;
+            }
+        }
+    } else {
+        for(i = 0; i < MAX_AUDIO_CARD_NUM; i++) {
+            if(adev->card_list[i]->supported_in_devices & device) {
+                  card = adev->card_list[i]->card;
+                  break;
+            }
+        }
+    }
+    return card;
+}
+/* must be called with hw device and output stream mutexes locked */
+static int start_output_stream_primary(struct imx_stream_out *out)
+{
+    struct imx_audio_device *adev = out->dev;
+    unsigned int card = -1;
+    unsigned int port = 0;
+    int i;
+    int pcm_device;
+    bool success = false;
+
+    ALOGI("start_output_stream... %d, device %d",(int)out, out->device);
+
+    if (adev->mode != AUDIO_MODE_IN_CALL) {
+        /* FIXME: only works if only one output can be active at a time */
+        select_output_device(adev);
+    }
+
+    pcm_device = out->device & (AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_AUX_DIGITAL);
+    if (pcm_device && (adev->active_output[OUTPUT_ESAI] == NULL || adev->active_output[OUTPUT_ESAI]->standby)) {
+        out->write_flags[PCM_NORMAL]            = PCM_OUT | PCM_MMAP | PCM_MONOTONIC;
+        out->write_threshold[PCM_NORMAL]        = PLAYBACK_LONG_PERIOD_COUNT * LONG_PERIOD_SIZE;
+        out->config[PCM_NORMAL] = pcm_config_mm_out;
+
+       if (out->device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) {
+            int rate = 0;
+            int channels = 0;
+
+            channels = adev_get_channels_for_device(adev, out->device, PCM_OUT);
+            if (channels == 2){
+                out->config[PCM_NORMAL].channels = 2;
+            } else if (channels == 1) {
+                ALOGW("start_output_stream_primary, channels is 1 !!!");
+                out->config[PCM_NORMAL].channels = 1;
+            } else {
+                  ALOGE("can not get channels for out_device %d ", out->device);
+                  return -EINVAL;
+            }
+
+            rate = adev_get_rate_for_device(adev, out->device, PCM_OUT);
+            if( rate == 0) {
+                  ALOGW("can not get rate for out_device %d ", out->device);
+                  return -EINVAL;
+            }
+            out->config[PCM_NORMAL].rate = rate;
+
+
+            if(out->resampler[PCM_NORMAL]) {
+                int ret;
+                release_resampler(out->resampler[PCM_NORMAL]);
+
+                //channels = 1 ?
+                ret = create_resampler(adev->default_rate,
+                           rate,
+                           channels,
+                           RESAMPLER_QUALITY_DEFAULT,
+                           NULL,
+                           &out->resampler[PCM_NORMAL]);
+                if (ret != 0) {
+                    ALOGE("can not create_resampler, inRate %d, outRate %d, channels %d, device 0x%x",
+                        adev->default_rate, rate, channels, out->device);
+                    return ret;
+                }
+            }
+        }
+
+        card = get_card_for_device(adev, pcm_device, PCM_OUT);
+        out->pcm[PCM_NORMAL] = pcm_open(card, port,out->write_flags[PCM_NORMAL], &out->config[PCM_NORMAL]);
+        ALOGW("card %d, port %d device 0x%x", card, port, out->device);
+        ALOGW("rate %d, channel %d period_size 0x%x", out->config[PCM_NORMAL].rate, out->config[PCM_NORMAL].channels, out->config[PCM_NORMAL].period_size);
+        success = true;
+    }
+
+    pcm_device = out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL;
+    if(pcm_device && (adev->active_output[OUTPUT_HDMI] == NULL || adev->active_output[OUTPUT_HDMI]->standby)) {
+        out->write_flags[PCM_HDMI]            = PCM_OUT | PCM_MONOTONIC;
+        out->write_threshold[PCM_HDMI]        = HDMI_PERIOD_SIZE * PLAYBACK_HDMI_PERIOD_COUNT;
+        out->config[PCM_HDMI] = pcm_config_mm_out;
+        card = get_card_for_device(adev, pcm_device, PCM_OUT);
+        out->pcm[PCM_HDMI] = pcm_open(card, port,out->write_flags[PCM_HDMI], &out->config[PCM_HDMI]);
+        ALOGW("card %d, port %d device 0x%x", card, port, out->device);
+        ALOGW("rate %d, channel %d period_size 0x%x", out->config[PCM_HDMI].rate, out->config[PCM_HDMI].channels, out->config[PCM_HDMI].period_size);
+        success = true;
+    }
+    /* default to low power: will be corrected in out_write if necessary before first write to
+     * tinyalsa.
+     */
+    out->low_power   = 0;
+    /* Close any PCMs that could not be opened properly and return an error */
+    for (i = 0; i < PCM_TOTAL; i++) {
+        if (out->pcm[i] && !pcm_is_ready(out->pcm[i])) {
+            ALOGE("cannot open pcm_out driver %d: %s", i, pcm_get_error(out->pcm[i]));
+            pcm_close(out->pcm[i]);
+            out->pcm[i] = NULL;
+            success = false;
+        }
+    }
+
+    if (success) {
+        out->buffer_frames = pcm_config_mm_out.period_size * 2;
+        if (out->buffer == NULL)
+            out->buffer = malloc(out->buffer_frames * audio_stream_frame_size(&out->stream.common));
+
+        if (adev->echo_reference != NULL)
+            out->echo_reference = adev->echo_reference;
+
+        for(i = 0; i < PCM_TOTAL; i++) {
+            if (out->resampler[i])
+                out->resampler[i]->reset(out->resampler[i]);
+        }
+
+        return 0;
+    }
+
+    return -ENOMEM;
+}
+
+static int start_output_stream_hdmi(struct imx_stream_out *out)
+{
+    struct imx_audio_device *adev = out->dev;
+    unsigned int card = -1;
+    unsigned int port = 0;
+    int i = 0;
+
+    ALOGI("start_output_stream_hdmi, out %d, device 0x%x", (int)out, out->device);
+    /* force standby on low latency output stream to close HDMI driver in case it was in use */
+    if (adev->active_output[OUTPUT_PRIMARY] != NULL &&
+            !adev->active_output[OUTPUT_PRIMARY]->standby) {
+        struct imx_stream_out *p_out = adev->active_output[OUTPUT_PRIMARY];
+        pthread_mutex_lock(&p_out->lock);
+        do_output_standby(p_out);
+        pthread_mutex_unlock(&p_out->lock);
+    }
+
+    card = get_card_for_device(adev, out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL, PCM_OUT);
+    ALOGW("card %d, port %d device 0x%x", card, port, out->device);
+    ALOGW("rate %d, channel %d period_size 0x%x", out->config[PCM_HDMI].rate, out->config[PCM_HDMI].channels, out->config[PCM_HDMI].period_size);
+
+    out->pcm[PCM_HDMI] = pcm_open(card, port, PCM_OUT | PCM_MONOTONIC, &out->config[PCM_HDMI]);
+
+    if (out->pcm[PCM_HDMI] && !pcm_is_ready(out->pcm[PCM_HDMI])) {
+        ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm[PCM_HDMI]));
+        pcm_close(out->pcm[PCM_HDMI]);
+        out->pcm[PCM_HDMI] = NULL;
+        return -ENOMEM;
+    }
+    return 0;
+}
+
+static int start_output_stream_esai(struct imx_stream_out *out)
+{
+    struct imx_audio_device *adev = out->dev;
+    unsigned int card = -1;
+    unsigned int port = 0;
+    int i = 0;
+
+    /* force standby on low latency output stream to close HDMI driver in case it was in use */
+    if (adev->active_output[OUTPUT_PRIMARY] != NULL &&
+            !adev->active_output[OUTPUT_PRIMARY]->standby) {
+        struct imx_stream_out *p_out = adev->active_output[OUTPUT_PRIMARY];
+        pthread_mutex_lock(&p_out->lock);
+        do_output_standby(p_out);
+        pthread_mutex_unlock(&p_out->lock);
+    }
+
+    card = get_card_for_device(adev, out->device & AUDIO_DEVICE_OUT_SPEAKER, PCM_OUT);
+    ALOGW("card %d, port %d device 0x%x", card, port, out->device);
+    ALOGW("rate %d, channel %d period_size 0x%x", out->config[PCM_ESAI].rate, out->config[PCM_ESAI].channels, out->config[PCM_ESAI].period_size);
+
+    out->pcm[PCM_ESAI] = pcm_open(card, port, PCM_OUT | PCM_MONOTONIC, &out->config[PCM_ESAI]);
+
+    if (out->pcm[PCM_ESAI] && !pcm_is_ready(out->pcm[PCM_ESAI])) {
+        ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm[PCM_ESAI]));
+        pcm_close(out->pcm[PCM_ESAI]);
+        out->pcm[PCM_ESAI] = NULL;
+        return -ENOMEM;
+    }
+    return 0;
+}
+
+static int check_input_parameters(uint32_t sample_rate, int format, int channel_count)
+{
+    if (format != AUDIO_FORMAT_PCM_16_BIT)
+        return -EINVAL;
+
+    if ((channel_count < 1) || (channel_count > 2))
+        return -EINVAL;
+
+    switch(sample_rate) {
+    case 8000:
+    case 11025:
+    case 16000:
+    case 22050:
+    case 24000:
+    case 32000:
+    case 44100:
+    case 48000:
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count)
+{
+    size_t size;
+    size_t device_rate;
+
+    if (check_input_parameters(sample_rate, format, channel_count) != 0)
+        return 0;
+
+    /* take resampling into account and return the closest majoring
+    multiple of 16 frames, as audioflinger expects audio buffers to
+    be a multiple of 16 frames */
+    size = (pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate;
+    size = ((size + 15) / 16) * 16;
+
+    ALOGW("get_input_buffer_size size = %d, channel_count = %d",size,channel_count);
+    return size * channel_count * sizeof(short);
+}
+
+static void add_echo_reference(struct imx_stream_out *out,
+                               struct echo_reference_itfe *reference)
+{
+    pthread_mutex_lock(&out->lock);
+    out->echo_reference = reference;
+    pthread_mutex_unlock(&out->lock);
+}
+
+static void remove_echo_reference(struct imx_stream_out *out,
+                                  struct echo_reference_itfe *reference)
+{
+    pthread_mutex_lock(&out->lock);
+    if (out->echo_reference == reference) {
+        /* stop writing to echo reference */
+        reference->write(reference, NULL);
+        out->echo_reference = NULL;
+    }
+    pthread_mutex_unlock(&out->lock);
+}
+
+static void put_echo_reference(struct imx_audio_device *adev,
+                          struct echo_reference_itfe *reference)
+{
+    if (adev->echo_reference != NULL &&
+            reference == adev->echo_reference) {
+
+        if (adev->active_output[OUTPUT_PRIMARY] != NULL &&
+             !adev->active_output[OUTPUT_PRIMARY]->standby )
+                remove_echo_reference(adev->active_output[OUTPUT_PRIMARY], reference);
+        release_echo_reference(reference);
+        adev->echo_reference = NULL;
+    }
+}
+
+static struct echo_reference_itfe *get_echo_reference(struct imx_audio_device *adev,
+                                               audio_format_t format,
+                                               uint32_t channel_count,
+                                               uint32_t sampling_rate)
+{
+    put_echo_reference(adev, adev->echo_reference);
+    /*only for mixer output, only one output*/
+    if (adev->active_output[OUTPUT_PRIMARY] != NULL &&
+             !adev->active_output[OUTPUT_PRIMARY]->standby){
+        struct audio_stream *stream = &adev->active_output[OUTPUT_PRIMARY]->stream.common;
+        uint32_t wr_channel_count = popcount(stream->get_channels(stream));
+        uint32_t wr_sampling_rate = stream->get_sample_rate(stream);
+
+        int status = create_echo_reference(AUDIO_FORMAT_PCM_16_BIT,
+                                           channel_count,
+                                           sampling_rate,
+                                           AUDIO_FORMAT_PCM_16_BIT,
+                                           wr_channel_count,
+                                           wr_sampling_rate,
+                                           &adev->echo_reference);
+        if (status == 0)
+            add_echo_reference(adev->active_output[OUTPUT_PRIMARY], adev->echo_reference);
+    }
+
+    return adev->echo_reference;
+}
+
+static int get_playback_delay(struct imx_stream_out *out,
+                       size_t frames,
+                       struct echo_reference_buffer *buffer)
+{
+    size_t kernel_frames;
+    int status;
+    int primary_pcm = 0;
+    struct imx_audio_device *adev = out->dev;
+
+    /* Find the first active PCM to act as primary */
+    while ((primary_pcm < PCM_TOTAL) && !out->pcm[primary_pcm])
+        primary_pcm++;
+
+    status = pcm_get_htimestamp(out->pcm[primary_pcm], &kernel_frames, &buffer->time_stamp);
+    if (status < 0) {
+        buffer->time_stamp.tv_sec  = 0;
+        buffer->time_stamp.tv_nsec = 0;
+        buffer->delay_ns           = 0;
+        ALOGV("get_playback_delay(): pcm_get_htimestamp error,"
+                "setting playbackTimestamp to 0");
+        return status;
+    }
+
+    kernel_frames = pcm_get_buffer_size(out->pcm[primary_pcm]) - kernel_frames;
+
+    /* adjust render time stamp with delay added by current driver buffer.
+     * Add the duration of current frame as we want the render time of the last
+     * sample being written. */
+    buffer->delay_ns = (long)(((int64_t)(kernel_frames + frames)* 1000000000)/
+                            adev->mm_rate);
+
+    return 0;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    return pcm_config_mm_out.rate;
+}
+
+static uint32_t out_get_sample_rate_hdmi(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    return out->config[PCM_HDMI].rate;
+}
+
+static uint32_t out_get_sample_rate_esai(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    return out->config[PCM_ESAI].rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+    ALOGW("out_set_sample_rate %d", rate);
+    return 0;
+}
+
+static size_t out_get_buffer_size_primary(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *adev = out->dev;
+
+    /* take resampling into account and return the closest majoring
+    multiple of 16 frames, as audioflinger expects audio buffers to
+    be a multiple of 16 frames */
+    size_t size = (pcm_config_mm_out.period_size * adev->default_rate) / pcm_config_mm_out.rate;
+    size = ((size + 15) / 16) * 16;
+    return size * audio_stream_frame_size((struct audio_stream *)stream);
+}
+
+static size_t out_get_buffer_size_hdmi(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+
+    /* take resampling into account and return the closest majoring
+    multiple of 16 frames, as audioflinger expects audio buffers to
+    be a multiple of 16 frames */
+    size_t size = pcm_config_hdmi_multi.period_size;
+    size = ((size + 15) / 16) * 16;
+    return size * audio_stream_frame_size((struct audio_stream *)stream);
+}
+
+static size_t out_get_buffer_size_esai(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+
+    /* take resampling into account and return the closest majoring
+    multiple of 16 frames, as audioflinger expects audio buffers to
+    be a multiple of 16 frames */
+    size_t size = pcm_config_esai_multi.period_size;
+    size = ((size + 15) / 16) * 16;
+    return size * audio_stream_frame_size((struct audio_stream *)stream);
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    return out->channel_mask;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+    return AUDIO_FORMAT_PCM_16_BIT;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+    ALOGW("out_set_format %d", format);
+    return 0;
+}
+
+/* must be called with hw device and output stream mutexes locked */
+static int do_output_standby(struct imx_stream_out *out)
+{
+    struct imx_audio_device *adev = out->dev;
+    int i;
+
+    if (!out->standby) {
+
+        for (i = 0; i < PCM_TOTAL; i++) {
+            if (out->pcm[i]) {
+                pcm_close(out->pcm[i]);
+                out->pcm[i] = NULL;
+            }
+
+            out->writeContiFailCount[i] = 0;
+        }
+
+        ALOGW("do_out_standby... %d",(int)out);
+
+        /* if in call, don't turn off the output stage. This will
+        be done when the call is ended */
+        if (adev->mode != AUDIO_MODE_IN_CALL) {
+            /* FIXME: only works if only one output can be active at a time */
+        }
+
+        /* stop writing to echo reference */
+        if (out->echo_reference != NULL) {
+            out->echo_reference->write(out->echo_reference, NULL);
+            out->echo_reference = NULL;
+        }
+
+        out->standby = 1;
+    }
+    return 0;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    int status;
+
+    pthread_mutex_lock(&out->dev->lock);
+    pthread_mutex_lock(&out->lock);
+    status = do_output_standby(out);
+    pthread_mutex_unlock(&out->lock);
+    pthread_mutex_unlock(&out->dev->lock);
+    return status;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+    return 0;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *adev = out->dev;
+    struct imx_stream_in *in;
+    struct str_parms *parms;
+    char *str;
+    char value[32];
+    int ret, val = 0;
+    bool force_input_standby = false;
+    bool out_is_active = false;
+    int  i;
+
+    parms = str_parms_create_str(kvpairs);
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+    if (ret >= 0) {
+        val = atoi(value);
+        pthread_mutex_lock(&adev->lock);
+        pthread_mutex_lock(&out->lock);
+
+        if (adev->out_device != val) {
+            if (out == adev->active_output[OUTPUT_PRIMARY] && !out->standby) {
+                /* a change in output device may change the microphone selection */
+                if (adev->active_input &&
+                        adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+                    force_input_standby = true;
+                }
+                /* force standby if moving to/from HDMI */
+                if (((val & AUDIO_DEVICE_OUT_AUX_DIGITAL) ^
+                        (adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL)) ||
+                        ((val & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ^
+                        (adev->out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) ||
+                        (adev->out_device & (AUDIO_DEVICE_OUT_AUX_DIGITAL |
+                                         AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) ||
+                        ((val & AUDIO_DEVICE_OUT_SPEAKER) ^
+                        (adev->out_device & AUDIO_DEVICE_OUT_SPEAKER)) ||
+                        (adev->mode == AUDIO_MODE_IN_CALL)) {
+                        ALOGI("out_set_parameters, old 0x%x, new 0x%x do_output_standby", adev->out_device, val);
+                    do_output_standby(out);
+                }
+            }
+            if ((out != adev->active_output[OUTPUT_HDMI]) && val) {
+                adev->out_device = val;
+                out->device    = val;
+                if(out->device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) {
+                    scan_available_device(adev, false, false, true);
+                }
+
+                select_output_device(adev);
+            }
+        }
+        pthread_mutex_unlock(&out->lock);
+        if (force_input_standby) {
+            in = adev->active_input;
+            pthread_mutex_lock(&in->lock);
+            do_input_standby(in);
+            pthread_mutex_unlock(&in->lock);
+        }
+        pthread_mutex_unlock(&adev->lock);
+
+        ret = 0;
+    }
+
+    ALOGW("out_set_parameters %s, ret %d, out %d",kvpairs, ret, (int)out);
+    str_parms_destroy(parms);
+
+    return ret;
+}
+
+static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+
+    struct str_parms *query = str_parms_create_str(keys);
+    char *str = NULL;
+    char value[256];
+    struct str_parms *reply = str_parms_create();
+    size_t i, j;
+    int ret;
+    bool first = true;
+    bool checked = false;
+    char temp[10];
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value));
+    if (ret >= 0) {
+        value[0] = '\0';
+        i = 0;
+        while (out->sup_channel_masks[i] != 0) {
+            for (j = 0; j < ARRAY_SIZE(out_channels_name_to_enum_table); j++) {
+                if (out_channels_name_to_enum_table[j].value == out->sup_channel_masks[i]) {
+                    if (!first) {
+                        strcat(value, "|");
+                    }
+                    strcat(value, out_channels_name_to_enum_table[j].name);
+                    first = false;
+                    break;
+                }
+            }
+            i++;
+        }
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
+        str = strdup(str_parms_to_str(reply));
+        checked = true;
+    }
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value, sizeof(value));
+    if (ret >= 0) {
+        value[0] = '\0';
+        i = 0;
+        while (out->sup_rates[i] != 0) {
+            if (!first) {
+                strcat(value, "|");
+            }
+            sprintf(temp, "%d", out->sup_rates[i]);
+            strcat(value, temp);
+            first = false;
+            i++;
+        }
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value);
+        str = strdup(str_parms_to_str(reply));
+	checked = true;
+    }
+
+    if (!checked) {
+        str = strdup(keys);
+    }
+
+    ALOGW("out get parameters query %s, reply %s",str_parms_to_str(query), str_parms_to_str(reply));
+    str_parms_destroy(query);
+    str_parms_destroy(reply);
+    return str;
+}
+
+static uint32_t out_get_latency_primary(const struct audio_stream_out *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+
+    return (pcm_config_mm_out.period_size * pcm_config_mm_out.period_count * 1000) / pcm_config_mm_out.rate;
+}
+
+static uint32_t out_get_latency_hdmi(const struct audio_stream_out *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+
+    return (pcm_config_hdmi_multi.period_size * pcm_config_hdmi_multi.period_count * 1000) / pcm_config_hdmi_multi.rate;
+}
+
+static uint32_t out_get_latency_esai(const struct audio_stream_out *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+
+    return (pcm_config_esai_multi.period_size * pcm_config_esai_multi.period_count * 1000) / pcm_config_esai_multi.rate;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+                          float right)
+{
+    return -ENOSYS;
+}
+
+static int pcm_read_convert(struct imx_stream_in *in, struct pcm *pcm, void *data, unsigned int count)
+{
+    bool bit_24b_2_16b = false;
+    bool mono2stereo = false;
+    bool stereo2mono = false;
+    size_t frames_rq = count / audio_stream_frame_size(&in->stream.common);
+
+    if (in->config.format == PCM_FORMAT_S24_LE && in->requested_format == PCM_FORMAT_S16_LE) bit_24b_2_16b = true;
+    if (in->config.channels == 2 && in->requested_channel == 1) stereo2mono = true;
+    if (in->config.channels == 1 && in->requested_channel == 2) mono2stereo = true;
+
+    if (bit_24b_2_16b || mono2stereo || stereo2mono) {
+        size_t size_in_bytes_tmp = pcm_frames_to_bytes(in->pcm, frames_rq);
+        if (in->read_tmp_buf_size < in->config.period_size) {
+            in->read_tmp_buf_size = in->config.period_size;
+            in->read_tmp_buf = (int32_t *) realloc(in->read_tmp_buf, size_in_bytes_tmp);
+            ALOG_ASSERT((in->read_tmp_buf != NULL),
+                        "get_next_buffer() failed to reallocate read_tmp_buf");
+            ALOGV("get_next_buffer(): read_tmp_buf %p extended to %d bytes",
+                     in->read_tmp_buf, size_in_bytes_tmp);
+        }
+
+        in->read_status = pcm_read_wrapper(pcm, (void*)in->read_tmp_buf, size_in_bytes_tmp);
+
+        if (in->read_status != 0) {
+            ALOGE("get_next_buffer() pcm_read_wrapper error %d", in->read_status);
+            return in->read_status;
+        }
+        convert_record_data((void *)in->read_tmp_buf, (void *)data, frames_rq, bit_24b_2_16b, mono2stereo, stereo2mono);
+    }
+    else {
+        in->read_status = pcm_read_wrapper(pcm, (void*)data, count);
+    }
+
+    return in->read_status;
+}
+
+static int pcm_read_wrapper(struct pcm *pcm, const void * buffer, size_t bytes)
+{
+    int ret = 0;
+    ret = pcm_read(pcm, (void *)buffer, bytes);
+
+    if(ret !=0) {
+         ALOGV("ret %d, pcm read %d error %s.", ret, bytes, pcm_get_error(pcm));
+
+         switch(pcm_state(pcm)) {
+              case PCM_STATE_SETUP:
+              case PCM_STATE_XRUN:
+                   ret = pcm_prepare(pcm);
+                   if(ret != 0) return ret;
+                   break;
+              default:
+                   return ret;
+         }
+
+         ret = pcm_read(pcm, (void *)buffer, bytes);
+    }
+
+    return ret;
+}
+
+static int pcm_write_wrapper(struct pcm *pcm, const void * buffer, size_t bytes, int flags)
+{
+    int ret = 0;
+    if(flags & PCM_MMAP)
+         ret = pcm_mmap_write(pcm, (void *)buffer, bytes);
+    else
+         ret = pcm_write(pcm, (void *)buffer, bytes);
+
+    if(ret !=0) {
+         ALOGW("ret %d, pcm write %d error %s", ret, bytes, pcm_get_error(pcm));
+
+         switch(pcm_state(pcm)) {
+              case PCM_STATE_SETUP:
+              case PCM_STATE_XRUN:
+                   ret = pcm_prepare(pcm);
+                   if(ret != 0) return ret;
+                   break;
+              default:
+                   return ret;
+         }
+
+         if(flags & PCM_MMAP)
+            ret = pcm_mmap_write(pcm, (void *)buffer, bytes);
+         else
+            ret = pcm_write(pcm, (void *)buffer, bytes);
+    }
+
+    return ret;
+}
+
+static ssize_t out_write_primary(struct audio_stream_out *stream, const void* buffer,
+                         size_t bytes)
+{
+    int ret;
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *adev = out->dev;
+    size_t frame_size = audio_stream_frame_size(&out->stream.common);
+    size_t in_frames = bytes / frame_size;
+    size_t out_frames = in_frames;
+    bool force_input_standby = false;
+    struct imx_stream_in *in;
+    int i;
+    /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
+     * on the output stream mutex - e.g. executing select_mode() while holding the hw device
+     * mutex
+     */
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&out->lock);
+    if (out->standby) {
+        ret = start_output_stream_primary(out);
+        if (ret != 0) {
+            pthread_mutex_unlock(&adev->lock);
+            goto exit;
+        }
+        out->standby = 0;
+        /* a change in output device may change the microphone selection */
+        if (adev->active_input &&
+                adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION)
+            force_input_standby = true;
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    /* only use resampler if required */
+    for (i = 0; i < PCM_TOTAL; i++) {
+        /* only use resampler if required */
+        if (out->pcm[i] && out->resampler[i] && (out->config[i].rate != adev->default_rate)) {
+            out_frames = out->buffer_frames;
+            out->resampler[i]->resample_from_input(out->resampler[i],
+                                                (int16_t *)buffer,
+                                                &in_frames,
+                                                (int16_t *)out->buffer,
+                                                &out_frames);
+            break;
+        }
+    }
+
+    if (out->echo_reference != NULL) {
+        struct echo_reference_buffer b;
+        b.raw = (void *)buffer;
+        b.frame_count = in_frames;
+
+        get_playback_delay(out, out_frames, &b);
+        out->echo_reference->write(out->echo_reference, &b);
+    }
+    /* do not allow more than out->write_threshold frames in kernel pcm driver buffer */
+    /* Write to all active PCMs */
+    for (i = 0; i < PCM_TOTAL; i++) {
+        if (out->pcm[i]) {
+            if (out->config[i].rate == adev->default_rate) {
+                /* PCM uses native sample rate */
+                ret = pcm_write_wrapper(out->pcm[i], (void *)buffer, bytes, out->write_flags[i]);
+            } else {
+                /* PCM needs resampler */
+                ret = pcm_write_wrapper(out->pcm[i], (void *)out->buffer, out_frames * frame_size, out->write_flags[i]);
+            }
+
+            if (ret) {
+                out->writeContiFailCount[i]++;
+                break;
+            } else {
+                out->writeContiFailCount[i] = 0;
+            }
+        }
+   }
+
+    //If continue fail, probably th fd is invalid.
+    for (i = 0; i < PCM_TOTAL; i++) {
+        if(out->writeContiFailCount[i] > 100) {
+            ALOGW("pcm_write_wrapper continues failed for pcm %d, standby", i);
+            do_output_standby(out);
+            break;
+        }
+    }
+
+exit:
+    out->written += bytes / frame_size;
+    pthread_mutex_unlock(&out->lock);
+
+    if (ret != 0) {
+        ALOGV("write error, sleep few ms");
+        usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+               out_get_sample_rate(&stream->common));
+    }
+
+    if (force_input_standby) {
+        pthread_mutex_lock(&adev->lock);
+        if (adev->active_input) {
+            in = adev->active_input;
+            pthread_mutex_lock(&in->lock);
+            do_input_standby(in);
+            pthread_mutex_unlock(&in->lock);
+        }
+        pthread_mutex_unlock(&adev->lock);
+    }
+    return bytes;
+}
+
+static ssize_t out_write_hdmi(struct audio_stream_out *stream, const void* buffer,
+                         size_t bytes)
+{
+    int ret;
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *adev = out->dev;
+    size_t frame_size = audio_stream_frame_size(&out->stream.common);
+    size_t in_frames = bytes / frame_size;
+
+    /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
+     * on the output stream mutex - e.g. executing select_mode() while holding the hw device
+     * mutex
+     */
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&out->lock);
+    if (out->standby) {
+        ret = start_output_stream_hdmi(out);
+        if (ret != 0) {
+            pthread_mutex_unlock(&adev->lock);
+            goto exit;
+        }
+        out->standby = 0;
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    /* do not allow more than out->write_threshold frames in kernel pcm driver buffer */
+
+    ret = pcm_write_wrapper(out->pcm[PCM_HDMI], (void *)buffer, bytes, out->write_flags[PCM_HDMI]);
+
+exit:
+    out->written += bytes / frame_size;
+    pthread_mutex_unlock(&out->lock);
+
+    if (ret != 0) {
+        ALOGV("write error, sleep few ms");
+        usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+               out_get_sample_rate(&stream->common));
+    }
+
+    return bytes;
+}
+
+/********************************************************************************************************
+For esai, it will use the first channels/2 for Left channels, use second half channels for Right channels.
+So we need to transform channel map in HAL for multichannel.
+when input is "FL, FR, C, LFE, BL, BR, SL, SR", output is "FL,BL,C,FR,BR,LFE".
+when input is "FL, FR, C, LFE, BL, BR, SL, SR", output is "FL,BL,C,SL,FR,BR,LFE,SR"
+*********************************************************************************************************/
+static void convert_output_for_esai(const void* buffer, size_t bytes, int channels)
+{
+    short *data_src = (short *)buffer;
+    short *data_dst = (short *)buffer;
+    short dataFL,dataFR,dataC,dataLFE,dataBL,dataBR,dataSL,dataSR;
+    int i;
+
+    if (channels == 6) {
+        for (i = 0; i < (int)bytes/(2*channels); i++ ) {
+           dataFL  = *data_src++;
+           dataFR  = *data_src++;
+           dataC   = *data_src++;
+           dataLFE = *data_src++;
+           dataBL  = *data_src++;
+           dataBR  = *data_src++;
+           *data_dst++ = dataFL;
+           *data_dst++ = dataBL;
+           *data_dst++ = dataC;
+           *data_dst++ = dataFR;
+           *data_dst++ = dataBR;
+           *data_dst++ = dataLFE;
+        }
+    }
+    else if (channels == 8) {
+        for (i = 0; i < (int)bytes/(2*channels); i++ ) {
+           dataFL  = *data_src++;
+           dataFR  = *data_src++;
+           dataC   = *data_src++;
+           dataLFE = *data_src++;
+           dataBL  = *data_src++;
+           dataBR  = *data_src++;
+           dataSL  = *data_src++;
+           dataSR  = *data_src++;
+           *data_dst++ = dataFL;
+           *data_dst++ = dataBL;
+           *data_dst++ = dataC;
+           *data_dst++ = dataSL;
+           *data_dst++ = dataFR;
+           *data_dst++ = dataBR;
+           *data_dst++ = dataLFE;
+           *data_dst++ = dataSR;
+        }
+    }
+}
+
+static ssize_t out_write_esai(struct audio_stream_out *stream, const void* buffer,
+                         size_t bytes)
+{
+    int ret;
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *adev = out->dev;
+    size_t frame_size = audio_stream_frame_size(&out->stream.common);
+    size_t in_frames = bytes / frame_size;
+
+    /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
+     * on the output stream mutex - e.g. executing select_mode() while holding the hw device
+     * mutex
+     */
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&out->lock);
+    if (out->standby) {
+        ret = start_output_stream_esai(out);
+        if (ret != 0) {
+            pthread_mutex_unlock(&adev->lock);
+            goto exit;
+        }
+        out->standby = 0;
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    /* do not allow more than out->write_threshold frames in kernel pcm driver buffer */
+
+    convert_output_for_esai(buffer, bytes, out->config[PCM_ESAI].channels);
+    ret = pcm_write_wrapper(out->pcm[PCM_ESAI], (void *)buffer, bytes, out->write_flags[PCM_ESAI]);
+
+exit:
+    out->written += bytes / frame_size;
+    pthread_mutex_unlock(&out->lock);
+
+    if (ret != 0) {
+        ALOGV("write error, sleep few ms");
+        usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+               out_get_sample_rate(&stream->common));
+    }
+
+    return bytes;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+                                   uint32_t *dsp_frames)
+{
+    ALOGW("get render position....");
+    return -EINVAL;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    return 0;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    return 0;
+}
+
+static int out_get_presentation_position(const struct audio_stream_out *stream,
+                                   uint64_t *frames, struct timespec *timestamp)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *adev = out->dev;
+    int ret = -1;
+    int i;
+
+    pthread_mutex_lock(&out->lock);
+
+    for (i = 0; i < PCM_TOTAL; i++)
+        if (out->pcm[i]) {
+            size_t avail;
+            if (pcm_get_htimestamp(out->pcm[i], &avail, timestamp) == 0) {
+                size_t kernel_buffer_size = out->config[i].period_size * out->config[i].period_count;
+		/*Actually we have no case for adev->default_rate != out->config[i].rate */
+                int64_t signed_frames = out->written - (kernel_buffer_size - avail) * adev->default_rate / out->config[i].rate;
+
+                if (signed_frames >= 0) {
+                    *frames = signed_frames;
+                    ret = 0;
+                }
+                break;
+            }
+        }
+
+    pthread_mutex_unlock(&out->lock);
+
+    return ret;
+}
+
+/** audio_stream_in implementation **/
+#define MID_RATE_0_8000  4000
+#define MID_RATE_8000_11025  ((8000+11025)/2)
+#define MID_RATE_11025_16000 ((11025+16000)/2)
+#define MID_RATE_16000_22050  ((16000+22050)/2)
+#define MID_RATE_22050_32000  ((22050+32000)/2)
+#define MID_RATE_32000_44100  ((32000+44100)/2)
+#define MID_RATE_44100_48000  ((44100+48000)/2)
+#define MID_RATE_48000_64000  ((48000+64000)/2)
+#define MID_RATE_64000_88200  ((64000+88200)/2)
+#define MID_RATE_88200_96000  ((88200+96000)/2)
+#define MID_RATE_96000_176400  ((96000+176400)/2)
+#define MID_RATE_176400_196000  ((176400+196000)/2)
+
+static int spdif_in_rate_check(struct imx_stream_in *in)
+{
+    struct imx_audio_device *adev = in->dev;
+    int i = adev->in_card_idx;
+    int ret = 0;
+
+    if(!strcmp(adev->card_list[i]->driver_name, "imx-spdif")) {
+        struct mixer_ctl *ctl;
+        unsigned int rate = 0;
+        struct mixer *mixer_spdif = adev->mixer[i];
+        ctl = mixer_get_ctl_by_name(mixer_spdif, "RX Sample Rate");
+        if (ctl) {
+            rate = mixer_ctl_get_value(ctl, 0);
+        }
+
+	if (rate <= MID_RATE_0_8000)
+		rate = 0;
+	else if (MID_RATE_0_8000 < rate && rate <= MID_RATE_8000_11025)
+		rate = 8000;
+	else if (MID_RATE_8000_11025 < rate && rate <= MID_RATE_11025_16000)
+		rate = 11025;
+	else if (MID_RATE_11025_16000 < rate && rate <= MID_RATE_16000_22050)
+		rate = 16000;
+	else if (MID_RATE_16000_22050 < rate && rate <= MID_RATE_22050_32000)
+		rate = 22050;
+	else if (MID_RATE_22050_32000 < rate && rate <= MID_RATE_32000_44100)
+		rate = 32000;
+	else if (MID_RATE_32000_44100 < rate && rate <= MID_RATE_44100_48000)
+		rate = 44100;
+	else if (MID_RATE_44100_48000 < rate && rate <= MID_RATE_48000_64000)
+		rate = 48000;
+	else if (MID_RATE_48000_64000 < rate && rate <= MID_RATE_64000_88200)
+		rate = 64000;
+	else if (MID_RATE_64000_88200 < rate && rate <= MID_RATE_88200_96000)
+		rate = 88200;
+	else if (MID_RATE_88200_96000 < rate && rate <= MID_RATE_96000_176400)
+		rate = 96000;
+	else if (MID_RATE_96000_176400 < rate && rate <= MID_RATE_176400_196000)
+		rate = 176400;
+	else
+		rate = 196000;
+
+	if (rate > 0 && rate != in->config.rate) {
+            in->config.rate = rate;
+            ALOGW("spdif input rate changed to %d", rate);
+
+            if (in->resampler) {
+                release_resampler(in->resampler);
+            }
+
+            if (in->requested_rate != in->config.rate) {
+                in->buf_provider.get_next_buffer = get_next_buffer;
+                in->buf_provider.release_buffer = release_buffer;
+
+                ret = create_resampler(in->config.rate,
+                               in->requested_rate,
+                               in->requested_channel,
+                               RESAMPLER_QUALITY_DEFAULT,
+                               &in->buf_provider,
+                               &in->resampler);
+            }
+
+            /* if no supported sample rate is available, use the resampler */
+            if (in->resampler) {
+                in->resampler->reset(in->resampler);
+            }
+        }
+    }
+
+    return 0;
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static int start_input_stream(struct imx_stream_in *in)
+{
+    int ret = 0;
+    int i;
+    struct imx_audio_device *adev = in->dev;
+    unsigned int card = -1;
+    unsigned int port = 0;
+    struct mixer *mixer;
+    int rate = 0, channels = 0, format = 0;
+
+    ALOGW("start_input_stream....");
+
+    adev->active_input = in;
+    if (adev->mode != AUDIO_MODE_IN_CALL) {
+        adev->in_device = in->device;
+        select_input_device(adev);
+    }
+
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++) {
+        if(adev->in_device & adev->card_list[i]->supported_in_devices) {
+            card = adev->card_list[i]->card;
+            adev->in_card_idx = i;
+            port = 0;
+            break;
+        }
+        if(i == MAX_AUDIO_CARD_NUM-1) {
+            ALOGE("can not find supported device for %d",in->device);
+            return -EINVAL;
+        }
+    }
+
+    /*Error handler for usb mic plug in/plug out when recording. */
+    memcpy(&in->config, &pcm_config_mm_in, sizeof(pcm_config_mm_in));
+
+    in->config.stop_threshold = in->config.period_size * in->config.period_count;
+
+    if ((in->device & AUDIO_DEVICE_IN_USB_DEVICE) || (in->device & AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET)) {
+        channels = adev_get_channels_for_device(adev, in->device, PCM_IN);
+        if (channels == 2){
+            in->config.channels = 2;
+        } else if (channels == 1) {
+            in->config.channels = 1;
+        } else {
+              ALOGW("can not get channels for in_device %d ", in->device);
+              return -EINVAL;
+        }
+
+        rate     = adev_get_rate_for_device(adev, in->device, PCM_IN);
+        if( rate == 0) {
+              ALOGW("can not get rate for in_device %d ", in->device);
+              return -EINVAL;
+        }
+        in->config.rate     =  rate;
+    } else if (in->device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
+        format     = adev_get_format_for_device(adev, in->device, PCM_IN);
+        in->config.format  = format;
+    }
+
+    ALOGW("card %d, port %d device 0x%x", card, port, in->device);
+    ALOGW("rate %d, channel %d format %d, period_size 0x%x", in->config.rate, in->config.channels, 
+                                 in->config.format, in->config.period_size);
+
+    if (in->need_echo_reference && in->echo_reference == NULL)
+        in->echo_reference = get_echo_reference(adev,
+                                        AUDIO_FORMAT_PCM_16_BIT,
+                                        in->requested_channel,
+                                        in->requested_rate);
+
+    /* this assumes routing is done previously */
+    in->pcm = pcm_open(card, port, PCM_IN, &in->config);
+    if (!pcm_is_ready(in->pcm)) {
+        ALOGE("cannot open pcm_in driver: %s", pcm_get_error(in->pcm));
+        pcm_close(in->pcm);
+        /*workaround for some usb camera (V-UBM46). the issue is that:
+          open camerarecorder,recording, suspend, resumed, recording. sometimes the audio input
+          will be failed to open.
+          Analysis: when resume, the usb will do some initialize in kernel, but if the user start
+          recording quickly, there will be some confliction which will cause the input open failed
+          and reopen also failed.
+          But if open and close the mixer here then the input will be opened successfully.
+        */
+        if(!strcmp(adev->card_list[i]->driver_name, "USB-Audio")) {
+                mixer = mixer_open(card);
+                mixer_close(mixer);
+        }
+        adev->active_input = NULL;
+        return -ENOMEM;
+    }
+
+    in->read_buf_frames = 0;
+    in->read_buf_size   = 0;
+    in->proc_buf_frames = 0;
+    in->proc_buf_size = 0;
+
+    if (in->resampler) {
+        release_resampler(in->resampler);
+    }
+    if (in->requested_rate != in->config.rate) {
+        in->buf_provider.get_next_buffer = get_next_buffer;
+        in->buf_provider.release_buffer = release_buffer;
+
+        ret = create_resampler(in->config.rate,
+                               in->requested_rate,
+                               in->requested_channel,
+                               RESAMPLER_QUALITY_DEFAULT,
+                               &in->buf_provider,
+                               &in->resampler);
+    }
+
+    /* if no supported sample rate is available, use the resampler */
+    if (in->resampler) {
+        in->resampler->reset(in->resampler);
+    }
+    return 0;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+
+    return in->requested_rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+    return 0;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+
+    return get_input_buffer_size(in->requested_rate,
+                                 AUDIO_FORMAT_PCM_16_BIT,
+                                 in->requested_channel);
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+
+    if (in->requested_channel == 1) {
+        return AUDIO_CHANNEL_IN_MONO;
+    } else {
+        return AUDIO_CHANNEL_IN_STEREO;
+    }
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    switch(in->requested_format) {
+    case PCM_FORMAT_S16_LE:
+         return AUDIO_FORMAT_PCM_16_BIT;
+    case PCM_FORMAT_S32_LE:
+         return AUDIO_FORMAT_PCM_32_BIT;
+    case PCM_FORMAT_S24_LE:
+         return AUDIO_FORMAT_PCM_8_24_BIT;
+    default:
+         return AUDIO_FORMAT_PCM_16_BIT;
+    }
+
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+    return 0;
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static int do_input_standby(struct imx_stream_in *in)
+{
+    struct imx_audio_device *adev = in->dev;
+
+    if (!in->standby) {
+        ALOGW("do_in_standby..");
+        pcm_close(in->pcm);
+        in->pcm = NULL;
+        in->last_time_of_xrun = 0;
+
+        adev->active_input = 0;
+        if (adev->mode != AUDIO_MODE_IN_CALL) {
+            adev->in_device = AUDIO_DEVICE_NONE;
+            select_input_device(adev);
+        }
+
+        if (in->echo_reference != NULL) {
+            /* stop reading from echo reference */
+            in->echo_reference->read(in->echo_reference, NULL);
+            put_echo_reference(adev, in->echo_reference);
+            in->echo_reference = NULL;
+        }
+
+        in->standby = 1;
+    }
+    return 0;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    int status;
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+    status = do_input_standby(in);
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+    return status;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+    return 0;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    struct imx_audio_device *adev = in->dev;
+    struct str_parms *parms;
+    char *str;
+    char value[32];
+    int ret, val = 0;
+    bool do_standby = false;
+
+    parms = str_parms_create_str(kvpairs);
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value));
+
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&in->lock);
+    if (ret >= 0) {
+        val = atoi(value);
+        /* no audio source uses val == 0 */
+        if ((in->source != val) && (val != 0)) {
+            in->source = val;
+            do_standby = true;
+        }
+    }
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+    if (ret >= 0) {
+        val = atoi(value) & ~AUDIO_DEVICE_BIT_IN;
+        if ((in->device != val) && (val != 0)) {
+            in->device = val;
+            do_standby = true;
+            if(in->device & AUDIO_DEVICE_IN_USB_DEVICE)
+               scan_available_device(adev, false, true, false);
+            in_update_aux_channels(in, NULL);
+        }
+    }
+
+    if (do_standby)
+        do_input_standby(in);
+
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&adev->lock);
+
+    ALOGW("in_set_parameters %s, ret %d", kvpairs, ret);
+    str_parms_destroy(parms);
+    return ret;
+}
+
+static char * in_get_parameters(const struct audio_stream *stream,
+                                const char *keys)
+{
+    return strdup("");
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+    return 0;
+}
+
+static void get_capture_delay(struct imx_stream_in *in,
+                       size_t frames,
+                       struct echo_reference_buffer *buffer)
+{
+
+    /* read frames available in kernel driver buffer */
+    size_t kernel_frames;
+    struct timespec tstamp;
+    long buf_delay;
+    long rsmp_delay;
+    long kernel_delay;
+    long delay_ns;
+
+    if (pcm_get_htimestamp(in->pcm, &kernel_frames, &tstamp) < 0) {
+        buffer->time_stamp.tv_sec  = 0;
+        buffer->time_stamp.tv_nsec = 0;
+        buffer->delay_ns           = 0;
+        ALOGW("read get_capture_delay(): pcm_htimestamp error");
+        return;
+    }
+
+    /* read frames available in audio HAL input buffer
+     * add number of frames being read as we want the capture time of first sample
+     * in current buffer */
+    buf_delay = (long)(((int64_t)(in->read_buf_frames) * 1000000000) / in->config.rate +
+                       ((int64_t)(in->proc_buf_frames) * 1000000000) /
+                           in->requested_rate);
+
+    /* add delay introduced by resampler */
+    rsmp_delay = 0;
+    if (in->resampler) {
+        rsmp_delay = in->resampler->delay_ns(in->resampler);
+    }
+
+    kernel_delay = (long)(((int64_t)kernel_frames * 1000000000) / in->config.rate);
+
+    delay_ns = kernel_delay + buf_delay + rsmp_delay;
+
+    buffer->time_stamp = tstamp;
+    buffer->delay_ns   = delay_ns;
+    ALOGV("get_capture_delay time_stamp = [%ld].[%ld], delay_ns: [%d],"
+         " kernel_delay:[%ld], buf_delay:[%ld], rsmp_delay:[%ld], kernel_frames:[%d], "
+         "in->read_buf_frames:[%d], in->proc_buf_frames:[%d], frames:[%d]",
+         buffer->time_stamp.tv_sec , buffer->time_stamp.tv_nsec, buffer->delay_ns,
+         kernel_delay, buf_delay, rsmp_delay, kernel_frames,
+         in->read_buf_frames, in->proc_buf_frames, frames);
+
+}
+
+static int32_t update_echo_reference(struct imx_stream_in *in, size_t frames)
+{
+    struct echo_reference_buffer b;
+    b.delay_ns = 0;
+
+    ALOGV("update_echo_reference, frames = [%d], in->ref_frames_in = [%d],  "
+          "b.frame_count = [%d]",
+         frames, in->ref_frames_in, frames - in->ref_frames_in);
+    if (in->ref_frames_in < frames) {
+        if (in->ref_buf_size < frames) {
+            in->ref_buf_size = frames;
+            in->ref_buf = (int16_t *)realloc(in->ref_buf,
+                                             in->ref_buf_size *
+                                                 in->requested_channel * sizeof(int16_t));
+        }
+
+        b.frame_count = frames - in->ref_frames_in;
+        b.raw = (void *)(in->ref_buf + in->ref_frames_in * in->requested_channel);
+
+        get_capture_delay(in, frames, &b);
+
+        if (in->echo_reference->read(in->echo_reference, &b) == 0)
+        {
+            in->ref_frames_in += b.frame_count;
+            ALOGV("update_echo_reference: in->ref_frames_in:[%d], "
+                    "in->ref_buf_size:[%d], frames:[%d], b.frame_count:[%d]",
+                 in->ref_frames_in, in->ref_buf_size, frames, b.frame_count);
+        }
+    } else
+        ALOGV("update_echo_reference: NOT enough frames to read ref buffer");
+    return b.delay_ns;
+}
+
+static int set_preprocessor_param(effect_handle_t handle,
+                           effect_param_t *param)
+{
+    uint32_t size = sizeof(int);
+    uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
+                        param->vsize;
+
+    int status = (*handle)->command(handle,
+                                   EFFECT_CMD_SET_PARAM,
+                                   sizeof (effect_param_t) + psize,
+                                   param,
+                                   &size,
+                                   &param->status);
+    if (status == 0)
+        status = param->status;
+
+    return status;
+}
+
+static int set_preprocessor_echo_delay(effect_handle_t handle,
+                                     int32_t delay_us)
+{
+    uint32_t buf[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+    effect_param_t *param = (effect_param_t *)buf;
+
+    param->psize = sizeof(uint32_t);
+    param->vsize = sizeof(uint32_t);
+    *(uint32_t *)param->data = AEC_PARAM_ECHO_DELAY;
+    *((int32_t *)param->data + 1) = delay_us;
+
+    return set_preprocessor_param(handle, param);
+}
+
+static void push_echo_reference(struct imx_stream_in *in, size_t frames)
+{
+    /* read frames from echo reference buffer and update echo delay
+     * in->ref_frames_in is updated with frames available in in->ref_buf */
+    int32_t delay_us = update_echo_reference(in, frames)/1000;
+    int i;
+    audio_buffer_t buf;
+
+    if (in->ref_frames_in < frames)
+        frames = in->ref_frames_in;
+
+    buf.frameCount = frames;
+    buf.raw = in->ref_buf;
+
+    for (i = 0; i < in->num_preprocessors; i++) {
+        if ((*in->preprocessors[i].effect_itfe)->process_reverse == NULL)
+            continue;
+
+        (*in->preprocessors[i].effect_itfe)->process_reverse(in->preprocessors[i].effect_itfe,
+                                               &buf,
+                                               NULL);
+        set_preprocessor_echo_delay(in->preprocessors[i].effect_itfe, delay_us);
+    }
+
+    in->ref_frames_in -= buf.frameCount;
+    if (in->ref_frames_in) {
+        memcpy(in->ref_buf,
+               in->ref_buf + buf.frameCount * in->requested_channel,
+               in->ref_frames_in * in->requested_channel * sizeof(int16_t));
+    }
+}
+
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+                                   struct resampler_buffer* buffer)
+{
+    struct imx_stream_in *in;
+
+    if (buffer_provider == NULL || buffer == NULL)
+        return -EINVAL;
+
+    in = (struct imx_stream_in *)((char *)buffer_provider -
+                                   offsetof(struct imx_stream_in, buf_provider));
+
+    if (in->pcm == NULL) {
+        buffer->raw = NULL;
+        buffer->frame_count = 0;
+        in->read_status = -ENODEV;
+        return -ENODEV;
+    }
+
+    if (in->read_buf_frames == 0) {
+        size_t size_in_bytes = in->config.period_size * audio_stream_frame_size(&in->stream.common);
+        if (in->read_buf_size < in->config.period_size) {
+            in->read_buf_size = in->config.period_size;
+            in->read_buf = (int16_t *) realloc(in->read_buf, size_in_bytes);
+            ALOG_ASSERT((in->read_buf != NULL),
+                        "get_next_buffer() failed to reallocate read_buf");
+            ALOGV("get_next_buffer(): read_buf %p extended to %d bytes",
+                  in->read_buf, size_in_bytes);
+        }
+
+        in->read_status = pcm_read_convert(in, in->pcm, (void*)in->read_buf, size_in_bytes);
+
+        if (in->read_status != 0) {
+            ALOGE("get_next_buffer() pcm_read_convert error %d", in->read_status);
+            buffer->raw = NULL;
+            buffer->frame_count = 0;
+            return in->read_status;
+        }
+        in->read_buf_frames = in->config.period_size;
+    }
+
+    buffer->frame_count = (buffer->frame_count > in->read_buf_frames) ?
+                                in->read_buf_frames : buffer->frame_count;
+    buffer->i16 = in->read_buf + (in->config.period_size - in->read_buf_frames) *
+                                                in->requested_channel;
+
+    return in->read_status;
+
+}
+
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+                                  struct resampler_buffer* buffer)
+{
+    struct imx_stream_in *in;
+
+    if (buffer_provider == NULL || buffer == NULL)
+        return;
+
+    in = (struct imx_stream_in *)((char *)buffer_provider -
+                                   offsetof(struct imx_stream_in, buf_provider));
+
+    in->read_buf_frames -= buffer->frame_count;
+}
+
+/* read_frames() reads frames from kernel driver, down samples to capture rate
+ * if necessary and output the number of frames requested to the buffer specified */
+static ssize_t read_frames(struct imx_stream_in *in, void *buffer, ssize_t frames)
+{
+    ssize_t frames_wr = 0;
+
+    while (frames_wr < frames) {
+        size_t frames_rd = frames - frames_wr;
+        if (in->resampler != NULL) {
+            in->resampler->resample_from_provider(in->resampler,
+                    (int16_t *)((char *)buffer +
+                            frames_wr * audio_stream_frame_size(&in->stream.common)),
+                    &frames_rd);
+        } else {
+            struct resampler_buffer buf = {
+                    { raw : NULL, },
+                    frame_count : frames_rd,
+            };
+            get_next_buffer(&in->buf_provider, &buf);
+            if (buf.raw != NULL) {
+                memcpy((char *)buffer +
+                           frames_wr * audio_stream_frame_size(&in->stream.common),
+                        buf.raw,
+                        buf.frame_count * audio_stream_frame_size(&in->stream.common));
+                frames_rd = buf.frame_count;
+            }
+            release_buffer(&in->buf_provider, &buf);
+        }
+        /* in->read_status is updated by getNextBuffer() also called by
+         * in->resampler->resample_from_provider() */
+        if (in->read_status != 0)
+            return in->read_status;
+
+        frames_wr += frames_rd;
+    }
+    return frames_wr;
+}
+
+/* process_frames() reads frames from kernel driver (via read_frames()),
+ * calls the active audio pre processings and output the number of frames requested
+ * to the buffer specified */
+static ssize_t process_frames(struct imx_stream_in *in, void* buffer, ssize_t frames)
+{
+    ssize_t frames_wr = 0;
+    audio_buffer_t in_buf;
+    audio_buffer_t out_buf;
+    int i;
+    bool has_aux_channels = (~in->main_channels & in->aux_channels);
+    void *proc_buf_out;
+
+    if (has_aux_channels)
+        proc_buf_out = in->proc_buf_out;
+    else
+        proc_buf_out = buffer;
+
+    /* since all the processing below is done in frames and using the config.channels
+     * as the number of channels, no changes is required in case aux_channels are present */
+    while (frames_wr < frames) {
+        /* first reload enough frames at the end of process input buffer */
+        if (in->proc_buf_frames < (size_t)frames) {
+            ssize_t frames_rd;
+
+            if (in->proc_buf_size < (size_t)frames) {
+                in->proc_buf_size = (size_t)frames;
+                in->proc_buf_in = (int16_t *)realloc(in->proc_buf_in,
+                                         in->proc_buf_size * in->requested_channel * sizeof(int16_t));
+
+                ALOG_ASSERT((in->proc_buf_in != NULL),
+                            "process_frames() failed to reallocate proc_buf_in");
+                if (has_aux_channels) {
+                    in->proc_buf_out = (int16_t *)realloc(in->proc_buf_out, in->proc_buf_size * in->requested_channel * sizeof(int16_t));
+                    ALOG_ASSERT((in->proc_buf_out != NULL),
+                                "process_frames() failed to reallocate proc_buf_out");
+                    proc_buf_out = in->proc_buf_out;
+                }
+                ALOGV("process_frames(): proc_buf_in %p extended to %d bytes",
+                     in->proc_buf_in,in->proc_buf_size * in->requested_channel * sizeof(int16_t));
+            }
+            frames_rd = read_frames(in,
+                                    in->proc_buf_in +
+                                        in->proc_buf_frames * in->requested_channel,
+                                    frames - in->proc_buf_frames);
+            if (frames_rd < 0) {
+                frames_wr = frames_rd;
+                break;
+            }
+            in->proc_buf_frames += frames_rd;
+        }
+
+        if (in->echo_reference != NULL)
+            push_echo_reference(in, in->proc_buf_frames);
+
+         /* in_buf.frameCount and out_buf.frameCount indicate respectively
+          * the maximum number of frames to be consumed and produced by process() */
+        in_buf.frameCount = in->proc_buf_frames;
+        in_buf.s16 = in->proc_buf_in;
+        out_buf.frameCount = frames - frames_wr;
+        out_buf.s16 = (int16_t *)proc_buf_out + frames_wr * in->requested_channel;
+
+        /* FIXME: this works because of current pre processing library implementation that
+         * does the actual process only when the last enabled effect process is called.
+         * The generic solution is to have an output buffer for each effect and pass it as
+         * input to the next.
+         */
+        for (i = 0; i < in->num_preprocessors; i++) {
+            (*in->preprocessors[i].effect_itfe)->process(in->preprocessors[i].effect_itfe,
+                                               &in_buf,
+                                               &out_buf);
+        }
+
+        /* process() has updated the number of frames consumed and produced in
+         * in_buf.frameCount and out_buf.frameCount respectively
+         * move remaining frames to the beginning of in->proc_buf */
+        in->proc_buf_frames -= in_buf.frameCount;
+        if (in->proc_buf_frames) {
+            memcpy(in->proc_buf_in,
+                   in->proc_buf_in + in_buf.frameCount * in->requested_channel,
+                   in->proc_buf_frames * in->requested_channel * sizeof(int16_t));
+        }
+
+        /* if not enough frames were passed to process(), read more and retry. */
+        if (out_buf.frameCount == 0) {
+            ALOGV("No frames produced by preproc");
+            continue;
+        }
+
+        if ((frames_wr + (ssize_t)out_buf.frameCount) <= frames) {
+            frames_wr += out_buf.frameCount;
+        } else {
+            /* The effect does not comply to the API. In theory, we should never end up here! */
+            ALOGE("preprocessing produced too many frames: %d + %d  > %d !",
+                  (unsigned int)frames_wr, out_buf.frameCount, (unsigned int)frames);
+            frames_wr = frames;
+        }
+    }
+    /* Remove aux_channels that have been added on top of main_channels
+     * Assumption is made that the channels are interleaved and that the main
+     * channels are first. */
+    if (has_aux_channels)
+    {
+        size_t src_channels = in->config.channels;
+        size_t dst_channels = popcount(in->main_channels);
+        int16_t* src_buffer = (int16_t *)proc_buf_out;
+        int16_t* dst_buffer = (int16_t *)buffer;
+
+        if (dst_channels == 1) {
+            for (i = frames_wr; i > 0; i--)
+            {
+                *dst_buffer++ = *src_buffer;
+                src_buffer += src_channels;
+            }
+        } else {
+            for (i = frames_wr; i > 0; i--)
+            {
+                memcpy(dst_buffer, src_buffer, dst_channels*sizeof(int16_t));
+                dst_buffer += dst_channels;
+                src_buffer += src_channels;
+            }
+        }
+    }
+
+    return frames_wr;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
+                       size_t bytes)
+{
+    int ret = 0;
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    struct imx_audio_device *adev = in->dev;
+    size_t frames_rq = bytes / audio_stream_frame_size(&stream->common);
+
+    /* acquiring hw device mutex systematically is useful if a low priority thread is waiting
+     * on the input stream mutex - e.g. executing select_mode() while holding the hw device
+     * mutex
+     */
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&in->lock);
+    if (in->standby) {
+        ret = start_input_stream(in);
+        if (ret == 0) {
+            in->standby = 0;
+            in->mute_500ms = in->requested_rate * audio_stream_frame_size(&stream->common)/2;
+        }
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    if (ret < 0)
+        goto exit;
+
+    spdif_in_rate_check(in);
+
+    if (in->num_preprocessors != 0)
+        ret = process_frames(in, buffer, frames_rq);
+    else if (in->resampler != NULL)
+        ret = read_frames(in, buffer, frames_rq);
+    else
+        ret = pcm_read_convert(in, in->pcm, buffer, bytes);
+
+    if(ret < 0) ALOGW("ret %d, pcm read error %s.", ret, pcm_get_error(in->pcm));
+
+    if (ret > 0)
+        ret = 0;
+
+    if (ret == 0 && adev->mic_mute)
+        memset(buffer, 0, bytes);
+
+    if (in->mute_500ms > 0) {
+        if(bytes <= in->mute_500ms) {
+                memset(buffer, 0, bytes);
+                in->mute_500ms = in->mute_500ms - bytes;
+        } else {
+                memset(buffer, 0, in->mute_500ms);
+                in->mute_500ms = 0;
+        }
+    }
+
+exit:
+    if (ret < 0) {
+        memset(buffer, 0, bytes);
+        usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+               in_get_sample_rate(&stream->common));
+    }
+    pthread_mutex_unlock(&in->lock);
+
+    return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+    int times, diff;
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    if (in->pcm == NULL)  return 0;
+#ifdef BRILLO
+    times = 0;
+#else
+    times = pcm_get_time_of_xrun(in->pcm);
+#endif
+    diff = times - in->last_time_of_xrun;
+    ALOGW_IF((diff != 0), "in_get_input_frames_lost %d ms total %d ms\n",diff, times);
+    in->last_time_of_xrun = times;
+    return diff * in->requested_rate / 1000;
+}
+
+#define GET_COMMAND_STATUS(status, fct_status, cmd_status) \
+            do {                                           \
+                if (fct_status != 0)                       \
+                    status = fct_status;                   \
+                else if (cmd_status != 0)                  \
+                    status = cmd_status;                   \
+            } while(0)
+
+static int in_configure_reverse(struct imx_stream_in *in)
+{
+    int32_t cmd_status;
+    uint32_t size = sizeof(int);
+    effect_config_t config;
+    int32_t status = 0;
+    int32_t fct_status = 0;
+    int i;
+
+    if (in->num_preprocessors > 0) {
+        config.inputCfg.channels = in->main_channels;
+        config.outputCfg.channels = in->main_channels;
+        config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+        config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+        config.inputCfg.samplingRate = in->requested_rate;
+        config.outputCfg.samplingRate = in->requested_rate;
+        config.inputCfg.mask =
+                ( EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT );
+        config.outputCfg.mask =
+                ( EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT );
+
+        for (i = 0; i < in->num_preprocessors; i++)
+        {
+            if ((*in->preprocessors[i].effect_itfe)->process_reverse == NULL)
+                continue;
+            fct_status = (*(in->preprocessors[i].effect_itfe))->command(
+                                                        in->preprocessors[i].effect_itfe,
+                                                        EFFECT_CMD_SET_CONFIG_REVERSE,
+                                                        sizeof(effect_config_t),
+                                                        &config,
+                                                        &size,
+                                                        &cmd_status);
+            GET_COMMAND_STATUS(status, fct_status, cmd_status);
+        }
+    }
+    return status;
+}
+
+#define MAX_NUM_CHANNEL_CONFIGS 10
+
+static void in_read_audio_effect_channel_configs(struct imx_stream_in *in,
+                                                 struct effect_info_s *effect_info)
+{
+    /* size and format of the cmd are defined in hardware/audio_effect.h */
+    effect_handle_t effect = effect_info->effect_itfe;
+    uint32_t cmd_size = 2 * sizeof(uint32_t);
+    uint32_t cmd[] = { EFFECT_FEATURE_AUX_CHANNELS, MAX_NUM_CHANNEL_CONFIGS };
+    /* reply = status + number of configs (n) + n x channel_config_t */
+    uint32_t reply_size =
+            2 * sizeof(uint32_t) + (MAX_NUM_CHANNEL_CONFIGS * sizeof(channel_config_t));
+    int32_t reply[reply_size];
+    int32_t cmd_status;
+
+    ALOG_ASSERT((effect_info->num_channel_configs == 0),
+                "in_read_audio_effect_channel_configs() num_channel_configs not cleared");
+    ALOG_ASSERT((effect_info->channel_configs == NULL),
+                "in_read_audio_effect_channel_configs() channel_configs not cleared");
+
+    /* if this command is not supported, then the effect is supposed to return -EINVAL.
+     * This error will be interpreted as if the effect supports the main_channels but does not
+     * support any aux_channels */
+    cmd_status = (*effect)->command(effect,
+                                EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,
+                                cmd_size,
+                                (void*)&cmd,
+                                &reply_size,
+                                (void*)&reply);
+
+    if (cmd_status != 0) {
+        ALOGV("in_read_audio_effect_channel_configs(): "
+                "fx->command returned %d", cmd_status);
+        return;
+    }
+
+    if (reply[0] != 0) {
+        ALOGW("in_read_audio_effect_channel_configs(): "
+                "command EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS error %d num configs %d",
+                reply[0], (reply[0] == -ENOMEM) ? reply[1] : MAX_NUM_CHANNEL_CONFIGS);
+        return;
+    }
+
+    /* the feature is not supported */
+    ALOGV("in_read_audio_effect_channel_configs()(): "
+            "Feature supported and adding %d channel configs to the list", reply[1]);
+    effect_info->num_channel_configs = reply[1];
+    effect_info->channel_configs =
+            (channel_config_t *) malloc(sizeof(channel_config_t) * reply[1]); /* n x configs */
+    memcpy(effect_info->channel_configs, (reply + 2), sizeof(channel_config_t) * reply[1]);
+}
+
+
+static uint32_t in_get_aux_channels(struct imx_stream_in *in)
+{
+    int i;
+    channel_config_t new_chcfg = {0, 0};
+
+    if (in->num_preprocessors == 0)
+        return 0;
+
+    /* do not enable dual mic configurations when capturing from other microphones than
+     * main or sub */
+    if (!(in->device & (AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BACK_MIC)))
+        return 0;
+
+    /* retain most complex aux channels configuration compatible with requested main channels and
+     * supported by audio driver and all pre processors */
+    for (i = 0; i < NUM_IN_AUX_CNL_CONFIGS; i++) {
+        channel_config_t *cur_chcfg = &in_aux_cnl_configs[i];
+        if (cur_chcfg->main_channels == in->main_channels) {
+            size_t match_cnt;
+            size_t idx_preproc;
+            for (idx_preproc = 0, match_cnt = 0;
+                 /* no need to continue if at least one preprocessor doesn't match */
+                 idx_preproc < (size_t)in->num_preprocessors && match_cnt == idx_preproc;
+                 idx_preproc++) {
+                struct effect_info_s *effect_info = &in->preprocessors[idx_preproc];
+                size_t idx_chcfg;
+
+                for (idx_chcfg = 0; idx_chcfg < effect_info->num_channel_configs; idx_chcfg++) {
+                    if (memcmp(effect_info->channel_configs + idx_chcfg,
+                               cur_chcfg,
+                               sizeof(channel_config_t)) == 0) {
+                        match_cnt++;
+                        break;
+                    }
+                }
+            }
+            /* if all preprocessors match, we have a candidate */
+            if (match_cnt == (size_t)in->num_preprocessors) {
+                /* retain most complex aux channels configuration */
+                if (popcount(cur_chcfg->aux_channels) > popcount(new_chcfg.aux_channels)) {
+                    new_chcfg = *cur_chcfg;
+                }
+            }
+        }
+    }
+
+    ALOGV("in_get_aux_channels(): return %04x", new_chcfg.aux_channels);
+
+    return new_chcfg.aux_channels;
+}
+
+static int in_configure_effect_channels(effect_handle_t effect,
+                                        channel_config_t *channel_config)
+{
+    int status = 0;
+    int fct_status;
+    int32_t cmd_status;
+    uint32_t reply_size;
+    effect_config_t config;
+    uint32_t cmd[(sizeof(uint32_t) + sizeof(channel_config_t) - 1) / sizeof(uint32_t) + 1];
+
+    ALOGV("in_configure_effect_channels(): configure effect with channels: [%04x][%04x]",
+            channel_config->main_channels,
+            channel_config->aux_channels);
+
+    config.inputCfg.mask = EFFECT_CONFIG_CHANNELS;
+    config.outputCfg.mask = EFFECT_CONFIG_CHANNELS;
+    reply_size = sizeof(effect_config_t);
+    fct_status = (*effect)->command(effect,
+                                EFFECT_CMD_GET_CONFIG,
+                                0,
+                                NULL,
+                                &reply_size,
+                                &config);
+    if (fct_status != 0) {
+        ALOGE("in_configure_effect_channels(): EFFECT_CMD_GET_CONFIG failed");
+        return fct_status;
+    }
+
+    config.inputCfg.channels = channel_config->main_channels | channel_config->aux_channels;
+    config.outputCfg.channels = config.inputCfg.channels;
+    reply_size = sizeof(uint32_t);
+    fct_status = (*effect)->command(effect,
+                                    EFFECT_CMD_SET_CONFIG,
+                                    sizeof(effect_config_t),
+                                    &config,
+                                    &reply_size,
+                                    &cmd_status);
+    GET_COMMAND_STATUS(status, fct_status, cmd_status);
+
+    cmd[0] = EFFECT_FEATURE_AUX_CHANNELS;
+    memcpy(cmd + 1, channel_config, sizeof(channel_config_t));
+    reply_size = sizeof(uint32_t);
+    fct_status = (*effect)->command(effect,
+                                EFFECT_CMD_SET_FEATURE_CONFIG,
+                                sizeof(cmd), //sizeof(uint32_t) + sizeof(channel_config_t),
+                                cmd,
+                                &reply_size,
+                                &cmd_status);
+    GET_COMMAND_STATUS(status, fct_status, cmd_status);
+
+    /* some implementations need to be re-enabled after a config change */
+    reply_size = sizeof(uint32_t);
+    fct_status = (*effect)->command(effect,
+                                  EFFECT_CMD_ENABLE,
+                                  0,
+                                  NULL,
+                                  &reply_size,
+                                  &cmd_status);
+    GET_COMMAND_STATUS(status, fct_status, cmd_status);
+
+    return status;
+}
+
+static int in_reconfigure_channels(struct imx_stream_in *in,
+                                   effect_handle_t effect,
+                                   channel_config_t *channel_config,
+                                   bool config_changed) {
+
+    int status = 0;
+
+    ALOGV("in_reconfigure_channels(): config_changed %d effect %p",
+          config_changed, effect);
+
+    /* if config changed, reconfigure all previously added effects */
+    if (config_changed) {
+        int i;
+        for (i = 0; i < in->num_preprocessors; i++)
+        {
+            int cur_status = in_configure_effect_channels(in->preprocessors[i].effect_itfe,
+                                                  channel_config);
+            if (cur_status != 0) {
+                ALOGV("in_reconfigure_channels(): error %d configuring effect "
+                        "%d with channels: [%04x][%04x]",
+                        cur_status,
+                        i,
+                        channel_config->main_channels,
+                        channel_config->aux_channels);
+                status = cur_status;
+            }
+        }
+    } else if (effect != NULL && channel_config->aux_channels) {
+        /* if aux channels config did not change but aux channels are present,
+         * we still need to configure the effect being added */
+        status = in_configure_effect_channels(effect, channel_config);
+    }
+    return status;
+}
+
+static void in_update_aux_channels(struct imx_stream_in *in,
+                                   effect_handle_t effect)
+{
+    uint32_t aux_channels;
+    channel_config_t channel_config;
+    int status;
+
+    aux_channels = in_get_aux_channels(in);
+
+    channel_config.main_channels = in->main_channels;
+    channel_config.aux_channels = aux_channels;
+    status = in_reconfigure_channels(in,
+                                     effect,
+                                     &channel_config,
+                                     (aux_channels != in->aux_channels));
+
+    if (status != 0) {
+        ALOGV("in_update_aux_channels(): in_reconfigure_channels error %d", status);
+        /* resetting aux channels configuration */
+        aux_channels = 0;
+        channel_config.aux_channels = 0;
+        in_reconfigure_channels(in, effect, &channel_config, true);
+    }
+    if (in->aux_channels != aux_channels) {
+        ALOGV("aux_channels_changed ");
+        in->aux_channels_changed = true;
+        in->aux_channels = aux_channels;
+        do_input_standby(in);
+    }
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+                               effect_handle_t effect)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    int status;
+    effect_descriptor_t desc;
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+    if (in->num_preprocessors >= MAX_PREPROCESSORS) {
+        status = -ENOSYS;
+        goto exit;
+    }
+
+    status = (*effect)->get_descriptor(effect, &desc);
+    if (status != 0)
+        goto exit;
+
+    in->preprocessors[in->num_preprocessors].effect_itfe = effect;
+    /* add the supported channel of the effect in the channel_configs */
+    in_read_audio_effect_channel_configs(in, &in->preprocessors[in->num_preprocessors]);
+
+    in->num_preprocessors++;
+
+    /* check compatibility between main channel supported and possible auxiliary channels */
+    in_update_aux_channels(in, effect);
+
+    ALOGV("in_add_audio_effect(), effect type: %08x", desc.type.timeLow);
+
+    if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
+        in->need_echo_reference = true;
+        do_input_standby(in);
+        in_configure_reverse(in);
+    }
+
+exit:
+
+    ALOGW_IF(status != 0, "in_add_audio_effect() error %d", status);
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+    return status;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+                                  effect_handle_t effect)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+    int i;
+    int status = -EINVAL;
+    effect_descriptor_t desc;
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+    if (in->num_preprocessors <= 0) {
+        status = -ENOSYS;
+        goto exit;
+    }
+
+    for (i = 0; i < in->num_preprocessors; i++) {
+        if (status == 0) { /* status == 0 means an effect was removed from a previous slot */
+            in->preprocessors[i - 1].effect_itfe = in->preprocessors[i].effect_itfe;
+            in->preprocessors[i - 1].channel_configs = in->preprocessors[i].channel_configs;
+            in->preprocessors[i - 1].num_channel_configs = in->preprocessors[i].num_channel_configs;
+            ALOGV("in_remove_audio_effect moving fx from %d to %d", i, i - 1);
+            continue;
+        }
+        if (in->preprocessors[i].effect_itfe == effect) {
+            ALOGV("in_remove_audio_effect found fx at index %d", i);
+            free(in->preprocessors[i].channel_configs);
+            status = 0;
+        }
+    }
+
+    if (status != 0)
+        goto exit;
+
+    in->num_preprocessors--;
+    /* if we remove one effect, at least the last preproc should be reset */
+    in->preprocessors[in->num_preprocessors].num_channel_configs = 0;
+    in->preprocessors[in->num_preprocessors].effect_itfe = NULL;
+    in->preprocessors[in->num_preprocessors].channel_configs = NULL;
+
+
+    /* check compatibility between main channel supported and possible auxiliary channels */
+    in_update_aux_channels(in, NULL);
+
+    status = (*effect)->get_descriptor(effect, &desc);
+    if (status != 0)
+        goto exit;
+
+    ALOGV("in_remove_audio_effect(), effect type: %08x", desc.type.timeLow);
+
+    if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
+        in->need_echo_reference = false;
+        do_input_standby(in);
+    }
+
+exit:
+
+    ALOGW_IF(status != 0, "in_remove_audio_effect() error %d", status);
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+    return status;
+}
+
+static int out_read_hdmi_channel_masks(struct imx_audio_device *adev, struct imx_stream_out *out) {
+
+    int count = 0;
+    int sup_channels[MAX_SUP_CHANNEL_NUM]; //temp buffer for supported channels
+    int card = -1;
+    int i = 0;
+    int j = 0;
+    struct mixer *mixer_hdmi = NULL;
+
+    for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+         if(!strcmp(adev->card_list[i]->driver_name, hdmi_card.driver_name)) {
+             mixer_hdmi = adev->mixer[i];
+             card = adev->card_list[i]->card;
+             break;
+         }
+    }
+
+    if (mixer_hdmi) {
+        struct mixer_ctl *ctl;
+        ctl = mixer_get_ctl_by_name(mixer_hdmi, "HDMI Support Channels");
+        if (ctl) {
+            count = mixer_ctl_get_num_values(ctl);
+            for(i = 0; i < count; i ++) {
+                sup_channels[i] = mixer_ctl_get_value(ctl, i);
+                ALOGW("out_read_hdmi_channel_masks() card %d got %d sup channels", card, sup_channels[i]);
+            }
+        }
+    }
+
+    /*when channel is 6, the mask is 5.1,when channel is 8, the mask is 7.1*/
+    for(i = 0; i < count; i++ ) {
+       if(sup_channels[i] == 2) {
+          out->sup_channel_masks[j]   = AUDIO_CHANNEL_OUT_STEREO;
+          j++;
+       }
+       if(sup_channels[i] == 6) {
+          out->sup_channel_masks[j]   = AUDIO_CHANNEL_OUT_5POINT1;
+          j++;
+       }
+       if(sup_channels[i] == 8) {
+          out->sup_channel_masks[j]   = AUDIO_CHANNEL_OUT_7POINT1;
+          j++;
+       }
+    }
+    /*if HDMI device does not support 2,6,8 channels, then return error*/
+    if (j == 0) return -ENOSYS;
+
+    return 0;
+}
+
+static int out_read_hdmi_rates(struct imx_audio_device *adev, struct imx_stream_out *out) {
+
+    int count = 0;
+    int card = -1;
+    int i = 0;
+    struct mixer *mixer_hdmi = NULL;
+
+    for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+         if(!strcmp(adev->card_list[i]->driver_name, hdmi_card.driver_name)) {
+             mixer_hdmi = adev->mixer[i];
+             card = adev->card_list[i]->card;
+             break;
+         }
+    }
+
+    if (mixer_hdmi) {
+        struct mixer_ctl *ctl;
+        ctl = mixer_get_ctl_by_name(mixer_hdmi, "HDMI Support Rates");
+        if (ctl) {
+            count = mixer_ctl_get_num_values(ctl);
+            for(i = 0; i < count; i ++) {
+                out->sup_rates[i] = mixer_ctl_get_value(ctl, i);
+                ALOGW("out_read_hdmi_rates() card %d got %d sup rates", card, out->sup_rates[i]);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int adev_open_output_stream(struct audio_hw_device *dev,
+                                   audio_io_handle_t handle,
+                                   audio_devices_t devices,
+                                   audio_output_flags_t flags,
+                                   struct audio_config *config,
+                                   struct audio_stream_out **stream_out)
+{
+    struct imx_audio_device *ladev = (struct imx_audio_device *)dev;
+    struct imx_stream_out *out;
+    int ret;
+    int output_type;
+    int i;
+
+    ALOGW("open output stream devices %d, format %d, channels %d, sample_rate %d, flag %d",
+                        devices, config->format, config->channel_mask, config->sample_rate, flags);
+
+    out = (struct imx_stream_out *)calloc(1, sizeof(struct imx_stream_out));
+    if (!out)
+        return -ENOMEM;
+
+    out->sup_rates[0] = ladev->mm_rate;
+    out->sup_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
+    out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+    if (flags & AUDIO_OUTPUT_FLAG_DIRECT &&
+                   devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+        ALOGW("adev_open_output_stream() HDMI multichannel");
+        if (ladev->active_output[OUTPUT_HDMI] != NULL) {
+            ret = -ENOSYS;
+            goto err_open;
+        }
+        ret = out_read_hdmi_channel_masks(ladev, out);
+        if (ret != 0)
+            goto err_open;
+
+        ret = out_read_hdmi_rates(ladev, out);
+
+        output_type = OUTPUT_HDMI;
+        if (config->sample_rate == 0)
+            config->sample_rate = ladev->mm_rate;
+        if (config->channel_mask == 0)
+            config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+        out->channel_mask = config->channel_mask;
+        out->stream.common.get_buffer_size = out_get_buffer_size_hdmi;
+        out->stream.common.get_sample_rate = out_get_sample_rate_hdmi;
+        out->stream.get_latency = out_get_latency_hdmi;
+        out->stream.write = out_write_hdmi;
+        out->config[PCM_HDMI] = pcm_config_hdmi_multi;
+        out->config[PCM_HDMI].rate = config->sample_rate;
+        out->config[PCM_HDMI].channels = popcount(config->channel_mask);
+    } else if (flags & AUDIO_OUTPUT_FLAG_DIRECT &&
+                   devices == AUDIO_DEVICE_OUT_SPEAKER && ladev->device_is_auto) {
+        ALOGW("adev_open_output_stream() ESAI multichannel");
+        if (ladev->active_output[OUTPUT_ESAI] != NULL) {
+            ret = -ENOSYS;
+            goto err_open;
+        }
+
+        output_type = OUTPUT_ESAI;
+        if (config->sample_rate == 0)
+            config->sample_rate = ladev->mm_rate;
+        if (config->channel_mask == 0)
+            config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+        out->channel_mask = config->channel_mask;
+        out->stream.common.get_buffer_size = out_get_buffer_size_esai;
+        out->stream.common.get_sample_rate = out_get_sample_rate_esai;
+        out->stream.get_latency = out_get_latency_esai;
+        out->stream.write = out_write_esai;
+        out->config[PCM_ESAI] = pcm_config_esai_multi;
+        out->config[PCM_ESAI].rate = config->sample_rate;
+        out->config[PCM_ESAI].channels = popcount(config->channel_mask);
+    } else {
+        ALOGV("adev_open_output_stream() normal buffer");
+        if (ladev->active_output[OUTPUT_PRIMARY] != NULL) {
+            ret = -ENOSYS;
+            goto err_open;
+        }
+        output_type = OUTPUT_PRIMARY;
+        out->stream.common.get_buffer_size = out_get_buffer_size_primary;
+        out->stream.common.get_sample_rate = out_get_sample_rate;
+        out->stream.get_latency = out_get_latency_primary;
+        out->stream.write = out_write_primary;
+    }
+
+
+    for(i = 0; i < PCM_TOTAL; i++) {
+         ret = create_resampler(ladev->default_rate,
+                               ladev->mm_rate,
+                               2,
+                               RESAMPLER_QUALITY_DEFAULT,
+                               NULL,
+                               &out->resampler[i]);
+        if (ret != 0)
+            goto err_open;
+    }
+
+    out->stream.common.set_sample_rate  = out_set_sample_rate;
+    out->stream.common.get_channels     = out_get_channels;
+    out->stream.common.get_format       = out_get_format;
+    out->stream.common.set_format       = out_set_format;
+    out->stream.common.standby          = out_standby;
+    out->stream.common.dump             = out_dump;
+    out->stream.common.set_parameters   = out_set_parameters;
+    out->stream.common.get_parameters   = out_get_parameters;
+    out->stream.common.add_audio_effect = out_add_audio_effect;
+    out->stream.common.remove_audio_effect  = out_remove_audio_effect;
+    out->stream.set_volume                  = out_set_volume;
+    out->stream.get_render_position         = out_get_render_position;
+    out->stream.get_presentation_position   = out_get_presentation_position;
+
+    out->dev = ladev;
+    out->standby = 1;
+    out->device      = devices;
+
+    /* FIXME: when we support multiple output devices, we will want to
+     * do the following:
+     * adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
+     * adev->devices |= out->device;
+     * select_output_device(adev);
+     * This is because out_set_parameters() with a route is not
+     * guaranteed to be called after an output stream is opened. */
+
+    config->format = out->stream.common.get_format(&out->stream.common);
+    config->channel_mask = out->stream.common.get_channels(&out->stream.common);
+    config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);
+
+    *stream_out = &out->stream;
+    ladev->active_output[output_type] = out;
+    ALOGW("opened out stream...%d, type %d",(int)out, output_type);
+    return 0;
+
+err_open:
+    free(out);
+    *stream_out = NULL;
+    return ret;
+}
+
+static void adev_close_output_stream(struct audio_hw_device *dev,
+                                     struct audio_stream_out *stream)
+{
+    struct imx_stream_out *out = (struct imx_stream_out *)stream;
+    struct imx_audio_device *ladev = (struct imx_audio_device *)dev;
+    int i;
+    ALOGW("adev_close_output_stream...%d",(int)out);
+
+    out_standby(&stream->common);
+
+    for (i = 0; i < OUTPUT_TOTAL; i++) {
+        if (ladev->active_output[i] == out) {
+            ladev->active_output[i] = NULL;
+            break;
+        }
+    }
+
+    if (out->buffer)
+        free(out->buffer);
+
+    for (i = 0; i < PCM_TOTAL; i++) {
+        if (out->resampler[i]) {
+            release_resampler(out->resampler[i]);
+            out->resampler[i] = NULL;
+        }
+    }
+
+    free(stream);
+}
+
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+    struct imx_audio_device *adev = (struct imx_audio_device *)dev;
+    struct str_parms *parms;
+    char *str;
+    char value[32];
+    int ret;
+    ALOGW("set parameters %s",kvpairs);
+    parms = str_parms_create_str(kvpairs);
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
+    if (ret >= 0) {
+        int tty_mode;
+
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
+            tty_mode = TTY_MODE_OFF;
+        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
+            tty_mode = TTY_MODE_VCO;
+        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
+            tty_mode = TTY_MODE_HCO;
+        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
+            tty_mode = TTY_MODE_FULL;
+        else
+            return -EINVAL;
+
+        pthread_mutex_lock(&adev->lock);
+        if (tty_mode != adev->tty_mode) {
+            adev->tty_mode = tty_mode;
+            if (adev->mode == AUDIO_MODE_IN_CALL)
+                select_output_device(adev);
+        }
+        pthread_mutex_unlock(&adev->lock);
+    }
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
+    if (ret >= 0) {
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+            adev->bluetooth_nrec = true;
+        else
+            adev->bluetooth_nrec = false;
+    }
+
+    ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
+    if (ret >= 0) {
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+            adev->low_power = false;
+        else
+            adev->low_power = true;
+    }
+
+    str_parms_destroy(parms);
+    return ret;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev,
+                                  const char *keys)
+{
+    return strdup("");
+}
+
+static int adev_init_check(const struct audio_hw_device *dev)
+{
+    return 0;
+}
+
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+    struct imx_audio_device *adev = (struct imx_audio_device *)dev;
+
+    adev->voice_volume = volume;
+
+    return 0;
+}
+
+static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+    return -ENOSYS;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, int mode)
+{
+    struct imx_audio_device *adev = (struct imx_audio_device *)dev;
+    pthread_mutex_lock(&adev->lock);
+    if (adev->mode != mode) {
+        adev->mode = mode;
+        select_mode(adev);
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+    struct imx_audio_device *adev = (struct imx_audio_device *)dev;
+
+    adev->mic_mute = state;
+
+    return 0;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+    struct imx_audio_device *adev = (struct imx_audio_device *)dev;
+
+    *state = adev->mic_mute;
+
+    return 0;
+}
+
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+                                         const struct audio_config *config)
+{
+    size_t size;
+    int channel_count = popcount(config->channel_mask);
+    if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0)
+        return 0;
+
+    return get_input_buffer_size(config->sample_rate, config->format, channel_count);
+}
+
+static int adev_open_input_stream(struct audio_hw_device *dev,
+                                  audio_io_handle_t handle,
+                                  audio_devices_t devices,
+                                  struct audio_config *config,
+                                  struct audio_stream_in **stream_in)
+{
+    struct imx_audio_device *ladev = (struct imx_audio_device *)dev;
+    struct imx_stream_in *in;
+    int ret;
+    int rate, channels;
+    int channel_count = popcount(config->channel_mask);
+
+    if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0)
+        return -EINVAL;
+
+    in = (struct imx_stream_in *)calloc(1, sizeof(struct imx_stream_in));
+    if (!in)
+        return -ENOMEM;
+
+    in->stream.common.get_sample_rate   = in_get_sample_rate;
+    in->stream.common.set_sample_rate   = in_set_sample_rate;
+    in->stream.common.get_buffer_size   = in_get_buffer_size;
+    in->stream.common.get_channels      = in_get_channels;
+    in->stream.common.get_format        = in_get_format;
+    in->stream.common.set_format        = in_set_format;
+    in->stream.common.standby           = in_standby;
+    in->stream.common.dump              = in_dump;
+    in->stream.common.set_parameters    = in_set_parameters;
+    in->stream.common.get_parameters    = in_get_parameters;
+    in->stream.common.add_audio_effect  = in_add_audio_effect;
+    in->stream.common.remove_audio_effect = in_remove_audio_effect;
+    in->stream.set_gain = in_set_gain;
+    in->stream.read = in_read;
+    in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+    in->requested_rate    = config->sample_rate;
+    in->requested_format  = PCM_FORMAT_S16_LE;
+    in->requested_channel = channel_count;
+    in->device  = devices & ~AUDIO_DEVICE_BIT_IN;
+
+    ALOGW("In channels %d, rate %d, devices 0x%x", channel_count, config->sample_rate, devices);
+    memcpy(&in->config, &pcm_config_mm_in, sizeof(pcm_config_mm_in));
+    //in->config.channels = channel_count;
+    //in->config.rate     = *sample_rate;
+    /*fix to 2 channel,  caused by the wm8958 driver*/
+
+    in->main_channels = config->channel_mask;
+
+    if (in->device & AUDIO_DEVICE_IN_USB_DEVICE) {
+        scan_available_device(ladev, true, true, false);
+    }
+
+    in->dev = ladev;
+    in->standby = 1;
+
+    *stream_in = &in->stream;
+
+    return 0;
+
+err:
+    if (in->resampler)
+        release_resampler(in->resampler);
+
+    free(in);
+    *stream_in = NULL;
+
+    return ret;
+}
+
+static void adev_close_input_stream(struct audio_hw_device *dev,
+                                   struct audio_stream_in *stream)
+{
+    struct imx_stream_in *in = (struct imx_stream_in *)stream;
+
+    in_standby(&stream->common);
+
+    if (in->read_buf)
+        free(in->read_buf);
+
+    if (in->resampler) {
+        release_resampler(in->resampler);
+    }
+    if (in->proc_buf_in)
+        free(in->proc_buf_in);
+    if (in->proc_buf_out)
+        free(in->proc_buf_out);
+    if (in->ref_buf)
+        free(in->ref_buf);
+
+    free(stream);
+    return;
+}
+
+static int adev_dump(const audio_hw_device_t *device, int fd)
+{
+    return 0;
+}
+
+static int adev_close(hw_device_t *device)
+{
+    struct imx_audio_device *adev = (struct imx_audio_device *)device;
+    int i;
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+        if(adev->mixer[i])
+            mixer_close(adev->mixer[i]);
+
+    free(device);
+    return 0;
+}
+
+static int adev_get_rate_for_device(struct imx_audio_device *adev, uint32_t devices, unsigned int flag)
+{
+     int i;
+     if (flag == PCM_OUT) {
+         for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+                   if (adev->card_list[i]->supported_out_devices & devices)
+		       return adev->card_list[i]->out_rate;
+         }
+     } else {
+         for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+                   if (adev->card_list[i]->supported_in_devices & devices)
+		       return adev->card_list[i]->in_rate;
+         }
+     }
+     return 0;
+}
+
+static int adev_get_channels_for_device(struct imx_audio_device *adev, uint32_t devices, unsigned int flag)
+{
+     int i;
+     if (flag == PCM_OUT) {
+         for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+                   if (adev->card_list[i]->supported_out_devices & devices)
+		       return adev->card_list[i]->out_channels;
+         }
+     } else {
+         for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+                   if (adev->card_list[i]->supported_in_devices & devices)
+		       return adev->card_list[i]->in_channels;
+         }
+     }
+     return 0;
+}
+
+static int adev_get_format_for_device(struct imx_audio_device *adev, uint32_t devices, unsigned int flag)
+{
+     int i;
+     if (flag == PCM_OUT) {
+         for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+                   if (adev->card_list[i]->supported_out_devices & devices)
+		       return adev->card_list[i]->out_format;
+         }
+     } else {
+         for (i = 0; i < MAX_AUDIO_CARD_NUM; i ++) {
+                   if (adev->card_list[i]->supported_in_devices & devices)
+		       return adev->card_list[i]->in_format;
+         }
+     }
+     return 0;
+}
+
+static int pcm_get_near_param_wrap(unsigned int card, unsigned int device,
+                     unsigned int flags, int type, int *data)
+{
+#ifdef BRILLO
+    return 0;
+#else
+    return pcm_get_near_param(card, device, flags, type, data);
+#endif
+}
+
+static int scan_available_device(struct imx_audio_device *adev, bool rescanusb, bool queryInput, bool queryOutput)
+{
+    int i,j,k;
+    int m,n;
+    bool found;
+    bool scanned;
+    struct control *imx_control;
+    int left_out_devices = SUPPORTED_DEVICE_OUT_MODULE;
+    int left_in_devices = SUPPORTED_DEVICE_IN_MODULE;
+    int rate, channels, format;
+    /* open the mixer for main sound card, main sound cara is like sgtl5000, wm8958, cs428888*/
+    /* note: some platform do not have main sound card, only have auxiliary card.*/
+    /* max num of supported card is 2 */
+    k = adev->audio_card_num;
+    for(i = 0; i < k; i++) {
+        left_out_devices &= ~adev->card_list[i]->supported_out_devices;
+        left_in_devices &= ~adev->card_list[i]->supported_in_devices;
+    }
+
+    for (i = 0; i < MAX_AUDIO_CARD_SCAN ; i ++) {
+        found = false;
+        imx_control = control_open(i);
+        if(!imx_control)
+            break;
+        ALOGW("card %d, id %s ,driver %s, name %s", i, control_card_info_get_id(imx_control),
+                                                      control_card_info_get_driver(imx_control),
+                                                      control_card_info_get_name(imx_control));
+        for(j = 0; j < SUPPORT_CARD_NUM; j++) {
+            if(strstr(control_card_info_get_driver(imx_control), audio_card_list[j]->driver_name) != NULL){
+                // check if the device have been scaned before
+                scanned = false;
+                n = k;
+                for (m = 0; m < k; m++) {
+                    if (!strcmp(audio_card_list[j]->driver_name, adev->card_list[m]->driver_name)) {
+                         scanned = true;
+                         found = true;
+                         if(!strcmp(adev->card_list[m]->driver_name, "USB-Audio")) {
+                             if(strcmp(control_card_info_get_name(imx_control), adev->usb_card_name) || rescanusb) {
+                                scanned = false;
+                                strcpy(adev->usb_card_name, control_card_info_get_name(imx_control));
+                                left_out_devices |= adev->card_list[m]->supported_out_devices;
+                                left_in_devices |= adev->card_list[m]->supported_in_devices;
+                                if(adev->mixer[m])
+                                   mixer_close(adev->mixer[m]);
+                                n = m;
+                                k --;
+                             }
+                         }
+                    }
+                }
+                if (scanned) break;
+                if(n >= MAX_AUDIO_CARD_NUM) {
+                    break;
+                }
+                adev->card_list[n]  = audio_card_list[j];
+                adev->card_list[n]->card = i;
+                if(!strcmp(adev->card_list[n]->driver_name, "USB-Audio")) {
+                    adev->mixer[n] = NULL;
+                } else {
+                    adev->mixer[n] = mixer_open(i);
+                    if (!adev->mixer[n]) {
+                         ALOGE("Unable to open the mixer, aborting.");
+                         return -EINVAL;
+                    }
+                }
+
+                if(queryOutput) {
+                    rate = 44100;
+                    if( pcm_get_near_param_wrap(i, 0, PCM_OUT, PCM_HW_PARAM_RATE, &rate) == 0)
+                            adev->card_list[n]->out_rate = rate;
+                    ALOGW("out rate %d",adev->card_list[n]->out_rate);
+
+                    if(adev->card_list[n]->out_rate > adev->mm_rate)
+                        adev->mm_rate = adev->card_list[n]->out_rate;
+
+                    channels = 2;
+                    if( pcm_get_near_param_wrap(i, 0, PCM_OUT, PCM_HW_PARAM_CHANNELS, &channels) == 0)
+                            adev->card_list[n]->out_channels = channels;
+                }
+
+                if(queryInput) {
+                    rate = 44100;
+                    if( pcm_get_near_param_wrap(i, 0, PCM_IN, PCM_HW_PARAM_RATE, &rate) == 0)
+                            adev->card_list[n]->in_rate = rate;
+
+                    channels = 1;
+                    if( pcm_get_near_param_wrap(i, 0, PCM_IN, PCM_HW_PARAM_CHANNELS, &channels) == 0)
+                            adev->card_list[n]->in_channels = channels;
+
+                    format = PCM_FORMAT_S16_LE;
+
+#ifdef BRILLO
+                    adev->card_list[n]->in_format = format;
+#else
+                    if( pcm_check_param_mask(i, 0, PCM_IN, PCM_HW_PARAM_FORMAT, format))
+                            adev->card_list[n]->in_format = format;
+                    else {
+                        format = PCM_FORMAT_S24_LE;
+                        if( pcm_check_param_mask(i, 0, PCM_IN, PCM_HW_PARAM_FORMAT, format))
+                            adev->card_list[n]->in_format = format;
+                    }
+#endif
+
+                    ALOGW("in rate %d, channels %d format %d",adev->card_list[n]->in_rate, adev->card_list[n]->in_channels, adev->card_list[n]->in_format);
+                }
+
+                left_out_devices &= ~audio_card_list[j]->supported_out_devices;
+                left_in_devices &= ~audio_card_list[j]->supported_in_devices;
+                k ++;
+                found = true;
+                break;
+            }
+        }
+
+        control_close(imx_control);
+        if(!found){
+            ALOGW("unrecognized card found.");
+        }
+    }
+    adev->audio_card_num = k;
+    /*must have one card*/
+    if(!adev->card_list[0]) {
+        ALOGE("no supported sound card found, aborting.");
+        return  -EINVAL;
+    }
+    /*second card maybe null*/
+    while (k < MAX_AUDIO_CARD_NUM) {
+        adev->card_list[k]  = audio_card_list[SUPPORT_CARD_NUM-1];
+        /*FIXME:This is workaround for some board which only have one card, whose supported device only is not full*/
+        adev->card_list[k]->supported_out_devices  = left_out_devices;
+        adev->card_list[k]->supported_in_devices  = left_in_devices;
+        k++;
+    }
+
+    return 0;
+}
+
+static int adev_open(const hw_module_t* module, const char* name,
+                     hw_device_t** device)
+{
+    struct imx_audio_device *adev;
+    int ret = 0;
+    int i,j,k;
+    bool found;
+
+    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
+        return -EINVAL;
+
+    adev = calloc(1, sizeof(struct imx_audio_device));
+    if (!adev)
+        return -ENOMEM;
+
+    adev->hw_device.common.tag      = HARDWARE_DEVICE_TAG;
+    adev->hw_device.common.version  = AUDIO_DEVICE_API_VERSION_2_0;
+    adev->hw_device.common.module   = (struct hw_module_t *) module;
+    adev->hw_device.common.close    = adev_close;
+
+    adev->hw_device.init_check              = adev_init_check;
+    adev->hw_device.set_voice_volume        = adev_set_voice_volume;
+    adev->hw_device.set_master_volume       = adev_set_master_volume;
+    adev->hw_device.set_mode                = adev_set_mode;
+    adev->hw_device.set_mic_mute            = adev_set_mic_mute;
+    adev->hw_device.get_mic_mute            = adev_get_mic_mute;
+    adev->hw_device.set_parameters          = adev_set_parameters;
+    adev->hw_device.get_parameters          = adev_get_parameters;
+    adev->hw_device.get_input_buffer_size   = adev_get_input_buffer_size;
+    adev->hw_device.open_output_stream      = adev_open_output_stream;
+    adev->hw_device.close_output_stream     = adev_close_output_stream;
+    adev->hw_device.open_input_stream       = adev_open_input_stream;
+    adev->hw_device.close_input_stream      = adev_close_input_stream;
+    adev->hw_device.dump                    = adev_dump;
+    adev->mm_rate                           = 44100;
+
+    ret = scan_available_device(adev, true, true, true);
+    if (ret != 0) {
+        free(adev);
+        return ret;
+    }
+
+    adev->default_rate                      = adev->mm_rate;
+    pcm_config_mm_out.rate                  = adev->mm_rate;
+    pcm_config_mm_in.rate                   = adev->mm_rate;
+    pcm_config_hdmi_multi.rate              = adev->mm_rate;
+    pcm_config_esai_multi.rate              = adev->mm_rate;
+
+    /* Set the default route before the PCM stream is opened */
+    pthread_mutex_lock(&adev->lock);
+    for(i = 0; i < MAX_AUDIO_CARD_NUM; i++)
+        set_route_by_array(adev->mixer[i], adev->card_list[i]->defaults, 1);
+    adev->mode    = AUDIO_MODE_NORMAL;
+    adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
+    adev->in_device  = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN;
+    select_output_device(adev);
+
+    adev->pcm_modem_dl  = NULL;
+    adev->pcm_modem_ul  = NULL;
+    adev->voice_volume  = 1.0f;
+    adev->tty_mode      = TTY_MODE_OFF;
+    adev->device_is_auto = is_device_auto();
+    adev->bluetooth_nrec = true;
+    adev->wb_amr = 0;
+    pthread_mutex_unlock(&adev->lock);
+
+    *device = &adev->hw_device.common;
+
+    return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+    .open = adev_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+    .common = {
+        .tag = HARDWARE_MODULE_TAG,
+        .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
+        .hal_api_version = HARDWARE_HAL_API_VERSION,
+        .id = AUDIO_HARDWARE_MODULE_ID,
+        .name = "Freescale i.MX Audio HW HAL",
+        .author = "The Android Open Source Project",
+        .methods = &hal_module_methods,
+    },
+};
+
+
diff --git a/soc/imx6ul/init.freescale.rc b/soc/imx6ul/init.freescale.rc
new file mode 100755
index 0000000..926b0ca
--- /dev/null
+++ b/soc/imx6ul/init.freescale.rc
@@ -0,0 +1,30 @@
+#
+# Copyright 2015 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.
+#
+
+import init.freescale.usb.rc
+
+on init
+   symlink /dev/block/mmcblk1p5 /dev/block/system_a
+   symlink /dev/block/mmcblk1p8 /dev/block/system_b
+
+on fs
+   mount_all /fstab.device
+   setprop ro.board.platform imx6ul
+
+on post-fs
+   restorecon /slotmeta
+   restorecon /slotmeta/slotmeta.data
+   restorecon /slotmeta/.slotmeta.data.bak
diff --git a/soc/imx6ul/init.freescale.usb.rc b/soc/imx6ul/init.freescale.usb.rc
new file mode 100644
index 0000000..c0021f3
--- /dev/null
+++ b/soc/imx6ul/init.freescale.usb.rc
@@ -0,0 +1,77 @@
+# Copyright 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.
+
+on init
+    write /sys/class/android_usb/android0/f_rndis/manufacturer Freescale
+    write /sys/class/android_usb/android0/f_rndis/vendorID 18D1
+    write /sys/class/android_usb/android0/f_rndis/wceis 1
+
+on boot
+    write /sys/class/android_usb/android0/iSerial $ro.serialno
+    write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer
+    write /sys/class/android_usb/android0/iProduct $ro.product.model
+
+on fs
+    mkdir /dev/usb-ffs 0770 shell shell
+    mkdir /dev/usb-ffs/adb 0770 shell shell
+    mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
+    write /sys/class/android_usb/android0/f_ffs/aliases adb
+
+# USB RNDIS configuration.
+on property:sys.usb.config=rndis
+    stop adbd
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/idVendor 18d1
+    write /sys/class/android_usb/android0/idProduct 4e23
+    write /sys/class/android_usb/android0/functions ${sys.usb.config}
+    write /sys/class/android_usb/android0/bDeviceClass 224
+    write /sys/class/android_usb/android0/enable 1
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp
+    stop adbd
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/idVendor 18d1
+    write /sys/class/android_usb/android0/idProduct 4e41
+    write /sys/class/android_usb/android0/functions ${sys.usb.config}
+    write /sys/class/android_usb/android0/enable 1
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp,adb
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/idVendor 18d1
+    write /sys/class/android_usb/android0/idProduct 4e42
+    write /sys/class/android_usb/android0/functions ${sys.usb.config}
+    write /sys/class/android_usb/android0/enable 1
+    start adbd
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp
+    stop adbd
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/idVendor 18d1
+    write /sys/class/android_usb/android0/idProduct 4e43
+    write /sys/class/android_usb/android0/functions ${sys.usb.config}
+    write /sys/class/android_usb/android0/enable 1
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp,adb
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/idVendor 18d1
+    write /sys/class/android_usb/android0/idProduct 4e44
+    write /sys/class/android_usb/android0/functions ${sys.usb.config}
+    write /sys/class/android_usb/android0/enable 1
+    start adbd
+    setprop sys.usb.state ${sys.usb.config}
+
diff --git a/soc/imx6ul/sepolicy/file_contexts b/soc/imx6ul/sepolicy/file_contexts
new file mode 100755
index 0000000..02ef075
--- /dev/null
+++ b/soc/imx6ul/sepolicy/file_contexts
@@ -0,0 +1,9 @@
+/dev/ttymxc[0-9]*               u:object_r:tty_device:s0
+/system/bin/fsl_sensor_fusion   u:object_r:sensors_exec:s0
+/system/bin/metagen             u:object_r:metagen_exec:s0
+/dev/FreescaleAccelerometer     u:object_r:sensors_device:s0
+/dev/FreescaleMagnetometer      u:object_r:sensors_device:s0
+/dev/FreescaleGyroscope         u:object_r:sensors_device:s0
+/slotmeta                       u:object_r:slotmeta:s0
+/slotmeta/slotmeta.data         u:object_r:slotmeta:s0
+/slotmeta/.slotmeta.data.bak    u:object_r:slotmeta:s0
diff --git a/soc/imx6ul/sepolicy/init.te b/soc/imx6ul/sepolicy/init.te
new file mode 100755
index 0000000..cccd7f3
--- /dev/null
+++ b/soc/imx6ul/sepolicy/init.te
@@ -0,0 +1 @@
+allow init functionfs:dir mounton;
diff --git a/soc/imx6ul/sepolicy/mediaserver.te b/soc/imx6ul/sepolicy/mediaserver.te
new file mode 100644
index 0000000..72acfbb
--- /dev/null
+++ b/soc/imx6ul/sepolicy/mediaserver.te
@@ -0,0 +1 @@
+allow mediaserver debug_prop:property_service set;
diff --git a/soc/imx6ul/sepolicy/metagen.te b/soc/imx6ul/sepolicy/metagen.te
new file mode 100755
index 0000000..cc50ac7
--- /dev/null
+++ b/soc/imx6ul/sepolicy/metagen.te
@@ -0,0 +1,4 @@
+type metagen, domain;
+type metagen_exec, exec_type, file_type;
+allow metagen slotmeta:dir { getattr search relabelfrom rw_dir_perms rmdir };
+allow metagen slotmeta:file rw_file_perms;
diff --git a/soc/imx6ul/sepolicy/sensors.te b/soc/imx6ul/sepolicy/sensors.te
new file mode 100755
index 0000000..81fdf8a
--- /dev/null
+++ b/soc/imx6ul/sepolicy/sensors.te
@@ -0,0 +1,27 @@
+# Integrated NXP sensor process.
+type sensors, domain;
+type sensors_exec, exec_type, file_type;
+
+# Started by init.
+init_daemon_domain(sensors)
+
+# Change own perms to (nobody,nobody).
+allow sensors self:capability { setuid setgid };
+# Chown /data/misc/sensors/debug/ to nobody.
+allow sensors self:capability chown;
+dontaudit sensors self:capability fsetid;
+
+# Access /data/misc/sensors/debug and /data/system/sensors/settings.
+allow sensors self:capability dac_read_search;
+dontaudit sensors self:capability dac_override;
+
+# Access sensor nodes (/dev/msm_dsps).
+allow sensors sensors_device:chr_file rw_file_perms;
+
+# Wake lock access.
+wakelock_use(sensors)
+
+allow sensors cgroup:dir { create add_name };
+allow sensors input_device:chr_file rw_file_perms;
+allow sensors input_device:dir r_dir_perms;
+allow sensors uhid_device:chr_file rw_file_perms;
diff --git a/soc/imx6ul/sepolicy/sensorservice.te b/soc/imx6ul/sepolicy/sensorservice.te
new file mode 100755
index 0000000..0dc30c5
--- /dev/null
+++ b/soc/imx6ul/sepolicy/sensorservice.te
@@ -0,0 +1,5 @@
+allow sensorservice sysfs:file write;
+allow sensorservice input_device:dir r_file_perms;
+allow sensorservice input_device:chr_file r_file_perms;
+allow sensorservice self:capability net_admin;
+allow sensorservice sysfs:dir r_file_perms;
diff --git a/soc/imx6ul/sepolicy/slotmeta.te b/soc/imx6ul/sepolicy/slotmeta.te
new file mode 100755
index 0000000..1ee9c13
--- /dev/null
+++ b/soc/imx6ul/sepolicy/slotmeta.te
@@ -0,0 +1 @@
+type slotmeta, file_type;
diff --git a/soc/imx6ul/sepolicy/update_engine.te b/soc/imx6ul/sepolicy/update_engine.te
new file mode 100755
index 0000000..6567e76
--- /dev/null
+++ b/soc/imx6ul/sepolicy/update_engine.te
@@ -0,0 +1,2 @@
+allow update_engine slotmeta:dir { getattr search relabelfrom rw_dir_perms rmdir };
+allow update_engine slotmeta:file rw_file_perms;
diff --git a/soc/imx6ul/sepolicy/wifi_setup.te b/soc/imx6ul/sepolicy/wifi_setup.te
new file mode 100644
index 0000000..a3d5c63
--- /dev/null
+++ b/soc/imx6ul/sepolicy/wifi_setup.te
@@ -0,0 +1 @@
+allow wifi_setup proc:file open;
diff --git a/soc/imx6ul/soc.mk b/soc/imx6ul/soc.mk
new file mode 100755
index 0000000..a99b258
--- /dev/null
+++ b/soc/imx6ul/soc.mk
@@ -0,0 +1,60 @@
+# Copyright 2015 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.
+#
+
+# TODO(leecam): Make this automatic.
+LOCAL_PATH := hardware/bsp/freescale/soc/imx6ul
+
+# Arm32 device.
+TARGET_ARCH := arm
+TARGET_ARCH_VARIANT := armv7-a
+TARGET_CPU_VARIANT := generic
+TARGET_CPU_ABI := armeabi-v7a
+TARGET_CPU_ABI2 := armeabi
+TARGET_KERNEL_ARCH := $(TARGET_ARCH)
+
+TARGET_NO_BOOTLOADER := false
+TARGET_NO_KERNEL := false
+
+TARGET_KERNEL_DTB := imx6ul-14x14-evk.dtb
+BOARD_KERNEL_BASE := 0x82800000
+
+BOARD_KERNEL_CMDLINE := console=ttymxc0,115200 init=/init androidboot.console=ttymxc0 consoleblank=0 androidboot.hardware=freescale
+
+TARGET_USERIMAGES_USE_EXT4 := true
+BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
+BOARD_FLASH_BLOCK_SIZE := 131072
+
+PRODUCT_COPY_FILES += \
+  $(LOCAL_PATH)/init.freescale.rc:root/init.freescale.rc \
+  $(LOCAL_PATH)/init.freescale.usb.rc:root/init.freescale.usb.rc \
+  $(LOCAL_PATH)/ueventd.freescale.rc:root/ueventd.freescale.rc \
+  system/core/rootdir/init.usb.rc:root/init.usb.rc \
+  system/core/rootdir/ueventd.rc:root/ueventd.rc \
+  $(LOCAL_PATH)/../../peripheral/bootctl/tool/misc.img:misc.img \
+
+BOARD_SEPOLICY_DIRS += $(LOCAL_PATH)/sepolicy
+
+# Set up the local kernel.
+TARGET_KERNEL_SRC := hardware/bsp/kernel/freescale/kernel_imx
+TARGET_KERNEL_DEFCONFIG := imx_v7_android_defconfig
+TARGET_KERNEL_CROSS_COMPILE_PREFIX := $(ANDROID_BUILD_TOP)/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-
+
+# Lights HAL package.
+DEVICE_PACKAGES += \
+  lights.$(soc_name)
+
+# WiFi HAL package.
+DEVICE_PACKAGES += \
+  wifi_driver.$(soc_name)
diff --git a/soc/imx6ul/ueventd.freescale.rc b/soc/imx6ul/ueventd.freescale.rc
new file mode 100755
index 0000000..967e40a
--- /dev/null
+++ b/soc/imx6ul/ueventd.freescale.rc
@@ -0,0 +1,87 @@
+#
+# Copyright 2015 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.
+#
+
+/dev/pmem_gpu             0660   system     graphics
+/dev/ttymxc5              0660   bluetooth  bluetooth
+/dev/snd/*                0660   system     audio
+/dev/ttyUSB*              0640   radio      radio
+/dev/ttyACM*              0640   radio      radio
+/dev/video*               0660   system     camera
+/dev/video0               0660   system     camera
+/dev/video1               0660   system     camera
+/dev/video2               0660   system     camera
+/dev/video16              0660   system     graphics
+/dev/mxc_ipu              0660   media      system
+/dev/mxc_vpu              0660   media      system
+/dev/uinput               0660   system     bluetooth
+/dev/ttymxc2              0660   system     gps
+/dev/ttya0                0660   system     gps
+/dev/ptya0                0660   system     gps
+/dev/ttya1                0660   system     gps
+/dev/ptya1                0660   system     gps
+/dev/ion                  0666   media      system
+/dev/galcore              0666   system     graphics
+/dev/graphics/fb0         0660   system     graphics
+/dev/watchdog             0660   system     root
+/dev/ir                   0660   system     system
+/dev/mma8x5x              0640   compass     system
+/dev/caam_kb              0660   system     system
+
+#sensors sysfs
+/sys/class/misc/Freescale*          name        0660  root   input
+/sys/class/misc/Freescale*          max         0660  root   input
+/sys/class/misc/Freescale*          min         0660  root   input
+/sys/class/misc/Freescale*          poll        0660  root   input
+/sys/class/misc/Freescale*          enable      0660  root   input
+/sys/class/misc/Freescale*          position    0660  root   input
+/sys/class/misc/Freescale*          poll_delay  0660  root   input
+/sys/class/misc/Freescale*          fifo        0660  root   input
+/dev/FreescaleAccelerometer                     0660  system input
+/dev/FreescaleMagnetometer                      0660  system input
+/dev/FreescaleGyroscope                         0660  system input
+
+
+# sysfs properties
+/sys/devices/virtual/input/input*   name        0660  root   input
+/sys/devices/virtual/input/input*   max         0660  root   input
+/sys/devices/virtual/input/input*   min         0660  root   input
+/sys/devices/virtual/input/input*   poll        0660  root   input
+/sys/devices/virtual/input/input*   enable      0660  root   input
+/sys/devices/virtual/input/input*   position    0660  root   input
+/sys/devices/virtual/input/input*   poll_delay  0660  root   input
+/sys/devices/virtual/input/input*   fifo        0660  root   input
+/sys/devices/virtual/thermal/thermal_zone* trip_point_0_temp 0660 system system
+/sys/devices/virtual/thermal/thermal_zone* trip_point_1_temp 0660 system system
+/sys/devices/virtual/thermal/thermal_zone* trip_point_2_temp 0660 system system
+
+/sys/devices/platform/mxsdhci.2/mmc_host/mmc0/mmc0:000* boot_bus_config 0660 root root
+/sys/devices/platform/mxsdhci.2/mmc_host/mmc0/mmc0:000* boot_config 0660 root root
+# for sabresd
+/sys/devices/soc0/soc.0/2100000.aips-bus/21a8000.i2c/i2c-2/2-0044 mode 0660 system system
+/sys/devices/soc0/soc.0/2100000.aips-bus/21a8000.i2c/i2c-2/2-0044 lux 0660 system system
+/sys/devices/soc0/soc.0/2100000.aips-bus/21a8000.i2c/i2c-2/2-0044 int_lt_lux 0660 system system
+/sys/devices/soc0/soc.0/2100000.aips-bus/21a8000.i2c/i2c-2/2-0044 int_ht_lux 0660 system system
+# for sabreauto
+/sys/devices/soc0/soc.1/2100000.aips-bus/21a8000.i2c/i2c-1/1-0044 mode 0660 system system
+/sys/devices/soc0/soc.1/2100000.aips-bus/21a8000.i2c/i2c-1/1-0044 lux 0660 system system
+/sys/devices/soc0/soc.1/2100000.aips-bus/21a8000.i2c/i2c-1/1-0044 int_lt_lux 0660 system system
+/sys/devices/soc0/soc.1/2100000.aips-bus/21a8000.i2c/i2c-1/1-0044 int_ht_lux 0660 system system
+/sys/devices/platform/mxc_sdc_fb.0/graphics/fb* fsl_disp_property 0660 system graphics
+/sys/devices/platform/mxc_sdc_fb.1/graphics/fb* fsl_disp_property 0660 system graphics
+/sys/devices/platform/mxc_sdc_fb.0/graphics/fb* mode 0660 system graphics
+/sys/devices/platform/mxc_sdc_fb.1/graphics/fb* mode 0660 system graphics
+/sys/devices/platform/mxc_dvfs_core.0 enable 0660 system system
+/sys/devices/platform/busfreq.0 enable 0660 system system