diff options
Diffstat (limited to 'yocto')
29 files changed, 1547 insertions, 683 deletions
diff --git a/yocto/README b/yocto/README index f170e18..8e63844 100644 --- a/yocto/README +++ b/yocto/README @@ -7,6 +7,8 @@ https://developer.toradex.com/linux-bsp/os-development/build-yocto/build-a-refer https://developer.toradex.com/linux-bsp/os-development/build-u-boot-and-linux-kernel-from-source-code/build-u-boot/ https://developer.toradex.com/linux-bsp/os-development/build-u-boot-and-linux-kernel-from-source-code/build-linux-kernel-from-source-code/ https://developer.toradex.com/linux-bsp/os-development/build-u-boot-and-linux-kernel-from-source-code/build-device-tree-overlays-from-source-code/ +https://developer.toradex.com/linux-bsp/os-development/build-yocto/custom-meta-layers-recipes-and-images-in-yocto-project-hello-world-examples/ +https://developer.toradex.com/linux-bsp/os-development/build-yocto/device-tree-overlays-linux - install dependencies: # dependencies for yocto: @@ -43,12 +45,9 @@ curl https://commondatastorage.googleapis.com/git-repo-downloads/repo > bin/repo chmod 755 bin/repo - install the ARM GCC toolchain: -mkdir arm -cd arm wget https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz tar xvf arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz ln -s arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-linux-gnu gcc-linaro-aarch64 -cd .. - install device tree compiler tool: git clone https://git.kernel.org/pub/scm/utils/dtc/dtc.git -b v1.7.2 @@ -74,6 +73,7 @@ yocto image ----------- - clone meta-information +cd /build mkdir oe-core cd oe-core # branch: @@ -95,40 +95,79 @@ MACHINE ?= "verdin-imx8mp" # set debian packages PACKAGE_CLASSES ?= "package_deb" ... -# append: -ACCEPT_FSL_EULA = "1" -TOOLCHAIN_TARGET_TASK:append = " kernel-devsrc" -TOOLCHAIN_TARGET_TASK:remove = "target-sdk-provides-dummy" +# set distro +DISTRO ?= "mikrophone" ... +# append +ACCEPT_FSL_EULA = "1" - append mikrophone layer: vi conf/bblayers.conf # append to BBLAYERS variable: +BBLAYERS ?= " \ ... ${TOPDIR}/../layers/meta-mikrophone \ " # copy yocto/meta-mikrophone to ../layers/ dir from mikroPhone repo -- build: -# bitbake -k tdx-reference-minimal-image -# bitbake -k tdx-reference-minimal-image -c populate_sdk +- build image: bitbake -k mikrophone-image bitbake -k mikrophone-image -c populate_sdk - install sdk: -deploy/sdk/tdx-xwayland-glibc-x86_64-mikroPhone-Image-armv8a-verdin-imx8mp-toolchain-7.x.y.sh +deploy/sdk/mikrophone-glibc-x86_64-mikroPhone-Image-armv8a-verdin-imx8mp-toolchain-7.x.y.sh # install into: /build/tdx-xwayland/7.x.y +- prepare sdk for building kernel modules: +vi /build/tdx-xwayland/7.x.y/environment-setup-armv8a-tdx-linux +... +# append +export KERNEL_SRC="$SDKTARGETSYSROOT/lib/modules/6.x.y-$OECORE_SDK_VERSION-devel/build" +... + +. /build/tdx-xwayland/7.x.y/environment-setup-armv8a-tdx-linux +cd /build/tdx-xwayland/7.x.y/sysroots/armv8a-tdx-linux/usr/lib/modules/6.x.y-7.x.y-devel/build +make prepare + - machine.conf in: layers/meta-toradex-nxp/conf/machine/verdin-imx8mp.conf -- mikrophone distro.conf in: layers/meta-mikrophone/conf/distro/*.conf -- mikrophone images in: layers/meta-mikrophone/recipes-images/images/*.bb + layers/meta-mikrophone/conf/machine/include/verdin-imx8mp.inc +- mikrophone distro.conf in: layers/meta-mikrophone/conf/distro/mikrophone.conf +- mikrophone images in: layers/meta-mikrophone/recipes-images/images/mikrophone-image.bb + - deployable tarballs in: build/deploy/images/verdin-imx8mp/ - deployable sdk in: build/deploy/sdk/ - toradex distro.conf in: layers/meta-toradex-distro/conf/distro/*.conf - toradex demo images in: layers/meta-toradex-demos/recipes-images/images/*.bb + +upgrade yocto image +------------------- + +- upgrade yocto: +. /build/tools/start.sh +cd /build/oe-core +repo sync + +- build image: +. export +bitbake -k mikrophone-image +bitbake -k mikrophone-image -c populate_sdk + +- to launch TEZI installer: insert sdcard, reboot linux then stop u-boot auto boot and type: +setenv fdtfile imx8mp-verdin-wifi-dev.dtb +boot + + +install image (Tezi) +-------------------- + +Verdin iMX8MP # editenv fdtfile +edit: imx8mp-verdin-wifi-dev.dtb +Verdin iMX8MP # boot + + u-boot ------ @@ -176,74 +215,72 @@ git clone -b toradex_6.6-2.1.x-imx git://git.toradex.com/linux-toradex.git cd linux-toradex git checkout 3493ccd66900420e2462daf4db187b315b50469e cp ../oe-core/build/deploy/images/verdin-imx8mp/kernel-config .config +# enable vivante gpu driver +# should be exactly the same as kernel module from NXP's linux-imx buit by oe-core +# diff -ur ../linux-imx/drivers/mxc/gpu-viv drivers/mxc/gpu-viv +vi .config +... +CONFIG_MXC_GPU_VIV=m +... # verify config make olddefconfig # copy yocto/kernel_lvds_freq.patch from mikroPhone repo # apply LVDS patch patch < ../kernel_lvds_freq.patch -# build kernel and dtb -make -j$(nproc) Image.gz 2>&1 | tee build.log -make freescale/imx8mp-verdin-wifi-dev.dtb +# build kernel (remove + from kernel version string) +make LOCALVERSION= -j$(nproc) Image.gz 2>&1 | tee build.log # ls ./arch/arm64/boot/Image.gz -# ls ./arch/arm64/boot/dts/freescale/imx8mp-verdin-wifi-dev.dtb # build kernel modules -make -j$(nproc) modules 2>&1 | tee build.log -mkdir modules -INSTALL_MOD_PATH=`pwd`/modules make modules_install -cd modules -tar -czf ../modules.tar.gz . -cd .. - +make LOCALVERSION= -j$(nproc) modules 2>&1 | tee build.log +# copy Module.symvers if there are some additional function exports: EXPORT_SYMBOL_GPL(...) +cp Module.symvers /build/tdx-xwayland/7.x.y/sysroots/armv8a-tdx-linux/usr/lib/modules/6.x.y-7.x.y-devel/build/ +make INSTALL_MOD_PATH=modules modules_install +# build dtb +cp ../oe-core/layers/meta-mikrophone/recipes-kernel/linux/linux-toradex/*.dts arch/arm64/boot/dts/freescale/ +cp ../oe-core/layers/meta-mikrophone/recipes-kernel/linux/linux-toradex/*.dtsi arch/arm64/boot/dts/freescale/ +make DTC_FLAGS="-@" freescale/imx8mp-verdin-nonwifi-mikrophone.dtb +make DTC_FLAGS="-@" freescale/imx8mp-verdin-wifi-mikrophone.dtb +# ls ./arch/arm64/boot/dts/freescale/*.dtb +# deploy +tar -czf modules.tar.gz modules/ +scp ./arch/arm64/boot/Image.gz root@mikrophone:/boot/ +scp ./arch/arm64/boot/dts/freescale/*.dtb root@mikrophone:/boot/ +scp modules.tar.gz root@mikrophone:. + + +esp32mod +-------- -overlay -------- +- setup environment: +. /build/tdx-xwayland/7.x.y/environment-setup-armv8a-tdx-linux -# toradex overlays (match branch to kernel branch) -# git clone -b toradex_6.6-2.1.x-imx git://git.toradex.com/device-tree-overlays.git -# copy yocto/overlays from mikroPhone repo -cd overlays -STAGING_KERNEL_DIR=../linux-toradex make mikroPhone-panel_overlay.dtbo +- build: +# copy yocto/esp32mod from mikroPhone repo +cd esp32mod +make cd .. -esp32d ------- +esp32tun +-------- - setup environment: . /build/tdx-xwayland/7.x.y/environment-setup-armv8a-tdx-linux - build: -# copy yocto/esp32d from mikroPhone repo -cd esp32d +# copy yocto/esp32tun from mikroPhone repo +cd esp32tun make cd .. -debian repository ------------------ - -- install aptly and configure aptly: -apt-get install aptly gnupg1 gpgv1 -aptly # creates config file -vi ~/.aptly.conf -... - "gpgProvider": "internal", -... - "FileSystemPublishEndpoints": { - "mikrophone": { - "rootDir": "/build/repo", - "linkMethod": "copy", - "verifyMethod": "md5" - } - }, -... - -- generate gpg signing key: -gpg1 --gen-key -gpg1 --export --armor # signing key for apt-key add +overlays +-------- -- create and publish repository: -aptly repo create -distribution=koshuta -component=main mikrophone -aptly repo add mikrophone /build/oe-core/build/deploy/deb -aptly publish repo mikrophone filesystem:mikrophone: -aptly publish update koshuta filesystem:mikrophone: +# toradex overlays (match branch to kernel branch) +# git clone -b toradex_6.6-2.1.x-imx git://git.toradex.com/device-tree-overlays.git +# copy yocto/overlays from mikroPhone repo +cd overlays +STAGING_KERNEL_DIR=../linux-toradex make mikroPhone-panel_overlay.dtbo +STAGING_KERNEL_DIR=../linux-toradex make mikroPhone-esp32_overlay.dtbo +cd .. diff --git a/yocto/esp32d/msgq.h b/yocto/esp32d/msgq.h deleted file mode 100644 index 32b20d0..0000000 --- a/yocto/esp32d/msgq.h +++ /dev/null @@ -1,22 +0,0 @@ -#include <stdint.h> - -#define MSGQ_OK 0 -#define MSGQ_ERR -1 - -#define MSGQ_ERR_SIZE -10 -#define MSGQ_ERR_FULL -11 -#define MSGQ_ERR_EMPTY -12 - -typedef struct MSGQueue { - uint16_t idx_r; - uint16_t idx_w; - uint16_t size; - unsigned char **array; - pthread_mutex_t mutex; - pthread_cond_t cond; -} MSGQueue; - -int msgq_init(MSGQueue *msgq, unsigned char **array, uint16_t size); -int msgq_push(MSGQueue *msgq, unsigned char *buffer); -unsigned char *msgq_pop(MSGQueue *msgq); -uint16_t msgq_len(MSGQueue *msgq); diff --git a/yocto/esp32d/spi.c b/yocto/esp32d/spi.c deleted file mode 100644 index db46a63..0000000 --- a/yocto/esp32d/spi.c +++ /dev/null @@ -1,411 +0,0 @@ -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <assert.h> -#include <stdio.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include <linux/spi/spidev.h> - -#include <gpiod.h> -#include <pthread.h> - -#include "msgq.h" -#include "tun.h" -#include "spi.h" - -static pthread_t worker_thd; -static pthread_t rtscts_thd; -static pthread_t msg_handler_thd; -static pthread_t tun_handler_thd; -static pthread_mutex_t mutex; -static pthread_cond_t cond; - -static MSGQueue spi_bufq; -static unsigned char *spi_bufq_array[SPI_SIZE_BUFQ]; - -static MSGQueue spi_msgq_in; -static unsigned char *spi_msgq_in_array[SPI_SIZE_MSGQ_IN]; - -static MSGQueue spi_msgq_out; -static unsigned char *spi_msgq_out_array[SPI_SIZE_MSGQ_OUT]; - -static uint32_t spi_speed = SPI_SPEED; -static int spi_fd; -static volatile int spi_cts; - -struct gpiod_line_request *request = NULL; - -static void _spi_wait4cts(void) { - pthread_mutex_lock(&mutex); - while (!spi_cts) { - pthread_cond_wait(&cond, &mutex); - } - spi_cts = 0; - pthread_mutex_unlock(&mutex); -} - -static int _spi_xchg(unsigned char *buffer) { - int rv; - uint16_t len_tx; - uint16_t len_rx; - struct spi_ioc_transfer tr; - - memset(&tr, 0, sizeof(tr)); - tr.tx_buf = (unsigned long)buffer; - tr.rx_buf = (unsigned long)buffer; - tr.speed_hz = spi_speed; - - len_tx = (uint16_t)buffer[1] << 8; - len_tx |= (uint16_t)buffer[2] & 0xFF; - if (len_tx > SPI_MTU) return SPI_ERR; - - if (buffer[0]) { - len_tx += SPI_SIZE_HDR; - // esp32 dma workaraund - if (len_tx < 8) { - len_tx = 8; - } else if (len_tx % 4 != 0) { - len_tx = (len_tx / 4 + 1) * 4; - } - - tr.len = len_tx; - } else { - /* nothing to send, reset esp32 spi transaction */ - tr.len = 1; - - _spi_wait4cts(); - rv = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr); - if (rv < 0) return SPI_ERR_MSG; - - /* receive SPI_SIZE_RECEIVE bytes in first transaction (estimate) */ - len_tx = SPI_SIZE_RECEIVE + SPI_SIZE_HDR; - tr.len = len_tx; - buffer[1] = 0; - buffer[2] = 0; - } - - _spi_wait4cts(); - rv = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr); - if (rv < 0) return SPI_ERR_MSG; - - len_rx = (uint16_t)buffer[1] << 8; - len_rx |= (uint16_t)buffer[2] & 0xFF; - if (len_rx > SPI_MTU) return SPI_ERR; - - len_rx += SPI_SIZE_HDR; - if (len_rx > len_tx) { - tr.tx_buf = (unsigned long)NULL; - tr.rx_buf = (unsigned long)(buffer + len_tx); - - len_tx = len_rx - len_tx; - // esp32 dma workaraund - if (len_tx < 8) { - len_tx = 8; - } else if (len_tx % 4 != 0) { - len_tx = (len_tx / 4 + 1) * 4; - } - - tr.len = len_tx; - - _spi_wait4cts(); - rv = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr); - if (rv < 0) return SPI_ERR_MSG; - } - - return SPI_OK; -} - -static void *worker(void *arg) { - MSGQueue *bufq = &spi_bufq; - MSGQueue *msgq_in = &spi_msgq_in; - MSGQueue *msgq_out = &spi_msgq_out; - int rv; - unsigned char *buffer; - - while (1) { - pthread_mutex_lock(&msgq_out->mutex); - buffer = msgq_pop(msgq_out); - if ((buffer == NULL) && (gpiod_line_request_get_value(request, SPI_GPIO_RTS) == GPIOD_LINE_VALUE_INACTIVE)) { - pthread_mutex_lock(&bufq->mutex); - buffer = msgq_pop(bufq); - pthread_mutex_unlock(&bufq->mutex); - } - if (buffer == NULL) { - pthread_cond_wait(&msgq_out->cond, &msgq_out->mutex); - buffer = msgq_pop(msgq_out); - } - pthread_mutex_unlock(&msgq_out->mutex); - if (buffer) { - rv = _spi_xchg(buffer); - if (rv || (buffer[0] == 0)) { - buffer[0] = 0; - buffer[1] = 0; - buffer[2] = 0; - pthread_mutex_lock(&bufq->mutex); - msgq_push(bufq, buffer); - pthread_mutex_unlock(&bufq->mutex); - } else { - pthread_mutex_lock(&msgq_in->mutex); - rv = msgq_push(msgq_in, buffer); - if (rv == MSGQ_OK) pthread_cond_signal(&msgq_in->cond); - pthread_mutex_unlock(&msgq_in->mutex); - - if (rv) { - pthread_mutex_lock(&bufq->mutex); - msgq_push(bufq, buffer); - pthread_mutex_unlock(&bufq->mutex); - } - } - } - } - - return NULL; -} - -static void *rtscts_handler(void *arg) { - MSGQueue *msgq_out = &spi_msgq_out; - struct gpiod_edge_event_buffer *event_buffer; - struct gpiod_edge_event *event; - int rv; - - event_buffer = gpiod_edge_event_buffer_new(1); - - while (1) { - rv = gpiod_line_request_read_edge_events(request, event_buffer, 1); - if (rv != 1) continue; - - event = gpiod_edge_event_buffer_get_event(event_buffer, 0); - switch (gpiod_edge_event_get_line_offset(event)) { - case SPI_GPIO_RTS: { - pthread_mutex_lock(&msgq_out->mutex); - pthread_cond_signal(&msgq_out->cond); - pthread_mutex_unlock(&msgq_out->mutex); - break; - } - - case SPI_GPIO_CTS: { - pthread_mutex_lock(&mutex); - spi_cts = 1; - pthread_cond_signal(&cond); - pthread_mutex_unlock(&mutex); - break; - } - } - } - - gpiod_edge_event_buffer_free(event_buffer); - return NULL; -} - -static void *msg_handler(void *arg) { - MSGQueue *bufq = &spi_bufq; - MSGQueue *msgq_in = &spi_msgq_in; - unsigned char *buffer; - unsigned char mtype; - uint16_t len; - int rv; - - while (1) { - pthread_mutex_lock(&msgq_in->mutex); - buffer = msgq_pop(msgq_in); - if (buffer == NULL) { - pthread_cond_wait(&msgq_in->cond, &msgq_in->mutex); - buffer = msgq_pop(msgq_in); - } - pthread_mutex_unlock(&msgq_in->mutex); - if (buffer) { - mtype = buffer[0]; - len = (uint16_t)buffer[1] << 8; - len |= (uint16_t)buffer[2] & 0xFF; - - switch (mtype) { - case SPI_MTYPE_TUN: - tun_write(buffer + SPI_SIZE_HDR, len); - break; - } - buffer[0] = 0; - buffer[1] = 0; - buffer[2] = 0; - pthread_mutex_lock(&bufq->mutex); - msgq_push(bufq, buffer); - pthread_mutex_unlock(&bufq->mutex); - } - } - - return NULL; -} - -static void *tun_handler(void *arg) { - unsigned char *buffer; - ssize_t len; - - while (1) { - buffer = spi_alloc(); - if (buffer == NULL) continue; - - len = tun_read(buffer + SPI_SIZE_HDR, SPI_SIZE_BUF - SPI_SIZE_HDR); - if (len < 0) { - perror("tun read"); - continue; - } - spi_xchg(SPI_MTYPE_TUN, buffer, len); - } - - return NULL; -} - -unsigned char *spi_alloc(void) { - MSGQueue *bufq = &spi_bufq; - unsigned char *buffer; - - pthread_mutex_lock(&bufq->mutex); - buffer = msgq_pop(bufq); - pthread_mutex_unlock(&bufq->mutex); - - return buffer; -} - -void spi_free(unsigned char *buffer) { - MSGQueue *bufq = &spi_bufq; - - buffer[0] = 0; - buffer[1] = 0; - buffer[2] = 0; - pthread_mutex_lock(&bufq->mutex); - msgq_push(bufq, buffer); - pthread_mutex_unlock(&bufq->mutex); -} - -int spi_xchg(unsigned char mtype, unsigned char *buffer, uint16_t len) { - MSGQueue *bufq = &spi_bufq; - MSGQueue *msgq_out = &spi_msgq_out; - int rv; - - buffer[0] = mtype; - buffer[1] = len >> 8; - buffer[2] = len & 0xFF; - - pthread_mutex_lock(&msgq_out->mutex); - rv = msgq_push(msgq_out, buffer); - if (rv == MSGQ_OK) pthread_cond_signal(&msgq_out->cond); - pthread_mutex_unlock(&msgq_out->mutex); - - if (rv) { - pthread_mutex_lock(&bufq->mutex); - msgq_push(bufq, buffer); - pthread_mutex_unlock(&bufq->mutex); - } - - return rv; -} - -int gpio_init(void) { - struct gpiod_chip *chip = NULL; - struct gpiod_line_settings *line_settings = NULL; - struct gpiod_line_config *line_cfg = NULL; - struct gpiod_request_config *req_cfg = NULL; - int rv; - - unsigned int line_offsets[2] = { SPI_GPIO_RTS, SPI_GPIO_CTS }; - - chip = gpiod_chip_open(SPI_GPIO_DEV); - if (chip == NULL) goto gpio_init_fin; - - line_settings = gpiod_line_settings_new(); - if (line_settings == NULL) goto gpio_init_fin; - - gpiod_line_settings_set_direction(line_settings, GPIOD_LINE_DIRECTION_INPUT); - gpiod_line_settings_set_edge_detection(line_settings, GPIOD_LINE_EDGE_FALLING); - gpiod_line_settings_set_bias(line_settings, GPIOD_LINE_BIAS_PULL_UP); - - line_cfg = gpiod_line_config_new(); - if (line_cfg == NULL) goto gpio_init_fin; - - rv = gpiod_line_config_add_line_settings(line_cfg, line_offsets, 2, line_settings); - if (rv) goto gpio_init_fin; - - req_cfg = gpiod_request_config_new(); - if (req_cfg == NULL) goto gpio_init_fin; - - gpiod_request_config_set_consumer(req_cfg, "rts-cts"); - - request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); - -gpio_init_fin: - rv = (request ? SPI_OK : (chip ? SPI_ERR : SPI_ERR_OPEN)); - - if (req_cfg) gpiod_request_config_free(req_cfg); - if (line_cfg) gpiod_line_config_free(line_cfg); - if (line_settings) gpiod_line_settings_free(line_settings); - if (chip) gpiod_chip_close(chip); - - return rv; -} - -int spi_init(void) { - unsigned char *buffer; - int rv, i; - - spi_fd = open(SPI_DEV, O_RDWR); - if (spi_fd < 0) return SPI_ERR_OPEN; - - rv = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); - if (rv == -1) return SPI_ERR; - - rv = msgq_init(&spi_bufq, spi_bufq_array, SPI_SIZE_BUFQ); - assert(rv == MSGQ_OK); - - rv = msgq_init(&spi_msgq_in, spi_msgq_in_array, SPI_SIZE_MSGQ_IN); - assert(rv == MSGQ_OK); - - rv = msgq_init(&spi_msgq_out, spi_msgq_out_array, SPI_SIZE_MSGQ_OUT); - assert(rv == MSGQ_OK); - - for (i=0; i<SPI_SIZE_BUFQ; i++) { - buffer = malloc(SPI_SIZE_BUF); - assert(buffer); - msgq_push(&spi_bufq, buffer); - } - - rv = pthread_mutex_init(&mutex, NULL); - assert(rv == 0); - - rv = pthread_cond_init(&cond, NULL); - assert(rv == 0); - - /* assret initial contitions */ - pthread_mutex_lock(&mutex); - spi_cts = (gpiod_line_request_get_value(request, SPI_GPIO_CTS) == GPIOD_LINE_VALUE_INACTIVE); - pthread_mutex_unlock(&mutex); - - rv = pthread_create(&worker_thd, NULL, worker, NULL); - assert(rv == 0); - - rv = pthread_create(&rtscts_thd, NULL, rtscts_handler, NULL); - assert(rv == 0); - - rv = pthread_create(&msg_handler_thd, NULL, msg_handler, NULL); - assert(rv == 0); - - rv = pthread_create(&tun_handler_thd, NULL, tun_handler, NULL); - assert(rv == 0); - - return SPI_OK; -} - -int main(int argc, char *argv[]) { - int rv; - - rv = tun_init(SPI_TUN_NAME); - if (rv) printf("TUN INIT ERR\n"); - - rv = gpio_init(); - if (rv) printf("GPIO INIT ERR\n"); - - rv = spi_init(); - if (rv) printf("SPI INIT ERR\n"); - - while (1); -} diff --git a/yocto/esp32d/spi.h b/yocto/esp32d/spi.h deleted file mode 100644 index 4d9ff86..0000000 --- a/yocto/esp32d/spi.h +++ /dev/null @@ -1,31 +0,0 @@ -#include <stdint.h> - -#define SPI_DEV "/dev/spidev0" -#define SPI_SPEED 10000000 - -#define SPI_TUN_NAME "tun0" - -#define SPI_GPIO_DEV "/dev/gpiochip3" -#define SPI_GPIO_CTS 28 -#define SPI_GPIO_RTS 25 - -#define SPI_MTU 1500 -#define SPI_SIZE_HDR 3 -#define SPI_SIZE_BUF (SPI_MTU + SPI_SIZE_HDR) -#define SPI_SIZE_RECEIVE 16 /* guestimate on number of bytes for transaction initiated by falling RTS */ - -#define SPI_SIZE_BUFQ 64 -#define SPI_SIZE_MSGQ_IN 32 -#define SPI_SIZE_MSGQ_OUT 32 - -#define SPI_MTYPE_TUN 1 - -#define SPI_OK 0 -#define SPI_ERR -1 -#define SPI_ERR_OPEN -10 -#define SPI_ERR_MSG -11 - -unsigned char *spi_alloc(void); -void spi_free(unsigned char *buffer); -int spi_xchg(unsigned char mtype, unsigned char *buffer, uint16_t len); -int spi_init(void);
\ No newline at end of file diff --git a/yocto/esp32d/tun.c b/yocto/esp32d/tun.c deleted file mode 100644 index 75043a3..0000000 --- a/yocto/esp32d/tun.c +++ /dev/null @@ -1,84 +0,0 @@ -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <stdio.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include <linux/if.h> -#include <linux/if_tun.h> - -#include <pthread.h> - -#include "spi.h" -#include "tun.h" - -static pthread_t read_thd; - -static int tun_fd; -static char tun_name[IFNAMSIZ]; - -static int tun_alloc(char *dev, int flags) { - struct ifreq ifr; - int fd, err; - char *clonedev = "/dev/net/tun"; - - /* Arguments taken by the function: - * - * char *dev: the name of an interface (or '\0'). MUST have enough - * space to hold the interface name if '\0' is passed - * int flags: interface flags (eg, IFF_TUN etc.) - */ - - fd = open(clonedev, O_RDWR); - if (fd < 0) { - return fd; - } - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */ - - if (*dev) { - /* if a device name was specified, put it in the structure; otherwise, - * the kernel will try to allocate the "next" device of the - * specified type */ - strncpy(ifr.ifr_name, dev, IFNAMSIZ); - } - - /* try to create the device */ - err = ioctl(fd, TUNSETIFF, (void *) &ifr); - if (err < 0) { - close(fd); - return err; - } - - /* if the operation was successful, write back the name of the - * interface to the variable "dev", so the caller can know - * it. Note that the caller MUST reserve space in *dev (see calling - * code below) */ - strcpy(dev, ifr.ifr_name); - - /* this is the special file descriptor that the caller will use to talk - * with the virtual interface */ - return fd; -} - -ssize_t tun_read(unsigned char *buffer, size_t buf_size) { - return read(tun_fd, buffer, buf_size); -} - -ssize_t tun_write(unsigned char *buffer, size_t buf_len) { - return write(tun_fd, buffer, buf_len); -} - -int tun_init(char *name) { - int rv; - - if (strlen(name) >= sizeof(tun_name) - 1) return -1; - strcpy(tun_name, name); - - tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); - if (tun_fd < 0) return -1; - - return 0; -} diff --git a/yocto/esp32d/tun.h b/yocto/esp32d/tun.h deleted file mode 100644 index 793a4c9..0000000 --- a/yocto/esp32d/tun.h +++ /dev/null @@ -1,5 +0,0 @@ -#include <sys/types.h> - -ssize_t tun_read(unsigned char *buffer, size_t buf_size); -ssize_t tun_write(unsigned char *buffer, size_t buf_len); -int tun_init(char *name);
\ No newline at end of file diff --git a/yocto/esp32mod/Makefile b/yocto/esp32mod/Makefile new file mode 100755 index 0000000..31b58fb --- /dev/null +++ b/yocto/esp32mod/Makefile @@ -0,0 +1,15 @@ +obj-m := esp32mod.o +esp32mod-y := esp32.o msgq.o + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c *.mod + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers diff --git a/yocto/esp32mod/esp32.c b/yocto/esp32mod/esp32.c new file mode 100755 index 0000000..8c7b5e4 --- /dev/null +++ b/yocto/esp32mod/esp32.c @@ -0,0 +1,535 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/completion.h> +#include <linux/kthread.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/cdev.h> +#include <linux/spi/spi.h> +#include <linux/gpio.h> + +#include <linux/of_device.h> +#include <linux/uaccess.h> + +#include "msgq.h" +#include "esp32.h" + +#define wait_event_interruptible_lock(wq_head, condition, lock) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + __ret = ___wait_event(wq_head, condition, TASK_INTERRUPTIBLE, 0, 0, \ + spin_unlock(&lock); \ + schedule(); \ + spin_lock(&lock)); \ + __ret; \ +}) + +/* has to be power of 2 */ +#define MSGQ_SIZE_SND 128 +#define MSGQ_SIZE_RCV 128 + +#define ESP32_SIZE_HDR 3 +#define ESP32_SIZE_BUF (ESP32_MTU + 4) + +static int esp32_open(struct inode *inode, struct file *file); +static int esp32_release(struct inode *inode, struct file *file); +static ssize_t esp32_read(struct file *file, char __user *buf, size_t len, loff_t *off); +static ssize_t esp32_write(struct file *file, const char __user *buf, size_t len, loff_t *off); + +static int esp32_probe(struct spi_device *spi); +static void esp32_remove(struct spi_device *spi); + +static DECLARE_WAIT_QUEUE_HEAD(xchg_wq); +static DECLARE_WAIT_QUEUE_HEAD(cts_wq); + +static MSGQueue snd_msgq; +static MSGQueue rcv_msgq[ESP32_MAX_MTYPE]; + +static struct spi_device *esp32_spi_dev; +static struct gpio_desc *gpio_rts, *gpio_cts; +static int irq_rts, irq_cts; +static int cts_val; +static struct task_struct *kthd; + +static dev_t dev; +static struct class *dev_class; +static struct cdev dev_cdev; +static struct file_operations dev_fops = { + .owner = THIS_MODULE, + .open = esp32_open, + .release = esp32_release, + .read = esp32_read, + .write = esp32_write, +}; + +static const struct of_device_id esp32_of_match[] = { + { + .compatible = "mikrophone,esp32", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, esp32_of_match); + +static const struct spi_device_id esp32_spi_ids[] = { + { "esp32", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, esp32_spi_ids); + +static struct spi_driver esp32_spi_driver = { + .probe = esp32_probe, + .remove = esp32_remove, + .driver = { + .name = "esp32", + .of_match_table = of_match_ptr(esp32_of_match), + }, + .id_table = esp32_spi_ids, +}; + +static irqreturn_t rts_irq_handler(int irq, void *dev_id) { + wake_up(&xchg_wq); + + return IRQ_HANDLED; +} + +static irqreturn_t cts_irq_handler(int irq, void *dev_id) { + cts_val = 0; + wake_up(&cts_wq); + + return IRQ_HANDLED; +} + +/* esp32 dma workaraund */ +static inline unsigned int esp32_fix_len(unsigned int len) { + if (len < 8) { + return 8; + } else if (len % 4 != 0) { + return (len / 4 + 1) * 4; + } + return len; +} + +static inline int wait4cts(void) { + return wait_event_interruptible(cts_wq, !cts_val); +} + +static inline int spi_xchg(struct spi_transfer *spi_tr) { + cts_val = 1; + return spi_sync_transfer(esp32_spi_dev, spi_tr, 1); +} + +static int xchg_thd(void *arg) { + unsigned char *buffer, *_buffer, *buf_rx; + unsigned char mtype, mtype_flags; + int rv, wake_sndq, wake_rcvq, bfree; + struct spi_transfer spi_tr; + uint16_t len_tx, len_rx; + + _buffer = NULL; + memset(&spi_tr, 0, sizeof(spi_tr)); + + buf_rx = esp32_alloc(); + if (buf_rx == NULL) return -ENOMEM; + + while (!kthread_should_stop()) { + /* preallocate buffer outside of spin lock: GFP_KERNEL allocator can sleep */ + if (_buffer == NULL) { + _buffer = esp32_alloc(); + if (_buffer == NULL) return -ENOMEM; + _buffer[0] = 0; + _buffer[1] = 0; + _buffer[2] = 0; + } + + rv = 0; + wake_sndq = 0; + wake_rcvq = 0; + bfree = 1; + + spin_lock(&snd_msgq.lock); + do { + buffer = msgq_pop(&snd_msgq); + if (buffer && (msgq_len(&snd_msgq) == (msgq_size(&snd_msgq) - 1))) { + wake_sndq = 1; + } + if ((buffer == NULL) && (gpiod_get_value(gpio_rts) == 0)) { + buffer = _buffer; + _buffer = NULL; + } + if (buffer == NULL) { + rv = wait_event_interruptible_lock(xchg_wq, (msgq_len(&snd_msgq) != 0) || (gpiod_get_value(gpio_rts) == 0), snd_msgq.lock); + if (rv) break; + } + } while (buffer == NULL); + spin_unlock(&snd_msgq.lock); + + /* kthread_should_stop() might be true */ + if (rv) continue; + + if (wake_sndq) wake_up(&snd_msgq.wq); + + len_tx = (uint16_t)buffer[1] << 8; + len_tx |= (uint16_t)buffer[2] & 0xFF; + if (len_tx > ESP32_MTU) goto xchg_thd_fin; + + spi_tr.tx_buf = buffer; + spi_tr.rx_buf = buf_rx; + spi_tr.len = len_tx + ESP32_SIZE_HDR; + spi_tr.len = esp32_fix_len(spi_tr.len); + + /* wait for cts */ + rv = wait4cts(); + if (rv) goto xchg_thd_fin; + + /* nothing to send/receive */ + if ((buffer[0] == 0) && gpiod_get_value(gpio_rts)) goto xchg_thd_fin; + + rv = spi_xchg(&spi_tr); + if (rv) goto xchg_thd_fin; + + mtype = buf_rx[0] & ESP32_MTYPE_MASK; + mtype_flags = buf_rx[0] & ~ESP32_MTYPE_MASK; + if (mtype >= ESP32_MAX_MTYPE) goto xchg_thd_fin; + + if (mtype) { + memcpy(buffer, spi_tr.rx_buf, spi_tr.len); + + len_rx = (uint16_t)buffer[1] << 8; + len_rx |= (uint16_t)buffer[2] & 0xFF; + if (len_rx > ESP32_MTU) goto xchg_thd_fin; + + if (len_rx + ESP32_SIZE_HDR > spi_tr.len) { + spi_tr.tx_buf = NULL; + spi_tr.rx_buf = buffer + spi_tr.len; + spi_tr.len = len_rx + ESP32_SIZE_HDR - spi_tr.len; + spi_tr.len = esp32_fix_len(spi_tr.len); + + /* wait for cts, again */ + rv = wait4cts(); + if (rv) goto xchg_thd_fin; + + rv = spi_xchg(&spi_tr); + if (rv) goto xchg_thd_fin; + } + + spin_lock(&rcv_msgq[mtype].lock); + rv = msgq_push(&rcv_msgq[mtype], buffer); + if (!rv && (msgq_len(&rcv_msgq[mtype]) == 1)) { + wake_rcvq = 1; + } + spin_unlock(&rcv_msgq[mtype].lock); + + if (!rv) bfree = 0; + if (wake_rcvq) wake_up(&rcv_msgq[mtype].wq); + } + +xchg_thd_fin: + if (bfree) { + if (_buffer == NULL) { + _buffer = buffer; + _buffer[0] = 0; + _buffer[1] = 0; + _buffer[2] = 0; + } else { + esp32_free(buffer); + } + } + } + + esp32_free(_buffer); + esp32_free(buf_rx); + return 0; +} + +static int esp32_open(struct inode *inode, struct file *file) { + unsigned int major, minor; + + major = MAJOR(dev); + minor = MINOR(dev); + if (imajor(inode) != major) return -EINVAL; + if ((iminor(inode) < minor) || (iminor(inode) >= minor + ESP32_MAX_MTYPE)) return -EINVAL; + file->private_data = (void *)(iminor(inode) - minor); + + return 0; +} + +static int esp32_release(struct inode *inode, struct file *file) { + return 0; +} + +static ssize_t esp32_read(struct file *file, char __user *buf, size_t len, loff_t *off) { + unsigned char mtype; + unsigned char *buffer; + uint16_t msg_len; + unsigned long _rv; + int block, rv = 0; + + block = !(file->f_flags & O_NONBLOCK); + mtype = (unsigned char)(file->private_data); + rv = esp32_receive(mtype, block, &buffer); + if (rv) goto esp32_read_fin; + + msg_len = (uint16_t)buffer[1] << 8; + msg_len |= (uint16_t)buffer[2] & 0xFF; + if (len < msg_len) rv = -EINVAL; + if (rv) goto esp32_read_fin; + + _rv = copy_to_user(buf, buffer + ESP32_SIZE_HDR, msg_len); + if (_rv) rv = -EFAULT; + +esp32_read_fin: + if (buffer) esp32_free(buffer); + if (rv) return rv; + return msg_len; +} + +static ssize_t esp32_write(struct file *file, const char __user *buf, size_t len, loff_t *off) { + unsigned char mtype; + unsigned char *buffer; + unsigned long _rv; + int block, rv = 0; + + if (len > ESP32_MTU) return -EINVAL; + + buffer = esp32_alloc(); + if (buffer == NULL) return -ENOMEM; + + _rv = copy_from_user(buffer + ESP32_SIZE_HDR, buf, len); + if (_rv) rv = -EFAULT; + if (rv) { + esp32_free(buffer); + return rv; + } + + block = !(file->f_flags & O_NONBLOCK); + mtype = (unsigned char)(file->private_data); + buffer[0] = mtype; + buffer[1] = len >> 8; + buffer[2] = len & 0xFF; + + rv = esp32_send(buffer, block); + if (rv) return rv; + + return len; +} + +static int esp32_probe(struct spi_device *spi) { + dev_t _dev; + struct device *dev_ptr; + const char *fmt; + int i, rv, major, minor; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(esp32_of_match), &spi->dev); + if (match == NULL) return -ENODEV; + + esp32_spi_dev = spi; + + gpio_rts = gpiod_get(&spi->dev, "rts", GPIOD_IN); + if (IS_ERR(gpio_rts)) goto err_gpiod_get_rts; + + irq_rts = gpiod_to_irq(gpio_rts); + if (irq_rts < 0) goto err_gpiod_get_rts; + + gpio_cts = gpiod_get(&spi->dev, "cts", GPIOD_IN); + if (IS_ERR(gpio_cts) || gpiod_cansleep(gpio_rts)) goto err_gpiod_get_cts; + + irq_cts = gpiod_to_irq(gpio_cts); + if (irq_cts < 0) goto err_gpiod_get_cts; + + rv = request_irq(irq_rts, rts_irq_handler, IRQF_TRIGGER_FALLING, "esp32_rts", NULL); + if (rv < 0) goto err_request_irq_rts; + + rv = request_irq(irq_cts, cts_irq_handler, IRQF_TRIGGER_FALLING, "esp32_cts", NULL); + if (rv < 0) goto err_request_irq_cts; + + kthd = kthread_run(xchg_thd, NULL, "esp32"); + if (IS_ERR(kthd)) goto err_kthread_run; + + rv = alloc_chrdev_region(&dev, 0, ESP32_MAX_MTYPE, "esp32"); + if (rv) goto err_alloc_chrdev; + + cdev_init(&dev_cdev, &dev_fops); + + rv = cdev_add(&dev_cdev, dev, ESP32_MAX_MTYPE); + if (rv) goto err_cdev_add; + + dev_class = class_create("esp32"); + if (IS_ERR(dev_class)) goto err_class_create; + + major = MAJOR(dev); + minor = MINOR(dev); + for (i=0; i<ESP32_MAX_MTYPE; i++) { + _dev = MKDEV(major, minor + i); + fmt = i & ESP32_MTYPE_FLAG_BRIDGE ? "fe310.%d" : "esp32.%d"; + dev_ptr = device_create(dev_class, NULL, _dev, NULL, fmt, i & ~ESP32_MTYPE_FLAG_BRIDGE); + if (IS_ERR(dev_ptr)) goto err_device_create; + } + + return 0; + +err_device_create: + for (i--; i>=0; i--) { + _dev = MKDEV(major, minor + i); + device_destroy(dev_class, _dev); + } + class_destroy(dev_class); + +err_class_create: + cdev_del(&dev_cdev); + +err_cdev_add: + unregister_chrdev_region(dev, ESP32_MAX_MTYPE); + +err_alloc_chrdev: + kthread_stop(kthd); + +err_kthread_run: + free_irq(irq_cts, NULL); + +err_request_irq_cts: + free_irq(irq_rts, NULL); + +err_request_irq_rts: +err_gpiod_get_cts: + if (!IS_ERR(gpio_cts)) gpiod_put(gpio_rts); + +err_gpiod_get_rts: + if (!IS_ERR(gpio_rts)) gpiod_put(gpio_rts); + return -1; +} + +static void esp32_remove(struct spi_device *spi) { + unsigned char *buffer; + dev_t _dev; + int i, major, minor; + + major = MAJOR(dev); + minor = MINOR(dev); + for (i=0; i<ESP32_MAX_MTYPE; i++) { + _dev = MKDEV(major, minor + i); + device_destroy(dev_class, _dev); + } + class_destroy(dev_class); + cdev_del(&dev_cdev); + unregister_chrdev_region(dev, ESP32_MAX_MTYPE); + kthread_stop(kthd); + free_irq(irq_cts, NULL); + free_irq(irq_rts, NULL); + gpiod_put(gpio_cts); + gpiod_put(gpio_rts); + + /* flush remaining buffers */ + spin_lock(&snd_msgq.lock); + while ((buffer = msgq_pop(&snd_msgq))) { + esp32_free(buffer); + } + msgq_destroy(&snd_msgq); + spin_unlock(&snd_msgq.lock); + for (i=0; i<ESP32_MAX_MTYPE; i++) { + spin_lock(&rcv_msgq[i].lock); + while ((buffer = msgq_pop(&rcv_msgq[i]))) { + esp32_free(buffer); + } + msgq_destroy(&rcv_msgq[i]); + spin_unlock(&rcv_msgq[i].lock); + } +} + +static int __init esp32_init(void) { + int i, rv; + + rv = msgq_create(&snd_msgq, MSGQ_SIZE_SND); + if (rv) return rv; + + for (i=0; i<ESP32_MAX_MTYPE; i++) { + rv = msgq_create(&rcv_msgq[i], MSGQ_SIZE_RCV); + if (rv) goto esp32_init_err; + } + + rv = spi_register_driver(&esp32_spi_driver); + if (rv) goto esp32_init_err; + + return 0; + +esp32_init_err: + for (i--; i>=0; i--) { + msgq_destroy(&rcv_msgq[i]); + } + msgq_destroy(&snd_msgq); + return rv; +} + +static void __exit esp32_exit(void) { + spi_unregister_driver(&esp32_spi_driver); +} + +module_init(esp32_init); +module_exit(esp32_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Uros Majstorovic <majstor@majstor.org>"); +MODULE_DESCRIPTION("esp32 SPI driver"); +MODULE_VERSION("1.0"); + +int esp32_send(unsigned char *buffer, int block) { + int rv, wake_xchg; + + spin_lock(&snd_msgq.lock); + do { + wake_xchg = 0; + rv = msgq_push(&snd_msgq, buffer); + if (!rv && (msgq_len(&snd_msgq) == 1)) { + wake_xchg = 1; + } + if (block && rv) { + int _rv; + + _rv = wait_event_interruptible_lock(snd_msgq.wq, msgq_len(&snd_msgq) != msgq_size(&snd_msgq), snd_msgq.lock); + if (_rv) { + rv = _rv; + break; + } + } + } while (block && rv); + spin_unlock(&snd_msgq.lock); + + if (wake_xchg) wake_up(&xchg_wq); + if (rv) esp32_free(buffer); + + return rv; +} + +int esp32_receive(unsigned char mtype, int block, unsigned char **buffer) { + int rv; + + *buffer = NULL; + if (mtype >= ESP32_MAX_MTYPE) return -EINVAL; + + rv = 0; + spin_lock(&rcv_msgq[mtype].lock); + *buffer = msgq_pop(&rcv_msgq[mtype]); + while (block && (*buffer == NULL)) { + rv = wait_event_interruptible_lock(rcv_msgq[mtype].wq, msgq_len(&rcv_msgq[mtype]) != 0, rcv_msgq[mtype].lock); + if (rv) break; + + *buffer = msgq_pop(&rcv_msgq[mtype]); + } + spin_unlock(&rcv_msgq[mtype].lock); + + if (!rv && (*buffer == NULL)) rv = -EAGAIN; + return rv; +} + +unsigned char *esp32_alloc(void) { + return kmalloc(ESP32_SIZE_BUF, GFP_KERNEL); +} + +void esp32_free(unsigned char *buffer) { + kfree(buffer); +} diff --git a/yocto/esp32mod/esp32.h b/yocto/esp32mod/esp32.h new file mode 100755 index 0000000..b00cc4f --- /dev/null +++ b/yocto/esp32mod/esp32.h @@ -0,0 +1,16 @@ +#define ESP32_MTU 1500 + +#define ESP32_MAX_MTYPE 16 + +#define ESP32_MTYPE_SLEEP 0x20 /* does not have net handler */ + +#define ESP32_MTYPE_FLAG_ONEW 0x80 +#define ESP32_MTYPE_FLAG_REPL 0x40 +#define ESP32_MTYPE_FLAG_BRIDGE 0x08 + +#define ESP32_MTYPE_MASK 0x0F + +int esp32_send(unsigned char *buffer, int block); +int esp32_receive(unsigned char msg_type, int block, unsigned char **buffer); +unsigned char *esp32_alloc(void); +void esp32_free(unsigned char *buffer);
\ No newline at end of file diff --git a/yocto/esp32d/msgq.c b/yocto/esp32mod/msgq.c index 3039f13..77ece4b 100644..100755 --- a/yocto/esp32d/msgq.c +++ b/yocto/esp32mod/msgq.c @@ -1,35 +1,46 @@ -#include <stdlib.h> -#include <pthread.h> +#include <linux/module.h> #include "msgq.h" #define IDX_MASK(IDX, SIZE) ((IDX) & ((SIZE) - 1)) -int msgq_init(MSGQueue *msgq, unsigned char **array, uint16_t size) { - int rv; +int msgq_create(MSGQueue *msgq, uint16_t size) { + unsigned char **array; + + array = kmalloc(size * sizeof(unsigned char *), GFP_KERNEL); + if (array == NULL) return -ENOMEM; msgq->idx_r = 0; msgq->idx_w = 0; msgq->size = size; msgq->array = array; - rv = pthread_mutex_init(&msgq->mutex, NULL); - if (rv) { - return MSGQ_ERR; - } - - rv = pthread_cond_init(&msgq->cond, NULL); - if (rv) { - pthread_mutex_destroy(&msgq->mutex); - return MSGQ_ERR; - } - return MSGQ_OK; + spin_lock_init(&msgq->lock); + init_waitqueue_head(&msgq->wq); + + return 0; +} + +void msgq_destroy(MSGQueue *msgq) { + kfree(msgq->array); + msgq->idx_r = 0; + msgq->idx_w = 0; + msgq->size = 0; + msgq->array = NULL; +} + +uint16_t msgq_len(MSGQueue *msgq) { + return (uint16_t)(msgq->idx_w - msgq->idx_r); +} + +uint16_t msgq_size(MSGQueue *msgq) { + return msgq->size; } int msgq_push(MSGQueue *msgq, unsigned char *buffer) { - if ((uint16_t)(msgq->idx_w - msgq->idx_r) == msgq->size) return MSGQ_ERR_FULL; + if ((uint16_t)(msgq->idx_w - msgq->idx_r) == msgq->size) return -EAGAIN; msgq->array[IDX_MASK(msgq->idx_w++, msgq->size)] = buffer; - return MSGQ_OK; + return 0; } unsigned char *msgq_pop(MSGQueue *msgq) { @@ -37,7 +48,3 @@ unsigned char *msgq_pop(MSGQueue *msgq) { return msgq->array[IDX_MASK(msgq->idx_r++, msgq->size)]; } - -uint16_t msgq_len(MSGQueue *msgq) { - return (uint16_t)(msgq->idx_w - msgq->idx_r); -} diff --git a/yocto/esp32mod/msgq.h b/yocto/esp32mod/msgq.h new file mode 100755 index 0000000..f0d82f4 --- /dev/null +++ b/yocto/esp32mod/msgq.h @@ -0,0 +1,15 @@ +typedef struct MSGQueue { + uint16_t idx_r; + uint16_t idx_w; + uint16_t size; + unsigned char **array; + spinlock_t lock; + wait_queue_head_t wq; +} MSGQueue; + +int msgq_create(MSGQueue *msgq, uint16_t size); +void msgq_destroy(MSGQueue *msgq); +uint16_t msgq_len(MSGQueue *msgq); +uint16_t msgq_size(MSGQueue *msgq); +int msgq_push(MSGQueue *msgq, unsigned char *buffer); +unsigned char *msgq_pop(MSGQueue *msgq); diff --git a/yocto/esp32d/Makefile b/yocto/esp32tun/Makefile index 677d09d..574c480 100644..100755 --- a/yocto/esp32d/Makefile +++ b/yocto/esp32tun/Makefile @@ -1,7 +1,7 @@ #CFLAGS = LDFLAGS = -pthread -lgpiod -TARGET = esp32d -obj = msgq.o spi.o tun.o +TARGET = esp32tun +obj = esp32tun.o all: $(TARGET) diff --git a/yocto/esp32tun/esp32tun.c b/yocto/esp32tun/esp32tun.c new file mode 100755 index 0000000..8f9e3bd --- /dev/null +++ b/yocto/esp32tun/esp32tun.c @@ -0,0 +1,173 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <pthread.h> + +#define TUN_NAME "tun0" + +#define ESP32_MTU 1500 +#define ESP32_FNAMSIZ 64 +#define ESP32_DEVFN "/dev/esp32.?" +#define ESP32_MTYPE_TUN 1 +#define ESP32_MAX_MTYPE 8 + +static pthread_t tun_handler_thd, esp32_handler_thd; + +static int tun_fd, esp32_fd; +static char tun_name[IFNAMSIZ]; + +/* Arguments taken by the function: + * + * char *dev: the name of an interface (or '\0'). MUST have enough + * space to hold the interface name if '\0' is passed + * int flags: interface flags (eg, IFF_TUN etc.) + */ +int tun_alloc(char *dev, int flags) { + struct ifreq ifr; + int fd, rv; + char *clonedev = "/dev/net/tun"; + + fd = open(clonedev, O_RDWR); + if (fd < 0) return fd; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */ + + if (*dev) { + /* if a device name was specified, put it in the structure; otherwise, + * the kernel will try to allocate the "next" device of the + * specified type */ + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + } + + /* try to create the device */ + rv = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (rv < 0) { + close(fd); + return rv; + } + + /* if the operation was successful, write back the name of the + * interface to the variable "dev", so the caller can know + * it. Note that the caller MUST reserve space in *dev (see calling + * code below) */ + strcpy(dev, ifr.ifr_name); + + /* this is the special file descriptor that the caller will use to talk + * with the virtual interface */ + return fd; +} + +ssize_t tun_read(unsigned char *buffer, size_t buf_size) { + return read(tun_fd, buffer, buf_size); +} + +ssize_t tun_write(unsigned char *buffer, size_t buf_len) { + return write(tun_fd, buffer, buf_len); +} + +int tun_init(char *name) { + if (strlen(name) + 1 > sizeof(tun_name)) return -1; + + strcpy(tun_name, name); + tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); + + if (tun_fd < 0) return tun_fd; + return 0; +} + +ssize_t esp32_read(unsigned char *buffer, size_t buf_size) { + return read(esp32_fd, buffer, buf_size); +} + +ssize_t esp32_write(unsigned char *buffer, size_t buf_len) { + return write(esp32_fd, buffer, buf_len); +} + +int esp32_fname(char *fname, int mtype) { + if (mtype >= ESP32_MAX_MTYPE) return -1; + + strcpy(fname, ESP32_DEVFN); + fname[strlen(fname) - 1] = '0' + mtype; + + return 0; +} + +int esp32_init(void) { + char fname[ESP32_FNAMSIZ]; + int rv; + + rv = esp32_fname(fname, ESP32_MTYPE_TUN); + if (rv) return rv; + + esp32_fd = open(fname, O_RDWR); + + if (esp32_fd < 0) return esp32_fd; + return 0; +} + +void *tun_handler(void *arg) { + unsigned char buffer[ESP32_MTU]; + ssize_t len, rv; + + while (1) { + len = tun_read(buffer, ESP32_MTU); + if (len < 0) { + perror("tun read"); + continue; + } + rv = esp32_write(buffer, len); + if (rv < 0) { + perror("esp32 write"); + continue; + } + } + + return NULL; +} + +void *esp32_handler(void *arg) { + unsigned char buffer[ESP32_MTU]; + ssize_t len, rv; + + while (1) { + len = esp32_read(buffer, ESP32_MTU); + if (len < 0) { + perror("esp32 read"); + continue; + } + rv = tun_write(buffer, len); + if (rv < 0) { + perror("tun write"); + continue; + } + } + + return NULL; +} + +int main(int argc, char *argv[]) { + int rv; + + rv = tun_init(TUN_NAME); + assert(rv == 0); + + rv = esp32_init(); + assert(rv == 0); + + rv = pthread_create(&tun_handler_thd, NULL, tun_handler, NULL); + assert(rv == 0); + + rv = pthread_create(&esp32_handler_thd, NULL, esp32_handler, NULL); + assert(rv == 0); + + while(1); +} diff --git a/yocto/meta-mikrophone/conf/machine/._verdin-imx8mp-mikrophone.conf b/yocto/meta-mikrophone/conf/machine/._verdin-imx8mp-mikrophone.conf Binary files differnew file mode 100644 index 0000000..fe7655c --- /dev/null +++ b/yocto/meta-mikrophone/conf/machine/._verdin-imx8mp-mikrophone.conf diff --git a/yocto/meta-mikrophone/conf/machine/include/verdin-imx8mp.inc b/yocto/meta-mikrophone/conf/machine/include/verdin-imx8mp.inc new file mode 100644 index 0000000..b64f4f1 --- /dev/null +++ b/yocto/meta-mikrophone/conf/machine/include/verdin-imx8mp.inc @@ -0,0 +1,14 @@ +# needed in recent builds for wifi and bt +MACHINE_FIRMWARE:append = " firmware-nxp-wifi-nxp8997-sdio" + +# our device tree +KERNEL_DEVICETREE:append = " \ + ${KERNEL_DTB_PREFIX}imx8mp-verdin-wifi-mikrophone.dtb \ + ${KERNEL_DTB_PREFIX}imx8mp-verdin-nonwifi-mikrophone.dtb \ +" +# DTB for u-boot only +UBOOT_DTB_NAME = "imx8mp-verdin-wifi-mikrophone.dtb" + +# for SDK only +TOOLCHAIN_TARGET_TASK:append = " kernel-devsrc" +TOOLCHAIN_TARGET_TASK:remove = "target-sdk-provides-dummy" diff --git a/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-mikrophone.dtsi b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-mikrophone.dtsi new file mode 100644 index 0000000..6ce6362 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-mikrophone.dtsi @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +/* Verdin SPI_1 */ +&ecspi1 { + status = "okay"; +}; + +&eqos { + status = "disable"; +}; + +&fec { + status = "disable"; +}; + +&flexcan1 { + status = "disable"; +}; + +&flexcan2 { + status = "disable"; +}; + +/* Verdin QSPI_1 */ +&flexspi { + status = "disable"; +}; + +/* Verdin I2C_2_DSI */ +&i2c2 { + status = "disabled"; +}; + +&i2c3 { + status = "disabled"; +}; + +/* Verdin I2C_1 */ +&i2c4 { + status = "okay"; +}; + +/* Verdin PCIE_1 */ +&pcie { + status = "okay"; +}; + +&pcie_phy { + status = "okay"; +}; + +/* Verdin PWM_1 */ +&pwm1 { + status = "okay"; +}; + +/* Verdin PWM_2 */ +&pwm2 { + status = "disabled"; +}; + +/* Verdin PWM_3_DSI */ +&pwm3 { + status = "disabled"; +}; + +®_usdhc2_vmmc { + vin-supply = <®_3p3v>; +}; + +/* Verdin UART_1 */ +&uart1 { + status = "disabled"; +}; + +/* Verdin UART_2 */ +&uart2 { + status = "disabled"; +}; + +/* Verdin UART_3, used as the Linux Console */ +&uart3 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb3_0 { + status = "okay"; +}; + +&usb3_phy0 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3_1 { + fsl,permanently-attached; + status = "okay"; +}; + +&usb3_phy1 { + status = "okay"; +}; + +/* Verdin SDCard */ +&usdhc2 { + status = "okay"; +}; diff --git a/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-nonwifi-mikrophone.dts b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-nonwifi-mikrophone.dts new file mode 100644 index 0000000..e8ba274 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-nonwifi-mikrophone.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +/dts-v1/; + +#include "imx8mp-verdin.dtsi" +#include "imx8mp-verdin-nonwifi.dtsi" +#include "imx8mp-verdin-mikrophone.dtsi" + +/ { + model = "Toradex Verdin iMX8M Plus on mikroPhone"; + compatible = "toradex,verdin-imx8mp-nonwifi-mikrophone", + "toradex,verdin-imx8mp-nonwifi", + "toradex,verdin-imx8mp", + "fsl,imx8mp"; +}; diff --git a/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-wifi-mikrophone.dts b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-wifi-mikrophone.dts new file mode 100644 index 0000000..3b04ab5 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-wifi-mikrophone.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +/dts-v1/; + +#include "imx8mp-verdin.dtsi" +#include "imx8mp-verdin-wifi.dtsi" +#include "imx8mp-verdin-mikrophone.dtsi" + +/ { + model = "Toradex Verdin iMX8M Plus WB on mikroPhone"; + compatible = "toradex,verdin-imx8mp-wifi-mikrophone", + "toradex,verdin-imx8mp-wifi", + "toradex,verdin-imx8mp", + "fsl,imx8mp"; +}; diff --git a/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex_%.bbappend b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex_%.bbappend new file mode 100644 index 0000000..0a6f719 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex_%.bbappend @@ -0,0 +1,19 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/u-boot-toradex:" + +SRC_URI += "\ + file://imx8mp-verdin-mikrophone.dtsi \ + file://imx8mp-verdin-wifi-mikrophone.dts \ + file://imx8mp-verdin-nonwifi-mikrophone.dts \ + " + +DTS_SRCDIR = "dts/upstream/src/arm64/freescale" + +do_configure:append() { + cp ${WORKDIR}/imx8mp-verdin-mikrophone.dtsi ${S}/${DTS_SRCDIR} + cp ${WORKDIR}/imx8mp-verdin-wifi-mikrophone.dts ${S}/${DTS_SRCDIR} + cp ${WORKDIR}/imx8mp-verdin-nonwifi-mikrophone.dts ${S}/${DTS_SRCDIR} + # Remove exisiting fdtfile, if there is one + sed -i '/"fdtfile=.*\\0" \\/d' ${S}/include/configs/verdin-imx8mp.h + # Add new fdtfile + sed -i 's/\("fdt_board=.*\\0" \\\)/\0\n\t"fdtfile=imx8mp-verdin-wifi-mikrophone.dtb\\0" \\/' ${S}/include/configs/verdin-imx8mp.h +}
\ No newline at end of file diff --git a/yocto/meta-mikrophone/recipes-images/images/mikrophone-image.bb b/yocto/meta-mikrophone/recipes-images/images/mikrophone-image.bb index a4ced94..4a88221 100644 --- a/yocto/meta-mikrophone/recipes-images/images/mikrophone-image.bb +++ b/yocto/meta-mikrophone/recipes-images/images/mikrophone-image.bb @@ -50,8 +50,8 @@ IMAGE_INSTALL += " \ 'timestamp-service systemd-analyze', '', d)} \ ${@bb.utils.contains('DISTRO_FEATURES', 'x11 wayland', \ 'weston-xwayland xterm', '', d)} \ - ${@bb.utils.contains("MACHINE_FEATURES", "tpm2", \ - "packagegroup-tpm2-tdx-cli", "",d)} \ + ${@bb.utils.contains('MACHINE_FEATURES', 'tpm2', \ + 'packagegroup-tpm2-tdx-cli', '', d)} \ \ packagegroup-tdx-cli \ packagegroup-tdx-graphical \ @@ -70,5 +70,5 @@ IMAGE_INSTALL += " \ media-files \ \ gnupg \ - weston weston-init wayland-terminal-launch \ + weston weston-init wayland-terminal-launch \ " diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays/mikroPhone-panel_overlay.dts b/yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays/mikroPhone-panel_overlay.dts new file mode 100644 index 0000000..bee2682 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays/mikroPhone-panel_overlay.dts @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +// adapted from: https://git.toradex.com/cgit/device-tree-overlays.git/tree/overlays/verdin-imx8mp_panel-cap-touch-10inch-lvds_overlay.dts?h=toradex_6.6-2.2.x-imx +// Verdin iMX8M Plus single-channel LVDS + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/pwm/pwm.h> +#include "freescale/imx8mp-pinfunc.h" + +/ { + compatible = "toradex,verdin-imx8mp"; +}; + +&{/} { + backlight_lvds_native: backlight-lvds-native { + compatible = "pwm-backlight"; + pinctrl-names = "default"; + brightness-levels = <0 45 63 88 119 158 203 255>; + default-brightness-level = <4>; + /* Verdin PWM_1 (SODIMM 15) */ + pwms = <&pwm1 0 6666667>; + }; + + panel-lvds-native { + compatible = "panel-lvds"; + backlight = <&backlight_lvds_native>; + data-mapping = "vesa-24"; + height-mm = <110>; + width-mm = <62>; + + panel-timing { + clock-frequency = <36000000>; + hactive = <480>; + hfront-porch = <23 60 71>; + hsync-len = <15 40 47>; + hback-porch = <23 60 71>; + + vactive = <854>; + vfront-porch = <5 7 10>; + vsync-len = <6 9 12>; + vback-porch = <5 7 10>; + + hsync-active = <0>; + vsync-active = <0>; + de-active = <0>; + pixelclk-active = <1>; /* positive edge */ + }; + + port { + panel_lvds_native_in: endpoint { + remote-endpoint = <&lvds_out>; + }; + }; + }; +}; + +/* Verdin I2C_1 */ +&i2c4 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + gt911@5d { + compatible = "goodix,gt911"; + reg = <0x5d>; + pinctrl-names = "default"; + pinctrl-0 = <&gpio1>; + /* Verdin GPIO_3 (SODIMM 210) */ + interrupt-parent = <&gpio1>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + /* Verdin GPIO_4 (SODIMM 212) */ + reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; + +&lcdif2 { + status = "okay"; +}; + +&ldb { + status = "okay"; + + lvds-channel@0 { + #address-cells = <1>; + #size-cells = <0>; + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + status = "okay"; + + port@1 { + reg = <1>; + + lvds_out: endpoint { + remote-endpoint = <&panel_lvds_native_in>; + }; + }; + }; +}; + +&ldb_phy { + status = "okay"; +}; diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays_git.bbappend b/yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays_git.bbappend new file mode 100644 index 0000000..cae7974 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays_git.bbappend @@ -0,0 +1,15 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/device-tree-overlays:" + +MIKROPHONE_OVERLAYS_BINARY = "verdin-imx8mp_hdmi_overlay.dtbo mikroPhone-panel_overlay.dtbo" + +SRC_URI += " \ + file://mikroPhone-panel_overlay.dts \ +" + +TEZI_EXTERNAL_KERNEL_DEVICETREE = "${MIKROPHONE_OVERLAYS_BINARY}" + +TEZI_EXTERNAL_KERNEL_DEVICETREE_BOOT = "${MIKROPHONE_OVERLAYS_BINARY}" + +do_collect_overlays:prepend() { + cp ${WORKDIR}/mikroPhone-panel_overlay.dts ${S} +}
\ No newline at end of file diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex%.bbappend b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex%.bbappend new file mode 100644 index 0000000..a706b06 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex%.bbappend @@ -0,0 +1,20 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/linux-toradex:" + +# Prevent the use of in-tree defconfig +# unset KBUILD_DEFCONFIG + +SRC_URI += "\ + file://imx8mp-verdin-mikrophone.dtsi \ + file://imx8mp-verdin-wifi-mikrophone.dts \ + file://imx8mp-verdin-nonwifi-mikrophone.dts \ + file://kernel_lvds_freq.patch \ + " + +# For arm64 bit freescale/NXP devices +DTS_SRCDIR = "arch/arm64/boot/dts/freescale" + +do_configure:append() { + cp ${WORKDIR}/imx8mp-verdin-mikrophone.dtsi ${S}/${DTS_SRCDIR} + cp ${WORKDIR}/imx8mp-verdin-wifi-mikrophone.dts ${S}/${DTS_SRCDIR} + cp ${WORKDIR}/imx8mp-verdin-nonwifi-mikrophone.dts ${S}/${DTS_SRCDIR} +}
\ No newline at end of file diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-mikrophone.dtsi b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-mikrophone.dtsi new file mode 100644 index 0000000..7c23309 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-mikrophone.dtsi @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +/* Verdin HDMI_1 Audio */ +&aud2htx { + status = "okay"; +}; + +/* Verdin SPI_1 */ +&ecspi1 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + spidev@0 { + /* Use compatible "rohm,dh2228fv" to bind spidev driver */ + compatible = "rohm,dh2228fv"; + reg = <0>; + spi-max-frequency = <50000000>; + }; +}; + +&eqos { + status = "disabled"; +}; + +&fec { + status = "disabled"; +}; + +&flexcan1 { + status = "disabled"; +}; + +&flexcan2 { + status = "disabled"; +}; + +/* Verdin QSPI_1 */ +&flexspi { + status = "disabled"; +}; + +&gpio4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; +}; + +/* Verdin I2C_2_DSI */ +&i2c2 { + status = "disabled"; +}; + +/* CAM I2C, disabled for now */ +&i2c3 { + status = "disabled"; +}; + +/* Verdin I2C_1 */ +&i2c4 { + status = "okay"; +}; + +/* Verdin PCIE_1 */ +&pcie { + status = "okay"; +}; + +&pcie_phy { + status = "okay"; +}; + +/* Verdin PWM_1 */ +&pwm1 { + status = "okay"; +}; + +/* Verdin PWM_2 */ +&pwm2 { + status = "disabled"; +}; + +/* Verdin PWM_3_DSI */ +&pwm3 { + status = "disabled"; +}; + +®_usdhc2_vmmc { + vin-supply = <®_3p3v>; +}; + +/* Verdin HDMI_1 Audio */ +&sound_hdmi { + status = "okay"; +}; + +/* Verdin UART_1 */ +&uart1 { + status = "disabled"; +}; + +/* Verdin UART_2 */ +&uart2 { + status = "disabled"; +}; + +/* Verdin UART_3, used as the Linux Console */ +&uart3 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb3_0 { + status = "okay"; +}; + +&usb3_phy0 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3_1 { + // fsl,permanently-attached; + status = "okay"; +}; + +&usb3_phy1 { + status = "okay"; +}; + +/* Verdin SDCard */ +&usdhc2 { + status = "okay"; +}; + +/* Video and graphics */ +&vpu_g1 { + status = "okay"; +}; + +&vpu_g2 { + status = "okay"; +}; + +&vpu_vc8000e { + status = "okay"; +}; + +&vpu_v4l2 { + status = "okay"; +}; + +&gpu_2d { + status = "okay"; +}; + +&gpu_3d { + status = "okay"; +}; + +&ml_vipsi { + status = "okay"; +}; + +&mix_gpu_ml { + status = "okay"; +}; diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-nonwifi-mikrophone.dts b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-nonwifi-mikrophone.dts new file mode 100644 index 0000000..91c08a7 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-nonwifi-mikrophone.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +/dts-v1/; + +#include "imx8mp-verdin.dtsi" +#include "imx8mp-verdin-nonwifi.dtsi" +#include "imx8mp-verdin-mikrophone.dtsi" + +/ { + model = "Toradex Verdin iMX8M Plus on mikroPhone"; + compatible = "toradex,verdin-imx8mp-nonwifi-mikrophone", + "toradex,verdin-imx8mp-wifi", + "toradex,verdin-imx8mp", + "fsl,imx8mp"; +}; diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-wifi-mikrophone.dts b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-wifi-mikrophone.dts new file mode 100644 index 0000000..35792e1 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-wifi-mikrophone.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +/dts-v1/; + +#include "imx8mp-verdin.dtsi" +#include "imx8mp-verdin-wifi.dtsi" +#include "imx8mp-verdin-mikrophone.dtsi" + +/ { + model = "Toradex Verdin iMX8M Plus WB on mikroPhone"; + compatible = "toradex,verdin-imx8mp-wifi-mikrophone", + "toradex,verdin-imx8mp-wifi", + "toradex,verdin-imx8mp", + "fsl,imx8mp"; +}; diff --git a/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/kernel_lvds_freq.patch b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/kernel_lvds_freq.patch new file mode 100644 index 0000000..9955bc4 --- /dev/null +++ b/yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/kernel_lvds_freq.patch @@ -0,0 +1,62 @@ +diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi +index 55adf1bf45d9..c875474e6c45 100644 +--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi ++++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi +@@ -752,7 +752,7 @@ clk: clock-controller@30380000 { + <800000000>, + <393216000>, + <361267200>, +- <1039500000>; ++ <252000000>; + }; + + src: reset-controller@30390000 { +diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c +index 6c17786ecb9f..8c9840f08c0e 100644 +--- a/drivers/clk/imx/clk-pll14xx.c ++++ b/drivers/clk/imx/clk-pll14xx.c +@@ -75,6 +75,7 @@ static const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = { + PLL_1443X_RATE(49152000U, 393, 3, 6, 0x374c), + PLL_1443X_RATE(45158400U, 241, 2, 6, 0xd845), + PLL_1443X_RATE(40960000U, 109, 1, 6, 0x3a07), ++ PLL_1443X_RATE(252000000U, 84, 2, 2, 0), + }; + + struct imx_pll14xx_clk imx_1443x_pll = { +diff --git a/drivers/gpu/drm/imx/imx8mp-ldb.c b/drivers/gpu/drm/imx/imx8mp-ldb.c +index e3f5c5e6e842..55dbafa863cf 100644 +--- a/drivers/gpu/drm/imx/imx8mp-ldb.c ++++ b/drivers/gpu/drm/imx/imx8mp-ldb.c +@@ -186,15 +186,6 @@ imx8mp_ldb_encoder_atomic_check(struct drm_encoder *encoder, + return -EINVAL; + } + +- /* +- * Due to limited video PLL frequency points on i.MX8mp, +- * we do mode fixup here in case any mode is unsupported. +- */ +- if (ldb->dual) +- mode->clock = mode->clock > 100000 ? 148500 : 74250; +- else +- mode->clock = 74250; +- + return 0; + } + +@@ -212,16 +203,6 @@ imx8mp_ldb_encoder_mode_valid(struct drm_encoder *encoder, + if (ldb_ch->panel) + return MODE_OK; + +- /* +- * Due to limited video PLL frequency points on i.MX8mp, +- * we do mode valid check here. +- */ +- if (ldb->dual && mode->clock != 74250 && mode->clock != 148500) +- return MODE_NOCLOCK; +- +- if (!ldb->dual && mode->clock != 74250) +- return MODE_NOCLOCK; +- + return MODE_OK; + } + diff --git a/yocto/overlays/mikroPhone-esp32_overlay.dts b/yocto/overlays/mikroPhone-esp32_overlay.dts new file mode 100755 index 0000000..9a6f61a --- /dev/null +++ b/yocto/overlays/mikroPhone-esp32_overlay.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright 2025 Uros Majstorovic + */ + +// adapted from: https://git.toradex.com/cgit/device-tree-overlays.git/tree/overlays/verdin-imx8mp_spidev_overlay.dts?h=toradex_6.6-2.2.x-imx +// Verdin imx8mp esp32 + +/dts-v1/; +/plugin/; + +/ { + compatible = "toradex,verdin-imx8mp"; +}; + +/* Verdin SPI_1 */ +&ecspi1 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + esp32@0 { + compatible = "mikrophone,esp32"; + reg = <0>; + spi-max-frequency = <20000000>; + rts-gpio = <&gpio4 25 0>; + cts-gpio = <&gpio4 28 0>; + }; +}; diff --git a/yocto/overlays/mikroPhone-panel_overlay.dts b/yocto/overlays/mikroPhone-panel_overlay.dts index 0bc14b3..aaf494c 100644 --- a/yocto/overlays/mikroPhone-panel_overlay.dts +++ b/yocto/overlays/mikroPhone-panel_overlay.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT /* - * Copyright 2023 Toradex + * Copyright 2025 Uros Majstorovic */ // adapted from: https://git.toradex.com/cgit/device-tree-overlays.git/tree/overlays/verdin-imx8mp_panel-cap-touch-10inch-lvds_overlay.dts?h=toradex_6.6-2.2.x-imx @@ -19,58 +19,57 @@ }; &{/} { - backlight_lvds_native: backlight-lvds-native { + backlight_lvds: backlight-lvds { compatible = "pwm-backlight"; pinctrl-names = "default"; brightness-levels = <0 45 63 88 119 158 203 255>; - default-brightness-level = <4>; + default-brightness-level = <0>; /* Verdin PWM_1 (SODIMM 15) */ - pwms = <&pwm1 0 6666667>; + pwms = <&pwm1 0 6666667 0>; }; - panel-lvds-native { + lvds0_panel: panel-lvds-focuslcds { compatible = "panel-lvds"; - backlight = <&backlight_lvds_native>; + backlight = <&backlight_lvds>; data-mapping = "vesa-24"; - height-mm = <136>; - width-mm = <217>; + width-mm = <62>; + height-mm = <110>; panel-timing { - clock-frequency = <36000000>; + clock-frequency = <26250000>; + hfront-porch = <8>; + hsync-len = <4>; + hback-porch = <8>; hactive = <480>; - hfront-porch = <23 60 71>; - hsync-len = <15 40 47>; - hback-porch = <23 60 71>; + vfront-porch = <7>; + vsync-len = <7>; + vback-porch = <7>; vactive = <854>; - vfront-porch = <5 7 10>; - vsync-len = <6 9 12>; - vback-porch = <5 7 10>; hsync-active = <0>; vsync-active = <0>; - de-active = <0>; - pixelclk-active = <1>; /* positive edge */ + de-active = <1>; + // pixelclk-active = <0>; }; port { - panel_lvds_native_in: endpoint { + panel_lvds_in: endpoint { remote-endpoint = <&lvds_out>; }; }; }; -}; - -&gpu_2d { - status = "okay"; -}; - -&gpu_3d { - status = "okay"; + + dmux { + compatible = "mikrophone,dmux"; + dmux-gpio = <&gpio1 1 0>; + ctp-dev-name = "3-005d"; + status = "okay"; + }; }; /* Verdin I2C_1 */ -&i2c1 { +&i2c4 { #address-cells = <1>; #size-cells = <0>; status = "okay"; @@ -78,13 +77,11 @@ gt911@5d { compatible = "goodix,gt911"; reg = <0x5d>; - pinctrl-names = "default"; - pinctrl-0 = <&gpio1>; - /* Verdin GPIO_3 (SODIMM 210) */ + interrupts = <5 IRQ_TYPE_LEVEL_LOW>; interrupt-parent = <&gpio1>; - interrupts = <5 IRQ_TYPE_EDGE_FALLING>; - /* Verdin GPIO_4 (SODIMM 212) */ - reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; + irq-gpio = <&gpio1 5 0>; + reset-gpio = <&gpio1 6 0>; + defer-init; status = "okay"; }; }; @@ -94,11 +91,14 @@ }; &ldb { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; lvds-channel@0 { #address-cells = <1>; #size-cells = <0>; + reg = <0>; fsl,data-mapping = "spwg"; fsl,data-width = <24>; status = "okay"; @@ -107,7 +107,7 @@ reg = <1>; lvds_out: endpoint { - remote-endpoint = <&panel_lvds_native_in>; + remote-endpoint = <&panel_lvds_in>; }; }; }; @@ -117,15 +117,13 @@ status = "okay"; }; -&mix_gpu_ml { - status = "okay"; -}; - -&ml_vipsi { +&pwm1 { status = "okay"; }; -/* Verdin PWM_2 */ -&pwm1 { - status = "okay"; +/* +&media_blk_ctrl { + assigned-clock-rates = <500000000>, <200000000>, + <0>, <0>, <183750000>; }; +*/ |
