Hi, guys
I am writing a Linux Kernel Module for a TFT display, ILI9341. However, I am having problems even setting GPIO states.
It should turn on the GPIOs when I configure them, but it doesn’t.
Here is what I tried
- Monitor for error messages in through dmesg → No error shown
- Verify the GPIO configurations through duo-pinmux → All pins set to GPIO
Here is the code. I would love any suggestions
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
enum ILI9341_ERROR {
ILI9341_ERROR_NONE,
ILI9341_ERROR_DC_PIN,
ILI9341_ERROR_CS_PIN,
ILI9341_ERROR_RST_PIN,
};
struct display {
struct {
int pin;
struct gpio_desc *desc;
} dc, cs, rst;
};
1
#define GPIOA_OFFSET 480
static struct display display = {
.dc = {.pin=GPIOA_OFFSET + 23},
.cs = {.pin=GPIOA_OFFSET + 24},
.rst = {.pin=GPIOA_OFFSET + 25},
};
static inline
int ili9341_config_pins(void)
{
int status = 0;
display.dc.desc = gpio_to_desc(display.dc.pin);
if (!display.dc.desc) {
status = ILI9341_ERROR_DC_PIN;
goto exit;
}
display.cs.desc = gpio_to_desc(display.cs.pin);
if (!display.cs.desc) {
status = ILI9341_ERROR_CS_PIN;
goto error_cs;
}
display.rst.desc = gpio_to_desc(display.rst.pin);
if (!display.rst.desc) {
status = ILI9341_ERROR_RST_PIN;
goto error_rst;
}
error_rst:
gpiod_put(display.cs.desc);
display.cs.desc = NULL;
error_cs:
gpiod_put(display.dc.desc);
display.dc.desc = NULL;
exit:
return status;
}
int ili9341_init(void)
{
int status = 0;
status = ili9341_config_pins();
if (status) {
goto exit;
}
gpiod_direction_output(display.dc.desc, 0);
gpiod_direction_output(display.cs.desc, 0);
gpiod_direction_output(display.rst.desc, 0);
// Here, they should turn on, but they don't
gpiod_set_value(display.dc.desc, 1);
gpiod_set_value(display.cs.desc, 1);
gpiod_set_value(display.rst.desc, 1);
exit:
return status;
}
void ili9341_deinit(void)
{
gpiod_set_value(display.dc.desc, 0);
gpiod_set_value(display.cs.desc, 0);
gpiod_set_value(display.rst.desc, 0);
gpiod_put(display.dc.desc);
gpiod_put(display.cs.desc);
gpiod_put(display.rst.desc);
display.dc.desc = NULL;
display.cs.desc = NULL;
display.rst.desc = NULL;
}
MODULE_LICENSE("GPL");
int __init initialize(void)
{
int status = 0;
pr_info("Initializing display\n");
status = ili9341_init();
if (status) {
pr_err("Error initializing display: %d\n", status);
goto exit;
}
exit:
return status;
}
void __exit terminate(void)
{
pr_info("Deinitializing display\n");
ili9341_deinit();
}
module_init(initialize);
module_exit(terminate);
Here is the makefile
PROJECT:=my_project
ARCH?=riscv
KDIR?=~/Documents/duo-buildroot-sdk/linux_5.10
CROSS_COMPILE?=~/.local/bin/gcc/riscv64-linux-x86_64/bin/riscv64-unknown-linux-gnu-
BUILD_DIR?=$(PROJECT_DIR)/build
PROJECT_DIR:=$(patsubst %/,%, $(dir $(realpath $(lastword $(MAKEFILE_LIST)))))
PWD:= $(shell pwd)
obj-m+=$(PROJECT).o
CODE_DIR:=$(PROJECT_DIR)/src
OUT_DIR:=$(PROJECT_DIR)/build
C_FILES:=$(patsubst $(PROJECT_DIR)/%,%,$(foreach d,$(CODE_DIR),$(shell find $(d) -type f -name '*.c')))
O_FILES:=$(patsubst %.c,%.o,$(C_FILES))
$(PROJECT)-objs+=$(O_FILES)
all:
make -C $(KDIR) M=$(PROJECT_DIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) V=0 -I$(KDIR)/include modules
@mkdir -p $(OUT_DIR)
@mv $(PROJECT_DIR)/*.ko $(PROJECT_DIR)/*.mod $(PROJECT_DIR)/*mod.c $(PROJECT_DIR)/*.o \
$(PROJECT_DIR)/modules.order $(PROJECT_DIR)/Module.symvers $(PROJECT_DIR)/.*.cmd $(OUT_DIR)
clean:
make -C $(KDIR) M=$(PROJECT_DIR) clean
rm -rf $(OUT_DIR) 2>/dev/null || true
.PHONY: all clean insmod
Is there any other point I am missing?
I am using the MilkV Duo 64M.
Edit:
I didn’t even get to the part for configuring the SPI. I am still trying to set up the GPIOs for DC, RST, and CS.
I try to write to it using gpiod_set_value(…), but it doesn’t work (I have LEDs attached to the pins). The only pin that works is the XGPIOA_25 (the DC pin). The others don’t.
