diff options
author | Yogesh Siraswar | 2018-01-17 15:29:58 -0600 |
---|---|---|
committer | Yogesh Siraswar | 2018-01-17 15:29:58 -0600 |
commit | 6502befe40ac5922193dde753c8fcc62cd2ae9ad (patch) | |
tree | 21c14ab66e7a784fc1f6ef2e29c3af9c32d5e65f | |
download | device-ti-am43xevm-d-marshmallow-mr3-release.tar.gz device-ti-am43xevm-d-marshmallow-mr3-release.tar.xz device-ti-am43xevm-d-marshmallow-mr3-release.zip |
am43xevm device filed-marshmallow-mr3-release
These file were verified on AM438x EVM:
1) Android 6AM1.3 release
2) 4.4 kernel
Signed-off-by: Yogesh Siraswar <yogeshs@ti.com>
57 files changed, 5704 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..270ceb1 --- /dev/null +++ b/Android.mk | |||
@@ -0,0 +1,36 @@ | |||
1 | # | ||
2 | # Copyright (C) 2011 The Android Open-Source Project | ||
3 | # | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | # you may not use this file except in compliance with the License. | ||
6 | # You may obtain a copy of the License at | ||
7 | # | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | # | ||
10 | # Unless required by applicable law or agreed to in writing, software | ||
11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | # See the License for the specific language governing permissions and | ||
14 | # limitations under the License. | ||
15 | # | ||
16 | |||
17 | # WARNING: Everything listed here will be built on ALL platforms, | ||
18 | # including x86, the emulator, and the SDK. Modules must be uniquely | ||
19 | # named (liblights.panda), and must build everywhere, or limit themselves | ||
20 | # to only building on ARM if they include assembly. Individual makefiles | ||
21 | # are responsible for having their own logic, for fine-grained control. | ||
22 | |||
23 | LOCAL_PATH := $(call my-dir) | ||
24 | |||
25 | # protect makefile from other boards | ||
26 | # if some modules are built directly from this directory (not subdirectories), | ||
27 | # their rules should be written here. | ||
28 | ifeq ($(TARGET_DEVICE),am43xevm) | ||
29 | include $(CLEAR_VARS) | ||
30 | |||
31 | # if some modules are built directly from this directory (not subdirectories), | ||
32 | # their rules should be written here. | ||
33 | |||
34 | include $(call all-makefiles-under,$(LOCAL_PATH)) | ||
35 | |||
36 | endif | ||
diff --git a/AndroidProducts.mk b/AndroidProducts.mk new file mode 100644 index 0000000..18e7b5d --- /dev/null +++ b/AndroidProducts.mk | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # Copyright (C) 2011 The Android Open-Source Project | ||
3 | # | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | # you may not use this file except in compliance with the License. | ||
6 | # You may obtain a copy of the License at | ||
7 | # | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | # | ||
10 | # Unless required by applicable law or agreed to in writing, software | ||
11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | # See the License for the specific language governing permissions and | ||
14 | # limitations under the License. | ||
15 | # | ||
16 | |||
17 | PRODUCT_MAKEFILES := $(LOCAL_DIR)/full_am43xevm.mk | ||
diff --git a/BoardConfig.mk b/BoardConfig.mk new file mode 100644 index 0000000..cbfe61b --- /dev/null +++ b/BoardConfig.mk | |||
@@ -0,0 +1,86 @@ | |||
1 | # | ||
2 | # Copyright (C) 2011 The Android Open-Source Project | ||
3 | # | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | # you may not use this file except in compliance with the License. | ||
6 | # You may obtain a copy of the License at | ||
7 | # | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | # | ||
10 | # Unless required by applicable law or agreed to in writing, software | ||
11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | # See the License for the specific language governing permissions and | ||
14 | # limitations under the License. | ||
15 | # | ||
16 | |||
17 | # These two variables are set first, so they can be overridden | ||
18 | # by BoardConfigVendor.mk | ||
19 | BOARD_USES_GENERIC_AUDIO := true | ||
20 | #USE_CAMERA_STUB := true | ||
21 | OMAP_ENHANCEMENT := true | ||
22 | |||
23 | ifeq ($(OMAP_ENHANCEMENT),true) | ||
24 | COMMON_GLOBAL_CFLAGS += -DOMAP_ENHANCEMENT | ||
25 | endif | ||
26 | |||
27 | TARGET_CPU_ABI := armeabi-v7a | ||
28 | TARGET_CPU_ABI2 := armeabi | ||
29 | TARGET_CPU_SMP := true | ||
30 | TARGET_ARCH := arm | ||
31 | TARGET_ARCH_VARIANT := armv7-a-neon | ||
32 | TARGET_CPU_VARIANT := cortex-a9 | ||
33 | |||
34 | ENABLE_CPUSETS := true | ||
35 | |||
36 | BOARD_HAVE_BLUETOOTH := false | ||
37 | BOARD_HAVE_BLUETOOTH_TI := false | ||
38 | BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/ti/am43xevm/bluetooth | ||
39 | TARGET_NO_BOOTLOADER := true | ||
40 | |||
41 | BOARD_KERNEL_BASE := 0x80008000 | ||
42 | #BOARD_KERNEL_CMDLINE := console=ttyO2,115200n8 mem=1024M androidboot.console=ttyO2 androidboot.hardware=am43xevmboard vram=20M omapfb.vram=0:16M | ||
43 | BOARD_MKBOOTIMG_ARGS := --ramdisk_offset 0x03000000 | ||
44 | |||
45 | TARGET_NO_RADIOIMAGE := true | ||
46 | TARGET_BOARD_PLATFORM := omap3 | ||
47 | TARGET_BOOTLOADER_BOARD_NAME := am43xevm | ||
48 | |||
49 | BOARD_EGL_CFG := device/ti/am43xevm/egl.cfg | ||
50 | |||
51 | USE_OPENGL_RENDERER := true | ||
52 | |||
53 | TARGET_USERIMAGES_USE_EXT4 := true | ||
54 | BOARD_SYSTEMIMAGE_PARTITION_SIZE := 805306368 | ||
55 | BOARD_USERDATAIMAGE_PARTITION_SIZE := 2147483648 | ||
56 | BOARD_CACHEIMAGE_PARTITION_SIZE := 268435456 | ||
57 | BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4 | ||
58 | BOARD_FLASH_BLOCK_SIZE := 4096 | ||
59 | |||
60 | TARGET_RECOVERY_FSTAB = device/ti/am43xevm/fstab.am43xevmboard | ||
61 | TARGET_RECOVERY_PIXEL_FORMAT := "RGB565" | ||
62 | TARGET_RELEASETOOLS_EXTENSIONS := device/ti/am43xevm | ||
63 | |||
64 | # Connectivity - Wi-Fi | ||
65 | USES_TI_MAC80211 := false | ||
66 | ifeq ($(USES_TI_MAC80211),true) | ||
67 | BOARD_WPA_SUPPLICANT_DRIVER := NL80211 | ||
68 | WPA_SUPPLICANT_VERSION := VER_0_8_X | ||
69 | BOARD_HOSTAPD_DRIVER := NL80211 | ||
70 | BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_wl12xx | ||
71 | BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_wl12xx | ||
72 | BOARD_WLAN_DEVICE := wl12xx_mac80211 | ||
73 | BOARD_SOFTAP_DEVICE := wl12xx_mac80211 | ||
74 | COMMON_GLOBAL_CFLAGS += -DUSES_TI_MAC80211 | ||
75 | COMMON_GLOBAL_CFLAGS += -DANDROID_LIB_STUB | ||
76 | endif | ||
77 | |||
78 | BOARD_SEPOLICY_DIRS := device/ti/am43xevm/sepolicy | ||
79 | |||
80 | # lidbrm driver | ||
81 | BOARD_GPU_DRIVERS := omapdrm | ||
82 | |||
83 | # DispSync vsync offsets in nanoseconds | ||
84 | VSYNC_EVENT_PHASE_OFFSET_NS := 7500000 | ||
85 | SF_VSYNC_EVENT_PHASE_OFFSET_NS := 5000000 | ||
86 | |||
diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..07f668f --- /dev/null +++ b/CleanSpec.mk | |||
@@ -0,0 +1,51 @@ | |||
1 | # Copyright (C) 2007 The Android Open Source Project | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | # | ||
15 | |||
16 | # If you don't need to do a full clean build but would like to touch | ||
17 | # a file or delete some intermediate files, add a clean step to the end | ||
18 | # of the list. These steps will only be run once, if they haven't been | ||
19 | # run before. | ||
20 | # | ||
21 | # E.g.: | ||
22 | # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) | ||
23 | # $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) | ||
24 | # | ||
25 | # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with | ||
26 | # files that are missing or have been moved. | ||
27 | # | ||
28 | # Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. | ||
29 | # Use $(OUT_DIR) to refer to the "out" directory. | ||
30 | # | ||
31 | # If you need to re-do something that's already mentioned, just copy | ||
32 | # the command and add it to the bottom of the list. E.g., if a change | ||
33 | # that you made last week required touching a file and a change you | ||
34 | # made today requires touching the same file, just copy the old | ||
35 | # touch step and add it to the end of the list. | ||
36 | # | ||
37 | # ************************************************ | ||
38 | # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST | ||
39 | # ************************************************ | ||
40 | |||
41 | # For example: | ||
42 | #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) | ||
43 | #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) | ||
44 | #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) | ||
45 | #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) | ||
46 | |||
47 | # ************************************************ | ||
48 | # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST | ||
49 | # ************************************************ | ||
50 | $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/build.prop) | ||
51 | $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/build.prop) \ No newline at end of file | ||
diff --git a/audio/Android.mk b/audio/Android.mk new file mode 100644 index 0000000..4b34495 --- /dev/null +++ b/audio/Android.mk | |||
@@ -0,0 +1,22 @@ | |||
1 | # Copyright (C) 2015 Texas Instruments | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | LOCAL_PATH := $(call my-dir) | ||
16 | |||
17 | |||
18 | ifeq ($(APPE_AUDIO),true) | ||
19 | include $(LOCAL_PATH)/hdmi/Android.mk | ||
20 | else | ||
21 | include $(call all-makefiles-under,$(LOCAL_PATH)) | ||
22 | endif | ||
diff --git a/audio/audio_policy.conf b/audio/audio_policy.conf new file mode 100644 index 0000000..f7a6f02 --- /dev/null +++ b/audio/audio_policy.conf | |||
@@ -0,0 +1,102 @@ | |||
1 | # Global configuration section: lists input and output devices always present on the device | ||
2 | # as well as the output device selected by default. | ||
3 | # Devices are designated by a string that corresponds to the enum in audio.h | ||
4 | |||
5 | global_configuration { | ||
6 | attached_output_devices AUDIO_DEVICE_OUT_SPEAKER | ||
7 | default_output_device AUDIO_DEVICE_OUT_SPEAKER | ||
8 | attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_LINE | ||
9 | } | ||
10 | |||
11 | # audio hardware module section: contains descriptors for all audio hw modules present on the | ||
12 | # device. Each hw module node is named after the corresponding hw module library base name. | ||
13 | # For instance, "primary" corresponds to audio.primary.<device>.so. | ||
14 | # The "primary" module is mandatory and must include at least one output with | ||
15 | # AUDIO_OUTPUT_FLAG_PRIMARY flag. | ||
16 | # Each module descriptor contains one or more output profile descriptors and zero or more | ||
17 | # input profile descriptors. Each profile lists all the parameters supported by a given output | ||
18 | # or input stream category. | ||
19 | # The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding | ||
20 | # to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n". | ||
21 | |||
22 | audio_hw_modules { | ||
23 | primary { | ||
24 | outputs { | ||
25 | primary { | ||
26 | sampling_rates 44100 | ||
27 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
28 | formats AUDIO_FORMAT_PCM_16_BIT | ||
29 | devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE | ||
30 | flags AUDIO_OUTPUT_FLAG_PRIMARY | ||
31 | } | ||
32 | } | ||
33 | inputs { | ||
34 | primary { | ||
35 | sampling_rates 8000|11025|16000|22050|32000|44100|48000 | ||
36 | channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO | ||
37 | formats AUDIO_FORMAT_PCM_16_BIT | ||
38 | devices AUDIO_DEVICE_IN_BUILTIN_MIC | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | hdmi { | ||
43 | outputs { | ||
44 | stereo { | ||
45 | sampling_rates 44100|48000 | ||
46 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
47 | formats AUDIO_FORMAT_PCM_16_BIT | ||
48 | devices AUDIO_DEVICE_OUT_AUX_DIGITAL | ||
49 | } | ||
50 | multichannel { | ||
51 | sampling_rates 44100|48000 | ||
52 | channel_masks dynamic | ||
53 | formats AUDIO_FORMAT_PCM_16_BIT | ||
54 | devices AUDIO_DEVICE_OUT_AUX_DIGITAL | ||
55 | flags AUDIO_OUTPUT_FLAG_DIRECT | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | a2dp { | ||
60 | outputs { | ||
61 | a2dp { | ||
62 | sampling_rates 44100 | ||
63 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
64 | formats AUDIO_FORMAT_PCM_16_BIT | ||
65 | devices AUDIO_DEVICE_OUT_ALL_A2DP | ||
66 | } | ||
67 | } | ||
68 | inputs { | ||
69 | a2dp { | ||
70 | sampling_rates 44100|48000 | ||
71 | channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO | ||
72 | formats AUDIO_FORMAT_PCM_16_BIT | ||
73 | devices AUDIO_DEVICE_IN_BLUETOOTH_A2DP | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | r_submix { | ||
78 | outputs { | ||
79 | r_submix { | ||
80 | sampling_rates 44100|48000 | ||
81 | channel_masks AUDIO_CHANNEL_OUT_STEREO | ||
82 | formats AUDIO_FORMAT_PCM_16_BIT | ||
83 | devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX | ||
84 | } | ||
85 | multichannel { | ||
86 | sampling_rates 44100|48000 | ||
87 | channel_masks AUDIO_CHANNEL_OUT_5POINT1 | ||
88 | formats AUDIO_FORMAT_PCM_16_BIT | ||
89 | devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX | ||
90 | flags AUDIO_OUTPUT_FLAG_DIRECT | ||
91 | } | ||
92 | } | ||
93 | inputs { | ||
94 | r_submix { | ||
95 | sampling_rates 44100|48000 | ||
96 | channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO|AUDIO_CHANNEL_IN_5POINT1EMUL | ||
97 | formats AUDIO_FORMAT_PCM_16_BIT | ||
98 | devices AUDIO_DEVICE_IN_REMOTE_SUBMIX | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | } | ||
diff --git a/audio/hdmi/Android.mk b/audio/hdmi/Android.mk new file mode 100644 index 0000000..976f84b --- /dev/null +++ b/audio/hdmi/Android.mk | |||
@@ -0,0 +1,40 @@ | |||
1 | # Copyright (C) 2015 Texas Instruments | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | LOCAL_PATH := $(call my-dir) | ||
16 | |||
17 | ifeq ($(TARGET_BOARD_PLATFORM), $(filter $(TARGET_BOARD_PLATFORM), am57x)) | ||
18 | |||
19 | include $(CLEAR_VARS) | ||
20 | |||
21 | LOCAL_MODULE := audio.hdmi.$(TARGET_BOARD_PLATFORM) | ||
22 | |||
23 | LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw | ||
24 | LOCAL_SRC_FILES := hdmi_audio_hw.c \ | ||
25 | hdmi_audio_utils.c | ||
26 | |||
27 | LOCAL_C_INCLUDES += \ | ||
28 | external/tinyalsa/include \ | ||
29 | external/libdrm \ | ||
30 | external/libdrm/include/drm \ | ||
31 | system/media/audio_utils/include \ | ||
32 | system/media/audio_effects/include \ | ||
33 | frameworks/native/include/media/openmax | ||
34 | |||
35 | LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libdl libdrm | ||
36 | LOCAL_MODULE_TAGS := optional | ||
37 | |||
38 | include $(BUILD_SHARED_LIBRARY) | ||
39 | |||
40 | endif | ||
diff --git a/audio/hdmi/hdmi_audio_hal.h b/audio/hdmi/hdmi_audio_hal.h new file mode 100644 index 0000000..545e50e --- /dev/null +++ b/audio/hdmi/hdmi_audio_hal.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */ | ||
2 | /* | ||
3 | * Copyright (C) 2012 Texas Instruments | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | #ifndef TI_HDMI_AUDIO_HAL | ||
18 | #define TI_HDMI_AUDIO_HAL | ||
19 | |||
20 | typedef struct _hdmi_audio_caps { | ||
21 | int has_audio; | ||
22 | int speaker_alloc; | ||
23 | } hdmi_audio_caps_t; | ||
24 | |||
25 | /* Speaker allocation bits */ | ||
26 | #define CEA_SPKR_FLFR (1 << 0) | ||
27 | #define CEA_SPKR_LFE (1 << 1) | ||
28 | #define CEA_SPKR_FC (1 << 2) | ||
29 | #define CEA_SPKR_RLRR (1 << 3) | ||
30 | #define CEA_SPKR_RC (1 << 4) | ||
31 | #define CEA_SPKR_FLCFRC (1 << 5) | ||
32 | #define CEA_SPKR_RLCRRC (1 << 6) | ||
33 | |||
34 | /* Defined in file hdmi_audio_utils.c */ | ||
35 | int hdmi_query_audio_caps(hdmi_audio_caps_t *caps); | ||
36 | |||
37 | #endif /* TI_HDMI_AUDIO_HAL */ | ||
diff --git a/audio/hdmi/hdmi_audio_hw.c b/audio/hdmi/hdmi_audio_hw.c new file mode 100644 index 0000000..1f5c520 --- /dev/null +++ b/audio/hdmi/hdmi_audio_hw.c | |||
@@ -0,0 +1,824 @@ | |||
1 | /* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */ | ||
2 | /* | ||
3 | * Copyright (C) 2012 Texas Instruments | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | |||
18 | #define LOG_TAG "hdmi_audio_hw" | ||
19 | /* #define LOG_NDEBUG 0 */ | ||
20 | /* #define LOG_TRACE_FUNCTION */ | ||
21 | |||
22 | #ifndef LOG_TRACE_FUNCTION | ||
23 | #define TRACE() ((void)0) | ||
24 | #define TRACEM(fmt, ...) ((void)0) | ||
25 | #else | ||
26 | #define tfmt(x) x | ||
27 | #define TRACE() (ALOGV("%s() %s:%d", __func__, __FILE__, __LINE__)) | ||
28 | #define TRACEM(fmt, ...) (ALOGV("%s() " tfmt(fmt) " %s:%d", __func__, ##__VA_ARGS__, __FILE__, __LINE__)) | ||
29 | #endif | ||
30 | |||
31 | #include <errno.h> | ||
32 | #include <pthread.h> | ||
33 | #include <stdint.h> | ||
34 | #include <sys/time.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <stdio.h> | ||
37 | |||
38 | #include <cutils/log.h> | ||
39 | #include <cutils/str_parms.h> | ||
40 | #include <cutils/properties.h> | ||
41 | |||
42 | #include <hardware/hardware.h> | ||
43 | #include <system/audio.h> | ||
44 | #include <hardware/audio.h> | ||
45 | |||
46 | #include <tinyalsa/asoundlib.h> | ||
47 | |||
48 | #include <OMX_Audio.h> | ||
49 | |||
50 | #include "hdmi_audio_hal.h" | ||
51 | |||
52 | #define UNUSED(x) (void)(x) | ||
53 | |||
54 | /* XXX TODO: Dynamically detect the HDMI card | ||
55 | * E.g. if a USB device is plugged in at boot time, | ||
56 | * it sometimes takes the card #1 slot and puts us | ||
57 | * on card #2. | ||
58 | */ | ||
59 | #define HDMI_PCM_CARD 1 | ||
60 | #define HDMI_PCM_DEV 0 | ||
61 | #define HDMI_SAMPLING_RATE 44100 | ||
62 | #define HDMI_PERIOD_SIZE 1920 | ||
63 | #define HDMI_PERIOD_COUNT 4 | ||
64 | #define HDMI_MAX_CHANNELS 8 | ||
65 | |||
66 | typedef struct _hdmi_device { | ||
67 | audio_hw_device_t device; | ||
68 | int map[HDMI_MAX_CHANNELS]; | ||
69 | bool CEAMap; | ||
70 | } hdmi_device_t; | ||
71 | |||
72 | int cea_channel_map[HDMI_MAX_CHANNELS] = {OMX_AUDIO_ChannelLF,OMX_AUDIO_ChannelRF,OMX_AUDIO_ChannelLFE, | ||
73 | OMX_AUDIO_ChannelCF,OMX_AUDIO_ChannelLS,OMX_AUDIO_ChannelRS, | ||
74 | OMX_AUDIO_ChannelLR,OMX_AUDIO_ChannelRR}; /*Using OMX_AUDIO_CHANNELTYPE mapping*/ | ||
75 | |||
76 | typedef struct _hdmi_out { | ||
77 | audio_stream_out_t stream_out; | ||
78 | hdmi_device_t *dev; | ||
79 | struct pcm_config config; | ||
80 | struct pcm *pcm; | ||
81 | audio_config_t android_config; | ||
82 | int up; | ||
83 | void *buffcpy; | ||
84 | } hdmi_out_t; | ||
85 | |||
86 | #define S16_SIZE sizeof(int16_t) | ||
87 | |||
88 | |||
89 | /***************************************************************** | ||
90 | * UTILITY FUNCTIONS | ||
91 | ***************************************************************** | ||
92 | */ | ||
93 | |||
94 | /***************************************************************** | ||
95 | * AUDIO STREAM OUT (hdmi_out_*) DEFINITION | ||
96 | ***************************************************************** | ||
97 | */ | ||
98 | |||
99 | uint32_t hdmi_out_get_sample_rate(const struct audio_stream *stream) | ||
100 | { | ||
101 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
102 | struct pcm_config *config = &out->config; | ||
103 | TRACEM("stream=%p returning %d", stream, config->rate); | ||
104 | return config->rate; | ||
105 | } | ||
106 | |||
107 | /* DEPRECATED API */ | ||
108 | int hdmi_out_set_sample_rate(struct audio_stream *stream, uint32_t rate) | ||
109 | { | ||
110 | TRACE(); | ||
111 | UNUSED(stream); | ||
112 | UNUSED(rate); | ||
113 | |||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | /* Returns bytes for ONE PERIOD */ | ||
118 | size_t hdmi_out_get_buffer_size(const struct audio_stream *stream) | ||
119 | { | ||
120 | const struct audio_stream_out *s = (const struct audio_stream_out *)stream; | ||
121 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
122 | struct pcm_config *config = &out->config; | ||
123 | size_t ans; | ||
124 | |||
125 | ans = audio_stream_out_frame_size(s) * config->period_size; | ||
126 | |||
127 | TRACEM("stream=%p returning %u", stream, ans); | ||
128 | |||
129 | return ans; | ||
130 | } | ||
131 | |||
132 | audio_channel_mask_t hdmi_out_get_channels(const struct audio_stream *stream) | ||
133 | { | ||
134 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
135 | TRACEM("stream=%p returning %x", stream, out->android_config.channel_mask); | ||
136 | return out->android_config.channel_mask; | ||
137 | } | ||
138 | |||
139 | audio_format_t hdmi_out_get_format(const struct audio_stream *stream) | ||
140 | { | ||
141 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
142 | TRACEM("stream=%p returning %x", stream, out->android_config.format); | ||
143 | return out->android_config.format; | ||
144 | } | ||
145 | |||
146 | /* DEPRECATED API */ | ||
147 | int hdmi_out_set_format(struct audio_stream *stream, audio_format_t format) | ||
148 | { | ||
149 | TRACE(); | ||
150 | UNUSED(stream); | ||
151 | UNUSED(format); | ||
152 | |||
153 | return -EINVAL; | ||
154 | } | ||
155 | |||
156 | int hdmi_out_standby(struct audio_stream *stream) | ||
157 | { | ||
158 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
159 | |||
160 | TRACEM("stream=%p", stream); | ||
161 | |||
162 | if (out->up && out->pcm) { | ||
163 | out->up = 0; | ||
164 | pcm_close(out->pcm); | ||
165 | out->pcm = 0; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | int hdmi_out_dump(const struct audio_stream *stream, int fd) | ||
172 | { | ||
173 | TRACE(); | ||
174 | UNUSED(stream); | ||
175 | UNUSED(fd); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | audio_devices_t hdmi_out_get_device(const struct audio_stream *stream) | ||
181 | { | ||
182 | TRACEM("stream=%p", stream); | ||
183 | UNUSED(stream); | ||
184 | |||
185 | return AUDIO_DEVICE_OUT_AUX_DIGITAL; | ||
186 | } | ||
187 | |||
188 | /* DEPRECATED API */ | ||
189 | int hdmi_out_set_device(struct audio_stream *stream, audio_devices_t device) | ||
190 | { | ||
191 | TRACE(); | ||
192 | UNUSED(stream); | ||
193 | UNUSED(device); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | int hdmi_out_set_parameters(struct audio_stream *stream, const char *kv_pairs) | ||
199 | { | ||
200 | TRACEM("stream=%p kv_pairs='%s'", stream, kv_pairs); | ||
201 | UNUSED(stream); | ||
202 | UNUSED(kv_pairs); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | #define MASK_CEA_QUAD ( CEA_SPKR_FLFR | CEA_SPKR_RLRR ) | ||
208 | #define MASK_CEA_SURROUND ( CEA_SPKR_FLFR | CEA_SPKR_FC | CEA_SPKR_RC ) | ||
209 | #define MASK_CEA_5POINT1 ( CEA_SPKR_FLFR | CEA_SPKR_FC | CEA_SPKR_LFE | CEA_SPKR_RLRR ) | ||
210 | #define MASK_CEA_7POINT1 ( CEA_SPKR_FLFR | CEA_SPKR_FC | CEA_SPKR_LFE | CEA_SPKR_RLRR | CEA_SPKR_RLCRRC ) | ||
211 | #define SUPPORTS_ARR(spkalloc, profile) (((spkalloc) & (profile)) == (profile)) | ||
212 | |||
213 | char * hdmi_out_get_parameters(const struct audio_stream *stream, | ||
214 | const char *keys) | ||
215 | { | ||
216 | struct str_parms *query = str_parms_create_str(keys); | ||
217 | char *str; | ||
218 | char value[256]; | ||
219 | struct str_parms *reply = str_parms_create(); | ||
220 | int status; | ||
221 | hdmi_audio_caps_t caps; | ||
222 | |||
223 | TRACEM("stream=%p keys='%s'", stream, keys); | ||
224 | UNUSED(stream); | ||
225 | |||
226 | if (hdmi_query_audio_caps(&caps)) { | ||
227 | ALOGE("Unable to get the HDMI audio capabilities"); | ||
228 | str = calloc(1, 1); | ||
229 | goto end; | ||
230 | } | ||
231 | |||
232 | status = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, | ||
233 | value, sizeof(value)); | ||
234 | if (status >= 0) { | ||
235 | unsigned sa = caps.speaker_alloc; | ||
236 | bool first = true; | ||
237 | |||
238 | /* STEREO is intentionally skipped. This code is only | ||
239 | * executed for the 'DIRECT' interface, and we don't | ||
240 | * want stereo on a DIRECT thread. | ||
241 | */ | ||
242 | value[0] = '\0'; | ||
243 | if (SUPPORTS_ARR(sa, MASK_CEA_QUAD)) { | ||
244 | if (!first) { | ||
245 | strcat(value, "|"); | ||
246 | } | ||
247 | first = false; | ||
248 | strcat(value, "AUDIO_CHANNEL_OUT_QUAD"); | ||
249 | } | ||
250 | if (SUPPORTS_ARR(sa, MASK_CEA_SURROUND)) { | ||
251 | if (!first) { | ||
252 | strcat(value, "|"); | ||
253 | } | ||
254 | first = false; | ||
255 | strcat(value, "AUDIO_CHANNEL_OUT_SURROUND"); | ||
256 | } | ||
257 | if (SUPPORTS_ARR(sa, MASK_CEA_5POINT1)) { | ||
258 | if (!first) { | ||
259 | strcat(value, "|"); | ||
260 | } | ||
261 | first = false; | ||
262 | strcat(value, "AUDIO_CHANNEL_OUT_5POINT1"); | ||
263 | } | ||
264 | if (SUPPORTS_ARR(sa, MASK_CEA_7POINT1)) { | ||
265 | if (!first) { | ||
266 | strcat(value, "|"); | ||
267 | } | ||
268 | first = false; | ||
269 | strcat(value, "AUDIO_CHANNEL_OUT_7POINT1"); | ||
270 | } | ||
271 | str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value); | ||
272 | str = strdup(str_parms_to_str(reply)); | ||
273 | } else { | ||
274 | str = strdup(keys); | ||
275 | } | ||
276 | |||
277 | ALOGV("%s() reply: '%s'", __func__, str); | ||
278 | |||
279 | end: | ||
280 | str_parms_destroy(query); | ||
281 | str_parms_destroy(reply); | ||
282 | return str; | ||
283 | } | ||
284 | int hdmi_out_add_audio_effect(const struct audio_stream *stream, | ||
285 | effect_handle_t effect) | ||
286 | { | ||
287 | TRACE(); | ||
288 | UNUSED(stream); | ||
289 | UNUSED(effect); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | int hdmi_out_remove_audio_effect(const struct audio_stream *stream, | ||
294 | effect_handle_t effect) | ||
295 | { | ||
296 | TRACE(); | ||
297 | UNUSED(stream); | ||
298 | UNUSED(effect); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | /* returns milliseconds */ | ||
304 | uint32_t hdmi_out_get_latency(const struct audio_stream_out *stream) | ||
305 | { | ||
306 | uint32_t latency; | ||
307 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
308 | struct pcm_config *config = &out->config; | ||
309 | |||
310 | TRACEM("stream=%p", stream); | ||
311 | |||
312 | return (1000 * config->period_size * config->period_count) / config->rate; | ||
313 | } | ||
314 | |||
315 | int hdmi_out_set_volume(struct audio_stream_out *stream, float left, float right) | ||
316 | { | ||
317 | TRACE(); | ||
318 | UNUSED(stream); | ||
319 | UNUSED(left); | ||
320 | UNUSED(right); | ||
321 | |||
322 | return -ENOSYS; | ||
323 | } | ||
324 | |||
325 | static int hdmi_out_open_pcm(hdmi_out_t *out) | ||
326 | { | ||
327 | int card = HDMI_PCM_CARD; | ||
328 | int dev = HDMI_PCM_DEV; | ||
329 | int ret; | ||
330 | |||
331 | TRACEM("out=%p", out); | ||
332 | |||
333 | /* out->up must be 0 (down) */ | ||
334 | if (out->up) { | ||
335 | ALOGE("Trying to open a PCM that's already up. " | ||
336 | "This will probably deadlock... so aborting"); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | out->pcm = pcm_open(card, dev, PCM_OUT, &out->config); | ||
341 | |||
342 | if(out->pcm && pcm_is_ready(out->pcm)) { | ||
343 | out->up = 1; | ||
344 | ret = 0; | ||
345 | } else { | ||
346 | ALOGE("cannot open HDMI pcm card %d dev %d error: %s", | ||
347 | card, dev, pcm_get_error(out->pcm)); | ||
348 | pcm_close(out->pcm); | ||
349 | out->pcm = 0; | ||
350 | out->up = 0; | ||
351 | ret = 1; | ||
352 | } | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | void channel_remap(struct audio_stream_out *stream, const void *buffer, | ||
358 | size_t bytes) | ||
359 | { | ||
360 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
361 | hdmi_device_t *adev = out->dev; | ||
362 | int x, y, frames; | ||
363 | int16_t *buf = (int16_t *)buffer; | ||
364 | int16_t *tmp_buf = (int16_t *)out->buffcpy; | ||
365 | |||
366 | frames = bytes / audio_stream_out_frame_size(stream); | ||
367 | while (frames--){ | ||
368 | for(y = 0; y < (int)out->config.channels; y++){ | ||
369 | for(x = 0; x < (int)out->config.channels; x++){ | ||
370 | if (cea_channel_map[y] == adev->map[x]){ | ||
371 | tmp_buf[y] = buf[x]; | ||
372 | break; | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | tmp_buf += (int)out->config.channels; | ||
377 | buf += (int)out->config.channels; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | ssize_t hdmi_out_write(struct audio_stream_out *stream, const void* buffer, | ||
382 | size_t bytes) | ||
383 | { | ||
384 | hdmi_out_t *out = (hdmi_out_t*)stream; | ||
385 | hdmi_device_t *adev = out->dev; | ||
386 | ssize_t ret; | ||
387 | |||
388 | TRACEM("stream=%p buffer=%p bytes=%d", stream, buffer, bytes); | ||
389 | |||
390 | if (!out->up) { | ||
391 | if(hdmi_out_open_pcm(out)) { | ||
392 | ret = -ENOSYS; | ||
393 | goto exit; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | if (out->config.channels > 2 && !adev->CEAMap){ | ||
398 | channel_remap(stream, buffer, bytes); | ||
399 | ret = pcm_write(out->pcm, out->buffcpy, bytes); | ||
400 | } else { | ||
401 | ret = pcm_write(out->pcm, buffer, bytes); | ||
402 | } | ||
403 | exit: | ||
404 | if (ret != 0) { | ||
405 | ALOGE("Error writing to HDMI pcm: %s", | ||
406 | out->pcm ? pcm_get_error(out->pcm) : "failed to open PCM device"); | ||
407 | hdmi_out_standby((struct audio_stream*)stream); | ||
408 | unsigned int usecs = bytes * 1000000 / | ||
409 | audio_stream_out_frame_size(stream) / | ||
410 | hdmi_out_get_sample_rate((struct audio_stream*)stream); | ||
411 | if (usecs >= 1000000L) { | ||
412 | usecs = 999999L; | ||
413 | } | ||
414 | usleep(usecs); | ||
415 | } | ||
416 | |||
417 | return bytes; | ||
418 | } | ||
419 | |||
420 | |||
421 | int hdmi_out_get_render_position(const struct audio_stream_out *stream, | ||
422 | uint32_t *dsp_frames) | ||
423 | { | ||
424 | TRACE(); | ||
425 | UNUSED(stream); | ||
426 | UNUSED(dsp_frames); | ||
427 | |||
428 | return -EINVAL; | ||
429 | } | ||
430 | |||
431 | int hdmi_out_get_next_write_timestamp(const struct audio_stream_out *stream, | ||
432 | int64_t *timestamp) | ||
433 | { | ||
434 | TRACE(); | ||
435 | UNUSED(stream); | ||
436 | UNUSED(timestamp); | ||
437 | |||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | |||
442 | |||
443 | audio_stream_out_t hdmi_stream_out_descriptor = { | ||
444 | .common = { | ||
445 | .get_sample_rate = hdmi_out_get_sample_rate, | ||
446 | .set_sample_rate = hdmi_out_set_sample_rate, | ||
447 | .get_buffer_size = hdmi_out_get_buffer_size, | ||
448 | .get_channels = hdmi_out_get_channels, | ||
449 | .get_format = hdmi_out_get_format, | ||
450 | .set_format = hdmi_out_set_format, | ||
451 | .standby = hdmi_out_standby, | ||
452 | .dump = hdmi_out_dump, | ||
453 | .get_device = hdmi_out_get_device, | ||
454 | .set_device = hdmi_out_set_device, | ||
455 | .set_parameters = hdmi_out_set_parameters, | ||
456 | .get_parameters = hdmi_out_get_parameters, | ||
457 | .add_audio_effect = hdmi_out_add_audio_effect, | ||
458 | .remove_audio_effect = hdmi_out_remove_audio_effect, | ||
459 | }, | ||
460 | .get_latency = hdmi_out_get_latency, | ||
461 | .set_volume = hdmi_out_set_volume, | ||
462 | .write = hdmi_out_write, | ||
463 | .get_render_position = hdmi_out_get_render_position, | ||
464 | .get_next_write_timestamp = hdmi_out_get_next_write_timestamp, | ||
465 | }; | ||
466 | |||
467 | /***************************************************************** | ||
468 | * AUDIO DEVICE (hdmi_adev_*) DEFINITION | ||
469 | ***************************************************************** | ||
470 | */ | ||
471 | |||
472 | static int hdmi_adev_close(struct hw_device_t *device) | ||
473 | { | ||
474 | TRACE(); | ||
475 | UNUSED(device); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static uint32_t hdmi_adev_get_supported_devices(const audio_hw_device_t *dev) | ||
481 | { | ||
482 | TRACE(); | ||
483 | UNUSED(dev); | ||
484 | |||
485 | return AUDIO_DEVICE_OUT_AUX_DIGITAL; | ||
486 | } | ||
487 | |||
488 | static int hdmi_adev_init_check(const audio_hw_device_t *dev) | ||
489 | { | ||
490 | TRACE(); | ||
491 | UNUSED(dev); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static int hdmi_adev_set_voice_volume(audio_hw_device_t *dev, float volume) | ||
497 | { | ||
498 | TRACE(); | ||
499 | UNUSED(dev); | ||
500 | UNUSED(volume); | ||
501 | |||
502 | return -ENOSYS; | ||
503 | } | ||
504 | |||
505 | static int hdmi_adev_set_master_volume(audio_hw_device_t *dev, float volume) | ||
506 | { | ||
507 | TRACE(); | ||
508 | UNUSED(dev); | ||
509 | UNUSED(volume); | ||
510 | |||
511 | return -ENOSYS; | ||
512 | } | ||
513 | |||
514 | static int hdmi_adev_get_master_volume(audio_hw_device_t *dev, float *volume) | ||
515 | { | ||
516 | TRACE(); | ||
517 | UNUSED(dev); | ||
518 | UNUSED(volume); | ||
519 | |||
520 | return -ENOSYS; | ||
521 | } | ||
522 | |||
523 | static int hdmi_adev_set_mode(audio_hw_device_t *dev, audio_mode_t mode) | ||
524 | { | ||
525 | TRACE(); | ||
526 | UNUSED(dev); | ||
527 | UNUSED(mode); | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | static int hdmi_adev_set_mic_mute(audio_hw_device_t *dev, bool state) | ||
533 | { | ||
534 | TRACE(); | ||
535 | UNUSED(dev); | ||
536 | UNUSED(state); | ||
537 | |||
538 | return -ENOSYS; | ||
539 | } | ||
540 | |||
541 | static int hdmi_adev_get_mic_mute(const audio_hw_device_t *dev, bool *state) | ||
542 | { | ||
543 | TRACE(); | ||
544 | UNUSED(dev); | ||
545 | UNUSED(state); | ||
546 | |||
547 | return -ENOSYS; | ||
548 | } | ||
549 | |||
550 | static int hdmi_adev_set_parameters(audio_hw_device_t *dev, const char *kv_pairs) | ||
551 | { | ||
552 | TRACEM("dev=%p kv_pairss='%s'", dev, kv_pairs); | ||
553 | |||
554 | struct str_parms *params; | ||
555 | char *str; | ||
556 | char value[HDMI_MAX_CHANNELS]; | ||
557 | int ret, x, val, numMatch = 0; | ||
558 | hdmi_device_t *adev = (hdmi_device_t *)dev; | ||
559 | |||
560 | params = str_parms_create_str(kv_pairs); | ||
561 | //Handle maximum of 8 channels | ||
562 | ret = str_parms_get_str(params, "channel_map", value, HDMI_MAX_CHANNELS); | ||
563 | if (ret >= 0) { | ||
564 | val = strtol(value, NULL, 10); | ||
565 | for(x = 0; x < HDMI_MAX_CHANNELS; x++) { | ||
566 | adev->map[x] = (val & (0xF << x*4)) >> x*4; | ||
567 | if (adev->map[x] == cea_channel_map[x]) | ||
568 | numMatch += 1; | ||
569 | } | ||
570 | if (numMatch >= 5) | ||
571 | adev->CEAMap = true; | ||
572 | else | ||
573 | adev->CEAMap = false; | ||
574 | } | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static char* hdmi_adev_get_parameters(const audio_hw_device_t *dev, | ||
579 | const char *keys) | ||
580 | { | ||
581 | TRACEM("dev=%p keys='%s'", dev, keys); | ||
582 | UNUSED(dev); | ||
583 | UNUSED(keys); | ||
584 | |||
585 | return NULL; | ||
586 | } | ||
587 | |||
588 | static size_t hdmi_adev_get_input_buffer_size(const audio_hw_device_t *dev, | ||
589 | const struct audio_config *config) | ||
590 | { | ||
591 | TRACE(); | ||
592 | UNUSED(dev); | ||
593 | UNUSED(config); | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | #define DUMP_FLAG(flags, flag) { \ | ||
599 | if ((flags) & (flag)) { \ | ||
600 | ALOGV("set: " #flag); \ | ||
601 | } else { \ | ||
602 | ALOGV("unset: " #flag); \ | ||
603 | } \ | ||
604 | } | ||
605 | |||
606 | static int hdmi_adev_open_output_stream(audio_hw_device_t *dev, | ||
607 | audio_io_handle_t handle, | ||
608 | audio_devices_t devices, | ||
609 | audio_output_flags_t flags, | ||
610 | struct audio_config *config, | ||
611 | struct audio_stream_out **stream_out, | ||
612 | const char *address) | ||
613 | { | ||
614 | hdmi_out_t *out = 0; | ||
615 | struct pcm_config *pcm_config = 0; | ||
616 | struct audio_config *a_config = 0; | ||
617 | |||
618 | TRACE(); | ||
619 | UNUSED(handle); | ||
620 | UNUSED(devices); | ||
621 | UNUSED(flags); | ||
622 | UNUSED(address); | ||
623 | |||
624 | out = calloc(1, sizeof(hdmi_out_t)); | ||
625 | if (!out) { | ||
626 | return -ENOMEM; | ||
627 | } | ||
628 | |||
629 | out->dev = (hdmi_device_t *)dev; | ||
630 | memcpy(&out->stream_out, &hdmi_stream_out_descriptor, | ||
631 | sizeof(audio_stream_out_t)); | ||
632 | memcpy(&out->android_config, config, sizeof(audio_config_t)); | ||
633 | |||
634 | pcm_config = &out->config; | ||
635 | a_config = &out->android_config; | ||
636 | |||
637 | #if defined(LOG_NDEBUG) && (LOG_NDEBUG == 0) | ||
638 | /* Analyze flags */ | ||
639 | if (flags) { | ||
640 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_DIRECT) | ||
641 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_PRIMARY) | ||
642 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_FAST) | ||
643 | DUMP_FLAG(flags, AUDIO_OUTPUT_FLAG_DEEP_BUFFER) | ||
644 | } else { | ||
645 | ALOGV("flags == AUDIO_OUTPUT_FLAG_NONE (0)"); | ||
646 | } | ||
647 | #endif /* defined(LOG_NDEBUG) && (LOG_NDEBUG == 0) */ | ||
648 | /* Initialize the PCM Configuration */ | ||
649 | pcm_config->period_size = HDMI_PERIOD_SIZE; | ||
650 | pcm_config->period_count = HDMI_PERIOD_COUNT; | ||
651 | |||
652 | if (a_config->sample_rate) { | ||
653 | pcm_config->rate = config->sample_rate; | ||
654 | } else { | ||
655 | pcm_config->rate = HDMI_SAMPLING_RATE; | ||
656 | a_config->sample_rate = HDMI_SAMPLING_RATE; | ||
657 | } | ||
658 | |||
659 | switch (a_config->format) { | ||
660 | case AUDIO_FORMAT_DEFAULT: | ||
661 | a_config->format = AUDIO_FORMAT_PCM_16_BIT; | ||
662 | /* fall through */ | ||
663 | case AUDIO_FORMAT_PCM_16_BIT: | ||
664 | pcm_config->format = PCM_FORMAT_S16_LE; | ||
665 | break; | ||
666 | default: | ||
667 | ALOGE("HDMI rejecting format %x", config->format); | ||
668 | goto fail; | ||
669 | } | ||
670 | |||
671 | a_config->channel_mask = config->channel_mask; | ||
672 | switch (config->channel_mask) { | ||
673 | case AUDIO_CHANNEL_OUT_STEREO: | ||
674 | pcm_config->channels = 2; | ||
675 | break; | ||
676 | case AUDIO_CHANNEL_OUT_QUAD: | ||
677 | pcm_config->channels = 4; | ||
678 | break; | ||
679 | case AUDIO_CHANNEL_OUT_5POINT1: | ||
680 | pcm_config->channels = 6; | ||
681 | break; | ||
682 | case AUDIO_CHANNEL_OUT_7POINT1: | ||
683 | pcm_config->channels = 8; | ||
684 | break; | ||
685 | default: | ||
686 | ALOGE("HDMI setting a default channel_mask %x -> 8", config->channel_mask); | ||
687 | config->channel_mask = AUDIO_CHANNEL_OUT_7POINT1; | ||
688 | a_config->channel_mask = AUDIO_CHANNEL_OUT_7POINT1; | ||
689 | pcm_config->channels = 8; | ||
690 | } | ||
691 | |||
692 | //Allocating buffer for at most 8 channels | ||
693 | out->buffcpy = malloc(pcm_config->period_size * sizeof(int16_t) * HDMI_MAX_CHANNELS); | ||
694 | if (!out->buffcpy){ | ||
695 | ALOGE("Could not allocate memory"); | ||
696 | goto fail; | ||
697 | } | ||
698 | |||
699 | ALOGV("stream = %p", out); | ||
700 | *stream_out = &out->stream_out; | ||
701 | |||
702 | return 0; | ||
703 | |||
704 | fail: | ||
705 | free(out); | ||
706 | return -ENOSYS; | ||
707 | } | ||
708 | |||
709 | static void hdmi_adev_close_output_stream(audio_hw_device_t *dev, | ||
710 | struct audio_stream_out* stream_out) | ||
711 | { | ||
712 | hdmi_out_t *out = (hdmi_out_t*)stream_out; | ||
713 | |||
714 | TRACEM("dev=%p stream_out=%p", dev, stream_out); | ||
715 | UNUSED(dev); | ||
716 | |||
717 | stream_out->common.standby((audio_stream_t*)stream_out); | ||
718 | free(out->buffcpy); | ||
719 | out->buffcpy = NULL; | ||
720 | free(stream_out); | ||
721 | } | ||
722 | |||
723 | static int hdmi_adev_open_input_stream(audio_hw_device_t *dev, | ||
724 | audio_io_handle_t handle, | ||
725 | audio_devices_t devices, | ||
726 | struct audio_config *config, | ||
727 | struct audio_stream_in **stream_in, | ||
728 | audio_input_flags_t flags, | ||
729 | const char *address, | ||
730 | audio_source_t source) | ||
731 | { | ||
732 | TRACE(); | ||
733 | UNUSED(dev); | ||
734 | UNUSED(handle); | ||
735 | UNUSED(devices); | ||
736 | UNUSED(config); | ||
737 | UNUSED(stream_in); | ||
738 | UNUSED(flags); | ||
739 | UNUSED(address); | ||
740 | UNUSED(source); | ||
741 | |||
742 | return -ENOSYS; | ||
743 | } | ||
744 | |||
745 | static void hdmi_adev_close_input_stream(audio_hw_device_t *dev, | ||
746 | struct audio_stream_in *stream_in) | ||
747 | { | ||
748 | TRACE(); | ||
749 | UNUSED(dev); | ||
750 | UNUSED(stream_in); | ||
751 | } | ||
752 | |||
753 | static int hdmi_adev_dump(const audio_hw_device_t *dev, int fd) | ||
754 | { | ||
755 | TRACE(); | ||
756 | UNUSED(dev); | ||
757 | UNUSED(fd); | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static hdmi_device_t hdmi_adev = { | ||
763 | .device = { | ||
764 | .common = { | ||
765 | .tag = HARDWARE_DEVICE_TAG, | ||
766 | .version = AUDIO_DEVICE_API_VERSION_2_0, | ||
767 | .module = NULL, | ||
768 | .close = hdmi_adev_close, | ||
769 | }, | ||
770 | .get_supported_devices = hdmi_adev_get_supported_devices, | ||
771 | .init_check = hdmi_adev_init_check, | ||
772 | .set_voice_volume = hdmi_adev_set_voice_volume, | ||
773 | .set_master_volume = hdmi_adev_set_master_volume, | ||
774 | .get_master_volume = hdmi_adev_get_master_volume, | ||
775 | .set_mode = hdmi_adev_set_mode, | ||
776 | .set_mic_mute = hdmi_adev_set_mic_mute, | ||
777 | .get_mic_mute = hdmi_adev_get_mic_mute, | ||
778 | .set_parameters = hdmi_adev_set_parameters, | ||
779 | .get_parameters = hdmi_adev_get_parameters, | ||
780 | .get_input_buffer_size = hdmi_adev_get_input_buffer_size, | ||
781 | .open_output_stream = hdmi_adev_open_output_stream, | ||
782 | .close_output_stream = hdmi_adev_close_output_stream, | ||
783 | .open_input_stream = hdmi_adev_open_input_stream, | ||
784 | .close_input_stream = hdmi_adev_close_input_stream, | ||
785 | .dump = hdmi_adev_dump, | ||
786 | }, | ||
787 | |||
788 | // Don't reorder channels until a valid CEA mapping has been | ||
789 | // explicitly set. IOW, assume default channel mapping is | ||
790 | // fine until some other mapping is requested. | ||
791 | .CEAMap = true, | ||
792 | .map = {0}, | ||
793 | }; | ||
794 | |||
795 | static int hdmi_adev_open(const hw_module_t* module, | ||
796 | const char* name, | ||
797 | hw_device_t** device) | ||
798 | { | ||
799 | TRACE(); | ||
800 | |||
801 | if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) | ||
802 | return -EINVAL; | ||
803 | |||
804 | hdmi_adev.device.common.module = (struct hw_module_t *) module; | ||
805 | *device = &hdmi_adev.device.common; | ||
806 | |||
807 | return 0; | ||
808 | } | ||
809 | |||
810 | static struct hw_module_methods_t hal_module_methods = { | ||
811 | .open = hdmi_adev_open, | ||
812 | }; | ||
813 | |||
814 | struct audio_module HAL_MODULE_INFO_SYM = { | ||
815 | .common = { | ||
816 | .tag = HARDWARE_MODULE_TAG, | ||
817 | .version_major = 1, | ||
818 | .version_minor = 0, | ||
819 | .id = AUDIO_HARDWARE_MODULE_ID, | ||
820 | .name = "OMAP HDMI audio HW HAL", | ||
821 | .author = "Texas Instruments", | ||
822 | .methods = &hal_module_methods, | ||
823 | }, | ||
824 | }; | ||
diff --git a/audio/hdmi/hdmi_audio_utils.c b/audio/hdmi/hdmi_audio_utils.c new file mode 100644 index 0000000..b29ec25 --- /dev/null +++ b/audio/hdmi/hdmi_audio_utils.c | |||
@@ -0,0 +1,414 @@ | |||
1 | /* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */ | ||
2 | /* | ||
3 | * Copyright (C) 2012 Texas Instruments | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | |||
18 | #define LOG_TAG "hdmi_audio_caps" | ||
19 | /* #define LOG_NDEBUG 0 */ | ||
20 | |||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <string.h> | ||
25 | #include <errno.h> | ||
26 | |||
27 | #include <xf86drm.h> | ||
28 | #include <xf86drmMode.h> | ||
29 | |||
30 | #include "hdmi_audio_hal.h" | ||
31 | |||
32 | /* | ||
33 | * This program can be compiled standalone for debugging and | ||
34 | * testing by defining HDMI_CAPS_STANDALONE | ||
35 | */ | ||
36 | |||
37 | #ifdef HDMI_CAPS_STANDALONE | ||
38 | #include <stdio.h> | ||
39 | |||
40 | #define lfmt(x) x | ||
41 | #define ALOGV(fmt, ...) printf(lfmt(fmt) "\n", ##__VA_ARGS__) | ||
42 | #define ALOGW(fmt, ...) printf(lfmt(fmt) "\n", ##__VA_ARGS__) | ||
43 | #define ALOGE(fmt, ...) printf(lfmt(fmt) "\n", ##__VA_ARGS__) | ||
44 | #define ALOGV_IF(cond, fmt, ...) { if (cond) { printf(lfmt(fmt) "\n", ##__VA_ARGS__); } } | ||
45 | #define ALOGE_IF(cond, fmt, ...) { if (cond) { printf(lfmt(fmt) "\n", ##__VA_ARGS__); } } | ||
46 | #else /* HDMI_CAPS_STANDALONE */ | ||
47 | |||
48 | #include <cutils/log.h> | ||
49 | |||
50 | #endif /* HDMI_CAPS_STANDALONE */ | ||
51 | |||
52 | /***************************************************************** | ||
53 | * HDMI AUDIO CAPABILITIES (EDID PARSING) | ||
54 | * | ||
55 | * To maintain this section, you need these references: | ||
56 | * | ||
57 | * - High-Definition Multimedia Interface Specification Version 1.4a | ||
58 | * Mar 4, 2010, Hitachi et al. (See Section 8.3 "E-EDID Data | ||
59 | * Structure") | ||
60 | * | ||
61 | * - E-EDID, VESA Enhanced Extended Display Identification Data | ||
62 | * Standard, Release A, Revision 2, September 25. 2006, | ||
63 | * Video Electronics Standards Association (VESA) | ||
64 | * | ||
65 | * - CEA-861-D, A DTV Profile for Uncompressed High Speed Digital | ||
66 | * Interfaces, July 2006 (Section 7.5) | ||
67 | * | ||
68 | * | ||
69 | * Summary: The E-EDID is composed of 128-byte blocks. The first byte | ||
70 | * identifies the block. HDMI devices are required to send a CEA | ||
71 | * Extension block (version 3), and the first one is the one we /must/ | ||
72 | * parse. (You can probably go far with just this info and the | ||
73 | * CEA-861-D spec.) | ||
74 | * | ||
75 | ***************************************************************** | ||
76 | */ | ||
77 | |||
78 | #define DISPLAY_MAX 3 | ||
79 | #define DISPLAY_NAME_MAX 20 | ||
80 | #define HDMI_DISPLAY_NAME "hdmi" | ||
81 | #define OMAP_DSS_SYSFS "/sys/devices/platform/omapdss/" | ||
82 | #define DRM_OMAP_DEVNAME "omapdrm" | ||
83 | #define DRM_EDID_PROPNAME "EDID" | ||
84 | |||
85 | /* TODO: Figure this out dynamically, but ATM this is enforced | ||
86 | * in the kernel. | ||
87 | */ | ||
88 | #define HDMI_MAX_EDID 512 | ||
89 | |||
90 | #define EDID_BLOCK_SIZE 128 | ||
91 | #define EDID_BLOCK_MAP_ID 0xF0 | ||
92 | #define EDID_BLOCK_CEA_ID 0x02 | ||
93 | #define EDID_BLOCK_CEA_REV_3 0x03 | ||
94 | |||
95 | #define CEA_TAG_MASK 0xE0 | ||
96 | #define CEA_SIZE_MASK 0x1F | ||
97 | #define CEA_TAG_AUDIO (1 << 5) | ||
98 | #define CEA_TAG_VIDEO (2 << 5) | ||
99 | #define CEA_TAG_SPKRS (4 << 5) | ||
100 | #define CEA_BIT_AUDIO (1 << 6) | ||
101 | |||
102 | static void hdmi_dump_short_audio_descriptor_block(unsigned char *mem) | ||
103 | { | ||
104 | const unsigned char FORMAT_MASK = 0x78; | ||
105 | const unsigned char MAX_CH_MASK = 0x07; | ||
106 | int n, size; | ||
107 | unsigned char *p, *end, byte, format, chs; | ||
108 | const char* formats[] = { | ||
109 | "Reserved (0)", | ||
110 | "LPCM", | ||
111 | "AC-3", | ||
112 | "MPEG1 (Layers 1 & 2)", | ||
113 | "MP3 (MPEG1 Layer 3)", | ||
114 | "MPEG2 (multichannel)", | ||
115 | "AAC", | ||
116 | "DTS", | ||
117 | "ATRAC", | ||
118 | "One Bit Audio", | ||
119 | "Dolby Digital +", | ||
120 | "DTS-HD", | ||
121 | "MAT (MLP)", | ||
122 | "DST", | ||
123 | "WMA Pro", | ||
124 | "Reserved (15)", | ||
125 | }; | ||
126 | |||
127 | size = *mem & CEA_SIZE_MASK; | ||
128 | end = mem + 1 + size; | ||
129 | |||
130 | for (p = mem + 1 ; p < end ; p += 3) { | ||
131 | byte = p[0]; | ||
132 | format = (byte & FORMAT_MASK) >> 3; | ||
133 | chs = byte & MAX_CH_MASK; | ||
134 | |||
135 | ALOGV("Parsing Short Audio Descriptor:"); | ||
136 | ALOGV(" format: %s", formats[format]); | ||
137 | ALOGV(" max channels: %d", chs + 1); | ||
138 | ALOGV(" sample rates:"); | ||
139 | |||
140 | byte = p[1]; | ||
141 | ALOGV_IF(byte & (1 << 0), " 32.0 kHz"); | ||
142 | ALOGV_IF(byte & (1 << 1), " 44.1 kHz"); | ||
143 | ALOGV_IF(byte & (1 << 2), " 48.0 kHz"); | ||
144 | ALOGV_IF(byte & (1 << 3), " 88.2 kHz"); | ||
145 | ALOGV_IF(byte & (1 << 4), " 96.0 kHz"); | ||
146 | ALOGV_IF(byte & (1 << 5), " 176.4 kHz"); | ||
147 | ALOGV_IF(byte & (1 << 6), " 192.0 kHz"); | ||
148 | |||
149 | byte = p[2]; | ||
150 | if ((format >= 2) && (format <= 8)) { | ||
151 | ALOGV(" max bit rate: %d kHz", ((int)byte) * 8); | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | int hdmi_get_edid_sysfs(unsigned char *edid) | ||
157 | { | ||
158 | int fd; | ||
159 | char fn[256]; | ||
160 | char disp_name[DISPLAY_NAME_MAX]; | ||
161 | int status; | ||
162 | int index, n; | ||
163 | |||
164 | ALOGI("Trying to read the HDMI EDID through sysfs"); | ||
165 | |||
166 | for (index = 0; index < DISPLAY_MAX; index++) { | ||
167 | snprintf(fn, sizeof(fn), OMAP_DSS_SYSFS "display%u", index); | ||
168 | fd = open(fn, O_RDONLY); | ||
169 | if (fd < 0) { | ||
170 | ALOGE("HDMI sysfs entry not found"); | ||
171 | return -ENODEV; | ||
172 | } | ||
173 | close(fd); | ||
174 | |||
175 | snprintf(fn, sizeof(fn), OMAP_DSS_SYSFS "display%u/name", index); | ||
176 | fd = open(fn, O_RDONLY); | ||
177 | if (fd < 0) { | ||
178 | ALOGE("Error opening display name sysfs entry"); | ||
179 | return -ENODEV; | ||
180 | } | ||
181 | |||
182 | status = read(fd, disp_name, sizeof(disp_name)); | ||
183 | close(fd); | ||
184 | if (status == -1) { | ||
185 | ALOGE("Error reading display name sysfs entry"); | ||
186 | return -errno; | ||
187 | } | ||
188 | |||
189 | if (!strncasecmp(disp_name, HDMI_DISPLAY_NAME, strlen(HDMI_DISPLAY_NAME))) { | ||
190 | ALOGV("HDMI device found at display%u", index); | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (index == DISPLAY_MAX) { | ||
196 | ALOGE("HDMI sysfs entry not found"); | ||
197 | return -ENODEV; | ||
198 | } | ||
199 | |||
200 | snprintf(fn, sizeof(fn), OMAP_DSS_SYSFS "display%u/edid", index); | ||
201 | fd = open(fn, O_RDONLY); | ||
202 | if (fd == -1) { | ||
203 | return -errno; | ||
204 | } | ||
205 | |||
206 | memset(edid, 0, HDMI_MAX_EDID); | ||
207 | |||
208 | status = read(fd, edid, HDMI_MAX_EDID); | ||
209 | close(fd); | ||
210 | if (status == -1) { | ||
211 | ALOGV("Error reading EDID through sysfs"); | ||
212 | return -errno; | ||
213 | } else { | ||
214 | ALOGV("read %d bytes from edid sysfs file", status); | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | int hdmi_get_edid_drm(unsigned char *edid) | ||
221 | { | ||
222 | drmModeRes *resources; | ||
223 | drmModeConnector *connector; | ||
224 | drmModePropertyPtr prop; | ||
225 | drmModePropertyBlobPtr blob; | ||
226 | unsigned char *blob_data; | ||
227 | int fd, i, j; | ||
228 | int found = 0; | ||
229 | |||
230 | ALOGI("Trying to read the HDMI EDID through DRM"); | ||
231 | |||
232 | fd = drmOpen(DRM_OMAP_DEVNAME, NULL); | ||
233 | if (fd < 0) { | ||
234 | ALOGE("Failed to open DRM device '%s'", DRM_OMAP_DEVNAME); | ||
235 | return -ENODEV; | ||
236 | } | ||
237 | |||
238 | resources = drmModeGetResources(fd); | ||
239 | if (!resources) { | ||
240 | ALOGE("Failed to get DRM resources"); | ||
241 | drmClose(fd); | ||
242 | return -EINVAL; | ||
243 | } | ||
244 | |||
245 | for (i = 0; (i < resources->count_connectors) && !found; i++) { | ||
246 | connector = drmModeGetConnector(fd, resources->connectors[i]); | ||
247 | if (!connector) { | ||
248 | continue; | ||
249 | } | ||
250 | |||
251 | if (!connector->count_modes) { | ||
252 | drmModeFreeConnector(connector); | ||
253 | continue; | ||
254 | } | ||
255 | |||
256 | for (j = 0; (j < connector->count_props) && !found; j++) { | ||
257 | prop = drmModeGetProperty(fd, connector->props[j]); | ||
258 | if (!prop) { | ||
259 | continue; | ||
260 | } | ||
261 | |||
262 | if (strcmp(prop->name, DRM_EDID_PROPNAME) || | ||
263 | !(prop->flags & DRM_MODE_PROP_BLOB)) { | ||
264 | drmModeFreeProperty(prop); | ||
265 | continue; | ||
266 | } | ||
267 | |||
268 | blob = drmModeGetPropertyBlob(fd, connector->prop_values[j]); | ||
269 | if (!blob) { | ||
270 | drmModeFreeProperty(prop); | ||
271 | continue; | ||
272 | } | ||
273 | |||
274 | if (blob->length > HDMI_MAX_EDID) { | ||
275 | ALOGW("EDID blob length %u is larger than expected %d, skipping", | ||
276 | blob->length, HDMI_MAX_EDID); | ||
277 | drmModeFreePropertyBlob(blob); | ||
278 | drmModeFreeProperty(prop); | ||
279 | continue; | ||
280 | } | ||
281 | |||
282 | ALOGV("EDID has been found, length is %u bytes", blob->length); | ||
283 | memset(edid, 0, HDMI_MAX_EDID); | ||
284 | memcpy(edid, blob->data, blob->length); | ||
285 | drmModeFreePropertyBlob(blob); | ||
286 | drmModeFreeProperty(prop); | ||
287 | found = 1; | ||
288 | } | ||
289 | |||
290 | drmModeFreeConnector(connector); | ||
291 | } | ||
292 | |||
293 | drmModeFreeResources(resources); | ||
294 | |||
295 | drmClose(fd); | ||
296 | |||
297 | return found ? 0 : -ENODEV; | ||
298 | } | ||
299 | |||
300 | int hdmi_query_audio_caps(hdmi_audio_caps_t *caps) | ||
301 | { | ||
302 | unsigned char edid[HDMI_MAX_EDID]; | ||
303 | int index, n; | ||
304 | int nblocks; | ||
305 | int edid_size; | ||
306 | int has_audio = 0; | ||
307 | int speaker_alloc = 0; | ||
308 | int done = 0; | ||
309 | int status; | ||
310 | |||
311 | status = hdmi_get_edid_sysfs(edid); | ||
312 | if (status) { | ||
313 | ALOGE("Failed to read EDID through sysfs"); | ||
314 | status = hdmi_get_edid_drm(edid); | ||
315 | if (status) { | ||
316 | ALOGE("Failed to read EDID through DRM"); | ||
317 | return status; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | nblocks = edid[0x7E]; | ||
322 | if (edid[EDID_BLOCK_SIZE] == EDID_BLOCK_MAP_ID) { | ||
323 | /* The block map is just an index... don't need it. */ | ||
324 | ++nblocks; | ||
325 | } | ||
326 | ALOGV("EDID contians %d additional extension block(s)", nblocks); | ||
327 | edid_size = EDID_BLOCK_SIZE * (nblocks + 1); | ||
328 | if (edid_size > HDMI_MAX_EDID) { | ||
329 | edid_size = HDMI_MAX_EDID; | ||
330 | } | ||
331 | |||
332 | for (index = EDID_BLOCK_SIZE ; index < edid_size ; index += EDID_BLOCK_SIZE) { | ||
333 | if ((edid[index] == EDID_BLOCK_CEA_ID) | ||
334 | && (edid[index+1] == EDID_BLOCK_CEA_REV_3)) { | ||
335 | /* CEA BLOCK */ | ||
336 | unsigned char d; | ||
337 | unsigned char byte, tag, size; | ||
338 | |||
339 | /* Parse CEA header for size and audio presence */ | ||
340 | d = edid[index + 2]; | ||
341 | if (edid[index + 3] & CEA_BIT_AUDIO) { | ||
342 | has_audio = 1; | ||
343 | } else { | ||
344 | break; | ||
345 | } | ||
346 | |||
347 | n = 4; | ||
348 | while (n < d) { | ||
349 | byte = edid[index + n]; | ||
350 | tag = byte & CEA_TAG_MASK; | ||
351 | size = byte & CEA_SIZE_MASK; | ||
352 | ++n; | ||
353 | |||
354 | switch (tag) { | ||
355 | case CEA_TAG_AUDIO: | ||
356 | /* ignore for now, needed for bitstream. */ | ||
357 | hdmi_dump_short_audio_descriptor_block(&edid[index + n - 1]); | ||
358 | break; | ||
359 | case CEA_TAG_SPKRS: /* I think this fails... not sure why */ | ||
360 | ALOGE_IF(size != 3, "CEA Speaker Allocation Block is wrong size " | ||
361 | "(got %d, expected 3)", size); | ||
362 | byte = edid[index + n]; | ||
363 | speaker_alloc = byte; | ||
364 | break; | ||
365 | } | ||
366 | |||
367 | n += size; | ||
368 | } | ||
369 | |||
370 | /* Per spec, only need to parse the first block */ | ||
371 | break; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | caps->has_audio = has_audio; | ||
376 | caps->speaker_alloc = speaker_alloc; | ||
377 | |||
378 | ALOGI_IF(caps->has_audio, "HDMI speaker allocation 0x%02x", | ||
379 | caps->speaker_alloc); | ||
380 | |||
381 | ALOGW_IF(!caps->has_audio, "HDMI device doesn't support audio"); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | |||
387 | #ifdef HDMI_CAPS_STANDALONE | ||
388 | int main(int argc, char* argv[]) | ||
389 | { | ||
390 | const char prog_name[] = "hdmi_audio_caps"; | ||
391 | hdmi_audio_caps_t caps = { | ||
392 | .has_audio = 0, | ||
393 | }; | ||
394 | |||
395 | if (argc < 1) { | ||
396 | printf("usage: %s\n", argc ? argv[0] : prog_name); | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | if (hdmi_query_audio_caps(&caps)) { | ||
401 | fprintf(stderr, "Fatal error: could not read EDID (%s)\n", | ||
402 | strerror(errno)); | ||
403 | return 1; | ||
404 | } | ||
405 | |||
406 | printf("\n"); | ||
407 | printf("caps = {\n"); | ||
408 | printf(" .has_audio = %d\n", caps.has_audio); | ||
409 | printf(" .speaker_alloc = 0x%02x\n", caps.speaker_alloc); | ||
410 | printf("}\n"); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | #endif /* HDMI_CAPS_STANDALONE */ | ||
diff --git a/audio/primary/Android.mk b/audio/primary/Android.mk new file mode 100644 index 0000000..40f60da --- /dev/null +++ b/audio/primary/Android.mk | |||
@@ -0,0 +1,43 @@ | |||
1 | # Copyright (C) 2013 Texas Instruments | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | LOCAL_PATH := $(call my-dir) | ||
16 | |||
17 | ifeq ($(findstring am57x, $(TARGET_BOARD_PLATFORM)),am57x) | ||
18 | |||
19 | include $(CLEAR_VARS) | ||
20 | |||
21 | LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM) | ||
22 | |||
23 | LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw | ||
24 | LOCAL_SRC_FILES := audio_hw.c | ||
25 | |||
26 | LOCAL_C_INCLUDES += \ | ||
27 | external/tinyalsa/include \ | ||
28 | system/media/audio_route/include \ | ||
29 | system/media/audio_utils/include \ | ||
30 | system/media/audio_effects/include | ||
31 | |||
32 | LOCAL_SHARED_LIBRARIES := \ | ||
33 | liblog \ | ||
34 | libcutils \ | ||
35 | libtinyalsa \ | ||
36 | libaudioroute \ | ||
37 | libaudioutils | ||
38 | |||
39 | LOCAL_MODULE_TAGS := optional | ||
40 | |||
41 | include $(BUILD_SHARED_LIBRARY) | ||
42 | |||
43 | endif | ||
diff --git a/audio/primary/audio_hw.c b/audio/primary/audio_hw.c new file mode 100644 index 0000000..95f0f06 --- /dev/null +++ b/audio/primary/audio_hw.c | |||
@@ -0,0 +1,1770 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Texas Instruments | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #define LOG_TAG "audio_hw_primary" | ||
18 | //#define LOG_NDEBUG 0 | ||
19 | //#define VERY_VERBOSE_LOGGING | ||
20 | #ifdef VERY_VERBOSE_LOGGING | ||
21 | #define ALOGVV ALOGV | ||
22 | #else | ||
23 | #define ALOGVV(a...) do { } while(0) | ||
24 | #endif | ||
25 | |||
26 | #include <errno.h> | ||
27 | #include <stdint.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <sys/time.h> | ||
30 | |||
31 | #include <cutils/log.h> | ||
32 | #include <cutils/str_parms.h> | ||
33 | #include <cutils/properties.h> | ||
34 | |||
35 | #include <audio_utils/resampler.h> | ||
36 | #include <audio_route/audio_route.h> | ||
37 | #include <system/audio.h> | ||
38 | #include <hardware/hardware.h> | ||
39 | #include <hardware/audio.h> | ||
40 | #include <hardware/audio_effect.h> | ||
41 | |||
42 | #include <tinyalsa/asoundlib.h> | ||
43 | |||
44 | #define UNUSED(x) (void)(x) | ||
45 | |||
46 | /* yet another definition of ARRAY_SIZE macro) */ | ||
47 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | ||
48 | |||
49 | /* | ||
50 | * additional space in resampler buffer allowing for extra samples to be returned | ||
51 | * by speex resampler when sample rates ratio is not an integer | ||
52 | */ | ||
53 | #define RESAMPLER_HEADROOM_FRAMES 10 | ||
54 | |||
55 | /* buffer_remix: functor for doing in-place buffer manipulations. | ||
56 | * | ||
57 | * NB. When remix_func is called, the memory at `buf` must be at least | ||
58 | * as large as frames * sample_size * MAX(in_chans, out_chans). | ||
59 | */ | ||
60 | struct buffer_remix { | ||
61 | void (*remix_func)(struct buffer_remix *data, void *buf, size_t frames); | ||
62 | size_t sample_size; /* size of one audio sample, in bytes */ | ||
63 | size_t in_chans; /* number of input channels */ | ||
64 | size_t out_chans; /* number of output channels */ | ||
65 | }; | ||
66 | |||
67 | struct j6_voice_stream { | ||
68 | struct j6_audio_device *dev; | ||
69 | struct pcm *pcm_in; | ||
70 | struct pcm *pcm_out; | ||
71 | struct pcm_config in_config; | ||
72 | struct pcm_config out_config; | ||
73 | struct resampler_itfe *resampler; | ||
74 | struct resampler_buffer_provider buf_provider; | ||
75 | struct buffer_remix *remix; | ||
76 | pthread_t thread; | ||
77 | int16_t *in_buffer; | ||
78 | int16_t *out_buffer; | ||
79 | size_t in_frames; | ||
80 | size_t out_frames; | ||
81 | size_t in_frame_size; | ||
82 | size_t out_frame_size; | ||
83 | char *name; | ||
84 | }; | ||
85 | |||
86 | struct j6_voice { | ||
87 | struct j6_voice_stream ul; | ||
88 | struct j6_voice_stream dl; | ||
89 | }; | ||
90 | |||
91 | struct j6_audio_device { | ||
92 | struct audio_hw_device device; | ||
93 | struct j6_stream_in *in; | ||
94 | struct j6_stream_out *out; | ||
95 | struct j6_voice voice; | ||
96 | struct audio_route *route; | ||
97 | audio_devices_t in_device; | ||
98 | audio_devices_t out_device; | ||
99 | pthread_mutex_t lock; | ||
100 | unsigned int card; | ||
101 | unsigned int in_port; | ||
102 | unsigned int out_port; | ||
103 | unsigned int bt_card; | ||
104 | unsigned int bt_port; | ||
105 | bool mic_mute; | ||
106 | bool in_call; | ||
107 | audio_mode_t mode; | ||
108 | }; | ||
109 | |||
110 | struct j6_stream_in { | ||
111 | struct audio_stream_in stream; | ||
112 | struct j6_audio_device *dev; | ||
113 | struct pcm_config config; | ||
114 | struct pcm *pcm; | ||
115 | struct buffer_remix *remix; /* adapt hw chan count to client */ | ||
116 | struct resampler_itfe *resampler; | ||
117 | struct resampler_buffer_provider buf_provider; | ||
118 | int16_t *buffer; | ||
119 | size_t frames_in; | ||
120 | size_t hw_frame_size; | ||
121 | unsigned int requested_rate; | ||
122 | unsigned int requested_channels; | ||
123 | int read_status; | ||
124 | pthread_mutex_t lock; | ||
125 | bool standby; | ||
126 | }; | ||
127 | |||
128 | struct j6_stream_out { | ||
129 | struct audio_stream_out stream; | ||
130 | struct j6_audio_device *dev; | ||
131 | struct pcm_config config; | ||
132 | struct pcm *pcm; | ||
133 | struct timespec last; | ||
134 | pthread_mutex_t lock; | ||
135 | bool standby; | ||
136 | int64_t written; /* total frames written, not cleared when entering standby */ | ||
137 | }; | ||
138 | |||
139 | |||
140 | static const char *supported_media_cards[] = { | ||
141 | "dra7evm", | ||
142 | "VayuEVM", | ||
143 | "DRA7xxEVM", | ||
144 | }; | ||
145 | |||
146 | static const char *supported_bt_cards[] = { | ||
147 | "DRA7xxWiLink", | ||
148 | }; | ||
149 | |||
150 | #define SUPPORTED_IN_DEVICES (AUDIO_DEVICE_IN_BUILTIN_MIC | \ | ||
151 | AUDIO_DEVICE_IN_WIRED_HEADSET | \ | ||
152 | AUDIO_DEVICE_IN_DEFAULT) | ||
153 | #define SUPPORTED_OUT_DEVICES (AUDIO_DEVICE_OUT_SPEAKER | \ | ||
154 | AUDIO_DEVICE_OUT_WIRED_HEADSET | \ | ||
155 | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | \ | ||
156 | AUDIO_DEVICE_OUT_DEFAULT) | ||
157 | |||
158 | #define CAPTURE_SAMPLE_RATE 44100 | ||
159 | #define CAPTURE_PERIOD_SIZE 256 | ||
160 | #define CAPTURE_PERIOD_COUNT 4 | ||
161 | #define CAPTURE_BUFFER_SIZE (CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT) | ||
162 | |||
163 | #define PLAYBACK_SAMPLE_RATE 44100 | ||
164 | #define PLAYBACK_PERIOD_SIZE 256 | ||
165 | #define PLAYBACK_PERIOD_COUNT 4 | ||
166 | #define PLAYBACK_BUFFER_SIZE (PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT) | ||
167 | |||
168 | #define BT_SAMPLE_RATE 8000 | ||
169 | #define BT_PERIOD_SIZE 80 | ||
170 | #define BT_PERIOD_COUNT 4 | ||
171 | #define BT_BUFFER_SIZE (BT_PERIOD_SIZE * BT_PERIOD_COUNT) | ||
172 | |||
173 | struct pcm_config pcm_config_capture = { | ||
174 | .channels = 2, | ||
175 | .rate = CAPTURE_SAMPLE_RATE, | ||
176 | .format = PCM_FORMAT_S16_LE, | ||
177 | .period_size = CAPTURE_PERIOD_SIZE, | ||
178 | .period_count = CAPTURE_PERIOD_COUNT, | ||
179 | .start_threshold = 1, | ||
180 | .stop_threshold = CAPTURE_BUFFER_SIZE, | ||
181 | }; | ||
182 | |||
183 | struct pcm_config pcm_config_playback = { | ||
184 | .channels = 2, | ||
185 | .rate = PLAYBACK_SAMPLE_RATE, | ||
186 | .format = PCM_FORMAT_S16_LE, | ||
187 | .period_size = PLAYBACK_PERIOD_SIZE, | ||
188 | .period_count = PLAYBACK_PERIOD_COUNT, | ||
189 | .start_threshold = PLAYBACK_BUFFER_SIZE / 2, | ||
190 | .stop_threshold = PLAYBACK_BUFFER_SIZE, | ||
191 | .avail_min = PLAYBACK_PERIOD_SIZE, | ||
192 | }; | ||
193 | |||
194 | struct pcm_config pcm_config_bt_in = { | ||
195 | .channels = 1, | ||
196 | .rate = BT_SAMPLE_RATE, | ||
197 | .format = PCM_FORMAT_S16_LE, | ||
198 | .period_size = BT_PERIOD_SIZE, | ||
199 | .period_count = BT_PERIOD_COUNT, | ||
200 | }; | ||
201 | |||
202 | struct pcm_config pcm_config_bt_out = { | ||
203 | .channels = 1, | ||
204 | .rate = BT_SAMPLE_RATE, | ||
205 | .format = PCM_FORMAT_S16_LE, | ||
206 | .period_size = BT_PERIOD_SIZE, | ||
207 | .period_count = BT_PERIOD_COUNT, | ||
208 | }; | ||
209 | |||
210 | static void do_out_standby(struct j6_stream_out *out); | ||
211 | |||
212 | /* must be called with device lock held */ | ||
213 | static void select_input_device(struct j6_audio_device *adev) | ||
214 | { | ||
215 | if (adev->in_device & ~SUPPORTED_IN_DEVICES) | ||
216 | ALOGW("select_input_device() device not supported, will use default device"); | ||
217 | } | ||
218 | |||
219 | /* must be called with device lock held */ | ||
220 | static void select_output_device(struct j6_audio_device *adev) | ||
221 | { | ||
222 | if (adev->out_device & ~SUPPORTED_OUT_DEVICES) | ||
223 | ALOGW("select_output_device() device(s) not supported, will use default devices"); | ||
224 | } | ||
225 | |||
226 | static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count) | ||
227 | { | ||
228 | size_t size; | ||
229 | |||
230 | /* | ||
231 | * take resampling into account and return the closest majoring | ||
232 | * multiple of 16 frames, as audioflinger expects audio buffers to | ||
233 | * be a multiple of 16 frames | ||
234 | */ | ||
235 | size = (pcm_config_capture.period_size * sample_rate) / pcm_config_capture.rate; | ||
236 | size = ((size + 15) / 16) * 16; | ||
237 | |||
238 | return size * channel_count * audio_bytes_per_sample(format); | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Implementation of buffer_remix::remix_func that removes | ||
243 | * channels in place without doing any other processing. The | ||
244 | * extra channels are truncated. | ||
245 | */ | ||
246 | static void remove_channels_from_buf(struct buffer_remix *data, void *buf, size_t frames) | ||
247 | { | ||
248 | size_t samp_size, in_frame, out_frame; | ||
249 | size_t N, c; | ||
250 | char *s, *d; | ||
251 | |||
252 | ALOGVV("remove_channels_from_buf() remix=%p buf=%p frames=%u", | ||
253 | data, buf, frames); | ||
254 | |||
255 | if (frames == 0) | ||
256 | return; | ||
257 | |||
258 | samp_size = data->sample_size; | ||
259 | in_frame = data->in_chans * samp_size; | ||
260 | out_frame = data->out_chans * samp_size; | ||
261 | |||
262 | if (out_frame >= in_frame) { | ||
263 | ALOGE("BUG: remove_channels_from_buf() can not add channels to a buffer.\n"); | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | N = frames - 1; | ||
268 | d = (char*)buf + out_frame; | ||
269 | s = (char*)buf + in_frame; | ||
270 | |||
271 | /* take the first several channels and truncate the rest */ | ||
272 | while (N--) { | ||
273 | for (c = 0; c < out_frame; ++c) | ||
274 | d[c] = s[c]; | ||
275 | d += out_frame; | ||
276 | s += in_frame; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | static int setup_stereo_to_mono_input_remix(struct j6_stream_in *in) | ||
281 | { | ||
282 | ALOGV("setup_stereo_to_mono_input_remix() stream=%p", in); | ||
283 | |||
284 | struct buffer_remix *br = (struct buffer_remix *)calloc(1, sizeof(struct buffer_remix)); | ||
285 | if (!br) | ||
286 | return -ENOMEM; | ||
287 | |||
288 | br->remix_func = remove_channels_from_buf; | ||
289 | br->sample_size = sizeof(int16_t); | ||
290 | br->in_chans = 2; | ||
291 | br->out_chans = 1; | ||
292 | in->remix = br; | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * Implementation of buffer_remix::remix_func that duplicates the first | ||
299 | * channel into the rest of channels in the frame without doing any other | ||
300 | * processing | ||
301 | */ | ||
302 | static void duplicate_channels_from_mono(struct buffer_remix *data, void *buf, size_t frames) | ||
303 | { | ||
304 | int samp_size, in_frame, out_frame; | ||
305 | int N, c; | ||
306 | char *s, *d; | ||
307 | |||
308 | ALOGVV("duplicate_channels_from_mono() remix=%p buf=%p frames=%u", | ||
309 | data, buf, frames); | ||
310 | |||
311 | if (frames == 0) | ||
312 | return; | ||
313 | |||
314 | samp_size = data->sample_size; | ||
315 | in_frame = data->in_chans * samp_size; | ||
316 | out_frame = data->out_chans * samp_size; | ||
317 | |||
318 | if (in_frame >= out_frame) { | ||
319 | ALOGE("BUG: duplicate_channels_from_mono() can not drop channels\n"); | ||
320 | return; | ||
321 | } | ||
322 | |||
323 | N = frames - 1; | ||
324 | d = (char*)buf + N * out_frame; | ||
325 | s = (char*)buf + N * in_frame; | ||
326 | |||
327 | /* duplicate first channel into the rest of channels in the frame */ | ||
328 | while (N-- >= 0) { | ||
329 | for (c = 0; c < out_frame; ++c) | ||
330 | d[c] = s[c % in_frame]; | ||
331 | d -= out_frame; | ||
332 | s -= in_frame; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | static int setup_voice_remix(struct j6_voice_stream *stream) | ||
337 | { | ||
338 | ALOGVV("setup_voice_mono_to_stereo_input_remix() %s stream", stream->name); | ||
339 | |||
340 | struct buffer_remix *br = (struct buffer_remix *)calloc(1, sizeof(struct buffer_remix)); | ||
341 | if (!br) | ||
342 | return -ENOMEM; | ||
343 | |||
344 | br->remix_func = duplicate_channels_from_mono; | ||
345 | br->sample_size = sizeof(int16_t); | ||
346 | br->in_chans = stream->in_config.channels; | ||
347 | br->out_chans = stream->out_config.channels; | ||
348 | |||
349 | if (br->in_chans == 1) | ||
350 | br->remix_func = duplicate_channels_from_mono; | ||
351 | else if (br->out_chans == 1) | ||
352 | br->remix_func = remove_channels_from_buf; | ||
353 | else | ||
354 | return -ENOTSUP; | ||
355 | |||
356 | stream->remix = br; | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static int voice_get_next_buffer(struct resampler_buffer_provider *buffer_provider, | ||
362 | struct resampler_buffer* buffer) | ||
363 | { | ||
364 | struct j6_voice_stream *stream; | ||
365 | int ret; | ||
366 | |||
367 | if (buffer_provider == NULL || buffer == NULL) { | ||
368 | ALOGE("voice_get_next_buffer() invalid buffer/provider"); | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | stream = (struct j6_voice_stream *)((char *)buffer_provider - | ||
373 | offsetof(struct j6_voice_stream, buf_provider)); | ||
374 | |||
375 | if (stream->pcm_in == NULL) { | ||
376 | buffer->raw = NULL; | ||
377 | buffer->frame_count = 0; | ||
378 | return -ENODEV; | ||
379 | } | ||
380 | |||
381 | if (buffer->frame_count > stream->in_frames) { | ||
382 | ALOGW("voice_get_next_buffer() %s unexpected frame count %u, " | ||
383 | "buffer was allocated for %u frames", | ||
384 | stream->name, buffer->frame_count, stream->in_frames); | ||
385 | buffer->frame_count = stream->in_frames; | ||
386 | } | ||
387 | |||
388 | ret = pcm_read(stream->pcm_in, stream->in_buffer, | ||
389 | buffer->frame_count * stream->in_frame_size); | ||
390 | if (ret) { | ||
391 | ALOGE("voice_get_next_buffer() failed to read %s: %s", | ||
392 | stream->name, pcm_get_error(stream->pcm_in)); | ||
393 | buffer->raw = NULL; | ||
394 | buffer->frame_count = 0; | ||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | buffer->i16 = stream->in_buffer; | ||
399 | |||
400 | return ret; | ||
401 | } | ||
402 | |||
403 | static void voice_release_buffer(struct resampler_buffer_provider *buffer_provider, | ||
404 | struct resampler_buffer* buffer) | ||
405 | { | ||
406 | UNUSED(buffer_provider); | ||
407 | UNUSED(buffer); | ||
408 | } | ||
409 | |||
410 | static void *voice_thread_func(void *arg) | ||
411 | { | ||
412 | struct j6_voice_stream *stream = (struct j6_voice_stream *)arg; | ||
413 | struct j6_audio_device *adev = stream->dev; | ||
414 | size_t frames = stream->out_frames; | ||
415 | uint32_t periods = 0; | ||
416 | uint32_t avail; | ||
417 | bool in_steady = false; | ||
418 | bool out_steady = false; | ||
419 | int ret = 0; | ||
420 | |||
421 | pcm_start(stream->pcm_in); | ||
422 | |||
423 | memset(stream->out_buffer, 0, stream->out_frames * stream->out_frame_size); | ||
424 | |||
425 | while (adev->in_call) { | ||
426 | if (out_steady) { | ||
427 | if (in_steady) { | ||
428 | stream->resampler->resample_from_provider(stream->resampler, | ||
429 | stream->out_buffer, | ||
430 | &frames); | ||
431 | } else { | ||
432 | avail = pcm_avail_update(stream->pcm_in); | ||
433 | if (avail > 0) { | ||
434 | in_steady = true; | ||
435 | continue; | ||
436 | } | ||
437 | } | ||
438 | } else if (++periods == stream->out_config.period_count) { | ||
439 | out_steady = true; | ||
440 | } | ||
441 | |||
442 | if (stream->remix) | ||
443 | stream->remix->remix_func(stream->remix, stream->out_buffer, frames); | ||
444 | |||
445 | ret = pcm_write(stream->pcm_out, stream->out_buffer, | ||
446 | frames * stream->out_frame_size); | ||
447 | if (ret) { | ||
448 | ALOGE("voice_thread_func() failed to write %s: %s", | ||
449 | stream->name, pcm_get_error(stream->pcm_out)); | ||
450 | usleep((frames * 1000000) / stream->out_config.rate); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | return (void*)ret; | ||
455 | } | ||
456 | |||
457 | static void voice_stream_exit(struct j6_voice_stream *stream) | ||
458 | { | ||
459 | if (stream->resampler) { | ||
460 | release_resampler(stream->resampler); | ||
461 | stream->resampler = NULL; | ||
462 | } | ||
463 | |||
464 | if (stream->pcm_out) { | ||
465 | pcm_close(stream->pcm_out); | ||
466 | stream->pcm_out = NULL; | ||
467 | } | ||
468 | |||
469 | if (stream->pcm_in) { | ||
470 | pcm_close(stream->pcm_in); | ||
471 | stream->pcm_in = NULL; | ||
472 | } | ||
473 | |||
474 | if (stream->in_buffer) { | ||
475 | free(stream->in_buffer); | ||
476 | stream->in_buffer = NULL; | ||
477 | stream->in_frames = 0; | ||
478 | } | ||
479 | |||
480 | if (stream->out_buffer) { | ||
481 | free(stream->out_buffer); | ||
482 | stream->out_buffer = NULL; | ||
483 | stream->out_frames = 0; | ||
484 | } | ||
485 | |||
486 | if (stream->remix) { | ||
487 | free(stream->remix); | ||
488 | stream->remix = NULL; | ||
489 | } | ||
490 | |||
491 | if (stream->name) { | ||
492 | free(stream->name); | ||
493 | stream->name = NULL; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | static int voice_stream_init(struct j6_voice_stream *stream, | ||
498 | unsigned int in_card, | ||
499 | unsigned int in_port, | ||
500 | unsigned int out_card, | ||
501 | unsigned int out_port) | ||
502 | { | ||
503 | struct j6_audio_device *adev = stream->dev; | ||
504 | int frames; | ||
505 | int ret; | ||
506 | |||
507 | stream->buf_provider.get_next_buffer = voice_get_next_buffer; | ||
508 | stream->buf_provider.release_buffer = voice_release_buffer; | ||
509 | ret = create_resampler(stream->in_config.rate, | ||
510 | stream->out_config.rate, | ||
511 | stream->in_config.channels, | ||
512 | RESAMPLER_QUALITY_DEFAULT, | ||
513 | &stream->buf_provider, | ||
514 | &stream->resampler); | ||
515 | if (ret) { | ||
516 | ALOGE("voice_stream_init() failed to create %s resampler %d", stream->name, ret); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | stream->pcm_in = pcm_open(in_card, in_port, | ||
521 | PCM_IN | PCM_MONOTONIC, | ||
522 | &stream->in_config); | ||
523 | stream->pcm_out = pcm_open(out_card, out_port, | ||
524 | PCM_OUT | PCM_MONOTONIC, | ||
525 | &stream->out_config); | ||
526 | |||
527 | if (!pcm_is_ready(stream->pcm_in) || !pcm_is_ready(stream->pcm_out)) { | ||
528 | ALOGE("voice_stream_init() failed to open pcm %s devices", stream->name); | ||
529 | voice_stream_exit(stream); | ||
530 | return -ENODEV; | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Choose the largest frame size since mono-to-stereo and | ||
535 | * stereo-to-mono conversion happens in place | ||
536 | */ | ||
537 | stream->in_frame_size = pcm_frames_to_bytes(stream->pcm_in, 1); | ||
538 | stream->out_frame_size = pcm_frames_to_bytes(stream->pcm_out, 1); | ||
539 | frames = (stream->in_frame_size > stream->out_frame_size) ? | ||
540 | stream->in_frame_size : stream->out_frame_size; | ||
541 | |||
542 | /* out_buffer will store the resampled data */ | ||
543 | stream->out_frames = stream->out_config.period_size; | ||
544 | stream->out_buffer = malloc(stream->out_frames * frames); | ||
545 | |||
546 | /* in_buffer will store the frames recorded from the PCM device */ | ||
547 | stream->in_frames = (stream->out_frames * stream->in_config.rate) / stream->out_config.rate + | ||
548 | RESAMPLER_HEADROOM_FRAMES; | ||
549 | stream->in_buffer = malloc(stream->in_frames * frames); | ||
550 | |||
551 | if (!stream->in_buffer || !stream->out_buffer) { | ||
552 | ALOGE("voice_stream_init() failed to allocate %s buffers", stream->name); | ||
553 | voice_stream_exit(stream); | ||
554 | return -ENOMEM; | ||
555 | } | ||
556 | |||
557 | ret = setup_voice_remix(stream); | ||
558 | if (ret) { | ||
559 | ALOGE("voice_stream_init() failed to setup remix %d", ret); | ||
560 | voice_stream_exit(stream); | ||
561 | } | ||
562 | |||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | static int enter_voice_call(struct j6_audio_device *adev) | ||
567 | { | ||
568 | struct j6_voice *voice = &adev->voice; | ||
569 | int ret; | ||
570 | |||
571 | ALOGI("enter_voice_call() entering bluetooth voice call"); | ||
572 | |||
573 | /* Let the primary output switch to a dummy sink */ | ||
574 | if (adev->out) | ||
575 | do_out_standby(adev->out); | ||
576 | |||
577 | /* Uplink: Mic (44.1kHz) -> BT (8kHz) */ | ||
578 | voice->ul.name = strdup("UL"); | ||
579 | voice->ul.in_config = pcm_config_capture; | ||
580 | voice->ul.out_config = pcm_config_bt_out; | ||
581 | voice->ul.dev = adev; | ||
582 | ret = voice_stream_init(&voice->ul, adev->card, adev->in_port, | ||
583 | adev->bt_card, adev->bt_port); | ||
584 | if (ret) { | ||
585 | ALOGE("enter_voice_call() failed to init uplink %d", ret); | ||
586 | return ret; | ||
587 | } | ||
588 | |||
589 | /* Downlink: BT (8kHz) -> HP/Spk (44.1kHz) */ | ||
590 | voice->dl.name = strdup("DL"); | ||
591 | voice->dl.in_config = pcm_config_bt_in; | ||
592 | voice->dl.out_config = pcm_config_playback; | ||
593 | voice->dl.dev = adev; | ||
594 | ret = voice_stream_init(&voice->dl, adev->bt_card, adev->bt_port, | ||
595 | adev->card, adev->out_port); | ||
596 | if (ret) { | ||
597 | ALOGE("enter_voice_call() failed to init downlink %d", ret); | ||
598 | goto err_dl_init; | ||
599 | } | ||
600 | |||
601 | adev->in_call = true; | ||
602 | |||
603 | /* Create uplink thread: Mic -> BT */ | ||
604 | ret = pthread_create(&voice->ul.thread, NULL, voice_thread_func, &voice->ul); | ||
605 | if (ret) { | ||
606 | ALOGE("enter_voice_call() failed to create uplink thread %d", ret); | ||
607 | adev->in_call = false; | ||
608 | goto err_ul_thread; | ||
609 | } | ||
610 | |||
611 | /* Create downlink thread: BT -> HP/Spk */ | ||
612 | ret = pthread_create(&voice->dl.thread, NULL, voice_thread_func, &voice->dl); | ||
613 | if (ret) { | ||
614 | ALOGE("enter_voice_call() failed to create downlink thread %d", ret); | ||
615 | adev->in_call = false; | ||
616 | goto err_dl_thread; | ||
617 | } | ||
618 | |||
619 | return 0; | ||
620 | |||
621 | err_dl_thread: | ||
622 | pthread_join(voice->ul.thread, NULL); | ||
623 | err_ul_thread: | ||
624 | voice_stream_exit(&voice->ul); | ||
625 | err_dl_init: | ||
626 | voice_stream_exit(&voice->dl); | ||
627 | |||
628 | return ret; | ||
629 | } | ||
630 | |||
631 | static void leave_voice_call(struct j6_audio_device *adev) | ||
632 | { | ||
633 | struct j6_voice *voice = &adev->voice; | ||
634 | struct j6_voice_stream *ul = &voice->ul; | ||
635 | struct j6_voice_stream *dl = &voice->dl; | ||
636 | void *ret; | ||
637 | |||
638 | ALOGI("leave_voice_call() leaving bluetooth voice call"); | ||
639 | |||
640 | adev->in_call = false; | ||
641 | |||
642 | if (ul->pcm_out) | ||
643 | pcm_stop(ul->pcm_out); | ||
644 | if (dl->pcm_in) | ||
645 | pcm_stop(dl->pcm_in); | ||
646 | |||
647 | pthread_join(voice->dl.thread, &ret); | ||
648 | pthread_join(voice->ul.thread, &ret); | ||
649 | |||
650 | voice_stream_exit(&voice->dl); | ||
651 | voice_stream_exit(&voice->ul); | ||
652 | |||
653 | /* Let the primary output switch back to its ALSA PCM device */ | ||
654 | if (adev->out) | ||
655 | do_out_standby(adev->out); | ||
656 | } | ||
657 | |||
658 | static uint32_t time_diff(struct timespec t1, struct timespec t0) | ||
659 | { | ||
660 | struct timespec temp; | ||
661 | |||
662 | if ((t1.tv_nsec - t0.tv_nsec) < 0) { | ||
663 | temp.tv_sec = t1.tv_sec - t0.tv_sec-1; | ||
664 | temp.tv_nsec = 1000000000UL + t1.tv_nsec - t0.tv_nsec; | ||
665 | } else { | ||
666 | temp.tv_sec = t1.tv_sec - t0.tv_sec; | ||
667 | temp.tv_nsec = t1.tv_nsec - t0.tv_nsec; | ||
668 | } | ||
669 | |||
670 | return (temp.tv_sec * 1000000UL + temp.tv_nsec / 1000); | ||
671 | } | ||
672 | |||
673 | /* audio HAL functions */ | ||
674 | |||
675 | static uint32_t out_get_sample_rate(const struct audio_stream *stream) | ||
676 | { | ||
677 | uint32_t rate = PLAYBACK_SAMPLE_RATE; | ||
678 | |||
679 | UNUSED(stream); | ||
680 | ALOGVV("out_get_sample_rate() stream=%p rate=%u", stream, rate); | ||
681 | |||
682 | return rate; | ||
683 | } | ||
684 | |||
685 | static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) | ||
686 | { | ||
687 | UNUSED(stream); | ||
688 | UNUSED(rate); | ||
689 | ALOGVV("out_set_sample_rate() stream=%p rate=%u", stream, rate); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static size_t out_get_buffer_size(const struct audio_stream *stream) | ||
695 | { | ||
696 | const struct audio_stream_out *s = (const struct audio_stream_out *)stream; | ||
697 | uint32_t frames = ((PLAYBACK_PERIOD_SIZE + 15) / 16) * 16; | ||
698 | size_t bytes = frames * audio_stream_out_frame_size(s); | ||
699 | |||
700 | ALOGVV("out_get_buffer_size() stream=%p frames=%u bytes=%u", stream, frames, bytes); | ||
701 | |||
702 | return bytes; | ||
703 | } | ||
704 | |||
705 | static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) | ||
706 | { | ||
707 | audio_channel_mask_t channels = AUDIO_CHANNEL_OUT_STEREO; | ||
708 | |||
709 | UNUSED(stream); | ||
710 | ALOGVV("out_get_channels() stream=%p channels=%u", stream, popcount(channels)); | ||
711 | |||
712 | return channels; | ||
713 | } | ||
714 | |||
715 | static audio_format_t out_get_format(const struct audio_stream *stream) | ||
716 | { | ||
717 | audio_format_t format = AUDIO_FORMAT_PCM_16_BIT; | ||
718 | |||
719 | UNUSED(stream); | ||
720 | ALOGVV("out_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
721 | stream, format, audio_bytes_per_sample(format) << 3); | ||
722 | |||
723 | return format; | ||
724 | } | ||
725 | |||
726 | static int out_set_format(struct audio_stream *stream, audio_format_t format) | ||
727 | { | ||
728 | UNUSED(stream); | ||
729 | ALOGVV("out_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
730 | stream, format, audio_bytes_per_sample(format) << 3); | ||
731 | |||
732 | if (format != AUDIO_FORMAT_PCM_16_BIT) { | ||
733 | return -ENOSYS; | ||
734 | } else { | ||
735 | return 0; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | /* must be called with locks held */ | ||
740 | static void do_out_standby(struct j6_stream_out *out) | ||
741 | { | ||
742 | struct j6_audio_device *adev = out->dev; | ||
743 | |||
744 | if (!out->standby) { | ||
745 | if (adev->mode != AUDIO_MODE_IN_CALL) { | ||
746 | ALOGI("do_out_standby() close card %u port %u", adev->card, adev->out_port); | ||
747 | pcm_close(out->pcm); | ||
748 | out->pcm = NULL; | ||
749 | } else { | ||
750 | ALOGI("do_out_standby() close dummy card"); | ||
751 | } | ||
752 | out->standby = true; | ||
753 | } | ||
754 | } | ||
755 | |||
756 | static int out_standby(struct audio_stream *stream) | ||
757 | { | ||
758 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
759 | struct j6_audio_device *adev = out->dev; | ||
760 | |||
761 | ALOGV("out_standby() stream=%p", out); | ||
762 | pthread_mutex_lock(&adev->lock); | ||
763 | pthread_mutex_lock(&out->lock); | ||
764 | do_out_standby(out); | ||
765 | pthread_mutex_unlock(&out->lock); | ||
766 | pthread_mutex_unlock(&adev->lock); | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int out_dump(const struct audio_stream *stream, int fd) | ||
772 | { | ||
773 | UNUSED(stream); | ||
774 | UNUSED(fd); | ||
775 | |||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) | ||
780 | { | ||
781 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
782 | struct j6_audio_device *adev = out->dev; | ||
783 | struct str_parms *parms; | ||
784 | char value[32]; | ||
785 | int ret; | ||
786 | uint32_t val = 0; | ||
787 | |||
788 | ALOGV("out_set_parameters() stream=%p parameter='%s'", out, kvpairs); | ||
789 | |||
790 | parms = str_parms_create_str(kvpairs); | ||
791 | |||
792 | ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); | ||
793 | if (ret >= 0) { | ||
794 | val = atoi(value); | ||
795 | pthread_mutex_lock(&adev->lock); | ||
796 | pthread_mutex_lock(&out->lock); | ||
797 | if (val != 0) { | ||
798 | if ((adev->out_device & AUDIO_DEVICE_OUT_ALL) != val) | ||
799 | do_out_standby(out); | ||
800 | |||
801 | /* set the active output device */ | ||
802 | adev->out_device = val; | ||
803 | select_output_device(adev); | ||
804 | } | ||
805 | pthread_mutex_unlock(&out->lock); | ||
806 | pthread_mutex_unlock(&adev->lock); | ||
807 | } | ||
808 | |||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | static char* out_get_parameters(const struct audio_stream *stream, const char *keys) | ||
813 | { | ||
814 | UNUSED(stream); | ||
815 | UNUSED(keys); | ||
816 | |||
817 | return strdup(""); | ||
818 | } | ||
819 | |||
820 | static uint32_t out_get_latency(const struct audio_stream_out *stream) | ||
821 | { | ||
822 | const struct j6_stream_out *out = (const struct j6_stream_out *)(stream); | ||
823 | uint32_t frames = PLAYBACK_BUFFER_SIZE; | ||
824 | uint32_t latency = (frames * 1000) / PLAYBACK_SAMPLE_RATE; | ||
825 | |||
826 | ALOGVV("out_get_latency() stream=%p latency=%u msecs", out, latency); | ||
827 | |||
828 | return latency; | ||
829 | } | ||
830 | |||
831 | static int out_set_volume(struct audio_stream_out *stream, float left, | ||
832 | float right) | ||
833 | { | ||
834 | UNUSED(stream); | ||
835 | UNUSED(left); | ||
836 | UNUSED(right); | ||
837 | |||
838 | return -ENOSYS; | ||
839 | } | ||
840 | |||
841 | static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, | ||
842 | size_t bytes) | ||
843 | { | ||
844 | const struct audio_stream_out *s = (const struct audio_stream_out *)stream; | ||
845 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
846 | struct j6_audio_device *adev = out->dev; | ||
847 | struct timespec now; | ||
848 | const size_t frame_size = audio_stream_out_frame_size(s); | ||
849 | const size_t frames = bytes / frame_size; | ||
850 | uint32_t rate = out->config.rate; | ||
851 | uint32_t write_usecs = frames * 1000000 / rate; | ||
852 | uint32_t diff_usecs; | ||
853 | int ret = 0; | ||
854 | |||
855 | ALOGVV("out_write() stream=%p buffer=%p size=%u/%u time=%u usecs", | ||
856 | out, buffer, frames, rate, write_usecs); | ||
857 | |||
858 | pthread_mutex_lock(&adev->lock); | ||
859 | pthread_mutex_lock(&out->lock); | ||
860 | |||
861 | if (out->standby) { | ||
862 | if (!adev->in_call) { | ||
863 | select_output_device(adev); | ||
864 | |||
865 | ALOGI("out_write() open card %u port %u", adev->card, adev->out_port); | ||
866 | out->pcm = pcm_open(adev->card, adev->out_port, | ||
867 | PCM_OUT | PCM_MONOTONIC, | ||
868 | &out->config); | ||
869 | if (!pcm_is_ready(out->pcm)) { | ||
870 | ALOGE("out_write() failed to open pcm out: %s", pcm_get_error(out->pcm)); | ||
871 | pcm_close(out->pcm); | ||
872 | out->pcm = NULL; | ||
873 | ret = -ENODEV; | ||
874 | } | ||
875 | } else { | ||
876 | ALOGI("out_write() open dummy port"); | ||
877 | clock_gettime(CLOCK_REALTIME, &out->last); | ||
878 | } | ||
879 | |||
880 | if (ret) { | ||
881 | usleep(write_usecs); /* limits the rate of error messages */ | ||
882 | pthread_mutex_unlock(&out->lock); | ||
883 | pthread_mutex_unlock(&adev->lock); | ||
884 | return ret; | ||
885 | } | ||
886 | |||
887 | out->standby = false; | ||
888 | } | ||
889 | |||
890 | pthread_mutex_unlock(&adev->lock); | ||
891 | |||
892 | if (!adev->in_call) { | ||
893 | ret = pcm_write(out->pcm, buffer, bytes); | ||
894 | if (ret) { | ||
895 | ALOGE("out_write() failed to write audio data %d", ret); | ||
896 | usleep(write_usecs); /* limits the rate of error messages */ | ||
897 | } | ||
898 | } else { | ||
899 | clock_gettime(CLOCK_REALTIME, &now); | ||
900 | diff_usecs = time_diff(now, out->last); | ||
901 | if (write_usecs > diff_usecs) | ||
902 | usleep(write_usecs - diff_usecs); | ||
903 | |||
904 | clock_gettime(CLOCK_REALTIME, &out->last); | ||
905 | } | ||
906 | |||
907 | out->written += frames; | ||
908 | |||
909 | pthread_mutex_unlock(&out->lock); | ||
910 | |||
911 | return bytes; | ||
912 | } | ||
913 | |||
914 | static int out_get_render_position(const struct audio_stream_out *stream, | ||
915 | uint32_t *dsp_frames) | ||
916 | { | ||
917 | UNUSED(stream); | ||
918 | UNUSED(dsp_frames); | ||
919 | |||
920 | return -EINVAL; | ||
921 | } | ||
922 | |||
923 | static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
924 | { | ||
925 | UNUSED(stream); | ||
926 | UNUSED(effect); | ||
927 | |||
928 | return 0; | ||
929 | } | ||
930 | |||
931 | static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
932 | { | ||
933 | UNUSED(stream); | ||
934 | UNUSED(effect); | ||
935 | |||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | static int out_get_next_write_timestamp(const struct audio_stream_out *stream, | ||
940 | int64_t *timestamp) | ||
941 | { | ||
942 | UNUSED(stream); | ||
943 | UNUSED(timestamp); | ||
944 | |||
945 | return -EINVAL; | ||
946 | } | ||
947 | |||
948 | static int out_get_presentation_position(const struct audio_stream_out *stream, | ||
949 | uint64_t *frames, struct timespec *timestamp) | ||
950 | { | ||
951 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
952 | struct j6_audio_device *adev = out->dev; | ||
953 | int64_t signed_frames = -1; | ||
954 | size_t avail; | ||
955 | int ret = -1; | ||
956 | |||
957 | pthread_mutex_lock(&out->lock); | ||
958 | |||
959 | if (!adev->in_call) { | ||
960 | if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { | ||
961 | signed_frames = out->written - pcm_get_buffer_size(out->pcm) + avail; | ||
962 | } | ||
963 | } else { | ||
964 | clock_gettime(CLOCK_MONOTONIC, timestamp); | ||
965 | signed_frames = out->written + | ||
966 | (time_diff(*timestamp, out->last) * out->config.rate) / 1000000; | ||
967 | } | ||
968 | |||
969 | /* It would be unusual for this value to be negative, but check just in case ... */ | ||
970 | if (signed_frames >= 0) { | ||
971 | *frames = signed_frames; | ||
972 | ret = 0; | ||
973 | } | ||
974 | |||
975 | pthread_mutex_unlock(&out->lock); | ||
976 | |||
977 | return ret; | ||
978 | } | ||
979 | |||
980 | /** audio_stream_in implementation **/ | ||
981 | static uint32_t in_get_sample_rate(const struct audio_stream *stream) | ||
982 | { | ||
983 | const struct j6_stream_in *in = (const struct j6_stream_in *)(stream); | ||
984 | |||
985 | ALOGVV("in_get_sample_rate() stream=%p rate=%u", stream, in->requested_rate); | ||
986 | |||
987 | return in->requested_rate; | ||
988 | } | ||
989 | |||
990 | static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) | ||
991 | { | ||
992 | ALOGV("in_set_sample_rate() stream=%p rate=%u", stream, rate); | ||
993 | |||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static size_t in_get_buffer_size(const struct audio_stream *stream) | ||
998 | { | ||
999 | const struct j6_stream_in *in = (const struct j6_stream_in *)(stream); | ||
1000 | |||
1001 | size_t bytes = get_input_buffer_size(in->requested_rate, | ||
1002 | AUDIO_FORMAT_PCM_16_BIT, | ||
1003 | in->requested_channels); | ||
1004 | |||
1005 | ALOGVV("in_get_buffer_size() stream=%p bytes=%u", in, bytes); | ||
1006 | |||
1007 | return bytes; | ||
1008 | } | ||
1009 | |||
1010 | static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) | ||
1011 | { | ||
1012 | const struct j6_stream_in *in = (const struct j6_stream_in *)(stream); | ||
1013 | audio_channel_mask_t channels = audio_channel_in_mask_from_count(in->requested_channels); | ||
1014 | |||
1015 | ALOGVV("in_get_channels() stream=%p channels=%u", in, in->requested_channels); | ||
1016 | |||
1017 | return channels; | ||
1018 | } | ||
1019 | |||
1020 | static audio_format_t in_get_format(const struct audio_stream *stream) | ||
1021 | { | ||
1022 | audio_format_t format = AUDIO_FORMAT_PCM_16_BIT; | ||
1023 | |||
1024 | UNUSED(stream); | ||
1025 | ALOGVV("in_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
1026 | stream, format, audio_bytes_per_sample(format) << 3); | ||
1027 | |||
1028 | return format; | ||
1029 | } | ||
1030 | |||
1031 | static int in_set_format(struct audio_stream *stream, audio_format_t format) | ||
1032 | { | ||
1033 | UNUSED(stream); | ||
1034 | ALOGV("in_set_format() stream=%p format=0x%08x (%u bits/sample)", | ||
1035 | stream, format, audio_bytes_per_sample(format) << 3); | ||
1036 | |||
1037 | if (format != AUDIO_FORMAT_PCM_16_BIT) { | ||
1038 | return -ENOSYS; | ||
1039 | } else { | ||
1040 | return 0; | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | /* must be called with locks held */ | ||
1045 | static void do_in_standby(struct j6_stream_in *in) | ||
1046 | { | ||
1047 | struct j6_audio_device *adev = in->dev; | ||
1048 | |||
1049 | if (!in->standby) { | ||
1050 | ALOGI("do_in_standby() close card %u port %u", adev->card, adev->out_port); | ||
1051 | pcm_close(in->pcm); | ||
1052 | in->pcm = NULL; | ||
1053 | in->standby = true; | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | static int in_standby(struct audio_stream *stream) | ||
1058 | { | ||
1059 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1060 | struct j6_audio_device *adev = in->dev; | ||
1061 | |||
1062 | ALOGV("in_standby() stream=%p", in); | ||
1063 | pthread_mutex_lock(&adev->lock); | ||
1064 | pthread_mutex_lock(&in->lock); | ||
1065 | do_in_standby(in); | ||
1066 | pthread_mutex_unlock(&in->lock); | ||
1067 | pthread_mutex_unlock(&adev->lock); | ||
1068 | |||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | static int in_dump(const struct audio_stream *stream, int fd) | ||
1073 | { | ||
1074 | UNUSED(stream); | ||
1075 | UNUSED(fd); | ||
1076 | |||
1077 | return 0; | ||
1078 | } | ||
1079 | |||
1080 | static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) | ||
1081 | { | ||
1082 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1083 | struct j6_audio_device *adev = in->dev; | ||
1084 | struct str_parms *parms; | ||
1085 | char value[32]; | ||
1086 | int ret; | ||
1087 | uint32_t val = 0; | ||
1088 | |||
1089 | ALOGV("in_set_parameters() stream=%p parameter='%s'", stream, kvpairs); | ||
1090 | |||
1091 | parms = str_parms_create_str(kvpairs); | ||
1092 | |||
1093 | /* Nothing to do for AUDIO_PARAMETER_STREAM_INPUT_SOURCE, so it's ignored */ | ||
1094 | |||
1095 | ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); | ||
1096 | if (ret >= 0) { | ||
1097 | val = atoi(value); | ||
1098 | pthread_mutex_lock(&adev->lock); | ||
1099 | pthread_mutex_lock(&in->lock); | ||
1100 | if (val != 0) { | ||
1101 | if ((adev->in_device & AUDIO_DEVICE_IN_ALL) != val) | ||
1102 | do_in_standby(in); | ||
1103 | |||
1104 | /* set the active input device */ | ||
1105 | adev->in_device = val; | ||
1106 | select_input_device(adev); | ||
1107 | } | ||
1108 | pthread_mutex_unlock(&in->lock); | ||
1109 | pthread_mutex_unlock(&adev->lock); | ||
1110 | } | ||
1111 | |||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | static char * in_get_parameters(const struct audio_stream *stream, | ||
1116 | const char *keys) | ||
1117 | { | ||
1118 | UNUSED(stream); | ||
1119 | UNUSED(keys); | ||
1120 | |||
1121 | return strdup(""); | ||
1122 | } | ||
1123 | |||
1124 | static int in_set_gain(struct audio_stream_in *stream, float gain) | ||
1125 | { | ||
1126 | UNUSED(stream); | ||
1127 | UNUSED(gain); | ||
1128 | |||
1129 | return 0; | ||
1130 | } | ||
1131 | |||
1132 | static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, | ||
1133 | struct resampler_buffer* buffer) | ||
1134 | { | ||
1135 | struct j6_stream_in *in; | ||
1136 | struct buffer_remix *remix; | ||
1137 | |||
1138 | if (buffer_provider == NULL || buffer == NULL) | ||
1139 | return -EINVAL; | ||
1140 | |||
1141 | in = (struct j6_stream_in *)((char *)buffer_provider - | ||
1142 | offsetof(struct j6_stream_in, buf_provider)); | ||
1143 | |||
1144 | if (in->pcm == NULL) { | ||
1145 | buffer->raw = NULL; | ||
1146 | buffer->frame_count = 0; | ||
1147 | in->read_status = -ENODEV; | ||
1148 | return -ENODEV; | ||
1149 | } | ||
1150 | |||
1151 | if (in->frames_in == 0) { | ||
1152 | in->read_status = pcm_read(in->pcm, | ||
1153 | (void*)in->buffer, | ||
1154 | buffer->frame_count * in->hw_frame_size); | ||
1155 | if (in->read_status != 0) { | ||
1156 | ALOGE("get_next_buffer() pcm_read error %d", in->read_status); | ||
1157 | buffer->raw = NULL; | ||
1158 | buffer->frame_count = 0; | ||
1159 | return in->read_status; | ||
1160 | } | ||
1161 | in->frames_in = buffer->frame_count; | ||
1162 | |||
1163 | remix = in->remix; | ||
1164 | if (remix) | ||
1165 | remix->remix_func(remix, in->buffer, in->frames_in); | ||
1166 | } | ||
1167 | |||
1168 | buffer->frame_count = (buffer->frame_count > in->frames_in) ? | ||
1169 | in->frames_in : buffer->frame_count; | ||
1170 | buffer->i16 = in->buffer; | ||
1171 | |||
1172 | return in->read_status; | ||
1173 | } | ||
1174 | |||
1175 | static void release_buffer(struct resampler_buffer_provider *buffer_provider, | ||
1176 | struct resampler_buffer* buffer) | ||
1177 | { | ||
1178 | struct j6_stream_in *in; | ||
1179 | |||
1180 | if (buffer_provider == NULL || buffer == NULL) | ||
1181 | return; | ||
1182 | |||
1183 | in = (struct j6_stream_in *)((char *)buffer_provider - | ||
1184 | offsetof(struct j6_stream_in, buf_provider)); | ||
1185 | |||
1186 | in->frames_in -= buffer->frame_count; | ||
1187 | } | ||
1188 | |||
1189 | /* | ||
1190 | * read_frames() reads frames from kernel driver, down samples to capture rate | ||
1191 | * if necessary and output the number of frames requested to the buffer specified | ||
1192 | */ | ||
1193 | static ssize_t read_frames(struct j6_stream_in *in, void *buffer, ssize_t frames) | ||
1194 | { | ||
1195 | const struct audio_stream_in *s = (const struct audio_stream_in *)in; | ||
1196 | ssize_t frames_wr = 0; | ||
1197 | size_t frame_size; | ||
1198 | |||
1199 | ALOGVV("read_frames() stream=%p frames=%u", in, frames); | ||
1200 | |||
1201 | if (in->remix) | ||
1202 | frame_size = audio_stream_in_frame_size(s); | ||
1203 | else | ||
1204 | frame_size = in->hw_frame_size; | ||
1205 | |||
1206 | while (frames_wr < frames) { | ||
1207 | size_t frames_rd = frames - frames_wr; | ||
1208 | |||
1209 | if (in->resampler) { | ||
1210 | in->resampler->resample_from_provider(in->resampler, | ||
1211 | (int16_t *)((char *)buffer + frames_wr * frame_size), | ||
1212 | &frames_rd); | ||
1213 | } else { | ||
1214 | struct resampler_buffer buf = { | ||
1215 | { raw : NULL, }, | ||
1216 | frame_count : frames_rd, | ||
1217 | }; | ||
1218 | get_next_buffer(&in->buf_provider, &buf); | ||
1219 | if (buf.raw) { | ||
1220 | memcpy((char *)buffer + frames_wr * frame_size, | ||
1221 | buf.raw, | ||
1222 | buf.frame_count * frame_size); | ||
1223 | frames_rd = buf.frame_count; | ||
1224 | } | ||
1225 | release_buffer(&in->buf_provider, &buf); | ||
1226 | } | ||
1227 | |||
1228 | /* in->read_status is updated by getNextBuffer() also called by | ||
1229 | * in->resampler->resample_from_provider() */ | ||
1230 | if (in->read_status != 0) | ||
1231 | return in->read_status; | ||
1232 | |||
1233 | frames_wr += frames_rd; | ||
1234 | } | ||
1235 | |||
1236 | return frames_wr; | ||
1237 | } | ||
1238 | |||
1239 | static ssize_t in_read(struct audio_stream_in *stream, void* buffer, | ||
1240 | size_t bytes) | ||
1241 | { | ||
1242 | const struct audio_stream_in *s = (const struct audio_stream_in *)stream; | ||
1243 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1244 | struct j6_audio_device *adev = in->dev; | ||
1245 | const size_t frame_size = audio_stream_in_frame_size(stream); | ||
1246 | const size_t frames = bytes / frame_size; | ||
1247 | uint32_t rate = in_get_sample_rate(&stream->common); | ||
1248 | uint32_t read_usecs = frames * 1000000 / rate; | ||
1249 | int ret; | ||
1250 | |||
1251 | ALOGVV("in_read() stream=%p buffer=%p size=%u/%u time=%u usecs", | ||
1252 | stream, buffer, frames, rate, read_usecs); | ||
1253 | |||
1254 | pthread_mutex_lock(&adev->lock); | ||
1255 | pthread_mutex_lock(&in->lock); | ||
1256 | |||
1257 | if (in->standby) { | ||
1258 | select_input_device(adev); | ||
1259 | |||
1260 | ALOGI("in_read() open card %u port %u", adev->card, adev->in_port); | ||
1261 | in->pcm = pcm_open(adev->card, adev->in_port, | ||
1262 | PCM_IN | PCM_MONOTONIC, | ||
1263 | &in->config); | ||
1264 | if (!pcm_is_ready(in->pcm)) { | ||
1265 | ALOGE("in_read() failed to open pcm in: %s", pcm_get_error(in->pcm)); | ||
1266 | pcm_close(in->pcm); | ||
1267 | in->pcm = NULL; | ||
1268 | usleep(read_usecs); /* limits the rate of error messages */ | ||
1269 | pthread_mutex_unlock(&in->lock); | ||
1270 | pthread_mutex_unlock(&adev->lock); | ||
1271 | return -ENODEV; | ||
1272 | } | ||
1273 | |||
1274 | /* if no supported sample rate is available, use the resampler */ | ||
1275 | if (in->resampler) { | ||
1276 | in->resampler->reset(in->resampler); | ||
1277 | in->frames_in = 0; | ||
1278 | } | ||
1279 | |||
1280 | in->standby = false; | ||
1281 | } | ||
1282 | |||
1283 | pthread_mutex_unlock(&adev->lock); | ||
1284 | |||
1285 | if (in->resampler || in->remix) | ||
1286 | ret = read_frames(in, buffer, frames); | ||
1287 | else | ||
1288 | ret = pcm_read(in->pcm, buffer, bytes); | ||
1289 | |||
1290 | if (ret < 0) { | ||
1291 | ALOGE("in_read() failed to read audio data %d", ret); | ||
1292 | usleep(read_usecs); /* limits the rate of error messages */ | ||
1293 | memset(buffer, 0, bytes); | ||
1294 | } else if (adev->mic_mute) { | ||
1295 | memset(buffer, 0, bytes); | ||
1296 | } | ||
1297 | |||
1298 | pthread_mutex_unlock(&in->lock); | ||
1299 | |||
1300 | return bytes; | ||
1301 | } | ||
1302 | |||
1303 | static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) | ||
1304 | { | ||
1305 | UNUSED(stream); | ||
1306 | ALOGVV("in_get_input_frames_lost() stream=%p frames=%u", stream, 0); | ||
1307 | |||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
1312 | { | ||
1313 | UNUSED(stream); | ||
1314 | UNUSED(effect); | ||
1315 | |||
1316 | return 0; | ||
1317 | } | ||
1318 | |||
1319 | static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | ||
1320 | { | ||
1321 | UNUSED(stream); | ||
1322 | UNUSED(effect); | ||
1323 | |||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | static int adev_open_output_stream(struct audio_hw_device *dev, | ||
1328 | audio_io_handle_t handle, | ||
1329 | audio_devices_t devices, | ||
1330 | audio_output_flags_t flags, | ||
1331 | struct audio_config *config, | ||
1332 | struct audio_stream_out **stream_out, | ||
1333 | const char *address) | ||
1334 | { | ||
1335 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1336 | struct j6_stream_out *out; | ||
1337 | |||
1338 | UNUSED(handle); | ||
1339 | UNUSED(devices); | ||
1340 | UNUSED(address); | ||
1341 | |||
1342 | out = (struct j6_stream_out *)calloc(1, sizeof(struct j6_stream_out)); | ||
1343 | if (!out) | ||
1344 | return -ENOMEM; | ||
1345 | |||
1346 | ALOGV("adev_open_output_stream() stream=%p rate=%u channels=%u " | ||
1347 | "format=0x%08x flags=0x%08x", | ||
1348 | out, config->sample_rate, popcount(config->channel_mask), | ||
1349 | config->format, flags); | ||
1350 | |||
1351 | pthread_mutex_init(&out->lock, NULL); | ||
1352 | |||
1353 | out->stream.common.get_sample_rate = out_get_sample_rate; | ||
1354 | out->stream.common.set_sample_rate = out_set_sample_rate; | ||
1355 | out->stream.common.get_buffer_size = out_get_buffer_size; | ||
1356 | out->stream.common.get_channels = out_get_channels; | ||
1357 | out->stream.common.get_format = out_get_format; | ||
1358 | out->stream.common.set_format = out_set_format; | ||
1359 | out->stream.common.standby = out_standby; | ||
1360 | out->stream.common.dump = out_dump; | ||
1361 | out->stream.common.set_parameters = out_set_parameters; | ||
1362 | out->stream.common.get_parameters = out_get_parameters; | ||
1363 | out->stream.common.add_audio_effect = out_add_audio_effect; | ||
1364 | out->stream.common.remove_audio_effect = out_remove_audio_effect; | ||
1365 | out->stream.get_latency = out_get_latency; | ||
1366 | out->stream.set_volume = out_set_volume; | ||
1367 | out->stream.write = out_write; | ||
1368 | out->stream.get_render_position = out_get_render_position; | ||
1369 | out->stream.get_next_write_timestamp = out_get_next_write_timestamp; | ||
1370 | out->stream.get_presentation_position = out_get_presentation_position; | ||
1371 | |||
1372 | out->dev = adev; | ||
1373 | out->standby = true; | ||
1374 | out->config = pcm_config_playback; | ||
1375 | out->written = 0; | ||
1376 | adev->out = out; | ||
1377 | |||
1378 | config->format = out_get_format(&out->stream.common); | ||
1379 | config->channel_mask = out_get_channels(&out->stream.common); | ||
1380 | config->sample_rate = out_get_sample_rate(&out->stream.common); | ||
1381 | |||
1382 | *stream_out = &out->stream; | ||
1383 | |||
1384 | return 0; | ||
1385 | } | ||
1386 | |||
1387 | static void adev_close_output_stream(struct audio_hw_device *dev, | ||
1388 | struct audio_stream_out *stream) | ||
1389 | { | ||
1390 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1391 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | ||
1392 | |||
1393 | ALOGV("adev_close_output_stream() stream=%p", out); | ||
1394 | |||
1395 | out_standby(&stream->common); | ||
1396 | out->dev = NULL; | ||
1397 | adev->out = NULL; | ||
1398 | |||
1399 | free(stream); | ||
1400 | } | ||
1401 | |||
1402 | static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) | ||
1403 | { | ||
1404 | UNUSED(dev); | ||
1405 | UNUSED(kvpairs); | ||
1406 | |||
1407 | return -ENOSYS; | ||
1408 | } | ||
1409 | |||
1410 | static char * adev_get_parameters(const struct audio_hw_device *dev, | ||
1411 | const char *keys) | ||
1412 | { | ||
1413 | UNUSED(dev); | ||
1414 | UNUSED(keys); | ||
1415 | |||
1416 | return strdup("");; | ||
1417 | } | ||
1418 | |||
1419 | static int adev_init_check(const struct audio_hw_device *dev) | ||
1420 | { | ||
1421 | UNUSED(dev); | ||
1422 | |||
1423 | return 0; | ||
1424 | } | ||
1425 | |||
1426 | static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) | ||
1427 | { | ||
1428 | UNUSED(dev); | ||
1429 | UNUSED(volume); | ||
1430 | |||
1431 | return -ENOSYS; | ||
1432 | } | ||
1433 | |||
1434 | static int adev_set_master_volume(struct audio_hw_device *dev, float volume) | ||
1435 | { | ||
1436 | UNUSED(dev); | ||
1437 | UNUSED(volume); | ||
1438 | |||
1439 | return -ENOSYS; | ||
1440 | } | ||
1441 | |||
1442 | static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) | ||
1443 | { | ||
1444 | UNUSED(dev); | ||
1445 | UNUSED(volume); | ||
1446 | |||
1447 | return -ENOSYS; | ||
1448 | } | ||
1449 | |||
1450 | static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) | ||
1451 | { | ||
1452 | UNUSED(dev); | ||
1453 | UNUSED(muted); | ||
1454 | |||
1455 | return -ENOSYS; | ||
1456 | } | ||
1457 | |||
1458 | static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) | ||
1459 | { | ||
1460 | UNUSED(dev); | ||
1461 | UNUSED(muted); | ||
1462 | |||
1463 | return -ENOSYS; | ||
1464 | } | ||
1465 | |||
1466 | static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) | ||
1467 | { | ||
1468 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1469 | struct j6_stream_out *out = adev->out; | ||
1470 | int ret = 0; | ||
1471 | |||
1472 | ALOGV("adev_set_mode() mode=0x%08x", mode); | ||
1473 | |||
1474 | pthread_mutex_lock(&adev->lock); | ||
1475 | pthread_mutex_lock(&out->lock); | ||
1476 | |||
1477 | if (adev->mode == mode) { | ||
1478 | ALOGV("adev_set_mode() already in mode=0x%08x", mode); | ||
1479 | goto out; | ||
1480 | } | ||
1481 | |||
1482 | if (mode == AUDIO_MODE_IN_CALL) { | ||
1483 | ret = enter_voice_call(adev); | ||
1484 | if (ret) { | ||
1485 | ALOGE("adev_set_mode() failed to initialize voice call %d", ret); | ||
1486 | goto out; | ||
1487 | } | ||
1488 | } else if (adev->mode == AUDIO_MODE_IN_CALL) { | ||
1489 | leave_voice_call(adev); | ||
1490 | } | ||
1491 | |||
1492 | adev->mode = mode; | ||
1493 | |||
1494 | out: | ||
1495 | pthread_mutex_unlock(&out->lock); | ||
1496 | pthread_mutex_unlock(&adev->lock); | ||
1497 | |||
1498 | return ret; | ||
1499 | } | ||
1500 | |||
1501 | static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) | ||
1502 | { | ||
1503 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1504 | |||
1505 | ALOGV("adev_set_mic_mute() state=%s", state ? "mute" : "unmute"); | ||
1506 | adev->mic_mute = state; | ||
1507 | |||
1508 | return 0; | ||
1509 | } | ||
1510 | |||
1511 | static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) | ||
1512 | { | ||
1513 | const struct j6_audio_device *adev = (const struct j6_audio_device *)dev; | ||
1514 | |||
1515 | *state = adev->mic_mute; | ||
1516 | ALOGV("adev_get_mic_mute() state=%s", *state ? "mute" : "unmute"); | ||
1517 | |||
1518 | return 0; | ||
1519 | } | ||
1520 | |||
1521 | static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, | ||
1522 | const struct audio_config *config) | ||
1523 | { | ||
1524 | UNUSED(dev); | ||
1525 | |||
1526 | size_t bytes = get_input_buffer_size(config->sample_rate, | ||
1527 | config->format, | ||
1528 | popcount(config->channel_mask)); | ||
1529 | |||
1530 | ALOGVV("adev_in_get_buffer_size() bytes=%u", bytes); | ||
1531 | |||
1532 | return bytes; | ||
1533 | } | ||
1534 | |||
1535 | static int adev_open_input_stream(struct audio_hw_device *dev, | ||
1536 | audio_io_handle_t handle, | ||
1537 | audio_devices_t devices, | ||
1538 | struct audio_config *config, | ||
1539 | struct audio_stream_in **stream_in, | ||
1540 | audio_input_flags_t flags, | ||
1541 | const char *address, | ||
1542 | audio_source_t source) | ||
1543 | { | ||
1544 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1545 | struct j6_stream_in *in; | ||
1546 | int buffer_size; | ||
1547 | int ret; | ||
1548 | |||
1549 | UNUSED(handle); | ||
1550 | UNUSED(devices); | ||
1551 | UNUSED(flags); | ||
1552 | UNUSED(address); | ||
1553 | UNUSED(source); | ||
1554 | |||
1555 | in = (struct j6_stream_in *)calloc(1, sizeof(struct j6_stream_in)); | ||
1556 | if (!in) | ||
1557 | return -ENOMEM; | ||
1558 | |||
1559 | ALOGV("adev_open_input_stream() stream=%p rate=%u channels=%u format=0x%08x", | ||
1560 | in, config->sample_rate, popcount(config->channel_mask), config->format); | ||
1561 | |||
1562 | pthread_mutex_init(&in->lock, NULL); | ||
1563 | |||
1564 | in->stream.common.get_sample_rate = in_get_sample_rate; | ||
1565 | in->stream.common.set_sample_rate = in_set_sample_rate; | ||
1566 | in->stream.common.get_buffer_size = in_get_buffer_size; | ||
1567 | in->stream.common.get_channels = in_get_channels; | ||
1568 | in->stream.common.get_format = in_get_format; | ||
1569 | in->stream.common.set_format = in_set_format; | ||
1570 | in->stream.common.standby = in_standby; | ||
1571 | in->stream.common.dump = in_dump; | ||
1572 | in->stream.common.set_parameters = in_set_parameters; | ||
1573 | in->stream.common.get_parameters = in_get_parameters; | ||
1574 | in->stream.common.add_audio_effect = in_add_audio_effect; | ||
1575 | in->stream.common.remove_audio_effect = in_remove_audio_effect; | ||
1576 | in->stream.set_gain = in_set_gain; | ||
1577 | in->stream.read = in_read; | ||
1578 | in->stream.get_input_frames_lost = in_get_input_frames_lost; | ||
1579 | |||
1580 | in->dev = adev; | ||
1581 | in->standby = true; | ||
1582 | in->config = pcm_config_capture; | ||
1583 | in->requested_rate = config->sample_rate; | ||
1584 | in->requested_channels = popcount(config->channel_mask); | ||
1585 | in->hw_frame_size = in->config.channels * sizeof(int16_t); | ||
1586 | in->remix = NULL; | ||
1587 | in->resampler = NULL; | ||
1588 | in->buffer = NULL; | ||
1589 | adev->in = in; | ||
1590 | |||
1591 | /* in-place stereo-to-mono remix since capture stream is stereo */ | ||
1592 | if (in->requested_channels == 1) { | ||
1593 | ALOGV("adev_open_input_stream() stereo-to-mono remix needed"); | ||
1594 | ret = setup_stereo_to_mono_input_remix(in); | ||
1595 | if (ret) { | ||
1596 | ALOGE("adev_open_input_stream() failed to setup remix %d", ret); | ||
1597 | goto err1; | ||
1598 | } | ||
1599 | } | ||
1600 | |||
1601 | if (in->requested_rate != in->config.rate) { | ||
1602 | ALOGV("adev_open_input_stream() resample needed, req=%uHz got=%uHz", | ||
1603 | in->requested_rate, in->config.rate); | ||
1604 | |||
1605 | in->buf_provider.get_next_buffer = get_next_buffer; | ||
1606 | in->buf_provider.release_buffer = release_buffer; | ||
1607 | ret = create_resampler(in->config.rate, | ||
1608 | in->requested_rate, | ||
1609 | in->requested_channels, | ||
1610 | RESAMPLER_QUALITY_DEFAULT, | ||
1611 | &in->buf_provider, | ||
1612 | &in->resampler); | ||
1613 | if (ret) { | ||
1614 | ALOGE("adev_open_input_stream() failed to create resampler %d", ret); | ||
1615 | goto err2; | ||
1616 | } | ||
1617 | } | ||
1618 | |||
1619 | /* | ||
1620 | * buffer size needs to be enough to allow stereo-to-mono remix | ||
1621 | * and resample if needed | ||
1622 | */ | ||
1623 | if (in->resampler || in->remix) { | ||
1624 | buffer_size = in->config.period_size * in->hw_frame_size; | ||
1625 | if (in->resampler) | ||
1626 | buffer_size *= 2; | ||
1627 | if (in->remix) | ||
1628 | buffer_size *= 2; | ||
1629 | |||
1630 | in->buffer = malloc(buffer_size); | ||
1631 | if (!in->buffer) { | ||
1632 | ret = -ENOMEM; | ||
1633 | goto err3; | ||
1634 | } | ||
1635 | } | ||
1636 | |||
1637 | *stream_in = &in->stream; | ||
1638 | |||
1639 | return 0; | ||
1640 | |||
1641 | err3: | ||
1642 | release_resampler(in->resampler); | ||
1643 | err2: | ||
1644 | free(in->remix); | ||
1645 | err1: | ||
1646 | free(in); | ||
1647 | return ret; | ||
1648 | } | ||
1649 | |||
1650 | static void adev_close_input_stream(struct audio_hw_device *dev, | ||
1651 | struct audio_stream_in *stream) | ||
1652 | { | ||
1653 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1654 | struct j6_stream_in *in = (struct j6_stream_in *)(stream); | ||
1655 | |||
1656 | ALOGV("adev_close_input_stream() stream=%p", stream); | ||
1657 | |||
1658 | in_standby(&stream->common); | ||
1659 | |||
1660 | if (in->resampler) | ||
1661 | release_resampler(in->resampler); | ||
1662 | |||
1663 | if (in->remix) | ||
1664 | free(in->remix); | ||
1665 | |||
1666 | free(in->buffer); | ||
1667 | free(in); | ||
1668 | adev->in = NULL; | ||
1669 | } | ||
1670 | |||
1671 | static int adev_dump(const audio_hw_device_t *device, int fd) | ||
1672 | { | ||
1673 | UNUSED(device); | ||
1674 | UNUSED(fd); | ||
1675 | |||
1676 | return 0; | ||
1677 | } | ||
1678 | |||
1679 | static int adev_close(hw_device_t *device) | ||
1680 | { | ||
1681 | struct j6_audio_device *adev = (struct j6_audio_device *)device; | ||
1682 | |||
1683 | ALOGI("adev_close()"); | ||
1684 | |||
1685 | audio_route_free(adev->route); | ||
1686 | free(device); | ||
1687 | |||
1688 | return 0; | ||
1689 | } | ||
1690 | |||
1691 | static int adev_open(const hw_module_t* module, const char* name, | ||
1692 | hw_device_t** device) | ||
1693 | { | ||
1694 | struct j6_audio_device *adev; | ||
1695 | |||
1696 | ALOGI("adev_open() %s", name); | ||
1697 | |||
1698 | if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) | ||
1699 | return -EINVAL; | ||
1700 | |||
1701 | adev = (struct j6_audio_device*)calloc(1, sizeof(struct j6_audio_device)); | ||
1702 | if (!adev) | ||
1703 | return -ENOMEM; | ||
1704 | |||
1705 | pthread_mutex_init(&adev->lock, NULL); | ||
1706 | |||
1707 | adev->device.common.tag = HARDWARE_DEVICE_TAG; | ||
1708 | adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; | ||
1709 | adev->device.common.module = (struct hw_module_t *) module; | ||
1710 | adev->device.common.close = adev_close; | ||
1711 | |||
1712 | adev->device.init_check = adev_init_check; | ||
1713 | adev->device.set_voice_volume = adev_set_voice_volume; | ||
1714 | adev->device.set_master_volume = adev_set_master_volume; | ||
1715 | adev->device.get_master_volume = adev_get_master_volume; | ||
1716 | adev->device.set_master_mute = adev_set_master_mute; | ||
1717 | adev->device.get_master_mute = adev_get_master_mute; | ||
1718 | adev->device.set_mode = adev_set_mode; | ||
1719 | adev->device.set_mic_mute = adev_set_mic_mute; | ||
1720 | adev->device.get_mic_mute = adev_get_mic_mute; | ||
1721 | adev->device.set_parameters = adev_set_parameters; | ||
1722 | adev->device.get_parameters = adev_get_parameters; | ||
1723 | adev->device.get_input_buffer_size = adev_get_input_buffer_size; | ||
1724 | adev->device.open_output_stream = adev_open_output_stream; | ||
1725 | adev->device.close_output_stream = adev_close_output_stream; | ||
1726 | adev->device.open_input_stream = adev_open_input_stream; | ||
1727 | adev->device.close_input_stream = adev_close_input_stream; | ||
1728 | adev->device.dump = adev_dump; | ||
1729 | |||
1730 | adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC; | ||
1731 | adev->out_device = AUDIO_DEVICE_OUT_SPEAKER; | ||
1732 | adev->card = 0; | ||
1733 | adev->in_port = 0; | ||
1734 | adev->out_port = 0; | ||
1735 | ALOGI("Media card is hw:%d\n", adev->card); | ||
1736 | adev->bt_card=2; | ||
1737 | adev->bt_port = 0; | ||
1738 | ALOGI("Bluetooth SCO card is hw:%d\n", adev->bt_card); | ||
1739 | |||
1740 | adev->mic_mute = false; | ||
1741 | adev->in_call = false; | ||
1742 | adev->mode = AUDIO_MODE_NORMAL; | ||
1743 | |||
1744 | adev->route = audio_route_init(adev->card, NULL); | ||
1745 | if (!adev->route) { | ||
1746 | ALOGE("Unable to initialize audio routes"); | ||
1747 | free(adev); | ||
1748 | return -EINVAL; | ||
1749 | } | ||
1750 | |||
1751 | *device = &adev->device.common; | ||
1752 | |||
1753 | return 0; | ||
1754 | } | ||
1755 | |||
1756 | static struct hw_module_methods_t hal_module_methods = { | ||
1757 | .open = adev_open, | ||
1758 | }; | ||
1759 | |||
1760 | struct audio_module HAL_MODULE_INFO_SYM = { | ||
1761 | .common = { | ||
1762 | .tag = HARDWARE_MODULE_TAG, | ||
1763 | .module_api_version = AUDIO_MODULE_API_VERSION_0_1, | ||
1764 | .hal_api_version = HARDWARE_HAL_API_VERSION, | ||
1765 | .id = AUDIO_HARDWARE_MODULE_ID, | ||
1766 | .name = "Jacinto6 Audio HAL", | ||
1767 | .author = "Texas Instruments Inc.", | ||
1768 | .methods = &hal_module_methods, | ||
1769 | }, | ||
1770 | }; | ||
diff --git a/audio/primary/mixer_paths.xml b/audio/primary/mixer_paths.xml new file mode 100644 index 0000000..b872afa --- /dev/null +++ b/audio/primary/mixer_paths.xml | |||
@@ -0,0 +1,31 @@ | |||
1 | <mixer> | ||
2 | |||
3 | <!-- Card default routes --> | ||
4 | |||
5 | <!-- Capture: Mic --> | ||
6 | <ctl name="Left PGA Mixer Mic3L Switch" value="1" /> | ||
7 | <ctl name="Right PGA Mixer Mic3R Switch" value="1" /> | ||
8 | <ctl name="PGA Capture Switch" value="1" /> | ||
9 | <ctl name="PGA Capture Volume" value="24" /> | ||
10 | <ctl name="Left Line1L Mux" value="single-ended" /> | ||
11 | <ctl name="Right Line1L Mux" value="single-ended" /> | ||
12 | <ctl name="Left Line1R Mux" value="single-ended" /> | ||
13 | <ctl name="Right Line1R Mux" value="single-ended" /> | ||
14 | |||
15 | <!-- Playback: Line-Out and Headphone --> | ||
16 | <ctl name="Left DAC Mux" value="DAC_L1" /> | ||
17 | <ctl name="Right DAC Mux" value="DAC_R1" /> | ||
18 | <ctl name="PCM Playback Volume" value="127" /> | ||
19 | |||
20 | <ctl name="Left HP Mixer DACL1 Switch" value="1" /> | ||
21 | <ctl name="Right HP Mixer DACR1 Switch" value="1" /> | ||
22 | <ctl name="HP Playback Switch" value="1" /> | ||
23 | <ctl name="HP DAC Playback Volume" value="118" /> | ||
24 | <ctl name="Output Driver Power-On time" value="200ms" /> | ||
25 | |||
26 | <ctl name="Left Line Mixer DACL1 Switch" value="1" /> | ||
27 | <ctl name="Right Line Mixer DACR1 Switch" value="1" /> | ||
28 | <ctl name="Line Playback Switch" value="1" /> | ||
29 | <ctl name="Line DAC Playback Volume" value="118" /> | ||
30 | |||
31 | </mixer> | ||
diff --git a/bluetooth/bdroid_buildcfg.h b/bluetooth/bdroid_buildcfg.h new file mode 100644 index 0000000..a45ac86 --- /dev/null +++ b/bluetooth/bdroid_buildcfg.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments Inc. | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #ifndef _BDROID_BUILDCFG_H | ||
18 | #define _BDROID_BUILDCFG_H | ||
19 | |||
20 | #define BTM_DEF_LOCAL_NAME "AM57x" | ||
21 | |||
22 | /* Handsfree device */ | ||
23 | #define BTA_DM_COD {0x20, 0x04, 0x08} | ||
24 | |||
25 | /* Enable A2DP sink */ | ||
26 | #define BTA_AV_SINK_INCLUDED TRUE | ||
27 | |||
28 | #endif | ||
diff --git a/bootanimation.zip b/bootanimation.zip new file mode 100644 index 0000000..938ceae --- /dev/null +++ b/bootanimation.zip | |||
Binary files differ | |||
diff --git a/device.mk b/device.mk new file mode 100644 index 0000000..d044d59 --- /dev/null +++ b/device.mk | |||
@@ -0,0 +1,154 @@ | |||
1 | # | ||
2 | # Copyright (C) 2011 The Android Open-Source Project | ||
3 | # | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | # you may not use this file except in compliance with the License. | ||
6 | # You may obtain a copy of the License at | ||
7 | # | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | # | ||
10 | # Unless required by applicable law or agreed to in writing, software | ||
11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | # See the License for the specific language governing permissions and | ||
14 | # limitations under the License. | ||
15 | # | ||
16 | |||
17 | # Audio Post Processing Engine (APPE) | ||
18 | APPE_AUDIO := false | ||
19 | |||
20 | ifeq ($(TARGET_PREBUILT_KERNEL),) | ||
21 | LOCAL_KERNEL := device/ti/am43xevm/kernel | ||
22 | else | ||
23 | LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL) | ||
24 | endif | ||
25 | |||
26 | PRODUCT_COPY_FILES := \ | ||
27 | $(LOCAL_KERNEL):kernel \ | ||
28 | device/ti/am43xevm/tablet_core_hardware_am43xevm.xml:system/etc/permissions/tablet_core_hardware_am43xevm.xml \ | ||
29 | device/ti/am43xevm/init.am43xevmboard.rc:root/init.am43xevmboard.rc \ | ||
30 | device/ti/am43xevm/init.am43xevmboard.usb.rc:root/init.am43xevmboard.usb.rc \ | ||
31 | device/ti/am43xevm/ueventd.am43xevmboard.rc:root/ueventd.am43xevmboard.rc \ | ||
32 | device/ti/am43xevm/fstab.am43xevmboard:root/fstab.am43xevmboard \ | ||
33 | device/ti/am43xevm/media_profiles.xml:system/etc/media_profiles.xml \ | ||
34 | device/ti/am43xevm/media_codecs.xml:system/etc/media_codecs.xml \ | ||
35 | device/ti/am43xevm/media_codecs_performance.xml:system/etc/media_codecs_performance.xml \ | ||
36 | frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \ | ||
37 | frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \ | ||
38 | frameworks/native/data/etc/android.hardware.wifi.xml:system/etc/permissions/android.hardware.wifi.xml \ | ||
39 | frameworks/native/data/etc/android.hardware.wifi.direct.xml:system/etc/permissions/android.hardware.wifi.direct.xml \ | ||
40 | frameworks/native/data/etc/android.hardware.usb.host.xml:system/etc/permissions/android.hardware.usb.host.xml \ | ||
41 | frameworks/native/data/etc/android.hardware.usb.accessory.xml:system/etc/permissions/android.hardware.usb.accessory.xml \ | ||
42 | frameworks/native/data/etc/android.hardware.ethernet.xml:system/etc/permissions/android.hardware.ethernet.xml \ | ||
43 | device/ti/am43xevm/pixcir_tangoc.idc:system/usr/idc/pixcir_tangoc.idc \ | ||
44 | |||
45 | # These are the hardware-specific features | ||
46 | PRODUCT_COPY_FILES += \ | ||
47 | frameworks/native/data/etc/android.hardware.camera.xml:system/etc/permissions/android.hardware.camera.xml | ||
48 | |||
49 | # Audio | ||
50 | ifeq ($(APPE_AUDIO),true) | ||
51 | PRODUCT_COPY_FILES += \ | ||
52 | hardware/ti/radio/vis_sdk/packages/android/hal/mixer_paths.xml:system/etc/mixer_paths.xml | ||
53 | else | ||
54 | PRODUCT_COPY_FILES += \ | ||
55 | device/ti/am43xevm/audio/primary/mixer_paths.xml:system/etc/mixer_paths.xml | ||
56 | # device/ti/am43xevm/audio/primary/mixer_paths.xml:system/etc/mixer_paths.xml \ | ||
57 | # device/ti/am43xevm/audio/jamr3/jamr3_mixer_paths.xml:system/etc/jamr3_mixer_paths.xml | ||
58 | endif | ||
59 | |||
60 | PRODUCT_COPY_FILES += \ | ||
61 | device/ti/am43xevm/audio/audio_policy.conf:system/etc/audio_policy.conf | ||
62 | |||
63 | # cpuset configuration | ||
64 | PRODUCT_COPY_FILES += \ | ||
65 | device/ti/am43xevm/init.am43xevmboard.cpuset.sh:system/bin/init.am43xevmboard.cpuset.sh | ||
66 | |||
67 | |||
68 | PRODUCT_PROPERTY_OVERRIDES := \ | ||
69 | hwui.render_dirty_regions=false | ||
70 | |||
71 | PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \ | ||
72 | persist.sys.usb.config=mtp | ||
73 | |||
74 | PRODUCT_PROPERTY_OVERRIDES += \ | ||
75 | ro.opengles.version=131072 | ||
76 | |||
77 | PRODUCT_CHARACTERISTICS := tablet,nosdcard | ||
78 | |||
79 | DEVICE_PACKAGE_OVERLAYS := \ | ||
80 | device/ti/am43xevm/overlay | ||
81 | |||
82 | PRODUCT_PACKAGES += \ | ||
83 | com.android.future.usb.accessory | ||
84 | |||
85 | PRODUCT_PROPERTY_OVERRIDES += \ | ||
86 | ro.sf.lcd_density=120 | ||
87 | |||
88 | # WI-Fi | ||
89 | # PRODUCT_PACKAGES += \ | ||
90 | wpa_supplicant \ | ||
91 | wpa_supplicant.conf \ | ||
92 | wpa_supplicant_overlay.conf \ | ||
93 | libwpa_client \ | ||
94 | hostapd \ | ||
95 | hostapd.conf \ | ||
96 | dhcpcd.conf \ | ||
97 | wifical.sh \ | ||
98 | TQS_D_1.7.ini \ | ||
99 | TQS_D_1.7_127x.ini \ | ||
100 | crda \ | ||
101 | regulatory.bin \ | ||
102 | wlconf | ||
103 | |||
104 | PRODUCT_PACKAGES += \ | ||
105 | LegacyCamera \ | ||
106 | camera_test \ | ||
107 | ion_tiler_test \ | ||
108 | iontest \ | ||
109 | ion_ti_test2 \ | ||
110 | vpetest \ | ||
111 | modetest \ | ||
112 | libdrm | ||
113 | |||
114 | # Audio HAL modules | ||
115 | PRODUCT_PACKAGES += audio.primary.am57x | ||
116 | PRODUCT_PACKAGES += audio.hdmi.am57x | ||
117 | |||
118 | # JAMR3 Audio HAL module | ||
119 | # ifneq ($(APPE_AUDIO),true) | ||
120 | # PRODUCT_PACKAGES += audio.jamr3.am57x | ||
121 | # endif | ||
122 | |||
123 | # BlueDroid a2dp Audio HAL module | ||
124 | PRODUCT_PACKAGES += audio.a2dp.default | ||
125 | # Remote submix | ||
126 | PRODUCT_PACKAGES += audio.r_submix.default | ||
127 | |||
128 | PRODUCT_PACKAGES += \ | ||
129 | tinymix \ | ||
130 | tinyplay \ | ||
131 | tinycap | ||
132 | |||
133 | # Radio | ||
134 | # PRODUCT_PACKAGES += \ | ||
135 | RadioApp \ | ||
136 | lad_dra7xx \ | ||
137 | libtiipc \ | ||
138 | libtiipcutils \ | ||
139 | libtitransportrpmsg | ||
140 | |||
141 | # Launcher3 | ||
142 | PRODUCT_PACKAGES += Launcher3 | ||
143 | |||
144 | # Enable AAC 5.1 decode (decoder) | ||
145 | PRODUCT_PROPERTY_OVERRIDES += \ | ||
146 | media.aac_51_output_enabled=true | ||
147 | |||
148 | $(call inherit-product, frameworks/native/build/tablet-7in-hdpi-1024-dalvik-heap.mk) | ||
149 | $(call inherit-product-if-exists, hardware/ti/dra7xx/am57x.mk) | ||
150 | #$(call inherit-product-if-exists, hardware/ti/wpan/ti-wpan-products.mk) | ||
151 | $(call inherit-product-if-exists, device/ti/proprietary-open/jacinto6/ti-jacinto6-vendor.mk) | ||
152 | #$(call inherit-product-if-exists, device/ti/proprietary-open/jacinto6/ducati-full_jacinto6evm.mk) | ||
153 | #$(call inherit-product-if-exists, device/ti/proprietary-open/wl12xx/wlan/wl12xx-wlan-fw-products.mk) | ||
154 | #$(call inherit-product-if-exists, device/ti/proprietary-open/wl12xx/wpan/wl12xx-wpan-fw-products.mk) | ||
@@ -0,0 +1,2 @@ | |||
1 | 0 0 android | ||
2 | 0 1 POWERVR_SGX530_125 | ||
diff --git a/fastboot.sh b/fastboot.sh new file mode 100755 index 0000000..240d410 --- /dev/null +++ b/fastboot.sh | |||
@@ -0,0 +1,253 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | usage () | ||
4 | { | ||
5 | echo "Usage: sudo fastboot.sh <options>"; | ||
6 | echo "options:"; | ||
7 | echo " --help Show this message and exit" | ||
8 | exit 1; | ||
9 | } | ||
10 | |||
11 | #no args case | ||
12 | if [ "$1" = "--help" ] ; then | ||
13 | usage | ||
14 | fi | ||
15 | |||
16 | # Pre-packaged DB | ||
17 | export FASTBOOT=${FASTBOOT-"./fastboot"} | ||
18 | export PRODUCT_OUT=${PRODUCT_OUT-"./"} | ||
19 | export LD_LIBRARY_PATH=./ | ||
20 | |||
21 | echo "Fastboot: $FASTBOOT" | ||
22 | echo "Image location: $PRODUCT_OUT" | ||
23 | |||
24 | |||
25 | # ============================================================================= | ||
26 | # pre-run | ||
27 | # ============================================================================= | ||
28 | |||
29 | # Verify fastboot program is available | ||
30 | # Verify user permission to run fastboot | ||
31 | # Verify fastboot detects a device, otherwise exit | ||
32 | if [ -f ${FASTBOOT} ]; then | ||
33 | fastboot_status=`${FASTBOOT} devices 2>&1` | ||
34 | if [ `echo $fastboot_status | grep -wc "no permissions"` -gt 0 ]; then | ||
35 | cat <<-EOF >&2 | ||
36 | ------------------------------------------- | ||
37 | Fastboot requires administrator permissions | ||
38 | Please run the script as root or create a | ||
39 | fastboot udev rule, e.g: | ||
40 | |||
41 | % cat /etc/udev/rules.d/99_android.rules | ||
42 | SUBSYSTEM=="usb", | ||
43 | SYSFS{idVendor}=="0451" | ||
44 | OWNER="<username>" | ||
45 | GROUP="adm" | ||
46 | ------------------------------------------- | ||
47 | EOF | ||
48 | exit 1 | ||
49 | elif [ "X$fastboot_status" = "X" ]; then | ||
50 | echo "No device detected. Please ensure that" \ | ||
51 | "fastboot is running on the target device" | ||
52 | exit -1; | ||
53 | else | ||
54 | device=`echo $fastboot_status | awk '{print$1}'` | ||
55 | echo -e "\nFastboot - device detected: $device\n" | ||
56 | fi | ||
57 | else | ||
58 | echo "Error: fastboot is not available at ${FASTBOOT}" | ||
59 | exit -1; | ||
60 | fi | ||
61 | |||
62 | ## poll the board to find out its configuration | ||
63 | #product=`${FASTBOOT} getvar product 2>&1 | grep product | awk '{print$2}'` | ||
64 | cpu=`${FASTBOOT} getvar cpu 2>&1 | grep cpu | awk '{print$2}'` | ||
65 | cputype=`${FASTBOOT} getvar secure 2>&1 | grep secure | awk '{print$2}'` | ||
66 | boardrev=`${FASTBOOT} getvar board_rev 2>&1 | grep board_rev | awk '{print$2}'` | ||
67 | |||
68 | |||
69 | # Make EMU = HS | ||
70 | if [ ${cputype} = "EMU" ] || [ ${cputype} = "HS" ]; then | ||
71 | cputype="HS" | ||
72 | xloader="${PRODUCT_OUT}${cputype}_QSPI_MLO" | ||
73 | uboot="${PRODUCT_OUT}${cputype}_u-boot.img" | ||
74 | # If fastboot does not support getvar default to GP | ||
75 | elif [ ${cputype} = "" ] || [ ${cputype} = "GP" ]; then | ||
76 | cputype="GP" | ||
77 | xloader="${PRODUCT_OUT}${cputype}_MLO" | ||
78 | uboot="${PRODUCT_OUT}u-boot.img" | ||
79 | fi | ||
80 | |||
81 | # Based on cpu, decide the dtb to flash, default fall back to J6 and LCD 10 | ||
82 | if [ ${cpu} = "J6ECO" ]; then | ||
83 | if [ ${boardrev} = "C" ]; then | ||
84 | environment="${PRODUCT_OUT}dra72-evm-lcd-osd.dtb" | ||
85 | elif [ ${boardrev} = "1.3A" ]; then | ||
86 | environment="${PRODUCT_OUT}am571x-idk-lcd-osd101t2587.dtb" | ||
87 | else | ||
88 | environment="${PRODUCT_OUT}dra72-evm-lcd10.dtb" | ||
89 | fi | ||
90 | else | ||
91 | if [ ${boardrev} = "H" ]; then | ||
92 | environment="${PRODUCT_OUT}dra7-evm-lcd-osd.dtb" | ||
93 | else | ||
94 | environment="${PRODUCT_OUT}am57xx-evm-reva3.dtb" | ||
95 | fi | ||
96 | fi | ||
97 | |||
98 | if [ ${cpu} = "J6" ]; then | ||
99 | if [ ${boardrev} = "A.30" ]; then | ||
100 | environment="${PRODUCT_OUT}am57xx-evm-reva3.dtb" | ||
101 | elif [ ${boardrev} = "1.3A" ]; then | ||
102 | environment="${PRODUCT_OUT}am572x-idk-lcd-osd.dtb" | ||
103 | elif [ ${boardrev} = "B.10" ]; then | ||
104 | environment="${PRODUCT_OUT}am57xx-beagle-x15-revb1.dtb" | ||
105 | fi | ||
106 | fi | ||
107 | |||
108 | # Create the filename | ||
109 | bootimg="${PRODUCT_OUT}boot.img" | ||
110 | systemimg="${PRODUCT_OUT}system.img" | ||
111 | userdataimg="${PRODUCT_OUT}userdata.img" | ||
112 | cacheimg="${PRODUCT_OUT}cache.img" | ||
113 | recoveryimg="${PRODUCT_OUT}recovery.img" | ||
114 | |||
115 | |||
116 | # Verify that all the files required for the fastboot flash | ||
117 | # process are available | ||
118 | |||
119 | if [ ! -e "${bootimg}" ] ; then | ||
120 | echo "Missing ${bootimg}" | ||
121 | exit -1; | ||
122 | fi | ||
123 | if [ ! -e "$xloader" ] ; then | ||
124 | echo "Missing ${xloader}" | ||
125 | exit -1; | ||
126 | fi | ||
127 | if [ ! -e "${uboot}" ] ; then | ||
128 | echo "Missing ${uboot}" | ||
129 | exit -1; | ||
130 | fi | ||
131 | if [ ! -e "${environment}" ] ; then | ||
132 | echo "Missing ${environment}" | ||
133 | exit -1; | ||
134 | else | ||
135 | echo "DTB = ${environment}" | ||
136 | fi | ||
137 | if [ ! -e "${systemimg}" ] ; then | ||
138 | echo "Missing ${systemimg}" | ||
139 | exit -1; | ||
140 | fi | ||
141 | if [ ! -e "${userdataimg}" ] ; then | ||
142 | echo "Missing ${userdataimg}" | ||
143 | exit -1; | ||
144 | fi | ||
145 | if [ ! -e "${cacheimg}" ] ; then | ||
146 | echo "Missing ${cacheimg}" | ||
147 | exit -1; | ||
148 | fi | ||
149 | if [ ! -e "${recoveryimg}" ] ; then | ||
150 | echo "Missing ${recoveryimg}" | ||
151 | exit -1; | ||
152 | fi | ||
153 | |||
154 | echo "Create GPT partition table" | ||
155 | ${FASTBOOT} oem format | ||
156 | |||
157 | echo "Setting target for bootloader to emmc" | ||
158 | ${FASTBOOT} oem mmc | ||
159 | |||
160 | sleep 3 | ||
161 | |||
162 | echo "Flashing bootloader....." | ||
163 | echo " xloader: ${xloader}" | ||
164 | ${FASTBOOT} flash xloader ${xloader} | ||
165 | |||
166 | sleep 3 | ||