initial commit

This commit is contained in:
Mikkeli Matlock
2026-02-15 02:48:59 +09:00
commit 19db125619
258 changed files with 345581 additions and 0 deletions

View File

@@ -0,0 +1 @@
f72797539ecd2b14776c1eec0c39b9b9a48e587c5155297e248dcffdb3bcaaac

View File

@@ -0,0 +1,21 @@
# Changelog
## v0.5.5
- Add board support for `ESP32_S3_KORVO_2L`, `ESP32_S3_EchoEar`, `ATOMS3_ECHO_BASE`
- Mount SDcard use high speed for better performance on esp32s3 and esp32p4
## v0.5.4
- Add PDM support
- Add play and record support for `XD_AIOT_C3` and `ESP_SPOT`
## v0.5.3
- Add support for mount SDCard on esp32
- Add support for I2C manually control use `init_i2c` and `deinit_i2c`
- Fixed I2S driver failed to initialized if more than one
## v0.5.0
- Initial version of `codec_board`

View File

@@ -0,0 +1,17 @@
set(COMPONENT_PRIV_REQUIRE driver esp_codec_dev fatfs esp_lcd)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
list(APPEND COMPONENT_PRIV_REQUIRE esp_driver_i2c)
endif()
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
list(APPEND COMPONENT_PRIV_REQUIRE esp_driver_i2c)
endif()
idf_component_register(SRCS "cfg_parse.c" "codec_board.c" "codec_init.c" "dummy_codec.c"
"lcd_init.c" "drv/tca9554.c"
INCLUDE_DIRS ./include
PRIV_INCLUDE_DIRS drv
REQUIRES "${COMPONENT_PRIV_REQUIRE}"
EMBED_TXTFILES "board_cfg.txt")

View File

@@ -0,0 +1,22 @@
Espressif Modified MIT License
Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD
Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products.
This includes the right to use, copy, modify, merge, publish, distribute, and sublicense
the Software, subject to the following conditions:
1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS.
2. The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS
is strictly prohibited.
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.
SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT

View File

@@ -0,0 +1,48 @@
# Codec_board
## Overview
`codec_board` is a simple implementation of board support for a variety of ESP32 series boards.
Its main purpose is to:
- Gather board-specific peripheral settings from user.
- Add drivers for the supported peripherals.
- Provide high-level APIs to interact with the peripherals or abstract the peripherals into manageable handles.
## Supported Peripherals
- **Codec**
Currently support `ES8311`, `ES8388`, `ES7210`, `ES7243`, user can add other codec also.
After `init_codec` is called, codec is abstracted into `esp_codec_dev` handle.
Then use can use `esp_codec_dev` API to do playback and record.
Simple configuration of codec as:
```
out: {codec: ES8311, pa: 38, use_mclk: 0, pa_gain:6}
in: {codec: ES7210, i2s_port: 1}
```
- **SDcard**
Users can configure SDCard related GPIO then use API `mount_sdcard` to mount sdcard to use it.
SDcard configuration as:
```
sdcard: {clk: 43, cmd: 44, d0: 39, d1: 40, d2: 41, d3: 42}
```
- **Camera**
Currently support DVP and MIPI camera, use can get camera settings use API `get_camera_cfg`
- **LCD**
Currently only support LCD on `S3_Korvo_V2` and `ESP32_P4_FUNCTION_EV`.
User can get LCD handle through API `board_get_lcd_handle` after `board_lcd_init`
## Customized a New Board
To reuse the pre-defined devices, follow these steps:
1. Add a new section to the [board_cfg.txt](board_cfg.txt) file and modify corresponding GPIO settings as needed.
2. Call `set_codec_board_type` with the name of the newly added section.
For new devices, you will need to manually modify the code.
## Comparison with esp-bsp
[esp-bsp](https://github.com/espressif/esp-bsp) supports almost all devices carried on board while this module only support media related devices. It provides a quick verification option if reusing the same device with different GPIO settings. Users can easily replace this module with `esp-bsp` by making the following substitutions:
- `bsp_audio_codec_speaker_init`: Replaces `get_playback_handle`
- `bsp_audio_codec_microphone_init`: Replaces `get_record_handle`
- `bsp_display_new`: Replaces `board_get_lcd_handle`

View File

@@ -0,0 +1,27 @@
# support in, out, in_out type
# support i2c_port, i2s_port settings
# support pa_gain, i2c_addr setting
Board: C6_AMOLED_1_43
i2c: {sda: 18, scl: 8}
i2s: {bclk: 21, ws: 22, dout: 23, din: 20, mclk: 19}
out: {codec: ES8311, pa: -1, use_mclk: 1, pa_gain:6}
in: {codec: ES7210}
Board: S3_Korvo_V2
i2c: {sda: 17, scl: 18}
i2s: {mclk: 16, bclk: 9, ws: 45, din: 10, dout: 8}
out: {codec: ES8311, pa: 48, pa_gain: 6, use_mclk: 1, pa_gain:6}
in: {codec: ES7210}
Board: S3_LCD_3_49
i2c: {sda: 47, scl: 48}
i2s: {mclk: 7, bclk: 15, ws: 46, din: 6, dout: 45}
out: {codec: ES8311, pa: -1, pa_gain: 6, use_mclk: 1, pa_gain:6}
in: {codec: ES7210}
Board: S3_RLCD_4_2
i2c: {sda: 13, scl: 14}
i2s: {mclk: 16, bclk: 9, ws: 45, din: 10, dout: 8}
out: {codec: ES8311, pa: 46, pa_gain: 6, use_mclk: 1, pa_gain:6}
in: {codec: ES7210}

View File

@@ -0,0 +1,596 @@
#include "codec_board.h"
#define BOARD_SECTION "Board:"
#define IN_STR(a, const_b, len) in_str(a, const_b, sizeof(const_b) - 1, len)
typedef struct _board_cfg_attr {
const char *attr;
const char *value;
struct _board_cfg_attr *next;
} board_cfg_attr_t;
typedef struct {
const char *type;
board_cfg_attr_t *attr;
} board_cfg_line_t;
static board_section_t *codec_section;
extern const char board_cfg_start[] asm("_binary_board_cfg_txt_start");
extern const char board_cfg_end[] asm("_binary_board_cfg_txt_end");
static bool is_word(char s)
{
return ((s >= 'A' && s <= 'Z') || (s >= 'a' && s <= 'z') || (s >= '0' && s <= '9') || s == '_' || s == '-' ||
s == '.');
}
bool str_same(const char *a, const char *b)
{
while (*b && *a == *b) {
a++;
b++;
}
if (*b == 0) {
if (is_word(*a) == false) {
return true;
}
}
return false;
}
static const char *in_str(const char *s, const char *org, int org_len, int len)
{
while (len > org_len) {
if (*s == *org && memcmp(s, org, org_len) == 0 && is_word(s[org_len]) == false) {
return s;
}
s++;
len--;
}
return NULL;
}
const char *get_section_data(const char *data, int size, const char *section_name)
{
const char *s = data;
while (1) {
int left = size - (s - data);
s = IN_STR(s, BOARD_SECTION, left);
if (s == NULL) {
break;
}
s += sizeof(BOARD_SECTION) - 1;
while (!is_word(*s)) {
s++;
}
if (str_same(s, section_name)) {
while (*(s++) != '\n')
;
return s;
}
}
return NULL;
}
static int str_len(const char *s)
{
int len = 0;
while (is_word(*s)) {
len++;
s++;
}
return len;
}
static void print_cfg_line(board_cfg_line_t *cfg_line)
{
printf("%.*s: {", str_len(cfg_line->type), cfg_line->type);
board_cfg_attr_t *attr = cfg_line->attr;
while (attr) {
printf("%.*s: %.*s", str_len(attr->attr), attr->attr, str_len(attr->value), attr->value);
if (attr->next) {
printf(", ");
}
attr = attr->next;
}
printf("}\n");
}
static void free_cfg_line(board_cfg_line_t *cfg_line)
{
if (cfg_line) {
board_cfg_attr_t *attr = cfg_line->attr;
while (attr) {
board_cfg_attr_t *nxt = attr->next;
free(attr);
attr = nxt;
}
free(cfg_line);
}
}
static board_cfg_line_t *parse_section(const char *s, int size, int *consume)
{
board_cfg_line_t *cfg_line;
const char *start = s;
const char *end = s + size;
const char *name = s;
bool is_comment = false;
while (size) {
if (*name == '#') {
is_comment = true;
}
if (is_comment) {
if (*name == '\n') {
is_comment = false;
}
} else {
if (is_word(*name)) {
break;
}
}
size--;
name++;
}
if (size == 0 || str_same(name, BOARD_SECTION)) {
return NULL;
}
cfg_line = calloc(1, sizeof(board_cfg_line_t));
if (cfg_line == NULL) {
return NULL;
}
board_cfg_attr_t *tail = NULL;
cfg_line->type = name;
s = name;
while (*(s++) != '{')
;
int word_len = 0;
const char *attr = NULL;
while (s < end) {
if (*s == '}') {
*consume = (s + 1 - start);
break;
}
if (is_word(*s)) {
if (word_len == 0) {
if (attr == NULL) {
attr = s;
} else {
board_cfg_attr_t *cfg_attr = calloc(1, sizeof(board_cfg_attr_t));
if (cfg_attr) {
cfg_attr->attr = attr;
cfg_attr->value = s;
if (tail == NULL) {
cfg_line->attr = cfg_attr;
} else {
tail->next = cfg_attr;
}
tail = cfg_attr;
}
attr = NULL;
}
}
word_len++;
} else {
word_len = 0;
}
s++;
}
return cfg_line;
}
static int fill_i2c_cfg(board_cfg_attr_t *attr)
{
if (codec_section->i2c_num >= MAX_I2C_NUM) {
return -1;
}
codec_i2c_pin_t *i2c_cfg = &codec_section->i2c_pin[codec_section->i2c_num++];
while (attr) {
if (str_same(attr->attr, "sda")) {
i2c_cfg->sda = atoi(attr->value);
} else if (str_same(attr->attr, "scl")) {
i2c_cfg->scl = atoi(attr->value);
}
attr = attr->next;
}
return 0;
}
static int fill_i2s_cfg(board_cfg_attr_t *attr)
{
if (codec_section->i2s_num >= MAX_I2C_NUM) {
return -1;
}
codec_i2s_pin_t *i2s_cfg = &codec_section->i2s_pin[codec_section->i2s_num++];
i2s_cfg->din = i2s_cfg->dout = -1;
while (attr) {
if (str_same(attr->attr, "bclk")) {
i2s_cfg->bclk = atoi(attr->value);
} else if (str_same(attr->attr, "din")) {
i2s_cfg->din = atoi(attr->value);
} else if (str_same(attr->attr, "dout")) {
i2s_cfg->dout = atoi(attr->value);
} else if (str_same(attr->attr, "ws")) {
i2s_cfg->ws = atoi(attr->value);
} else if (str_same(attr->attr, "mclk")) {
i2s_cfg->mclk = atoi(attr->value);
}
attr = attr->next;
}
return 0;
}
static extend_io_type_t lcd_get_io_type(const char* s)
{
if (str_same(s, "tca9554")) {
return EXTENT_IO_TYPE_TCA9554;
}
return EXTENT_IO_TYPE_NONE;
}
static lcd_controller_type_t lcd_get_controller(const char* s)
{
if (str_same(s, "st7789")) {
return LCD_CONTROLLER_TYPE_ST7789;
}
return LCD_CONTROLLER_TYPE_NONE;
}
static lcd_bus_type_t lcd_get_bus(const char* s)
{
if (str_same(s, "spi")) {
return LCD_BUS_TYPE_SPI;
}
if (str_same(s, "rgb")) {
return LCD_BUS_TYPE_RGB;
}
if (str_same(s, "i80")) {
return LCD_BUS_TYPE_I80;
}
if (str_same(s, "mipi")) {
return LCD_BUS_TYPE_MIPI;
}
return LCD_BUS_TYPE_NONE;
}
static int16_t get_pin(const char* s)
{
char* ext = strstr(s, "ext");
if (ext) {
return 0x1000 + atoi(ext + 3);
}
return (int16_t)atoi(s);
}
static int fill_lcd_cfg(board_cfg_attr_t *attr)
{
if (codec_section->lcd_num >= 1) {
return -1;
}
lcd_cfg_t *lcd_cfg = &codec_section->lcd;
lcd_cfg->reset_pin = lcd_cfg->ctrl_pin = -1;
codec_section->lcd_num++;
while (attr) {
if (str_same(attr->attr, "bus")) {
lcd_cfg->bus_type = lcd_get_bus(attr->value);
if (lcd_cfg->bus_type == LCD_BUS_TYPE_SPI) {
for (int i = 0; i < sizeof(lcd_cfg->spi_cfg.d)/sizeof(lcd_cfg->spi_cfg.d[0]); i++) {
lcd_cfg->spi_cfg.d[i] = -1;
}
}
} else if (str_same(attr->attr, "controller")) {
lcd_cfg->controller = lcd_get_controller(attr->value);
}
else if (str_same(attr->attr, "extend_io")) {
lcd_cfg->io_type = lcd_get_io_type(attr->value);
} else if (str_same(attr->attr, "width")) {
lcd_cfg->width = atoi(attr->value);
} else if (str_same(attr->attr, "height")) {
lcd_cfg->height = atoi(attr->value);
} else if (str_same(attr->attr, "mirror_x")) {
lcd_cfg->mirror_x = atoi(attr->value) ? 1: 0;
} else if (str_same(attr->attr, "mirror_y")) {
lcd_cfg->mirror_y = atoi(attr->value) ? 1: 0;
} else if (str_same(attr->attr, "swap_xy")) {
lcd_cfg->swap_xy = atoi(attr->value) ? 1: 0;
} else if (str_same(attr->attr, "color_inv")) {
lcd_cfg->color_inv = atoi(attr->value) ? 1: 0;
} else if (str_same(attr->attr, "ctrl")) {
lcd_cfg->ctrl_pin = get_pin(attr->value);
} else if (str_same(attr->attr, "rst")) {
lcd_cfg->reset_pin = get_pin(attr->value);
}
else if (lcd_cfg->bus_type == LCD_BUS_TYPE_SPI) {
// Bus configuration
if (str_same(attr->attr, "spi_bus")) {
lcd_cfg->spi_cfg.spi_bus = atoi(attr->value);
} else if (str_same(attr->attr, "cs")) {
lcd_cfg->spi_cfg.cs = get_pin(attr->value);
} else if (str_same(attr->attr, "dc")) {
lcd_cfg->spi_cfg.dc = get_pin(attr->value);
} else if (str_same(attr->attr, "clk")) {
lcd_cfg->spi_cfg.clk = get_pin(attr->value);
} else if (str_same(attr->attr, "mosi")) {
lcd_cfg->spi_cfg.mosi = get_pin(attr->value);
} else if (str_same(attr->attr, "cmd_bits")) {
lcd_cfg->spi_cfg.cmd_bits = (uint8_t)atoi(attr->value);
} else if (str_same(attr->attr, "param_bits")) {
lcd_cfg->spi_cfg.param_bits = (uint8_t)atoi(attr->value);
}
} else if (lcd_cfg->bus_type == LCD_BUS_TYPE_MIPI) {
if (str_same(attr->attr, "ldo_chan")) {
lcd_cfg->mipi_cfg.ldo_chan = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "ldo_voltage")) {
lcd_cfg->mipi_cfg.ldo_voltage = (uint16_t)atoi(attr->value);
}
else if (str_same(attr->attr, "lane_num")) {
lcd_cfg->mipi_cfg.lane_num = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "fb_num")) {
lcd_cfg->mipi_cfg.fb_num = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "bit_depth")) {
lcd_cfg->mipi_cfg.bit_depth = (uint16_t)atoi(attr->value);
}
else if (str_same(attr->attr, "lane_bitrate")) {
lcd_cfg->mipi_cfg.lane_bitrate = (uint32_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dpi_clk")) {
lcd_cfg->mipi_cfg.dpi_clk = (uint32_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dsi_hsync")) {
lcd_cfg->mipi_cfg.dsi_hsync = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dsi_vsync")) {
lcd_cfg->mipi_cfg.dsi_vsync = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dsi_hbp")) {
lcd_cfg->mipi_cfg.dsi_hbp = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dsi_hfp")) {
lcd_cfg->mipi_cfg.dsi_hfp = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dsi_vbp")) {
lcd_cfg->mipi_cfg.dsi_vbp = (uint8_t)atoi(attr->value);
}
else if (str_same(attr->attr, "dsi_vfp")) {
lcd_cfg->mipi_cfg.dsi_vfp = (uint8_t)atoi(attr->value);
}
}
attr = attr->next;
}
return 0;
}
// TODO add other codec
static codec_type_t get_codec_type(const char *type)
{
if (str_same(type, "ES8311")) {
return CODEC_TYPE_ES8311;
}
if (str_same(type, "ES8388")) {
return CODEC_TYPE_ES8388;
}
if (str_same(type, "ES7243")) {
return CODEC_TYPE_ES7243;
}
if (str_same(type, "ES7210")) {
return CODEC_TYPE_ES7210;
}
if (str_same(type, "DUMMY")) {
return CODEC_TYPE_DUMMY;
}
return CODEC_TYPE_NONE;
}
static int fill_codec_cfg(board_cfg_attr_t *attr, uint8_t codec_dir)
{
if (codec_section->codec_num >= MAX_CODEC_NUM) {
return -1;
}
codec_section->codec[codec_section->codec_num].codec_dir = codec_dir;
codec_cfg_t *codec_cfg = &codec_section->codec[codec_section->codec_num].codec_cfg;
while (attr) {
if (str_same(attr->attr, "i2c_port")) {
codec_cfg->i2c_port = atoi(attr->value);
if (codec_cfg->i2c_port >= codec_section->i2c_num) {
return -1;
}
} else if (str_same(attr->attr, "i2s_port")) {
codec_cfg->i2s_port = atoi(attr->value);
if (codec_cfg->i2s_port >= codec_section->i2s_num) {
return -1;
}
} else if (str_same(attr->attr, "pa")) {
codec_cfg->pa_pin = atoi(attr->value);
} else if (str_same(attr->attr, "pa_gain")) {
codec_cfg->pa_gain = atof(attr->value);
} else if (str_same(attr->attr, "use_mclk")) {
codec_cfg->use_mclk = (bool) atoi(attr->value);
} else if (str_same(attr->attr, "codec")) {
codec_cfg->codec_type = get_codec_type(attr->value);
if (codec_cfg->codec_type == CODEC_TYPE_NONE) {
return -1;
}
} else if (str_same(attr->attr, "i2c_addr")) {
codec_cfg->i2c_addr = (uint8_t) atoi(attr->value);
}
attr = attr->next;
}
printf("Codec %d dir %d type:%d\n", codec_section->codec_num, codec_dir, codec_cfg->codec_type);
codec_section->codec_num++;
return 0;
}
static int fill_sdcard_cfg(board_cfg_attr_t *attr)
{
if (codec_section->sdcard_num >= 1) {
return -1;
}
sdcard_cfg_t *sdcard_cfg = &codec_section->sdcard;
sdcard_cfg->power = -1;
while (attr) {
if (str_same(attr->attr, "clk")) {
sdcard_cfg->clk = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "cmd")) {
sdcard_cfg->cmd = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "d0")) {
sdcard_cfg->d0 = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "d1")) {
sdcard_cfg->d1 = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "d2")) {
sdcard_cfg->d2 = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "d3")) {
sdcard_cfg->d3 = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "power")) {
sdcard_cfg->power = (int16_t) atoi(attr->value);
}
attr = attr->next;
}
printf("Sdcard clk:%d cmd:%d d0:%d\n", sdcard_cfg->clk, sdcard_cfg->cmd, sdcard_cfg->d0);
codec_section->sdcard_num++;
return 0;
}
static camera_type_t get_camera_type(const char* s) {
if (str_same(s, "dvp")) {
return CAMERA_TYPE_DVP;
}
if (str_same(s, "usb")) {
return CAMERA_TYPE_USB;
}
if (str_same(s, "mipi")) {
return CAMERA_TYPE_MIPI;
}
return CAMERA_TYPE_NONE;
}
static int fill_camera_cfg(board_cfg_attr_t *attr)
{
if (codec_section->camera_num >= 1) {
return -1;
}
camera_cfg_t *camera_cfg = &codec_section->camera;
for (int i = 0; i < sizeof(camera_cfg->data)/sizeof(camera_cfg->data[0]); i++) {
camera_cfg->data[i] = -1;
}
camera_cfg->pwr = -1;
camera_cfg->reset = -1;
while (attr) {
if (str_same(attr->attr, "type")) {
camera_cfg->type = get_camera_type(attr->value);
}
if (str_same(attr->attr, "xclk")) {
camera_cfg->xclk = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "pclk")) {
camera_cfg->pclk = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "pwr")) {
camera_cfg->pwr = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "reset")) {
camera_cfg->reset = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "vsync")) {
camera_cfg->vsync = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "href")) {
camera_cfg->href = (int16_t) atoi(attr->value);
} else if (str_same(attr->attr, "de")) {
camera_cfg->de = (int16_t) atoi(attr->value);
} else if (attr->attr[0] == 'd' && attr->attr[1] >= '0' && attr->attr[1] <= '9') {
int n = atoi(attr->attr + 1);
if (n < sizeof(camera_cfg->data)/sizeof(camera_cfg->data[0])) {
camera_cfg->data[n] = (int16_t) atoi(attr->value);
}
}
attr = attr->next;
}
codec_section->camera_num++;
return 0;
}
static int fill_cfg(board_cfg_line_t *cfg_line)
{
board_cfg_attr_t *attr = cfg_line->attr;
if (str_same(cfg_line->type, "i2c")) {
if (fill_i2c_cfg(attr) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "i2s")) {
if (fill_i2s_cfg(attr) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "sdcard")) {
if (fill_sdcard_cfg(attr) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "in_out")) {
if (fill_codec_cfg(attr, CODEC_DIR_IN_OUT) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "out")) {
if (fill_codec_cfg(attr, CODEC_DIR_OUT) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "in")) {
if (fill_codec_cfg(attr, CODEC_DIR_IN) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "lcd")) {
if (fill_lcd_cfg(attr) != 0) {
return -1;
}
} else if (str_same(cfg_line->type, "camera")) {
if (fill_camera_cfg(attr) != 0) {
return -1;
}
}
return 0;
}
static int parse_cfg(const char *section, int size)
{
int consume = 0;
while (1) {
board_cfg_line_t *cfg_line = parse_section(section, size, &consume);
if (cfg_line == NULL) {
break;
}
size -= consume;
section += consume;
print_cfg_line(cfg_line);
int ret = fill_cfg(cfg_line);
free_cfg_line(cfg_line);
if (ret != 0) {
printf("Fail to parse cfg line\n");
return ret;
}
}
return 0;
}
board_section_t *get_codec_section(const char *codec_type)
{
if (codec_section) {
free(codec_section);
}
codec_section = calloc(1, sizeof(board_section_t));
if (codec_type == NULL) {
return NULL;
}
int cfg_size = board_cfg_end - board_cfg_start;
do {
const char *section = get_section_data(board_cfg_start, cfg_size, codec_type);
if (section == NULL) {
break;
}
int left_size = cfg_size - (section - board_cfg_start);
if (parse_cfg(section, left_size) != 0) {
break;
}
return codec_section;
} while (0);
free(codec_section);
return NULL;
}

View File

@@ -0,0 +1,101 @@
#include "codec_board.h"
#include "esp_log.h"
#define TAG "BOARD"
board_section_t *get_codec_section(const char *codec_type);
static board_section_t *codec;
#define RET_ON_NOT_INIT() if (codec == NULL) { \
return -1; \
}
void set_codec_board_type(const char *codec_type)
{
if (codec) {
return;
}
codec = get_codec_section(codec_type);
}
int get_sdcard_config(sdcard_cfg_t *card_cfg)
{
RET_ON_NOT_INIT();
if (codec->sdcard_num == 0) {
ESP_LOGE(TAG, "Sdcard not exits on board");
return -1;
}
memcpy(card_cfg, &codec->sdcard, sizeof(sdcard_cfg_t));
return 0;
}
int get_i2c_pin(uint8_t port, codec_i2c_pin_t *i2c_pin)
{
RET_ON_NOT_INIT();
if (port > codec->i2c_num) {
ESP_LOGE(TAG, "I2C %d not exits on board", port);
return -1;
}
memcpy(i2c_pin, &codec->i2c_pin[port], sizeof(codec_i2c_pin_t));
return 0;
}
int get_i2s_pin(uint8_t port, codec_i2s_pin_t *i2s_pin)
{
RET_ON_NOT_INIT();
if (port > codec->i2s_num) {
ESP_LOGE(TAG, "I2S %d not exits on board", port);
return -1;
}
memcpy(i2s_pin, &codec->i2s_pin[port], sizeof(codec_i2s_pin_t));
return 0;
}
int get_out_codec_cfg(codec_cfg_t *out_cfg)
{
RET_ON_NOT_INIT();
for (int i = 0; i < codec->codec_num; i++) {
if (codec->codec[i].codec_dir & CODEC_DIR_OUT) {
memcpy(out_cfg, &codec->codec[i].codec_cfg, sizeof(codec_cfg_t));
return 0;
}
}
ESP_LOGE(TAG, "Output codec not exits on board");
return -1;
}
int get_in_codec_cfg(codec_cfg_t *in_cfg)
{
RET_ON_NOT_INIT();
for (int i = 0; i < codec->codec_num; i++) {
if (codec->codec[i].codec_dir & CODEC_DIR_IN) {
memcpy(in_cfg, &codec->codec[i].codec_cfg, sizeof(codec_cfg_t));
return 0;
}
}
ESP_LOGE(TAG, "Input codec not exits on board");
return -1;
}
int get_camera_cfg(camera_cfg_t *cam_cfg)
{
RET_ON_NOT_INIT();
if (codec->camera_num == 0) {
ESP_LOGE(TAG, "Camera not exits on board");
return -1;
}
memcpy(cam_cfg, &codec->camera, sizeof(camera_cfg_t));
return 0;
}
int get_lcd_cfg(lcd_cfg_t *lcd_cfg)
{
RET_ON_NOT_INIT();
if (codec->lcd_num) {
memcpy(lcd_cfg, &codec->lcd, sizeof(lcd_cfg_t));
return 0;
}
ESP_LOGE(TAG, "LCD not exits on board");
return -1;
}

View File

@@ -0,0 +1,710 @@
#include <string.h>
#include <stdint.h>
#include "driver/i2c.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"
#include "soc/soc_caps.h"
#else
#include "driver/i2s.h"
#endif
#include "esp_codec_dev.h"
#include "esp_codec_dev_defaults.h"
#include "esp_codec_dev_os.h"
#include "codec_board.h"
#include "codec_init.h"
#include "esp_log.h"
#include "dummy_codec.h"
#include "driver/i2c_master.h"
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "driver/sdmmc_defs.h"
#define TAG "CODEC_INIT"
typedef struct {
bool inited;
const audio_codec_data_if_t *data_if;
const audio_codec_data_if_t *data_in_if;
const audio_codec_gpio_if_t *gpio_if;
const audio_codec_ctrl_if_t *in_ctrl_if;
const audio_codec_ctrl_if_t *out_ctrl_if;
const audio_codec_if_t *out_codec_if;
const audio_codec_if_t *in_codec_if;
esp_codec_dev_handle_t play_dev;
esp_codec_dev_handle_t record_dev;
} codec_res_t;
#define USE_I2C_MASTER
typedef struct {
bool inited;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
#endif
} i2s_keep_t;
static bool i2c_inited[MAX_I2C_NUM];
static i2c_master_bus_handle_t i2c_bus_handle[MAX_I2C_NUM];
static i2s_keep_t *i2s_keep[MAX_I2S_NUM];
static codec_res_t codec_res;
static int _i2c_init(uint8_t port)
{
if (port >= MAX_I2C_NUM) {
return -1;
}
if (i2c_inited[port]) {
return 0;
}
codec_i2c_pin_t i2c_pin;
if (get_i2c_pin(port, &i2c_pin)) {
ESP_LOGE(TAG, "Fail to get i2c pin");
return -1;
}
// workaround to check i2c initialized already
int ret = 0;
#ifdef USE_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 = i2c_pin.scl;
i2c_bus_config.sda_io_num = i2c_pin.sda;
i2c_bus_config.glitch_ignore_cnt = 7;
i2c_bus_config.flags.enable_internal_pullup = true;
ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle[port]);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "failed to initialize I2C master bus port %d", port);
return -1;
}
ESP_LOGI(TAG, "Set mater handle %d %p", port, i2c_bus_handle[port]);
#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 = i2c_pin.sda;
i2c_cfg.scl_io_num = i2c_pin.scl;
ret = i2c_param_config(port, &i2c_cfg);
if (ret != ESP_OK) {
return -1;
}
ret = i2c_driver_install(port, i2c_cfg.mode, 0, 0, 0);
#endif
if (ret == 0) {
i2c_inited[port] = true;
}
return ret;
}
void *get_i2c_bus_handle(uint8_t port)
{
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
// Try to get port from I2C driver directly
i2c_master_bus_handle_t bus_handle = NULL;
i2c_master_get_bus_handle(port, &bus_handle);
return bus_handle;
#endif
return i2c_bus_handle[port];
}
static int _i2c_deinit(uint8_t port)
{
if (port >= MAX_I2C_NUM || i2c_inited[port] == false) {
return -1;
}
#ifdef USE_I2C_MASTER
i2c_del_master_bus(i2c_bus_handle[port]);
#else
i2c_driver_delete(port);
#endif
i2c_bus_handle[port] = NULL;
i2c_inited[port] = false;
return 0;
}
static int _i2s_init(uint8_t port, esp_codec_dev_type_t dev_type, codec_init_cfg_t *init_cfg)
{
if (port >= MAX_I2S_NUM) {
return -1;
}
if (i2s_keep[port]) {
return 0;
}
codec_i2s_pin_t i2s_cfg;
if (get_i2s_pin(port, &i2s_cfg)) {
ESP_LOGE(TAG, "Fail to get i2s pin");
return -1;
}
ESP_LOGI(TAG, "Init i2s %d type: %d mclk:%d bclk:%d ws:%d din:%d dout:%d",
port, dev_type, i2s_cfg.mclk,
i2s_cfg.bclk, i2s_cfg.ws, i2s_cfg.din, i2s_cfg.dout);
i2s_keep[port] = (i2s_keep_t *)calloc(1, sizeof(i2s_keep_t));
if (i2s_keep[port] == NULL) {
return -1;
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = i2s_cfg.mclk,
.bclk = i2s_cfg.bclk,
.ws = i2s_cfg.ws,
.dout = i2s_cfg.dout,
.din = i2s_cfg.din,
},
};
bool output = (dev_type & ESP_CODEC_DEV_TYPE_OUT) > 0;
bool input = (dev_type & ESP_CODEC_DEV_TYPE_IN) > 0;
// Same I2S have tx and rx but use rx or tx only
if (output && init_cfg->out_mode == CODEC_I2S_MODE_NONE) {
output = false;
}
if (input && init_cfg->in_mode == CODEC_I2S_MODE_NONE) {
input = false;
}
if (input == false && output == false) {
return 0;
}
#ifdef 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(32, I2S_SLOT_MODE_STEREO, slot_mask),
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
.gpio_cfg = {
.mclk = i2s_cfg.mclk,
.bclk = i2s_cfg.bclk,
.ws = i2s_cfg.ws,
.dout = i2s_cfg.dout,
.din = i2s_cfg.din,
},
};
tdm_cfg.slot_cfg.total_slot = 4;
#endif
chan_cfg.id = I2S_NUM_AUTO; // Use auto ID
int ret = i2s_new_channel(&chan_cfg, output == false ? NULL : &i2s_keep[port]->tx_handle,
input == false ? NULL : &i2s_keep[port]->rx_handle);
ESP_LOGI(TAG, "tx:%p rx:%p", i2s_keep[port]->tx_handle, i2s_keep[port]->rx_handle);
if (i2s_keep[port]->tx_handle) {
if (init_cfg->out_mode == CODEC_I2S_MODE_STD) {
ret = i2s_channel_init_std_mode(i2s_keep[port]->tx_handle, &std_cfg);
ESP_LOGI(TAG, "output init std ret %d", ret);
}
#ifdef SOC_I2S_SUPPORTS_TDM
else if (init_cfg->out_mode == CODEC_I2S_MODE_TDM) {
ret = i2s_channel_init_tdm_mode(i2s_keep[port]->tx_handle, &tdm_cfg);
ESP_LOGI(TAG, "output init tdm ret %d", ret);
}
#endif
#ifdef SOC_I2S_SUPPORTS_PDM_TX
else if (init_cfg->out_mode == CODEC_I2S_MODE_PDM) {
i2s_pdm_tx_config_t pdm_cfg = {
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(16000),
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.dout = i2s_cfg.dout,
.clk = i2s_cfg.bclk,
.invert_flags = {
.clk_inv = false,
},
},
};
ret = i2s_channel_init_pdm_tx_mode(i2s_keep[port]->tx_handle, &pdm_cfg);
ESP_LOGI(TAG, "output init pdm ret %d", ret);
}
#endif
}
if (i2s_keep[port]->rx_handle) {
if (init_cfg->in_mode == CODEC_I2S_MODE_STD) {
ret = i2s_channel_init_std_mode(i2s_keep[port]->rx_handle, &std_cfg);
ESP_LOGI(TAG, "Input init std ret %d", ret);
}
#ifdef SOC_I2S_SUPPORTS_TDM
else if (init_cfg->in_mode == CODEC_I2S_MODE_TDM) {
ret = i2s_channel_init_tdm_mode(i2s_keep[port]->rx_handle, &tdm_cfg);
ESP_LOGI(TAG, "Input init tdm ret %d", ret);
}
#endif
#ifdef SOC_I2S_SUPPORTS_PDM_RX
else if (init_cfg->in_mode == CODEC_I2S_MODE_PDM) {
i2s_pdm_rx_config_t pdm_cfg = {
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(16000),
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.din = i2s_cfg.din,
.clk = i2s_cfg.bclk,
.invert_flags = {
.clk_inv = false,
},
},
};
ret = i2s_channel_init_pdm_rx_mode(i2s_keep[port]->rx_handle, &pdm_cfg);
ESP_LOGI(TAG, "Input init pdm ret %d", ret);
}
#endif
}
// Enable I2S here for maybe some codec need I2S clock to set register correctly
if (i2s_keep[port]->tx_handle) {
i2s_channel_enable(i2s_keep[port]->tx_handle);
}
if (i2s_keep[port]->rx_handle) {
i2s_channel_enable(i2s_keep[port]->rx_handle);
}
#else
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX),
.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 = 3,
.dma_buf_len = 300,
.use_apll = true,
.tx_desc_auto_clear = true,
.fixed_mclk = 0
//.mclk_multiple = 256,
};
int ret = i2s_driver_install(port, &i2s_config, 0, NULL);
i2s_pin_config_t i2s_pin_cfg = {
.mck_io_num = i2s_cfg.mclk,
.bck_io_num = i2s_cfg.bclk,
.ws_io_num = i2s_cfg.ws,
.data_out_num = i2s_cfg.dout,
.data_in_num = i2s_cfg.din,
};
i2s_set_pin(port, &i2s_pin_cfg);
#endif
ESP_LOGI(TAG, "Init i2s %d ok", port);
i2s_keep[port]->inited = true;
return ret;
}
static int _i2s_deinit(uint8_t port)
{
if (port >= MAX_I2S_NUM) {
return -1;
}
if (i2s_keep[port] == NULL) {
return 0;
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
if (i2s_keep[port]->tx_handle) {
i2s_channel_disable(i2s_keep[port]->tx_handle);
}
if (i2s_keep[port]->rx_handle) {
i2s_channel_disable(i2s_keep[port]->rx_handle);
}
if (i2s_keep[port]->tx_handle) {
i2s_del_channel(i2s_keep[port]->tx_handle);
}
if (i2s_keep[port]->rx_handle) {
i2s_del_channel(i2s_keep[port]->rx_handle);
}
#else
i2s_driver_uninstall(port);
#endif
free(i2s_keep[port]);
i2s_keep[port] = NULL;
return 0;
}
int init_i2c(uint8_t port)
{
return _i2c_init(port);
}
int deinit_i2c(uint8_t port)
{
return _i2c_deinit(port);
}
static int check_i2c_inited(int8_t port)
{
if (port < 0) {
return 0;
}
// Already installed
if (get_i2c_bus_handle(port)) {
return 0;
}
return _i2c_init(port);
}
int init_codec(codec_init_cfg_t *cfg)
{
if (cfg == NULL) {
return -1;
}
if (codec_res.inited) {
ESP_LOGI(TAG, "Already initialized");
return 0;
}
codec_cfg_t out_cfg = { 0 };
codec_cfg_t in_cfg = { 0 };
bool has_out = false;
bool has_in = false;
// Get codec configuration
if (get_out_codec_cfg(&out_cfg) == 0) {
has_out = true;
}
if (get_in_codec_cfg(&in_cfg) == 0) {
has_in = true;
}
if (has_out == false && has_in == false) {
ESP_LOGE(TAG, "No codec device found");
return -1;
}
// Try to get I2C handle
check_i2c_inited(0);
// Init i2c and i2s
bool same_i2s = (has_in && has_out && out_cfg.i2s_port == in_cfg.i2s_port);
ESP_LOGI(TAG, "in:%d out:%d port: %d", has_in, has_out, out_cfg.i2s_port == in_cfg.i2s_port);
if (has_out) {
if (check_i2c_inited(out_cfg.i2c_port) < 0) {
ESP_LOGE(TAG, "Fail to int i2c: %d", out_cfg.i2c_port);
return -1;
}
ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port);
if (_i2s_init(out_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_OUT, cfg)) {
ESP_LOGE(TAG, "Fail to init i2s: %d", out_cfg.i2s_port);
return -1;
}
ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port);
}
if (has_in) {
if (check_i2c_inited(in_cfg.i2c_port) < 0) {
ESP_LOGE(TAG, "Fail to int i2c: %d", in_cfg.i2c_port);
return -1;
}
ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port);
if (_i2s_init(in_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_IN, cfg)) {
ESP_LOGE(TAG, "Fail to init i2s: %d", in_cfg.i2s_port);
return -1;
}
ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port);
}
// Create gpio interface
codec_res.gpio_if = audio_codec_new_gpio();
bool same_codec = same_i2s && (in_cfg.codec_type == out_cfg.codec_type);
if (has_out) {
audio_codec_i2s_cfg_t i2s_out_cfg = {
.port = out_cfg.i2s_port,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
.tx_handle = i2s_keep[out_cfg.i2s_port]->tx_handle,
.rx_handle = i2s_keep[out_cfg.i2s_port]->rx_handle,
#endif
};
ESP_LOGI(TAG, "Get out handle %p port %d", i2s_out_cfg.tx_handle, out_cfg.i2s_port);
codec_res.data_if = audio_codec_new_i2s_data(&i2s_out_cfg);
audio_codec_i2c_cfg_t i2c_cfg = {
.port = out_cfg.i2c_port,
#ifdef USE_I2C_MASTER
.bus_handle = get_i2c_bus_handle(out_cfg.i2c_port),
#endif
};
// TODO add other codec support
switch (out_cfg.codec_type) {
case CODEC_TYPE_ES8311: {
i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8311_CODEC_DEFAULT_ADDR;
codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
es8311_codec_cfg_t es8311_cfg = {
.codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC,
.ctrl_if = codec_res.out_ctrl_if,
.gpio_if = codec_res.gpio_if,
.pa_pin = out_cfg.pa_pin,
.use_mclk = out_cfg.use_mclk,
.hw_gain.pa_gain = out_cfg.pa_gain,
};
codec_res.out_codec_if = es8311_codec_new(&es8311_cfg);
} break;
case CODEC_TYPE_ES8388: {
i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8388_CODEC_DEFAULT_ADDR;
codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
es8388_codec_cfg_t es8388_cfg = {
.codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC,
.ctrl_if = codec_res.out_ctrl_if,
.gpio_if = codec_res.gpio_if,
.pa_pin = out_cfg.pa_pin,
.hw_gain.pa_gain = out_cfg.pa_gain,
};
codec_res.out_codec_if = es8388_codec_new(&es8388_cfg);
} break;
case CODEC_TYPE_DUMMY: {
dummy_codec_cfg_t dummy_cfg = {
.gpio_if = codec_res.gpio_if,
.enable_gpio = out_cfg.pa_pin,
};
codec_res.out_codec_if = dummy_codec_new(&dummy_cfg);
} break;
default:
ESP_LOGE(TAG, "TODO not supported output codec type %d", out_cfg.codec_type);
break;
}
esp_codec_dev_cfg_t dev_cfg = {
.codec_if = codec_res.out_codec_if,
.data_if = codec_res.data_if,
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
};
if (same_codec && cfg->reuse_dev) {
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT;
}
codec_res.play_dev = esp_codec_dev_new(&dev_cfg);
if (same_codec) {
if (cfg->reuse_dev) {
codec_res.record_dev = codec_res.play_dev;
} else {
// separate record from playback so that can set different fs
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN;
codec_res.record_dev = esp_codec_dev_new(&dev_cfg);
}
}
}
if (same_codec == false && has_in) {
if (same_i2s == false) {
audio_codec_i2s_cfg_t i2s_in_cfg = {
.port = in_cfg.i2s_port,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
.rx_handle = i2s_keep[in_cfg.i2s_port]->rx_handle,
#endif
};
ESP_LOGI(TAG, "Get in handle %p port %d", i2s_in_cfg.rx_handle, i2s_in_cfg.port);
codec_res.data_in_if = audio_codec_new_i2s_data(&i2s_in_cfg);
}
audio_codec_i2c_cfg_t i2c_cfg = {
.port = in_cfg.i2c_port,
#ifdef USE_I2C_MASTER
.bus_handle = get_i2c_bus_handle(in_cfg.i2c_port),
#endif
};
// TODO add other codec support
switch (in_cfg.codec_type) {
case CODEC_TYPE_ES7210: {
i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7210_CODEC_DEFAULT_ADDR;
codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
es7210_codec_cfg_t es7210_cfg = {
.ctrl_if = codec_res.in_ctrl_if,
.mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC3,
};
if (cfg->in_use_tdm || (cfg->in_mode == CODEC_I2S_MODE_TDM)) {
es7210_cfg.mic_selected |= ES7210_SEL_MIC2 | ES7210_SEL_MIC4;
}
codec_res.in_codec_if = es7210_codec_new(&es7210_cfg);
} break;
case CODEC_TYPE_ES7243: {
i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7243_CODEC_DEFAULT_ADDR;
codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
es7243_codec_cfg_t es7243_cfg = {
.ctrl_if = codec_res.in_ctrl_if,
};
codec_res.in_codec_if = es7243_codec_new(&es7243_cfg);
} break;
case CODEC_TYPE_DUMMY: {
dummy_codec_cfg_t dummy_cfg = {
.gpio_if = codec_res.gpio_if,
.enable_gpio = out_cfg.pa_pin,
};
codec_res.in_codec_if = dummy_codec_new(&dummy_cfg);
}
default:
break;
}
esp_codec_dev_cfg_t dev_cfg = {
.codec_if = codec_res.in_codec_if,
.data_if = same_i2s ? codec_res.data_if : codec_res.data_in_if,
.dev_type = ESP_CODEC_DEV_TYPE_IN,
};
codec_res.record_dev = esp_codec_dev_new(&dev_cfg);
}
// Set default volume and gain for play and record
if (codec_res.play_dev) {
esp_codec_dev_set_out_vol(codec_res.play_dev, 60.0);
}
if (codec_res.record_dev) {
esp_codec_dev_set_in_gain(codec_res.record_dev, 30.0);
}
if ((codec_res.play_dev != NULL) || (codec_res.record_dev != NULL)) {
codec_res.inited = true;
return 0;
}
return -1;
}
esp_codec_dev_handle_t get_playback_handle(void)
{
return codec_res.play_dev;
}
esp_codec_dev_handle_t get_record_handle(void)
{
return codec_res.record_dev;
}
void deinit_codec(void)
{
if (codec_res.play_dev) {
esp_codec_dev_delete(codec_res.play_dev);
if (codec_res.record_dev == codec_res.play_dev) {
codec_res.record_dev = NULL;
}
codec_res.play_dev = NULL;
}
if (codec_res.record_dev) {
esp_codec_dev_delete(codec_res.record_dev);
codec_res.record_dev = NULL;
}
// Delete codec interface
if (codec_res.in_codec_if) {
audio_codec_delete_codec_if(codec_res.in_codec_if);
codec_res.in_codec_if = NULL;
}
if (codec_res.out_codec_if) {
audio_codec_delete_codec_if(codec_res.out_codec_if);
codec_res.out_codec_if = NULL;
}
// Delete codec control interface
if (codec_res.in_ctrl_if) {
audio_codec_delete_ctrl_if(codec_res.in_ctrl_if);
codec_res.in_ctrl_if = NULL;
}
if (codec_res.out_ctrl_if) {
audio_codec_delete_ctrl_if(codec_res.out_ctrl_if);
codec_res.out_ctrl_if = NULL;
}
if (codec_res.data_if) {
audio_codec_delete_data_if(codec_res.data_if);
codec_res.data_if = NULL;
}
if (codec_res.data_in_if) {
audio_codec_delete_data_if(codec_res.data_in_if);
codec_res.data_in_if = NULL;
}
if (codec_res.gpio_if) {
audio_codec_delete_gpio_if(codec_res.gpio_if);
codec_res.gpio_if = NULL;
}
for (int i = 0; i < MAX_I2C_NUM; i++) {
_i2c_deinit(i);
_i2s_deinit(i);
}
codec_res.inited = false;
}
static sdmmc_card_t *card = NULL;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#if CONFIG_IDF_TARGET_ESP32P4
#include "esp_ldo_regulator.h"
#endif
#endif
static void enable_mmc_phy_power(void)
{
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#if CONFIG_IDF_TARGET_ESP32P4
esp_ldo_channel_config_t ldo_cfg = {
.chan_id = 4,
.voltage_mv = 3300,
};
esp_ldo_channel_handle_t ldo_phy_chan;
esp_ldo_acquire_channel(&ldo_cfg, &ldo_phy_chan);
#endif
#endif
}
#if CONFIG_IDF_TARGET_ESP32P4
static void sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config)
{
memset(config, 0, sizeof(sdmmc_slot_config_t));
config->cd = SDMMC_SLOT_NO_CD;
config->wp = SDMMC_SLOT_NO_WP;
config->width = 4;
config->flags = 0;
}
#endif
int mount_sdcard(void)
{
sdcard_cfg_t cfg = { 0 };
enable_mmc_phy_power();
int ret = get_sdcard_config(&cfg);
if (ret != 0) {
return ret;
}
#if defined CONFIG_IDF_TARGET_ESP32
gpio_config_t sdcard_pwr_pin_cfg = {
.pin_bit_mask = 1UL << GPIO_NUM_13,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&sdcard_pwr_pin_cfg);
gpio_set_level(GPIO_NUM_13, 0);
#endif
#if SOC_SDMMC_HOST_SUPPORTED
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 5,
};
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
#if CONFIG_IDF_TARGET_ESP32P4
host.slot = 0;
#endif
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
#endif
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.width = cfg.d3 ? 4 : 1;
#if SOC_SDMMC_USE_GPIO_MATRIX
slot_config.width = cfg.d3 ? 4 : 1;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
slot_config.d4 = -1;
slot_config.d5 = -1;
slot_config.d6 = -1;
slot_config.d7 = -1;
slot_config.cd = -1;
slot_config.wp = -1;
slot_config.clk = cfg.clk;
slot_config.cmd = cfg.cmd;
slot_config.d0 = cfg.d0;
slot_config.d1 = cfg.d1 ? cfg.d1 : -1;
slot_config.d2 = cfg.d2 ? cfg.d2 : -1;
slot_config.d3 = cfg.d3 ? cfg.d3 : -1;
#endif /* SOC_SDMMC_USE_GPIO_MATRIX */
#if CONFIG_IDF_TARGET_ESP32P4
sdmmc_get_slot(0, &slot_config);
#endif
printf("use %d %d %d %d\n", cfg.d0, cfg.d1, cfg.d2, cfg.d3);
return esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
#else
return -1;
#endif
}
void *get_sdcard_handle(void)
{
return card;
}
void unmount_sdcard(void)
{
if (card) {
esp_vfs_fat_sdcard_unmount("/sdcard", card);
card = NULL;
}
}

View File

@@ -0,0 +1,178 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#include <string.h>
#include "esp_log.h"
#include "tca9554.h"
#include "codec_init.h"
#include "audio_codec_ctrl_if.h"
#include "esp_codec_dev_defaults.h"
static char *TAG = "TCA9554";
#define SET_BITS(_m, _s, _v) ((_v) ? (_m)|((_s)) : (_m)&~((_s)))
#define GET_BITS(_m, _s) (((_m) & (_s)) ? true : false)
#define TCA9554_INPUT_PORT 0x00
#define TCA9554_OUTPUT_PORT 0x01
#define TCA9554_POLARITY_INVERSION_PORT 0x02
#define TCA9554_CONFIGURATION_PORT 0x03
typedef struct {
uint8_t addr;
char *name;
} tca9554_dev_t;
static tca9554_dev_t dev_list[] = {
{ 0x70, "TCA9554A"},
{ 0x40, "TCA9554"},
};
const static audio_codec_ctrl_if_t *i2c_ctrl;
static esp_err_t expander_dev_prob(uint8_t port)
{
audio_codec_i2c_cfg_t i2c_cfg = {
.port = port,
};
for (size_t i = 0; i < sizeof(dev_list) / sizeof(dev_list[0]); i++) {
i2c_cfg.addr = dev_list[i].addr;
i2c_cfg.bus_handle = get_i2c_bus_handle(port);
i2c_ctrl = audio_codec_new_i2c_ctrl(&i2c_cfg);
uint8_t data = 0;
if (i2c_ctrl->read_reg(i2c_ctrl, TCA9554_OUTPUT_PORT, 1, &data, 1) == 0) {
ESP_LOGI(TAG, "Detected IO expander device at 0x%02X, name is: %s",
dev_list[i].addr, dev_list[i].name);
return ESP_OK;
}
audio_codec_delete_ctrl_if(i2c_ctrl);
i2c_ctrl = NULL;
}
ESP_LOGE(TAG, "IO expander device has not detected");
return ESP_ERR_NOT_FOUND;
}
static esp_err_t tca9554_write_reg(uint8_t reg_addr, uint8_t data)
{
return i2c_ctrl->write_reg(i2c_ctrl, reg_addr, 1, &data, 1);
}
static char tca9554_read_reg(uint8_t reg_addr)
{
uint8_t data;
i2c_ctrl->read_reg(i2c_ctrl, reg_addr, 1, &data, 1);
return data;
}
esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num)
{
char data = 0;
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
data = tca9554_read_reg(TCA9554_INPUT_PORT);
} else {
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
return TCA9554_LEVEL_ERROR;
}
return GET_BITS(data, gpio_num);
}
esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num)
{
char data = 0;
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
data = tca9554_read_reg(TCA9554_OUTPUT_PORT);
} else {
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
return TCA9554_LEVEL_ERROR;
}
return GET_BITS(data, gpio_num);
}
esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level)
{
char data;
esp_err_t res = ESP_FAIL;
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
data = tca9554_read_reg(TCA9554_OUTPUT_PORT);
res = tca9554_write_reg(TCA9554_OUTPUT_PORT, SET_BITS(data, gpio_num, level));
} else {
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
}
return res;
}
esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity)
{
char data;
esp_err_t res = ESP_FAIL;
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
data = tca9554_read_reg(TCA9554_POLARITY_INVERSION_PORT);
res = tca9554_write_reg(TCA9554_POLARITY_INVERSION_PORT, SET_BITS(data, gpio_num, polarity));
} else {
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
}
return res;
}
esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num)
{
char data = 0;
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT);
} else {
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
return TCA9554_LEVEL_ERROR;
}
return GET_BITS(data, gpio_num);
}
esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config)
{
char data;
esp_err_t res = ESP_FAIL;
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT);
res = tca9554_write_reg(TCA9554_CONFIGURATION_PORT, SET_BITS(data, gpio_num, io_config));
} else {
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
}
return res;
}
esp_err_t tca9554_init(uint8_t port)
{
return expander_dev_prob(port);
}
esp_err_t tca9554_deinit()
{
if (i2c_ctrl) {
audio_codec_delete_ctrl_if(i2c_ctrl);
i2c_ctrl = NULL;
}
return ESP_OK;
}

View File

@@ -0,0 +1,158 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#ifndef _TCA9554_H
#define _TCA9554_H
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
TCA9554_GPIO_NUM_0 = 1 << 0,
TCA9554_GPIO_NUM_1 = 1 << 1,
TCA9554_GPIO_NUM_2 = 1 << 2,
TCA9554_GPIO_NUM_3 = 1 << 3,
TCA9554_GPIO_NUM_4 = 1 << 4,
TCA9554_GPIO_NUM_5 = 1 << 5,
TCA9554_GPIO_NUM_6 = 1 << 6,
TCA9554_GPIO_NUM_7 = 1 << 7,
TCA9554_GPIO_NUM_MAX
} esp_tca9554_gpio_num_t;
typedef enum {
TCA9554_IO_LOW,
TCA9554_IO_HIGH,
TCA9554_LEVEL_ERROR
} esp_tca9554_io_level_t;
typedef enum {
TCA9554_IO_RETAINED,
TCA9554_IO_INVERTED
} esp_tca9554_io_polarity_t;
typedef enum {
TCA9554_IO_OUTPUT,
TCA9554_IO_INPUT
} esp_tca9554_io_config_t;
/*
* @brief Initialize TCA9554 chip
*
* @param codec_cfg configuration of TCA9554
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t tca9554_init(uint8_t i2c_port);
/**
* @brief Deinitialize TCA9554 chip
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t tca9554_deinit(void);
/*
* @brief Get TCA9554 input level
*
* @param gpio_num GPIO of TCA9554
*
* @return
* - esp_tca9554_io_level_t
*/
esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num);
/*
* @brief Get PCA95xian39 output level
*
* @param gpio_num GPIO of TCA9554
*
* @return
* - esp_tca9554_io_level_t
*/
esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num);
/*
* @brief Get TCA9554 output state
*
* @param gpio_num GPIO of TCA9554
*
* @return
* - esp_tca9554_io_level_t
*/
esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level);
/*
* @brief Set TCA9554 polarity
*
* @param gpio_num GPIO of TCA9554
* polarity polarity
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity);
/*
* @brief Get TCA9554 output level
*
* @param gpio_num GPIO of TCA9554
*
* @return
* - esp_tca9554_io_level_t
*/
esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num);
/*
* @brief Set TCA9554 io config
*
* @param gpio_num GPIO of TCA9554
* io_config io config
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config);
/**
* @brief Print all TCA9554 registers
*
* @return
* - void
*/
void tca9554_read_all();
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,72 @@
#include "dummy_codec.h"
typedef struct {
audio_codec_if_t base;
const audio_codec_gpio_if_t *gpio_if;
bool is_open;
bool enable;
} dummy_codec_t;
typedef struct {
audio_codec_ctrl_if_t base;
bool is_open;
} dummy_codec_ctrl_t;
static int dummy_codec_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
dummy_codec_cfg_t *codec_cfg = (dummy_codec_cfg_t *)cfg;
if (cfg_size != sizeof(dummy_codec_cfg_t) || codec_cfg->gpio_if == NULL) {
return -1;
}
dummy_codec_t *codec = (dummy_codec_t *)h;
codec->gpio_if = codec_cfg->gpio_if;
codec->gpio_if->setup(codec_cfg->enable_gpio, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->gpio_if->set(codec_cfg->enable_gpio, true);
codec->is_open = true;
return 0;
}
static bool dummy_codec_is_open(const audio_codec_if_t *h)
{
dummy_codec_t *codec = (dummy_codec_t *)h;
return codec->is_open;
}
static int dummy_codec_enable(const audio_codec_if_t *h, bool enable)
{
dummy_codec_t *codec = (dummy_codec_t *)h;
codec->enable = enable;
return 0;
}
static int dummy_codec_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
return 0;
}
static int dummy_codec_close(const audio_codec_if_t *h)
{
dummy_codec_t *codec = (dummy_codec_t *)h;
// Auto disable when codec closed
if (codec->enable) {
dummy_codec_enable(h, false);
}
codec->is_open = false;
return 0;
}
const audio_codec_if_t *dummy_codec_new(dummy_codec_cfg_t *codec_cfg)
{
dummy_codec_t *codec = (dummy_codec_t *)calloc(1, sizeof(dummy_codec_t));
if (codec == NULL) {
return NULL;
}
codec->base.open = dummy_codec_open;
codec->base.is_open = dummy_codec_is_open;
codec->base.enable = dummy_codec_enable;
codec->base.set_fs = dummy_codec_set_fs;
codec->base.close = dummy_codec_close;
codec->base.open(&codec->base, codec_cfg, sizeof(dummy_codec_cfg_t));
return &codec->base;
}

View File

@@ -0,0 +1,33 @@
#include "esp_codec_dev.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Dummy codec configuration
*/
typedef struct {
int16_t enable_gpio; /*!< Enable GPIO setting */
const audio_codec_gpio_if_t *gpio_if; /*!< GPIO interface to control gpio */
} dummy_codec_cfg_t;
/**
* @brief Create a dummy codec
*
* @note Dummy codec means use I2S to transfer audio data with out I2C control interface
*
* @param[in] codec_cfg Dummy codec configuration
*
* @return
* - NULL No memory for dummy codec
* - Others Dummy codec instance
*
*/
const audio_codec_if_t *dummy_codec_new(dummy_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,12 @@
dependencies:
esp_lcd_ek79007:
rules:
- if: target in [esp32p4]
version: ~1.0.2
espressif/esp_codec_dev: ~1.4.0
espressif/esp_lcd_ili9881c:
rules:
- if: target in [esp32p4]
version: ~1.0.1
description: Simple ESP32 series codec board realization
version: 0.5.5

View File

@@ -0,0 +1,346 @@
/**
* ESPRESSIF MIT License
*
* Copyright (c) 2025 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Maximum of supported I2C number
*/
#define MAX_I2C_NUM (2)
/**
* @brief Maximum of supported I2S number
*/
#define MAX_I2S_NUM (2)
/**
* @brief Maximum of supported codec number
*/
#define MAX_CODEC_NUM (2)
/**
* @brief Codec input and output direction
*/
#define CODEC_DIR_IN (1 << 0)
#define CODEC_DIR_OUT (1 << 1)
#define CODEC_DIR_IN_OUT (CODEC_DIR_IN | CODEC_DIR_OUT)
/**
* @brief Start of extended GPIO start
*/
#define BOARD_EXTEND_IO_START (0x1000)
/**
* @brief Codec type
*/
typedef enum {
CODEC_TYPE_NONE, /*!< Codec type none */
CODEC_TYPE_ES8311, /*!< ES8311 codec type */
CODEC_TYPE_ES7210, /*!< ES7210 codec type */
CODEC_TYPE_ES7243, /*!< ES7243 codec type */
CODEC_TYPE_ES8388, /*!< ES8388 codec type */
CODEC_TYPE_DUMMY, /*!< Dummy codec type (which have I2S interface only) */
} codec_type_t;
/**
* @brief Camera type
*/
typedef enum {
CAMERA_TYPE_NONE, /*!< Camera type none */
CAMERA_TYPE_DVP, /*!< DVP camera type */
CAMERA_TYPE_USB, /*!< USB camera type */
CAMERA_TYPE_MIPI /*!< MIPI camera type */
} camera_type_t;
/**
* @brief I2C pin setting
*/
typedef struct {
int16_t sda; /*!< GPIO for SDA */
int16_t scl; /*!< GPIO for SCL */
} codec_i2c_pin_t;
/**
* @brief I2S pin setting
*/
typedef struct {
int16_t mclk; /*!< GPIO for MCLK */
int16_t bclk; /*!< GPIO for BCLK */
int16_t ws; /*!< GPIO for Word Selction */
int16_t dout; /*!< GPIO for digital output */
int16_t din; /*!< GPIO for digital input */
} codec_i2s_pin_t;
/**
* @brief Codec configuration
*/
typedef struct {
codec_type_t codec_type; /*!< Codec type */
int16_t pa_pin; /*!< GPIO for PA control */
float pa_gain; /*!< PA gain */
uint8_t i2c_addr; /*!< I2C address */
int8_t i2c_port; /*!< I2C port */
int8_t i2s_port; /*!< I2S port */
bool use_mclk; /*!< Whether codec need MCLK clock */
} codec_cfg_t;
/**
* @brief Sdcard configuration
*/
typedef struct {
int16_t clk; /*!< GPIO for clock */
int16_t cmd; /*!< GPIO for command */
int16_t d0; /*!< GPIO for d0 */
int16_t d1; /*!< GPIO for d1 (if only one line need set to -1) */
int16_t d2; /*!< GPIO for d2 (if only one line need set to -1) */
int16_t d3; /*!< GPIO for d3 (if only one line need set to -1) */
int16_t power; /*!< GPIO for power */
} sdcard_cfg_t;
/**
* @brief Camera configuration
*/
typedef struct {
camera_type_t type; /*!< Camera type */
int16_t pwr; /*!< GPIO for power */
int16_t reset; /*!< GPIO for reset */
int16_t xclk; /*!< GPIO for XCLK */
int16_t pclk; /*!< GPIO for PCLK */
int16_t vsync; /*!< GPIO for VSYNC */
int16_t de; /*!< GPIO for DE */
int16_t href; /*!< GPIO for HREF */
int16_t data[16]; /*!< GPIO for DATA */
} camera_cfg_t;
/**
* @brief Codec setting
*/
typedef struct {
codec_cfg_t codec_cfg; /*!< Codec configuration */
uint8_t codec_dir; /*!< Codec direction */
} codec_setting_t;
/**
* @brief Extent IO board type
*/
typedef enum {
EXTENT_IO_TYPE_NONE, /*!< NOne extent IO */
EXTENT_IO_TYPE_TCA9554, /*!< TCA9554 extent IO type */
} extend_io_type_t;
/**
* @brief LCD controller type
*/
typedef enum {
LCD_CONTROLLER_TYPE_NONE, /*!< None controller type */
LCD_CONTROLLER_TYPE_ST7789, /*!< ST7789 controller type */
} lcd_controller_type_t;
/**
* @brief LCD bus type
*/
typedef enum {
LCD_BUS_TYPE_NONE, /*!< None LCD bus type */
LCD_BUS_TYPE_SPI, /*!< SPI LCD bus type */
LCD_BUS_TYPE_RGB, /*!< RGB LCD bus type */
LCD_BUS_TYPE_I80, /*!< I80 LCD bus type */
LCD_BUS_TYPE_MIPI, /*!< MIPI LCD bus type */
} lcd_bus_type_t;
/**
* @brief SPI LCD configuration
*/
typedef struct {
uint8_t spi_bus; /*!< SPI bus number */
int pclk_clk; /*!< PCLK clock */
uint8_t cmd_bits; /*!< Command bit width */
uint8_t param_bits; /*!< Parameter bit width */
int16_t cs; /*!< CS GPIO */
int16_t dc; /*!< DC GPIO */
int16_t clk; /*!< Clock GPIO */
int16_t mosi; /*!< MOSI GPIO */
int16_t d[7]; /*!< Data GPIOs */
} lcd_spi_cfg_t;
/**
* @brief MIPI LCD configuration
*/
typedef struct {
uint8_t ldo_chan;
uint16_t ldo_voltage;
uint8_t lane_num;
uint32_t lane_bitrate; // Mbps
uint32_t dpi_clk; // MHz
uint8_t bit_depth;
uint8_t fb_num;
uint8_t dsi_hsync;
uint8_t dsi_vsync;
uint8_t dsi_hbp;
uint8_t dsi_hfp;
uint8_t dsi_vbp;
uint8_t dsi_vfp;
} lcd_mipi_cfg_t;
/**
* @brief LCD configuration
*/
typedef struct {
extend_io_type_t io_type;
uint8_t io_i2c_port;
lcd_bus_type_t bus_type;
lcd_controller_type_t controller;
int width;
int height;
uint8_t i2c_port;
uint8_t mirror_x : 1;
uint8_t mirror_y : 1;
uint8_t swap_xy : 1;
uint8_t color_inv : 1;
int16_t ctrl_pin;
int16_t reset_pin;
union {
lcd_spi_cfg_t spi_cfg;
lcd_mipi_cfg_t mipi_cfg;
};
} lcd_cfg_t;
/**
* @brief Board section
*/
typedef struct {
codec_i2c_pin_t i2c_pin[MAX_I2C_NUM];
codec_i2s_pin_t i2s_pin[MAX_I2S_NUM];
codec_setting_t codec[MAX_CODEC_NUM];
lcd_cfg_t lcd;
sdcard_cfg_t sdcard;
camera_cfg_t camera;
uint8_t i2c_num;
uint8_t i2s_num;
uint8_t codec_num;
uint8_t sdcard_num;
uint8_t lcd_num;
uint8_t camera_num;
} board_section_t;
/**
* @brief Set codec board type
*
* @param[in] board_type Board name selection
*/
void set_codec_board_type(const char *board_type);
/**
* @brief Get SDCard configuration
*
* @param[out] card_cfg SDCard configuration to store
*
* @return
* - 0 On success
* - -1 Not exists
*
*/
int get_sdcard_config(sdcard_cfg_t *card_cfg);
/**
* @brief Get I2C pin setting by port
*
* @param[in] port I2C port
* @param[out] i2c_pin I2C pin setting
*
* @return
* - 0 On success
* - -1 Not exists
*
*/
int get_i2c_pin(uint8_t port, codec_i2c_pin_t *i2c_pin);
/**
* @brief Get I2S pin setting by port
*
* @param[in] port I2S port
* @param[out] i2s_pin I2S pin setting
*
* @return
* - 0 On success
* - -1 Not exists
*/
int get_i2s_pin(uint8_t port, codec_i2s_pin_t *i2s_pin);
/**
* @brief Get output codec configuration
*
* @param[out] out_cfg Output codec configuration to store
*
* @return
* - 0 On success
* - -1 Not exists
*/
int get_out_codec_cfg(codec_cfg_t *out_cfg);
/**
* @brief Get input codec configuration
*
* @param[out] in_cfg Input codec configuration to store
*
* @return
* - 0 On success
* - -1 Not exists
*/
int get_in_codec_cfg(codec_cfg_t *in_cfg);
/**
* @brief Get LCD configuration
*
* @param[out] lcd_cfg LCD configuration to store
*
* @return
* - 0 On success
* - -1 Not exists
*/
int get_lcd_cfg(lcd_cfg_t *lcd_cfg);
/**
* @brief Get camera configuration
*
* @param[out] cam_cfg Camera configuration to store
*
* @return
* - 0 On success
* - -1 Not exists
*/
int get_camera_cfg(camera_cfg_t *cam_cfg);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,168 @@
/**
* ESPRESSIF MIT License
*
* Copyright (c) 2025 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#pragma once
#include "esp_codec_dev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2S mode
*/
typedef enum {
CODEC_I2S_MODE_STD = 0, /*!< STD mode */
CODEC_I2S_MODE_TDM, /*!< TDM mode */
CODEC_I2S_MODE_PDM, /*!< PDM mode */
CODEC_I2S_MODE_NONE, /*!< NONE means not support */
} codec_i2s_mode_t;
/**
* @brief Codec initialize configuration
*/
typedef struct {
codec_i2s_mode_t in_mode; /*!< I2S input mode */
codec_i2s_mode_t out_mode; /*!< I2S output mode */
bool in_use_tdm; /*!< Whether codec use TDM mode for input (codec use TDM, I2S can use STD mode) */
bool reuse_dev; /*!< Use same handle for both input and output */
} codec_init_cfg_t;
/**
* @brief Initialize I2C driver
*
* @note When user want to get I2C bus handle either use `get_i2c_bus_handle`
* Or use IDF API `i2c_master_get_bus_handle` on IDFv5.4 or high
*
* @param[in] port I2C port
*
* @return
* - 0 On success
* - Others Fail to initialize
*/
int init_i2c(uint8_t port);
/**
* @brief Deinitialize I2C driver
*
* @note Attention make sure no driver use I2C bus anymore before do this action
*
* @param[in] port I2C port
*
* @return
* - 0 On success
* - Others Fail to initialize
*/
int deinit_i2c(uint8_t port);
/**
* @brief Initialize codec
*
* @param[in] cfg Codec initialize configuration
*
* @return
* - 0 On success
* - Others Fail to initialize
*/
int init_codec(codec_init_cfg_t *cfg);
/**
* @brief Get `esp_codec_dev` handle for playback
*
* @return
* - NULL Fail to get playback handle
* - Others Playback `esp_codec_dev` handle
*/
esp_codec_dev_handle_t get_playback_handle(void);
/**
* @brief Get `esp_codec_dev` handle for record
*
* @return
* - NULL Fail to get record handle
* - Others Record `esp_codec_dev` handle
*/
esp_codec_dev_handle_t get_record_handle(void);
/**
* @brief Get I2C master bus handle by port
*
* @param[in] port I2C bus port
*
* @return
* - NULL Fail to get I2C bus handle
* - Others I2C bus handle
*/
void *get_i2c_bus_handle(uint8_t port);
/**
* @brief Mount SDCard
*
* @return
* - 0 On success
* - Others Fail to mount
*/
int mount_sdcard(void);
/**
* @brief Get mounted SDCard handle
*
* @return
* - 0 On success
* - Others Fail to mount
*/
void *get_sdcard_handle(void);
/**
* @brief Unmount SDCard
*/
void unmount_sdcard(void);
/**
* @brief Deinitialize codec
*/
void deinit_codec(void);
/**
* @brief Initialized for LCD
*
* @return
* - 0 On success
* - Others Fail to init LCD
*/
int board_lcd_init(void);
/**
* @brief Get LCD handle
*
* @return
* - NULL Fail to get LCD handle
* - Others LCD handle
*/
void *board_get_lcd_handle(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,378 @@
#include "codec_board.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "tca9554.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#include "esp_lcd_panel_dev.h"
#include "esp_lcd_panel_st7789.h"
#else
#include "esp_lcd_panel_vendor.h"
#endif
#if CONFIG_IDF_TARGET_ESP32P4
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_lcd_ili9881c.h"
#include "esp_ldo_regulator.h"
#include "soc/mipi_dsi_bridge_struct.h"
#include "esp_lcd_ek79007.h"
#endif
#include "freertos/FreeRTOS.h"
#define TAG "LCD_INIT"
#define RETURN_ON_ERR(ret) if (ret != 0) { \
ESP_LOGE(TAG, "Fail to run on %d ret %d", __LINE__, ret); \
return ret; \
}
typedef struct {
int (*init)(lcd_cfg_t *cfg);
int (*set_dir)(int16_t gpio, bool output);
int (*set_gpio)(int16_t gpio, bool high);
} extend_io_ops_t;
static extend_io_ops_t extend_io_ops;
static esp_lcd_panel_handle_t panel_handle = NULL;
static int tca9554_io_init(lcd_cfg_t *cfg)
{
return tca9554_init(cfg->io_i2c_port);
}
static int tca9554_io_set_dir(int16_t gpio, bool output)
{
gpio = (1 << gpio);
tca9554_set_io_config(gpio, output ? TCA9554_IO_OUTPUT : TCA9554_IO_INPUT);
return 0;
}
static int tca9554_io_set(int16_t gpio, bool high)
{
gpio = (1 << gpio);
return tca9554_set_output_state(gpio, high ? TCA9554_IO_HIGH : TCA9554_IO_LOW);
}
static void register_tca9554(void)
{
extend_io_ops.init = tca9554_io_init;
extend_io_ops.set_dir = tca9554_io_set_dir;
extend_io_ops.set_gpio = tca9554_io_set;
}
static int init_extend_io(lcd_cfg_t *cfg)
{
if (cfg->io_type == EXTENT_IO_TYPE_NONE) {
return 0;
}
switch (cfg->io_type) {
case EXTENT_IO_TYPE_TCA9554:
register_tca9554();
break;
default:
return -1;
}
return extend_io_ops.init(cfg);
}
static int set_pin_dir(int16_t pin, bool output)
{
if (pin & BOARD_EXTEND_IO_START) {
pin &= ~BOARD_EXTEND_IO_START;
extend_io_ops.set_dir(pin, output);
} else {
gpio_config_t bk_gpio_config = {
.mode = output ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT,
.pin_bit_mask = pin > 0 ? 1ULL << pin : 0ULL,
};
gpio_config(&bk_gpio_config);
}
return 0;
}
static int set_pin_state(int16_t pin, bool high)
{
if (pin & BOARD_EXTEND_IO_START) {
extend_io_ops.set_gpio(pin, high);
} else {
gpio_set_level(pin, true);
}
return 0;
}
static int16_t get_hw_gpio(int16_t pin)
{
if (pin == -1) {
return pin;
}
if (pin & BOARD_EXTEND_IO_START) {
return -1;
}
return pin;
}
static void sleep_ms(int ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}
static int _lcd_rest(lcd_cfg_t *cfg)
{
if (cfg->reset_pin >= 0) {
set_pin_state(cfg->reset_pin, false);
sleep_ms(100);
set_pin_state(cfg->reset_pin, true);
}
return 0;
}
static int _init_spi_lcd(lcd_cfg_t *cfg)
{
int ret = 0;
if (cfg->spi_cfg.cs & BOARD_EXTEND_IO_START) {
set_pin_dir(cfg->spi_cfg.cs, true);
sleep_ms(10);
set_pin_dir(cfg->spi_cfg.cs, false);
sleep_ms(10);
}
spi_bus_config_t buscfg = {
.sclk_io_num = cfg->spi_cfg.clk,
.mosi_io_num = cfg->spi_cfg.mosi,
.miso_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = cfg->width * cfg->height * 2,
};
#if SOC_SPI_SUPPORT_OCT
if (cfg->spi_cfg.d[6] >= 0) {
buscfg.data1_io_num = cfg->spi_cfg.d[0];
buscfg.data2_io_num = cfg->spi_cfg.d[1];
;
buscfg.data3_io_num = cfg->spi_cfg.d[2];
;
buscfg.data4_io_num = cfg->spi_cfg.d[3];
;
buscfg.data5_io_num = cfg->spi_cfg.d[4];
;
buscfg.data6_io_num = cfg->spi_cfg.d[5];
;
buscfg.data7_io_num = cfg->spi_cfg.d[6];
;
buscfg.flags = SPICOMMON_BUSFLAG_OCTAL;
}
#endif
int bus_id = SPI1_HOST + (cfg->spi_cfg.spi_bus - 1);
ret = spi_bus_initialize(bus_id, &buscfg, SPI_DMA_CH_AUTO);
ESP_LOGI(TAG, "CLK %d MOSI %d CS:%d DC: %d Bus:%d",
cfg->spi_cfg.clk, cfg->spi_cfg.mosi,
get_hw_gpio(cfg->spi_cfg.cs), cfg->spi_cfg.dc,
bus_id);
RETURN_ON_ERR(ret);
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = cfg->spi_cfg.dc,
.cs_gpio_num = get_hw_gpio(cfg->spi_cfg.cs),
.pclk_hz = cfg->spi_cfg.pclk_clk ? cfg->spi_cfg.pclk_clk : 60 * 1000 * 1000,
.spi_mode = 0,
.trans_queue_depth = 10,
.lcd_cmd_bits = cfg->spi_cfg.cmd_bits ? cfg->spi_cfg.cmd_bits : 8,
.lcd_param_bits = cfg->spi_cfg.param_bits ? cfg->spi_cfg.param_bits : 8,
.on_color_trans_done = NULL,
.user_ctx = NULL,
};
#if SOC_SPI_SUPPORT_OCT
if (cfg->spi_cfg.d[6] >= 0) {
io_config.flags.octal_mode = 1;
io_config.spi_mode = 3;
}
#endif
esp_lcd_panel_io_handle_t io_handle;
ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)bus_id, &io_config, &io_handle);
RETURN_ON_ERR(ret);
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = get_hw_gpio(cfg->reset_pin),
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
.rgb_ele_order = ESP_LCD_COLOR_SPACE_BGR,
#else
.rgb_endian = LCD_RGB_ENDIAN_BGR,
#endif
.bits_per_pixel = 16,
};
switch (cfg->controller) {
default:
return -1;
case LCD_CONTROLLER_TYPE_ST7789:
ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle);
RETURN_ON_ERR(ret);
ESP_LOGI(TAG, "Init driver ST7789 finished");
break;
}
return ret;
}
#if CONFIG_IDF_TARGET_ESP32P4
static int power_on_dsi(lcd_mipi_cfg_t *mipi_cfg)
{
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
esp_ldo_channel_config_t ldo_mipi_phy_config = {
.chan_id = mipi_cfg->ldo_chan,
.voltage_mv = mipi_cfg->ldo_voltage,
};
return esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy);
}
static int _init_mipi_lcd(lcd_cfg_t *cfg)
{
int ret = 0;
lcd_mipi_cfg_t *mipi_cfg = &cfg->mipi_cfg;
power_on_dsi(mipi_cfg);
// create MIPI DSI bus first, it will initialize the DSI PHY as well
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0,
.num_data_lanes = mipi_cfg->lane_num,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = mipi_cfg->lane_bitrate,
};
ret = esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
RETURN_ON_ERR(ret);
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
esp_lcd_panel_io_handle_t mipi_dbi_io;
// we use DBI interface to send LCD commands and parameters
esp_lcd_dbi_io_config_t dbi_config = {
.virtual_channel = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
};
ret = esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io);
RETURN_ON_ERR(ret);
esp_lcd_dpi_panel_config_t dpi_config = {
.num_fbs = mipi_cfg->fb_num,
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = mipi_cfg->dpi_clk,
.pixel_format = mipi_cfg->bit_depth == 24 ? LCD_COLOR_PIXEL_FORMAT_RGB888 : LCD_COLOR_PIXEL_FORMAT_RGB565,
.video_timing = {
.h_size = cfg->width,
.v_size = cfg->height,
.hsync_back_porch = mipi_cfg->dsi_hbp,
.hsync_pulse_width = mipi_cfg->dsi_hsync,
.hsync_front_porch = mipi_cfg->dsi_hfp,
.vsync_back_porch = mipi_cfg->dsi_vbp,
.vsync_pulse_width = mipi_cfg->dsi_vsync,
.vsync_front_porch = mipi_cfg->dsi_vfp,
},
.flags.use_dma2d = true,
};
esp_lcd_panel_dev_config_t panel_config = {
.bits_per_pixel = mipi_cfg->bit_depth,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.reset_gpio_num = cfg->reset_pin,
};
if (cfg->width == 1024 && cfg->height == 600) {
ESP_LOGI(TAG, "Install EK79007 LCD control panel");
esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
ek79007_vendor_config_t vendor_config = {
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
};
panel_config.vendor_config = &vendor_config;
ret = esp_lcd_new_panel_ek79007(mipi_dbi_io, &panel_config, &panel_handle);
RETURN_ON_ERR(ret);
} else {
ili9881c_vendor_config_t vendor_config = {
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
.lane_num = mipi_cfg->lane_num,
},
};
panel_config.vendor_config = &vendor_config;
ret = esp_lcd_new_panel_ili9881c(mipi_dbi_io, &panel_config, &panel_handle);
}
RETURN_ON_ERR(ret);
esp_lcd_panel_reset(panel_handle);
ESP_LOGI(TAG, "Install MIPI DSI LCD data panel");
return ret;
}
#else
static int _init_mipi_lcd(lcd_cfg_t *cfg)
{
return -1;
}
#endif
static int _init_lcd(lcd_cfg_t *cfg)
{
int ret = 0;
if (cfg->io_type != EXTENT_IO_TYPE_NONE) {
ret = init_extend_io(cfg);
if (ret != 0) {
return ret;
}
}
// Config reset and ctrl gpio dir
if (cfg->reset_pin >= 0) {
set_pin_dir(cfg->reset_pin, true);
}
if (cfg->ctrl_pin >= 0) {
set_pin_dir(cfg->ctrl_pin, true);
}
if (cfg->bus_type == LCD_BUS_TYPE_SPI) {
if (cfg->spi_cfg.cs >= 0) {
set_pin_dir(cfg->spi_cfg.cs, true);
}
}
_lcd_rest(cfg);
if (cfg->ctrl_pin >= 0) {
set_pin_dir(cfg->ctrl_pin, true);
}
if (cfg->bus_type == LCD_BUS_TYPE_SPI) {
ret = _init_spi_lcd(cfg);
} else if (cfg->bus_type == LCD_BUS_TYPE_MIPI) {
ret = _init_mipi_lcd(cfg);
}
if (panel_handle) {
ret = esp_lcd_panel_init(panel_handle);
RETURN_ON_ERR(ret);
if (cfg->color_inv) {
ret = esp_lcd_panel_invert_color(panel_handle, cfg->color_inv);
}
// ret = esp_lcd_panel_set_gap(panel_handle, 0, 0);
if (cfg->swap_xy) {
ret = esp_lcd_panel_swap_xy(panel_handle, cfg->swap_xy);
}
if (cfg->mirror_x || cfg->mirror_y) {
ret = esp_lcd_panel_mirror(panel_handle, cfg->mirror_x, cfg->mirror_y);
}
ret = esp_lcd_panel_disp_on_off(panel_handle, true);
}
return ret;
}
int board_lcd_init(void)
{
lcd_cfg_t cfg = { 0 };
int ret = get_lcd_cfg(&cfg);
if (ret != 0) {
return ret;
}
return _init_lcd(&cfg);
}
void *board_get_lcd_handle(void)
{
if (panel_handle) {
return panel_handle;
}
return NULL;
}