summaryrefslogtreecommitdiff
path: root/yocto
diff options
context:
space:
mode:
Diffstat (limited to 'yocto')
-rw-r--r--yocto/README161
-rw-r--r--yocto/esp32d/msgq.h22
-rw-r--r--yocto/esp32d/spi.c411
-rw-r--r--yocto/esp32d/spi.h31
-rw-r--r--yocto/esp32d/tun.c84
-rw-r--r--yocto/esp32d/tun.h5
-rwxr-xr-xyocto/esp32mod/Makefile15
-rwxr-xr-xyocto/esp32mod/esp32.c535
-rwxr-xr-xyocto/esp32mod/esp32.h16
-rwxr-xr-x[-rw-r--r--]yocto/esp32mod/msgq.c (renamed from yocto/esp32d/msgq.c)49
-rwxr-xr-xyocto/esp32mod/msgq.h15
-rwxr-xr-x[-rw-r--r--]yocto/esp32tun/Makefile (renamed from yocto/esp32d/Makefile)4
-rwxr-xr-xyocto/esp32tun/esp32tun.c173
-rw-r--r--yocto/meta-mikrophone/conf/machine/._verdin-imx8mp-mikrophone.confbin0 -> 4096 bytes
-rw-r--r--yocto/meta-mikrophone/conf/machine/include/verdin-imx8mp.inc14
-rw-r--r--yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-mikrophone.dtsi111
-rw-r--r--yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-nonwifi-mikrophone.dts18
-rw-r--r--yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex/imx8mp-verdin-wifi-mikrophone.dts18
-rw-r--r--yocto/meta-mikrophone/recipes-bsp/u-boot/u-boot-toradex_%.bbappend19
-rw-r--r--yocto/meta-mikrophone/recipes-images/images/mikrophone-image.bb6
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays/mikroPhone-panel_overlay.dts110
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/device-tree-overlays_git.bbappend15
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex%.bbappend20
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-mikrophone.dtsi169
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-nonwifi-mikrophone.dts18
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/imx8mp-verdin-wifi-mikrophone.dts18
-rw-r--r--yocto/meta-mikrophone/recipes-kernel/linux/linux-toradex/kernel_lvds_freq.patch62
-rwxr-xr-xyocto/overlays/mikroPhone-esp32_overlay.dts29
-rw-r--r--yocto/overlays/mikroPhone-panel_overlay.dts82
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
new file mode 100644
index 0000000..fe7655c
--- /dev/null
+++ b/yocto/meta-mikrophone/conf/machine/._verdin-imx8mp-mikrophone.conf
Binary files differ
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";
+};
+
+&reg_usdhc2_vmmc {
+ vin-supply = <&reg_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";
+};
+
+&reg_usdhc2_vmmc {
+ vin-supply = <&reg_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>;
};
+*/