mirror of
https://github.com/garagetinkering/Video_Game_Mini_Maps.git
synced 2026-01-17 17:47:00 +01:00
initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
014948481bda426cd46714f297fe1891711246c62bea288863a8cc8cf13ef1f0
|
||||
52
managed_components/espressif__esp_codec_dev/CHANGELOG.md
Normal file
52
managed_components/espressif__esp_codec_dev/CHANGELOG.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
## v1.2.0
|
||||
|
||||
### Features
|
||||
|
||||
- Add IDF v5.3 support
|
||||
Using new i2c driver `esp_driver_i2c`, add `bus_handle` configuration for `audio_codec_i2c_cfg_t`.
|
||||
User need create the `bus_handle` using API `i2c_new_master_bus` instead of `i2c_driver_install`.
|
||||
- Change test code to standalone application, user can directly build it under folder [codec_dev_test](test_apps/codec_dev_test)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix I2S work in PDM mode record or play mono channel audio wrongly
|
||||
|
||||
|
||||
## v1.1.0
|
||||
|
||||
### Features
|
||||
|
||||
- Add driver for AW88298, see detail datasheet [AW88298](https://datasheetspdf.com/download_new.php?id=1513778)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix ES8311 playback fade in for long time
|
||||
|
||||
|
||||
## v1.0.3
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix I2S TX and RX work in share mode, need enable TX before RX
|
||||
|
||||
|
||||
## v1.0.2
|
||||
|
||||
### Features
|
||||
|
||||
- Add I2S TDM support
|
||||
- Add API to `esp_codec_dev_set_in_channel_gain` to set input channel gain dependently
|
||||
|
||||
|
||||
## v1.0.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix ES8388 volume register set incorrectly
|
||||
|
||||
|
||||
## v1.0.0
|
||||
|
||||
- Initial version of `esp_codec_dev`
|
||||
80
managed_components/espressif__esp_codec_dev/CMakeLists.txt
Normal file
80
managed_components/espressif__esp_codec_dev/CMakeLists.txt
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS "device/zl38063/api_lib" "device/zl38063/firmware" "device/priv_include")
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS include interface device/include)
|
||||
|
||||
# set conversion sources
|
||||
set(COMPONENT_SRCS
|
||||
esp_codec_dev.c
|
||||
esp_codec_dev_vol.c
|
||||
esp_codec_dev_if.c
|
||||
audio_codec_sw_vol.c
|
||||
)
|
||||
|
||||
list(APPEND COMPONENT_SRCS
|
||||
platform/audio_codec_gpio.c
|
||||
platform/audio_codec_ctrl_i2c.c
|
||||
platform/audio_codec_data_i2s.c
|
||||
platform/audio_codec_ctrl_spi.c
|
||||
platform/esp_codec_dev_os.c
|
||||
)
|
||||
|
||||
if (CONFIG_CODEC_ES8311_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es8311/es8311.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ES8156_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es8156/es8156.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ES7243E_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es7243e/es7243e.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ES7210_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es7210/es7210.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ES7243_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es7243/es7243.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ES8388_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es8388/es8388.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_TAS5805M_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/tas5805m/tas5805m.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ES8374_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/es8374/es8374.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_AW88298_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/aw88298/aw88298.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_CODEC_ZL38063_SUPPORT)
|
||||
list(APPEND COMPONENT_SRCS device/zl38063/zl38063.c
|
||||
device/zl38063/api_lib/vprocTwolf_access.c
|
||||
device/zl38063/api_lib/vproc_common.c
|
||||
device/zl38063/example_apps/tw_hal_verify.c
|
||||
device/zl38063/example_apps/tw_ldcfg.c
|
||||
device/zl38063/example_apps/tw_ldfw.c
|
||||
device/zl38063/example_apps/tw_ldfwcfg.c
|
||||
device/zl38063/example_apps/tw_spi_access.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${COMPONENT_SRCS}"
|
||||
INCLUDE_DIRS "${COMPONENT_ADD_INCLUDEDIRS}"
|
||||
PRIV_INCLUDE_DIRS "${COMPONENT_PRIV_INCLUDEDIRS}"
|
||||
REQUIRES driver
|
||||
PRIV_REQUIRES freertos)
|
||||
# Library only support xtensa
|
||||
if (CONFIG_CODEC_ZL38063_SUPPORT)
|
||||
if (NOT ((CONFIG_IDF_TARGET STREQUAL "esp32c6") OR (CONFIG_IDF_TARGET STREQUAL "esp32c3")))
|
||||
target_link_libraries(${COMPONENT_TARGET} INTERFACE "-L${CMAKE_CURRENT_LIST_DIR}/device/zl38063/firmware")
|
||||
target_link_libraries(${COMPONENT_TARGET} INTERFACE firmware)
|
||||
endif()
|
||||
endif()
|
||||
63
managed_components/espressif__esp_codec_dev/Kconfig
Normal file
63
managed_components/espressif__esp_codec_dev/Kconfig
Normal file
@@ -0,0 +1,63 @@
|
||||
menu "Audio Codec Device Configuration"
|
||||
|
||||
config CODEC_ES8311_SUPPORT
|
||||
bool "Support ES8311 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES8311.
|
||||
|
||||
config CODEC_ES7210_SUPPORT
|
||||
bool "Support ES7210 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES7210.
|
||||
|
||||
config CODEC_ES7243_SUPPORT
|
||||
bool "Support ES7243 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES7243.
|
||||
|
||||
config CODEC_ES7243E_SUPPORT
|
||||
bool "Support ES7243E Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES7243E.
|
||||
|
||||
config CODEC_ES8156_SUPPORT
|
||||
bool "Support ES8156 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES8156.
|
||||
|
||||
config CODEC_AW88298_SUPPORT
|
||||
bool "Support AW88298 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec AW88298.
|
||||
|
||||
config CODEC_ES8374_SUPPORT
|
||||
bool "Support ES8374 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES8374.
|
||||
|
||||
config CODEC_ES8388_SUPPORT
|
||||
bool "Support ES8388 Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec ES8388.
|
||||
|
||||
config CODEC_TAS5805M_SUPPORT
|
||||
bool "Support TAS5805M Codec Chip"
|
||||
default y
|
||||
help
|
||||
Enable this option to support codec TAS5805M.
|
||||
|
||||
config CODEC_ZL38063_SUPPORT
|
||||
bool "Support ZL38063 Codec Chip"
|
||||
default n
|
||||
help
|
||||
Enable this option to support codec ZL38063.
|
||||
ZL38063 firmware only support xtensa, don't enable for RISC-V IC.
|
||||
endmenu
|
||||
202
managed_components/espressif__esp_codec_dev/LICENSE
Normal file
202
managed_components/espressif__esp_codec_dev/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
238
managed_components/espressif__esp_codec_dev/README.md
Normal file
238
managed_components/espressif__esp_codec_dev/README.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# ESP Codec Device
|
||||
- [](https://components.espressif.com/components/espressif/esp_codec_dev)
|
||||
- [中文版本](./README_CN.md)
|
||||
|
||||
## General Information
|
||||
|
||||
`esp_codec_dev` is a driver component for audio codec devices. Following features are supported currently:
|
||||
* Support driver for common audio codec devices
|
||||
* Support multiple instance of codec devices (including device of same type)
|
||||
* Add unified abstract interface to operate on codec device
|
||||
* Support customized codec realization based on provided interface
|
||||
* Easy-to-use high-level API for playback and recording
|
||||
* Support for volume adjustment in software when it is not supported in hardware
|
||||
* Support customized volume curve and customized volume control
|
||||
* Easy to port to other platform after replacing codes under [platform](./platform)
|
||||
|
||||
The currently supported codec devices are listed as below:
|
||||
|
||||
| |Playback|Record|
|
||||
| :-----| :---- | :---- |
|
||||
|ES8311 |Y|Y|
|
||||
|ES8388 |Y|Y|
|
||||
|ES8374 |Y|Y|
|
||||
|ZL38063 |Y|Y|
|
||||
|TAS6805M |Y|N|
|
||||
|AW88298 |Y|N|
|
||||
|ES7210 |N|Y|
|
||||
|ES7243 |N|Y|
|
||||
|ES7243E |N|Y|
|
||||
|ES8156 |N|Y|
|
||||
|
||||
|
||||
## Architecture overview
|
||||
|
||||
Hardware connection and software architecture are introduced respectively by taking the codec device (ES8311) as an example.
|
||||
The hardware connection diagram between the codec device (ES8311) and the main IC (ESP32-S3) is as below:
|
||||
```mermaid
|
||||
graph LR;
|
||||
subgraph Mic
|
||||
Microphone
|
||||
end
|
||||
subgraph ESP32-S3
|
||||
I2C_Bus
|
||||
I2S_Bus
|
||||
PA_GPIO
|
||||
end
|
||||
subgraph ES8311
|
||||
ADC
|
||||
DAC
|
||||
I2C_Port
|
||||
I2S_Port
|
||||
end
|
||||
subgraph NS4150
|
||||
IN
|
||||
CTRL
|
||||
OUT
|
||||
end
|
||||
subgraph "Left Speaker"
|
||||
Speaker
|
||||
end
|
||||
I2C_Bus --> I2C_Port
|
||||
I2S_Bus --> I2S_Port
|
||||
DAC --> IN
|
||||
OUT --> Speaker
|
||||
PA_GPIO --> CTRL
|
||||
Microphone --> ADC
|
||||
```
|
||||
|
||||
ESP32-S3 sends control data to ES8311 through I2C bus and exchanges audio data through I2S bus. During playing, ES8311 receives digital audio data from I2S bus and performs DAC operation, then the analog signal is amplified by PA chip (NS4150) and finally is output through the speaker. During recording, ES8311 gets the analog signal from the microphone, amplifies it, and performs ADC operation, then digital audio data can be obtained from ESP32-S3.
|
||||
|
||||
Communication between ESP32-S3 and ES8311 mainly occurs on two paths:
|
||||
* Control path: Set up the codec chip (using I2C bus)
|
||||
* Data path: Exchange audio data (using I2S bus)
|
||||
|
||||
In software architecture, the above hardware behavior is abstracted as:
|
||||
```mermaid
|
||||
classDiagram
|
||||
direction LR;
|
||||
class audio_codec_ctrl_if_t {
|
||||
open()
|
||||
read_reg()
|
||||
write_reg()
|
||||
close()
|
||||
}
|
||||
|
||||
class audio_codec_gpio_if_t {
|
||||
setup()
|
||||
set()
|
||||
get()
|
||||
}
|
||||
|
||||
class es8311_codec_cfg_t {
|
||||
audio_codec_ctrl_if_t *ctrl_if
|
||||
audio_codec_gpio_if_t *gpio_if
|
||||
int16_t pa_pin
|
||||
esp_codec_dev_hw_gain_t hw_gain
|
||||
}
|
||||
|
||||
class audio_codec_if_t {
|
||||
audio_codec_ctrl_if_t* ctrl_if
|
||||
open()
|
||||
enable()
|
||||
set_fs()
|
||||
set_vol()
|
||||
set_mic_gain()
|
||||
close()
|
||||
}
|
||||
|
||||
class audio_codec_data_if_t {
|
||||
open()
|
||||
set_fmt()
|
||||
read()
|
||||
write()
|
||||
close()
|
||||
}
|
||||
|
||||
class esp_codec_dev {
|
||||
audio_codec_data_if_t* data_if
|
||||
audio_codec_if_t* codec_if
|
||||
esp_codec_dev_new()
|
||||
esp_codec_dev_open()
|
||||
esp_codec_dev_read()
|
||||
esp_codec_dev_write()
|
||||
esp_codec_dev_set_out_vol()
|
||||
esp_codec_dev_set_in_gain()
|
||||
esp_codec_dev_set_vol_curve()
|
||||
esp_codec_dev_set_vol_handler()
|
||||
esp_codec_dev_close()
|
||||
}
|
||||
|
||||
audio_codec_ctrl_if_t ..> es8311_codec_cfg_t
|
||||
audio_codec_gpio_if_t ..> es8311_codec_cfg_t
|
||||
es8311_codec_cfg_t ..> audio_codec_if_t
|
||||
audio_codec_if_t ..> esp_codec_dev
|
||||
audio_codec_data_if_t ..> esp_codec_dev
|
||||
```
|
||||
|
||||
`esp_codec_dev` abstracts the above communication path into two interfaces:
|
||||
* `audio_codec_ctrl_if_t` for the control path:
|
||||
The control interface mainly offers `read_reg` and `write_reg` APIs to do codec setup
|
||||
Commonly used control channels include I2C, SPI, etc
|
||||
* `audio_codec_data_if_t` for data path:
|
||||
The data interface mainly offers `read` and `write` APIs to exchange audio data
|
||||
Commonly used data channels include I2S, SPI, etc
|
||||
|
||||
`esp_codec_dev` provides users with convenient high-level API to implement playback and recording functions. It is composed of `audio_codec_data_if_t` and `audio_codec_if_t`. `audio_codec_if_t` abstracts codec control operations and constructed by specified codec configuration (configured by `audio_codec_ctrl_if_t` and `audio_codec_gpio_if_t` through `es8311_codec_cfg_t`). `audio_codec_gpio_if_t` abstracts the IO control to adapt to the main control IO or the expansion chip IO, and called inside the codec to match the unique set timing.
|
||||
|
||||
## DAC Volume setting
|
||||
|
||||
Volume setting is realized by common API: `esp_codec_dev_set_out_vol`.
|
||||
`esp_codec_dev` supports the following volume setup methods:
|
||||
1. Use codec register to adjust the volume
|
||||
2. Use built-in software volume `audio_codec_new_sw_vol` when codec hardware does not support volume adjustment
|
||||
3. Use customized software volume interface through `esp_codec_dev_set_vol_handler`
|
||||
|
||||
The default volume range is 0 - 100. Volume [1:100] is mapped to [-49.5 dB:0 dB] with the scale being 0.5 dB. Volume 0 is mapped to -96 dB. To change this mapping, you can define your own volume curve through API `esp_codec_dev_set_vol_curve`. The volume curve is an array of `esp_codec_dev_vol_map_t` which uses linear interpolation to calculate the decibel value at a certain volume point internally (please sort volume maps in advance).
|
||||
To balance the speaker's loudness across different platforms when playing the same content, you need to know some mechanism of the audio gain. In short, audio gain consists of two parts: software gain (adjustable) and hardware gain (fixed). Software gain can be adjusted by changing the input PCM data level or setting the codec volume register. The hardware gain is affected by the peripheral circuit, mainly by the amplification efficiency of the analog signal. The typical impact parameter of hardware gain is extracted into `esp_codec_dev_hw_gain_t`, which can be configured to codec devices to ensure loudness consistency. For more details, please refer to the comments in [esp_codec_dev_vol.h](include/esp_codec_dev_vol.h).
|
||||
|
||||
## Usage
|
||||
|
||||
The steps below take the ES8311 codec as an example to illustrate how to play and record audio.
|
||||
1. Install the driver for codec control and data bus referring to [test_board.c](test_apps/codec_dev_test/main/test_board.c)
|
||||
```c
|
||||
ut_i2c_init(0);
|
||||
ut_i2s_init(0);
|
||||
```
|
||||
2. Create the control and data interfaces for the codec using the interface provided by default
|
||||
```c
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.rx_handle = i2s_keep[0]->rx_handle,
|
||||
.tx_handle = i2s_keep[0]->tx_handle,
|
||||
#endif
|
||||
};
|
||||
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {.addr = ES8311_CODEC_DEFAULT_ADDR};
|
||||
const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
```
|
||||
|
||||
3. Create the codec interface based on control interface and codec-specified configuration
|
||||
```c
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
|
||||
.ctrl_if = out_ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.pa_pin = YOUR_PA_GPIO,
|
||||
.use_mclk = true,
|
||||
};
|
||||
const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg);
|
||||
```
|
||||
|
||||
4. Get `esp_codec_dev_handle_t` through `esp_codec_dev_new`
|
||||
Now you can use the handle for further playback and recording as follows:
|
||||
```c
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.codec_if = out_codec_if; // codec interface from es8311_codec_new
|
||||
.data_if = data_if; // data interface from audio_codec_new_i2s_data
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT; // codec support both playback and record
|
||||
};
|
||||
esp_codec_dev_handle_t codec_dev = esp_codec_dev_new(&dev_cfg);
|
||||
// Below code shows how to play
|
||||
esp_codec_dev_set_out_vol(codec_dev, 60.0);
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.sample_rate = 48000,
|
||||
.channel = 2,
|
||||
.bits_per_sample = 16,
|
||||
};
|
||||
esp_codec_dev_open(codec_dev, &fs);
|
||||
uint8_t data[256];
|
||||
esp_codec_dev_write(codec_dev, data, sizeof(data));
|
||||
|
||||
// Below code shows how to record
|
||||
esp_codec_dev_set_in_gain(codec_dev, 30.0);
|
||||
esp_codec_dev_read(codec_dev, data, sizeof(data));
|
||||
esp_codec_dev_close(codec_dev);
|
||||
```
|
||||
|
||||
|
||||
## How to customize for new codec device
|
||||
|
||||
1. Implement `audio_codec_ctrl_if_t` and `audio_codec_data_if_t`
|
||||
If you are using I2C bus for control and I2S bus for data, you can use the implementation provided by default:
|
||||
`audio_codec_new_i2c_ctrl` and `audio_codec_new_i2s_data`
|
||||
|
||||
2. Implement `audio_codec_if_t` based on the interface built in step 1
|
||||
```c
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< If you want to operate GPIO */
|
||||
//................................... Other settings
|
||||
} my_codec_cfg_t;
|
||||
const audio_codec_if_t *my_codec_new(my_codec_cfg_t *codec_cfg);
|
||||
```
|
||||
|
||||
For details, refer to the sample code [my_codec.c](test_apps/codec_dev_test/main/my_codec.c).
|
||||
237
managed_components/espressif__esp_codec_dev/README_CN.md
Normal file
237
managed_components/espressif__esp_codec_dev/README_CN.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# ESP Codec Device
|
||||
- [](https://components.espressif.com/components/espressif/esp_codec_dev)
|
||||
- [English version](./README.md)
|
||||
|
||||
## 概要
|
||||
|
||||
`esp_codec_dev` 是为音频编解码器设备提供驱动的组件,目前支持以下功能:
|
||||
* 提供常用音频编解码器设备的驱动
|
||||
* 支持音频编解码器设备的多实例 (包括同类型设备)
|
||||
* 提供统一的抽象化接口来操作编解码器设备
|
||||
* 支持客户定制化编解码器设备 (仅需实例化提供的接口)
|
||||
* 为播放和录音提供易用的上层 API
|
||||
* 支持软件音量调节 (硬件不支持音量调节时)
|
||||
* 支持定制化音量曲线以及音量控制实现
|
||||
* 兼容多平台仅需替换 [platform](./platform)
|
||||
|
||||
已经支持的编解码器设备如下:
|
||||
| |播放|录音|
|
||||
| :-----| :---- | :---- |
|
||||
|ES8311 |Y|Y|
|
||||
|ES8388 |Y|Y|
|
||||
|ES8374 |Y|Y|
|
||||
|ZL38063 |Y|Y|
|
||||
|TAS6805M |Y|N|
|
||||
|AW88298 |Y|N|
|
||||
|ES7210 |N|Y|
|
||||
|ES7243 |N|Y|
|
||||
|ES7243E |N|Y|
|
||||
|ES8156 |N|Y|
|
||||
|
||||
|
||||
## 架构预览
|
||||
|
||||
以编解码器设备 (ES8311) 为例,下面分别介绍硬件框图和软件架构。
|
||||
编解码器设备 (ES8311) 和主芯片(ESP32-S3) 之间的硬件连接简图如下:
|
||||
```mermaid
|
||||
graph LR;
|
||||
subgraph "麦克风"
|
||||
Microphone
|
||||
end
|
||||
subgraph ESP32-S3
|
||||
I2C总线
|
||||
I2S总线
|
||||
PA_GPIO
|
||||
end
|
||||
subgraph ES8311
|
||||
ADC
|
||||
DAC
|
||||
I2C端口
|
||||
I2S端口
|
||||
end
|
||||
subgraph NS4150
|
||||
IN
|
||||
CTRL
|
||||
OUT
|
||||
end
|
||||
subgraph "扬声器"
|
||||
Speaker
|
||||
end
|
||||
I2C总线 --> I2C端口
|
||||
I2S总线 --> I2S端口
|
||||
DAC --> IN
|
||||
OUT --> Speaker
|
||||
PA_GPIO --> CTRL
|
||||
Microphone --> ADC
|
||||
```
|
||||
|
||||
ESP32-S3 通过 I2C 总线向 ES8311 发送控制命令,通过 I2S 总线传递音频数据。在播放过程中, ES8311 从 I2S 总线接收数字音频数据进行数模转换后发送给功放芯片(NS4150), 最后发送给扬声器输出声音。在录音过程中,ES8311将从麦克风采集到的模拟信号放大,进行模数转换后发送给 ESP32-S3。
|
||||
ESP32-S3 同 ES8311 在以下两个通道进行通讯:
|
||||
1. 控制通道:用来配置编解码器设备 (通过 I2C 总线)
|
||||
2. 数据通道: 用来交换音频数据 (通过 I2S 总线)
|
||||
|
||||
软件架构上,对硬件行为进行了下述抽象:
|
||||
```mermaid
|
||||
classDiagram
|
||||
direction LR;
|
||||
class audio_codec_ctrl_if_t {
|
||||
open()
|
||||
read_reg()
|
||||
write_reg()
|
||||
close()
|
||||
}
|
||||
|
||||
class audio_codec_gpio_if_t {
|
||||
setup()
|
||||
set()
|
||||
get()
|
||||
}
|
||||
|
||||
class es8311_codec_cfg_t {
|
||||
audio_codec_ctrl_if_t *ctrl_if
|
||||
audio_codec_gpio_if_t *gpio_if
|
||||
int16_t pa_pin
|
||||
esp_codec_dev_hw_gain_t hw_gain
|
||||
}
|
||||
|
||||
class audio_codec_if_t {
|
||||
audio_codec_ctrl_if_t* ctrl_if
|
||||
open()
|
||||
enable()
|
||||
set_fs()
|
||||
set_vol()
|
||||
set_mic_gain()
|
||||
close()
|
||||
}
|
||||
|
||||
class audio_codec_data_if_t {
|
||||
open()
|
||||
set_fmt()
|
||||
read()
|
||||
write()
|
||||
close()
|
||||
}
|
||||
|
||||
class esp_codec_dev {
|
||||
audio_codec_data_if_t* data_if
|
||||
audio_codec_if_t* codec_if
|
||||
esp_codec_dev_new()
|
||||
esp_codec_dev_open()
|
||||
esp_codec_dev_read()
|
||||
esp_codec_dev_write()
|
||||
esp_codec_dev_set_out_vol()
|
||||
esp_codec_dev_set_in_gain()
|
||||
esp_codec_dev_set_vol_curve()
|
||||
esp_codec_dev_set_vol_handler()
|
||||
esp_codec_dev_close()
|
||||
}
|
||||
|
||||
audio_codec_ctrl_if_t ..> es8311_codec_cfg_t
|
||||
audio_codec_gpio_if_t ..> es8311_codec_cfg_t
|
||||
es8311_codec_cfg_t ..> audio_codec_if_t
|
||||
audio_codec_if_t ..> esp_codec_dev
|
||||
audio_codec_data_if_t ..> esp_codec_dev
|
||||
```
|
||||
|
||||
通讯通道抽象为两种接口:
|
||||
* `audio_codec_ctrl_if_t` 控制接口:
|
||||
主要提供 `read_reg` 和 `write_reg` API 来配置编解码器设备
|
||||
常用控制通道包括 I2C, SPI 等
|
||||
* `audio_codec_data_if_t` 数据接口:
|
||||
主要提供 `read` 和 `write` API 用来交换音频数据
|
||||
常用数据通道包括 I2S, SPI 等
|
||||
|
||||
`esp_codec_dev` 为用户提供便捷的上层 API 来实现播放和录音功能。它是由 `audio_codec_data_if_t` 和 `audio_codec_if_t` 组成。`audio_codec_if_t` 对编解码器控制操作进行抽象,通过编解码器特有的配置参数构建(由 `audio_codec_ctrl_if_t` 和`audio_codec_gpio_if_t` 通过 `es8311_codec_cfg_t` 进行配置)。`audio_codec_gpio_if_t ` 对 IO 控制进行抽象,以适配主控 IO 或者扩展芯片 IO, 在编解码器内部进行调用用以匹配特有的设定时序。
|
||||
|
||||
## 解码器音量设定
|
||||
|
||||
音量统一通过 API `esp_codec_dev_set_out_vol` 进行设定。
|
||||
`esp_codec_dev` 支持以下音量设定实现:
|
||||
1. 通过调节音量寄存器实现
|
||||
2. 在硬件不支持音量调节下,可以使用内置的软件音量实现 `audio_codec_new_sw_vol`
|
||||
3. 客制化音量接口实现,并通过 `esp_codec_dev_set_vol_handler` 设定
|
||||
|
||||
默认的音量调节区间是 0 - 100,音量 100 对应为 0 dB,每个刻度对应 0.5 dB,音量 0 被特殊映射为 -96 dB。用户可以通过 `esp_codec_dev_set_vol_curve` 设定音量曲线来改变这一行为。音量曲线是音量映射 `esp_codec_dev_vol_map_t` 的数组,通过线性插值的方法来计算每一个音量对应的 dB 值 (请预先将音量映射排好序)。
|
||||
|
||||
为了平衡在不同平台上播放相同内容的响度差异,需要了解音频增益的相关机制。简单说来音频增益包括软件增益 (可调节)和硬件增益 (不可调节) 两部分。软件增益可以通过改变音频数据的幅值或者改变音量寄存器实现。硬件增益受外围电路的影响,主要取决于模拟信号的放大系数。实现中选取了典型的影响参数 `esp_codec_dev_hw_gain_t`,作为配置参数进行配置,以抵消平台间响度差异,详情可参考代码注释 [esp_codec_dev_vol.h](include/esp_codec_dev_vol.h)。
|
||||
|
||||
## 使用方法
|
||||
|
||||
以 ES8311 为例,下面将演示播放和录音的具体步骤
|
||||
1. 为编解码器设备的控制和数据总线安装驱动,可参考[test_board.c](test_apps/codec_dev_test/main/test_board.c)
|
||||
```c
|
||||
ut_i2c_init(0);
|
||||
ut_i2s_init(0);
|
||||
```
|
||||
|
||||
2. 为编解码器设备实现控制接口,数据接口和 GPIO 接口 (使用默认提供的接口实现)
|
||||
```c
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.rx_handle = i2s_keep[0]->rx_handle,
|
||||
.tx_handle = i2s_keep[0]->tx_handle,
|
||||
#endif
|
||||
};
|
||||
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {.addr = ES8311_CODEC_DEFAULT_ADDR};
|
||||
const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
```
|
||||
|
||||
3. 基于控制接口和 ES8311 特有的配置实现 `audio_codec_if_t` 接口
|
||||
```c
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
|
||||
.ctrl_if = out_ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.pa_pin = YOUR_PA_GPIO,
|
||||
.use_mclk = true,
|
||||
};
|
||||
const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg);
|
||||
```
|
||||
|
||||
4. 通过 API `esp_codec_dev_new` 获取 `esp_codec_dev_handle_t` 句柄
|
||||
参考下面代码用获取到的句柄来进行播放和录制操作:
|
||||
```c
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.codec_if = out_codec_if; // es8311_codec_new 获取到的接口实现
|
||||
.data_if = data_if; // audio_codec_new_i2s_data 获取到的数据接口实现
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT; // 设备同时支持录制和播放
|
||||
};
|
||||
esp_codec_dev_handle_t codec_dev = esp_codec_dev_new(&dev_cfg);
|
||||
// 以下代码展示如何播放音频
|
||||
esp_codec_dev_set_out_vol(codec_dev, 60.0);
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.sample_rate = 48000,
|
||||
.channel = 2,
|
||||
.bits_per_sample = 16,
|
||||
};
|
||||
esp_codec_dev_open(codec_dev, &fs);
|
||||
uint8_t data[256];
|
||||
esp_codec_dev_write(codec_dev, data, sizeof(data));
|
||||
|
||||
// 以下代码展示如何录制音频
|
||||
esp_codec_dev_set_in_gain(codec_dev, 30.0);
|
||||
esp_codec_dev_read(codec_dev, data, sizeof(data));
|
||||
esp_codec_dev_close(codec_dev);
|
||||
```
|
||||
|
||||
## 客制化编解码器设备
|
||||
|
||||
1. 实现接口 `audio_codec_ctrl_if_t` 和 `audio_codec_data_if_t`
|
||||
如果使用 I2C 总线作控制,I2S 总线做数据传输,可以使用默认的接口实现:
|
||||
`audio_codec_new_i2c_ctrl` 和 `audio_codec_new_i2s_data`
|
||||
|
||||
2. 在第一步的基础上实现接口 `audio_codec_if_t`
|
||||
```c
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< 创建的控制接口 */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< 需要操作 GPIO */
|
||||
//................................... 其他设定
|
||||
} my_codec_cfg_t;
|
||||
const audio_codec_if_t *my_codec_new(my_codec_cfg_t *codec_cfg);
|
||||
```
|
||||
|
||||
更细节的实现可以参考测试代码 [my_codec.c](test_apps/codec_dev_test/main/my_codec.c)。
|
||||
140
managed_components/espressif__esp_codec_dev/audio_codec_sw_vol.c
Normal file
140
managed_components/espressif__esp_codec_dev/audio_codec_sw_vol.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "audio_codec_sw_vol.h"
|
||||
|
||||
#define GAIN_0DB_SHIFT (15)
|
||||
|
||||
typedef struct {
|
||||
audio_codec_vol_if_t base;
|
||||
esp_codec_dev_sample_info_t fs;
|
||||
uint16_t gain;
|
||||
bool is_open;
|
||||
int cur;
|
||||
int step;
|
||||
int block_size;
|
||||
int duration;
|
||||
} audio_vol_t;
|
||||
|
||||
static int _sw_vol_close(const audio_codec_vol_if_t *h)
|
||||
{
|
||||
audio_vol_t *vol = (audio_vol_t *)h;
|
||||
if (h) {
|
||||
vol->is_open = false;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
|
||||
static int _sw_vol_open(const audio_codec_vol_if_t *h, esp_codec_dev_sample_info_t *fs, int duration)
|
||||
{
|
||||
audio_vol_t *vol = (audio_vol_t *)h;
|
||||
if (vol == NULL || fs == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (fs->bits_per_sample != 16) {
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
vol->fs = *fs;
|
||||
vol->block_size = (vol->fs.bits_per_sample * vol->fs.channel) >> 3;
|
||||
vol->duration = duration;
|
||||
vol->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _sw_vol_process(const audio_codec_vol_if_t *h, uint8_t *in, int len,
|
||||
uint8_t *out, int out_len)
|
||||
{
|
||||
audio_vol_t *vol = (audio_vol_t *) h;
|
||||
if (vol == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (vol->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int sample = len / vol->block_size;
|
||||
if (vol->fs.bits_per_sample == 16) {
|
||||
int16_t *v_in = (int16_t *) in;
|
||||
int16_t *v_out = (int16_t *) out;
|
||||
if (vol->cur == vol->gain) {
|
||||
if (vol->gain == 0) {
|
||||
memset(out, 0, len);
|
||||
return 0;
|
||||
} else {
|
||||
for (int i = 0; i < sample; i++) {
|
||||
for (int j = 0; j < vol->fs.channel; j++) {
|
||||
*(v_out++) = ((*v_in++) * vol->cur) >> GAIN_0DB_SHIFT;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < sample; i++) {
|
||||
for (int j = 0; j < vol->fs.channel; j++) {
|
||||
*(v_out++) = ((*v_in++) * vol->cur) >> GAIN_0DB_SHIFT;
|
||||
}
|
||||
if (vol->step) {
|
||||
vol->cur += vol->step;
|
||||
if (vol->step > 0) {
|
||||
if (vol->cur > vol->gain) {
|
||||
vol->cur = vol->gain;
|
||||
vol->step = 0;
|
||||
}
|
||||
} else {
|
||||
if (vol->cur < vol->gain) {
|
||||
vol->cur = vol->gain;
|
||||
vol->step = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sw_vol_set(const audio_codec_vol_if_t *h, float db_value)
|
||||
{
|
||||
audio_vol_t *vol = (audio_vol_t *) h;
|
||||
if (vol == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
// Support set volume when not opened
|
||||
int gain;
|
||||
if (db_value <= -96.0) {
|
||||
gain = 0;
|
||||
} else {
|
||||
gain = (int) (exp(db_value / 20 * log(10)) * (1 << GAIN_0DB_SHIFT));
|
||||
}
|
||||
vol->gain = gain;
|
||||
if (vol->is_open) {
|
||||
float step = (float) (vol->gain - vol->cur) * 1000 / vol->duration / vol->fs.sample_rate;
|
||||
vol->step = (int) step;
|
||||
if (step == 0) {
|
||||
vol->cur = vol->gain;
|
||||
}
|
||||
} else {
|
||||
vol->step = 0;
|
||||
vol->cur = vol->gain;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
const audio_codec_vol_if_t *audio_codec_new_sw_vol(void)
|
||||
{
|
||||
audio_vol_t *vol = calloc(1, sizeof(audio_vol_t));
|
||||
if (vol == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vol->base.open = _sw_vol_open;
|
||||
vol->base.set_vol = _sw_vol_set;
|
||||
vol->base.process = _sw_vol_process;
|
||||
vol->base.close = _sw_vol_close;
|
||||
// Default no audio output
|
||||
vol->cur = vol->gain = 0;
|
||||
return &vol->base;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AUDIO_CODEC_SW_VOL_H_
|
||||
#define _AUDIO_CODEC_SW_VOL_H_
|
||||
|
||||
#include "audio_codec_vol_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief New software volume processor interface
|
||||
* Notes: currently only support 16bits input
|
||||
* @return NULL: Memory not enough
|
||||
* -Others: Software volume interface handle
|
||||
*/
|
||||
const audio_codec_vol_if_t* audio_codec_new_sw_vol(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "aw88298_dac.h"
|
||||
#include "aw88298_reg.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "AW88298"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
int16_t reset_pin;
|
||||
float hw_gain;
|
||||
} audio_codec_aw88298_t;
|
||||
|
||||
/* The volume register mapped to decibel table can get from codec data-sheet
|
||||
Volume control register 0x0C description:
|
||||
0xC0 - '-96dB' ... 0x00 - '+0dB'
|
||||
*/
|
||||
const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 0xC0,
|
||||
.db_value = -96,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0,
|
||||
.db_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static int aw88298_write_reg(audio_codec_aw88298_t *codec, int reg, int value)
|
||||
{
|
||||
uint8_t write_data[2] = {(uint8_t) ((value & 0xFFFF) >> 8), (uint8_t) (value & 0xFF)};
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, write_data, 2);
|
||||
}
|
||||
|
||||
static int aw88298_read_reg(audio_codec_aw88298_t *codec, int reg, int *value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t read_data[2] = {0};
|
||||
ret = codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, read_data, 2);
|
||||
*value = ((int)read_data[0] << 8) | read_data[1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw88298_set_bits_per_sample(audio_codec_aw88298_t *codec, uint8_t bits)
|
||||
{
|
||||
int ret = 0;
|
||||
int dac_iface = 0;
|
||||
ret = aw88298_read_reg(codec, AW88298_I2SCTRL_REG06, &dac_iface);
|
||||
dac_iface &= ~(0xF0);
|
||||
switch (bits) {
|
||||
case 16:
|
||||
default:
|
||||
break;
|
||||
case 24:
|
||||
dac_iface |= 0x90;
|
||||
break;
|
||||
case 32:
|
||||
dac_iface |= 0xE0;
|
||||
break;
|
||||
}
|
||||
ret |= aw88298_write_reg(codec, AW88298_I2SCTRL_REG06, dac_iface);
|
||||
ESP_LOGD(TAG, "Bits %d", bits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw88298_config_sample(audio_codec_aw88298_t *codec, int sample_rate)
|
||||
{
|
||||
int ret = 0;
|
||||
int dac_iface = 0;
|
||||
ret = aw88298_read_reg(codec, AW88298_I2SCTRL_REG06, &dac_iface);
|
||||
dac_iface &= ~(0x0F);
|
||||
switch (sample_rate) {
|
||||
case 8000:
|
||||
break;
|
||||
case 11025:
|
||||
dac_iface |= 0x01;
|
||||
break;
|
||||
case 12000:
|
||||
dac_iface |= 0x02;
|
||||
break;
|
||||
case 16000:
|
||||
dac_iface |= 0x03;
|
||||
break;
|
||||
case 22050:
|
||||
dac_iface |= 0x04;
|
||||
break;
|
||||
case 24000:
|
||||
dac_iface |= 0x05;
|
||||
break;
|
||||
case 32000:
|
||||
dac_iface |= 0x06;
|
||||
break;
|
||||
case 44100:
|
||||
dac_iface |= 0x07;
|
||||
break;
|
||||
case 48000:
|
||||
dac_iface |= 0x08;
|
||||
break;
|
||||
case 96000:
|
||||
dac_iface |= 0x09;
|
||||
break;
|
||||
case 192000:
|
||||
dac_iface |= 0x0A;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Sample rate(%d) can not support", sample_rate);
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
ESP_LOGD(TAG, "Current sample rate: %d", sample_rate);
|
||||
ret |= aw88298_write_reg(codec, AW88298_I2SCTRL_REG06, dac_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw88298_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = 0;
|
||||
ret |= aw88298_set_bits_per_sample(codec, fs->bits_per_sample);
|
||||
ret |= aw88298_config_sample(codec, fs->sample_rate);
|
||||
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;;
|
||||
}
|
||||
|
||||
static int aw88298_stop(audio_codec_aw88298_t *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
int data = 0;
|
||||
ret |= aw88298_read_reg(codec, AW88298_SYSCTRL_REG04, &data);
|
||||
data |= 0x03;
|
||||
data &= ~(1 << 6);
|
||||
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL_REG04, data);
|
||||
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;;
|
||||
}
|
||||
|
||||
static int aw88298_start(audio_codec_aw88298_t *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
int data = 0;
|
||||
ret |= aw88298_read_reg(codec, AW88298_SYSCTRL_REG04, &data);
|
||||
data &= ~0x03;
|
||||
data |= (1 << 6);
|
||||
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL_REG04, data);
|
||||
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;;
|
||||
}
|
||||
|
||||
static int aw88298_set_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int regv;
|
||||
int ret = aw88298_read_reg(codec, AW88298_SYSCTRL2_REG05, ®v);
|
||||
if (ret < 0) {
|
||||
return ESP_CODEC_DEV_READ_FAIL;
|
||||
}
|
||||
if (mute) {
|
||||
regv = regv | (1 << 4);
|
||||
} else {
|
||||
regv = regv & (~(1 << 4));
|
||||
}
|
||||
return aw88298_write_reg(codec, AW88298_SYSCTRL2_REG05, regv);
|
||||
}
|
||||
|
||||
static int aw88298_set_vol(const audio_codec_if_t *h, float volume)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
volume -= codec->hw_gain;
|
||||
int reg = esp_codec_dev_vol_calc_reg(&vol_range, volume);
|
||||
reg = (reg << 8) | 0x64;
|
||||
int ret = aw88298_write_reg(codec, AW88298_HAGCCFG4_REG0C, reg);
|
||||
ESP_LOGD(TAG, "Set volume reg:%x db:%.2f", reg, esp_codec_dev_vol_calc_db(&vol_range, reg >> 8));
|
||||
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static void aw88298_reset(audio_codec_aw88298_t *codec, int16_t reset_pin)
|
||||
{
|
||||
if (reset_pin <= 0 || codec->gpio_if == NULL) {
|
||||
return;
|
||||
}
|
||||
codec->gpio_if->setup(reset_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->gpio_if->set(reset_pin, 0);
|
||||
esp_codec_dev_sleep(10);
|
||||
codec->gpio_if->set(reset_pin, 1);
|
||||
esp_codec_dev_sleep(50);
|
||||
}
|
||||
|
||||
static int aw88298_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
aw88298_codec_cfg_t *codec_cfg = (aw88298_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || cfg_size != sizeof(aw88298_codec_cfg_t) || codec_cfg->ctrl_if == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->gpio_if = codec_cfg->gpio_if;
|
||||
codec->reset_pin = codec_cfg->reset_pin;
|
||||
aw88298_reset(codec, codec_cfg->reset_pin);
|
||||
|
||||
ret |= aw88298_write_reg(codec, AW88298_RESET_REG00, 0x55aa); // Reset chip
|
||||
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL_REG04, 0x4040); // I2SEN=1 AMPPD=0 PWDN=0
|
||||
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL2_REG05, 0x0008); // RMSE=0 HAGCE=0 HDCCE=0 HMUTE=0
|
||||
ret |= aw88298_write_reg(codec, AW88298_I2SCTRL_REG06, 0x3CC8); // I2SBCK=0 (BCK mode 16*2)
|
||||
ret |= aw88298_write_reg(codec, AW88298_HAGCCFG4_REG0C, 0x3064); // volume setting
|
||||
ret |= aw88298_write_reg(codec, AW88298_BSTCTRL2_REG61, 0x0673); // default:0x6673: BOOST mode disabled
|
||||
|
||||
if (ret != 0) {
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int aw88298_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
aw88298_stop(codec);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int aw88298_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if (enable) {
|
||||
if (codec->reset_pin > 0) {
|
||||
codec->gpio_if->set(codec->reset_pin, 1);
|
||||
esp_codec_dev_sleep(10);
|
||||
}
|
||||
ret = aw88298_start(codec);
|
||||
} else {
|
||||
ret = aw88298_stop(codec);
|
||||
if (codec->reset_pin > 0) {
|
||||
esp_codec_dev_sleep(10);
|
||||
codec->gpio_if->set(codec->reset_pin, 0);
|
||||
}
|
||||
}
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw88298_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return aw88298_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int aw88298_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return aw88298_read_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static void aw88298_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i <= 0x14; i++) {
|
||||
if (i == 0x08 || i == 0x0D || i == 0x0E || i == 0x0F || i == 0x11) {
|
||||
continue;
|
||||
}
|
||||
int value = 0;
|
||||
int ret = aw88298_read_reg(codec, i, &value);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %04x", i, value);
|
||||
}
|
||||
for (int i = 0x60; i <= 0x61; i++) {
|
||||
int value = 0;
|
||||
int ret = aw88298_read_reg(codec, i, &value);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %04x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *aw88298_codec_new(aw88298_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) calloc(1, sizeof(audio_codec_aw88298_t));
|
||||
if (codec == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->base.open = aw88298_open;
|
||||
codec->base.enable = aw88298_enable;
|
||||
codec->base.set_fs = aw88298_set_fs;
|
||||
codec->base.set_vol = aw88298_set_vol;
|
||||
codec->base.mute = aw88298_set_mute;
|
||||
codec->base.set_reg = aw88298_set_reg;
|
||||
codec->base.get_reg = aw88298_get_reg;
|
||||
codec->base.dump_reg = aw88298_dump;
|
||||
codec->base.close = aw88298_close;
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(aw88298_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
free(codec);
|
||||
return NULL;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AW88298_REG_H_
|
||||
#define _AW88298_REG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* AW88298 register space */
|
||||
|
||||
/*
|
||||
* RESET Control
|
||||
*/
|
||||
#define AW88298_RESET_REG00 0x00
|
||||
|
||||
/*
|
||||
* System Control
|
||||
*/
|
||||
#define AW88298_SYSST_REG01 0x01
|
||||
|
||||
/*
|
||||
* System interrupt
|
||||
*/
|
||||
#define AW88298_SYSINT_REG02 0x02
|
||||
#define AW88298_SYSINTM_REG03 0x03
|
||||
|
||||
/*
|
||||
* System Control
|
||||
*/
|
||||
#define AW88298_SYSCTRL_REG04 0x04
|
||||
#define AW88298_SYSCTRL2_REG05 0x05
|
||||
|
||||
/*
|
||||
* I2S control and config
|
||||
*/
|
||||
#define AW88298_I2SCTRL_REG06 0x06
|
||||
#define AW88298_I2SCFG1_REG07 0x07
|
||||
|
||||
/*
|
||||
* HAGC config
|
||||
*/
|
||||
#define AW88298_HAGCCFG1_REG09 0x09
|
||||
#define AW88298_HAGCCFG2_REG0A 0x0a
|
||||
#define AW88298_HAGCCFG3_REG0B 0x0b
|
||||
#define AW88298_HAGCCFG4_REG0C 0x0C
|
||||
|
||||
/*
|
||||
* HAGC boost output voltage
|
||||
*/
|
||||
#define AW88298_HAGCST_REG10 0x10
|
||||
|
||||
/*
|
||||
* Detected voltage of battery
|
||||
*/
|
||||
#define AW88298_VDD_REG12 0x12
|
||||
|
||||
/*
|
||||
* Detected die temperature
|
||||
*/
|
||||
#define AW88298_TEMP_REG13 0x13
|
||||
|
||||
/*
|
||||
* Detected voltage of PVDD
|
||||
*/
|
||||
#define AW88298_PVDD_REG14 0x14
|
||||
|
||||
/*
|
||||
* Smart boost control
|
||||
*/
|
||||
#define AW88298_BSTCTRL1_REG60 0x60
|
||||
#define AW88298_BSTCTRL2_REG61 0x61
|
||||
|
||||
/*
|
||||
* Chip Information
|
||||
*/
|
||||
#define AW88298_CHIP_VERSION_REG00 0x00 // ID: 1852h
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _AW88298_REG_H_ */
|
||||
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "es7210_adc.h"
|
||||
#include "es7210_reg.h"
|
||||
#include "es_common.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#define I2S_DSP_MODE 0
|
||||
#define ENABLE_TDM_MAX_NUM 3
|
||||
#define TAG "ES7210"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
es7210_input_mics_t mic_select;
|
||||
es7210_gain_value_t gain;
|
||||
bool master_mode;
|
||||
uint8_t off_reg;
|
||||
uint16_t mclk_div;
|
||||
} audio_codec_es7210_t;
|
||||
|
||||
/*
|
||||
* Clock coefficient structure
|
||||
*/
|
||||
struct _coeff_div {
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t lrck; /* lrck */
|
||||
uint8_t ss_ds;
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dll; /* dll_bypass */
|
||||
uint8_t doubler; /* doubler enable */
|
||||
uint8_t osr; /* adc osr */
|
||||
uint8_t mclk_src; /* select mclk source */
|
||||
uint32_t lrck_h; /* The high 4 bits of lrck */
|
||||
uint32_t lrck_l; /* The low 8 bits of lrck */
|
||||
};
|
||||
|
||||
/* Codec hifi mclk clock divider coefficients
|
||||
* MEMBER REG
|
||||
* mclk: 0x03
|
||||
* lrck: standard
|
||||
* ss_ds: --
|
||||
* adc_div: 0x02
|
||||
* dll: 0x06
|
||||
* doubler: 0x02
|
||||
* osr: 0x07
|
||||
* mclk_src: 0x03
|
||||
* lrckh: 0x04
|
||||
* lrckl: 0x05
|
||||
*/
|
||||
static const struct _coeff_div coeff_div[] = {
|
||||
// mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl
|
||||
/* 8k */
|
||||
{12288000, 8000, 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00},
|
||||
{16384000, 8000, 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00},
|
||||
{19200000, 8000, 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60},
|
||||
{4096000, 8000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40},
|
||||
|
||||
/* 16k */
|
||||
{4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80},
|
||||
{16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20},
|
||||
|
||||
/* 32k */
|
||||
{8192000, 32000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80},
|
||||
{16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90},
|
||||
|
||||
/* 64k */
|
||||
{16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
{19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
|
||||
};
|
||||
|
||||
static int es7210_write_reg(audio_codec_es7210_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int es7210_read_reg(audio_codec_es7210_t *codec, int reg, int *value)
|
||||
{
|
||||
*value = 0;
|
||||
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
|
||||
static int es7210_update_reg_bit(audio_codec_es7210_t *codec, uint8_t reg_addr, uint8_t update_bits, uint8_t data)
|
||||
{
|
||||
int regv = 0;
|
||||
es7210_read_reg(codec, reg_addr, ®v);
|
||||
regv = (regv & (~update_bits)) | (update_bits & data);
|
||||
return es7210_write_reg(codec, reg_addr, regv);
|
||||
}
|
||||
|
||||
static int get_coeff(uint32_t mclk, uint32_t lrck)
|
||||
{
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int es7210_config_sample(audio_codec_es7210_t *codec, int sample_fre)
|
||||
{
|
||||
if (codec->master_mode == false) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int regv;
|
||||
int coeff;
|
||||
int mclk_fre = 0;
|
||||
int ret = 0;
|
||||
mclk_fre = sample_fre * codec->mclk_div;
|
||||
coeff = get_coeff(mclk_fre, sample_fre);
|
||||
if (coeff < 0) {
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Set clock parameters */
|
||||
if (coeff >= 0) {
|
||||
/* Set adc_div & doubler & dll */
|
||||
ret |= es7210_read_reg(codec, ES7210_MAINCLK_REG02, ®v);
|
||||
regv &= 0x00;
|
||||
regv |= coeff_div[coeff].adc_div;
|
||||
regv |= coeff_div[coeff].doubler << 6;
|
||||
regv |= coeff_div[coeff].dll << 7;
|
||||
ret |= es7210_write_reg(codec, ES7210_MAINCLK_REG02, regv);
|
||||
/* Set osr */
|
||||
regv = coeff_div[coeff].osr;
|
||||
ret |= es7210_write_reg(codec, ES7210_OSR_REG07, regv);
|
||||
/* Set lrck */
|
||||
regv = coeff_div[coeff].lrck_h;
|
||||
ret |= es7210_write_reg(codec, ES7210_LRCK_DIVH_REG04, regv);
|
||||
regv = coeff_div[coeff].lrck_l;
|
||||
ret |= es7210_write_reg(codec, ES7210_LRCK_DIVL_REG05, regv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool es7210_is_tdm_mode(audio_codec_es7210_t *codec)
|
||||
{
|
||||
uint16_t mic_num = 0;
|
||||
for (int i = ES7210_INPUT_MIC1; i <= ES7210_INPUT_MIC4; i = i << 1) {
|
||||
if (codec->mic_select & i) {
|
||||
mic_num++;
|
||||
}
|
||||
}
|
||||
return (mic_num >= ENABLE_TDM_MAX_NUM);
|
||||
}
|
||||
|
||||
static int es7210_mic_select(audio_codec_es7210_t *codec, es7210_input_mics_t mic)
|
||||
{
|
||||
int ret = 0;
|
||||
if (codec->mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00);
|
||||
}
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0xff);
|
||||
if (codec->mic_select & ES7210_INPUT_MIC1) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1");
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0x00);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43, 0x10, 0x10);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43, 0x0f, codec->gain);
|
||||
}
|
||||
if (codec->mic_select & ES7210_INPUT_MIC2) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2");
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0x00);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC2_GAIN_REG44, 0x10, 0x10);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC2_GAIN_REG44, 0x0f, codec->gain);
|
||||
}
|
||||
if (codec->mic_select & ES7210_INPUT_MIC3) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3");
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0x00);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC3_GAIN_REG45, 0x10, 0x10);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC3_GAIN_REG45, 0x0f, codec->gain);
|
||||
}
|
||||
if (codec->mic_select & ES7210_INPUT_MIC4) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4");
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0x00);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC4_GAIN_REG46, 0x10, 0x10);
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC4_GAIN_REG46, 0x0f, codec->gain);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Microphone selection error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (es7210_is_tdm_mode(codec)) {
|
||||
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE2_REG12, 0x02);
|
||||
ESP_LOGI(TAG, "Enable TDM mode");
|
||||
} else {
|
||||
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE2_REG12, 0x00);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7210_config_fmt(audio_codec_es7210_t *codec, es_i2s_fmt_t fmt)
|
||||
{
|
||||
int ret = 0;
|
||||
int adc_iface = 0;
|
||||
ret = es7210_read_reg(codec, ES7210_SDP_INTERFACE1_REG11, &adc_iface);
|
||||
adc_iface &= 0xfc;
|
||||
switch (fmt) {
|
||||
case ES_I2S_NORMAL:
|
||||
ESP_LOGD(TAG, "ES7210 in I2S Format");
|
||||
adc_iface |= 0x00;
|
||||
break;
|
||||
case ES_I2S_LEFT:
|
||||
case ES_I2S_RIGHT:
|
||||
ESP_LOGD(TAG, "ES7210 in LJ Format");
|
||||
adc_iface |= 0x01;
|
||||
break;
|
||||
case ES_I2S_DSP:
|
||||
if (I2S_DSP_MODE) {
|
||||
ESP_LOGD(TAG, "ES7210 in DSP-A Format");
|
||||
adc_iface |= 0x13;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "ES7210 in DSP-B Format");
|
||||
adc_iface |= 0x03;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
adc_iface &= 0xfc;
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE1_REG11, adc_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7210_set_bits(audio_codec_es7210_t *codec, uint8_t bits)
|
||||
{
|
||||
int ret = 0;
|
||||
int adc_iface = 0;
|
||||
ret = es7210_read_reg(codec, ES7210_SDP_INTERFACE1_REG11, &adc_iface);
|
||||
adc_iface &= 0x1f;
|
||||
switch (bits) {
|
||||
case 16:
|
||||
adc_iface |= 0x60;
|
||||
break;
|
||||
case 24:
|
||||
adc_iface |= 0x00;
|
||||
break;
|
||||
case 32:
|
||||
adc_iface |= 0x80;
|
||||
break;
|
||||
default:
|
||||
adc_iface |= 0x60;
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE1_REG11, adc_iface);
|
||||
ESP_LOGI(TAG, "Bits %d", bits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7210_start(audio_codec_es7210_t *codec, uint8_t clock_reg_value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret |= es7210_write_reg(codec, ES7210_CLOCK_OFF_REG01, clock_reg_value);
|
||||
ret |= es7210_write_reg(codec, ES7210_POWER_DOWN_REG06, 0x00);
|
||||
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0x43);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC1_POWER_REG47, 0x08);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC2_POWER_REG48, 0x08);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC3_POWER_REG49, 0x08);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC4_POWER_REG4A, 0x08);
|
||||
ret |= es7210_mic_select(codec, codec->mic_select);
|
||||
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0x43);
|
||||
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0x71);
|
||||
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0x41);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7210_stop(audio_codec_es7210_t *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC1_POWER_REG47, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC2_POWER_REG48, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC3_POWER_REG49, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC4_POWER_REG4A, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0xc0);
|
||||
ret |= es7210_write_reg(codec, ES7210_CLOCK_OFF_REG01, 0x7f);
|
||||
ret |= es7210_write_reg(codec, ES7210_POWER_DOWN_REG06, 0x07);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static es7210_gain_value_t get_db(float db)
|
||||
{
|
||||
db += 0.5;
|
||||
if (db < 33) {
|
||||
int idx = db < 3 ? 0 : db / 3;
|
||||
return GAIN_0DB + idx;
|
||||
}
|
||||
if (db < 34.5) {
|
||||
return GAIN_30DB;
|
||||
}
|
||||
if (db < 36) {
|
||||
return GAIN_34_5DB;
|
||||
}
|
||||
if (db < 37) {
|
||||
return GAIN_36DB;
|
||||
}
|
||||
return GAIN_37_5DB;
|
||||
}
|
||||
|
||||
static int _es7210_set_channel_gain(audio_codec_es7210_t *codec, uint16_t channel_mask, float db)
|
||||
{
|
||||
int ret = 0;
|
||||
es7210_gain_value_t gain = get_db(db);
|
||||
if ((codec->mic_select & ES7210_INPUT_MIC1) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0))) {
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43, 0x0f, gain);
|
||||
}
|
||||
if ((codec->mic_select & ES7210_INPUT_MIC2) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(1))) {
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC2_GAIN_REG44, 0x0f, gain);
|
||||
}
|
||||
if ((codec->mic_select & ES7210_INPUT_MIC3) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(2))) {
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC3_GAIN_REG45, 0x0f, gain);
|
||||
}
|
||||
if ((codec->mic_select & ES7210_INPUT_MIC4) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(3))) {
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MIC4_GAIN_REG46, 0x0f, gain);
|
||||
}
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static int _es7210_set_mute(audio_codec_es7210_t *codec, bool mute)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mute) {
|
||||
ret |= es7210_update_reg_bit(codec, 0x14, 0x03, 0x03);
|
||||
ret |= es7210_update_reg_bit(codec, 0x15, 0x03, 0x03);
|
||||
} else {
|
||||
ret |= es7210_update_reg_bit(codec, 0x14, 0x03, 0x00);
|
||||
ret |= es7210_update_reg_bit(codec, 0x15, 0x03, 0x00);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s", mute ? "Muted" : "Unmuted");
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static int es7210_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (enable == codec->enabled) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = 0;
|
||||
if (enable) {
|
||||
ret |= es7210_start(codec, codec->off_reg);
|
||||
} else {
|
||||
ret |= es7210_stop(codec);
|
||||
}
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
codec->enabled = enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7210_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
es7210_codec_cfg_t *codec_cfg = (es7210_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es7210_codec_cfg_t)) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = 0;
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0xff);
|
||||
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0x41);
|
||||
ret |= es7210_write_reg(codec, ES7210_CLOCK_OFF_REG01, 0x3f);
|
||||
ret |= es7210_write_reg(codec, ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */
|
||||
ret |= es7210_write_reg(codec, ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */
|
||||
ret |= es7210_write_reg(codec, ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */
|
||||
ret |= es7210_write_reg(codec, ES7210_ADC12_HPF1_REG22, 0x0a);
|
||||
ret |= es7210_write_reg(codec, ES7210_ADC34_HPF2_REG20, 0x0a);
|
||||
ret |= es7210_write_reg(codec, ES7210_ADC34_HPF1_REG21, 0x2a);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Write register fail");
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
if (codec_cfg->master_mode) {
|
||||
ESP_LOGI(TAG, "Work in Master mode");
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MODE_CONFIG_REG08, 0x01, 0x01);
|
||||
/* Select clock source for internal mclk */
|
||||
switch (codec_cfg->mclk_src) {
|
||||
case ES7210_MCLK_FROM_PAD:
|
||||
default:
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MASTER_CLK_REG03, 0x80, 0x00);
|
||||
break;
|
||||
case ES7210_MCLK_FROM_CLOCK_DOUBLER:
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MASTER_CLK_REG03, 0x80, 0x80);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Work in Slave mode");
|
||||
ret |= es7210_update_reg_bit(codec, ES7210_MODE_CONFIG_REG08, 0x01, 0x00);
|
||||
}
|
||||
/* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */
|
||||
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0x43);
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */
|
||||
ret |= es7210_write_reg(codec, ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */
|
||||
ret |= es7210_write_reg(codec, ES7210_OSR_REG07, 0x20);
|
||||
/* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */
|
||||
ret |= es7210_write_reg(codec, ES7210_MAINCLK_REG02, 0xc1);
|
||||
codec->mic_select = (es7210_input_mics_t) codec_cfg->mic_selected;
|
||||
if (codec->mic_select == 0) {
|
||||
codec->mic_select = ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2;
|
||||
}
|
||||
ret |= es7210_mic_select(codec, codec->mic_select);
|
||||
ret |= _es7210_set_channel_gain(codec, 0xF, 30.0);
|
||||
if (ret != 0) {
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
codec->master_mode = codec_cfg->master_mode;
|
||||
codec->mclk_div = codec_cfg->mclk_div;
|
||||
if (codec->mclk_div == 0) {
|
||||
codec->mclk_div = MCLK_DEFAULT_DIV;
|
||||
}
|
||||
es7210_read_reg(codec, ES7210_CLOCK_OFF_REG01, &ret);
|
||||
if (ret >= 0) {
|
||||
codec->off_reg = (uint8_t) ret;
|
||||
}
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es7210_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int ret = 0;
|
||||
uint8_t bits = fs->bits_per_sample;
|
||||
// Use 2 channel to fetch TDM data
|
||||
if (es7210_is_tdm_mode(codec) && fs->channel <= 2 && fs->channel_mask == 0) {
|
||||
bits >>= 1;
|
||||
}
|
||||
ret |= es7210_set_bits(codec, bits);
|
||||
ret |= es7210_config_sample(codec, fs->sample_rate);
|
||||
ret |= es7210_config_fmt(codec, ES_I2S_NORMAL);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static int es7210_set_gain(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return _es7210_set_channel_gain(codec, 0xF, db);
|
||||
}
|
||||
|
||||
static int es7210_set_channel_gain(const audio_codec_if_t *h, uint16_t channel_mask, float db)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return _es7210_set_channel_gain(codec, channel_mask, db);
|
||||
}
|
||||
|
||||
static int es7210_set_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return _es7210_set_mute(codec, mute);
|
||||
}
|
||||
|
||||
static int es7210_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = 0;
|
||||
if (codec->is_open) {
|
||||
ret = es7210_enable(h, false);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7210_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es7210_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int es7210_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es7210_read_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static void es7210_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
|
||||
if (codec == NULL) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i <= 0x4E; i++) {
|
||||
int reg = 0;
|
||||
if (es7210_read_reg(codec, i, ®) != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, reg);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es7210_codec_new(es7210_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) calloc(1, sizeof(audio_codec_es7210_t));
|
||||
CODEC_MEM_CHECK(codec);
|
||||
if (codec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = es7210_open;
|
||||
codec->base.enable = es7210_enable;
|
||||
codec->base.set_fs = es7210_set_fs;
|
||||
codec->base.set_mic_gain = es7210_set_gain;
|
||||
codec->base.set_mic_channel_gain = es7210_set_channel_gain;
|
||||
codec->base.mute_mic = es7210_set_mute;
|
||||
codec->base.set_reg = es7210_set_reg;
|
||||
codec->base.get_reg = es7210_get_reg;
|
||||
codec->base.dump_reg = es7210_dump;
|
||||
codec->base.close = es7210_close;
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es7210_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES7210_REG_H
|
||||
#define _ES7210_REG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES7210_RESET_REG00 0x00 /* Reset control */
|
||||
#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */
|
||||
#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */
|
||||
#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */
|
||||
#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */
|
||||
#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */
|
||||
#define ES7210_POWER_DOWN_REG06 0x06 /* power down */
|
||||
#define ES7210_OSR_REG07 0x07
|
||||
#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */
|
||||
#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/
|
||||
#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */
|
||||
#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */
|
||||
#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */
|
||||
#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */
|
||||
#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */
|
||||
#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */
|
||||
#define ES7210_ADC34_HPF1_REG21 0x21
|
||||
#define ES7210_ADC12_HPF1_REG22 0x22
|
||||
#define ES7210_ADC12_HPF2_REG23 0x23
|
||||
#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */
|
||||
#define ES7210_MIC12_BIAS_REG41 0x41
|
||||
#define ES7210_MIC34_BIAS_REG42 0x42
|
||||
#define ES7210_MIC1_GAIN_REG43 0x43
|
||||
#define ES7210_MIC2_GAIN_REG44 0x44
|
||||
#define ES7210_MIC3_GAIN_REG45 0x45
|
||||
#define ES7210_MIC4_GAIN_REG46 0x46
|
||||
#define ES7210_MIC1_POWER_REG47 0x47
|
||||
#define ES7210_MIC2_POWER_REG48 0x48
|
||||
#define ES7210_MIC3_POWER_REG49 0x49
|
||||
#define ES7210_MIC4_POWER_REG4A 0x4A
|
||||
#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */
|
||||
#define ES7210_MIC34_POWER_REG4C 0x4C
|
||||
|
||||
typedef enum {
|
||||
ES7210_AD1_AD0_00 = 0x80,
|
||||
ES7210_AD1_AD0_01 = 0x82,
|
||||
ES7210_AD1_AD0_10 = 0x84,
|
||||
ES7210_AD1_AD0_11 = 0x86
|
||||
} es7210_address_t;
|
||||
|
||||
typedef enum {
|
||||
ES7210_INPUT_MIC1 = 0x01,
|
||||
ES7210_INPUT_MIC2 = 0x02,
|
||||
ES7210_INPUT_MIC3 = 0x04,
|
||||
ES7210_INPUT_MIC4 = 0x08
|
||||
} es7210_input_mics_t;
|
||||
|
||||
typedef enum gain_value {
|
||||
GAIN_0DB = 0,
|
||||
GAIN_3DB,
|
||||
GAIN_6DB,
|
||||
GAIN_9DB,
|
||||
GAIN_12DB,
|
||||
GAIN_15DB,
|
||||
GAIN_18DB,
|
||||
GAIN_21DB,
|
||||
GAIN_24DB,
|
||||
GAIN_27DB,
|
||||
GAIN_30DB,
|
||||
GAIN_33DB,
|
||||
GAIN_34_5DB,
|
||||
GAIN_36DB,
|
||||
GAIN_37_5DB,
|
||||
} es7210_gain_value_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ES7210_H_ */
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "es7243_adc.h"
|
||||
#include "esp_log.h"
|
||||
#include "es_common.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
|
||||
#define MCLK_PULSES_NUMBER (20)
|
||||
#define TAG "ES7243"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
} audio_codec_es7243_t;
|
||||
|
||||
static int es7243_write_reg(audio_codec_es7243_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int es7243_adc_set_voice_mute(audio_codec_es7243_t *codec, bool mute)
|
||||
{
|
||||
ESP_LOGI(TAG, "mute = %d", mute);
|
||||
if (mute) {
|
||||
es7243_write_reg(codec, 0x05, 0x1B);
|
||||
} else {
|
||||
es7243_write_reg(codec, 0x05, 0x13);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es7243_adc_set_gain(audio_codec_es7243_t *codec, float db)
|
||||
{
|
||||
int ret = 0;
|
||||
if (db <= 1) {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x11); // 1db
|
||||
} else if (db <= 4) {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x13); // 3.5db
|
||||
} else if (db <= 18) {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x21); // 18db
|
||||
} else if (db < 21) {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x23); // 20.5db
|
||||
} else if (db < 23) {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x06); // 22.5db
|
||||
} else if (db < 25) {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x41); // 24.5db
|
||||
} else {
|
||||
ret = es7243_write_reg(codec, 0x08, 0x43); // 27db
|
||||
}
|
||||
ESP_LOGI(TAG, "Set DB %f", db);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243_adc_enable(audio_codec_es7243_t *codec, bool enable)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
if (enable) {
|
||||
// slave mode only
|
||||
ret |= es7243_write_reg(codec, 0x00, 0x01);
|
||||
ret |= es7243_write_reg(codec, 0x06, 0x00);
|
||||
ret |= es7243_write_reg(codec, 0x05, 0x1B);
|
||||
ret |= es7243_write_reg(codec, 0x01, 0x0C);
|
||||
ret |= es7243_write_reg(codec, 0x08, 0x43);
|
||||
ret |= es7243_write_reg(codec, 0x05, 0x13);
|
||||
} else {
|
||||
ret |= es7243_write_reg(codec, 0x06, 0x05);
|
||||
ret |= es7243_write_reg(codec, 0x05, 0x1B);
|
||||
ret |= es7243_write_reg(codec, 0x06, 0x5C);
|
||||
ret |= es7243_write_reg(codec, 0x07, 0x3F);
|
||||
ret |= es7243_write_reg(codec, 0x08, 0x4B);
|
||||
ret |= es7243_write_reg(codec, 0x09, 0x9F);
|
||||
}
|
||||
ESP_LOGI(TAG, "Set enable %d", enable);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
es7243_codec_cfg_t *codec_cfg = (es7243_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es7243_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (es7243_adc_enable(codec, true)) {
|
||||
ESP_LOGE(TAG, "Fail to write register");
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
codec->enabled = true;
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es7243_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es7243_adc_set_voice_mute(codec, mute);
|
||||
}
|
||||
|
||||
static int es7243_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = es7243_adc_enable(codec, enable);
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243_set_gain(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es7243_adc_set_gain(codec, db);
|
||||
}
|
||||
|
||||
static int es7243_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es7243_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es7243_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int es7243_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static void es7243_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int value = 0;
|
||||
int ret = codec->ctrl_if->read_reg(codec->ctrl_if, i, 1, &value, 1);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es7243_codec_new(es7243_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
// verify param
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) calloc(1, sizeof(audio_codec_es7243_t));
|
||||
if (codec == NULL) {
|
||||
CODEC_MEM_CHECK(codec);
|
||||
return NULL;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->base.open = es7243_open;
|
||||
codec->base.enable = es7243_enable;
|
||||
codec->base.set_mic_gain = es7243_set_gain;
|
||||
codec->base.mute_mic = es7243_mute;
|
||||
codec->base.set_reg = es7243_set_reg;
|
||||
codec->base.get_reg = es7243_get_reg;
|
||||
codec->base.dump_reg = es7243_dump;
|
||||
codec->base.close = es7243_close;
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es7243_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to open");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "es7243e_adc.h"
|
||||
#include "esp_log.h"
|
||||
#include "es_common.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
|
||||
#define TAG "ES7243E"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
} audio_codec_es7243e_t;
|
||||
|
||||
static uint8_t get_db_reg(float db)
|
||||
{
|
||||
// reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
|
||||
db += 0.5;
|
||||
if (db <= 33.0) {
|
||||
return (uint8_t) db / 3;
|
||||
}
|
||||
if (db < 36.0) {
|
||||
return 12;
|
||||
}
|
||||
if (db < 37.0) {
|
||||
return 13;
|
||||
}
|
||||
return 14;
|
||||
}
|
||||
|
||||
static int es7243e_write_reg(audio_codec_es7243e_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
int es7243e_adc_enable(audio_codec_es7243e_t *codec, bool enable)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
if (enable) {
|
||||
// slave mode only
|
||||
ret |= es7243e_write_reg(codec, 0xF9, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x04, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0x17, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0x20, 0x10);
|
||||
ret |= es7243e_write_reg(codec, 0x21, 0x10);
|
||||
ret |= es7243e_write_reg(codec, 0x00, 0x80);
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x3A);
|
||||
ret |= es7243e_write_reg(codec, 0x16, 0x3F);
|
||||
ret |= es7243e_write_reg(codec, 0x16, 0x00);
|
||||
} else {
|
||||
ret |= es7243e_write_reg(codec, 0x04, 0x02);
|
||||
ret |= es7243e_write_reg(codec, 0x04, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0xF7, 0x30);
|
||||
ret |= es7243e_write_reg(codec, 0xF9, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0x16, 0xFF);
|
||||
ret |= es7243e_write_reg(codec, 0x17, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x38);
|
||||
ret |= es7243e_write_reg(codec, 0x20, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x21, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x00, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x00, 0x1E);
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x30);
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x00);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243e_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
es7243e_codec_cfg_t *codec_cfg = (es7243e_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es7243e_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
|
||||
int ret = 0;
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x3A);
|
||||
ret |= es7243e_write_reg(codec, 0x00, 0x80);
|
||||
ret |= es7243e_write_reg(codec, 0xF9, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x04, 0x02);
|
||||
ret |= es7243e_write_reg(codec, 0x04, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0xF9, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0x00, 0x1E);
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x00);
|
||||
|
||||
ret |= es7243e_write_reg(codec, 0x02, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x03, 0x20);
|
||||
ret |= es7243e_write_reg(codec, 0x04, 0x01);
|
||||
ret |= es7243e_write_reg(codec, 0x0D, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x05, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x06, 0x03); // SCLK=MCLK/4
|
||||
ret |= es7243e_write_reg(codec, 0x07, 0x00); // LRCK=MCLK/256
|
||||
ret |= es7243e_write_reg(codec, 0x08, 0xFF); // LRCK=MCLK/256
|
||||
|
||||
ret |= es7243e_write_reg(codec, 0x09, 0xCA);
|
||||
ret |= es7243e_write_reg(codec, 0x0A, 0x85);
|
||||
ret |= es7243e_write_reg(codec, 0x0B, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x0E, 0xBF);
|
||||
ret |= es7243e_write_reg(codec, 0x0F, 0x80);
|
||||
ret |= es7243e_write_reg(codec, 0x14, 0x0C);
|
||||
ret |= es7243e_write_reg(codec, 0x15, 0x0C);
|
||||
ret |= es7243e_write_reg(codec, 0x17, 0x02);
|
||||
ret |= es7243e_write_reg(codec, 0x18, 0x26);
|
||||
ret |= es7243e_write_reg(codec, 0x19, 0x77);
|
||||
ret |= es7243e_write_reg(codec, 0x1A, 0xF4);
|
||||
ret |= es7243e_write_reg(codec, 0x1B, 0x66);
|
||||
ret |= es7243e_write_reg(codec, 0x1C, 0x44);
|
||||
ret |= es7243e_write_reg(codec, 0x1E, 0x00);
|
||||
ret |= es7243e_write_reg(codec, 0x1F, 0x0C);
|
||||
ret |= es7243e_write_reg(codec, 0x20, 0x1A); // PGA gain +30dB
|
||||
ret |= es7243e_write_reg(codec, 0x21, 0x1A);
|
||||
|
||||
ret |= es7243e_write_reg(codec, 0x00, 0x80); // Slave Mode
|
||||
ret |= es7243e_write_reg(codec, 0x01, 0x3A);
|
||||
ret |= es7243e_write_reg(codec, 0x16, 0x3F);
|
||||
ret |= es7243e_write_reg(codec, 0x16, 0x00);
|
||||
if (ret != 0 || es7243e_adc_enable(codec, true)) {
|
||||
ESP_LOGI(TAG, "Fail to write register");
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
codec->enabled = true;
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es7243e_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = es7243e_adc_enable(codec, enable);
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243e_set_gain(const audio_codec_if_t *h, float db_value)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
uint8_t reg = get_db_reg(db_value);
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
ret |= es7243e_write_reg(codec, 0x20, 0x10 | reg);
|
||||
ret |= es7243e_write_reg(codec, 0x21, 0x10 | reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243e_set_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int ret;
|
||||
if (mute) {
|
||||
ret = es7243e_write_reg(codec, 0x0B, 0xC0);
|
||||
} else {
|
||||
ret = es7243e_write_reg(codec, 0x0B, 0x00);
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", mute ? "Muted" : "Unmuted");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es7243e_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
es7243e_adc_enable(codec, false);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es7243e_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es7243e_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int es7243e_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static void es7243e_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 0xFF; i++) {
|
||||
int value = 0;
|
||||
int ret = codec->ctrl_if->read_reg(codec->ctrl_if, i, 1, &value, 1);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es7243e_codec_new(es7243e_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) calloc(1, sizeof(audio_codec_es7243e_t));
|
||||
if (codec == NULL) {
|
||||
CODEC_MEM_CHECK(codec);
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = es7243e_open;
|
||||
codec->base.enable = es7243e_enable;
|
||||
codec->base.mute_mic = es7243e_set_mute;
|
||||
codec->base.set_mic_gain = es7243e_set_gain;
|
||||
codec->base.set_reg = es7243e_set_reg;
|
||||
codec->base.get_reg = es7243e_get_reg;
|
||||
codec->base.dump_reg = es7243e_dump;
|
||||
codec->base.close = es7243e_close;
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es7243e_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "es8156_dac.h"
|
||||
#include "es8156_reg.h"
|
||||
#include "esp_log.h"
|
||||
#include "es_common.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
|
||||
#define TAG "ES8156"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
int16_t pa_pin;
|
||||
bool pa_reverted;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
float hw_gain;
|
||||
} audio_codec_es8156_t;
|
||||
|
||||
/* The volume register mapped to decibel table can get from codec data-sheet
|
||||
Volume control register 0x14 description:
|
||||
0x00 - '-95.5dB' ... 0xFF - '+32dB'
|
||||
*/
|
||||
const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 0x0,
|
||||
.db_value = -95.5,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0xFF,
|
||||
.db_value = 32.0,
|
||||
},
|
||||
};
|
||||
|
||||
static int es8156_write_reg(audio_codec_es8156_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int es8156_read_reg(audio_codec_es8156_t *codec, int reg, int *value)
|
||||
{
|
||||
*value = 0;
|
||||
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
|
||||
static int es8156_stop(audio_codec_es8156_t *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = es8156_write_reg(codec, 0x14, 0x00);
|
||||
ret |= es8156_write_reg(codec, 0x19, 0x02);
|
||||
ret |= es8156_write_reg(codec, 0x21, 0x1F);
|
||||
ret |= es8156_write_reg(codec, 0x22, 0x02);
|
||||
ret |= es8156_write_reg(codec, 0x25, 0x21);
|
||||
ret |= es8156_write_reg(codec, 0x25, 0xA1);
|
||||
ret |= es8156_write_reg(codec, 0x18, 0x01);
|
||||
ret |= es8156_write_reg(codec, 0x09, 0x02);
|
||||
ret |= es8156_write_reg(codec, 0x09, 0x01);
|
||||
ret |= es8156_write_reg(codec, 0x08, 0x00);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8156_start(audio_codec_es8156_t *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
ret |= es8156_write_reg(codec, 0x08, 0x3F);
|
||||
ret |= es8156_write_reg(codec, 0x09, 0x00);
|
||||
ret |= es8156_write_reg(codec, 0x18, 0x00);
|
||||
|
||||
ret |= es8156_write_reg(codec, 0x25, 0x20);
|
||||
ret |= es8156_write_reg(codec, 0x22, 0x00);
|
||||
ret |= es8156_write_reg(codec, 0x21, 0x3C);
|
||||
ret |= es8156_write_reg(codec, 0x19, 0x20);
|
||||
ret |= es8156_write_reg(codec, 0x14, 179);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8156_set_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int regv;
|
||||
int ret = es8156_read_reg(codec, ES8156_DAC_MUTE_REG13, ®v);
|
||||
if (ret < 0) {
|
||||
return ESP_CODEC_DEV_READ_FAIL;
|
||||
}
|
||||
if (mute) {
|
||||
regv = regv | BITS(1) | BITS(2);
|
||||
} else {
|
||||
regv = regv & (~(BITS(1) | BITS(2)));
|
||||
}
|
||||
return es8156_write_reg(codec, ES8156_DAC_MUTE_REG13, (uint8_t) regv);
|
||||
}
|
||||
|
||||
static int es8156_set_vol(const audio_codec_if_t *h, float volume)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
volume -= codec->hw_gain;
|
||||
int reg = esp_codec_dev_vol_calc_reg(&vol_range, volume);
|
||||
int ret = es8156_write_reg(codec, ES8156_VOLUME_CONTROL_REG14, reg);
|
||||
ESP_LOGD(TAG, "Set volume reg:%x db:%f", reg, volume);
|
||||
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static void es8156_pa_power(audio_codec_es8156_t *codec, es_pa_setting_t pa_setting)
|
||||
{
|
||||
int16_t pa_pin = codec->pa_pin;
|
||||
if (pa_pin == -1 || codec->gpio_if == NULL) {
|
||||
return;
|
||||
}
|
||||
if (pa_setting & ES_PA_SETUP) {
|
||||
codec->gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
}
|
||||
if (pa_setting & ES_PA_ENABLE) {
|
||||
codec->gpio_if->set(pa_pin, codec->pa_reverted ? false : true);
|
||||
}
|
||||
if (pa_setting & ES_PA_DISABLE) {
|
||||
codec->gpio_if->set(pa_pin, codec->pa_reverted ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
static int es8156_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
es8156_codec_cfg_t *codec_cfg = (es8156_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || cfg_size != sizeof(es8156_codec_cfg_t) || codec_cfg->ctrl_if == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->gpio_if = codec_cfg->gpio_if;
|
||||
codec->pa_pin = codec_cfg->pa_pin;
|
||||
codec->pa_reverted = codec_cfg->pa_reverted;
|
||||
|
||||
ret |= es8156_write_reg(codec, 0x02, 0x04);
|
||||
ret |= es8156_write_reg(codec, 0x20, 0x2A);
|
||||
ret |= es8156_write_reg(codec, 0x21, 0x3C);
|
||||
ret |= es8156_write_reg(codec, 0x22, 0x00);
|
||||
ret |= es8156_write_reg(codec, 0x24, 0x07);
|
||||
ret |= es8156_write_reg(codec, 0x23, 0x00);
|
||||
|
||||
ret |= es8156_write_reg(codec, 0x0A, 0x01);
|
||||
ret |= es8156_write_reg(codec, 0x0B, 0x01);
|
||||
ret |= es8156_write_reg(codec, 0x11, 0x00);
|
||||
ret |= es8156_write_reg(codec, 0x14, 179); // volume 70%
|
||||
|
||||
ret |= es8156_write_reg(codec, 0x0D, 0x14);
|
||||
ret |= es8156_write_reg(codec, 0x18, 0x00);
|
||||
ret |= es8156_write_reg(codec, 0x08, 0x3F);
|
||||
ret |= es8156_write_reg(codec, 0x00, 0x02);
|
||||
ret |= es8156_write_reg(codec, 0x00, 0x03);
|
||||
ret |= es8156_write_reg(codec, 0x25, 0x20);
|
||||
if (ret != 0) {
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
es8156_pa_power(codec, ES_PA_SETUP | ES_PA_ENABLE);
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8156_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
es8156_stop(codec);
|
||||
es8156_pa_power(codec, ES_PA_DISABLE);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8156_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if (enable) {
|
||||
ret = es8156_start(codec);
|
||||
es8156_pa_power(codec, ES_PA_ENABLE);
|
||||
} else {
|
||||
es8156_pa_power(codec, ES_PA_DISABLE);
|
||||
ret = es8156_stop(codec);
|
||||
}
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8156_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8156_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int es8156_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8156_read_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static void es8156_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i <= 0x25; i++) {
|
||||
int value = 0;
|
||||
int ret = es8156_read_reg(codec, i, &value);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es8156_codec_new(es8156_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) calloc(1, sizeof(audio_codec_es8156_t));
|
||||
if (codec == NULL) {
|
||||
CODEC_MEM_CHECK(codec);
|
||||
return NULL;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->base.open = es8156_open;
|
||||
codec->base.enable = es8156_enable;
|
||||
codec->base.set_vol = es8156_set_vol;
|
||||
codec->base.mute = es8156_set_mute;
|
||||
codec->base.set_reg = es8156_set_reg;
|
||||
codec->base.get_reg = es8156_get_reg;
|
||||
codec->base.dump_reg = es8156_dump;
|
||||
codec->base.close = es8156_close;
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8156_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8156_H
|
||||
#define _ES8156_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ES8156 register space */
|
||||
/*
|
||||
* RESET Control
|
||||
*/
|
||||
#define ES8156_RESET_REG00 0x00
|
||||
/*
|
||||
* Clock Managerment
|
||||
*/
|
||||
#define ES8156_MAINCLOCK_CTL_REG01 0x01
|
||||
#define ES8156_SCLK_MODE_REG02 0x02
|
||||
#define ES8156_LRCLK_DIV_H_REG03 0x03
|
||||
#define ES8156_LRCLK_DIV_L_REG04 0x04
|
||||
#define ES8156_SCLK_DIV_REG05 0x05
|
||||
#define ES8156_NFS_CONFIG_REG06 0x06
|
||||
#define ES8156_MISC_CONTROL1_REG07 0x07
|
||||
#define ES8156_CLOCK_ON_OFF_REG08 0x08
|
||||
#define ES8156_MISC_CONTROL2_REG09 0x09
|
||||
#define ES8156_TIME_CONTROL1_REG0A 0x0a
|
||||
#define ES8156_TIME_CONTROL2_REG0B 0x0b
|
||||
/*
|
||||
* System Control
|
||||
*/
|
||||
#define ES8156_CHIP_STATUS_REG0C 0x0c
|
||||
#define ES8156_P2S_CONTROL_REG0D 0x0d
|
||||
#define ES8156_DAC_OSR_COUNTER_REG10 0x10
|
||||
/*
|
||||
* SDP Control
|
||||
*/
|
||||
#define ES8156_DAC_SDP_REG11 0x11
|
||||
#define ES8156_AUTOMUTE_SET_REG12 0x12
|
||||
#define ES8156_DAC_MUTE_REG13 0x13
|
||||
#define ES8156_VOLUME_CONTROL_REG14 0x14
|
||||
|
||||
/*
|
||||
* ALC Control
|
||||
*/
|
||||
#define ES8156_ALC_CONFIG1_REG15 0x15
|
||||
#define ES8156_ALC_CONFIG2_REG16 0x16
|
||||
#define ES8156_ALC_CONFIG3_REG17 0x17
|
||||
#define ES8156_MISC_CONTROL3_REG18 0x18
|
||||
#define ES8156_EQ_CONTROL1_REG19 0x19
|
||||
#define ES8156_EQ_CONTROL2_REG1A 0x1a
|
||||
/*
|
||||
* Analog System Control
|
||||
*/
|
||||
#define ES8156_ANALOG_SYS1_REG20 0x20
|
||||
#define ES8156_ANALOG_SYS2_REG21 0x21
|
||||
#define ES8156_ANALOG_SYS3_REG22 0x22
|
||||
#define ES8156_ANALOG_SYS4_REG23 0x23
|
||||
#define ES8156_ANALOG_LP_REG24 0x24
|
||||
#define ES8156_ANALOG_SYS5_REG25 0x25
|
||||
/*
|
||||
* Chip Information
|
||||
*/
|
||||
#define ES8156_I2C_PAGESEL_REGFC 0xFC
|
||||
#define ES8156_CHIPID1_REGFD 0xFD
|
||||
#define ES8156_CHIPID0_REGFE 0xFE
|
||||
#define ES8156_CHIP_VERSION_REGFF 0xFF
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "es8311_codec.h"
|
||||
#include "es8311_reg.h"
|
||||
#include "esp_log.h"
|
||||
#include "es_common.h"
|
||||
|
||||
#define TAG "ES8311"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
es8311_codec_cfg_t cfg;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
float hw_gain;
|
||||
} audio_codec_es8311_t;
|
||||
|
||||
/*
|
||||
* Clock coefficient structure
|
||||
*/
|
||||
struct _coeff_div {
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t rate; /* sample rate */
|
||||
uint8_t pre_div; /* the pre divider with range from 1 to 8 */
|
||||
uint8_t pre_multi; /* the pre multiplier with x1, x2, x4 and x8 selection */
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dac_div; /* dacclk divider */
|
||||
uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */
|
||||
uint8_t lrck_h; /* adclrck divider and daclrck divider */
|
||||
uint8_t lrck_l;
|
||||
uint8_t bclk_div; /* sclk divider */
|
||||
uint8_t adc_osr; /* adc osr */
|
||||
uint8_t dac_osr; /* dac osr */
|
||||
};
|
||||
|
||||
/* codec hifi mclk clock divider coefficients */
|
||||
static const struct _coeff_div coeff_div[] = {
|
||||
// mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr
|
||||
/* 8k */
|
||||
{12288000, 8000, 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{18432000, 8000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x20},
|
||||
{16384000, 8000, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{8192000, 8000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{3072000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{2048000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{1536000, 8000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{5644800, 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{2822400, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{1411200, 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{6144000, 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{3072000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{1536000, 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
|
||||
/* 16k */
|
||||
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x20},
|
||||
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{8192000, 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{6144000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{4096000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{3072000, 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{2048000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{1536000, 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
{1024000, 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
{1024000, 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 64k */
|
||||
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{4096000, 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{2048000, 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
|
||||
{1024000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 96k */
|
||||
{24576000, 96000, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
};
|
||||
|
||||
static const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 0x0,
|
||||
.db_value = -95.5,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0xFF,
|
||||
.db_value = 32.0,
|
||||
},
|
||||
};
|
||||
|
||||
static int es8311_write_reg(audio_codec_es8311_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int es8311_read_reg(audio_codec_es8311_t *codec, int reg, int *value)
|
||||
{
|
||||
*value = 0;
|
||||
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
|
||||
static int es8311_config_fmt(audio_codec_es8311_t *codec, es_i2s_fmt_t fmt)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
int adc_iface = 0, dac_iface = 0;
|
||||
ret = es8311_read_reg(codec, ES8311_SDPIN_REG09, &dac_iface);
|
||||
ret |= es8311_read_reg(codec, ES8311_SDPOUT_REG0A, &adc_iface);
|
||||
switch (fmt) {
|
||||
case ES_I2S_NORMAL:
|
||||
ESP_LOGD(TAG, "ES8311 in I2S Format");
|
||||
dac_iface &= 0xFC;
|
||||
adc_iface &= 0xFC;
|
||||
break;
|
||||
case ES_I2S_LEFT:
|
||||
case ES_I2S_RIGHT:
|
||||
ESP_LOGD(TAG, "ES8311 in LJ Format");
|
||||
adc_iface &= 0xFC;
|
||||
dac_iface &= 0xFC;
|
||||
adc_iface |= 0x01;
|
||||
dac_iface |= 0x01;
|
||||
break;
|
||||
case ES_I2S_DSP:
|
||||
ESP_LOGD(TAG, "ES8311 in DSP-A Format");
|
||||
adc_iface &= 0xDC;
|
||||
dac_iface &= 0xDC;
|
||||
adc_iface |= 0x03;
|
||||
dac_iface |= 0x03;
|
||||
break;
|
||||
default:
|
||||
dac_iface &= 0xFC;
|
||||
adc_iface &= 0xFC;
|
||||
break;
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_SDPIN_REG09, dac_iface);
|
||||
ret |= es8311_write_reg(codec, ES8311_SDPOUT_REG0A, adc_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8311_set_bits_per_sample(audio_codec_es8311_t *codec, int bits)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
int adc_iface = 0, dac_iface = 0;
|
||||
ret |= es8311_read_reg(codec, ES8311_SDPIN_REG09, &dac_iface);
|
||||
ret |= es8311_read_reg(codec, ES8311_SDPOUT_REG0A, &adc_iface);
|
||||
switch (bits) {
|
||||
case 16:
|
||||
default:
|
||||
dac_iface |= 0x0c;
|
||||
adc_iface |= 0x0c;
|
||||
break;
|
||||
case 24:
|
||||
dac_iface &= ~0x1c;
|
||||
adc_iface &= ~0x1c;
|
||||
break;
|
||||
case 32:
|
||||
dac_iface |= 0x10;
|
||||
adc_iface |= 0x10;
|
||||
break;
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_SDPIN_REG09, dac_iface);
|
||||
ret |= es8311_write_reg(codec, ES8311_SDPOUT_REG0A, adc_iface);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_coeff(uint32_t mclk, uint32_t rate)
|
||||
{
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
|
||||
return i;
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int es8311_suspend(audio_codec_es8311_t *codec)
|
||||
{
|
||||
int ret = es8311_write_reg(codec, ES8311_DAC_REG32, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG17, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0E, 0xFF);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG12, 0x02);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG14, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0D, 0xFA);
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG15, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, 0x10);
|
||||
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, 0x1F);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, 0x30);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_GP_REG45, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0D, 0xFC);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, 0x00);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8311_start(audio_codec_es8311_t *codec)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
int adc_iface = 0, dac_iface = 0;
|
||||
int regv = 0x80;
|
||||
if (codec->cfg.master_mode) {
|
||||
regv |= 0x40;
|
||||
} else {
|
||||
regv &= 0xBF;
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, regv);
|
||||
regv = 0x3F;
|
||||
if (codec->cfg.use_mclk) {
|
||||
regv &= 0x7F;
|
||||
} else {
|
||||
regv |= 0x80;
|
||||
}
|
||||
if (codec->cfg.invert_mclk) {
|
||||
regv |= 0x40;
|
||||
} else {
|
||||
regv &= ~(0x40);
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, regv);
|
||||
|
||||
ret = es8311_read_reg(codec, ES8311_SDPIN_REG09, &dac_iface);
|
||||
ret |= es8311_read_reg(codec, ES8311_SDPOUT_REG0A, &adc_iface);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
dac_iface &= 0xBF;
|
||||
adc_iface &= 0xBF;
|
||||
adc_iface |= BITS(6);
|
||||
dac_iface |= BITS(6);
|
||||
int codec_mode = codec->cfg.codec_mode;
|
||||
if (codec_mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
|
||||
ESP_LOGE(TAG, "Codec not support LINE mode");
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
if (codec_mode == ESP_CODEC_DEV_WORK_MODE_ADC || codec_mode == ESP_CODEC_DEV_WORK_MODE_BOTH) {
|
||||
adc_iface &= ~(BITS(6));
|
||||
}
|
||||
if (codec_mode == ESP_CODEC_DEV_WORK_MODE_DAC || codec_mode == ESP_CODEC_DEV_WORK_MODE_BOTH) {
|
||||
dac_iface &= ~(BITS(6));
|
||||
}
|
||||
|
||||
ret |= es8311_write_reg(codec, ES8311_SDPIN_REG09, dac_iface);
|
||||
ret |= es8311_write_reg(codec, ES8311_SDPOUT_REG0A, adc_iface);
|
||||
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG17, 0xBF);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0E, 0x02);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG12, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG14, 0x1A);
|
||||
|
||||
// pdm dmic enable or disable
|
||||
regv = 0;
|
||||
ret |= es8311_read_reg(codec, ES8311_SYSTEM_REG14, ®v);
|
||||
if (codec->cfg.digital_mic) {
|
||||
regv |= 0x40;
|
||||
} else {
|
||||
regv &= ~(0x40);
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG14, regv);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0D, 0x01);
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG15, 0x40);
|
||||
ret |= es8311_write_reg(codec, ES8311_DAC_REG37, 0x08);
|
||||
ret |= es8311_write_reg(codec, ES8311_GP_REG45, 0x00);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8311_set_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int regv;
|
||||
int ret = es8311_read_reg(codec, ES8311_DAC_REG31, ®v);
|
||||
regv &= 0x9f;
|
||||
if (mute) {
|
||||
es8311_write_reg(codec, ES8311_DAC_REG31, regv | 0x60);
|
||||
} else {
|
||||
es8311_write_reg(codec, ES8311_DAC_REG31, regv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8311_set_vol(const audio_codec_if_t *h, float db_value)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
db_value -= codec->hw_gain;
|
||||
int reg = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
|
||||
ESP_LOGD(TAG, "Set volume reg:%x db:%d", reg, (int) db_value);
|
||||
return es8311_write_reg(codec, ES8311_DAC_REG32, (uint8_t) reg);
|
||||
}
|
||||
|
||||
static int es8311_set_mic_gain(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
es8311_mic_gain_t gain_db = ES8311_MIC_GAIN_0DB;
|
||||
if (db < 6) {
|
||||
} else if (db < 12) {
|
||||
gain_db = ES8311_MIC_GAIN_6DB;
|
||||
} else if (db < 18) {
|
||||
gain_db = ES8311_MIC_GAIN_12DB;
|
||||
} else if (db < 24) {
|
||||
gain_db = ES8311_MIC_GAIN_18DB;
|
||||
} else if (db < 30) {
|
||||
gain_db = ES8311_MIC_GAIN_24DB;
|
||||
} else if (db < 36) {
|
||||
gain_db = ES8311_MIC_GAIN_30DB;
|
||||
} else if (db < 42) {
|
||||
gain_db = ES8311_MIC_GAIN_36DB;
|
||||
} else {
|
||||
gain_db = ES8311_MIC_GAIN_42DB;
|
||||
}
|
||||
int ret = es8311_write_reg(codec, ES8311_ADC_REG16, gain_db); // MIC gain scale
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static void es8311_pa_power(audio_codec_es8311_t *codec, es_pa_setting_t pa_setting)
|
||||
{
|
||||
int16_t pa_pin = codec->cfg.pa_pin;
|
||||
if (pa_pin == -1 || codec->cfg.gpio_if == NULL) {
|
||||
return;
|
||||
}
|
||||
if (pa_setting & ES_PA_SETUP) {
|
||||
codec->cfg.gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
}
|
||||
if (pa_setting & ES_PA_ENABLE) {
|
||||
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? false : true);
|
||||
}
|
||||
if (pa_setting & ES_PA_DISABLE) {
|
||||
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
static int es8311_config_sample(audio_codec_es8311_t *codec, int sample_rate)
|
||||
{
|
||||
int datmp, regv;
|
||||
int mclk_fre = sample_rate * codec->cfg.mclk_div;
|
||||
int coeff = get_coeff(mclk_fre, sample_rate);
|
||||
if (coeff < 0) {
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_rate, mclk_fre);
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
int ret = 0;
|
||||
ret = es8311_read_reg(codec, ES8311_CLK_MANAGER_REG02, ®v);
|
||||
regv &= 0x7;
|
||||
regv |= (coeff_div[coeff].pre_div - 1) << 5;
|
||||
datmp = 0;
|
||||
switch (coeff_div[coeff].pre_multi) {
|
||||
case 1:
|
||||
datmp = 0;
|
||||
break;
|
||||
case 2:
|
||||
datmp = 1;
|
||||
break;
|
||||
case 4:
|
||||
datmp = 2;
|
||||
break;
|
||||
case 8:
|
||||
datmp = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (codec->cfg.use_mclk == false) {
|
||||
datmp = 3;
|
||||
}
|
||||
regv |= (datmp) << 3;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, regv);
|
||||
|
||||
regv = 0x00;
|
||||
regv |= (coeff_div[coeff].adc_div - 1) << 4;
|
||||
regv |= (coeff_div[coeff].dac_div - 1) << 0;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG05, regv);
|
||||
|
||||
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG03, ®v);
|
||||
regv &= 0x80;
|
||||
regv |= coeff_div[coeff].fs_mode << 6;
|
||||
regv |= coeff_div[coeff].adc_osr << 0;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG03, regv);
|
||||
|
||||
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG04, ®v);
|
||||
regv &= 0x80;
|
||||
regv |= coeff_div[coeff].dac_osr << 0;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG04, regv);
|
||||
|
||||
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG07, ®v);
|
||||
regv &= 0xC0;
|
||||
regv |= coeff_div[coeff].lrck_h << 0;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG07, regv);
|
||||
|
||||
regv = 0x00;
|
||||
regv |= coeff_div[coeff].lrck_l << 0;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG08, regv);
|
||||
|
||||
ret = es8311_read_reg(codec, ES8311_CLK_MANAGER_REG06, ®v);
|
||||
regv &= 0xE0;
|
||||
if (coeff_div[coeff].bclk_div < 19) {
|
||||
regv |= (coeff_div[coeff].bclk_div - 1) << 0;
|
||||
} else {
|
||||
regv |= (coeff_div[coeff].bclk_div) << 0;
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG06, regv);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static int es8311_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
es8311_codec_cfg_t *codec_cfg = (es8311_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8311_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
memcpy(&codec->cfg, cfg, sizeof(es8311_codec_cfg_t));
|
||||
if (codec->cfg.mclk_div == 0) {
|
||||
codec->cfg.mclk_div = MCLK_DEFAULT_DIV;
|
||||
}
|
||||
int regv;
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, 0x30);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG03, 0x10);
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG16, 0x24);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG04, 0x10);
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG05, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0B, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0C, 0x00);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG10, 0x1F);
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG11, 0x7F);
|
||||
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, 0x80);
|
||||
|
||||
ret = es8311_read_reg(codec, ES8311_RESET_REG00, ®v);
|
||||
if (codec_cfg->master_mode) {
|
||||
ESP_LOGI(TAG, "Work in Master mode");
|
||||
regv |= 0x40;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Work in Slave mode");
|
||||
regv &= 0xBF;
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, regv);
|
||||
|
||||
// Select clock source for internal mclk
|
||||
regv = 0x3F;
|
||||
if (codec_cfg->use_mclk) {
|
||||
regv &= 0x7F;
|
||||
} else {
|
||||
regv |= 0x80;
|
||||
}
|
||||
// MCLK inverted or not
|
||||
if (codec_cfg->invert_mclk) {
|
||||
regv |= 0x40;
|
||||
} else {
|
||||
regv &= ~(0x40);
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, regv);
|
||||
// SCLK inverted or not
|
||||
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG06, ®v);
|
||||
if (codec_cfg->invert_sclk) {
|
||||
regv |= 0x20;
|
||||
} else {
|
||||
regv &= ~(0x20);
|
||||
}
|
||||
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG06, regv);
|
||||
|
||||
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG13, 0x10);
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG1B, 0x0A);
|
||||
ret |= es8311_write_reg(codec, ES8311_ADC_REG1C, 0x6A);
|
||||
if (codec_cfg->no_dac_ref == false) {
|
||||
/* set internal reference signal (ADCL + DACR) */
|
||||
ret |= es8311_write_reg(codec, ES8311_GPIO_REG44, 0x50);
|
||||
} else {
|
||||
ret |= es8311_write_reg(codec, ES8311_GPIO_REG44, 0);
|
||||
}
|
||||
if (ret != 0) {
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
es8311_pa_power(codec, ES_PA_SETUP | ES_PA_ENABLE);
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8311_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
es8311_suspend(codec);
|
||||
es8311_pa_power(codec, ES_PA_DISABLE);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8311_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
es8311_set_bits_per_sample(codec, fs->bits_per_sample);
|
||||
es8311_config_fmt(codec, ES_I2S_NORMAL);
|
||||
es8311_config_sample(codec, fs->sample_rate);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8311_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (enable == codec->enabled) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if (enable) {
|
||||
ret = es8311_start(codec);
|
||||
es8311_pa_power(codec, ES_PA_ENABLE);
|
||||
} else {
|
||||
es8311_pa_power(codec, ES_PA_DISABLE);
|
||||
ret = es8311_suspend(codec);
|
||||
}
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8311_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8311_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int es8311_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8311_read_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static void es8311_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < ES8311_MAX_REGISTER; i++) {
|
||||
int value = 0;
|
||||
int ret = es8311_read_reg(codec, i, &value);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es8311_codec_new(es8311_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) calloc(1, sizeof(audio_codec_es8311_t));
|
||||
if (codec == NULL) {
|
||||
CODEC_MEM_CHECK(codec);
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = es8311_open;
|
||||
codec->base.enable = es8311_enable;
|
||||
codec->base.set_fs = es8311_set_fs;
|
||||
codec->base.set_vol = es8311_set_vol;
|
||||
codec->base.set_mic_gain = es8311_set_mic_gain;
|
||||
codec->base.mute = es8311_set_mute;
|
||||
codec->base.set_reg = es8311_set_reg;
|
||||
codec->base.get_reg = es8311_get_reg;
|
||||
codec->base.dump_reg = es8311_dump;
|
||||
codec->base.close = es8311_close;
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8311_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8311_REG_H_
|
||||
#define _ES8311_REG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ES8311_REGISTER NAME_REG_REGISTER ADDRESS
|
||||
*/
|
||||
#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/
|
||||
|
||||
/*
|
||||
* Clock Scheme Register definition
|
||||
*/
|
||||
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
|
||||
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
||||
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
||||
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
||||
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
|
||||
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
|
||||
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
|
||||
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
|
||||
/*
|
||||
* SDP
|
||||
*/
|
||||
#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */
|
||||
#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */
|
||||
/*
|
||||
* SYSTEM
|
||||
*/
|
||||
#define ES8311_SYSTEM_REG0B 0x0B /* system */
|
||||
#define ES8311_SYSTEM_REG0C 0x0C /* system */
|
||||
#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */
|
||||
#define ES8311_SYSTEM_REG10 0x10 /* system */
|
||||
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
||||
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
||||
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
||||
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
|
||||
/*
|
||||
* ADC
|
||||
*/
|
||||
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
||||
#define ES8311_ADC_REG16 0x16 /* ADC */
|
||||
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
||||
#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */
|
||||
#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */
|
||||
#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */
|
||||
#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */
|
||||
#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */
|
||||
/*
|
||||
* DAC
|
||||
*/
|
||||
#define ES8311_DAC_REG31 0x31 /* DAC, mute */
|
||||
#define ES8311_DAC_REG32 0x32 /* DAC, volume */
|
||||
#define ES8311_DAC_REG33 0x33 /* DAC, offset */
|
||||
#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */
|
||||
#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */
|
||||
#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */
|
||||
/*
|
||||
*GPIO
|
||||
*/
|
||||
#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */
|
||||
#define ES8311_GP_REG45 0x45 /* GP CONTROL */
|
||||
/*
|
||||
* CHIP
|
||||
*/
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */
|
||||
#define ES8311_CHVER_REGFF 0xFF /* VERSION */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
|
||||
#define ES8311_MAX_REGISTER 0xFF
|
||||
|
||||
typedef enum {
|
||||
ES8311_MIC_GAIN_MIN = -1,
|
||||
ES8311_MIC_GAIN_0DB,
|
||||
ES8311_MIC_GAIN_6DB,
|
||||
ES8311_MIC_GAIN_12DB,
|
||||
ES8311_MIC_GAIN_18DB,
|
||||
ES8311_MIC_GAIN_24DB,
|
||||
ES8311_MIC_GAIN_30DB,
|
||||
ES8311_MIC_GAIN_36DB,
|
||||
ES8311_MIC_GAIN_42DB,
|
||||
ES8311_MIC_GAIN_MAX
|
||||
} es8311_mic_gain_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "es8374_codec.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
#include "es_common.h"
|
||||
|
||||
#define TAG "ES8374"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
es8374_codec_cfg_t cfg;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
} audio_codec_es8374_t;
|
||||
|
||||
static const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 0xC0,
|
||||
.db_value = -96.0,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0x0,
|
||||
.db_value = 0.0,
|
||||
},
|
||||
};
|
||||
|
||||
static int es8374_write_reg(audio_codec_es8374_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int es8374_read_reg(audio_codec_es8374_t *codec, int reg, int *value)
|
||||
{
|
||||
*value = 0;
|
||||
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
|
||||
static int es8374_set_voice_mute(audio_codec_es8374_t *codec, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
ret |= es8374_read_reg(codec, 0x36, ®);
|
||||
if (ret == 0) {
|
||||
reg = reg & 0xdf;
|
||||
ret |= es8374_write_reg(codec, 0x36, reg | (((int) enable) << 5));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static es_bits_length_t get_bits(uint8_t bits)
|
||||
{
|
||||
switch (bits) {
|
||||
default:
|
||||
case 16:
|
||||
return BIT_LENGTH_16BITS;
|
||||
case 18:
|
||||
return BIT_LENGTH_18BITS;
|
||||
case 20:
|
||||
return BIT_LENGTH_20BITS;
|
||||
case 24:
|
||||
return BIT_LENGTH_24BITS;
|
||||
case 32:
|
||||
return BIT_LENGTH_32BITS;
|
||||
}
|
||||
}
|
||||
|
||||
static int es8374_set_bits_per_sample(audio_codec_es8374_t *codec, uint8_t bits)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
int reg = 0;
|
||||
es_bits_length_t bit_per_sample = get_bits(bits);
|
||||
bits = (int) bit_per_sample & 0x0f;
|
||||
|
||||
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
ret |= es8374_read_reg(codec, 0x10, ®);
|
||||
if (ret == 0) {
|
||||
reg = reg & 0xe3;
|
||||
ret |= es8374_write_reg(codec, 0x10, reg | (bits << 2));
|
||||
}
|
||||
}
|
||||
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
ret |= es8374_read_reg(codec, 0x11, ®);
|
||||
if (ret == 0) {
|
||||
reg = reg & 0xe3;
|
||||
ret |= es8374_write_reg(codec, 0x11, reg | (bits << 2));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _set_mic_gain(audio_codec_es8374_t *codec, float gain)
|
||||
{
|
||||
int ret = 0;
|
||||
if (gain >= 0 && gain < 24) {
|
||||
int gain_n = 0;
|
||||
gain_n = (int) gain / 3;
|
||||
ret = es8374_write_reg(codec, 0x22, gain_n | (gain_n << 4)); // MIC PGA
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_i2s_config_clock(audio_codec_es8374_t *codec, es_i2s_clock_t cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
ret |= es8374_read_reg(codec, 0x0f, ®); // power up adc and input
|
||||
reg &= 0xe0;
|
||||
int divratio = 0;
|
||||
switch (cfg.sclk_div) {
|
||||
case MCLK_DIV_1:
|
||||
divratio = 1;
|
||||
break;
|
||||
case MCLK_DIV_2: // = 2,
|
||||
divratio = 2;
|
||||
break;
|
||||
case MCLK_DIV_3: // = 3,
|
||||
divratio = 3;
|
||||
break;
|
||||
case MCLK_DIV_4: // = 4,
|
||||
divratio = 4;
|
||||
break;
|
||||
case MCLK_DIV_5: // = 20,
|
||||
divratio = 5;
|
||||
break;
|
||||
case MCLK_DIV_6: // = 5,
|
||||
divratio = 6;
|
||||
break;
|
||||
case MCLK_DIV_7: // = 29,
|
||||
divratio = 7;
|
||||
break;
|
||||
case MCLK_DIV_8: // = 6,
|
||||
divratio = 8;
|
||||
break;
|
||||
case MCLK_DIV_9: // = 7,
|
||||
divratio = 9;
|
||||
break;
|
||||
case MCLK_DIV_10: // = 21,
|
||||
divratio = 10;
|
||||
break;
|
||||
case MCLK_DIV_11: // = 8,
|
||||
divratio = 11;
|
||||
break;
|
||||
case MCLK_DIV_12: // = 9,
|
||||
divratio = 12;
|
||||
break;
|
||||
case MCLK_DIV_13: // = 30,
|
||||
divratio = 13;
|
||||
break;
|
||||
case MCLK_DIV_14: // = 31
|
||||
divratio = 14;
|
||||
break;
|
||||
case MCLK_DIV_15: // = 22,
|
||||
divratio = 15;
|
||||
break;
|
||||
case MCLK_DIV_16: // = 10,
|
||||
divratio = 16;
|
||||
break;
|
||||
case MCLK_DIV_17: // = 23,
|
||||
divratio = 17;
|
||||
break;
|
||||
case MCLK_DIV_18: // = 11,
|
||||
divratio = 18;
|
||||
break;
|
||||
case MCLK_DIV_20: // = 24,
|
||||
divratio = 19;
|
||||
break;
|
||||
case MCLK_DIV_22: // = 12,
|
||||
divratio = 20;
|
||||
break;
|
||||
case MCLK_DIV_24: // = 13,
|
||||
divratio = 21;
|
||||
break;
|
||||
case MCLK_DIV_25: // = 25,
|
||||
divratio = 22;
|
||||
break;
|
||||
case MCLK_DIV_30: // = 26,
|
||||
divratio = 23;
|
||||
break;
|
||||
case MCLK_DIV_32: // = 27,
|
||||
divratio = 24;
|
||||
break;
|
||||
case MCLK_DIV_33: // = 14,
|
||||
divratio = 25;
|
||||
break;
|
||||
case MCLK_DIV_34: // = 28,
|
||||
divratio = 26;
|
||||
break;
|
||||
case MCLK_DIV_36: // = 15,
|
||||
divratio = 27;
|
||||
break;
|
||||
case MCLK_DIV_44: // = 16,
|
||||
divratio = 28;
|
||||
break;
|
||||
case MCLK_DIV_48: // = 17,
|
||||
divratio = 29;
|
||||
break;
|
||||
case MCLK_DIV_66: // = 18,
|
||||
divratio = 30;
|
||||
break;
|
||||
case MCLK_DIV_72: // = 19,
|
||||
divratio = 31;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
reg |= divratio;
|
||||
ret |= es8374_write_reg(codec, 0x0f, reg);
|
||||
|
||||
int dacratio_l = 0;
|
||||
int dacratio_h = 0;
|
||||
|
||||
switch (cfg.lclk_div) {
|
||||
case LCLK_DIV_128:
|
||||
dacratio_l = 128 % 256;
|
||||
dacratio_h = 128 / 256;
|
||||
break;
|
||||
case LCLK_DIV_192:
|
||||
dacratio_l = 192 % 256;
|
||||
dacratio_h = 192 / 256;
|
||||
break;
|
||||
case LCLK_DIV_256:
|
||||
dacratio_l = 256 % 256;
|
||||
dacratio_h = 256 / 256;
|
||||
break;
|
||||
case LCLK_DIV_384:
|
||||
dacratio_l = 384 % 256;
|
||||
dacratio_h = 384 / 256;
|
||||
break;
|
||||
case LCLK_DIV_512:
|
||||
dacratio_l = 512 % 256;
|
||||
dacratio_h = 512 / 256;
|
||||
break;
|
||||
case LCLK_DIV_576:
|
||||
dacratio_l = 576 % 256;
|
||||
dacratio_h = 576 / 256;
|
||||
break;
|
||||
case LCLK_DIV_768:
|
||||
dacratio_l = 768 % 256;
|
||||
dacratio_h = 768 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1024:
|
||||
dacratio_l = 1024 % 256;
|
||||
dacratio_h = 1024 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1152:
|
||||
dacratio_l = 1152 % 256;
|
||||
dacratio_h = 1152 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1408:
|
||||
dacratio_l = 1408 % 256;
|
||||
dacratio_h = 1408 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1536:
|
||||
dacratio_l = 1536 % 256;
|
||||
dacratio_h = 1536 / 256;
|
||||
break;
|
||||
case LCLK_DIV_2112:
|
||||
dacratio_l = 2112 % 256;
|
||||
dacratio_h = 2112 / 256;
|
||||
break;
|
||||
case LCLK_DIV_2304:
|
||||
dacratio_l = 2304 % 256;
|
||||
dacratio_h = 2304 / 256;
|
||||
break;
|
||||
case LCLK_DIV_125:
|
||||
dacratio_l = 125 % 256;
|
||||
dacratio_h = 125 / 256;
|
||||
break;
|
||||
case LCLK_DIV_136:
|
||||
dacratio_l = 136 % 256;
|
||||
dacratio_h = 136 / 256;
|
||||
break;
|
||||
case LCLK_DIV_250:
|
||||
dacratio_l = 250 % 256;
|
||||
dacratio_h = 250 / 256;
|
||||
break;
|
||||
case LCLK_DIV_272:
|
||||
dacratio_l = 272 % 256;
|
||||
dacratio_h = 272 / 256;
|
||||
break;
|
||||
case LCLK_DIV_375:
|
||||
dacratio_l = 375 % 256;
|
||||
dacratio_h = 375 / 256;
|
||||
break;
|
||||
case LCLK_DIV_500:
|
||||
dacratio_l = 500 % 256;
|
||||
dacratio_h = 500 / 256;
|
||||
break;
|
||||
case LCLK_DIV_544:
|
||||
dacratio_l = 544 % 256;
|
||||
dacratio_h = 544 / 256;
|
||||
break;
|
||||
case LCLK_DIV_750:
|
||||
dacratio_l = 750 % 256;
|
||||
dacratio_h = 750 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1000:
|
||||
dacratio_l = 1000 % 256;
|
||||
dacratio_h = 1000 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1088:
|
||||
dacratio_l = 1088 % 256;
|
||||
dacratio_h = 1088 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1496:
|
||||
dacratio_l = 1496 % 256;
|
||||
dacratio_h = 1496 / 256;
|
||||
break;
|
||||
case LCLK_DIV_1500:
|
||||
dacratio_l = 1500 % 256;
|
||||
dacratio_h = 1500 / 256;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret |= es8374_write_reg(codec, 0x06, dacratio_h); // ADCFsMode,singel SPEED,RATIO=256
|
||||
ret |= es8374_write_reg(codec, 0x07, dacratio_l); // ADCFsMode,singel SPEED,RATIO=256
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_set_d2se_pga(audio_codec_es8374_t *codec, es_d2se_pga_t gain)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
if (gain > D2SE_PGA_GAIN_MIN && gain < D2SE_PGA_GAIN_MAX) {
|
||||
ret = es8374_read_reg(codec, 0x21, ®);
|
||||
reg &= 0xfb;
|
||||
reg |= gain << 2;
|
||||
ret = es8374_write_reg(codec, 0x21, reg); // MIC PGA
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_config_fmt(audio_codec_es8374_t *codec, es_i2s_fmt_t fmt)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
int fmt_i2s = fmt & 0x0f;
|
||||
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
ret |= es8374_read_reg(codec, 0x10, ®);
|
||||
if (ret == 0) {
|
||||
reg = reg & 0xfc;
|
||||
ret |= es8374_write_reg(codec, 0x10, reg | fmt_i2s);
|
||||
}
|
||||
}
|
||||
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
ret |= es8374_read_reg(codec, 0x11, ®);
|
||||
if (ret == 0) {
|
||||
reg = reg & 0xfc;
|
||||
ret |= es8374_write_reg(codec, 0x11, reg | (fmt_i2s));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_config_dac_output(audio_codec_es8374_t *codec, es_dac_output_t output)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
ret = es8374_write_reg(codec, 0x1d, 0x02);
|
||||
ret |= es8374_read_reg(codec, 0x1c, ®); // set spk mixer
|
||||
reg |= 0x80;
|
||||
ret |= es8374_write_reg(codec, 0x1c, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x02); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0xA0); // spk on
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_config_adc_input(audio_codec_es8374_t *codec, es_adc_input_t input)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
ret |= es8374_read_reg(codec, 0x21, ®);
|
||||
if (ret == 0) {
|
||||
reg = (reg & 0xcf) | 0x14;
|
||||
ret |= es8374_write_reg(codec, 0x21, reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_set_adc_dac_volume(audio_codec_es8374_t *codec, esp_codec_dec_work_mode_t mode, float db_value)
|
||||
{
|
||||
int reg = esp_codec_dev_vol_calc_db(&vol_range, db_value);
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
ret = es8374_write_reg(codec, 0x25, (uint8_t) reg);
|
||||
}
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
ret = es8374_write_reg(codec, 0x38, (uint8_t) reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_init_reg(audio_codec_es8374_t *codec, es_i2s_fmt_t fmt, es_i2s_clock_t cfg,
|
||||
es_dac_output_t out_channel, es_adc_input_t in_channel)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
|
||||
ret |= es8374_write_reg(codec, 0x00, 0x3F); // IC Rst start
|
||||
ret |= es8374_write_reg(codec, 0x00, 0x03); // IC Rst stop
|
||||
ret |= es8374_write_reg(codec, 0x01, 0x7F); // IC clk on
|
||||
|
||||
ret |= es8374_read_reg(codec, 0x0F, ®);
|
||||
reg &= 0x7f;
|
||||
|
||||
reg |= (codec->cfg.master_mode << 7);
|
||||
ret |= es8374_write_reg(codec, 0x0f, reg); // CODEC IN I2S SLAVE MODE
|
||||
|
||||
ret |= es8374_write_reg(codec, 0x6F, 0xA0); // pll set:mode enable
|
||||
ret |= es8374_write_reg(codec, 0x72, 0x41); // pll set:mode set
|
||||
ret |= es8374_write_reg(codec, 0x09, 0x01); // pll set:reset on ,set start
|
||||
ret |= es8374_write_reg(codec, 0x0C, 0x22); // pll set:k
|
||||
ret |= es8374_write_reg(codec, 0x0D, 0x2E); // pll set:k
|
||||
ret |= es8374_write_reg(codec, 0x0E, 0xC6); // pll set:k
|
||||
ret |= es8374_write_reg(codec, 0x0A, 0x3A); // pll set:
|
||||
ret |= es8374_write_reg(codec, 0x0B, 0x07); // pll set:n
|
||||
ret |= es8374_write_reg(codec, 0x09, 0x41); // pll set:reset off ,set stop
|
||||
|
||||
ret |= es8374_i2s_config_clock(codec, cfg);
|
||||
|
||||
ret |= es8374_write_reg(codec, 0x24, 0x08); // adc set
|
||||
ret |= es8374_write_reg(codec, 0x36, 0x00); // dac set
|
||||
ret |= es8374_write_reg(codec, 0x12, 0x30); // timming set
|
||||
ret |= es8374_write_reg(codec, 0x13, 0x20); // timming set
|
||||
|
||||
ret |= es8374_config_fmt(codec, fmt);
|
||||
|
||||
ret |= es8374_write_reg(codec, 0x21, 0x50); // adc set: SEL LIN1 CH+PGAGAIN=0DB
|
||||
ret |= es8374_write_reg(codec, 0x22, 0xFF); // adc set: PGA GAIN=0DB
|
||||
ret |= es8374_write_reg(codec, 0x21, 0x14); // adc set: SEL LIN1 CH+PGAGAIN=18DB
|
||||
ret |= es8374_write_reg(codec, 0x22, 0x55); // pga = +15db
|
||||
ret |= es8374_write_reg(codec, 0x08, 0x21); // set class d divider = 33, to avoid the high frequency tone on laudspeaker
|
||||
ret |= es8374_write_reg(codec, 0x00, 0x80); // IC START
|
||||
|
||||
ret |= es8374_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_ADC, 0.0); // 0db
|
||||
|
||||
ret |= es8374_write_reg(codec, 0x14, 0x8A); // IC START
|
||||
ret |= es8374_write_reg(codec, 0x15, 0x40); // IC START
|
||||
ret |= es8374_write_reg(codec, 0x1A, 0xA0); // monoout set
|
||||
ret |= es8374_write_reg(codec, 0x1B, 0x19); // monoout set
|
||||
ret |= es8374_write_reg(codec, 0x1C, 0x90); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x01); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0x20); // spk on
|
||||
ret |= es8374_write_reg(codec, 0x28, 0x00); // alc set
|
||||
ret |= es8374_write_reg(codec, 0x25, 0x00); // ADCVOLUME on
|
||||
ret |= es8374_write_reg(codec, 0x38, 0x00); // DACVOLUME on
|
||||
ret |= es8374_write_reg(codec, 0x37, 0x30); // dac set
|
||||
ret |= es8374_write_reg(codec, 0x6D, 0x60); // SEL:GPIO1=DMIC CLK OUT+SEL:GPIO2=PLL CLK OUT
|
||||
ret |= es8374_write_reg(codec, 0x71, 0x05); // for automute setting
|
||||
ret |= es8374_write_reg(codec, 0x73, 0x70);
|
||||
|
||||
ret |= es8374_config_dac_output(codec, out_channel); // 0x3c Enable DAC and Enable Lout/Rout/1/2
|
||||
ret |= es8374_config_adc_input(codec, in_channel); // 0x00 LINSEL & RINSEL
|
||||
ret |= es8374_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_DAC, -96.0);
|
||||
ret |= es8374_write_reg(codec, 0x37, 0x00); // dac set
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_stop(audio_codec_es8374_t *codec)
|
||||
{
|
||||
int ret = 0;
|
||||
int reg = 0;
|
||||
if (codec->cfg.codec_mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
|
||||
ret |= es8374_read_reg(codec, 0x1a, ®); // disable lout
|
||||
reg |= 0x08;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
reg &= 0x9f;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x12); // mute speaker
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0x20); // disable class d
|
||||
ret |= es8374_read_reg(codec, 0x1c, ®); // disable spkmixer
|
||||
reg &= 0xbf;
|
||||
ret |= es8374_write_reg(codec, 0x1c, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
|
||||
}
|
||||
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
ret |= es8374_set_voice_mute(codec, true);
|
||||
|
||||
ret |= es8374_read_reg(codec, 0x1a, ®); // disable lout
|
||||
reg |= 0x08;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
reg &= 0xdf;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x12); // mute speaker
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0x20); // disable class d
|
||||
ret |= es8374_read_reg(codec, 0x15, ®); // power up dac
|
||||
reg |= 0x20;
|
||||
ret |= es8374_write_reg(codec, 0x15, reg);
|
||||
}
|
||||
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
ret |= es8374_read_reg(codec, 0x10, ®); // power up adc and input
|
||||
reg |= 0xc0;
|
||||
ret |= es8374_write_reg(codec, 0x10, reg);
|
||||
ret |= es8374_read_reg(codec, 0x21, ®); // power up adc and input
|
||||
reg |= 0xc0;
|
||||
ret |= es8374_write_reg(codec, 0x21, reg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_start(audio_codec_es8374_t *codec)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
int reg = 0;
|
||||
bool mode_line = (codec->cfg.codec_mode == ESP_CODEC_DEV_WORK_MODE_LINE);
|
||||
if (mode_line) {
|
||||
ret |= es8374_read_reg(codec, 0x1a, ®); // set monomixer
|
||||
reg |= 0x60;
|
||||
reg |= 0x20;
|
||||
reg &= 0xf7;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
ret |= es8374_read_reg(codec, 0x1c, ®); // set spk mixer
|
||||
reg |= 0x40;
|
||||
ret |= es8374_write_reg(codec, 0x1c, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x02); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0xA0); // spk on
|
||||
}
|
||||
if (mode_line || (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC)) {
|
||||
ret |= es8374_read_reg(codec, 0x21, ®); // power up adc and input
|
||||
reg &= 0x3f;
|
||||
ret |= es8374_write_reg(codec, 0x21, reg);
|
||||
ret |= es8374_read_reg(codec, 0x10, ®); // power up adc and input
|
||||
reg &= 0x3f;
|
||||
ret |= es8374_write_reg(codec, 0x10, reg);
|
||||
}
|
||||
|
||||
if (mode_line || (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC)) {
|
||||
ret |= es8374_read_reg(codec, 0x1a, ®); // disable lout
|
||||
reg |= 0x08;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
reg &= 0xdf;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x12); // mute speaker
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0x20); // disable class d
|
||||
ret |= es8374_read_reg(codec, 0x15, ®); // power up dac
|
||||
reg &= 0xdf;
|
||||
ret |= es8374_write_reg(codec, 0x15, reg);
|
||||
ret |= es8374_read_reg(codec, 0x1a, ®); // disable lout
|
||||
reg |= 0x20;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
reg &= 0xf7;
|
||||
ret |= es8374_write_reg(codec, 0x1a, reg);
|
||||
ret |= es8374_write_reg(codec, 0x1D, 0x02); // mute speaker
|
||||
ret |= es8374_write_reg(codec, 0x1E, 0xa0); // disable class d
|
||||
ret |= es8374_set_voice_mute(codec, false);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_set_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
return es8374_set_voice_mute(codec, mute);
|
||||
}
|
||||
|
||||
static int es8374_set_vol(const audio_codec_if_t *h, float db_value)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8374_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_DAC, db_value);
|
||||
}
|
||||
|
||||
static int es8374_set_mic_gain(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int ret = _set_mic_gain(codec, db);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static void es8374_pa_power(audio_codec_es8374_t *codec, bool enable)
|
||||
{
|
||||
int16_t pa_pin = codec->cfg.pa_pin;
|
||||
if (pa_pin == -1 || codec->cfg.gpio_if == NULL) {
|
||||
return;
|
||||
}
|
||||
codec->cfg.gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? !enable : enable);
|
||||
}
|
||||
|
||||
static int es8374_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
es8374_codec_cfg_t *codec_cfg = (es8374_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8374_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
memcpy(&codec->cfg, codec_cfg, sizeof(es8374_codec_cfg_t));
|
||||
es_i2s_clock_t clkdiv;
|
||||
clkdiv.lclk_div = LCLK_DIV_256;
|
||||
clkdiv.sclk_div = MCLK_DIV_4;
|
||||
ret |= es8374_stop(codec);
|
||||
ret |= es8374_init_reg(codec, (BIT_LENGTH_16BITS << 4) | ES_I2S_NORMAL, clkdiv, DAC_OUTPUT_ALL,
|
||||
ADC_INPUT_LINPUT1_RINPUT1);
|
||||
ret |= _set_mic_gain(codec, 15);
|
||||
ret |= es8374_set_d2se_pga(codec, D2SE_PGA_GAIN_EN);
|
||||
ret |= es8374_config_fmt(codec, ES_I2S_NORMAL);
|
||||
if (ret != 0) {
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8374_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
es8374_stop(codec);
|
||||
es8374_write_reg(codec, 0x00, 0x7F); // IC Reset and STOP
|
||||
es8374_pa_power(codec, false);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8374_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = 0;
|
||||
ret |= es8374_config_fmt(codec, ES_I2S_NORMAL);
|
||||
ret |= es8374_set_bits_per_sample(codec, fs->bits_per_sample);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8374_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if (enable) {
|
||||
ret = es8374_start(codec);
|
||||
es8374_pa_power(codec, true);
|
||||
} else {
|
||||
es8374_pa_power(codec, false);
|
||||
ret = es8374_stop(codec);
|
||||
es8374_write_reg(codec, 0x00, 0x7F); // IC Reset and STOP
|
||||
}
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enable" : "disable");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int es8374_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8374_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int es8374_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8374_read_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static void es8374_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int value = 0;
|
||||
int ret = es8374_read_reg(codec, i, &value);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es8374_codec_new(es8374_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) calloc(1, sizeof(audio_codec_es8374_t));
|
||||
if (codec == NULL) {
|
||||
CODEC_MEM_CHECK(codec);
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = es8374_open;
|
||||
codec->base.enable = es8374_enable;
|
||||
codec->base.set_fs = es8374_set_fs;
|
||||
codec->base.mute = es8374_set_mute;
|
||||
codec->base.set_vol = es8374_set_vol;
|
||||
codec->base.set_mic_gain = es8374_set_mic_gain;
|
||||
codec->base.set_reg = es8374_set_reg;
|
||||
codec->base.get_reg = es8374_get_reg;
|
||||
codec->base.dump_reg = es8374_dump,
|
||||
codec->base.close = es8374_close;
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8374_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "es8388_reg.h"
|
||||
#include "es8388_codec.h"
|
||||
#include "es_common.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#define TAG "ES8388"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
esp_codec_dec_work_mode_t codec_mode;
|
||||
int16_t pa_pin;
|
||||
bool pa_reverted;
|
||||
float hw_gain;
|
||||
} audio_codec_es8388_t;
|
||||
|
||||
static const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 0xC0,
|
||||
.db_value = -96,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0,
|
||||
.db_value = 0.0,
|
||||
},
|
||||
};
|
||||
|
||||
static int es8388_write_reg(audio_codec_es8388_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int es8388_read_reg(audio_codec_es8388_t *codec, int reg, int *value)
|
||||
{
|
||||
*value = 0;
|
||||
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
|
||||
static int es8388_set_adc_dac_volume(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode, int volume, int dot)
|
||||
{
|
||||
int res = 0;
|
||||
if (volume < -96 || volume > 0) {
|
||||
if (volume < -96)
|
||||
volume = -96;
|
||||
else
|
||||
volume = 0;
|
||||
}
|
||||
dot = (dot >= 5 ? 1 : 0);
|
||||
volume = (-volume << 1) + dot;
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL8, volume);
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL9, volume); // ADC Right Volume=0db
|
||||
}
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL5, volume);
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL4, volume);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_set_voice_mute(audio_codec_es8388_t *codec, bool enable)
|
||||
{
|
||||
int res = 0;
|
||||
int reg = 0;
|
||||
res = es8388_read_reg(codec, ES8388_DACCONTROL3, ®);
|
||||
reg = reg & 0xFB;
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL3, reg | (((int) enable) << 2));
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_start(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode)
|
||||
{
|
||||
int res = 0;
|
||||
int prev_data = 0, data = 0;
|
||||
es8388_read_reg(codec, ES8388_DACCONTROL21, &prev_data);
|
||||
if (mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL16,
|
||||
0x09); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 by pass enable
|
||||
res |= es8388_write_reg(
|
||||
codec, ES8388_DACCONTROL17,
|
||||
0x50); // left DAC to left mixer enable and LIN signal to left mixer enable 0db : bupass enable
|
||||
res |= es8388_write_reg(
|
||||
codec, ES8388_DACCONTROL20,
|
||||
0x50); // right DAC to right mixer enable and LIN signal to right mixer enable 0db : bupass enable
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0xC0); // enable adc
|
||||
} else {
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x80); // enable dac
|
||||
}
|
||||
es8388_read_reg(codec, ES8388_DACCONTROL21, &data);
|
||||
if (prev_data != data) {
|
||||
res |= es8388_write_reg(codec, ES8388_CHIPPOWER, 0xF0); // start state machine
|
||||
res |= es8388_write_reg(codec, ES8388_CHIPPOWER, 0x00); // start state machine
|
||||
}
|
||||
if ((mode & ESP_CODEC_DEV_WORK_MODE_ADC) || mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
|
||||
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0x00); // power up adc and line in
|
||||
}
|
||||
if ((mode & ESP_CODEC_DEV_WORK_MODE_DAC) || mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
|
||||
res |= es8388_write_reg(codec, ES8388_DACPOWER, 0x3c); // power up dac and line out
|
||||
res |= es8388_set_voice_mute(codec, false);
|
||||
ESP_LOGI(TAG, "Start on mode:%d", mode);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_stop(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode)
|
||||
{
|
||||
int res = 0;
|
||||
if (mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x80); // enable dac
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
|
||||
return res;
|
||||
}
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
res |= es8388_write_reg(codec, ES8388_DACPOWER, 0x00);
|
||||
res |= es8388_set_voice_mute(codec, true);
|
||||
}
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0xFF); // power down adc and line in
|
||||
}
|
||||
if (mode == ESP_CODEC_DEV_WORK_MODE_BOTH) {
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x9C); // disable mclk
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_config_fmt(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode, es_i2s_fmt_t fmt)
|
||||
{
|
||||
int res = 0;
|
||||
int reg = 0;
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
res = es8388_read_reg(codec, ES8388_ADCCONTROL4, ®);
|
||||
reg = reg & 0xfc;
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL4, reg | fmt);
|
||||
}
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
res = es8388_read_reg(codec, ES8388_DACCONTROL1, ®);
|
||||
reg = reg & 0xf9;
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL1, reg | (fmt << 1));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_set_mic_gain(audio_codec_es8388_t *codec, float db)
|
||||
{
|
||||
es_mic_gain_t gain = db > 0 ? (int) (db / 3) + MIC_GAIN_0DB : MIC_GAIN_0DB;
|
||||
int res, gain_n;
|
||||
gain_n = (int) gain / 3;
|
||||
gain_n = (gain_n << 4) + gain_n;
|
||||
res = es8388_write_reg(codec, ES8388_ADCCONTROL1, gain_n); // MIC PGA
|
||||
return res;
|
||||
}
|
||||
|
||||
static es_bits_length_t get_bits_enum(uint8_t bits)
|
||||
{
|
||||
switch (bits) {
|
||||
case 16:
|
||||
default:
|
||||
return BIT_LENGTH_16BITS;
|
||||
case 18:
|
||||
return BIT_LENGTH_18BITS;
|
||||
case 20:
|
||||
return BIT_LENGTH_20BITS;
|
||||
case 24:
|
||||
return BIT_LENGTH_24BITS;
|
||||
case 32:
|
||||
return BIT_LENGTH_32BITS;
|
||||
}
|
||||
}
|
||||
|
||||
static int es8388_set_bits_per_sample(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode, uint8_t bits_length)
|
||||
{
|
||||
int res = 0;
|
||||
int reg = 0;
|
||||
int bits = (int) get_bits_enum(bits_length);
|
||||
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
|
||||
res = es8388_read_reg(codec, ES8388_ADCCONTROL4, ®);
|
||||
reg = reg & 0xe3;
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL4, reg | (bits << 2));
|
||||
}
|
||||
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
|
||||
res = es8388_read_reg(codec, ES8388_DACCONTROL1, ®);
|
||||
reg = reg & 0xc7;
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL1, reg | (bits << 3));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void es8388_pa_power(audio_codec_es8388_t *codec, bool enable)
|
||||
{
|
||||
int16_t pa_pin = codec->pa_pin;
|
||||
if (pa_pin == -1 || codec->gpio_if == NULL) {
|
||||
return;
|
||||
}
|
||||
codec->gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->gpio_if->set(pa_pin, codec->pa_reverted ? !enable : enable);
|
||||
}
|
||||
|
||||
static int es8388_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
es8388_codec_cfg_t *codec_cfg = (es8388_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8388_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int res = ESP_CODEC_DEV_OK;
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->gpio_if = codec_cfg->gpio_if;
|
||||
codec->pa_pin = codec_cfg->pa_pin;
|
||||
codec->pa_reverted = codec_cfg->pa_reverted;
|
||||
codec->codec_mode = codec_cfg->codec_mode;
|
||||
|
||||
// 0x04 mute/0x00 unmute&ramp;
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL3, 0x04);
|
||||
/* Chip Control and Power Management */
|
||||
res |= es8388_write_reg(codec, ES8388_CONTROL2, 0x50);
|
||||
res |= es8388_write_reg(codec, ES8388_CHIPPOWER, 0x00); // normal all and power up all
|
||||
|
||||
// Disable the internal DLL to improve 8K sample rate
|
||||
res |= es8388_write_reg(codec, 0x35, 0xA0);
|
||||
res |= es8388_write_reg(codec, 0x37, 0xD0);
|
||||
res |= es8388_write_reg(codec, 0x39, 0xD0);
|
||||
|
||||
res |= es8388_write_reg(codec, ES8388_MASTERMODE, codec_cfg->master_mode); // CODEC IN I2S SLAVE MODE
|
||||
|
||||
/* dac */
|
||||
res |= es8388_write_reg(codec, ES8388_DACPOWER, 0xC0); // disable DAC and disable Lout/Rout/1/2
|
||||
res |= es8388_write_reg(codec, ES8388_CONTROL1, 0x12); // Enfr=0,Play&Record Mode,(0x17-both of mic&paly)
|
||||
// res |= es8388_write_reg(codec, ES8388_CONTROL2, 0); //LPVrefBuf=0,Pdn_ana=0
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL1, 0x18); // 1a 0x18:16bit iis , 0x00:24
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL2, 0x02); // DACFsMode,SINGLE SPEED; DACFsRatio,256
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
|
||||
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x80);
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL23, 0x00); // vroi=0
|
||||
res |= es8388_set_adc_dac_volume(codec, ES_MODULE_DAC, 0, 0); // 0db
|
||||
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL24, 0x1E); // Set L1 R1 L2 R2 volume. 0x00: -30dB, 0x1E: 0dB, 0x21: 3dB
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL25, 0x1E);
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL26, 0);
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL27, 0);
|
||||
|
||||
// TODO default use DAC_ALL
|
||||
int tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2;
|
||||
res |= es8388_write_reg(codec, ES8388_DACPOWER, tmp); // 0x3c Enable DAC and Enable Lout/Rout/1/2
|
||||
/* adc */
|
||||
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0xFF);
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL1, 0xbb); // MIC Left and Right channel PGA gain
|
||||
tmp = 0;
|
||||
// TODO default use ADC LINE1
|
||||
// 0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL2, ADC_INPUT_LINPUT1_RINPUT1);
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL3, 0x02);
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL4, 0x0c); // 16 Bits length and I2S serial audio data format
|
||||
res |= es8388_write_reg(codec, ES8388_ADCCONTROL5, 0x02); // ADCFsMode,singel SPEED,RATIO=256
|
||||
// ALC for Microphone
|
||||
res |= es8388_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_ADC, 0, 0); // 0db
|
||||
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0x09); // Power on ADC
|
||||
if (res != 0) {
|
||||
ESP_LOGI(TAG, "Fail to write register");
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8388_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int res;
|
||||
if (enable == false) {
|
||||
es8388_pa_power(codec, false);
|
||||
res = es8388_stop(codec, codec->codec_mode);
|
||||
} else {
|
||||
res = es8388_start(codec, codec->codec_mode);
|
||||
es8388_pa_power(codec, true);
|
||||
}
|
||||
if (res == ESP_CODEC_DEV_OK) {
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8388_set_voice_mute(codec, mute);
|
||||
}
|
||||
|
||||
static int es8388_set_vol(const audio_codec_if_t *h, float db_value)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
db_value -= codec->hw_gain;
|
||||
int volume = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
|
||||
int res = es8388_write_reg(codec, ES8388_DACCONTROL5, volume);
|
||||
res |= es8388_write_reg(codec, ES8388_DACCONTROL4, volume);
|
||||
ESP_LOGD(TAG, "Set volume reg:%x db:%f", volume, db_value);
|
||||
return res ? ESP_CODEC_DEV_WRITE_FAIL : ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int es8388_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL || fs == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int res = 0;
|
||||
res |= es8388_config_fmt(codec, ESP_CODEC_DEV_WORK_MODE_BOTH, ES_I2S_NORMAL);
|
||||
res |= es8388_set_bits_per_sample(codec, ESP_CODEC_DEV_WORK_MODE_BOTH, fs->bits_per_sample);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int es8388_set_gain(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return es8388_set_mic_gain(codec, db);
|
||||
}
|
||||
|
||||
static int es8388_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
es8388_pa_power(codec, false);
|
||||
codec->is_open = false;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static void es8388_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i <= ES8388_DACCONTROL30; i++) {
|
||||
int value = 0;
|
||||
int ret = es8388_read_reg(codec, i, &value);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *es8388_codec_new(es8388_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
// verify param
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) calloc(1, sizeof(audio_codec_es8388_t));
|
||||
if (codec == NULL) {
|
||||
CODEC_MEM_CHECK(codec);
|
||||
return NULL;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->base.open = es8388_open;
|
||||
codec->base.enable = es8388_enable;
|
||||
codec->base.set_fs = es8388_set_fs;
|
||||
codec->base.set_vol = es8388_set_vol;
|
||||
codec->base.mute = es8388_mute;
|
||||
codec->base.set_mic_gain = es8388_set_gain;
|
||||
codec->base.dump_reg = es8388_dump;
|
||||
codec->base.close = es8388_close;
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8388_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to open");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8388_REG_
|
||||
#define _ES8388_REG_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ES8388 register */
|
||||
#define ES8388_CONTROL1 0x00
|
||||
#define ES8388_CONTROL2 0x01
|
||||
|
||||
#define ES8388_CHIPPOWER 0x02
|
||||
|
||||
#define ES8388_ADCPOWER 0x03
|
||||
#define ES8388_DACPOWER 0x04
|
||||
|
||||
#define ES8388_CHIPLOPOW1 0x05
|
||||
#define ES8388_CHIPLOPOW2 0x06
|
||||
|
||||
#define ES8388_ANAVOLMANAG 0x07
|
||||
|
||||
#define ES8388_MASTERMODE 0x08
|
||||
/* ADC */
|
||||
#define ES8388_ADCCONTROL1 0x09
|
||||
#define ES8388_ADCCONTROL2 0x0a
|
||||
#define ES8388_ADCCONTROL3 0x0b
|
||||
#define ES8388_ADCCONTROL4 0x0c
|
||||
#define ES8388_ADCCONTROL5 0x0d
|
||||
#define ES8388_ADCCONTROL6 0x0e
|
||||
#define ES8388_ADCCONTROL7 0x0f
|
||||
#define ES8388_ADCCONTROL8 0x10
|
||||
#define ES8388_ADCCONTROL9 0x11
|
||||
#define ES8388_ADCCONTROL10 0x12
|
||||
#define ES8388_ADCCONTROL11 0x13
|
||||
#define ES8388_ADCCONTROL12 0x14
|
||||
#define ES8388_ADCCONTROL13 0x15
|
||||
#define ES8388_ADCCONTROL14 0x16
|
||||
/* DAC */
|
||||
#define ES8388_DACCONTROL1 0x17
|
||||
#define ES8388_DACCONTROL2 0x18
|
||||
#define ES8388_DACCONTROL3 0x19
|
||||
#define ES8388_DACCONTROL4 0x1a
|
||||
#define ES8388_DACCONTROL5 0x1b
|
||||
#define ES8388_DACCONTROL6 0x1c
|
||||
#define ES8388_DACCONTROL7 0x1d
|
||||
#define ES8388_DACCONTROL8 0x1e
|
||||
#define ES8388_DACCONTROL9 0x1f
|
||||
#define ES8388_DACCONTROL10 0x20
|
||||
#define ES8388_DACCONTROL11 0x21
|
||||
#define ES8388_DACCONTROL12 0x22
|
||||
#define ES8388_DACCONTROL13 0x23
|
||||
#define ES8388_DACCONTROL14 0x24
|
||||
#define ES8388_DACCONTROL15 0x25
|
||||
#define ES8388_DACCONTROL16 0x26
|
||||
#define ES8388_DACCONTROL17 0x27
|
||||
#define ES8388_DACCONTROL18 0x28
|
||||
#define ES8388_DACCONTROL19 0x29
|
||||
#define ES8388_DACCONTROL20 0x2a
|
||||
#define ES8388_DACCONTROL21 0x2b
|
||||
#define ES8388_DACCONTROL22 0x2c
|
||||
#define ES8388_DACCONTROL23 0x2d
|
||||
#define ES8388_DACCONTROL24 0x2e
|
||||
#define ES8388_DACCONTROL25 0x2f
|
||||
#define ES8388_DACCONTROL26 0x30
|
||||
#define ES8388_DACCONTROL27 0x31
|
||||
#define ES8388_DACCONTROL28 0x32
|
||||
#define ES8388_DACCONTROL29 0x33
|
||||
#define ES8388_DACCONTROL30 0x34
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__ES8388_H__
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AW88298_DAC_H_
|
||||
#define _AW88298_DAC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define AW88298_CODEC_DEFAULT_ADDR (0x36 << 1)
|
||||
|
||||
/**
|
||||
* @brief AW88298 codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
int16_t reset_pin; /*!< Reset pin */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
|
||||
} aw88298_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New AW88298 codec interface
|
||||
* @attention Need set mclk_multiple to I2S_MCLK_MULTIPLE_384 in esp_codec_dev_sample_info_t to support 44100
|
||||
* @param codec_cfg: AW88298 codec configuration
|
||||
* @return NULL: Fail to new AW88298 codec interface
|
||||
* -Others: AW88298 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *aw88298_codec_new(aw88298_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES7210_ADC_H_
|
||||
#define _ES7210_ADC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES7210_CODEC_DEFAULT_ADDR (0x80)
|
||||
|
||||
#define ES7120_SEL_MIC1 (uint8_t)(1 << 0)
|
||||
#define ES7120_SEL_MIC2 (uint8_t)(1 << 1)
|
||||
#define ES7120_SEL_MIC3 (uint8_t)(1 << 2)
|
||||
#define ES7120_SEL_MIC4 (uint8_t)(1 << 3)
|
||||
|
||||
/**
|
||||
* @brief ES7210 MCLK clock source when work in master mode
|
||||
*/
|
||||
typedef enum {
|
||||
ES7210_MCLK_FROM_PAD,
|
||||
ES7210_MCLK_FROM_CLOCK_DOUBLER,
|
||||
} es7210_mclk_src_t;
|
||||
|
||||
/**
|
||||
* @brief ES7210 codec configuration, only support ADC feature
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
bool master_mode; /*!< Whether codec works as I2S master or not */
|
||||
uint8_t mic_selected; /*!< Selected microphone */
|
||||
es7210_mclk_src_t mclk_src; /*!< MCLK clock source in master mode */
|
||||
uint16_t mclk_div; /*!< MCLK/LRCK default is 256 if not provided */
|
||||
} es7210_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES7210 codec interface
|
||||
* @param codec_cfg: ES7210 codec configuration
|
||||
* @return NULL: Fail to new ES7210 codec interface
|
||||
* -Others: ES7210 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es7210_codec_new(es7210_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES7243_ADC_H_
|
||||
#define _ES7243_ADC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES7243_CODEC_DEFAULT_ADDR (0x26)
|
||||
|
||||
/**
|
||||
* @brief ES7243 codec configuration, only support ADC feature
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
} es7243_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES7243 codec interface
|
||||
* Notes: this API should called after I2S clock ready
|
||||
* Or else write register may fail
|
||||
* @param codec_cfg: ES7243 codec configuration
|
||||
* @return NULL: Fail to new ES7243 codec interface
|
||||
* -Others: ES7243 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es7243_codec_new(es7243_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES7243E_ADC_H_
|
||||
#define _ES7243E_ADC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES7243E_CODEC_DEFAULT_ADDR (0x20)
|
||||
|
||||
/**
|
||||
* @brief ES7243E codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
} es7243e_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES7243E codec interface
|
||||
* @param codec_cfg: ES7243E codec configuration
|
||||
* @return NULL: Fail to new ES7243E codec interface
|
||||
* -Others: ES7243E codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es7243e_codec_new(es7243e_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8156_DAC_H_
|
||||
#define _ES8156_DAC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES8156_CODEC_DEFAULT_ADDR (0x10)
|
||||
|
||||
/**
|
||||
* @brief ES8156 codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
int16_t pa_pin; /*!< PA chip power pin */
|
||||
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
|
||||
} es8156_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES8156 codec interface
|
||||
* @param codec_cfg: ES8156 codec configuration
|
||||
* @return NULL: Fail to new ES8156 codec interface
|
||||
* -Others: ES8156 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es8156_codec_new(es8156_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8311_CODEC_H_
|
||||
#define _ES8311_CODEC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES8311_CODEC_DEFAULT_ADDR (0x30)
|
||||
|
||||
/**
|
||||
* @brief ES8311 codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
|
||||
int16_t pa_pin; /*!< PA chip power pin */
|
||||
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
|
||||
bool master_mode; /*!< Whether codec works as I2S master or not */
|
||||
bool use_mclk; /*!< Whether use external MCLK clock */
|
||||
bool digital_mic; /*!< Whether use digital microphone */
|
||||
bool invert_mclk; /*!< MCLK clock signal inverted or not */
|
||||
bool invert_sclk; /*!< SCLK clock signal inverted or not */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
|
||||
bool no_dac_ref; /*!< When record 2 channel data
|
||||
false: right channel filled with dac output
|
||||
true: right channel leave empty
|
||||
*/
|
||||
uint16_t mclk_div; /*!< MCLK/LRCK default is 256 if not provided */
|
||||
} es8311_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES8311 codec interface
|
||||
* @param codec_cfg: ES8311 codec configuration
|
||||
* @return NULL: Fail to new ES8311 codec interface
|
||||
* -Others: ES8311 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es8311_codec_new(es8311_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8374_CODEC_H_
|
||||
#define _ES8374_CODEC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES8374_CODEC_DEFAULT_ADDR (0x20)
|
||||
#define ES8374_CODEC_DEFAULT_ADDR_1 (0x21)
|
||||
|
||||
/**
|
||||
* @brief ES8374 codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
|
||||
bool master_mode; /*!< Whether codec works as I2S master or not */
|
||||
int16_t pa_pin; /*!< PA chip power pin */
|
||||
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
|
||||
} es8374_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES8374 codec interface
|
||||
* @param codec_cfg: ES8374 codec configuration
|
||||
* @return NULL: Fail to new ES8374 codec interface
|
||||
* -Others: ES8374 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es8374_codec_new(es8374_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__ES8374_H__
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES8388_CODEC_H_
|
||||
#define _ES8388_CODEC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ES8388 default I2C address
|
||||
*/
|
||||
#define ES8388_CODEC_DEFAULT_ADDR (0x20)
|
||||
#define ES8388_CODEC_DEFAULT_ADDR_1 (0x22)
|
||||
/**
|
||||
* @brief ES8388 codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode on ADC or DAC */
|
||||
bool master_mode; /*!< Whether codec works as I2S master or not */
|
||||
int16_t pa_pin; /*!< PA chip power pin */
|
||||
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
|
||||
} es8388_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ES8388 codec interface
|
||||
* @param codec_cfg: ES8388 codec configuration
|
||||
* @return NULL: Fail to new ES8388 codec interface
|
||||
* -Others: ES8388 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *es8388_codec_new(es8388_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _TAS5805M_DAC_H_
|
||||
#define _TAS5805M_DAC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TAS5805M_CODEC_DEFAULT_ADDR (0x5c)
|
||||
|
||||
/**
|
||||
* @brief TAS5805M codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
|
||||
bool master_mode; /*!< Whether codec works as I2S master or not */
|
||||
int16_t reset_pin; /*!< Reset pin */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
|
||||
} tas5805m_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New TAS5805M codec interface
|
||||
* @param codec_cfg: TAS5805M codec configuration
|
||||
* @return NULL: Fail to new TAS5805M codec interface
|
||||
* -Others: TAS5805M codec interface
|
||||
*/
|
||||
const audio_codec_if_t *tas5805m_codec_new(tas5805m_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ZL38063_CODEC_H_
|
||||
#define _ZL38063_CODEC_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ZL38063 codec configuration
|
||||
* Notes: ZL38063 codec driver provide default configuration of I2S settings in firmware.
|
||||
* Defaults are 48khz, 16 bits, 2 channels
|
||||
* To playback other sample rate need do resampling firstly
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
|
||||
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
|
||||
int16_t pa_pin; /*!< PA chip power pin */
|
||||
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
|
||||
int16_t reset_pin; /*!< Reset pin */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
|
||||
} zl38063_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New ZL38063 codec interface
|
||||
* @param codec_cfg: ZL38063 codec configuration
|
||||
* @return NULL: Fail to new ZL38063 codec interface
|
||||
* -Others: ZL38063 codec interface
|
||||
*/
|
||||
const audio_codec_if_t *zl38063_codec_new(zl38063_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ES_COMMON_H_
|
||||
#define _ES_COMMON_H_
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CODEC_MEM_CHECK(ptr) \
|
||||
if (ptr == NULL) { \
|
||||
ESP_LOGE(TAG, "Fail to alloc memory at %s:%d", __FUNCTION__, __LINE__);\
|
||||
}
|
||||
|
||||
#define BITS(n) (1 << n)
|
||||
|
||||
#define MCLK_DEFAULT_DIV (256)
|
||||
|
||||
typedef enum {
|
||||
BIT_LENGTH_MIN = -1,
|
||||
BIT_LENGTH_16BITS = 0x03,
|
||||
BIT_LENGTH_18BITS = 0x02,
|
||||
BIT_LENGTH_20BITS = 0x01,
|
||||
BIT_LENGTH_24BITS = 0x00,
|
||||
BIT_LENGTH_32BITS = 0x04,
|
||||
BIT_LENGTH_MAX,
|
||||
} es_bits_length_t;
|
||||
|
||||
typedef enum {
|
||||
MCLK_DIV_MIN = -1,
|
||||
MCLK_DIV_1 = 1,
|
||||
MCLK_DIV_2 = 2,
|
||||
MCLK_DIV_3 = 3,
|
||||
MCLK_DIV_4 = 4,
|
||||
MCLK_DIV_6 = 5,
|
||||
MCLK_DIV_8 = 6,
|
||||
MCLK_DIV_9 = 7,
|
||||
MCLK_DIV_11 = 8,
|
||||
MCLK_DIV_12 = 9,
|
||||
MCLK_DIV_16 = 10,
|
||||
MCLK_DIV_18 = 11,
|
||||
MCLK_DIV_22 = 12,
|
||||
MCLK_DIV_24 = 13,
|
||||
MCLK_DIV_33 = 14,
|
||||
MCLK_DIV_36 = 15,
|
||||
MCLK_DIV_44 = 16,
|
||||
MCLK_DIV_48 = 17,
|
||||
MCLK_DIV_66 = 18,
|
||||
MCLK_DIV_72 = 19,
|
||||
MCLK_DIV_5 = 20,
|
||||
MCLK_DIV_10 = 21,
|
||||
MCLK_DIV_15 = 22,
|
||||
MCLK_DIV_17 = 23,
|
||||
MCLK_DIV_20 = 24,
|
||||
MCLK_DIV_25 = 25,
|
||||
MCLK_DIV_30 = 26,
|
||||
MCLK_DIV_32 = 27,
|
||||
MCLK_DIV_34 = 28,
|
||||
MCLK_DIV_7 = 29,
|
||||
MCLK_DIV_13 = 30,
|
||||
MCLK_DIV_14 = 31,
|
||||
MCLK_DIV_MAX,
|
||||
} es_sclk_div_t;
|
||||
|
||||
typedef enum {
|
||||
LCLK_DIV_MIN = -1,
|
||||
LCLK_DIV_128 = 0,
|
||||
LCLK_DIV_192 = 1,
|
||||
LCLK_DIV_256 = 2,
|
||||
LCLK_DIV_384 = 3,
|
||||
LCLK_DIV_512 = 4,
|
||||
LCLK_DIV_576 = 5,
|
||||
LCLK_DIV_768 = 6,
|
||||
LCLK_DIV_1024 = 7,
|
||||
LCLK_DIV_1152 = 8,
|
||||
LCLK_DIV_1408 = 9,
|
||||
LCLK_DIV_1536 = 10,
|
||||
LCLK_DIV_2112 = 11,
|
||||
LCLK_DIV_2304 = 12,
|
||||
|
||||
LCLK_DIV_125 = 16,
|
||||
LCLK_DIV_136 = 17,
|
||||
LCLK_DIV_250 = 18,
|
||||
LCLK_DIV_272 = 19,
|
||||
LCLK_DIV_375 = 20,
|
||||
LCLK_DIV_500 = 21,
|
||||
LCLK_DIV_544 = 22,
|
||||
LCLK_DIV_750 = 23,
|
||||
LCLK_DIV_1000 = 24,
|
||||
LCLK_DIV_1088 = 25,
|
||||
LCLK_DIV_1496 = 26,
|
||||
LCLK_DIV_1500 = 27,
|
||||
LCLK_DIV_MAX,
|
||||
} es_lclk_div_t;
|
||||
|
||||
typedef enum {
|
||||
D2SE_PGA_GAIN_MIN = -1,
|
||||
D2SE_PGA_GAIN_DIS = 0,
|
||||
D2SE_PGA_GAIN_EN = 1,
|
||||
D2SE_PGA_GAIN_MAX = 2,
|
||||
} es_d2se_pga_t;
|
||||
|
||||
typedef enum {
|
||||
ADC_INPUT_MIN = -1,
|
||||
ADC_INPUT_LINPUT1_RINPUT1 = 0x00,
|
||||
ADC_INPUT_MIC1 = 0x05,
|
||||
ADC_INPUT_MIC2 = 0x06,
|
||||
ADC_INPUT_LINPUT2_RINPUT2 = 0x50,
|
||||
ADC_INPUT_DIFFERENCE = 0xf0,
|
||||
ADC_INPUT_MAX,
|
||||
} es_adc_input_t;
|
||||
|
||||
typedef enum {
|
||||
DAC_OUTPUT_MIN = -1,
|
||||
DAC_OUTPUT_LOUT1 = 0x04,
|
||||
DAC_OUTPUT_LOUT2 = 0x08,
|
||||
DAC_OUTPUT_SPK = 0x09,
|
||||
DAC_OUTPUT_ROUT1 = 0x10,
|
||||
DAC_OUTPUT_ROUT2 = 0x20,
|
||||
DAC_OUTPUT_ALL = 0x3c,
|
||||
DAC_OUTPUT_MAX,
|
||||
} es_dac_output_t;
|
||||
|
||||
typedef enum {
|
||||
MIC_GAIN_MIN = -1,
|
||||
MIC_GAIN_0DB = 0,
|
||||
MIC_GAIN_3DB = 3,
|
||||
MIC_GAIN_6DB = 6,
|
||||
MIC_GAIN_9DB = 9,
|
||||
MIC_GAIN_12DB = 12,
|
||||
MIC_GAIN_15DB = 15,
|
||||
MIC_GAIN_18DB = 18,
|
||||
MIC_GAIN_21DB = 21,
|
||||
MIC_GAIN_24DB = 24,
|
||||
MIC_GAIN_MAX,
|
||||
} es_mic_gain_t;
|
||||
|
||||
typedef enum {
|
||||
ES_MODULE_MIN = -1,
|
||||
ES_MODULE_ADC = 0x01,
|
||||
ES_MODULE_DAC = 0x02,
|
||||
ES_MODULE_ADC_DAC = 0x03,
|
||||
ES_MODULE_LINE = 0x04,
|
||||
ES_MODULE_MAX
|
||||
} es_module_t;
|
||||
|
||||
typedef enum {
|
||||
ES_MODE_MIN = -1,
|
||||
ES_MODE_SLAVE = 0x00,
|
||||
ES_MODE_MASTER = 0x01,
|
||||
ES_MODE_MAX,
|
||||
} es_mode_t;
|
||||
|
||||
typedef enum {
|
||||
ES_I2S_MIN = -1,
|
||||
ES_I2S_NORMAL = 0,
|
||||
ES_I2S_LEFT = 1,
|
||||
ES_I2S_RIGHT = 2,
|
||||
ES_I2S_DSP = 3,
|
||||
ES_I2S_MAX
|
||||
} es_i2s_fmt_t;
|
||||
|
||||
typedef struct {
|
||||
es_sclk_div_t sclk_div; /*!< bits clock divide */
|
||||
es_lclk_div_t lclk_div; /*!< WS clock divide */
|
||||
} es_i2s_clock_t;
|
||||
|
||||
typedef enum {
|
||||
ES_PA_SETUP = 1,
|
||||
ES_PA_ENABLE = (1 << 1),
|
||||
ES_PA_DISABLE = (1 << 2),
|
||||
} es_pa_setting_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "tas5805m_dac.h"
|
||||
#include "tas5805m_reg.h"
|
||||
#include "tas5805m_reg_cfg.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
#define TAG "TAS5805M"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
tas5805m_codec_cfg_t cfg;
|
||||
bool is_open;
|
||||
float hw_gain;
|
||||
} audio_codec_tas5805m_t;
|
||||
|
||||
static const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 0xFE,
|
||||
.db_value = -103.0,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0,
|
||||
.db_value = 24.0,
|
||||
},
|
||||
};
|
||||
|
||||
static int tas5805m_write_reg(audio_codec_tas5805m_t *codec, int reg, int value)
|
||||
{
|
||||
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static int tas5805m_read_reg(audio_codec_tas5805m_t *codec, int reg, int *value)
|
||||
{
|
||||
*value = 0;
|
||||
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
|
||||
static int tas5805m_write_data(audio_codec_tas5805m_t *codec, uint8_t reg_addr, uint8_t *data, int size)
|
||||
{
|
||||
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg_addr, 1, data, size);
|
||||
}
|
||||
|
||||
static int tas5805m_transmit_registers(audio_codec_tas5805m_t *codec, const tas5805m_cfg_reg_t *conf_buf, int size)
|
||||
{
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
while (i < size) {
|
||||
switch (conf_buf[i].offset) {
|
||||
case CFG_META_SWITCH:
|
||||
// Used in legacy applications. Ignored here.
|
||||
break;
|
||||
case CFG_META_DELAY:
|
||||
esp_codec_dev_sleep(conf_buf[i].value);
|
||||
break;
|
||||
case CFG_META_BURST:
|
||||
ret = tas5805m_write_data(codec, conf_buf[i + 1].offset, (uint8_t *) (&conf_buf[i + 1].value),
|
||||
conf_buf[i].value);
|
||||
i += (conf_buf[i].value / 2) + 1;
|
||||
break;
|
||||
default:
|
||||
ret = tas5805m_write_reg(codec, conf_buf[i].offset, conf_buf[i].value);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
ESP_LOGE(TAG, "Fail to load configuration to tas5805m");
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas5805m_set_mute_fade(audio_codec_tas5805m_t *codec, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t fade_reg = 0;
|
||||
/* Time for register value
|
||||
* 000: 11.5 ms
|
||||
* 001: 53 ms
|
||||
* 010: 106.5 ms
|
||||
* 011: 266.5 ms
|
||||
* 100: 0.535 sec
|
||||
* 101: 1.065 sec
|
||||
* 110: 2.665 sec
|
||||
* 111: 5.33 sec
|
||||
*/
|
||||
if (value <= 12) {
|
||||
fade_reg = 0;
|
||||
} else if (value <= 53) {
|
||||
fade_reg = 1;
|
||||
} else if (value <= 107) {
|
||||
fade_reg = 2;
|
||||
} else if (value <= 267) {
|
||||
fade_reg = 3;
|
||||
} else if (value <= 535) {
|
||||
fade_reg = 4;
|
||||
} else if (value <= 1065) {
|
||||
fade_reg = 5;
|
||||
} else if (value <= 2665) {
|
||||
fade_reg = 6;
|
||||
} else {
|
||||
fade_reg = 7;
|
||||
}
|
||||
fade_reg |= (fade_reg << 4);
|
||||
ret |= tas5805m_write_reg(codec, MUTE_TIME_REG_ADDR, fade_reg);
|
||||
ESP_LOGD(TAG, "Set mute fade, value:%d, reg:0x%x", value, fade_reg);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static void tas5805m_reset(audio_codec_tas5805m_t *codec, int16_t reset_pin)
|
||||
{
|
||||
if (reset_pin <= 0 || codec->cfg.gpio_if == NULL) {
|
||||
return;
|
||||
}
|
||||
codec->cfg.gpio_if->setup(reset_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->cfg.gpio_if->set(reset_pin, 0);
|
||||
esp_codec_dev_sleep(20);
|
||||
codec->cfg.gpio_if->set(reset_pin, 1);
|
||||
esp_codec_dev_sleep(200);
|
||||
}
|
||||
|
||||
static int tas5805m_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
tas5805m_codec_cfg_t *codec_cfg = (tas5805m_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(tas5805m_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
memcpy(&codec->cfg, codec_cfg, sizeof(tas5805m_codec_cfg_t));
|
||||
tas5805m_reset(codec, codec_cfg->reset_pin);
|
||||
int ret = tas5805m_transmit_registers(codec, tas5805m_registers,
|
||||
sizeof(tas5805m_registers) / sizeof(tas5805m_registers[0]));
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
ESP_LOGE(TAG, "Fail write register group");
|
||||
} else {
|
||||
codec->is_open = true;
|
||||
tas5805m_set_mute_fade(codec, 50);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas5805m_set_volume(const audio_codec_if_t *h, float db_value)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
db_value -= codec->hw_gain;
|
||||
int volume = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
|
||||
ESP_LOGD(TAG, "Set volume reg:%x db:%f", volume, db_value);
|
||||
return tas5805m_write_reg(codec, MASTER_VOL_REG_ADDR, volume);
|
||||
}
|
||||
|
||||
int tas5805m_get_volume(audio_codec_tas5805m_t *codec, float *value)
|
||||
{
|
||||
/// FIXME: Got the digit volume is not right.
|
||||
int vol_idx = 0;
|
||||
int ret = tas5805m_read_reg(codec, MASTER_VOL_REG_ADDR, &vol_idx);
|
||||
if (ret == ESP_CODEC_DEV_OK) {
|
||||
*value = esp_codec_dev_vol_calc_db(&vol_range, vol_idx);
|
||||
ESP_LOGD(TAG, "Volume is %fdb", *value);
|
||||
return 0;
|
||||
}
|
||||
return ESP_CODEC_DEV_READ_FAIL;
|
||||
}
|
||||
|
||||
static int tas5805m_set_mute(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int mute_reg = 0;
|
||||
tas5805m_read_reg(codec, TAS5805M_REG_03, &mute_reg);
|
||||
if (enable) {
|
||||
mute_reg |= 0x8;
|
||||
} else {
|
||||
mute_reg &= (~0x08);
|
||||
}
|
||||
int ret = tas5805m_write_reg(codec, TAS5805M_REG_03, mute_reg);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
static int tas5805m_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return tas5805m_write_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int tas5805m_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return tas5805m_read_reg(codec, reg, value);
|
||||
}
|
||||
|
||||
static int tas5805m_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
codec->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tas5805m_dump(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
|
||||
if (codec == NULL || codec->is_open == false) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i <= TAS5805M_REG_7F; i++) {
|
||||
int value = 0;
|
||||
if (tas5805m_read_reg(codec, i, &value) != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "%02x: %02x", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
const audio_codec_if_t *tas5805m_codec_new(tas5805m_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) calloc(1, sizeof(audio_codec_tas5805m_t));
|
||||
if (codec == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = tas5805m_open;
|
||||
codec->base.set_vol = tas5805m_set_volume;
|
||||
codec->base.mute = tas5805m_set_mute;
|
||||
codec->base.set_reg = tas5805m_set_reg;
|
||||
codec->base.get_reg = tas5805m_get_reg;
|
||||
codec->base.close = tas5805m_close;
|
||||
codec->base.dump_reg = tas5805m_dump;
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(tas5805m_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _TAS5805M_REG_H_
|
||||
#define _TAS5805M_REG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TAS5805M_REG_00 0x00
|
||||
#define TAS5805M_REG_02 0x02
|
||||
#define TAS5805M_REG_03 0x03
|
||||
#define TAS5805M_REG_24 0x24
|
||||
#define TAS5805M_REG_25 0x25
|
||||
#define TAS5805M_REG_26 0x26
|
||||
#define TAS5805M_REG_27 0x27
|
||||
#define TAS5805M_REG_28 0x28
|
||||
#define TAS5805M_REG_29 0x29
|
||||
#define TAS5805M_REG_2A 0x2a
|
||||
#define TAS5805M_REG_2B 0x2b
|
||||
#define TAS5805M_REG_35 0x35
|
||||
#define TAS5805M_REG_7E 0x7e
|
||||
#define TAS5805M_REG_7F 0x7f
|
||||
|
||||
#define TAS5805M_PAGE_00 0x00
|
||||
#define TAS5805M_PAGE_2A 0x2a
|
||||
|
||||
#define TAS5805M_BOOK_00 0x00
|
||||
#define TAS5805M_BOOK_8C 0x8c
|
||||
|
||||
#define MASTER_VOL_REG_ADDR 0X4C
|
||||
#define MUTE_TIME_REG_ADDR 0X51
|
||||
|
||||
#define TAS5805M_DAMP_MODE_BTL 0x0
|
||||
#define TAS5805M_DAMP_MODE_PBTL 0x04
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright 2018 Microsemi Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,36 @@
|
||||
The Source code for the Timberwolf device driver is partitioned into 4 folders.
|
||||
|
||||
\api_lib\
|
||||
There are 5 files in this folder
|
||||
The VprocTwolf_access.c/h -
|
||||
is the user's space OS independent API to read/write specific register of the device to
|
||||
reset the device into one of the 5 supported reset modes and to boot load a firmware/configuration record into the device.
|
||||
The API must be used in conjunction with a low level device driver such as microsemi_spis_tw.c/h, VprocGal_HAL.c/h
|
||||
|
||||
vproc_common.c/h -
|
||||
includes functions and variable declarations that are common to all Microsemi Voice processing devices.
|
||||
These functions include the Vproc_msDelay(), VprocWait(). The Variables declarations include
|
||||
the device status codes, and device reset modes enums. As well as multiple macros to enable debug mode.
|
||||
|
||||
the specific user_space hardware abstraction layer code for the ZL38040/05x/06x/08x Timberwolf devices.
|
||||
|
||||
\firmware\
|
||||
This folder contains the firmware image , configuration record files and header file.
|
||||
|
||||
NOTE: The firmware of the development board and its corresponding configuration have been encapsulated into a static library.
|
||||
|
||||
\example_apps\
|
||||
This folder contains example host applications for the zl38040/050/060/080 Microsemi devices.
|
||||
|
||||
zl38063.c/h -
|
||||
a series of operations for zl38063, such as initialize, adjust the volume, and so on.
|
||||
|
||||
|
||||
NOTE: You can get the latest firmware and related technical support by registering SDS account.
|
||||
http://sds.microsemi.com/software.php.
|
||||
|
||||
ZLS38063 GUI software(MiTuner Lite GUI Software), ZLS38063 and ZLS38508LITE
|
||||
http://sds.microsemi.com/software.php?view_type=listrev&id=103386.
|
||||
|
||||
firmware of ZL38063
|
||||
http://sds.microsemi.com/software.php?view_type=listrev&id=104598.
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef TW_SPI_ACCESS_H
|
||||
#define TW_SPI_ACCESS_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int tw_upload_dsp_firmware(int mode);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* vprocTwolf_access.h - Voice Processor devices high level access module function
|
||||
* prototypes, variables
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef VPROC_TWOLFACCESS_H
|
||||
#define VPROC_TWOLFACCESS_H
|
||||
|
||||
#include "vproc_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TWOLF_MAILBOX_SPINWAIT 1000 /*at least a 1000 to avoid mailbox busy */
|
||||
|
||||
/*device HBI command structure*/
|
||||
typedef struct hbiCmdInfo {
|
||||
unsigned char page;
|
||||
unsigned char offset;
|
||||
unsigned char numwords;
|
||||
} hbiCmdInfo;
|
||||
|
||||
/* external function prototypes */
|
||||
|
||||
VprocStatusType VprocTwolfHbiInit(void); /*Use this function to initialize the HBI bus*/
|
||||
|
||||
VprocStatusType VprocTwolfHbiRead(unsigned short cmd, /*the 16-bit register to read from*/
|
||||
unsigned char numwords, /* The number of 16-bit words to read*/
|
||||
unsigned short *pData); /* Pointer to the read data buffer*/
|
||||
|
||||
VprocStatusType VprocTwolfHbiWrite(unsigned short cmd, /*the 16-bit register to write to*/
|
||||
unsigned char numwords, /* The number of 16-bit words to write*/
|
||||
unsigned short *pData); /*the words (0-255) to write*/
|
||||
|
||||
VprocStatusType TwolfHbiNoOp( /*send no-op command to the device*/
|
||||
unsigned char numWords); /* The number of no-op (0-255) to write*/
|
||||
|
||||
/*An alternative method to loading the firmware into the device
|
||||
* USe this method if you have used the provided tool to convert the *.s3 into
|
||||
* c code that can be compiled with the application
|
||||
*/
|
||||
VprocStatusType
|
||||
VprocTwolfHbiBoot_alt(/*use this function to boot load the firmware (*.c) from the host to the device RAM*/
|
||||
twFirmware *st_firmware); /*Pointer to the firmware image in host RAM*/
|
||||
|
||||
VprocStatusType VprocTwolfLoadConfig(dataArr *pCr2Buf, unsigned short numElements);
|
||||
|
||||
VprocStatusType VprocTwolfHbiCleanup(void);
|
||||
VprocStatusType VprocTwolfHbiBootPrepare(void);
|
||||
VprocStatusType VprocTwolfHbiBootMoreData(char *dataBlock);
|
||||
VprocStatusType VprocTwolfHbiBootConclude(void);
|
||||
VprocStatusType VprocTwolfFirmwareStop(void); /*Use this function to halt the currently running firmware*/
|
||||
VprocStatusType VprocTwolfFirmwareStart(void); /*Use this function to start/restart the firmware currently in RAM*/
|
||||
VprocStatusType VprocTwolfSaveImgToFlash(void); /*Save current loaded firmware from device RAM to FLASH*/
|
||||
VprocStatusType VprocTwolfSaveCfgToFlash(void); /*Save current device config from device RAM to FLASH*/
|
||||
VprocStatusType VprocTwolfReset(VprocResetMode mode);
|
||||
VprocStatusType VprocTwolfEraseFlash(void);
|
||||
VprocStatusType VprocTwolfLoadFwrCfgFromFlash(uint16 image_number);
|
||||
VprocStatusType VprocTwolfSetVolume(uint8 vol);
|
||||
VprocStatusType VprocTwolfGetVolume(int8_t *vol);
|
||||
VprocStatusType VprocTwolfGetAppStatus(uint16 *status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* VPROCTWOLFACCESS_H */
|
||||
@@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
* vproc_common.c - Hal functions for the VPROC API
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "vproc_common.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
|
||||
/*Note - These functions are PLATFORM SPECIFIC- They must be modified
|
||||
* accordingly
|
||||
**********************************************************************/
|
||||
|
||||
static audio_codec_ctrl_if_t *vproc_ctrl_if;
|
||||
|
||||
void VprocSetCtrlIf(void *ctrl_if)
|
||||
{
|
||||
vproc_ctrl_if = (audio_codec_ctrl_if_t *) ctrl_if;
|
||||
}
|
||||
|
||||
static uint16_t convert_edian(uint16_t v)
|
||||
{
|
||||
return (v >> 8) | ((v & 0xFF) << 8);
|
||||
}
|
||||
|
||||
void VprocHALcleanup(void)
|
||||
{
|
||||
}
|
||||
|
||||
int VprocHALInit(void)
|
||||
{
|
||||
if (vproc_ctrl_if) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Vproc_msDelay(unsigned short time)
|
||||
{
|
||||
esp_codec_dev_sleep(time);
|
||||
}
|
||||
|
||||
/* VprocWait(): use this function to
|
||||
* force a delay of specified time in resolution of 125 micro-Seconds
|
||||
*
|
||||
* Input Argument: time in unsigned 32-bit
|
||||
* Return: none
|
||||
*/
|
||||
void VprocWait(unsigned long int time)
|
||||
{
|
||||
esp_codec_dev_sleep(time);
|
||||
}
|
||||
|
||||
/* This is the platform dependent low level spi
|
||||
* function to write 16-bit data to the ZL380xx device
|
||||
*/
|
||||
int VprocHALWrite(unsigned short val)
|
||||
{
|
||||
int ret = 0;
|
||||
if (vproc_ctrl_if) {
|
||||
val = convert_edian(val);
|
||||
ret = vproc_ctrl_if->write_reg(vproc_ctrl_if, 0, 0, &val, sizeof(val));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This is the platform dependent low level spi
|
||||
* function to read 16-bit data from the ZL380xx device
|
||||
*/
|
||||
int VprocHALRead(unsigned short *pVal)
|
||||
{
|
||||
unsigned short data = 0;
|
||||
int ret = 0;
|
||||
if (vproc_ctrl_if) {
|
||||
ret = vproc_ctrl_if->read_reg(vproc_ctrl_if, 0, 0, &data, sizeof(data));
|
||||
*pVal = convert_edian(data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/****************************************************************************
|
||||
* vproc_common.h - Hal functions prototypes, macros and variables for the VPROC API
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef VPROC_COMMON_H
|
||||
#define VPROC_COMMON_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEBUG_LOGD ESP_LOGD
|
||||
#define DEBUG_LOGE ESP_LOGE
|
||||
#define DEBUG_LOGI ESP_LOGI
|
||||
/*This header includes some platform dependent data types*/
|
||||
#include "vproc_data_types.h"
|
||||
|
||||
//#define RETRY_COUNT 100
|
||||
#define VPROC_TIMEOUT 500
|
||||
|
||||
#define TAG_SPI "SPI"
|
||||
|
||||
/* external defines */
|
||||
#undef VPROC_DEBUG
|
||||
|
||||
/*create a 16-bit word out of two bytes*/
|
||||
#define MAKE16(a, b) (unsigned short) (((unsigned short) (b) << 8) | (unsigned short) (a))
|
||||
/*create a 32-bit word out of 4 bytes*/
|
||||
|
||||
#define MAKE32(a, b, c, d) \
|
||||
(unsigned long) (((unsigned long) d << 24) | ((unsigned long) c << 16) | ((unsigned long) b << 8) | \
|
||||
((unsigned long) a))
|
||||
/*
|
||||
* debug - print the function name and line number for the source of the error
|
||||
* the line number count start at 1 and not 0
|
||||
*/
|
||||
|
||||
/*
|
||||
*Define this macro to report mode debug info
|
||||
*/
|
||||
#undef VPROC_API_DBG_INFO
|
||||
#ifdef VPROC_API_DBG_INFO
|
||||
#define VPROG_DBG_INFO(s, args...) printf(""s, ##args);
|
||||
#else
|
||||
#define VPROG_DBG_INFO(s, args...)
|
||||
#endif
|
||||
|
||||
#define VPROC_API_DBG_ERROR
|
||||
#ifdef VPROC_API_DBG_ERROR
|
||||
#define VPROG_DBG_ERROR(s, args...) printf("---%s %d: "s, __func__, __LINE__, ##args);
|
||||
#else
|
||||
#define VPROG_DBG_ERROR(s, args...)
|
||||
#endif
|
||||
|
||||
/*unsigned char deviceType;*/
|
||||
/*device/access Status codes*/
|
||||
typedef enum VprocStatusType {
|
||||
VPROC_STATUS_SUCCESS = 0,
|
||||
VPROC_STATUS_FAILURE,
|
||||
VPROC_STATUS_INIT_FAILED,
|
||||
VPROC_STATUS_WR_FAILED,
|
||||
VPROC_STATUS_RD_FAILED,
|
||||
VPROC_STATUS_FW_LOAD_FAILED,
|
||||
VPROC_STATUS_CFG_LOAD_FAILED,
|
||||
VPROC_STATUS_CLOSE_FAILED,
|
||||
VPROC_STATUS_FW_SAVE_FAILED,
|
||||
VPROC_STATUS_GFG_SAVE_FAILED,
|
||||
VPROC_STATUS_MAU_NOT_READY,
|
||||
VPROC_STATUS_CHK_FAILED,
|
||||
VPROC_STATUS_FUNC_NOT_SUPPORTED,
|
||||
VPROC_STATUS_INVALID_ARG,
|
||||
VPROC_STATUS_ERR_VTD_CODE,
|
||||
VPROC_STATUS_ERR_VERIFY,
|
||||
VPROC_STATUS_DEVICE_BUSY,
|
||||
VPROC_STATUS_ERR_HBI,
|
||||
VPROC_STATUS_ERR_IMAGE,
|
||||
VPROC_STATUS_MAILBOX_BUSY,
|
||||
VPROC_STATUS_CMDREG_BUSY,
|
||||
VPROC_STATUS_IN_CRTCL_SECTN,
|
||||
VPROC_STATUS_BOOT_LOADING_MORE_DATA,
|
||||
VPROC_STATUS_BOOT_LOADING_CMP,
|
||||
VPROC_STATUS_DEV_NOT_INITIALIZED,
|
||||
|
||||
} VprocStatusType;
|
||||
|
||||
/* Device Reset modes*/
|
||||
typedef enum VprocResetMode {
|
||||
VPROC_RST_HARDWARE_ROM = 0, /*hardware reset -reset the device and reload the firmware from flash*/
|
||||
VPROC_RST_HARDWARE_RAM = 1, /*hardware reset -reset the device and reload the firmware from RAM*/
|
||||
VPROC_RST_SOFTWARE = 2,
|
||||
VPROC_RST_AEC = 3, /*software reset -reset and runs the firmware from RAM*/
|
||||
VPROC_RST_BOOT = 4
|
||||
} VprocResetMode;
|
||||
|
||||
typedef enum vProcDeviceType {
|
||||
VPROC_DEV_GALILEO = 1, /*Galileo devices: ZL38004, ZL38012, ZL38005*/
|
||||
VPROC_DEV_TIMBERWOLF = 2 /*Timberwolf: ZL38040*/
|
||||
} VprocDeviceType;
|
||||
|
||||
extern void VprocSetCtrlIf(void *ctrl_if);
|
||||
extern void VprocHALcleanup(void);
|
||||
extern int VprocHALInit(void);
|
||||
extern void Vproc_msDelay(unsigned short time);
|
||||
extern void VprocWait(unsigned long int time);
|
||||
extern int VprocHALWrite(unsigned short val);
|
||||
extern int VprocHALRead(unsigned short *pVal);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VPROC_COMMON_H */
|
||||
@@ -0,0 +1,121 @@
|
||||
/** \file vproc_data_types.h
|
||||
* vproc_data_types.h
|
||||
*
|
||||
* This file is the header for all standard types used in the API code.
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef VP_API_TYPES_H
|
||||
#define VP_API_TYPES_H
|
||||
/* For maximum that can be stored in an int - if file exists in library */
|
||||
#include "limits.h"
|
||||
#include "esp_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL (0)
|
||||
#endif
|
||||
|
||||
#ifdef EXTERN
|
||||
#undef EXTERN
|
||||
#error EXTERN was redefined!
|
||||
#endif /* undef EXTERN */
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/********************* DECLARATIONS ***************************/
|
||||
/* Constants */
|
||||
#define FALSE (0) /* Boolean constant */
|
||||
#define TRUE (1) /* Boolean constant */
|
||||
#ifndef __cplusplus
|
||||
/* C++ language provides a boolean data type; So no need to define
|
||||
* one more data type; Make use of it
|
||||
* NOTE: The 'C' potions of the VP-API assume C++ "bool" to be of the
|
||||
* same size as that of "char". Please make sure this assumption is correct.
|
||||
*/
|
||||
|
||||
// typedef unsigned char bool;
|
||||
#endif /* __cplusplus */
|
||||
/****************** typedefs ***********************************/
|
||||
/* These are the basic number types used */
|
||||
/* for uint8, uint16, uint32, int8, int16, int32, bool */
|
||||
// PLATFORM SPECIFIC DEFINITIONS
|
||||
typedef unsigned char uchar;
|
||||
typedef signed char int8;
|
||||
typedef unsigned char UCharT; // 8 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef unsigned char UInt8T; // 8 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef unsigned short UInt16T; // 16 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef unsigned long UInt32T; // 32 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef signed long Int32T; // 32 bits signed - PLATFORM SPECIFIC
|
||||
typedef unsigned char uint8; // 8 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef unsigned short uint16; // 16 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef uint8 *uint8p; // pointer to 8 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef uint16 *uint16p; // pointer to 16 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef uint32_t uint32; // 32 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef signed short int16; // 32 bits unsigned - PLATFORM SPECIFIC
|
||||
typedef uint32 *uint32p;
|
||||
typedef int8 *int8p;
|
||||
typedef int16 *int16p;
|
||||
typedef Int32T *int32p;
|
||||
|
||||
/* external types */
|
||||
/* Some compilers optimize the size of enumeration data types based on
|
||||
* the maximum data value assigned to the members of that data type.
|
||||
* 'Standard C' requires enumeration data types to be of the same size
|
||||
* as that of native 'int' implementation.
|
||||
* The VP-API from a portability persepective adds a 'dummy' member to
|
||||
* all enumeration data types that force the compilers to allocate the size
|
||||
* of enumeration data types to be equal to that of native 'int'
|
||||
* implementation */
|
||||
#define FORCE_STANDARD_C_ENUM_SIZE (INT_MAX)
|
||||
|
||||
/* Eliminate error messages that occur when comparing an enumeration constant
|
||||
< 0 */
|
||||
#define FORCE_SIGNED_ENUM (INT_MIN)
|
||||
|
||||
/* Define any API specific basic data type ranges (that are necessary) */
|
||||
#define VP_INT16_MAX (SHRT_MAX)
|
||||
#define VP_INT16_MIN (SHRT_MIN)
|
||||
#define VP_INT32_MAX (LONG_MAX)
|
||||
#define VP_INT32_MIN (LONG_MIN)
|
||||
|
||||
/*firmware data structures*/
|
||||
typedef struct {
|
||||
uint16 buf[16]; /*the firmware data block to send to the device*/
|
||||
uint16 numWords; /*the number of words within the block of data stored in buf[]*/
|
||||
uint32 targetAddr; /*the target base address to write to register 0x00c of the device*/
|
||||
uint8 useTargetAddr; /*this value is either 0 or 1. When 1 the tarGetAddr must be written to the device*/
|
||||
} twFwr;
|
||||
|
||||
typedef struct {
|
||||
twFwr *st_Fwr;
|
||||
uint32 byteCount; /*The total number of bytes within the firmware - NOT USED*/
|
||||
uint8 havePrgmBase;
|
||||
uint32 prgmBase;
|
||||
uint32 execAddr; /*The execution start address of the firmware in RAM*/
|
||||
uint16 twFirmwareStreamLen; /*The number of blocks within the firmware*/
|
||||
} twFirmware;
|
||||
|
||||
/*config record structures*/
|
||||
typedef struct {
|
||||
uint16 reg; /*the register */
|
||||
uint16 value; /*the value to write into reg */
|
||||
} dataArr;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VP_API_TYPES_H */
|
||||
@@ -0,0 +1,299 @@
|
||||
/****************************************************************************
|
||||
* tw_hal_verify.c - Read/write registers of the device and verify whether the
|
||||
* device is accessed properly
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* malloc, free, rand */
|
||||
#include "esp_log.h"
|
||||
#include "vproc_common.h"
|
||||
#include "zl38063_config.h"
|
||||
#include "vprocTwolf_access.h"
|
||||
#include "zl38063_firmware.h"
|
||||
|
||||
/*NOTE: notice that the *.c code are included in the apps-
|
||||
* This is because the compiler I'm using requires that
|
||||
* But if your makefile is such that compiler knows where to find these files
|
||||
* then remove the #include *.c below
|
||||
*/
|
||||
|
||||
#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
|
||||
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
|
||||
/*quick test*/
|
||||
|
||||
#define TW_HAL_VERIFY_DEBUG
|
||||
|
||||
#define MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST 125
|
||||
|
||||
static const char *TAG = "TW_HAL_VERIFY";
|
||||
|
||||
/*LoadFwrConfig_Alt - to load a converted *s3, *cr2 to c code into the device.
|
||||
* Basically instead of loading the *.s3, *cr2 directly,
|
||||
* use the tw_convert tool to convert the ascii hex fwr mage into code and compile
|
||||
* with the application
|
||||
*
|
||||
* input arg: mode: 0 - load both firmware and confing
|
||||
* 1 - load firmware only
|
||||
* 2 - load config only
|
||||
*/
|
||||
VprocStatusType LoadFwrConfig_Alt(uint8 mode)
|
||||
{
|
||||
VprocStatusType status = VPROC_STATUS_SUCCESS;
|
||||
if ((mode == 0) || (mode == 1)) {
|
||||
twFirmware st_Firmware;
|
||||
st_Firmware.st_Fwr = (twFwr *) st_twFirmware;
|
||||
st_Firmware.twFirmwareStreamLen = (uint16) firmwareStreamLen;
|
||||
st_Firmware.execAddr = (uint32) executionAddress;
|
||||
st_Firmware.havePrgmBase = (uint8) haveProgramBaseAddress;
|
||||
st_Firmware.prgmBase = (uint32) programBaseAddress;
|
||||
ESP_LOGD(TAG, "Firmware boot loading started ....");
|
||||
status = VprocTwolfHbiBoot_alt(&st_Firmware);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBoot()", status);
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "Loading the image to RAM....done");
|
||||
#ifdef SAVE_IMAGE_TO_FLASH
|
||||
ESP_LOGD(TAG, "Saving firmware to flash....");
|
||||
status = VprocTwolfSaveImgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveImgToFlash()", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
ESP_LOGD(TAG, "Saving firmware to flash....done");
|
||||
|
||||
#endif
|
||||
|
||||
status = VprocTwolfFirmwareStart();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfFirmwareStart()", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
if ((mode == 0) || (mode == 2)) {
|
||||
ESP_LOGD(TAG, "Loading the config file into the device RAM....");
|
||||
|
||||
status = VprocTwolfLoadConfig((dataArr *) st_twConfig, (uint16) configStreamLen);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfLoadConfig()", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
#ifdef SAVE_CFG_TO_FLASH
|
||||
ESP_LOGD(TAG, "Saving config to flash....");
|
||||
status = VprocTwolfSaveCfgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveCfgToFlash()", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
ESP_LOGD(TAG, "Saving config to flash....done");
|
||||
|
||||
#endif
|
||||
}
|
||||
{ /*Verify that the boot loading PASS or Fail*/
|
||||
uint16 val = 0;
|
||||
|
||||
status = VprocTwolfHbiRead(0x0022, 1, &val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
if ((val == 38040) || (val == 38050) || (val == 38060) || (val == 38080) || (val == 38051) || (val == 38041)) {
|
||||
ESP_LOGD(TAG, "Device boot loading completed successfully...");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Device boot loading failed!!!...");
|
||||
return VPROC_STATUS_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*Firmware reset - in order for the configuration to take effect
|
||||
* NOTE: The ZL38040 needs a soft reset for the uploaded configuration
|
||||
* to take effect. This soft-reset is sent below
|
||||
* if the ZL38040 is an I2S slave, if the I2S master is not stable
|
||||
* at the time of this reset, then that reset will not take effect.
|
||||
* In that case the host has to to simply resend the reset
|
||||
* command once the I2S master
|
||||
* is up and running and is at a stable state.
|
||||
*/
|
||||
status = VprocTwolfReset(VPROC_RST_SOFTWARE);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfReset()", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Device boot loading completed successfully...");
|
||||
return status;
|
||||
}
|
||||
|
||||
int test_zl38063(void *arg)
|
||||
{
|
||||
int status = 0;
|
||||
uint16 cmdword = 0;
|
||||
uint16 val[MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST];
|
||||
uint8 numwords = 0;
|
||||
uint16 tempbuf[MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST];
|
||||
uint16 i = 0;
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
uint16 j = 0;
|
||||
#endif
|
||||
status = VprocTwolfHbiInit();
|
||||
if (status < 0) {
|
||||
perror("tw_spi_access open");
|
||||
return -1;
|
||||
}
|
||||
if ((MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST > 125) || (MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST < 2)) {
|
||||
ESP_LOGD(TAG, "MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST must between 2 and 126");
|
||||
}
|
||||
memset(val, 0, sizeof(val));
|
||||
memset(tempbuf, 0, sizeof(tempbuf));
|
||||
|
||||
ESP_LOGD(TAG, "Test 1 - Verifying that the device is present and working ....");
|
||||
cmdword = 0x00C;
|
||||
numwords = 2;
|
||||
val[0] = 0x1234;
|
||||
val[1] = 0x5678;
|
||||
status = VprocTwolfHbiWrite(cmdword, numwords, val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiWrite()\n", status);
|
||||
VprocHALcleanup();
|
||||
return -1;
|
||||
}
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
j = 0;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
ESP_LOGD(TAG, "wr: addr 0x%04x = 0x%04x", (cmdword + j), val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
#endif
|
||||
status = VprocTwolfHbiRead(cmdword, numwords, tempbuf);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
j = 0;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x", (cmdword + j), tempbuf[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
#endif
|
||||
if ((tempbuf[0] != 0x1234) && (tempbuf[1] != 0x5600)) {
|
||||
ESP_LOGD(TAG, "Test 1 - completed - FAIL!!!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Test 1 - completed - PASS\n\n");
|
||||
|
||||
status = VprocTwolfReset(0);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "Device reset completed successfully...");
|
||||
|
||||
ESP_LOGD(TAG, "Test 2 - Verifying single word write/read access ....");
|
||||
cmdword = 0x0300;
|
||||
val[0] = 0x4008;
|
||||
numwords = 1;
|
||||
status = VprocTwolfHbiWrite(cmdword, numwords, val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiWrite()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
j = 0;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
ESP_LOGD(TAG, "wr: addr 0x%04x = 0x%04x", (cmdword + j), val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
#endif
|
||||
status = VprocTwolfHbiRead(cmdword, numwords, val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
j = 0;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x\n", (cmdword + j), val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
#endif
|
||||
if ((val[0] != 0x4008)) {
|
||||
ESP_LOGD(TAG, "Test 2 - completed - FAIL!!!");
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "Test 2 - completed - PASS");
|
||||
|
||||
ESP_LOGD(TAG, "Test 3 - Verifying multiple words write/read access ....");
|
||||
|
||||
/* Fill the data buffer with unique data values. */
|
||||
for (i = 0; i < MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST; i++) {
|
||||
val[i] = i | ((0xFF - i) << 8);
|
||||
}
|
||||
|
||||
cmdword = 0x0300;
|
||||
numwords = MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST;
|
||||
status = VprocTwolfHbiWrite(cmdword, numwords, val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiWrite()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
j = 0;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
ESP_LOGD(TAG, "twr: addr 0x%04x = 0x%04x", (cmdword + j), val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
#endif
|
||||
status = VprocTwolfHbiRead(cmdword, numwords, tempbuf);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
#ifdef TW_HAL_VERIFY_DEBUG
|
||||
j = 0;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x =? 0x%04x", (cmdword + j), tempbuf[i], val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
#endif
|
||||
j = 0;
|
||||
for (i = 0; i < MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST; i++) {
|
||||
if (tempbuf[i] != val[i]) {
|
||||
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x =? 0x%04x", (cmdword + j), tempbuf[i], val[i]);
|
||||
ESP_LOGD(TAG, "Test 3 - completed - FAIL!!!");
|
||||
return -1;
|
||||
}
|
||||
j = j + 2;
|
||||
}
|
||||
ESP_LOGD(TAG, "Test 3 - completed - PASS");
|
||||
|
||||
ESP_LOGD(TAG, "Test 4 - Verifying the firmware/config boot loading ....");
|
||||
if (LoadFwrConfig_Alt(0) != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Device boot loading failed.....");
|
||||
ESP_LOGD(TAG, "Test 4 - completed - FAIL!!!");
|
||||
|
||||
} else
|
||||
ESP_LOGD(TAG, "Test 4 - completed - PASS");
|
||||
VprocTwolfHbiCleanup();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/****************************************************************************
|
||||
* tw_ldfwcfg.c - To load a *.s3 firmware and/or a *.cr2 into the device
|
||||
* and optionally save the loaded image to flash
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* malloc, free, rand */
|
||||
#include "esp_log.h"
|
||||
#include "vproc_common.h"
|
||||
#include "vprocTwolf_access.h"
|
||||
|
||||
/*NOTE: notice that the *.c code are included in the apps-
|
||||
* This is because the compiler I'm using requires that
|
||||
* But if your makefile is such that compiler knows where to find these files
|
||||
* then remove the #include *.c below
|
||||
*/
|
||||
|
||||
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
|
||||
static const char *TAG = "TW_LDCFG";
|
||||
uint16 numElements;
|
||||
dataArr *pCr2Buf;
|
||||
|
||||
/* fseekNunlines() -- The firmware file is an ascii text file.
|
||||
* the information from fseek will not be useful.
|
||||
* this is our own fseek equivalent
|
||||
*/
|
||||
static unsigned long fseekNunlines(FILE *BOOT_FD)
|
||||
{
|
||||
uint32 line_count = 0;
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(BOOT_FD)) != EOF) {
|
||||
if (c == '\n')
|
||||
line_count++;
|
||||
}
|
||||
return line_count;
|
||||
}
|
||||
|
||||
/* readCfgFile() use this function to
|
||||
* Read the Voice processing cr2 config file into RAM
|
||||
* filepath -- pointer to the location where to find the file
|
||||
* pCr2Buf -- the actual firmware data array will be pointed to this buffer
|
||||
*/
|
||||
static int readCfgFile(char *filepath)
|
||||
{
|
||||
unsigned int reg[2], val[2], len;
|
||||
uint8 done = 0;
|
||||
uint16 index = 0;
|
||||
FILE *BOOT_FD;
|
||||
char *s;
|
||||
char line[512] = "";
|
||||
|
||||
BOOT_FD = fopen(filepath, "rb");
|
||||
if (BOOT_FD != NULL) {
|
||||
len = fseekNunlines(BOOT_FD);
|
||||
if (len <= 0) {
|
||||
ESP_LOGD(TAG, "Error: file is not of the correct format...");
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "fileLength = %u", len);
|
||||
/*start at the beginning of the file*/
|
||||
// fseek(BOOT_FD, 0, SEEK_SET);
|
||||
|
||||
/* allocate memory to contain the reg and val:*/
|
||||
pCr2Buf = (dataArr *) malloc(len * sizeof(dataArr));
|
||||
if (pCr2Buf == NULL) {
|
||||
ESP_LOGD(TAG, "not enough memory to allocate %u bytes.. ", len * sizeof(dataArr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rewind(BOOT_FD);
|
||||
/*read and format the data accordingly*/
|
||||
numElements = 0;
|
||||
do {
|
||||
s = fgets(line, 512, BOOT_FD);
|
||||
if (line[0] == ';') {
|
||||
continue;
|
||||
} else if (s != NULL) {
|
||||
numElements++;
|
||||
sscanf(line, "%x %c %x", reg, s, val);
|
||||
pCr2Buf[index].reg = reg[0];
|
||||
pCr2Buf[index].value = val[0];
|
||||
// ESP_LOGD(TAG,"pCr2Buf[%d].reg pCr2Buf[%d].value = 0x%04x\t0x%04x\n", index, index,
|
||||
// pCr2Buf[index].reg, pCr2Buf[index].value);
|
||||
index++;
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
|
||||
} while (done == 0);
|
||||
|
||||
fclose(BOOT_FD);
|
||||
ESP_LOGD(TAG, "size of pCr2Buf = %u bytes.. ", sizeof(pCr2Buf));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Error: can't open file");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*This example host app load the *.s3 firmware to the device RAM. Optionally save it to flash
|
||||
* Then start the firmware from the execution address in RAM
|
||||
* It then stops the firmware - Load the cr2 file into RAM. Optionally save it to flash
|
||||
* Then restarts the firmware
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
VprocStatusType status = VPROC_STATUS_SUCCESS;
|
||||
|
||||
if (argc != 2) {
|
||||
ESP_LOGD(TAG, "Error: argc = %d - missing %d arg(s)... ", argc, 3 - (argc - 1));
|
||||
ESP_LOGD(TAG, "command Usage:%s ConfigPath", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
ESP_LOGD(TAG, ":%s %s %s", argv[0], argv[1], argv[2]);
|
||||
|
||||
/*global file handle*/
|
||||
status = VprocTwolfHbiInit();
|
||||
|
||||
if (status < 0) {
|
||||
perror("tw_spi_access open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (readCfgFile(argv[1]) < 0) {
|
||||
ESP_LOGD(TAG, "Error:read %s file", argv[1]);
|
||||
}
|
||||
ESP_LOGD(TAG, "a- Reading config file to host RAM - done....");
|
||||
|
||||
ESP_LOGD(TAG, "c- Loading the config file into the device RAM");
|
||||
status = VprocTwolfLoadConfig(pCr2Buf, numElements);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfLoadConfig()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef SAVE_CONFIG_TO_FLASH
|
||||
status = VprocTwolfSaveCfgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveCfgToFlash()", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "d- Saving config to flash- done....");
|
||||
#endif
|
||||
|
||||
ESP_LOGD(TAG, "e- Loading config record - done....");
|
||||
free(pCr2Buf);
|
||||
pCr2Buf = NULL;
|
||||
VprocTwolfHbiCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
|
||||
/****************************************************************************
|
||||
* tw_ldfw.c - To load a *.s3 firmware into the device
|
||||
* and optionally save the loaded image to flash
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "vproc_common.h"
|
||||
#include "vprocTwolf_access.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
/*NOTE: notice that the *.c code are included in the apps-
|
||||
* This is because the compiler I'm using requires that
|
||||
* But if your makefile is such that compiler knows where to find these files
|
||||
* then remove the #include *.c below
|
||||
*/
|
||||
|
||||
//#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
|
||||
static const char *TAG = "TW_LDFW";
|
||||
/*quick test*/
|
||||
|
||||
/*This example host app load the *.s3 firmware to the device RAM. Optionally save it to flash
|
||||
* Then start the firmware from the execution address in RAM
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
VprocStatusType status = VPROC_STATUS_SUCCESS;
|
||||
FILE *BOOT_FD;
|
||||
char line[256] = "";
|
||||
|
||||
if (argc != 2) {
|
||||
ESP_LOGD(TAG, "Error: argc = %d - missing %d arg(s)... ", argc, 3 - (argc - 1));
|
||||
ESP_LOGD(TAG, "command Usage:%s firmwarePath", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
ESP_LOGD(TAG, ":%s %s %s", argv[0], argv[1], argv[2]);
|
||||
|
||||
BOOT_FD = fopen(argv[1], "rb");
|
||||
if (BOOT_FD == NULL) {
|
||||
ESP_LOGD(TAG, "Error: can't open file %s", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*global file handle*/
|
||||
status = VprocTwolfHbiInit();
|
||||
if (status < 0) {
|
||||
perror("tw_spi_access open");
|
||||
fclose(BOOT_FD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "1- Opening firmware file - done....");
|
||||
|
||||
status = VprocTwolfHbiBootPrepare();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBootPrepare()", status);
|
||||
fclose(BOOT_FD);
|
||||
VprocHALcleanup();
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "-- Boot prepare - done....");
|
||||
|
||||
while (fgets(line, 256, BOOT_FD) != NULL) {
|
||||
status = VprocTwolfHbiBootMoreData(line);
|
||||
if (status == VPROC_STATUS_BOOT_LOADING_MORE_DATA) {
|
||||
continue;
|
||||
} else if (status == VPROC_STATUS_BOOT_LOADING_CMP) {
|
||||
break;
|
||||
} else if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBootMoreData()", status);
|
||||
fclose(BOOT_FD);
|
||||
VprocHALcleanup();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "-- Firmware data transfer - done....");
|
||||
fclose(BOOT_FD);
|
||||
/*clean up and verify that the boodloading completed correctly*/
|
||||
status = VprocTwolfHbiBootConclude();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBootConclude()", status);
|
||||
VprocHALcleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "2- Loading firmware - done....");
|
||||
#ifdef SAVE_IMAGE_TO_FLASH
|
||||
ESP_LOGD(TAG, "-- Saving firmware to flash....");
|
||||
status = VprocTwolfSaveImgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveImgToFlash()", status);
|
||||
VprocHALcleanup();
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGD(TAG, "-- Saving firmware to flash....done");
|
||||
|
||||
#endif
|
||||
|
||||
status = VprocTwolfFirmwareStart();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
ESP_LOGD(TAG, "Error %d:VprocTwolfFirmwareStart()", status);
|
||||
VprocHALcleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Device boot loading completed successfully...");
|
||||
|
||||
VprocHALcleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/****************************************************************************
|
||||
* tw_ldfwcfg.c - To load a *.s3 firmware and/or a *.cr2 into the device
|
||||
* and optionally save the loaded image to flash
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* malloc, free, rand */
|
||||
|
||||
#include "vproc_common.h"
|
||||
#include "vprocTwolf_access.h"
|
||||
|
||||
/*NOTE: notice that the *.c code are included in the apps-
|
||||
* This is because the compiler I'm using requires that
|
||||
* But if your makefile is such that compiler knows where to find these files
|
||||
* then remove the #include *.c below
|
||||
*/
|
||||
|
||||
#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
|
||||
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
|
||||
/*quick test*/
|
||||
|
||||
uint16 numElements;
|
||||
|
||||
dataArr *pCr2Buf;
|
||||
/* fseekNunlines() -- The firmware file is an ascii text file.
|
||||
* the information from fseek will not be useful.
|
||||
* this is our own fseek equivalent.
|
||||
*/
|
||||
static unsigned long fseekNunlines(FILE *BOOT_FD)
|
||||
{
|
||||
uint32 line_count = 0;
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(BOOT_FD)) != EOF) {
|
||||
if (c == '\n')
|
||||
line_count++;
|
||||
}
|
||||
return line_count;
|
||||
}
|
||||
|
||||
/* readCfgFile() use this function to
|
||||
* Read the Voice processing cr2 config file into RAM
|
||||
* filepath -- pointer to the location where to find the file
|
||||
* pCr2Buf -- the actual firmware data array will be pointed to this buffer
|
||||
*/
|
||||
static int readCfgFile(char *filepath)
|
||||
{
|
||||
unsigned int reg[2], val[2], len;
|
||||
uint8 done = 0;
|
||||
uint16 index = 0;
|
||||
FILE *BOOT_FD;
|
||||
char *s;
|
||||
char line[512] = "";
|
||||
|
||||
BOOT_FD = fopen(filepath, "rb");
|
||||
if (BOOT_FD != NULL) {
|
||||
len = fseekNunlines(BOOT_FD);
|
||||
if (len <= 0) {
|
||||
printf("Error: file is not of the correct format...\n");
|
||||
return -1;
|
||||
}
|
||||
// printf("fileLength = %u\n", len);
|
||||
/*start at the beginning of the file*/
|
||||
// fseek(BOOT_FD, 0, SEEK_SET);
|
||||
|
||||
/* allocate memory to contain the reg and val:*/
|
||||
pCr2Buf = (dataArr *) malloc(len * sizeof(dataArr));
|
||||
if (pCr2Buf == NULL) {
|
||||
printf("not enough memory to allocate %u bytes.. ", len * sizeof(dataArr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rewind(BOOT_FD);
|
||||
/*read and format the data accordingly*/
|
||||
numElements = 0;
|
||||
do {
|
||||
s = fgets(line, 512, BOOT_FD);
|
||||
if (line[0] == ';') {
|
||||
continue;
|
||||
} else if (s != NULL) {
|
||||
numElements++;
|
||||
sscanf(line, "%x %c %x", reg, s, val);
|
||||
pCr2Buf[index].reg = reg[0];
|
||||
pCr2Buf[index].value = val[0];
|
||||
// printf("pCr2Buf[%d].reg pCr2Buf[%d].value = 0x%04x\t0x%04x\n", index, index, pCr2Buf[index].reg,
|
||||
// pCr2Buf[index].value);
|
||||
index++;
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
|
||||
} while (done == 0);
|
||||
|
||||
fclose(BOOT_FD);
|
||||
// printf ("size of pCr2Buf = %u bytes.. \n", len*sizeof(pCr2Buf));
|
||||
} else {
|
||||
printf("Error: can't open file\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*This example host app load the *.s3 firmware to the device RAM. Optionally save it to flash
|
||||
* Then start the firmware from the execution address in RAM
|
||||
* It then stops the firmware - Load the cr2 file into RAM. Optionally save it to flash
|
||||
* Then resstarts the firmware
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
VprocStatusType status = VPROC_STATUS_SUCCESS;
|
||||
FILE *BOOT_FD;
|
||||
char line[256] = "";
|
||||
|
||||
if (argc < 3) {
|
||||
printf("Error: argc = %d - missing %d arg(s)... \n", argc, 3 - (argc - 1));
|
||||
printf("command Usage:%s firmwarePath ConfigPath\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
printf(":%s %s %s\n", argv[0], argv[1], argv[2]);
|
||||
|
||||
BOOT_FD = fopen(argv[1], "rb");
|
||||
if (BOOT_FD == NULL) {
|
||||
printf("Error: can't open file %s\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
/*global file handle*/
|
||||
status = VprocTwolfHbiInit();
|
||||
// gTwolf_fd = open(file_name, O_RDWR);
|
||||
if (status < 0) {
|
||||
perror("tw_spi_access open");
|
||||
fclose(BOOT_FD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("1- Opening firmware file - done....\n");
|
||||
|
||||
status = VprocTwolfHbiBootPrepare();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfHbiBootPrepare()\n", status);
|
||||
fclose(BOOT_FD);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
printf("-- Boot prepare - done....\n");
|
||||
|
||||
while (fgets(line, 256, BOOT_FD) != NULL) {
|
||||
status = VprocTwolfHbiBootMoreData(line);
|
||||
if (status == VPROC_STATUS_BOOT_LOADING_MORE_DATA) {
|
||||
continue;
|
||||
} else if (status == VPROC_STATUS_BOOT_LOADING_CMP) {
|
||||
break;
|
||||
} else if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfHbiBootMoreData()\n", status);
|
||||
fclose(BOOT_FD);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
printf("-- Firmware data transfer - done....\n");
|
||||
fclose(BOOT_FD);
|
||||
|
||||
status = VprocTwolfHbiBootConclude();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfHbiBootConclude()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef SAVE_IMAGE_TO_FLASH
|
||||
printf("-- Saving firmware to flash....\n");
|
||||
status = VprocTwolfSaveImgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfSaveImgToFlash()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
printf("-- Saving firmware to flash....done\n");
|
||||
|
||||
#endif
|
||||
|
||||
status = VprocTwolfFirmwareStart();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfFirmwareStart()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("3- Loading the config file into the device RAM\n");
|
||||
if (readCfgFile(argv[2]) < 0) {
|
||||
printf("Error:read %s file\n", argv[2]);
|
||||
}
|
||||
printf("a- Reading config file to host RAM - done....\n");
|
||||
|
||||
status = VprocTwolfLoadConfig(pCr2Buf, numElements);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfLoadConfig()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef SAVE_CFG_TO_FLASH
|
||||
printf("-- Saving config to flash....\n");
|
||||
status = VprocTwolfSaveCfgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfSaveCfgToFlash()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
printf("-- Saving config to flash....done\n");
|
||||
|
||||
#endif
|
||||
printf("Device boot loading completed successfully...\n");
|
||||
|
||||
VprocTwolfHbiCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
/****************************************************************************
|
||||
* tw_spi_access.c - Demo apps demonstrating how to access registers of the
|
||||
* device over spi or I2C. Loading a firmware and or config into the device
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
* Copyright Microsemi Inc, 2018. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.txt in the project
|
||||
* root for license information.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "vproc_common.h"
|
||||
#include "vprocTwolf_access.h"
|
||||
|
||||
/*NOTE: notice that the *.c code are included in the apps-
|
||||
* This is because the compiler I'm using requires that
|
||||
* But if your makefile is such that compiler knows where to find these files
|
||||
* then remove the #include *.c below
|
||||
*/
|
||||
#include "zl38063_config.h"
|
||||
#include "zl38063_firmware.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
|
||||
#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
|
||||
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
|
||||
|
||||
#define SAVE_IMAGE_TO_FLASH
|
||||
#define SAVE_CFG_TO_FLASH
|
||||
/*quick test*/
|
||||
|
||||
/*LoadFwrConfig_Alt - to load a converted *s3, *cr2 to c code into the device.
|
||||
* Basically instead of loading the *.s3, *cr2 directly,
|
||||
* use the tw_convert tool to convert the ascii hex fwr mage into code and compile
|
||||
* with the application
|
||||
*
|
||||
* input arg: mode: 0 - load both firmware and confing
|
||||
* 1 - load firmware only
|
||||
* 2 - load config only
|
||||
* -1 - Force loading
|
||||
*/
|
||||
int tw_upload_dsp_firmware(int mode)
|
||||
{
|
||||
union {
|
||||
short a;
|
||||
char b;
|
||||
} test_bigendian;
|
||||
if (mode >= 0) {
|
||||
uint16 vol = 0;
|
||||
esp_codec_dev_sleep(1000);
|
||||
int ret = VprocTwolfGetAppStatus(&vol);
|
||||
if (vol) {
|
||||
ESP_LOGW(TAG_SPI, "MCS ret:%d,Status:%d", ret, vol);
|
||||
return 0;
|
||||
}
|
||||
ESP_LOGI(TAG_SPI, "** Loading DSP firmware ret:%d,Status:%d **", ret, vol);
|
||||
} else {
|
||||
mode = 0;
|
||||
}
|
||||
test_bigendian.a = 1;
|
||||
ESP_LOGI(TAG_SPI, "b=%d", test_bigendian.b);
|
||||
|
||||
int status = VprocTwolfHbiInit();
|
||||
if (status < 0) {
|
||||
DEBUG_LOGE(TAG_SPI, "tw_spi_access open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((mode == 0) || (mode == 1)) {
|
||||
twFirmware st_Firmware;
|
||||
st_Firmware.st_Fwr = (twFwr *) st_twFirmware;
|
||||
st_Firmware.twFirmwareStreamLen = (uint16) firmwareStreamLen;
|
||||
st_Firmware.execAddr = (uint32) executionAddress;
|
||||
st_Firmware.havePrgmBase = (uint8) haveProgramBaseAddress;
|
||||
st_Firmware.prgmBase = (uint32) programBaseAddress;
|
||||
|
||||
ESP_LOGI(TAG_SPI, "1- Firmware boot loading started ....");
|
||||
|
||||
status = VprocTwolfHbiBoot_alt(&st_Firmware);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfHbiBoot()", status);
|
||||
// VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG_SPI, "2- Loading the image to RAM....done");
|
||||
#ifdef SAVE_IMAGE_TO_FLASH
|
||||
ESP_LOGI(TAG_SPI, "-- Saving firmware to flash....");
|
||||
status = VprocTwolfSaveImgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfSaveImgToFlash()", status);
|
||||
// VprocTwolfHbiCleanup();
|
||||
return status;
|
||||
}
|
||||
ESP_LOGI(TAG_SPI, "-- Saving firmware to flash....done");
|
||||
|
||||
#endif
|
||||
status = VprocTwolfFirmwareStart();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfFirmwareStart()", status);
|
||||
// VprocTwolfHbiCleanup();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
if ((mode == 0) || (mode == 2)) {
|
||||
ESP_LOGI(TAG_SPI, "3- Loading the config file into the device RAM....");
|
||||
|
||||
status = VprocTwolfLoadConfig((dataArr *) st_twConfig, (uint16) configStreamLen);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfLoadConfig()", status);
|
||||
// VprocTwolfHbiCleanup();
|
||||
return status;
|
||||
}
|
||||
#ifdef SAVE_CFG_TO_FLASH
|
||||
ESP_LOGI(TAG_SPI, "-- Saving config to flash....");
|
||||
status = VprocTwolfSaveCfgToFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfSaveCfgToFlash()", status);
|
||||
// VprocTwolfHbiCleanup();
|
||||
return status;
|
||||
}
|
||||
ESP_LOGI(TAG_SPI, "-- Saving config to flash....done");
|
||||
|
||||
#endif
|
||||
}
|
||||
/*Firmware reset - in order for the configuration to take effect*/
|
||||
status = VprocTwolfReset(VPROC_RST_SOFTWARE);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfReset()", status);
|
||||
ESP_LOGI(TAG_SPI, "Error");
|
||||
// VprocTwolfHbiCleanup();
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG_SPI, "Device boot loading completed successfully...");
|
||||
return status;
|
||||
}
|
||||
|
||||
int zl38063_comm(int argc, char **argv)
|
||||
{
|
||||
VprocStatusType status = VPROC_STATUS_SUCCESS;
|
||||
|
||||
if (argc == 1) {
|
||||
printf("Usage: for help type:%s -h", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "-h") == 0) {
|
||||
printf("\nUsage:\n\t%s [-cmd mode] [options...] see supported"
|
||||
" command modes below\n\n",
|
||||
argv[0]);
|
||||
printf("\t-wr : to write one or more 16-bit words to the device\n"
|
||||
"\t-rd : to read one or more 16-bit words from the device\n"
|
||||
"\t-rst : to reset the device in one of these"
|
||||
" supported reset modes:\n"
|
||||
"\t\t - [0: RAM | 1: ROM | 2: SOFT | 3: AEC | 4: BOOT]\n");
|
||||
printf("\t-lfcff : to load a specific firmware and related"
|
||||
" config from flash - arg: 1 to 14\n");
|
||||
printf("\t-lfcfh-a : to load a pre-compiled firmware and related config"
|
||||
" from host via SPI\n");
|
||||
printf("\t-lffh-a: to load a pre-compiled firmware from host via SPI\n");
|
||||
printf("\t-lcfh-a: to load a pre-compiled config from host via SPI\n");
|
||||
printf("\t-fclr : to erase the content of the ZL380xx slave flash\n");
|
||||
printf("\t-sto : to reset the device into boot mode\n");
|
||||
printf("\t-sta : to start execution of firmware found at "
|
||||
"exec address in RAM\n");
|
||||
printf("\t-apla : to configure the ZL380xx x-point for "
|
||||
"audio playback mode\n");
|
||||
printf("\t-arec : to configure the ZL380xx x-point for audio "
|
||||
"recording mode\n");
|
||||
printf("Example:\n");
|
||||
printf("\tEx to write 0x8004 into register 0x0300:"
|
||||
"\n\t%s -wr 0x0300 0x8004\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to read 12 words starting from register 0x020:"
|
||||
"\n\t%s -rd 0x0020 12\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to reset the device in boot mode:"
|
||||
"\n\t%s -rst n 'where n:[1-4]\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to load to RAM a firmware and config previously"
|
||||
" saved to flash at index 1:\n\t%s -lfcff 1\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to load to RAM a firmware previously"
|
||||
" saved to flash at index 1:\n\t%s -lfff 1\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to load to RAM the pre-compiled (in)firmware "
|
||||
"from teh host "
|
||||
"HBI (SPI):\n\t%s -lffh-a\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to load to RAM the pre-compiled (in)config "
|
||||
"from teh host "
|
||||
"HBI (SPI):\n\t%s -lcfh-a\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to load to RAM the pre-compiled (in)firmware and config "
|
||||
"from teh host "
|
||||
"HBI (SPI):\n\t%s -lfcfh-a\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to stop the firmware currently running and clear "
|
||||
"the RAM:\n\t%s -sto\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to start a firmware previously loaded into "
|
||||
"RAM:\n\t%s -sta\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to mute SOUT :\n\t%s -mute_s [1 | 0]\n\n", argv[0]);
|
||||
printf("\tEx to mute ROUT :\n\t%s -mute_r [1 | 0]\n\n", argv[0]);
|
||||
printf("\tEx to erase the slave flash device controlled by "
|
||||
"the ZL380xx :\n\t%s -fclr\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to configure the device for recording mode at a "
|
||||
"desired clock and sample rates"
|
||||
" with AEC off[0] or on [1]:\n\t%s -arec clkrate "
|
||||
"fsrate n 'where n:[0 | 1]'\n\n",
|
||||
argv[0]);
|
||||
printf("\tEx to configure the device for playback mode at a "
|
||||
"desired clock and sample rates"
|
||||
" with AEC off[0] or on [1]:\n\t%s -apla clkrate "
|
||||
"fsrate n 'where n:[0 | 1]'\n\n",
|
||||
argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((argc < 3) && (strcmp(argv[1], "-wr") == 0)) {
|
||||
printf("Usage:%s -wr register value0 value1....value124 \n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
if ((argc < 3) && (strcmp(argv[1], "-rd") == 0)) {
|
||||
printf("Usage:%s -rd register n 'where n:[1-127]'\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
if ((argc < 3) && (strcmp(argv[1], "-rst") == 0)) {
|
||||
printf("Usage:%s -rst n 'where n:[0-4]'\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
if ((argc < 3) && (strcmp(argv[1], "-apla") == 0)) {
|
||||
printf("Usage:%s -apla <clkrate in KHz> <fsrate in Hz> n"
|
||||
" 'where n:[0 | 1]'\n",
|
||||
argv[0]);
|
||||
return -1;
|
||||
}
|
||||
if ((argc < 3) && (strcmp(argv[1], "-arec") == 0)) {
|
||||
printf("Usage:%s -arec <clkrate in KHz> <fsrate in Hz> n"
|
||||
" 'where n:[0 | 1]'\n",
|
||||
argv[0]);
|
||||
return -1;
|
||||
}
|
||||
if ((argc < 3) && (strcmp(argv[1], "-lfcff") == 0)) {
|
||||
printf("Usage:%s -lfcfh 1\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
/*global file handle*/
|
||||
status = VprocTwolfHbiInit();
|
||||
if (status < 0) {
|
||||
perror("tw_spi_access open");
|
||||
return -1;
|
||||
}
|
||||
if ((strcmp(argv[1], "-wr") == 0) || (strcmp(argv[1], "-rd") == 0)) {
|
||||
int i = 0, j = 0;
|
||||
unsigned short val[128];
|
||||
unsigned short cmdword = (unsigned short) strtoul(argv[2], NULL, 0);
|
||||
unsigned char numwords = 0;
|
||||
|
||||
memset(val, 0, sizeof(val));
|
||||
|
||||
if (strcmp(argv[1], "-wr") == 0) { /*for WRITING 1 or more ZL380xx registers*/
|
||||
|
||||
unsigned short val[128];
|
||||
numwords = argc - 3; /*calculate the number of words to write*/
|
||||
;
|
||||
for (i = 0; i < numwords; i++) {
|
||||
val[i] = (unsigned short) strtoul(argv[3 + i], NULL, 0);
|
||||
}
|
||||
status = VprocTwolfHbiWrite(cmdword, numwords, val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfHbiWrite()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < numwords; i++) {
|
||||
printf("wr: addr 0x%04x = 0x%04x\n", (cmdword + j), val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
|
||||
} else { /*for READING 1 or more ZL380xx registers**/
|
||||
numwords = (unsigned char) strtoul(argv[3], NULL, 0);
|
||||
if ((numwords == 0) || (numwords > 128)) {
|
||||
printf("number of words is out of range. Maximum is 128\n");
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
status = VprocTwolfHbiRead(cmdword, numwords, val);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfHbiRead()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < numwords; i++) {
|
||||
printf("RD: addr 0x%04x = 0x%04x\n", (cmdword + j), val[i]);
|
||||
j = j + 2;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[1], "-rst") == 0) { /*for RESETTING ZL380xx*/
|
||||
unsigned char rstMode = (unsigned char) strtoul(argv[2], NULL, 0);
|
||||
status = VprocTwolfReset((uint16) rstMode);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfHbiRead()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
printf("Device reset completed successfully...\n");
|
||||
|
||||
} else if (strcmp(argv[1], "-lfcff") == 0) {
|
||||
/*Load ZL380x0 firmware + related config record from flash*/
|
||||
unsigned short image_num = (unsigned short) strtoul(argv[2], NULL, 0);
|
||||
status = VprocTwolfFirmwareStop();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfFirmwareStop()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
status = VprocTwolfLoadFwrCfgFromFlash(image_num);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfLoadFwrCfgFromFlash()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
status = VprocTwolfReset(VPROC_RST_HARDWARE_RAM);
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfReset()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
printf("Device boot loading from flash completed successfully...\n");
|
||||
} else if (strcmp(argv[1], "-lfff") == 0) {
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfLoadFwrFromFlash()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Device boot loading from flash completed successfully...\n");
|
||||
|
||||
} else if (strcmp(argv[1], "-lfcfh-a") == 0) { /*for LOADING FWR/CFG via SPI*/
|
||||
if (tw_upload_dsp_firmware(0) != VPROC_STATUS_SUCCESS)
|
||||
printf("Device boot loading failed.....\n");
|
||||
|
||||
} else if (strcmp(argv[1], "-lcfh-a") == 0) { /*for LOADING CFG via SPI*/
|
||||
if (tw_upload_dsp_firmware(2) != VPROC_STATUS_SUCCESS)
|
||||
printf("Device boot loading failed.....\n");
|
||||
|
||||
} else if (strcmp(argv[1], "-lffh-a") == 0) { /*for LOADING FWR via SPI*/
|
||||
if (tw_upload_dsp_firmware(1) != VPROC_STATUS_SUCCESS)
|
||||
printf("Device boot loading failed.....\n");
|
||||
|
||||
} else if (strcmp(argv[1], "-sto") == 0) { /*for resetting into boot mode*/
|
||||
if (VprocTwolfFirmwareStop() != 0)
|
||||
VprocTwolfHbiCleanup();
|
||||
else
|
||||
printf("Firmware stopped to boot mode completed"
|
||||
" successfully...\n");
|
||||
} else if (strcmp(argv[1], "-sta") == 0) { /*start executing FWR/CFG */
|
||||
if (VprocTwolfFirmwareStart() != 0)
|
||||
VprocTwolfHbiCleanup();
|
||||
else
|
||||
printf("Firmware is now running successfully...\n");
|
||||
} else if (strcmp(argv[1], "-mute_r") == 0) { /*start executing FWR/CFG */
|
||||
uint8 mute = (uint8) strtoul(argv[2], NULL, 0);
|
||||
// to do need fix
|
||||
// if(VprocTwolfMute(VPROC_ROUT, mute) != 0)
|
||||
if (1) {
|
||||
VprocTwolfHbiCleanup();
|
||||
} else {
|
||||
if (mute)
|
||||
printf("ROUT Port muted sucessfully...\n");
|
||||
else
|
||||
printf("ROUT Port unmuted sucessfully...\n");
|
||||
}
|
||||
} else if (strcmp(argv[1], "-mute_s") == 0) { /*start executing FWR/CFG */
|
||||
uint8 mute = (uint8) strtoul(argv[2], NULL, 0);
|
||||
// to do need fix
|
||||
// if(VprocTwolfMute(VPROC_SOUT, mute) != 0)
|
||||
if (1)
|
||||
VprocTwolfHbiCleanup();
|
||||
else {
|
||||
if (mute)
|
||||
printf("SOUT Port muted sucessfully...\n");
|
||||
else
|
||||
printf("SOUT Port unmuted sucessfully...\n");
|
||||
}
|
||||
} else if ((strcmp(argv[1], "-arec") == 0) || (strcmp(argv[1], "-apla") == 0))
|
||||
/* configure the ZL380x0 for either audio recording or playback
|
||||
* Over an I2S link
|
||||
*/
|
||||
{
|
||||
unsigned short pclkrate = (unsigned short) strtoul(argv[2], NULL, 0);
|
||||
unsigned short fsrate = (unsigned short) strtoul(argv[3], NULL, 0);
|
||||
unsigned short aecState = (unsigned char) strtoul(argv[4], NULL, 0);
|
||||
printf("pclkrate = %u KHz, fsrate = %u Hz, AEC state = %d\n", pclkrate, fsrate, aecState);
|
||||
// to do need fix
|
||||
#if 0
|
||||
if (strcmp(argv[1], "-arec") == 0) {
|
||||
if (VprocTwolfUpstreamConfigure(pclkrate, fsrate, aecState) != 0)
|
||||
VprocTwolfHbiCleanup();
|
||||
else
|
||||
printf("Device configured for audio recording...\n");
|
||||
} else if (strcmp(argv[1], "-apla") == 0) {
|
||||
if (VprocTwolfDownstreamConfigure(pclkrate, fsrate, aecState) != 0)
|
||||
VprocTwolfHbiCleanup();
|
||||
else
|
||||
printf("Device configured for audio playback...\n");
|
||||
}
|
||||
#endif
|
||||
} else if (strcmp(argv[1], "-fclr") == 0) {
|
||||
/*Erase the full content of the ZL380x0 controlled slave flash*/
|
||||
status = VprocTwolfEraseFlash();
|
||||
if (status != VPROC_STATUS_SUCCESS) {
|
||||
printf("Error %d:VprocTwolfEraseFlash()\n", status);
|
||||
VprocTwolfHbiCleanup();
|
||||
return -1;
|
||||
}
|
||||
printf("flash erasing completed successfully...\n");
|
||||
} else {
|
||||
printf("Usage: for help type:\n%s -h\n", argv[0]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*Configuration file version: Microsemi_ZLS38063_1_P1_4_0_Config.cr2, modified: Tue Sep 18 20:48:31 2018*/
|
||||
|
||||
#ifndef _ZL38063_CONFIG_H_
|
||||
#define _ZL38063_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const unsigned short configStreamLen;
|
||||
extern const dataArr st_twConfig[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*Firmware Version : Microsemi_ZLS38063_1_P1_4_0_Firmware.s3, modified: Tue Sep 18 20:50:24 2018 */
|
||||
|
||||
#ifndef _ZL38063_FIRMWARE_H_
|
||||
#define _ZL38063_FIRMWARE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const twFwr st_twFirmware[];
|
||||
|
||||
extern const unsigned short firmwareStreamLen;
|
||||
extern const unsigned long programBaseAddress;
|
||||
extern const unsigned long executionAddress;
|
||||
extern const unsigned char haveProgramBaseAddress;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "zl38063_codec.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
#include "tw_spi_access.h"
|
||||
#include "vproc_common.h"
|
||||
|
||||
#define TAG "zl38063"
|
||||
|
||||
#define HBI_PAGED_READ(offset, length) ((uint16_t) (((uint16_t) (offset) << 8) | (length)))
|
||||
#define HBI_PAGED_WRITE(offset, length) ((uint16_t) (HBI_PAGED_READ(offset, length) | 0x0080))
|
||||
#define HBI_SELECT_PAGE(page) ((uint16_t) (0xFE00 | (page)))
|
||||
#define HBI_DIRECT_READ(offset, length) ((uint16_t) (0x8000 | ((uint16_t) (offset) << 8) | (length)))
|
||||
#define HBI_DIRECT_WRITE(offset, length) ((uint16_t) (HBI_DIRECT_READ(offset, length) | 0x0080))
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
bool is_open;
|
||||
bool enabled;
|
||||
bool pa_reverted;
|
||||
int16_t pa_pin;
|
||||
int16_t reset_pin;
|
||||
float hw_gain;
|
||||
} audio_codec_zl38063_t;
|
||||
|
||||
static uint16_t convert_edian(uint16_t v)
|
||||
{
|
||||
return (v >> 8) | ((v & 0xFF) << 8);
|
||||
}
|
||||
|
||||
static void get_write_cmd(uint16_t addr, int size, uint16_t *dst, int *n)
|
||||
{
|
||||
uint8_t page;
|
||||
uint8_t offset;
|
||||
page = addr >> 8;
|
||||
offset = (addr & 0xFF) / 2;
|
||||
if (page == 0) {
|
||||
dst[(*n)++] = convert_edian(HBI_DIRECT_WRITE(offset, size - 1));
|
||||
}
|
||||
if (page) {
|
||||
/*indirect page access*/
|
||||
if (page != 0xFF) {
|
||||
page -= 1;
|
||||
}
|
||||
dst[(*n)++] = convert_edian(HBI_SELECT_PAGE(page));
|
||||
dst[(*n)++] = convert_edian(HBI_PAGED_WRITE(offset, size - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void get_read_cmd(uint16_t addr, int size, uint16_t *dst, int *n)
|
||||
{
|
||||
uint8_t page;
|
||||
uint8_t offset;
|
||||
page = addr >> 8;
|
||||
offset = (addr & 0xFF) / 2;
|
||||
if (page == 0) {
|
||||
dst[(*n)++] = convert_edian(HBI_DIRECT_READ(offset, size - 1));
|
||||
}
|
||||
if (page) {
|
||||
// Indirect page access
|
||||
if (page != 0xFF) {
|
||||
page -= 1;
|
||||
}
|
||||
dst[(*n)++] = convert_edian(HBI_SELECT_PAGE(page));
|
||||
dst[(*n)++] = convert_edian(HBI_PAGED_READ(offset, size - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static int read_addr(audio_codec_zl38063_t *codec, uint16_t addr, int words, uint16_t *data)
|
||||
{
|
||||
int total_addr = 0;
|
||||
int n = 0;
|
||||
get_read_cmd(addr, words, (uint16_t *) &total_addr, &n);
|
||||
if (codec->ctrl_if->read_reg) {
|
||||
int ret =
|
||||
codec->ctrl_if->read_reg(codec->ctrl_if, total_addr, n * sizeof(uint16_t), data, words * sizeof(uint16_t));
|
||||
for (int i = 0; i < words; i++) {
|
||||
data[i] = convert_edian(data[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
static int write_addr(audio_codec_zl38063_t *codec, uint16_t addr, int words, uint16_t *data)
|
||||
{
|
||||
int total_addr = 0;
|
||||
int n = 0;
|
||||
get_write_cmd(addr, words, (uint16_t *) &total_addr, &n);
|
||||
if (codec->ctrl_if->write_reg) {
|
||||
for (int i = 0; i < words; i++) {
|
||||
data[i] = convert_edian(data[i]);
|
||||
}
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, total_addr, n * sizeof(uint16_t), data,
|
||||
words * sizeof(uint16_t));
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
static int get_status(audio_codec_zl38063_t *codec, uint16_t *status)
|
||||
{
|
||||
return read_addr(codec, 0x030, 1, status);
|
||||
}
|
||||
|
||||
static int zl38063_pa_power(audio_codec_zl38063_t *codec, bool on)
|
||||
{
|
||||
int16_t pa_pin = codec->pa_pin;
|
||||
if (pa_pin != -1 && codec->gpio_if != NULL) {
|
||||
codec->gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->gpio_if->set(pa_pin, codec->pa_reverted ? !on: on);
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int zl38063_reset(audio_codec_zl38063_t *codec, bool on)
|
||||
{
|
||||
int16_t reset_pin = codec->reset_pin;
|
||||
if (reset_pin != -1 && codec->gpio_if != NULL) {
|
||||
codec->gpio_if->setup(reset_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->gpio_if->set(reset_pin, !on);
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _set_vol(audio_codec_zl38063_t *codec, uint8_t vol)
|
||||
{
|
||||
uint16_t reg = vol + (vol << 8);
|
||||
int ret = write_addr(codec, 0x238, 1, ®);
|
||||
ret |= write_addr(codec, 0x23A, 1, ®);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
|
||||
int zl38063_get_vol(audio_codec_zl38063_t *codec, float *vol)
|
||||
{
|
||||
uint16_t reg = 0;
|
||||
int ret = read_addr(codec, 0x238, 1, ®);
|
||||
*vol = (int8_t) (reg >> 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zl38063_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
zl38063_codec_cfg_t *codec_cfg = (zl38063_codec_cfg_t *) cfg;
|
||||
if (codec == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(zl38063_codec_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->gpio_if = codec_cfg->gpio_if;
|
||||
codec->pa_pin = codec_cfg->pa_pin;
|
||||
codec->reset_pin = codec_cfg->reset_pin;
|
||||
codec->pa_reverted = codec_cfg->pa_reverted;
|
||||
uint16_t status = 0;
|
||||
VprocSetCtrlIf((void *) codec_cfg->ctrl_if);
|
||||
zl38063_reset(codec, true);
|
||||
int ret = get_status(codec, &status);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to write register");
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
if (status == 0) {
|
||||
ESP_LOGI(TAG, "Start upload firmware");
|
||||
ret = tw_upload_dsp_firmware(0);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to upload firmware");
|
||||
return ESP_CODEC_DEV_WRITE_FAIL;
|
||||
}
|
||||
}
|
||||
codec->is_open = true;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int zl38063_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (codec->enabled == enable) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
zl38063_pa_power(codec, enable);
|
||||
codec->enabled = enable;
|
||||
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int zl38063_set_vol(const audio_codec_if_t *h, float db_vol)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
db_vol -= codec->hw_gain;
|
||||
if (db_vol < -90.0) {
|
||||
db_vol = -90.0;
|
||||
} else if (db_vol > 6.0) {
|
||||
db_vol = 6.0;
|
||||
}
|
||||
int8_t reg = (int8_t) db_vol;
|
||||
int ret = _set_vol(codec, reg);
|
||||
ESP_LOGD(TAG, "Set vol reg:%d", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zl38063_close(const audio_codec_if_t *h)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open) {
|
||||
zl38063_pa_power(codec, false);
|
||||
codec->is_open = false;
|
||||
}
|
||||
zl38063_reset(codec, false);
|
||||
VprocSetCtrlIf(NULL);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int zl38063_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return write_addr(codec, (uint16_t) reg, 1, (uint16_t *) &value);
|
||||
}
|
||||
|
||||
static int zl38063_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
*value = 0;
|
||||
return read_addr(codec, reg, 1, (uint16_t *) value);
|
||||
}
|
||||
|
||||
static int zl38063_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
|
||||
if (codec == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (codec->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
if (fs->channel != 2 || fs->sample_rate != 48000 || fs->bits_per_sample != 16) {
|
||||
ESP_LOGE(TAG, "Firmware only support 48k 2channel 16 bits");
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
const audio_codec_if_t *zl38063_codec_new(zl38063_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
// verify param
|
||||
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
|
||||
ESP_LOGE(TAG, "Wrong codec config");
|
||||
return NULL;
|
||||
}
|
||||
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
|
||||
ESP_LOGE(TAG, "Control interface not open yet");
|
||||
return NULL;
|
||||
}
|
||||
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) calloc(1, sizeof(audio_codec_zl38063_t));
|
||||
if (codec == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->base.open = zl38063_open;
|
||||
codec->base.enable = zl38063_enable;
|
||||
codec->base.set_vol = zl38063_set_vol;
|
||||
codec->base.set_reg = zl38063_set_reg;
|
||||
codec->base.get_reg = zl38063_get_reg;
|
||||
codec->base.set_fs = zl38063_set_fs;
|
||||
codec->base.close = zl38063_close;
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
do {
|
||||
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(zl38063_codec_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Open fail");
|
||||
break;
|
||||
}
|
||||
return &codec->base;
|
||||
} while (0);
|
||||
if (codec) {
|
||||
free(codec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
511
managed_components/espressif__esp_codec_dev/esp_codec_dev.c
Normal file
511
managed_components/espressif__esp_codec_dev/esp_codec_dev.c
Normal file
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "esp_codec_dev.h"
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_data_if.h"
|
||||
#include "audio_codec_sw_vol.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "Adev_Codec"
|
||||
|
||||
#define VOL_TRANSITION_TIME (50)
|
||||
|
||||
typedef struct {
|
||||
const audio_codec_if_t *codec_if;
|
||||
const audio_codec_data_if_t *data_if;
|
||||
const audio_codec_vol_if_t *sw_vol;
|
||||
esp_codec_dev_type_t dev_caps;
|
||||
bool input_opened;
|
||||
bool output_opened;
|
||||
int volume;
|
||||
float mic_gain;
|
||||
bool muted;
|
||||
bool mic_muted;
|
||||
bool sw_vol_alloced;
|
||||
esp_codec_dev_vol_curve_t vol_curve;
|
||||
bool disable_when_closed;
|
||||
} codec_dev_t;
|
||||
|
||||
static bool _verify_codec_ready(codec_dev_t *dev)
|
||||
{
|
||||
if (dev->codec_if && dev->codec_if->is_open) {
|
||||
if (dev->codec_if->is_open(dev->codec_if) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _verify_drv_ready(codec_dev_t *dev, bool playback)
|
||||
{
|
||||
if (_verify_codec_ready(dev) == false) {
|
||||
ESP_LOGE(TAG, "Codec is not open yet");
|
||||
return false;
|
||||
}
|
||||
if (dev->data_if->is_open && dev->data_if->is_open(dev->data_if) == false) {
|
||||
ESP_LOGE(TAG, "Codec data interface not open");
|
||||
return false;
|
||||
}
|
||||
if (playback && dev->data_if->write == NULL) {
|
||||
ESP_LOGE(TAG, "Need provide write API");
|
||||
return false;
|
||||
}
|
||||
if (playback == false && dev->data_if->read == NULL) {
|
||||
ESP_LOGE(TAG, "Need provide read API");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int _verify_codec_setting(codec_dev_t *dev, bool playback)
|
||||
{
|
||||
if ((playback && (dev->dev_caps & ESP_CODEC_DEV_TYPE_OUT) == 0) ||
|
||||
(!playback && (dev->dev_caps & ESP_CODEC_DEV_TYPE_IN) == 0)) {
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
if (_verify_codec_ready(dev) == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _get_default_vol_curve(esp_codec_dev_vol_curve_t *curve)
|
||||
{
|
||||
curve->vol_map = (esp_codec_dev_vol_map_t *) malloc(2 * sizeof(esp_codec_dev_vol_map_t));
|
||||
if (curve->vol_map) {
|
||||
curve->count = 2;
|
||||
curve->vol_map[0].vol = 0;
|
||||
curve->vol_map[0].db_value = -50.0;
|
||||
curve->vol_map[1].vol = 100;
|
||||
curve->vol_map[1].db_value = 0.0;
|
||||
}
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static float _get_vol_db(esp_codec_dev_vol_curve_t *curve, int vol)
|
||||
{
|
||||
if (vol == 0) {
|
||||
return -96.0;
|
||||
}
|
||||
int n = curve->count;
|
||||
if (n == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (vol >= curve->vol_map[n - 1].vol) {
|
||||
return curve->vol_map[n - 1].db_value;
|
||||
}
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
if (vol < curve->vol_map[i + 1].vol) {
|
||||
if (curve->vol_map[i].vol != curve->vol_map[i + 1].vol) {
|
||||
float ratio = (curve->vol_map[i + 1].db_value - curve->vol_map[i].db_value) /
|
||||
(curve->vol_map[i + 1].vol - curve->vol_map[i].vol);
|
||||
return curve->vol_map[i].db_value + (vol - curve->vol_map[i].vol) * ratio;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static void _update_codec_setting(codec_dev_t *dev)
|
||||
{
|
||||
esp_codec_dev_handle_t h = (esp_codec_dev_handle_t) dev;
|
||||
if (dev->output_opened) {
|
||||
esp_codec_dev_set_out_vol(h, dev->volume);
|
||||
esp_codec_dev_set_out_mute(h, dev->muted);
|
||||
}
|
||||
if (dev->input_opened) {
|
||||
esp_codec_dev_set_in_gain(h, dev->mic_gain);
|
||||
esp_codec_dev_set_in_mute(h, dev->mic_muted);
|
||||
}
|
||||
}
|
||||
|
||||
esp_codec_dev_handle_t esp_codec_dev_new(esp_codec_dev_cfg_t *cfg)
|
||||
{
|
||||
if (cfg == NULL || cfg->data_if == NULL || cfg->dev_type == ESP_CODEC_DEV_TYPE_NONE) {
|
||||
return NULL;
|
||||
}
|
||||
codec_dev_t *dev = (codec_dev_t *) calloc(1, sizeof(codec_dev_t));
|
||||
if (dev == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
dev->dev_caps = cfg->dev_type;
|
||||
dev->codec_if = cfg->codec_if;
|
||||
dev->data_if = cfg->data_if;
|
||||
if (cfg->dev_type & ESP_CODEC_DEV_TYPE_OUT) {
|
||||
_get_default_vol_curve(&dev->vol_curve);
|
||||
}
|
||||
dev->disable_when_closed = true;
|
||||
return (esp_codec_dev_handle_t) dev;
|
||||
}
|
||||
|
||||
int esp_codec_dev_open(esp_codec_dev_handle_t handle, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL || fs == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (dev->input_opened || dev->output_opened) {
|
||||
ESP_LOGI(TAG, "Input already open");
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if ((dev->dev_caps & ESP_CODEC_DEV_TYPE_IN)) {
|
||||
// check record
|
||||
if (_verify_drv_ready(dev, false) == false) {
|
||||
ESP_LOGE(TAG, "Codec not support input");
|
||||
} else {
|
||||
dev->input_opened = true;
|
||||
}
|
||||
}
|
||||
if ((dev->dev_caps & ESP_CODEC_DEV_TYPE_OUT)) {
|
||||
// check record
|
||||
if (_verify_drv_ready(dev, true) == false) {
|
||||
ESP_LOGE(TAG, "Codec not support output");
|
||||
} else {
|
||||
dev->output_opened = true;
|
||||
}
|
||||
}
|
||||
if (dev->input_opened == false && dev->output_opened == false) {
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
const audio_codec_data_if_t *data_if = dev->data_if;
|
||||
if (data_if->set_fmt) {
|
||||
data_if->set_fmt(data_if, dev->dev_caps, fs);
|
||||
}
|
||||
if (data_if->enable) {
|
||||
data_if->enable(data_if, dev->dev_caps, true);
|
||||
}
|
||||
if (codec) {
|
||||
// TODO not set codec fs
|
||||
if (codec->set_fs) {
|
||||
if (codec->set_fs(codec, fs) != 0) {
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
}
|
||||
if (codec->enable) {
|
||||
if (codec->enable(codec, true) != ESP_CODEC_DEV_OK) {
|
||||
ESP_LOGE(TAG, "Fail to enable codec");
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dev->output_opened) {
|
||||
if (codec == NULL || codec->set_vol == NULL) {
|
||||
if (dev->sw_vol == NULL) {
|
||||
dev->sw_vol = audio_codec_new_sw_vol();
|
||||
dev->sw_vol_alloced = true;
|
||||
}
|
||||
}
|
||||
if (dev->sw_vol) {
|
||||
dev->sw_vol->open(dev->sw_vol, fs, VOL_TRANSITION_TIME);
|
||||
}
|
||||
}
|
||||
// update settings to avoid lost after re-enable
|
||||
_update_codec_setting(dev);
|
||||
ESP_LOGI(TAG, "Open codec device OK");
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_read(esp_codec_dev_handle_t handle, void *data, int len)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL || data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (dev->input_opened == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
const audio_codec_data_if_t *data_if = dev->data_if;
|
||||
if (data_if->read) {
|
||||
return data_if->read(data_if, (uint8_t *) data, len);
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_write(esp_codec_dev_handle_t handle, void *data, int len)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL || data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (dev->output_opened == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
const audio_codec_data_if_t *data_if = dev->data_if;
|
||||
if (data_if->write) {
|
||||
// Soft volume process firstly
|
||||
if (dev->sw_vol) {
|
||||
dev->sw_vol->process(dev->sw_vol, (uint8_t *) data, len, (uint8_t *) data, len);
|
||||
}
|
||||
return data_if->write(data_if, (uint8_t *) data, len);
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_vol_curve(esp_codec_dev_handle_t handle, esp_codec_dev_vol_curve_t *curve)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL || curve == NULL || curve->vol_map == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
int size = curve->count * sizeof(esp_codec_dev_vol_map_t);
|
||||
esp_codec_dev_vol_map_t *new_map = (esp_codec_dev_vol_map_t *) realloc(dev->vol_curve.vol_map, size);
|
||||
if (new_map == NULL) {
|
||||
return ESP_CODEC_DEV_NO_MEM;
|
||||
}
|
||||
dev->vol_curve.vol_map = new_map;
|
||||
memcpy(dev->vol_curve.vol_map, curve->vol_map, size);
|
||||
dev->vol_curve.count = curve->count;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_out_vol(esp_codec_dev_handle_t handle, int volume)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
float db_value = _get_vol_db(&dev->vol_curve, volume);
|
||||
dev->volume = volume;
|
||||
// Prefer to use software volume setting
|
||||
if (dev->sw_vol) {
|
||||
dev->sw_vol->set_vol(dev->sw_vol, db_value);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if (codec && codec->set_vol) {
|
||||
codec->set_vol(codec, db_value);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_vol_handler(esp_codec_dev_handle_t handle, const audio_codec_vol_if_t *vol_handler)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL || vol_handler == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (dev->sw_vol == vol_handler) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
if (dev->sw_vol) {
|
||||
if (dev->sw_vol_alloced) {
|
||||
audio_codec_delete_vol_if(dev->sw_vol);
|
||||
dev->sw_vol_alloced = false;
|
||||
}
|
||||
}
|
||||
dev->sw_vol = vol_handler;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_get_out_vol(esp_codec_dev_handle_t handle, int *volume)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
*volume = dev->volume;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_out_mute(esp_codec_dev_handle_t handle, bool mute)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
dev->muted = mute;
|
||||
if (codec && codec->mute) {
|
||||
codec->mute(codec, mute);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
// When codec not support mute set volume instead
|
||||
if (dev->sw_vol) {
|
||||
float db_value = mute ? -100.0 : dev->volume;
|
||||
dev->sw_vol->set_vol(dev->sw_vol, db_value);
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_get_out_mute(esp_codec_dev_handle_t handle, bool *muted)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
*muted = dev->muted;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_in_gain(esp_codec_dev_handle_t handle, float db)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
if (codec && codec->set_mic_gain) {
|
||||
codec->set_mic_gain(codec, (int) db);
|
||||
dev->mic_gain = db;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_in_channel_gain(esp_codec_dev_handle_t handle, uint16_t channel_mask, float db)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL || channel_mask == 0) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
if (codec && codec->set_mic_channel_gain) {
|
||||
codec->set_mic_channel_gain(codec, channel_mask, (int) db);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_get_in_gain(esp_codec_dev_handle_t handle, float *db_value)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
*db_value = dev->mic_gain;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_set_in_mute(esp_codec_dev_handle_t handle, bool mute)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
if (codec && codec->mute_mic) {
|
||||
codec->mute_mic(codec, mute);
|
||||
dev->mic_muted = mute;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
int esp_codec_dev_get_in_mute(esp_codec_dev_handle_t handle, bool *muted)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = _verify_codec_setting(dev, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
*muted = dev->mic_muted;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_set_disable_when_closed(esp_codec_dev_handle_t handle, bool disable)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
dev->disable_when_closed = disable;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
int esp_codec_dev_close(esp_codec_dev_handle_t handle)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (dev->output_opened == false && dev->input_opened == false) {
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
const audio_codec_if_t *codec = dev->codec_if;
|
||||
if (dev->disable_when_closed && codec) {
|
||||
if (codec->enable) {
|
||||
codec->enable(codec, false);
|
||||
}
|
||||
}
|
||||
const audio_codec_data_if_t *data_if = dev->data_if;
|
||||
if (data_if->enable) {
|
||||
data_if->enable(data_if, dev->dev_caps, false);
|
||||
}
|
||||
if (dev->sw_vol) {
|
||||
dev->sw_vol->close(dev->sw_vol);
|
||||
}
|
||||
dev->output_opened = dev->input_opened = false;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
void esp_codec_dev_delete(esp_codec_dev_handle_t handle)
|
||||
{
|
||||
codec_dev_t *dev = (codec_dev_t *) handle;
|
||||
if (dev) {
|
||||
esp_codec_dev_close(handle);
|
||||
if (dev->vol_curve.vol_map) {
|
||||
free(dev->vol_curve.vol_map);
|
||||
}
|
||||
// Only delete software vol when alloced internally
|
||||
if (dev->sw_vol && dev->sw_vol_alloced) {
|
||||
audio_codec_delete_vol_if(dev->sw_vol);
|
||||
}
|
||||
free(dev);
|
||||
}
|
||||
}
|
||||
|
||||
const char *esp_codec_dev_get_version(void)
|
||||
{
|
||||
return ESP_CODEC_DEV_VERSION;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_data_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "audio_codec_vol_if.h"
|
||||
|
||||
int audio_codec_delete_codec_if(const audio_codec_if_t *h)
|
||||
{
|
||||
if (h) {
|
||||
int ret = 0;
|
||||
if (h->close) {
|
||||
ret = h->close(h);
|
||||
}
|
||||
free((void *) h);
|
||||
return ret;
|
||||
}
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
|
||||
int audio_codec_delete_ctrl_if(const audio_codec_ctrl_if_t *h)
|
||||
{
|
||||
if (h) {
|
||||
int ret = 0;
|
||||
if (h->close) {
|
||||
ret = h->close(h);
|
||||
}
|
||||
free((void *) h);
|
||||
return ret;
|
||||
}
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
|
||||
int audio_codec_delete_data_if(const audio_codec_data_if_t *h)
|
||||
{
|
||||
if (h) {
|
||||
int ret = 0;
|
||||
if (h->close) {
|
||||
ret = h->close(h);
|
||||
}
|
||||
free((void *) h);
|
||||
return ret;
|
||||
}
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
|
||||
int audio_codec_delete_gpio_if(const audio_codec_gpio_if_t *gpio_if)
|
||||
{
|
||||
if (gpio_if) {
|
||||
free((void *) gpio_if);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
|
||||
int audio_codec_delete_vol_if(const audio_codec_vol_if_t *h)
|
||||
{
|
||||
if (h) {
|
||||
int ret = 0;
|
||||
if (h->close) {
|
||||
ret = h->close(h);
|
||||
}
|
||||
free((void *) h);
|
||||
return ret;
|
||||
}
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <math.h>
|
||||
#include "esp_codec_dev_vol.h"
|
||||
|
||||
int esp_codec_dev_vol_calc_reg(const esp_codec_dev_vol_range_t *vol_range, float db)
|
||||
{
|
||||
if (vol_range->max_vol.db_value == vol_range->min_vol.db_value) {
|
||||
return vol_range->max_vol.vol;
|
||||
}
|
||||
if (db >= vol_range->max_vol.db_value) {
|
||||
return vol_range->max_vol.vol;
|
||||
}
|
||||
if (db <= vol_range->min_vol.db_value) {
|
||||
return vol_range->min_vol.vol;
|
||||
}
|
||||
float ratio =
|
||||
(vol_range->max_vol.vol - vol_range->min_vol.vol) / (vol_range->max_vol.db_value - vol_range->min_vol.db_value);
|
||||
return (int) ((db - vol_range->min_vol.db_value) * ratio + vol_range->min_vol.vol);
|
||||
}
|
||||
|
||||
float esp_codec_dev_vol_calc_db(const esp_codec_dev_vol_range_t *vol_range, int vol)
|
||||
{
|
||||
if (vol_range->max_vol.vol == vol_range->min_vol.vol) {
|
||||
return vol_range->max_vol.db_value;
|
||||
}
|
||||
if (vol_range->max_vol.vol > vol_range->min_vol.vol) {
|
||||
if (vol >= vol_range->max_vol.vol) {
|
||||
return vol_range->max_vol.db_value;
|
||||
}
|
||||
if (vol <= vol_range->min_vol.vol) {
|
||||
return vol_range->min_vol.db_value;
|
||||
}
|
||||
} else {
|
||||
if (vol <= vol_range->max_vol.vol) {
|
||||
return vol_range->max_vol.db_value;
|
||||
}
|
||||
if (vol >= vol_range->min_vol.vol) {
|
||||
return vol_range->min_vol.db_value;
|
||||
}
|
||||
}
|
||||
float ratio =
|
||||
(vol_range->max_vol.db_value - vol_range->min_vol.db_value) / (vol_range->max_vol.vol - vol_range->min_vol.vol);
|
||||
return ((vol - vol_range->min_vol.vol) * ratio + vol_range->min_vol.db_value);
|
||||
}
|
||||
|
||||
float esp_codec_dev_col_calc_hw_gain(esp_codec_dev_hw_gain_t *hw_gain)
|
||||
{
|
||||
float pa_voltage = hw_gain->pa_voltage;
|
||||
float dac_voltage = hw_gain->codec_dac_voltage;
|
||||
if (pa_voltage == 0.0) {
|
||||
pa_voltage = 5.0;
|
||||
}
|
||||
if (dac_voltage == 0.0) {
|
||||
dac_voltage = 3.3;
|
||||
}
|
||||
return 20 * log10(dac_voltage / pa_voltage) + hw_gain->pa_gain;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=4.0'
|
||||
description: Audio codec device support for Espressif SOC
|
||||
repository: git://github.com/espressif/esp-adf.git
|
||||
repository_info:
|
||||
commit_sha: d2613ac466375f34711bf88ab49b5ed047b8df79
|
||||
path: components/esp_codec_dev
|
||||
url: https://github.com/espressif/esp-adf/tree/master/components/esp_codec_dev
|
||||
version: 1.2.0
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_CODEC_DEV_H_
|
||||
#define _ESP_CODEC_DEV_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_data_if.h"
|
||||
#include "esp_codec_dev_types.h"
|
||||
#include "esp_codec_dev_vol.h"
|
||||
#include "audio_codec_vol_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Codec device configuration
|
||||
*/
|
||||
typedef struct {
|
||||
esp_codec_dev_type_t dev_type; /*!< Codec device type */
|
||||
const audio_codec_if_t *codec_if; /*!< Codec interface */
|
||||
const audio_codec_data_if_t *data_if; /*!< Codec data interface */
|
||||
} esp_codec_dev_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Codec device handle
|
||||
*/
|
||||
typedef void *esp_codec_dev_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Get `esp_codec_dev` version string
|
||||
* @return Version information
|
||||
*/
|
||||
const char *esp_codec_dev_get_version(void);
|
||||
|
||||
/**
|
||||
* @brief New codec device
|
||||
* @param codec_dev_cfg: Codec device configuration
|
||||
* @return NULL: Fail to new codec device
|
||||
* -Others: Codec device handle
|
||||
*/
|
||||
esp_codec_dev_handle_t esp_codec_dev_new(esp_codec_dev_cfg_t *codec_dev_cfg);
|
||||
|
||||
/**
|
||||
* @brief Open codec device
|
||||
* @param codec: Codec device handle
|
||||
* @param fs: Audio sample information
|
||||
* @return ESP_CODEC_DEV_OK: Open success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support or driver not ready yet
|
||||
*/
|
||||
int esp_codec_dev_open(esp_codec_dev_handle_t codec, esp_codec_dev_sample_info_t *fs);
|
||||
|
||||
/**
|
||||
* @brief Read data from codec
|
||||
* @param codec: Codec device handle
|
||||
* @param data: Data to be read
|
||||
* @param len: Data length to be read
|
||||
* @return ESP_CODEC_DEV_OK: Read success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_read(esp_codec_dev_handle_t codec, void *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Write data to codec
|
||||
* Notes: when enable software volume, it will change input data level directly without copy
|
||||
* Make sure that input data is writable
|
||||
* @param codec: Codec device handle
|
||||
* @param data: Data to be wrote
|
||||
* @param len: Data length to be wrote
|
||||
* @return ESP_CODEC_DEV_OK: Write success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_write(esp_codec_dev_handle_t codec, void *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Set codec hardware gain
|
||||
* @param codec: Codec device handle
|
||||
* @param volume: Volume setting
|
||||
* @return ESP_CODEC_DEV_OK: Set output volume success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support output mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_set_out_vol(esp_codec_dev_handle_t codec, int volume);
|
||||
|
||||
/**
|
||||
* @brief Set codec software volume handler
|
||||
* Notes: it is not needed when codec support volume adjust in hardware
|
||||
* If not provided, it will use internally software volume process handler instead
|
||||
* @param codec: Codec device handle
|
||||
* @param vol_handler: Software volume process interface
|
||||
* @return ESP_CODEC_DEV_OK: Set volume handler success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support output mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_set_vol_handler(esp_codec_dev_handle_t codec, const audio_codec_vol_if_t* vol_handler);
|
||||
|
||||
/**
|
||||
* @brief Set codec volume curve
|
||||
* Notes: When volume curve not provided, it will use internally volume curve which is:
|
||||
* 1 - "-49.5dB", 100 - "0dB"
|
||||
* Need to call this API if you want to customize volume curve
|
||||
* @param codec: Codec device handle
|
||||
* @param curve: Volume curve setting
|
||||
* @return ESP_CODEC_DEV_OK: Set curve success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support output mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
* ESP_CODEC_DEV_NO_MEM: Not enough memory to hold volume curve
|
||||
*/
|
||||
int esp_codec_dev_set_vol_curve(esp_codec_dev_handle_t codec, esp_codec_dev_vol_curve_t *curve);
|
||||
|
||||
/**
|
||||
* @brief Get codec output volume
|
||||
* @param codec: Codec device handle
|
||||
* @param[out] volume: Volume to get
|
||||
* @return ESP_CODEC_DEV_OK: Get volume success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support output mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_get_out_vol(esp_codec_dev_handle_t codec, int *volume);
|
||||
|
||||
/**
|
||||
* @brief Set codec output mute
|
||||
* @param codec: Codec device handle
|
||||
* @param mute: Whether mute output or not
|
||||
* @return ESP_CODEC_DEV_OK: Set output mute success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support output mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_set_out_mute(esp_codec_dev_handle_t codec, bool mute);
|
||||
|
||||
/**
|
||||
* @brief Get codec output mute setting
|
||||
* @param codec: Codec device handle
|
||||
* @param[out] muted: Mute status to get
|
||||
* @return ESP_CODEC_DEV_OK: Get output mute success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support output mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_get_out_mute(esp_codec_dev_handle_t codec, bool *muted);
|
||||
|
||||
/**
|
||||
* @brief Set codec input gain
|
||||
* @param codec: Codec device handle
|
||||
* @param db_value: Input gain setting
|
||||
* @return ESP_CODEC_DEV_OK: Set input gain success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support input mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_set_in_gain(esp_codec_dev_handle_t codec, float db_value);
|
||||
|
||||
/**
|
||||
* @brief Set codec input gain by channel
|
||||
* @param codec: Codec device handle
|
||||
* @param channel_mask: Mask for channel to be set
|
||||
* @param db_value: Input gain setting
|
||||
* @return ESP_CODEC_DEV_OK: Set input gain success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support input mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_set_in_channel_gain(esp_codec_dev_handle_t codec, uint16_t channel_mask, float db_value);
|
||||
|
||||
/**
|
||||
* @brief Get codec input gain
|
||||
* @param codec: Codec device handle
|
||||
* @param db_value: Input gain to get
|
||||
* @return ESP_CODEC_DEV_OK: Get input gain success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support input mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_get_in_gain(esp_codec_dev_handle_t codec, float *db_value);
|
||||
|
||||
/**
|
||||
* @brief Set codec input mute
|
||||
* @param codec: Codec device handle
|
||||
* @param mute: Whether mute code input or not
|
||||
* @return ESP_CODEC_DEV_OK: Set input mute success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support input mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_set_in_mute(esp_codec_dev_handle_t codec, bool mute);
|
||||
|
||||
/**
|
||||
* @brief Get codec input mute
|
||||
* @param codec: Codec device handle
|
||||
* @param muted: Mute value to get
|
||||
* @return ESP_CODEC_DEV_OK: Set input mute success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
* ESP_CODEC_DEV_NOT_SUPPORT: Codec not support input mode
|
||||
* ESP_CODEC_DEV_WRONG_STATE: Driver not open yet
|
||||
*/
|
||||
int esp_codec_dev_get_in_mute(esp_codec_dev_handle_t codec, bool *muted);
|
||||
|
||||
/**
|
||||
* @brief Whether disable codec when closed
|
||||
* @param codec: Codec device handle
|
||||
* @param disable: Disable when closed (default is true)
|
||||
* @return ESP_CODEC_DEV_OK: Setting success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
*/
|
||||
int esp_codec_set_disable_when_closed(esp_codec_dev_handle_t codec, bool disable);
|
||||
|
||||
/**
|
||||
* @brief Close codec device
|
||||
* @param codec: Codec device handle
|
||||
* @return ESP_CODEC_DEV_OK: Close success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Invalid arguments
|
||||
*/
|
||||
int esp_codec_dev_close(esp_codec_dev_handle_t codec);
|
||||
|
||||
/**
|
||||
* @brief Delete the specified codec device instance
|
||||
* @param codec: Codec device handle
|
||||
*/
|
||||
void esp_codec_dev_delete(esp_codec_dev_handle_t codec);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_CODEC_DEV_DEFAULTS_H_
|
||||
#define _ESP_CODEC_DEV_DEFAULTS_H_
|
||||
|
||||
#include "audio_codec_if.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_data_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
|
||||
#ifdef CONFIG_CODEC_ES8311_SUPPORT
|
||||
#include "es8311_codec.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ES7210_SUPPORT
|
||||
#include "es7210_adc.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ES7243_SUPPORT
|
||||
#include "es7243_adc.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ES7243E_SUPPORT
|
||||
#include "es7243e_adc.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ES8156_SUPPORT
|
||||
#include "es8156_dac.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_AW88298_SUPPORT
|
||||
#include "aw88298_dac.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ES8374_SUPPORT
|
||||
#include "es8374_codec.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ES8388_SUPPORT
|
||||
#include "es8388_codec.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_TAS5805M_SUPPORT
|
||||
#include "tas5805m_dac.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CODEC_ZL38063_SUPPORT
|
||||
#include "zl38063_codec.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Codec I2C configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t port; /*!< I2C port, this port need pre-installed by other modules */
|
||||
uint8_t addr; /*!< I2C address, default address can be gotten from codec head files */
|
||||
void *bus_handle; /*!< I2C Master bus handle (for IDFv5.3 or higher version) */
|
||||
} audio_codec_i2c_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Codec I2S configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t port; /*!< I2S port, this port need pre-installed by other modules */
|
||||
void *rx_handle; /*!< I2S rx handle, need provide on IDF 5.x */
|
||||
void *tx_handle; /*!< I2S tx handle, need provide on IDF 5.x */
|
||||
} audio_codec_i2s_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Codec SPI configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t spi_port; /*!< SPI port, this port need pre-installed by other modules */
|
||||
int16_t cs_pin; /*!< SPI CS GPIO pin setting */
|
||||
int clock_speed; /*!< SPI clock unit hz (use 10MHZif set to 0)*/
|
||||
} audio_codec_spi_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Get default codec GPIO interface
|
||||
* @return NULL: Failed
|
||||
* Others: Codec GPIO interface
|
||||
*/
|
||||
const audio_codec_gpio_if_t *audio_codec_new_gpio(void);
|
||||
|
||||
/**
|
||||
* @brief Get default SPI control interface
|
||||
* @return NULL: Failed
|
||||
* Others: SPI control interface
|
||||
*/
|
||||
const audio_codec_ctrl_if_t *audio_codec_new_spi_ctrl(audio_codec_spi_cfg_t *spi_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get default I2C control interface
|
||||
* @return NULL: Failed
|
||||
* Others: I2C control interface
|
||||
*/
|
||||
const audio_codec_ctrl_if_t *audio_codec_new_i2c_ctrl(audio_codec_i2c_cfg_t *i2c_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get default I2S data interface
|
||||
* @return NULL: Failed
|
||||
* Others: I2S data interface
|
||||
*/
|
||||
const audio_codec_data_if_t *audio_codec_new_i2s_data(audio_codec_i2s_cfg_t *i2s_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_CODEC_DEV_OS_H_
|
||||
#define _ESP_CODEC_DEV_OS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sleep in milliseconds
|
||||
* @param ms: Sleep time (unit ms)
|
||||
*/
|
||||
void esp_codec_dev_sleep(int ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_CODEC_DEV_TYPES_H_
|
||||
#define _ESP_CODEC_DEV_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ESP_CODEC_DEV_VERSION "1.2.0"
|
||||
|
||||
/**
|
||||
* @brief Define error number of codec device module
|
||||
* Inherit from `esp_err_t`
|
||||
*/
|
||||
#define ESP_CODEC_DEV_OK (0)
|
||||
#define ESP_CODEC_DEV_DRV_ERR (ESP_FAIL)
|
||||
#define ESP_CODEC_DEV_INVALID_ARG (ESP_ERR_INVALID_ARG)
|
||||
#define ESP_CODEC_DEV_NO_MEM (ESP_ERR_NO_MEM)
|
||||
#define ESP_CODEC_DEV_NOT_SUPPORT (ESP_ERR_NOT_SUPPORTED)
|
||||
#define ESP_CODEC_DEV_NOT_FOUND (ESP_ERR_NOT_FOUND)
|
||||
#define ESP_CODEC_DEV_WRONG_STATE (ESP_ERR_INVALID_STATE)
|
||||
#define ESP_CODEC_DEV_WRITE_FAIL (0x10D)
|
||||
#define ESP_CODEC_DEV_READ_FAIL (0x10E)
|
||||
|
||||
#define ESP_CODEC_DEV_MAKE_CHANNEL_MASK(channel) ((uint16_t)1 << (channel))
|
||||
|
||||
/**
|
||||
* @brief Codec Device type
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_CODEC_DEV_TYPE_NONE,
|
||||
ESP_CODEC_DEV_TYPE_IN = (1 << 0), /*!< Codec input device like ADC (capture data from microphone) */
|
||||
ESP_CODEC_DEV_TYPE_OUT = (1 << 1), /*!< Codec output device like DAC (output analog signal to speaker) */
|
||||
ESP_CODEC_DEV_TYPE_IN_OUT = (ESP_CODEC_DEV_TYPE_IN | ESP_CODEC_DEV_TYPE_OUT), /*!< Codec input and output device */
|
||||
} esp_codec_dev_type_t;
|
||||
|
||||
/**
|
||||
* @brief Codec audio sample information
|
||||
* Notes: channel_mask is used to filter wanted channels in driver side
|
||||
* when set to 0, default filter all channels
|
||||
* when channel is 2, can filter channel 0 (set to 1) or channel 1 (set to 2)
|
||||
* when channel is 4, can filter either 3,2 channels or 1 channel
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t bits_per_sample; /*!< Bit lengths of one channel data */
|
||||
uint8_t channel; /*!< Channels of sample */
|
||||
uint16_t channel_mask; /*!< Channel mask indicate which channel to be selected */
|
||||
uint32_t sample_rate; /*!< Sample rate of sample */
|
||||
int mclk_multiple; /*!< The multiple of MCLK to the sample rate
|
||||
If value is 0, mclk = sample_rate * 256
|
||||
If bits_per_sample is 24bit, mclk_multiple should be the multiple of 3
|
||||
*/
|
||||
} esp_codec_dev_sample_info_t;
|
||||
|
||||
/**
|
||||
* @brief Codec working mode
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_CODEC_DEV_WORK_MODE_NONE,
|
||||
ESP_CODEC_DEV_WORK_MODE_ADC = (1 << 0), /*!< Enable ADC, only support input */
|
||||
ESP_CODEC_DEV_WORK_MODE_DAC = (1 << 1), /*!< Enable DAC, only support output */
|
||||
ESP_CODEC_DEV_WORK_MODE_BOTH =
|
||||
(ESP_CODEC_DEV_WORK_MODE_ADC | ESP_CODEC_DEV_WORK_MODE_DAC), /*!< Support both DAC and ADC */
|
||||
ESP_CODEC_DEV_WORK_MODE_LINE = (1 << 2), /*!< Line mode */
|
||||
} esp_codec_dec_work_mode_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_CODEC_DEV_VOL_H_
|
||||
#define _ESP_CODEC_DEV_VOL_H_
|
||||
|
||||
#include "esp_codec_dev_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Codec volume map to decibel
|
||||
*/
|
||||
typedef struct {
|
||||
int vol; /*!< Volume value */
|
||||
float db_value; /*!< Volume decibel value */
|
||||
} esp_codec_dev_vol_map_t;
|
||||
|
||||
/**
|
||||
* @brief Codec volume range setting
|
||||
*/
|
||||
typedef struct {
|
||||
esp_codec_dev_vol_map_t min_vol; /*!< Minimum volume setting */
|
||||
esp_codec_dev_vol_map_t max_vol; /*!< Maximum volume setting */
|
||||
} esp_codec_dev_vol_range_t;
|
||||
|
||||
/**
|
||||
* @brief Codec volume curve configuration
|
||||
*/
|
||||
typedef struct {
|
||||
esp_codec_dev_vol_map_t *vol_map; /*!< Point of volume curve */
|
||||
int count; /*!< Curve point number */
|
||||
} esp_codec_dev_vol_curve_t;
|
||||
|
||||
/*
|
||||
* Audio gain overview:
|
||||
* |----------------Software Gain--------------|--Hardware Gain--|
|
||||
*
|
||||
* |--------------------| |--------------------| |------------------| |---------| |----------------|
|
||||
* | Digital Audio Data |-->| Audio Process Gain |-->| Codec DAC Volume |-->| PA Gain |-->| Speaker Output |
|
||||
* |--------------------| |--------------------| |------------------| |---------| |----------------|
|
||||
*
|
||||
* Final speaker loudness is affected by both Software Gain and Hardware Gain.
|
||||
*
|
||||
* Software Gain (Adjustable):
|
||||
* Audio Process Gain: Gain by audio post processor, such as ALC, AGC, DRC target MAX Gain.
|
||||
* Codec DAC Volume: The audio codec DAC volume control, such as ES8311 DAC_Volume control register.
|
||||
*
|
||||
* Hardware Gain (Fixed):
|
||||
* PA Gain: The speaker power amplifier Gain, which is determined by the hardware circuit.
|
||||
*
|
||||
* The speaker playback route gain (Audio Process Gain + Codec DAC Volume + PA Gain) needs to ensure that the
|
||||
* speaker PA output is not saturated and exceeds the speaker rated power. We define the maximum route gain
|
||||
* as MAX_GAIN. To ensure the speaker PA output is not saturated, MAX_GAIN can be calculated simply by the formula.
|
||||
* MAX_GAIN = 20 * log(Vpa/Vdac)
|
||||
* Vpa: PA power supply
|
||||
* Vdac: Codec DAC power supply
|
||||
* e.g., Vpa = 5V, Vdac = 3.3V, then MAX_GAIN = 20 * log(5/3.3) = 3.6 dB.
|
||||
* If the speaker rated power is lower than the speaker PA MAX power, MAX_GAIN should be defined according to
|
||||
* the speaker rated power.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Codec hardware gain setting
|
||||
* Notes: Hardware gain generally consists of 2 parts
|
||||
* 1. Codec DAC voltage and PA voltage to get MAX_GAIN
|
||||
* 2. PA gain can be calculate by connected resistors
|
||||
*/
|
||||
typedef struct {
|
||||
float pa_voltage; /*!< PA voltage: typical 5.0v */
|
||||
float codec_dac_voltage; /*!< Codec chip DAC voltage: typical 3.3v */
|
||||
float pa_gain; /*!< PA amplify coefficient in decibel unit */
|
||||
} esp_codec_dev_hw_gain_t;
|
||||
|
||||
/**
|
||||
* @brief Convert decibel value to register settings
|
||||
* @param vol_range: Volume range
|
||||
* @param db: Volume decibel
|
||||
* @return Codec register value
|
||||
*/
|
||||
int esp_codec_dev_vol_calc_reg(const esp_codec_dev_vol_range_t *vol_range, float db);
|
||||
|
||||
/**
|
||||
* @brief Convert codec register setting to decibel value
|
||||
* @param vol_range: Volume range
|
||||
* @param vol: Volume register setting
|
||||
* @return Codec volume in decibel unit
|
||||
*/
|
||||
float esp_codec_dev_vol_calc_db(const esp_codec_dev_vol_range_t *vol_range, int vol);
|
||||
|
||||
/**
|
||||
* @brief Calculate codec hardware gain value
|
||||
* @param hw_gain: Hardware gain settings
|
||||
* @return Codec hardware gain in decibel unit
|
||||
*/
|
||||
float esp_codec_dev_col_calc_hw_gain(esp_codec_dev_hw_gain_t* hw_gain);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AUDIO_CODEC_CTRL_IF_H_
|
||||
#define _AUDIO_CODEC_CTRL_IF_H_
|
||||
|
||||
#include "esp_codec_dev_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct audio_codec_ctrl_if_t audio_codec_ctrl_if_t;
|
||||
|
||||
/**
|
||||
* @brief Audio codec control interface structure
|
||||
*/
|
||||
struct audio_codec_ctrl_if_t {
|
||||
int (*open)(const audio_codec_ctrl_if_t *ctrl, void *cfg, int cfg_size); /*!< Open codec control interface */
|
||||
bool (*is_open)(const audio_codec_ctrl_if_t *ctrl); /*!< Check whether codec control opened or not */
|
||||
int (*read_reg)(const audio_codec_ctrl_if_t *ctrl,
|
||||
int reg, int reg_len, void *data, int data_len); /*!< Read data from codec device register */
|
||||
int (*write_reg)(const audio_codec_ctrl_if_t *ctrl,
|
||||
int reg, int reg_len, void *data, int data_len); /*!< Write data to codec device register */
|
||||
int (*close)(const audio_codec_ctrl_if_t *ctrl); /*!< Close codec control interface */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Delete codec control interface instance
|
||||
* @param ctrl_if: Audio codec interface
|
||||
* @return ESP_CODEC_DEV_OK: Delete success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Input is NULL pointer
|
||||
*/
|
||||
int audio_codec_delete_ctrl_if(const audio_codec_ctrl_if_t *ctrl_if);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AUDIO_CODEC_DATA_IF_H_
|
||||
#define _AUDIO_CODEC_DATA_IF_H_
|
||||
|
||||
#include "esp_codec_dev_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct audio_codec_data_if_t audio_codec_data_if_t;
|
||||
|
||||
/**
|
||||
* @brief Audio codec data interface structure
|
||||
*/
|
||||
struct audio_codec_data_if_t {
|
||||
int (*open)(const audio_codec_data_if_t *h, void *data_cfg, int cfg_size); /*!< Open data interface */
|
||||
bool (*is_open)(const audio_codec_data_if_t *h); /*!< Check whether data interface is opened */
|
||||
int (*enable)(const audio_codec_data_if_t *h,
|
||||
esp_codec_dev_type_t dev_type,
|
||||
bool enable); /*!< Enable input or output channel */
|
||||
int (*set_fmt)(const audio_codec_data_if_t *h,
|
||||
esp_codec_dev_type_t dev_type,
|
||||
esp_codec_dev_sample_info_t *fs); /*!< Set audio format to data interface */
|
||||
int (*read)(const audio_codec_data_if_t *h, uint8_t *data, int size); /*!< Read data from data interface */
|
||||
int (*write)(const audio_codec_data_if_t *h, uint8_t *data, int size); /*!< Write data to data interface */
|
||||
int (*close)(const audio_codec_data_if_t *h); /*!< Close data interface */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Delete codec data interface instance
|
||||
* @param data_if: Codec data interface
|
||||
* @return ESP_CODEC_DEV_OK: Delete success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Input is NULL pointer
|
||||
*/
|
||||
int audio_codec_delete_data_if(const audio_codec_data_if_t *data_if);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AUDIO_CODEC_GPIO_IF_H_
|
||||
#define _AUDIO_CODEC_GPIO_IF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO drive mode
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_GPIO_MODE_FLOAT, /*!< Float */
|
||||
AUDIO_GPIO_MODE_PULL_UP = (1 << 0), /*!< Internally pullup */
|
||||
AUDIO_GPIO_MODE_PULL_DOWN = (1 << 1), /*!< Internally pulldown */
|
||||
} audio_gpio_mode_t;
|
||||
|
||||
/**
|
||||
* @brief GPIO direction type
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_GPIO_DIR_OUT, /*!< Output GPIO */
|
||||
AUDIO_GPIO_DIR_IN, /*!< Input GPIO */
|
||||
} audio_gpio_dir_t;
|
||||
|
||||
/**
|
||||
* @brief Codec GPIO interface structure
|
||||
*/
|
||||
typedef struct {
|
||||
int (*setup)(int16_t gpio, audio_gpio_dir_t dir, audio_gpio_mode_t mode); /*!< Setup GPIO */
|
||||
int (*set)(int16_t gpio, bool high); /*!< Set GPIO level */
|
||||
bool (*get)(int16_t gpio); /*!< Get GPIO level */
|
||||
} audio_codec_gpio_if_t;
|
||||
|
||||
/**
|
||||
* @brief Delete GPIO interface instance
|
||||
* @param gpio_if: GPIO interface
|
||||
* @return ESP_CODEC_DEV_OK: Delete success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Input is NULL pointer
|
||||
*/
|
||||
int audio_codec_delete_gpio_if(const audio_codec_gpio_if_t *gpio_if);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AUDIO_CODEC_IF_H_
|
||||
#define _AUDIO_CODEC_IF_H_
|
||||
|
||||
#include "esp_codec_dev_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct audio_codec_if_t audio_codec_if_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for codec interface
|
||||
*/
|
||||
struct audio_codec_if_t {
|
||||
int (*open)(const audio_codec_if_t *h, void *cfg, int cfg_size); /*!< Open codec */
|
||||
bool (*is_open)(const audio_codec_if_t *h); /*!< Check whether codec is opened */
|
||||
int (*enable)(const audio_codec_if_t *h, bool enable); /*!< Enable codec, when codec disabled it can use less power if provided */
|
||||
int (*set_fs)(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs); /*!< Set audio format to codec */
|
||||
int (*mute)(const audio_codec_if_t *h, bool mute); /*!< Mute and un-mute DAC output */
|
||||
int (*set_vol)(const audio_codec_if_t *h, float db); /*!< Set DAC volume in decibel */
|
||||
int (*set_mic_gain)(const audio_codec_if_t *h, float db); /*!< Set microphone gain in decibel */
|
||||
int (*set_mic_channel_gain)(const audio_codec_if_t *h,
|
||||
uint16_t channel_mask, float db); /*!< Set microphone gain in decibel by channel */
|
||||
int (*mute_mic)(const audio_codec_if_t *h, bool mute); /*!< Mute and un-mute microphone */
|
||||
int (*set_reg)(const audio_codec_if_t *h, int reg, int value); /*!< Set register value to codec */
|
||||
int (*get_reg)(const audio_codec_if_t *h, int reg, int *value); /*!< Get register value from codec */
|
||||
void (*dump_reg)(const audio_codec_if_t *h); /*!< Dump all register settings */
|
||||
int (*close)(const audio_codec_if_t *h); /*!< Close codec */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Delete codec interface instance
|
||||
* @param codec_if: Codec interface
|
||||
* @return ESP_CODEC_DEV_OK: Delete success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Input is NULL pointer
|
||||
*/
|
||||
int audio_codec_delete_codec_if(const audio_codec_if_t *codec_if);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _AUDIO_CODEC_VOL_IF_H_
|
||||
#define _AUDIO_CODEC_VOL_IF_H_
|
||||
|
||||
#include "esp_codec_dev_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct audio_codec_vol_if_t audio_codec_vol_if_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for volume interface
|
||||
*/
|
||||
struct audio_codec_vol_if_t {
|
||||
int (*open)(const audio_codec_vol_if_t *h,
|
||||
esp_codec_dev_sample_info_t *fs, int fade_time); /*!< Open for software volume processor */
|
||||
int (*set_vol)(const audio_codec_vol_if_t *h, float db_value); /*!< Set volume in decibel unit */
|
||||
int (*process)(const audio_codec_vol_if_t *h,
|
||||
uint8_t *in, int len, uint8_t *out, int out_len); /*!< Process data */
|
||||
int (*close)(const audio_codec_vol_if_t *h); /*!< Close volume processor */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Delete volume interface instance
|
||||
* @param vol_if: Volume interface
|
||||
* @return ESP_CODEC_DEV_OK: Delete success
|
||||
* ESP_CODEC_DEV_INVALID_ARG: Input is NULL pointer
|
||||
*/
|
||||
int audio_codec_delete_vol_if(const audio_codec_vol_if_t *vol_if);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define TICK_PER_MS portTICK_PERIOD_MS
|
||||
#else
|
||||
#define TICK_PER_MS portTICK_RATE_MS
|
||||
#endif
|
||||
#define DEFAULT_I2C_CLOCK (100000)
|
||||
#define DEFAULT_I2C_TRANS_TIMEOUT (100)
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#include "driver/i2c_master.h"
|
||||
#define USE_IDF_I2C_MASTER
|
||||
#else
|
||||
#include "driver/i2c.h"
|
||||
#endif
|
||||
|
||||
#define TAG "I2C_If"
|
||||
typedef struct {
|
||||
audio_codec_ctrl_if_t base;
|
||||
bool is_open;
|
||||
uint8_t port;
|
||||
uint8_t addr;
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
#endif
|
||||
} i2c_ctrl_t;
|
||||
|
||||
static int _i2c_ctrl_open(const audio_codec_ctrl_if_t *ctrl, void *cfg, int cfg_size)
|
||||
{
|
||||
if (ctrl == NULL || cfg == NULL || cfg_size != sizeof(audio_codec_i2c_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
audio_codec_i2c_cfg_t *i2c_cfg = (audio_codec_i2c_cfg_t *) cfg;
|
||||
i2c_ctrl->port = i2c_cfg->port;
|
||||
i2c_ctrl->addr = i2c_cfg->addr;
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
if (i2c_cfg->bus_handle == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = (i2c_cfg->addr >> 1),
|
||||
.scl_speed_hz = DEFAULT_I2C_CLOCK,
|
||||
};
|
||||
int ret = i2c_master_bus_add_device(i2c_cfg->bus_handle, &dev_cfg, &i2c_ctrl->dev_handle);
|
||||
return (ret == ESP_OK) ? 0 : ESP_CODEC_DEV_DRV_ERR;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool _i2c_ctrl_is_open(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
if (ctrl) {
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
return i2c_ctrl->is_open;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
static int _i2c_master_read_reg(i2c_ctrl_t *i2c_ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
uint8_t addr_data[2] = {0};
|
||||
addr_data[0] = addr & 0xff;
|
||||
addr_data[1] = addr >> 8;
|
||||
int ret = i2c_master_transmit_receive(i2c_ctrl->dev_handle, addr_data, addr_len, data, data_len, DEFAULT_I2C_TRANS_TIMEOUT);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Fail to read from dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
return ret ? ESP_CODEC_DEV_READ_FAIL : ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _i2c_master_write_reg(i2c_ctrl_t *i2c_ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
esp_err_t ret = ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
int len = addr_len + data_len;
|
||||
if (len <= 4) {
|
||||
// Not support write huge data
|
||||
uint8_t write_data[4] = {0};
|
||||
int i = 0;
|
||||
write_data[i++] = addr & 0xff;
|
||||
if (addr_len > 1) {
|
||||
write_data[i++] = addr >> 8;
|
||||
}
|
||||
uint8_t *w = (uint8_t*)data;
|
||||
while (i < len) {
|
||||
write_data[i++] = *(w++);
|
||||
}
|
||||
ret = i2c_master_transmit(i2c_ctrl->dev_handle, write_data, len, DEFAULT_I2C_TRANS_TIMEOUT);
|
||||
}
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to write to dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
return ret ? ESP_CODEC_DEV_WRITE_FAIL : ESP_CODEC_DEV_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _i2c_ctrl_read_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
if (ctrl == NULL || data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
if (i2c_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
return _i2c_master_read_reg(i2c_ctrl, addr, addr_len, data, data_len);
|
||||
#else
|
||||
esp_err_t ret = ESP_OK;
|
||||
i2c_cmd_handle_t cmd;
|
||||
cmd = i2c_cmd_link_create();
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write_byte(cmd, i2c_ctrl->addr, 1);
|
||||
ret |= i2c_master_write(cmd, (uint8_t *) &addr, addr_len, 1);
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_ctrl->port, cmd, DEFAULT_I2C_TRANS_TIMEOUT / TICK_PER_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write_byte(cmd, i2c_ctrl->addr | 0x01, 1);
|
||||
|
||||
for (int i = 0; i < data_len - 1; i++) {
|
||||
ret |= i2c_master_read_byte(cmd, (uint8_t *) data + i, 0);
|
||||
}
|
||||
ret |= i2c_master_read_byte(cmd, (uint8_t *) data + (data_len - 1), 1);
|
||||
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_ctrl->port, cmd, DEFAULT_I2C_TRANS_TIMEOUT / TICK_PER_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Fail to read from dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
return ret ? ESP_CODEC_DEV_READ_FAIL : ESP_CODEC_DEV_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _i2c_ctrl_write_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
if (ctrl == NULL || data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2c_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
return _i2c_master_write_reg(i2c_ctrl, addr, addr_len, data, data_len);
|
||||
#else
|
||||
esp_err_t ret = ESP_OK;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
ret |= i2c_master_start(cmd);
|
||||
ret |= i2c_master_write_byte(cmd, i2c_ctrl->addr, 1);
|
||||
ret |= i2c_master_write(cmd, (uint8_t *) &addr, addr_len, 1);
|
||||
if (data_len) {
|
||||
ret |= i2c_master_write(cmd, data, data_len, 1);
|
||||
}
|
||||
ret |= i2c_master_stop(cmd);
|
||||
ret |= i2c_master_cmd_begin(i2c_ctrl->port, cmd, DEFAULT_I2C_TRANS_TIMEOUT / TICK_PER_MS);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to write to dev %x", i2c_ctrl->addr);
|
||||
}
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret ? ESP_CODEC_DEV_WRITE_FAIL : ESP_CODEC_DEV_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _i2c_ctrl_close(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
i2c_ctrl_t *i2c_ctrl = (i2c_ctrl_t *) ctrl;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
if (i2c_ctrl->dev_handle) {
|
||||
i2c_master_bus_rm_device(i2c_ctrl->dev_handle);
|
||||
}
|
||||
#endif
|
||||
i2c_ctrl->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_ctrl_if_t *audio_codec_new_i2c_ctrl(audio_codec_i2c_cfg_t *i2c_cfg)
|
||||
{
|
||||
if (i2c_cfg == NULL) {
|
||||
ESP_LOGE(TAG, "Bad configuration");
|
||||
return NULL;
|
||||
}
|
||||
i2c_ctrl_t *ctrl = calloc(1, sizeof(i2c_ctrl_t));
|
||||
if (ctrl == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
ctrl->base.open = _i2c_ctrl_open;
|
||||
ctrl->base.is_open = _i2c_ctrl_is_open;
|
||||
ctrl->base.read_reg = _i2c_ctrl_read_reg;
|
||||
ctrl->base.write_reg = _i2c_ctrl_write_reg;
|
||||
ctrl->base.close = _i2c_ctrl_close;
|
||||
int ret = _i2c_ctrl_open(&ctrl->base, i2c_cfg, sizeof(audio_codec_i2c_cfg_t));
|
||||
if (ret != 0) {
|
||||
free(ctrl);
|
||||
return NULL;
|
||||
}
|
||||
ctrl->is_open = true;
|
||||
return &ctrl->base;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "driver/spi_common.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "SPI_If"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_ctrl_if_t base;
|
||||
bool is_open;
|
||||
uint8_t port;
|
||||
spi_device_handle_t spi_handle;
|
||||
} spi_ctrl_t;
|
||||
|
||||
int _spi_ctrl_open(const audio_codec_ctrl_if_t *ctrl, void *cfg, int cfg_size)
|
||||
{
|
||||
if (ctrl == NULL || cfg == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (cfg_size != sizeof(audio_codec_spi_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
audio_codec_spi_cfg_t *spi_cfg = (audio_codec_spi_cfg_t *) cfg;
|
||||
int speed = spi_cfg->clock_speed ? spi_cfg->clock_speed : 1000000;
|
||||
spi_device_interface_config_t dev_cfg = {
|
||||
.clock_speed_hz = speed, // Clock out at 10 MHz
|
||||
.mode = 0, // SPI mode 0
|
||||
.queue_size = 6, // queue 7 transactions at a time
|
||||
};
|
||||
dev_cfg.spics_io_num = spi_cfg->cs_pin;
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 4))
|
||||
spi_host_device_t host_id = SPI2_HOST;
|
||||
#elif (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0))
|
||||
spi_host_device_t host_id = SPI3_HOST;
|
||||
#else
|
||||
spi_host_device_t host_id = HSPI_HOST;
|
||||
#endif
|
||||
int ret = spi_bus_add_device(host_id, &dev_cfg, &spi_ctrl->spi_handle);
|
||||
if (ret == 0) {
|
||||
gpio_set_pull_mode(spi_cfg->cs_pin, GPIO_FLOATING);
|
||||
spi_ctrl->is_open = true;
|
||||
}
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
bool _spi_ctrl_is_open(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
if (ctrl) {
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
return spi_ctrl->is_open;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _spi_ctrl_read_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
if (spi_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (addr_len) {
|
||||
uint16_t *v = (uint16_t *) &addr;
|
||||
while (addr_len >= 2) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = 2 * 8;
|
||||
t.tx_buffer = v;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
v++;
|
||||
addr_len -= 2;
|
||||
}
|
||||
}
|
||||
if (data_len) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = data_len * 8;
|
||||
t.rxlength = data_len * 8;
|
||||
t.rx_buffer = data;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
}
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static int _spi_ctrl_write_reg(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (spi_ctrl->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (addr_len) {
|
||||
uint16_t *v = (uint16_t *) &addr;
|
||||
while (addr_len >= 2) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = 2 * 8;
|
||||
t.tx_buffer = v;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
v++;
|
||||
addr_len -= 2;
|
||||
}
|
||||
}
|
||||
if (data_len) {
|
||||
spi_transaction_t t = {0};
|
||||
t.length = data_len * 8;
|
||||
t.tx_buffer = data;
|
||||
ret = spi_device_transmit(spi_ctrl->spi_handle, &t);
|
||||
}
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
int _spi_ctrl_close(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
spi_ctrl_t *spi_ctrl = (spi_ctrl_t *) ctrl;
|
||||
if (ctrl == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = 0;
|
||||
if (spi_ctrl->spi_handle) {
|
||||
ret = spi_bus_remove_device(spi_ctrl->spi_handle);
|
||||
}
|
||||
spi_ctrl->is_open = false;
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
const audio_codec_ctrl_if_t *audio_codec_new_spi_ctrl(audio_codec_spi_cfg_t *spi_cfg)
|
||||
{
|
||||
spi_ctrl_t *ctrl = calloc(1, sizeof(spi_ctrl_t));
|
||||
if (ctrl == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
ctrl->base.open = _spi_ctrl_open;
|
||||
ctrl->base.is_open = _spi_ctrl_is_open;
|
||||
ctrl->base.read_reg = _spi_ctrl_read_reg;
|
||||
ctrl->base.write_reg = _spi_ctrl_write_reg;
|
||||
ctrl->base.close = _spi_ctrl_close;
|
||||
int ret = _spi_ctrl_open(&ctrl->base, spi_cfg, sizeof(audio_codec_spi_cfg_t));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Fail to open SPI driver");
|
||||
free(ctrl);
|
||||
return NULL;
|
||||
}
|
||||
return &ctrl->base;
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "audio_codec_data_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#else
|
||||
#include "driver/i2s.h"
|
||||
#endif
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "I2S_IF"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_data_if_t base;
|
||||
bool is_open;
|
||||
uint8_t port;
|
||||
void *out_handle;
|
||||
void *in_handle;
|
||||
bool out_enable;
|
||||
bool in_enable;
|
||||
bool in_disable_pending;
|
||||
bool out_disable_pending;
|
||||
bool in_reconfig;
|
||||
bool out_reconfig;
|
||||
esp_codec_dev_sample_info_t in_fs;
|
||||
esp_codec_dev_sample_info_t out_fs;
|
||||
esp_codec_dev_sample_info_t fs;
|
||||
} i2s_data_t;
|
||||
|
||||
static bool _i2s_valid_fmt(esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
if (fs->sample_rate == 0 ||
|
||||
fs->sample_rate >= 192000) {
|
||||
ESP_LOGE(TAG, "Bad sample_rate %d", (int) fs->sample_rate);
|
||||
}
|
||||
if (fs->channel == 0 ||
|
||||
(fs->channel >> 1 << 1) != fs->channel) {
|
||||
ESP_LOGE(TAG, "Not support channel %d", fs->channel);
|
||||
return false;
|
||||
}
|
||||
if (fs->bits_per_sample < 8 || fs->bits_per_sample > 32 ||
|
||||
(fs->bits_per_sample >> 3 << 3) != fs->bits_per_sample) {
|
||||
ESP_LOGE(TAG, "Not support bits_per_sample %d", fs->bits_per_sample);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int _i2s_drv_enable(i2s_data_t *i2s_data, bool playback, bool enable)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t channel = (i2s_chan_handle_t) (
|
||||
playback ? i2s_data->out_handle : i2s_data->in_handle);
|
||||
if (channel == NULL) {
|
||||
return ESP_CODEC_DEV_NOT_FOUND;
|
||||
}
|
||||
int ret;
|
||||
if (enable) {
|
||||
ret = i2s_channel_enable(channel);
|
||||
} else {
|
||||
ret = i2s_channel_disable(channel);
|
||||
}
|
||||
return ret == ESP_OK ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
#endif
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
static uint8_t get_active_channel(esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
if (fs->channel_mask == 0) {
|
||||
return fs->channel;
|
||||
}
|
||||
int channel = 0;
|
||||
uint16_t mask = fs->channel_mask;
|
||||
while (mask > 0) {
|
||||
if (mask & 1) {
|
||||
channel++;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
static uint8_t get_bits(i2s_data_t *i2s_data, bool playback)
|
||||
{
|
||||
uint8_t total_bits = i2s_data->fs.bits_per_sample * i2s_data->fs.channel;
|
||||
if (playback) {
|
||||
return total_bits / i2s_data->out_fs.channel;
|
||||
}
|
||||
return total_bits / i2s_data->in_fs.channel;
|
||||
}
|
||||
|
||||
static int set_drv_fs(i2s_chan_handle_t channel, bool playback, uint8_t slot_bits, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
i2s_chan_info_t channel_info = {0};
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
i2s_channel_get_info(channel, &channel_info);
|
||||
ESP_LOGI(TAG, "channel mode %d bits:%d/%d channel:%d mask:%x",
|
||||
channel_info.mode, fs->bits_per_sample, slot_bits, (int)fs->channel, (int)fs->channel_mask);
|
||||
switch (channel_info.mode) {
|
||||
case I2S_COMM_MODE_STD: {
|
||||
uint8_t bits = fs->bits_per_sample;
|
||||
uint8_t active_channel = get_active_channel(fs);
|
||||
uint16_t channel_mask = fs->channel_mask;
|
||||
if (fs->channel > 2) {
|
||||
slot_bits = slot_bits * fs->channel / 2;
|
||||
active_channel = 2;
|
||||
bits = slot_bits;
|
||||
channel_mask = 0;
|
||||
}
|
||||
i2s_std_slot_mask_t slot_mask = fs->channel_mask ?
|
||||
(i2s_std_slot_mask_t) fs->channel_mask : I2S_STD_SLOT_BOTH;
|
||||
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(slot_bits, active_channel);
|
||||
slot_cfg.slot_mask = slot_mask;
|
||||
if (slot_bits > bits) {
|
||||
slot_cfg.data_bit_width = bits;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
if (fs->mclk_multiple) {
|
||||
clk_cfg.mclk_multiple = fs->mclk_multiple;
|
||||
}
|
||||
if (slot_bits == 24 && (clk_cfg.mclk_multiple % 3) != 0) {
|
||||
clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
|
||||
}
|
||||
ret = i2s_channel_reconfig_std_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
*(int *) 0 = 0;
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_std_clock(channel, &clk_cfg);
|
||||
ESP_LOGI(TAG, "STD Mode %d bits:%d/%d channel:%d sample_rate:%d mask:%x",
|
||||
playback, bits, slot_bits, fs->channel,
|
||||
(int)fs->sample_rate, channel_mask);
|
||||
}
|
||||
break;
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
case I2S_COMM_MODE_PDM: {
|
||||
if (playback == false) {
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
i2s_pdm_rx_clk_config_t clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(slot_bits, I2S_SLOT_MODE_STEREO);
|
||||
i2s_pdm_slot_mask_t slot_mask = fs->channel_mask ?
|
||||
(i2s_pdm_slot_mask_t) fs->channel_mask : I2S_PDM_SLOT_BOTH;
|
||||
// Stereo channel mask is ignored in driver, need use mono instead
|
||||
if (fs->channel_mask && fs->channel_mask < 3) {
|
||||
slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
|
||||
}
|
||||
slot_cfg.slot_mask = slot_mask;
|
||||
if (slot_bits > fs->bits_per_sample) {
|
||||
slot_cfg.data_bit_width = fs->bits_per_sample;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_rx_clock(channel, &clk_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_rx_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
#else
|
||||
ESP_LOGE(TAG, "PDM RX not supported");
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
#endif
|
||||
} else {
|
||||
#if SOC_I2S_SUPPORTS_PDM_TX
|
||||
i2s_pdm_tx_clk_config_t clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
i2s_pdm_tx_slot_config_t slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(slot_bits, I2S_SLOT_MODE_STEREO);
|
||||
// Stereo channel mask is ignored, need use mono instead
|
||||
if (fs->channel_mask && fs->channel_mask < 3) {
|
||||
slot_cfg.slot_mode = I2S_SLOT_MODE_MONO;
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
i2s_pdm_slot_mask_t slot_mask = fs->channel_mask ?
|
||||
(i2s_pdm_slot_mask_t) fs->channel_mask : I2S_PDM_SLOT_BOTH;
|
||||
slot_cfg.slot_mask = slot_mask;
|
||||
#endif
|
||||
if (slot_bits > fs->bits_per_sample) {
|
||||
slot_cfg.data_bit_width = fs->bits_per_sample;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_tx_clock(channel, &clk_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_pdm_tx_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
#else
|
||||
ESP_LOGE(TAG, "PDM TX not supported");
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
case I2S_COMM_MODE_TDM: {
|
||||
i2s_tdm_clk_config_t clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(fs->sample_rate);
|
||||
if (slot_bits == 24) {
|
||||
clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
|
||||
}
|
||||
i2s_tdm_slot_config_t slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
|
||||
slot_bits,
|
||||
I2S_SLOT_MODE_STEREO,
|
||||
(i2s_tdm_slot_mask_t)fs->channel_mask);
|
||||
slot_cfg.total_slot = fs->channel;
|
||||
if (slot_bits > fs->bits_per_sample) {
|
||||
slot_cfg.data_bit_width = fs->bits_per_sample;
|
||||
slot_cfg.slot_bit_width = slot_bits;
|
||||
}
|
||||
ret = i2s_channel_reconfig_tdm_slot(channel, &slot_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ret = i2s_channel_reconfig_tdm_clock(channel, &clk_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
ESP_LOGI(TAG, "TDM Mode %d bits:%d/%d channel:%d sample_rate:%d mask:%x",
|
||||
playback, fs->bits_per_sample, slot_bits, fs->channel,
|
||||
(int)fs->sample_rate, fs->channel_mask);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_fs(i2s_data_t *i2s_data, bool playback, bool skip)
|
||||
{
|
||||
i2s_chan_handle_t channel = (i2s_chan_handle_t) playback ? i2s_data->out_handle : i2s_data->in_handle;
|
||||
i2s_chan_info_t channel_info = {0};
|
||||
esp_codec_dev_sample_info_t *fs = playback ? &i2s_data->out_fs : &i2s_data->in_fs;
|
||||
uint8_t bits_per_sample = get_bits(i2s_data, playback);
|
||||
i2s_channel_get_info(channel, &channel_info);
|
||||
int ret = set_drv_fs(channel, playback, bits_per_sample, fs);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
return ret;
|
||||
}
|
||||
// Set RX clock will not take effect if in full duplex mode, need update TX clock also
|
||||
if (skip == false && playback == false && i2s_data->out_handle != NULL && i2s_data->out_enable == false) {
|
||||
// TX is master, set to RX not take effect need reconfig TX also
|
||||
channel = (i2s_chan_handle_t) i2s_data->out_handle;
|
||||
_i2s_drv_enable(i2s_data, true, false);
|
||||
ret = set_drv_fs(channel, true, bits_per_sample, fs);
|
||||
_i2s_drv_enable(i2s_data, true, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_fs_compatible(i2s_data_t *i2s_data, bool playback, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
// Set fs directly when only enable one channel
|
||||
esp_codec_dev_sample_info_t *channel_fs = playback ? &i2s_data->out_fs : &i2s_data->in_fs;
|
||||
if ((playback && i2s_data->in_enable == false) ||
|
||||
(!playback && i2s_data->out_enable == false)) {
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
memcpy(channel_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
return set_fs(i2s_data, playback, false);
|
||||
}
|
||||
if (fs->sample_rate != i2s_data->fs.sample_rate) {
|
||||
ESP_LOGE(TAG, "Mode %d conflict sample_rate %d with %d",
|
||||
playback, (int)fs->sample_rate, (int)i2s_data->fs.sample_rate);
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
// Channel and bits same, set directly
|
||||
if (fs->channel == i2s_data->fs.channel &&
|
||||
fs->bits_per_sample == i2s_data->fs.bits_per_sample) {
|
||||
memcpy(channel_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
return set_fs(i2s_data, playback, false);
|
||||
}
|
||||
memcpy(channel_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
uint16_t want_bits = fs->channel * fs->bits_per_sample;
|
||||
uint16_t run_bits = i2s_data->fs.channel * i2s_data->fs.bits_per_sample;
|
||||
int ret;
|
||||
// Need expand peer channel bits
|
||||
ESP_LOGI(TAG, "Mode %d need extend bits %d to %d", !playback, run_bits, want_bits);
|
||||
do {
|
||||
if (want_bits > run_bits) {
|
||||
if (playback == false) {
|
||||
i2s_data->out_reconfig = true;
|
||||
} else {
|
||||
i2s_data->in_reconfig = true;
|
||||
}
|
||||
ret = _i2s_drv_enable(i2s_data, !playback, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
}
|
||||
// Need set fs before enable
|
||||
ret = set_fs(i2s_data, playback, false);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
if (want_bits > run_bits) {
|
||||
ret = set_fs(i2s_data, !playback, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
ret = _i2s_drv_enable(i2s_data, !playback, true);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
i2s_data->out_reconfig = i2s_data->in_reconfig = false;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _i2s_data_open(const audio_codec_data_if_t *h, void *data_cfg, int cfg_size)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (h == NULL || data_cfg == NULL || cfg_size != sizeof(audio_codec_i2s_cfg_t)) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
audio_codec_i2s_cfg_t *i2s_cfg = (audio_codec_i2s_cfg_t *) data_cfg;
|
||||
i2s_data->is_open = true;
|
||||
i2s_data->port = i2s_cfg->port;
|
||||
i2s_data->out_handle = i2s_cfg->tx_handle;
|
||||
i2s_data->in_handle = i2s_cfg->rx_handle;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static bool _i2s_data_is_open(const audio_codec_data_if_t *h)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data) {
|
||||
return i2s_data->is_open;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _i2s_data_enable(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_type, bool enable)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
int ret = ESP_CODEC_DEV_OK;
|
||||
if (dev_type == ESP_CODEC_DEV_TYPE_IN_OUT) {
|
||||
ret = _i2s_drv_enable(i2s_data, true, enable);
|
||||
ret = _i2s_drv_enable(i2s_data, false, enable);
|
||||
} else {
|
||||
bool playback = dev_type & ESP_CODEC_DEV_TYPE_OUT ? true : false;
|
||||
// When RX is working TX disable should be blocked
|
||||
if (enable == false && i2s_data->in_enable && playback && i2s_data->out_handle) {
|
||||
ESP_LOGI(TAG, "Pending out channel for in channel running");
|
||||
i2s_data->out_disable_pending = true;
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_1
|
||||
// For ESP32 and ESP32S3 if disable RX, TX also not work need pending until TX not used
|
||||
else if (enable == false && i2s_data->out_enable && playback == false && i2s_data->in_handle) {
|
||||
ESP_LOGI(TAG, "Pending in channel for out channel running");
|
||||
i2s_data->in_disable_pending = true;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
ret = _i2s_drv_enable(i2s_data, playback, enable);
|
||||
// Disable TX when RX disable if TX disable is pending
|
||||
if (enable == false) {
|
||||
if (playback == false && i2s_data->out_disable_pending) {
|
||||
ret = _i2s_drv_enable(i2s_data, true, enable);
|
||||
i2s_data->out_disable_pending = false;
|
||||
}
|
||||
if (playback == true && i2s_data->in_disable_pending) {
|
||||
ret = _i2s_drv_enable(i2s_data, false, enable);
|
||||
i2s_data->in_disable_pending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_IN) {
|
||||
i2s_data->in_enable = enable;
|
||||
}
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_OUT) {
|
||||
i2s_data->out_enable = enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _i2s_data_set_fmt(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_type, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
esp_codec_dev_sample_info_t eq_fs;
|
||||
if (fs->channel == 1) {
|
||||
// When using one channel replace to select channel 0 in default
|
||||
memcpy(&eq_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
fs = &eq_fs;
|
||||
fs->channel = 2;
|
||||
fs->channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0);
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
if (fs->channel_mask == 0) {
|
||||
// Add channel mask automatically when not set
|
||||
memcpy(&eq_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
fs = &eq_fs;
|
||||
for (int i = 0; i < fs->channel; i++) {
|
||||
fs->channel_mask |= ESP_CODEC_DEV_MAKE_CHANNEL_MASK(i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_i2s_valid_fmt(fs) == false) {
|
||||
return ESP_CODEC_DEV_NOT_SUPPORT;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// disable internally
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_OUT) {
|
||||
_i2s_drv_enable(i2s_data, true, false);
|
||||
}
|
||||
if (dev_type & ESP_CODEC_DEV_TYPE_IN) {
|
||||
_i2s_drv_enable(i2s_data, false, false);
|
||||
}
|
||||
int ret;
|
||||
if ((dev_type & ESP_CODEC_DEV_TYPE_IN) != 0 &&
|
||||
(dev_type & ESP_CODEC_DEV_TYPE_OUT) != 0) {
|
||||
// Device support playback and record at same time
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
memcpy(&i2s_data->in_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
memcpy(&i2s_data->out_fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
ret = set_fs(i2s_data, true, true);
|
||||
ret = set_fs(i2s_data, false, true);
|
||||
} else {
|
||||
ret = check_fs_compatible(i2s_data, dev_type & ESP_CODEC_DEV_TYPE_OUT ? true : false, fs);
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
// When use multichannel data
|
||||
if (fs->channel_mask) {
|
||||
i2s_channel_t sel_channel = 0;
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
sel_channel = (i2s_channel_t)(fs->channel_mask << 16);
|
||||
#else
|
||||
if (fs->channel_mask == ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0)) {
|
||||
sel_channel = 1;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "IC not support TDM");
|
||||
return ESP_CODEC_DEV_NOT_FOUND;
|
||||
}
|
||||
#endif
|
||||
i2s_set_clk(i2s_data->port, fs->sample_rate, fs->bits_per_sample, sel_channel);
|
||||
} else {
|
||||
i2s_set_clk(i2s_data->port, fs->sample_rate, fs->bits_per_sample, fs->channel);
|
||||
}
|
||||
i2s_zero_dma_buffer(i2s_data->port);
|
||||
memcpy(&i2s_data->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
#endif
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
static int _i2s_data_read(const audio_codec_data_if_t *h, uint8_t *data, int size)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
size_t bytes_read = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t rx_chan = (i2s_chan_handle_t) i2s_data->in_handle;
|
||||
if (rx_chan == NULL) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
if (i2s_data->in_reconfig) {
|
||||
memset(data, 0, size);
|
||||
esp_codec_dev_sleep(10);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = i2s_channel_read(rx_chan, data, size, &bytes_read, 1000);
|
||||
#else
|
||||
int ret = i2s_read(i2s_data->port, data, size, &bytes_read, portMAX_DELAY);
|
||||
#endif
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static int _i2s_data_write(const audio_codec_data_if_t *h, uint8_t *data, int size)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
if (i2s_data->is_open == false) {
|
||||
return ESP_CODEC_DEV_WRONG_STATE;
|
||||
}
|
||||
size_t bytes_written = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t tx_chan = (i2s_chan_handle_t) i2s_data->out_handle;
|
||||
if (tx_chan == NULL) {
|
||||
return ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
if (i2s_data->out_reconfig) {
|
||||
esp_codec_dev_sleep(10);
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
int ret = i2s_channel_write(tx_chan, data, size, &bytes_written, 1000);
|
||||
#else
|
||||
int ret = i2s_write(i2s_data->port, data, size, &bytes_written, portMAX_DELAY);
|
||||
#endif
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static int _i2s_data_close(const audio_codec_data_if_t *h)
|
||||
{
|
||||
i2s_data_t *i2s_data = (i2s_data_t *) h;
|
||||
if (i2s_data == NULL) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
memset(&i2s_data->fs, 0, sizeof(esp_codec_dev_sample_info_t));
|
||||
memset(&i2s_data->in_fs, 0, sizeof(esp_codec_dev_sample_info_t));
|
||||
memset(&i2s_data->out_fs, 0, sizeof(esp_codec_dev_sample_info_t));
|
||||
i2s_data->is_open = false;
|
||||
return ESP_CODEC_DEV_OK;
|
||||
}
|
||||
|
||||
const audio_codec_data_if_t *audio_codec_new_i2s_data(audio_codec_i2s_cfg_t *i2s_cfg)
|
||||
{
|
||||
i2s_data_t *i2s_data = calloc(1, sizeof(i2s_data_t));
|
||||
if (i2s_data == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
i2s_data->base.open = _i2s_data_open;
|
||||
i2s_data->base.is_open = _i2s_data_is_open;
|
||||
i2s_data->base.enable = _i2s_data_enable;
|
||||
i2s_data->base.read = _i2s_data_read;
|
||||
i2s_data->base.write = _i2s_data_write;
|
||||
i2s_data->base.set_fmt = _i2s_data_set_fmt;
|
||||
i2s_data->base.close = _i2s_data_close;
|
||||
int ret = _i2s_data_open(&i2s_data->base, i2s_cfg, sizeof(audio_codec_i2s_cfg_t));
|
||||
if (ret != 0) {
|
||||
free(i2s_data);
|
||||
return NULL;
|
||||
}
|
||||
return &i2s_data->base;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "audio_codec_gpio_if.h"
|
||||
#include "esp_codec_dev_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "GPIO_If"
|
||||
|
||||
static int _gpio_cfg(int16_t gpio, audio_gpio_dir_t dir, audio_gpio_mode_t mode)
|
||||
{
|
||||
if (gpio == -1) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
gpio_config_t io_conf;
|
||||
memset(&io_conf, 0, sizeof(io_conf));
|
||||
io_conf.mode = (dir == AUDIO_GPIO_DIR_OUT ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT);
|
||||
io_conf.pin_bit_mask = BIT64(gpio);
|
||||
io_conf.pull_down_en = ((mode & AUDIO_GPIO_MODE_PULL_DOWN) != 0);
|
||||
io_conf.pull_up_en = ((mode & AUDIO_GPIO_MODE_PULL_UP) != 0);
|
||||
esp_err_t ret = 0;
|
||||
ret |= gpio_config(&io_conf);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
static int _gpio_set(int16_t gpio, bool high)
|
||||
{
|
||||
if (gpio == -1) {
|
||||
return ESP_CODEC_DEV_INVALID_ARG;
|
||||
}
|
||||
int ret = gpio_set_level((gpio_num_t) gpio, high ? 1 : 0);
|
||||
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_DRV_ERR;
|
||||
}
|
||||
|
||||
static bool _gpio_get(int16_t gpio)
|
||||
{
|
||||
if (gpio == -1) {
|
||||
return false;
|
||||
}
|
||||
return (bool) gpio_get_level((gpio_num_t) gpio);
|
||||
}
|
||||
|
||||
const audio_codec_gpio_if_t *audio_codec_new_gpio(void)
|
||||
{
|
||||
audio_codec_gpio_if_t *gpio_if = (audio_codec_gpio_if_t *) calloc(1, sizeof(audio_codec_gpio_if_t));
|
||||
if (gpio_if == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for instance");
|
||||
return NULL;
|
||||
}
|
||||
gpio_if->setup = _gpio_cfg;
|
||||
gpio_if->set = _gpio_set;
|
||||
gpio_if->get = _gpio_get;
|
||||
return gpio_if;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define TICK_PER_MS portTICK_PERIOD_MS
|
||||
#else
|
||||
#define TICK_PER_MS portTICK_RATE_MS
|
||||
#endif
|
||||
|
||||
void esp_codec_dev_sleep(int ms)
|
||||
{
|
||||
vTaskDelay(ms / TICK_PER_MS);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(codec_dev_test)
|
||||
@@ -0,0 +1,10 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | ESP32-P4 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# esp_codec_dev: Codec device test application
|
||||
|
||||
There are two sets of tests in this application:
|
||||
1. Using customized codec to test all related API
|
||||
2. Test codec device on special board
|
||||
|
||||
The default test board is ESP32S3_KORVO2_V3, if you are using other board, please change definition in [test_boards.h](main/test_board.h) and realization code is [test_board.c](main/test_board.c).
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
set(priv_requires
|
||||
unity
|
||||
driver
|
||||
esp_codec_dev
|
||||
)
|
||||
|
||||
idf_component_register(SRC_DIRS .
|
||||
PRIV_INCLUDE_DIRS .
|
||||
PRIV_REQUIRES unity esp_codec_dev
|
||||
PRIV_REQUIRES ${priv_requires}
|
||||
WHOLE_ARCHIVE TRUE
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
description: Codec Device Test
|
||||
|
||||
dependencies:
|
||||
esp_codec_dev:
|
||||
version: ">=1.1"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "my_codec.h"
|
||||
|
||||
/*
|
||||
* Codec reset GPIO
|
||||
*/
|
||||
#define MY_CODEC_RESET_PIN (9)
|
||||
/*
|
||||
* PA Control GPIO
|
||||
*/
|
||||
#define MY_CODEC_PA_CTRL_PIN (10)
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
const audio_codec_ctrl_if_t *ctrl_if;
|
||||
esp_codec_dev_sample_info_t fs;
|
||||
float hw_gain;
|
||||
bool is_open;
|
||||
bool muted;
|
||||
bool enable;
|
||||
} my_codec_t;
|
||||
|
||||
static const esp_codec_dev_vol_range_t vol_range = {
|
||||
.min_vol =
|
||||
{
|
||||
.vol = 100, /*!< Minimum volume mapped register */
|
||||
.db_value = -100.0,
|
||||
},
|
||||
.max_vol =
|
||||
{
|
||||
.vol = 0, /*!< Maximum volume mapped register */
|
||||
.db_value = 0.0,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Customization for codec data interface
|
||||
*/
|
||||
static int my_codec_data_open(const audio_codec_data_if_t *h, void *data_cfg, int cfg_size)
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) h;
|
||||
data_if->is_open = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool my_codec_data_is_open(const audio_codec_data_if_t *h)
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) h;
|
||||
return data_if->is_open;
|
||||
}
|
||||
|
||||
static int my_codec_data_set_fmt(const audio_codec_data_if_t *h, esp_codec_dev_type_t dev_caps,
|
||||
esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) h;
|
||||
memcpy(&data_if->fmt, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_data_read(const audio_codec_data_if_t *h, uint8_t *data, int size)
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) h;
|
||||
for (int i = 0; i < size; i++) {
|
||||
data[i] = (int) (data_if->read_idx + i);
|
||||
}
|
||||
data_if->read_idx += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_data_write(const audio_codec_data_if_t *h, uint8_t *data, int size)
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) h;
|
||||
data_if->write_idx += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_data_close(const audio_codec_data_if_t *h)
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) h;
|
||||
data_if->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_data_if_t *my_codec_data_new()
|
||||
{
|
||||
my_codec_data_t *data_if = (my_codec_data_t *) calloc(1, sizeof(my_codec_data_t));
|
||||
if (data_if == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
data_if->base.open = my_codec_data_open;
|
||||
data_if->base.is_open = my_codec_data_is_open;
|
||||
data_if->base.set_fmt = my_codec_data_set_fmt;
|
||||
data_if->base.read = my_codec_data_read;
|
||||
data_if->base.write = my_codec_data_write;
|
||||
data_if->base.close = my_codec_data_close;
|
||||
data_if->base.open(&data_if->base, NULL, 0);
|
||||
return &data_if->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Customization for codec control interface
|
||||
*/
|
||||
static int my_codec_ctrl_open(const audio_codec_ctrl_if_t *ctrl, void *cfg, int cfg_size)
|
||||
{
|
||||
my_codec_ctrl_t *ctrl_if = (my_codec_ctrl_t *) ctrl;
|
||||
ctrl_if->is_open = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool my_codec_ctrl_is_open(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
my_codec_ctrl_t *ctrl_if = (my_codec_ctrl_t *) ctrl;
|
||||
return ctrl_if->is_open;
|
||||
}
|
||||
|
||||
static int my_codec_ctrl_read_addr(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
my_codec_ctrl_t *ctrl_if = (my_codec_ctrl_t *) ctrl;
|
||||
if (data_len == 1 && addr < MY_CODEC_REG_MAX) {
|
||||
*(uint8_t *) data = ctrl_if->reg[addr];
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int my_codec_ctrl_write_addr(const audio_codec_ctrl_if_t *ctrl, int addr, int addr_len, void *data, int data_len)
|
||||
{
|
||||
my_codec_ctrl_t *ctrl_if = (my_codec_ctrl_t *) ctrl;
|
||||
if (data_len == 1 && addr < MY_CODEC_REG_MAX) {
|
||||
ctrl_if->reg[addr] = *(uint8_t *) data;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int my_codec_ctrl_close(const audio_codec_ctrl_if_t *ctrl)
|
||||
{
|
||||
my_codec_ctrl_t *ctrl_if = (my_codec_ctrl_t *) ctrl;
|
||||
ctrl_if->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_ctrl_if_t *my_codec_ctrl_new()
|
||||
{
|
||||
my_codec_ctrl_t *ctrl_if = (my_codec_ctrl_t *) calloc(1, sizeof(my_codec_ctrl_t));
|
||||
if (ctrl_if == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ctrl_if->base.open = my_codec_ctrl_open;
|
||||
ctrl_if->base.is_open = my_codec_ctrl_is_open;
|
||||
ctrl_if->base.read_reg = my_codec_ctrl_read_addr;
|
||||
ctrl_if->base.write_reg = my_codec_ctrl_write_addr;
|
||||
ctrl_if->base.close = my_codec_ctrl_close;
|
||||
ctrl_if->base.open(&ctrl_if->base, NULL, 0);
|
||||
return &ctrl_if->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Customization for codec interface
|
||||
*/
|
||||
static int my_codec_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
my_codec_cfg_t *codec_cfg = (my_codec_cfg_t *) cfg;
|
||||
if (cfg_size != sizeof(my_codec_cfg_t) || codec_cfg->ctrl_if == NULL || codec_cfg->gpio_if == NULL) {
|
||||
return -1;
|
||||
}
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
codec->ctrl_if = codec_cfg->ctrl_if;
|
||||
codec->gpio_if = codec_cfg->gpio_if;
|
||||
// Reset codec chip on boot up, suppose low to reset
|
||||
codec->gpio_if->set(MY_CODEC_RESET_PIN, false);
|
||||
// Reset sequence
|
||||
esp_codec_dev_sleep(10);
|
||||
codec->gpio_if->set(MY_CODEC_RESET_PIN, true);
|
||||
esp_codec_dev_sleep(10);
|
||||
// Set initial register
|
||||
uint8_t reg = 0;
|
||||
codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_VOL, 1, ®, 1);
|
||||
reg = 0;
|
||||
codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MUTE, 1, ®, 1);
|
||||
codec->is_open = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool my_codec_is_open(const audio_codec_if_t *h)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
return codec->is_open;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable can be used to control codec suspend/resume behavior to save power
|
||||
* Need care special sequence to avoid pop sound:
|
||||
* 1: PA chip need power on after codec reset done
|
||||
* 2: PA chip need power off before codec suspend
|
||||
* If codec support mute behavior, can mute before do operation
|
||||
*/
|
||||
static int my_codec_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
codec->enable = enable;
|
||||
uint8_t suspend = !enable;
|
||||
int ret;
|
||||
if (enable) {
|
||||
// Wakeup firstly
|
||||
codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_SUSPEND, 1, &suspend, 1);
|
||||
// Maybe some wait here, power on PA chip
|
||||
codec->gpio_if->set(MY_CODEC_PA_CTRL_PIN, true);
|
||||
// Restore mute settings
|
||||
ret = codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MUTE, 1, &codec->muted, 1);
|
||||
} else {
|
||||
uint8_t mute = 1;
|
||||
// Do mute firstly
|
||||
codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MUTE, 1, &mute, 1);
|
||||
// Maybe some wait here, power off PA chip
|
||||
codec->gpio_if->set(MY_CODEC_PA_CTRL_PIN, false);
|
||||
// Suspend codec chip
|
||||
ret = codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_SUSPEND, 1, &suspend, 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int my_codec_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
memcpy(&codec->fs, fs, sizeof(esp_codec_dev_sample_info_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_mute(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
uint8_t data = (uint8_t) mute;
|
||||
codec->muted = mute;
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MUTE, 1, &data, 1);
|
||||
}
|
||||
|
||||
static int my_codec_set_vol(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
// Need minus hw_gain to avoid hardware saturation
|
||||
db -= codec->hw_gain;
|
||||
if (db > 0) {
|
||||
db = 0;
|
||||
}
|
||||
uint8_t data = esp_codec_dev_vol_calc_reg(&vol_range, db);
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_VOL, 1, &data, 1);
|
||||
}
|
||||
|
||||
static int my_codec_set_mic_gain(const audio_codec_if_t *h, float db)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
uint8_t data = (uint8_t) (int) db;
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MIC_GAIN, 1, &data, 1);
|
||||
}
|
||||
|
||||
static int my_codec_mute_mic(const audio_codec_if_t *h, bool mute)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
uint8_t data = (uint8_t) (int) mute;
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MIC_MUTE, 1, &data, 1);
|
||||
}
|
||||
|
||||
static int my_codec_set_reg(const audio_codec_if_t *h, int reg, int value)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
if (reg < MY_CODEC_REG_MAX) {
|
||||
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int my_codec_get_reg(const audio_codec_if_t *h, int reg, int *value)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
if (reg < MY_CODEC_REG_MAX) {
|
||||
*value = 0;
|
||||
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int my_codec_close(const audio_codec_if_t *h)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) h;
|
||||
// Auto disable when codec closed
|
||||
if (codec->enable) {
|
||||
my_codec_enable(h, false);
|
||||
}
|
||||
codec->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_if_t *my_codec_new(my_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
my_codec_t *codec = (my_codec_t *) calloc(1, sizeof(my_codec_t));
|
||||
if (codec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = my_codec_open;
|
||||
codec->base.is_open = my_codec_is_open;
|
||||
codec->base.enable = my_codec_enable;
|
||||
codec->base.set_fs = my_codec_set_fs;
|
||||
codec->base.mute = my_codec_mute;
|
||||
codec->base.set_vol = my_codec_set_vol;
|
||||
codec->base.set_mic_gain = my_codec_set_mic_gain;
|
||||
codec->base.mute_mic = my_codec_mute_mic;
|
||||
codec->base.set_reg = my_codec_set_reg;
|
||||
codec->base.get_reg = my_codec_get_reg;
|
||||
codec->base.close = my_codec_close;
|
||||
// Calculate hardware gain from configuration
|
||||
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
|
||||
codec->base.open(&codec->base, codec_cfg, sizeof(my_codec_cfg_t));
|
||||
return &codec->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Customization for volume interface
|
||||
*/
|
||||
static int my_codec_vol_open(const audio_codec_vol_if_t *h, esp_codec_dev_sample_info_t *fs, int fade_time)
|
||||
{
|
||||
my_codec_vol_t *vol = (my_codec_vol_t *) h;
|
||||
if (vol == NULL) {
|
||||
return -1;
|
||||
}
|
||||
vol->fs = *fs;
|
||||
vol->is_open = true;
|
||||
vol->process_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_vol_set(const audio_codec_vol_if_t *h, float db_value)
|
||||
{
|
||||
my_codec_vol_t *vol = (my_codec_vol_t *) h;
|
||||
if (vol == NULL) {
|
||||
return -1;
|
||||
}
|
||||
vol->shift = (int) (db_value / 6.020599913279624);
|
||||
vol->vol_db = db_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_vol_process(const audio_codec_vol_if_t *h, uint8_t *in, int len, uint8_t *out, int out_len)
|
||||
{
|
||||
my_codec_vol_t *vol = (my_codec_vol_t *) h;
|
||||
if (vol == NULL || vol->is_open == false) {
|
||||
return -1;
|
||||
}
|
||||
if (vol->fs.bits_per_sample == 16) {
|
||||
int16_t *sample_in = (int16_t *) in;
|
||||
int16_t *sample_out = (int16_t *) out;
|
||||
int sample = len >> 1;
|
||||
if (vol->shift > 0) {
|
||||
while (sample-- > 0) {
|
||||
*(sample_out++) = (int16_t) (*(sample_in++) << vol->shift);
|
||||
}
|
||||
} else if (vol->shift < 0) {
|
||||
int shift = -vol->shift;
|
||||
while (sample-- > 0) {
|
||||
*(sample_out++) = (int16_t) (*(sample_in++) >> shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
vol->process_len += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_codec_vol_close(const audio_codec_vol_if_t *h)
|
||||
{
|
||||
my_codec_vol_t *vol = (my_codec_vol_t *) h;
|
||||
if (vol == NULL) {
|
||||
return -1;
|
||||
}
|
||||
vol->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_vol_if_t *my_codec_vol_new()
|
||||
{
|
||||
my_codec_vol_t *vol = (my_codec_vol_t *) calloc(1, sizeof(my_codec_vol_t));
|
||||
if (vol == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vol->base.open = my_codec_vol_open;
|
||||
vol->base.set_vol = my_codec_vol_set;
|
||||
vol->base.process = my_codec_vol_process;
|
||||
vol->base.close = my_codec_vol_close;
|
||||
return &vol->base;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _MY_CODEC_H_
|
||||
#define _MY_CODEC_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_codec_dev.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Customized codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec control interface
|
||||
If use i2c can use `audio_codec_new_i2c_ctrl` directly */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< GPIO interface to control gpio */
|
||||
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain, see `esp_codec_dev_vol.h` for details */
|
||||
} my_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Customized codec control instance
|
||||
*/
|
||||
typedef enum {
|
||||
MY_CODEC_REG_VOL, /*!< Register to control volume */
|
||||
MY_CODEC_REG_MUTE, /*!< Register to mute microphone */
|
||||
MY_CODEC_REG_MIC_GAIN, /*!< Register for microphone gain */
|
||||
MY_CODEC_REG_MIC_MUTE, /*!< Register to mute microphone */
|
||||
MY_CODEC_REG_SUSPEND, /*!< Register to suspend codec chip */
|
||||
MY_CODEC_REG_MAX,
|
||||
} my_codec_reg_type_t;
|
||||
|
||||
typedef struct {
|
||||
audio_codec_ctrl_if_t base;
|
||||
uint8_t reg[MY_CODEC_REG_MAX];
|
||||
bool is_open;
|
||||
} my_codec_ctrl_t;
|
||||
|
||||
/**
|
||||
* @brief Customized codec data instance
|
||||
*/
|
||||
typedef struct {
|
||||
audio_codec_data_if_t base;
|
||||
esp_codec_dev_sample_info_t fmt;
|
||||
int read_idx;
|
||||
int write_idx;
|
||||
bool is_open;
|
||||
} my_codec_data_t;
|
||||
|
||||
/**
|
||||
* @brief Customized codec volume instance
|
||||
*/
|
||||
typedef struct {
|
||||
audio_codec_vol_if_t base;
|
||||
esp_codec_dev_sample_info_t fs;
|
||||
int shift;
|
||||
int process_len;
|
||||
float vol_db;
|
||||
bool is_open;
|
||||
} my_codec_vol_t;
|
||||
|
||||
/**
|
||||
* @brief Customized codec control interface
|
||||
*/
|
||||
const audio_codec_ctrl_if_t *my_codec_ctrl_new();
|
||||
|
||||
/**
|
||||
* @brief Customized codec data interface
|
||||
* If use i2s for data path can use `audio_codec_new_i2s_data` directly
|
||||
*/
|
||||
const audio_codec_data_if_t *my_codec_data_new();
|
||||
|
||||
/**
|
||||
* @brief Customized codec realization
|
||||
*/
|
||||
const audio_codec_if_t *my_codec_new(my_codec_cfg_t *codec_cfg);
|
||||
|
||||
/**
|
||||
* @brief Customized volume algorithm
|
||||
*/
|
||||
const audio_codec_vol_if_t *my_codec_vol_new();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (400)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#else
|
||||
#include "driver/i2s.h"
|
||||
#endif
|
||||
#include "esp_codec_dev.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "test_board.h"
|
||||
#include "unity.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#include "driver/i2c_master.h"
|
||||
#define USE_IDF_I2C_MASTER
|
||||
#else
|
||||
#include "driver/i2c.h"
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
|
||||
#define I2S_MAX_KEEP SOC_I2S_NUM
|
||||
|
||||
typedef struct {
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
} i2s_keep_t;
|
||||
|
||||
static i2s_comm_mode_t i2s_in_mode = I2S_COMM_MODE_STD;
|
||||
static i2s_comm_mode_t i2s_out_mode = I2S_COMM_MODE_STD;
|
||||
static i2s_keep_t *i2s_keep[I2S_MAX_KEEP];
|
||||
#endif
|
||||
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
static i2c_master_bus_handle_t i2c_bus_handle;
|
||||
#endif
|
||||
|
||||
static int ut_i2c_init(uint8_t port)
|
||||
{
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
i2c_master_bus_config_t i2c_bus_config = {0};
|
||||
i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||||
i2c_bus_config.i2c_port = port;
|
||||
i2c_bus_config.scl_io_num = TEST_BOARD_I2C_SCL_PIN;
|
||||
i2c_bus_config.sda_io_num = TEST_BOARD_I2C_SDA_PIN;
|
||||
i2c_bus_config.glitch_ignore_cnt = 7;
|
||||
i2c_bus_config.flags.enable_internal_pullup = true;
|
||||
return i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||||
#else
|
||||
i2c_config_t i2c_cfg = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = 100000,
|
||||
};
|
||||
i2c_cfg.sda_io_num = TEST_BOARD_I2C_SDA_PIN;
|
||||
i2c_cfg.scl_io_num = TEST_BOARD_I2C_SCL_PIN;
|
||||
esp_err_t ret = i2c_param_config(port, &i2c_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return -1;
|
||||
}
|
||||
return i2c_driver_install(port, i2c_cfg.mode, 0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ut_i2c_deinit(uint8_t port)
|
||||
{
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
if (i2c_bus_handle) {
|
||||
i2c_del_master_bus(i2c_bus_handle);
|
||||
}
|
||||
i2c_bus_handle = NULL;
|
||||
return 0;
|
||||
#else
|
||||
return i2c_driver_delete(port);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
static void ut_set_i2s_mode(i2s_comm_mode_t out_mode, i2s_comm_mode_t in_mode)
|
||||
{
|
||||
i2s_in_mode = in_mode;
|
||||
i2s_out_mode = out_mode;
|
||||
}
|
||||
|
||||
static void ut_clr_i2s_mode(void)
|
||||
{
|
||||
i2s_in_mode = I2S_COMM_MODE_STD;
|
||||
i2s_out_mode = I2S_COMM_MODE_STD;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ut_i2s_init(uint8_t port)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
if (port >= I2S_MAX_KEEP) {
|
||||
return -1;
|
||||
}
|
||||
// Already installed
|
||||
if (i2s_keep[port]) {
|
||||
return 0;
|
||||
}
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg ={
|
||||
.mclk = TEST_BOARD_I2S_MCK_PIN,
|
||||
.bclk = TEST_BOARD_I2S_BCK_PIN,
|
||||
.ws = TEST_BOARD_I2S_DATA_WS_PIN,
|
||||
.dout = TEST_BOARD_I2S_DATA_OUT_PIN,
|
||||
.din = TEST_BOARD_I2S_DATA_IN_PIN,
|
||||
},
|
||||
};
|
||||
i2s_keep[port] = (i2s_keep_t *) calloc(1, sizeof(i2s_keep_t));
|
||||
if (i2s_keep[port] == NULL) {
|
||||
return -1;
|
||||
}
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
i2s_tdm_slot_mask_t slot_mask = I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3;
|
||||
i2s_tdm_config_t tdm_cfg = {
|
||||
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO, slot_mask),
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
|
||||
.gpio_cfg = {
|
||||
.mclk = TEST_BOARD_I2S_MCK_PIN,
|
||||
.bclk = TEST_BOARD_I2S_BCK_PIN,
|
||||
.ws = TEST_BOARD_I2S_DATA_WS_PIN,
|
||||
.dout = TEST_BOARD_I2S_DATA_OUT_PIN,
|
||||
.din = TEST_BOARD_I2S_DATA_IN_PIN,
|
||||
},
|
||||
};
|
||||
tdm_cfg.slot_cfg.total_slot = 4;
|
||||
#endif
|
||||
int ret = i2s_new_channel(&chan_cfg, &i2s_keep[port]->tx_handle, &i2s_keep[port]->rx_handle);
|
||||
TEST_ESP_OK(ret);
|
||||
if (i2s_out_mode == I2S_COMM_MODE_STD) {
|
||||
ret = i2s_channel_init_std_mode(i2s_keep[port]->tx_handle, &std_cfg);
|
||||
}
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
else if (i2s_out_mode == I2S_COMM_MODE_TDM) {
|
||||
ret = i2s_channel_init_tdm_mode(i2s_keep[port]->tx_handle, &tdm_cfg);
|
||||
}
|
||||
#endif
|
||||
TEST_ESP_OK(ret);
|
||||
if (i2s_in_mode == I2S_COMM_MODE_STD) {
|
||||
ret = i2s_channel_init_std_mode(i2s_keep[port]->rx_handle, &std_cfg);
|
||||
}
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
else if (i2s_in_mode == I2S_COMM_MODE_TDM) {
|
||||
ret = i2s_channel_init_tdm_mode(i2s_keep[port]->rx_handle, &tdm_cfg);
|
||||
}
|
||||
#endif
|
||||
TEST_ESP_OK(ret);
|
||||
// For tx master using duplex mode
|
||||
i2s_channel_enable(i2s_keep[port]->tx_handle);
|
||||
#else
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t) (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER),
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM,
|
||||
.dma_buf_count = 2,
|
||||
.dma_buf_len = 128,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true,
|
||||
};
|
||||
int ret = i2s_driver_install(port, &i2s_config, 0, NULL);
|
||||
i2s_pin_config_t i2s_pin_cfg = {
|
||||
.mck_io_num = TEST_BOARD_I2S_MCK_PIN,
|
||||
.bck_io_num = TEST_BOARD_I2S_BCK_PIN,
|
||||
.ws_io_num = TEST_BOARD_I2S_DATA_WS_PIN,
|
||||
.data_out_num = TEST_BOARD_I2S_DATA_OUT_PIN,
|
||||
.data_in_num = TEST_BOARD_I2S_DATA_IN_PIN,
|
||||
};
|
||||
i2s_set_pin(port, &i2s_pin_cfg);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ut_i2s_deinit(uint8_t port)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
if (port >= I2S_MAX_KEEP) {
|
||||
return -1;
|
||||
}
|
||||
// already installed
|
||||
if (i2s_keep[port] == NULL) {
|
||||
return 0;
|
||||
}
|
||||
i2s_channel_disable(i2s_keep[port]->tx_handle);
|
||||
i2s_channel_disable(i2s_keep[port]->rx_handle);
|
||||
i2s_del_channel(i2s_keep[port]->tx_handle);
|
||||
i2s_del_channel(i2s_keep[port]->rx_handle);
|
||||
free(i2s_keep[port]);
|
||||
i2s_keep[port] = NULL;
|
||||
#else
|
||||
i2s_driver_uninstall(port);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void codec_max_sample(uint8_t *data, int size, int *max_value, int *min_value)
|
||||
{
|
||||
int16_t *s = (int16_t *) data;
|
||||
size >>= 1;
|
||||
int i = 1, max, min;
|
||||
max = min = s[0];
|
||||
while (i < size) {
|
||||
if (s[i] > max) {
|
||||
max = s[i];
|
||||
} else if (s[i] < min) {
|
||||
min = s[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
*max_value = max;
|
||||
*min_value = min;
|
||||
}
|
||||
|
||||
TEST_CASE("esp codec dev test using S3 board", "[esp_codec_dev]")
|
||||
{
|
||||
// Need install driver (i2c and i2s) firstly
|
||||
int ret = ut_i2c_init(0);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = ut_i2s_init(0);
|
||||
TEST_ESP_OK(ret);
|
||||
// Do initialize of related interface: data_if, ctrl_if and gpio_if
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.rx_handle = i2s_keep[0]->rx_handle,
|
||||
.tx_handle = i2s_keep[0]->tx_handle,
|
||||
#endif
|
||||
};
|
||||
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
TEST_ASSERT_NOT_NULL(data_if);
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {.addr = ES8311_CODEC_DEFAULT_ADDR};
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
i2c_cfg.bus_handle = i2c_bus_handle;
|
||||
#endif
|
||||
const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
TEST_ASSERT_NOT_NULL(out_ctrl_if);
|
||||
|
||||
i2c_cfg.addr = ES7210_CODEC_DEFAULT_ADDR;
|
||||
const audio_codec_ctrl_if_t *in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
TEST_ASSERT_NOT_NULL(in_ctrl_if);
|
||||
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
TEST_ASSERT_NOT_NULL(gpio_if);
|
||||
// New output codec interface
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC,
|
||||
.ctrl_if = out_ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.pa_pin = TEST_BOARD_PA_PIN,
|
||||
.use_mclk = true,
|
||||
};
|
||||
const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg);
|
||||
TEST_ASSERT_NOT_NULL(out_codec_if);
|
||||
// New input codec interface
|
||||
es7210_codec_cfg_t es7210_cfg = {
|
||||
.ctrl_if = in_ctrl_if,
|
||||
.mic_selected = ES7120_SEL_MIC1 | ES7120_SEL_MIC2 | ES7120_SEL_MIC3,
|
||||
};
|
||||
const audio_codec_if_t *in_codec_if = es7210_codec_new(&es7210_cfg);
|
||||
TEST_ASSERT_NOT_NULL(in_codec_if);
|
||||
// New output codec device
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.codec_if = out_codec_if,
|
||||
.data_if = data_if,
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
|
||||
};
|
||||
esp_codec_dev_handle_t play_dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(play_dev);
|
||||
// New input codec device
|
||||
dev_cfg.codec_if = in_codec_if;
|
||||
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN;
|
||||
esp_codec_dev_handle_t record_dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(record_dev);
|
||||
|
||||
ret = esp_codec_dev_set_out_vol(play_dev, 60.0);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_set_in_gain(record_dev, 30.0);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.sample_rate = 48000,
|
||||
.channel = 2,
|
||||
.bits_per_sample = 16,
|
||||
};
|
||||
ret = esp_codec_dev_open(play_dev, &fs);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
ret = esp_codec_dev_open(record_dev, &fs);
|
||||
TEST_ESP_OK(ret);
|
||||
uint8_t *data = (uint8_t *) malloc(512);
|
||||
int limit_size = 10 * fs.sample_rate * fs.channel * (fs.bits_per_sample >> 3);
|
||||
int got_size = 0;
|
||||
// Playback the recording content directly
|
||||
while (got_size < limit_size) {
|
||||
ret = esp_codec_dev_read(record_dev, data, 512);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_write(play_dev, data, 512);
|
||||
TEST_ESP_OK(ret);
|
||||
int max_sample, min_sample;
|
||||
codec_max_sample(data, 512, &max_sample, &min_sample);
|
||||
// Verify recording data not constant
|
||||
TEST_ASSERT(max_sample > min_sample);
|
||||
got_size += 512;
|
||||
}
|
||||
free(data);
|
||||
|
||||
ret = esp_codec_dev_close(play_dev);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_close(record_dev);
|
||||
TEST_ESP_OK(ret);
|
||||
esp_codec_dev_delete(play_dev);
|
||||
esp_codec_dev_delete(record_dev);
|
||||
|
||||
// Delete codec interface
|
||||
audio_codec_delete_codec_if(in_codec_if);
|
||||
audio_codec_delete_codec_if(out_codec_if);
|
||||
// Delete codec control interface
|
||||
audio_codec_delete_ctrl_if(in_ctrl_if);
|
||||
audio_codec_delete_ctrl_if(out_ctrl_if);
|
||||
audio_codec_delete_gpio_if(gpio_if);
|
||||
// Delete codec data interface
|
||||
audio_codec_delete_data_if(data_if);
|
||||
|
||||
ut_i2c_deinit(0);
|
||||
ut_i2s_deinit(0);
|
||||
}
|
||||
|
||||
TEST_CASE("Playing while recording use TDM mode", "[esp_codec_dev]")
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if SOC_I2S_SUPPORTS_TDM
|
||||
ut_set_i2s_mode(I2S_COMM_MODE_STD, I2S_COMM_MODE_TDM);
|
||||
#else
|
||||
TEST_ESP_OK(-1);
|
||||
#endif
|
||||
// Need install driver (i2c and i2s) firstly
|
||||
int ret = ut_i2c_init(0);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = ut_i2s_init(0);
|
||||
TEST_ESP_OK(ret);
|
||||
// Do initialize of related interface: data_if, ctrl_if and gpio_if
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
.rx_handle = i2s_keep[0]->rx_handle,
|
||||
.tx_handle = i2s_keep[0]->tx_handle,
|
||||
};
|
||||
const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
TEST_ASSERT_NOT_NULL(data_if);
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {.addr = ES8311_CODEC_DEFAULT_ADDR};
|
||||
#ifdef USE_IDF_I2C_MASTER
|
||||
i2c_cfg.bus_handle = i2c_bus_handle;
|
||||
#endif
|
||||
const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
TEST_ASSERT_NOT_NULL(out_ctrl_if);
|
||||
|
||||
i2c_cfg.addr = ES7210_CODEC_DEFAULT_ADDR;
|
||||
const audio_codec_ctrl_if_t *in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
TEST_ASSERT_NOT_NULL(in_ctrl_if);
|
||||
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
TEST_ASSERT_NOT_NULL(gpio_if);
|
||||
// New output codec interface
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC,
|
||||
.ctrl_if = out_ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.pa_pin = TEST_BOARD_PA_PIN,
|
||||
.use_mclk = true,
|
||||
};
|
||||
const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg);
|
||||
TEST_ASSERT_NOT_NULL(out_codec_if);
|
||||
// New input codec interface
|
||||
es7210_codec_cfg_t es7210_cfg = {
|
||||
.ctrl_if = in_ctrl_if,
|
||||
.mic_selected = ES7120_SEL_MIC1 | ES7120_SEL_MIC2 | ES7120_SEL_MIC3 | ES7120_SEL_MIC4,
|
||||
};
|
||||
const audio_codec_if_t *in_codec_if = es7210_codec_new(&es7210_cfg);
|
||||
TEST_ASSERT_NOT_NULL(in_codec_if);
|
||||
// New output codec device
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.codec_if = out_codec_if,
|
||||
.data_if = data_if,
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
|
||||
};
|
||||
esp_codec_dev_handle_t play_dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(play_dev);
|
||||
// New input codec device
|
||||
dev_cfg.codec_if = in_codec_if;
|
||||
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN;
|
||||
esp_codec_dev_handle_t record_dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(record_dev);
|
||||
|
||||
ret = esp_codec_dev_set_out_vol(play_dev, 60.0);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_set_in_gain(record_dev, 30.0);
|
||||
TEST_ESP_OK(ret);
|
||||
// Play 16bits 2 channel
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.sample_rate = 48000,
|
||||
.channel = 2,
|
||||
.bits_per_sample = 16,
|
||||
};
|
||||
ret = esp_codec_dev_open(play_dev, &fs);
|
||||
TEST_ESP_OK(ret);
|
||||
// Record 16bits 4 channel select channel 0 and 3
|
||||
fs.channel = 4;
|
||||
fs.channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0) | ESP_CODEC_DEV_MAKE_CHANNEL_MASK(3);
|
||||
ret = esp_codec_dev_open(record_dev, &fs);
|
||||
TEST_ESP_OK(ret);
|
||||
uint8_t *data = (uint8_t *) malloc(512);
|
||||
int limit_size = 10 * fs.sample_rate * fs.channel * (fs.bits_per_sample >> 3);
|
||||
int got_size = 0;
|
||||
// Playback the recording content directly
|
||||
while (got_size < limit_size) {
|
||||
ret = esp_codec_dev_read(record_dev, data, 512);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_write(play_dev, data, 512);
|
||||
TEST_ESP_OK(ret);
|
||||
int max_sample, min_sample;
|
||||
codec_max_sample(data, 512, &max_sample, &min_sample);
|
||||
// Verify recording data not constant
|
||||
TEST_ASSERT(max_sample > min_sample);
|
||||
got_size += 512;
|
||||
}
|
||||
free(data);
|
||||
|
||||
ret = esp_codec_dev_close(play_dev);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_close(record_dev);
|
||||
TEST_ESP_OK(ret);
|
||||
esp_codec_dev_delete(play_dev);
|
||||
esp_codec_dev_delete(record_dev);
|
||||
|
||||
// Delete codec interface
|
||||
audio_codec_delete_codec_if(in_codec_if);
|
||||
audio_codec_delete_codec_if(out_codec_if);
|
||||
// Delete codec control interface
|
||||
audio_codec_delete_ctrl_if(in_ctrl_if);
|
||||
audio_codec_delete_ctrl_if(out_ctrl_if);
|
||||
audio_codec_delete_gpio_if(gpio_if);
|
||||
// Delete codec data interface
|
||||
audio_codec_delete_data_if(data_if);
|
||||
|
||||
ut_i2c_deinit(0);
|
||||
ut_i2s_deinit(0);
|
||||
ut_clr_i2s_mode();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _TEST_BOARD_H_
|
||||
#define _TEST_BOARD_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Codec configuration by ESP32S3_KORVO2_V3
|
||||
*/
|
||||
#define TEST_BOARD_I2C_SDA_PIN (17)
|
||||
#define TEST_BOARD_I2C_SCL_PIN (18)
|
||||
|
||||
#define TEST_BOARD_I2S_BCK_PIN (9)
|
||||
#define TEST_BOARD_I2S_MCK_PIN (16)
|
||||
#define TEST_BOARD_I2S_DATA_IN_PIN (10)
|
||||
#define TEST_BOARD_I2S_DATA_OUT_PIN (8)
|
||||
#define TEST_BOARD_I2S_DATA_WS_PIN (45)
|
||||
|
||||
#define TEST_BOARD_PA_PIN (48)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,350 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "my_codec.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
|
||||
// Customized volume curve taken from android framework
|
||||
static esp_codec_dev_vol_map_t volume_maps[] = {
|
||||
{.vol = 1, .db_value = -49.5},
|
||||
{.vol = 33, .db_value = -33.5},
|
||||
{.vol = 66, .db_value = -17.0},
|
||||
{.vol = 100, .db_value = 0.0},
|
||||
};
|
||||
|
||||
/*
|
||||
* Test case for esp_codec_dev API using customized interface
|
||||
*/
|
||||
TEST_CASE("esp codec dev API test", "[esp_codec_dev]")
|
||||
{
|
||||
const audio_codec_ctrl_if_t *ctrl_if = my_codec_ctrl_new();
|
||||
TEST_ASSERT_NOT_NULL(ctrl_if);
|
||||
my_codec_ctrl_t *codec_ctrl = (my_codec_ctrl_t *) ctrl_if;
|
||||
const audio_codec_data_if_t *data_if = my_codec_data_new();
|
||||
TEST_ASSERT_NOT_NULL(data_if);
|
||||
my_codec_data_t *codec_data = (my_codec_data_t *) data_if;
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
TEST_ASSERT_NOT_NULL(gpio_if);
|
||||
my_codec_cfg_t codec_cfg = {
|
||||
.ctrl_if = ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.hw_gain = {
|
||||
.pa_voltage = 3.3,
|
||||
.codec_dac_voltage = 3.3, // PA and codec use same voltage
|
||||
.pa_gain = 10.0, // PA gain 10db
|
||||
}
|
||||
};
|
||||
const audio_codec_if_t *codec_if = my_codec_new(&codec_cfg);
|
||||
TEST_ASSERT_NOT_NULL(codec_if);
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
|
||||
.codec_if = codec_if,
|
||||
.data_if = data_if,
|
||||
};
|
||||
esp_codec_dev_handle_t dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(codec_if);
|
||||
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.bits_per_sample = 16,
|
||||
.sample_rate = 48000,
|
||||
.channel = 2,
|
||||
};
|
||||
int ret = esp_codec_dev_open(dev, &fs);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
esp_codec_dev_vol_curve_t vol_curve = {
|
||||
.count = sizeof(volume_maps)/sizeof(esp_codec_dev_vol_map_t),
|
||||
.vol_map = volume_maps,
|
||||
};
|
||||
// Test for volume curve settings
|
||||
ret = esp_codec_dev_set_vol_curve(dev, &vol_curve);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
// Test for volume setting, considering volume curve
|
||||
ret = esp_codec_dev_set_out_vol(dev, 33.0);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(43, codec_ctrl->reg[MY_CODEC_REG_VOL]);
|
||||
ret = esp_codec_dev_set_out_vol(dev, 66.0);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(27, codec_ctrl->reg[MY_CODEC_REG_VOL]);
|
||||
|
||||
// Test for mute setting
|
||||
ret = esp_codec_dev_set_out_mute(dev, true);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(1, codec_ctrl->reg[MY_CODEC_REG_MUTE]);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_set_out_mute(dev, false);
|
||||
TEST_ASSERT_EQUAL(0, codec_ctrl->reg[MY_CODEC_REG_MUTE]);
|
||||
|
||||
// Test for microphone gain
|
||||
ret = esp_codec_dev_set_in_gain(dev, 20.0);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(20, codec_ctrl->reg[MY_CODEC_REG_MIC_GAIN]);
|
||||
ret = esp_codec_dev_set_in_gain(dev, 40.0);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(40, codec_ctrl->reg[MY_CODEC_REG_MIC_GAIN]);
|
||||
|
||||
// Test for microphone mute setting
|
||||
ret = esp_codec_dev_set_in_mute(dev, true);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(1, codec_ctrl->reg[MY_CODEC_REG_MIC_MUTE]);
|
||||
ret = esp_codec_dev_set_in_mute(dev, false);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(0, codec_ctrl->reg[MY_CODEC_REG_MIC_MUTE]);
|
||||
|
||||
// Test for read data
|
||||
uint8_t *data = (uint8_t *) calloc(1, 512);
|
||||
TEST_ASSERT_NOT_NULL(data);
|
||||
ret = esp_codec_dev_read(dev, data, 256);
|
||||
TEST_ESP_OK(ret);
|
||||
ret = esp_codec_dev_read(dev, data + 256, 256);
|
||||
TEST_ESP_OK(ret);
|
||||
for (int i = 0; i < 512; i++) {
|
||||
uint8_t v = (uint8_t) i;
|
||||
TEST_ASSERT_EQUAL(v, data[i]);
|
||||
}
|
||||
// Test for write data
|
||||
ret = esp_codec_dev_write(dev, data, 512);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(512, codec_data->write_idx);
|
||||
|
||||
esp_codec_dev_close(dev);
|
||||
|
||||
// Test for volume curve settings
|
||||
ret = esp_codec_dev_set_vol_curve(dev, &vol_curve);
|
||||
TEST_ASSERT(ret == 0);
|
||||
|
||||
// Test for volume setting
|
||||
ret = esp_codec_dev_set_out_vol(dev, 30.0);
|
||||
TEST_ESP_OK(ret);
|
||||
// Test for mute setting
|
||||
ret = esp_codec_dev_set_out_mute(dev, true);
|
||||
TEST_ESP_OK(ret);
|
||||
// Test for microphone gain
|
||||
ret = esp_codec_dev_set_in_gain(dev, 20.0);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
// Test for microphone mute setting
|
||||
ret = esp_codec_dev_set_in_mute(dev, true);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
// APP need fail after close
|
||||
// Test for read data
|
||||
ret = esp_codec_dev_read(dev, data, 256);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
// Test for write data
|
||||
ret = esp_codec_dev_write(dev, data, 512);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for volume interface
|
||||
const audio_codec_vol_if_t *vol_if = my_codec_vol_new();
|
||||
my_codec_vol_t *codec_vol = (my_codec_vol_t *) vol_if;
|
||||
TEST_ASSERT_NOT_NULL(data_if);
|
||||
// Should set vol handler before usage
|
||||
ret = esp_codec_dev_set_vol_handler(dev, vol_if);
|
||||
TEST_ESP_OK(ret);
|
||||
|
||||
ret = esp_codec_dev_set_out_vol(dev, 40.0);
|
||||
TEST_ESP_OK(ret);
|
||||
// Calculated from volume curve
|
||||
TEST_ASSERT_EQUAL(-30.0, codec_vol->vol_db);
|
||||
// Reopen device
|
||||
ret = esp_codec_dev_open(dev, &fs);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(true, codec_vol->is_open);
|
||||
|
||||
ret = esp_codec_dev_write(dev, data, 512);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(512, codec_vol->process_len);
|
||||
esp_codec_dev_close(dev);
|
||||
TEST_ESP_OK(ret);
|
||||
TEST_ASSERT_EQUAL(false, codec_vol->is_open);
|
||||
|
||||
free(data);
|
||||
// Delete codec dev handle
|
||||
esp_codec_dev_delete(dev);
|
||||
// Delete codec interface
|
||||
audio_codec_delete_codec_if(codec_if);
|
||||
// Delete codec control interface
|
||||
audio_codec_delete_ctrl_if(ctrl_if);
|
||||
// Delete codec data interface
|
||||
audio_codec_delete_data_if(data_if);
|
||||
audio_codec_delete_vol_if(vol_if);
|
||||
// Delete GPIO interface
|
||||
audio_codec_delete_gpio_if(gpio_if);
|
||||
}
|
||||
|
||||
TEST_CASE("esp codec dev wrong argument test", "[esp_codec_dev]")
|
||||
{
|
||||
const audio_codec_ctrl_if_t *ctrl_if = my_codec_ctrl_new();
|
||||
TEST_ASSERT_NOT_NULL(ctrl_if);
|
||||
const audio_codec_data_if_t *data_if = my_codec_data_new();
|
||||
TEST_ASSERT_NOT_NULL(data_if);
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
TEST_ASSERT_NOT_NULL(gpio_if);
|
||||
my_codec_cfg_t codec_cfg = {
|
||||
.ctrl_if = ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
};
|
||||
const audio_codec_if_t *codec_if = my_codec_new(&codec_cfg);
|
||||
TEST_ASSERT_NOT_NULL(codec_if);
|
||||
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
|
||||
.codec_if = codec_if,
|
||||
.data_if = data_if,
|
||||
};
|
||||
esp_codec_dev_handle_t dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(codec_if);
|
||||
|
||||
esp_codec_dev_handle_t dev_bad = esp_codec_dev_new(NULL);
|
||||
TEST_ASSERT(dev_bad == NULL);
|
||||
dev_cfg.data_if = NULL;
|
||||
dev_bad = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT(dev_bad == NULL);
|
||||
|
||||
int ret = esp_codec_dev_open(dev, NULL);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
esp_codec_dev_vol_curve_t vol_curve = {
|
||||
.count = 2,
|
||||
};
|
||||
// Test for volume curve settings
|
||||
ret = esp_codec_dev_set_vol_curve(dev, &vol_curve);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for volume setting
|
||||
ret = esp_codec_dev_set_out_vol(NULL, 50.0);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for mute setting
|
||||
ret = esp_codec_dev_set_out_mute(NULL, true);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for microphone gain
|
||||
ret = esp_codec_dev_set_in_gain(NULL, 20.0);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for microphone mute setting
|
||||
ret = esp_codec_dev_set_in_mute(NULL, true);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for read data
|
||||
uint8_t data[16];
|
||||
ret = esp_codec_dev_read(dev, NULL, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
ret = esp_codec_dev_read(dev, data, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
ret = esp_codec_dev_read(NULL, NULL, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for write data
|
||||
ret = esp_codec_dev_write(dev, data, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
ret = esp_codec_dev_write(NULL, data, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
ret = esp_codec_dev_write(dev, NULL, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
esp_codec_dev_close(dev);
|
||||
// Delete codec dev handle
|
||||
esp_codec_dev_delete(dev);
|
||||
// Delete codec interface
|
||||
audio_codec_delete_codec_if(codec_if);
|
||||
// Delete codec control interface
|
||||
audio_codec_delete_ctrl_if(ctrl_if);
|
||||
// Delete codec data interface
|
||||
audio_codec_delete_data_if(data_if);
|
||||
// Delete GPIO interface
|
||||
audio_codec_delete_gpio_if(gpio_if);
|
||||
}
|
||||
|
||||
TEST_CASE("esp codec dev feature should not support", "[esp_codec_dev]")
|
||||
{
|
||||
const audio_codec_ctrl_if_t *ctrl_if = my_codec_ctrl_new();
|
||||
TEST_ASSERT_NOT_NULL(ctrl_if);
|
||||
const audio_codec_data_if_t *data_if = my_codec_data_new();
|
||||
TEST_ASSERT_NOT_NULL(data_if);
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
TEST_ASSERT_NOT_NULL(gpio_if);
|
||||
my_codec_cfg_t codec_cfg = {
|
||||
.ctrl_if = ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
};
|
||||
const audio_codec_if_t *codec_if = my_codec_new(&codec_cfg);
|
||||
TEST_ASSERT_NOT_NULL(codec_if);
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN,
|
||||
.codec_if = codec_if,
|
||||
.data_if = data_if,
|
||||
};
|
||||
int ret = 0;
|
||||
uint8_t data[16];
|
||||
// Input device should not support output function
|
||||
{
|
||||
esp_codec_dev_handle_t dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(codec_if);
|
||||
esp_codec_dev_vol_map_t vol_maps[2] = {
|
||||
{.vol = 0, .db_value = 0 },
|
||||
{.vol = 100, .db_value = 100},
|
||||
};
|
||||
esp_codec_dev_vol_curve_t vol_curve = {
|
||||
.count = 2,
|
||||
.vol_map = vol_maps,
|
||||
};
|
||||
// Test for volume curve settings
|
||||
ret = esp_codec_dev_set_vol_curve(dev, &vol_curve);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for volume setting
|
||||
ret = esp_codec_dev_set_out_vol(dev, 30.0);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for mute setting
|
||||
ret = esp_codec_dev_set_out_mute(NULL, true);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for write data
|
||||
ret = esp_codec_dev_write(dev, data, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
esp_codec_dev_close(dev);
|
||||
// Delete codec dev handle
|
||||
esp_codec_dev_delete(dev);
|
||||
}
|
||||
// Output device should not support input function
|
||||
{
|
||||
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_OUT;
|
||||
esp_codec_dev_handle_t dev = esp_codec_dev_new(&dev_cfg);
|
||||
TEST_ASSERT_NOT_NULL(dev);
|
||||
|
||||
// Test for volume setting
|
||||
ret = esp_codec_dev_set_in_gain(dev, 20.0);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for mute setting
|
||||
ret = esp_codec_dev_set_in_mute(dev, true);
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
|
||||
// Test for write data
|
||||
ret = esp_codec_dev_read(dev, data, sizeof(data));
|
||||
TEST_ASSERT(ret != ESP_CODEC_DEV_OK);
|
||||
esp_codec_dev_close(dev);
|
||||
// Delete codec dev handle
|
||||
esp_codec_dev_delete(dev);
|
||||
}
|
||||
// Delete codec interface
|
||||
audio_codec_delete_codec_if(codec_if);
|
||||
// Delete codec control interface
|
||||
audio_codec_delete_ctrl_if(ctrl_if);
|
||||
// Delete codec data interface
|
||||
audio_codec_delete_data_if(data_if);
|
||||
// Delete GPIO interface
|
||||
audio_codec_delete_gpio_if(gpio_if);
|
||||
}
|
||||
Reference in New Issue
Block a user