initial commit

This commit is contained in:
valentineautos
2025-12-05 09:22:55 +00:00
parent 46949642a2
commit 16ffc2ab10
4033 changed files with 980542 additions and 0 deletions

View File

@@ -0,0 +1 @@
014948481bda426cd46714f297fe1891711246c62bea288863a8cc8cf13ef1f0

View 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`

View 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()

View 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

View 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.

View File

@@ -0,0 +1,238 @@
# ESP Codec Device
- [![Component Registry](https://components.espressif.com/components/espressif/esp_codec_dev/badge.svg)](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).

View File

@@ -0,0 +1,237 @@
# ESP Codec Device
- [![组件注册](https://components.espressif.com/components/espressif/esp_codec_dev/badge.svg)](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)。

View 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;
}

View File

@@ -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

View File

@@ -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, &regv);
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);
}

View File

@@ -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_ */

View File

@@ -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, &regv);
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, &regv);
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, &reg) != 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;
}

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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, &regv);
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;
}

View File

@@ -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

View File

@@ -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, &regv);
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, &regv);
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, &regv);
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, &regv);
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, &regv);
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, &regv);
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, &regv);
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, &regv);
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, &regv);
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;
}

View File

@@ -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

View File

@@ -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, &reg);
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, &reg);
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, &reg);
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, &reg); // 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);
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, &reg);
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, &reg);
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, &reg); // 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, &reg);
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);
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, &reg); // 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, &reg); // 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, &reg); // 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, &reg); // 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, &reg); // power up adc and input
reg |= 0xc0;
ret |= es8374_write_reg(codec, 0x10, reg);
ret |= es8374_read_reg(codec, 0x21, &reg); // 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, &reg); // set monomixer
reg |= 0x60;
reg |= 0x20;
reg &= 0xf7;
ret |= es8374_write_reg(codec, 0x1a, reg);
ret |= es8374_read_reg(codec, 0x1c, &reg); // 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, &reg); // power up adc and input
reg &= 0x3f;
ret |= es8374_write_reg(codec, 0x21, reg);
ret |= es8374_read_reg(codec, 0x10, &reg); // 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, &reg); // 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, &reg); // power up dac
reg &= 0xdf;
ret |= es8374_write_reg(codec, 0x15, reg);
ret |= es8374_read_reg(codec, 0x1a, &reg); // 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;
}

View File

@@ -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 = 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 = 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 = 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 = 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 = 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;
}

View File

@@ -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__

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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__

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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, &reg);
ret |= write_addr(codec, 0x23A, 1, &reg);
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, &reg);
*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;
}

View 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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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).

View File

@@ -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
)

View File

@@ -0,0 +1,6 @@
description: Codec Device Test
dependencies:
esp_codec_dev:
version: ">=1.1"
override_path: "../../../"

View File

@@ -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, &reg, 1);
reg = 0;
codec->ctrl_if->write_reg(codec->ctrl_if, MY_CODEC_REG_MUTE, 1, &reg, 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;
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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);
}