HamsterBear Linux ST7789V FBTFT驱动适配
HamsterBear Linux ST7789V FBTFT驱动适配
平台
- F1C200sLinux版本
- 5.18TFT屏
- 1.69寸IPS高清ST7789V
修改设备树,在spi0节点下添加
&spi0 { pinctrl-names = default; pinctrl-0 = <&spi0_pd_pins>; status = okay; spi-max-frequency = <50000000>; st7789v: st7789v@0 { #address-cells = <1>; #size-cells = <1>; compatible = sitronix,st7789v; reg = <0>; spi-max-frequency = <50000000>; rgb; buswidth = <8>; rotate = <90>; fps = <60>; spi-cpol; spi-cpha; reset-gpios = <&pio 4 10 GPIO_ACTIVE_HIGH>; /* PE10 */ dc-gpios = <&pio 2 3 GPIO_ACTIVE_LOW>; /* PC3 */ debug = <1>; }; };
确定内核config打开
需要注意的是,fbtft的驱动只有在内核framebuffer相关驱动开启后才会出现,所以要先开启fb驱动支持
Device Drivers -> Graphics support -> Frame buffer Devices --->,编译进内核
可根据需要将控制台映射到fb功能开启
这里我同时选择了16色的Tux 启动logo
然后寻找fbtft驱动,路径如下:
Device Drivers -> Staging drivers -> Support for small TFT LCD display modules --->,编译进内核
修改drivers/staging/fbtft/fb_st7789v.c
,如下几处
- 修改init函数,该初始化序列参考自厂家
static int init_display(struct fbtft_par *par) { int rc; par->fbtftops.reset(par); rc = init_tearing_effect_line(par); if (rc) return rc; write_reg(par, 0x11); //Sleep out mdelay(120); //Delay 120ms //************* Start Initial Sequence **********// write_reg(par, 0x36, 0x00); write_reg(par, 0x3A, 0x05); write_reg(par, 0xB2,0x0C,0x0C,0x00,0x33,0x33); write_reg(par, 0xB7,0x35); /* VCOM = 1.35V */ write_reg(par, 0xBB, 0x32); write_reg(par, 0xC2, 0x01); /* GVDD = 4.8V */ write_reg(par, 0xC3,0x15); /* VDX = 0V */ write_reg(par, 0xC4, 0x20); /* FPS = 60Hz */ write_reg(par, 0xC6,0x0F); write_reg(par, 0xD0, 0xA4, 0xA1); write_reg(par, 0xE0,0xD0,0x08,0x0E,0x09,0x09,0x05,0x31,0x33,0x48,0x17,0x14,0x15,0x31,0x34); write_reg(par, 0xE1,0xD0,0x08,0x0E,0x09,0x09,0x15,0x31,0x33,0x48,0x17,0x14,0x15,0x31,0x34); write_reg(par, MIPI_DCS_ENTER_INVERT_MODE); write_reg(par, MIPI_DCS_SET_DISPLAY_ON); return 0; }
2, 修改display参数
static struct fbtft_display display = { .regwidth = 8, .width = 240, .height = 280, /* 根据你的屏幕分辨率确定 */ .gamma_num = 2, .gamma_len = 14, .gamma = HSD20_IPS_GAMMA, .fbtftops = { .init_display = init_display, .write_vmem = write_vmem, .set_var = set_var, .set_gamma = set_gamma, .blank = blank, }, };
修改drivers/staging/fbtft/fbtft-core.c
,如下几处
- 添加头文件
#include <linux/of.h> #include <linux/of_gpio.h>
- 修改
fbtft_request_one_gpio
函数
static int fbtft_request_one_gpio(struct fbtft_par *par, const char *name, int index, struct gpio_desc **gpiop) { int ret, gpio; struct device *dev = par->info->device; struct device_node *np = dev->of_node; *gpiop = devm_gpiod_get_index_optional(dev, name, index, GPIOD_OUT_LOW); /* Get GPIO from device tree */ gpio = of_get_named_gpio(np, name, 0); if (gpio < 0) { dev_err(dev, Failed to retrieve %s from dts.\n, name); return 0; } ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, dev->driver->name); if (ret) { dev_err(dev, Failed to request %s GPIO%d\n, name, gpio); return -ENODEV; } *gpiop = gpio_to_desc(gpio); fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, %s: '%s' GPIO%d\n, __func__, name, gpio); return 0; }
- 修改
fbtft_request_gpios
函数
static int fbtft_request_gpios(struct fbtft_par *par) { int i; int ret; ret = fbtft_request_one_gpio(par, reset-gpios, 0, &par->gpio.reset); if (ret) return ret; ret = fbtft_request_one_gpio(par, dc-gpios, 0, &par->gpio.dc); if (ret) return ret; ret = fbtft_request_one_gpio(par, rd-gpios, 0, &par->gpio.rd); if (ret) return ret; ret = fbtft_request_one_gpio(par, wr-gpios, 0, &par->gpio.wr); if (ret) return ret; ret = fbtft_request_one_gpio(par, cs-gpios, 0, &par->gpio.cs); if (ret) return ret; ret = fbtft_request_one_gpio(par, latch-gpios, 0, &par->gpio.latch); if (ret) return ret; for (i = 0; i < 16; i++) { ret = fbtft_request_one_gpio(par, db-gpios, i, &par->gpio.db[i]); if (ret) return ret; ret = fbtft_request_one_gpio(par, led-gpios, i, &par->gpio.led[i]); if (ret) return ret; ret = fbtft_request_one_gpio(par, aux-gpios, i, &par->gpio.aux[i]); if (ret) return ret; } return 0; }
- 修改
fbtft_set_win_addr
函数
因为st7789v支持到240x320的分辨率,但是我这块屏是240*280,所以x start和x end都有偏移,
这个偏移值也是我从厂家源码中参考的。
static void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) { xs = xs + 20; xe = xe + 20; write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF); write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF); write_reg(par, MIPI_DCS_WRITE_MEMORY_START); }
- 修改
fbtft_reset
函数
st7789v是low level reset
static void fbtft_reset(struct fbtft_par *par) { if (!par->gpio.reset) return; fbtft_par_dbg(DEBUG_RESET, par, %s()\n, __func__); gpiod_set_value_cansleep(par->gpio.reset, 1); usleep_range(20, 40); gpiod_set_value_cansleep(par->gpio.reset, 0); /* Low level reset */ msleep(120); gpiod_set_value_cansleep(par->gpio.reset, 1); /* Activate chip */ }
最后附上两个文件的直观patch
diff --git a/drivers/staging/fbtft/fb_st7789v.c b/drivers/staging/fbtft/fb_st7789v.c index 861a154144e6..c09063ca3085 100644 --- a/drivers/staging/fbtft/fb_st7789v.c +++ b/drivers/staging/fbtft/fb_st7789v.c @@ -150,6 +150,7 @@ static int init_display(struct fbtft_par *par) if (rc) return rc; +#if 0 /* turn off sleep mode */ write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE); mdelay(120); @@ -213,7 +214,43 @@ static int init_display(struct fbtft_par *par) if (HSD20_IPS) write_reg(par, MIPI_DCS_ENTER_INVERT_MODE); +#endif + write_reg(par, 0x11); //Sleep out + mdelay(120); //Delay 120ms + //************* Start Initial Sequence **********// + write_reg(par, 0x36, 0x00); + + write_reg(par, 0x3A, 0x05); + + write_reg(par, 0xB2,0x0C,0x0C,0x00,0x33,0x33); + + write_reg(par, 0xB7,0x35); + + /* VCOM = 1.35V */ + write_reg(par, 0xBB, 0x32); + + write_reg(par, 0xC2, 0x01); + + /* GVDD = 4.8V */ + write_reg(par, 0xC3,0x15); + + /* VDX = 0V */ + write_reg(par, 0xC4, 0x20); + + /* FPS = 60Hz */ + write_reg(par, 0xC6,0x0F); + + write_reg(par, 0xD0, 0xA4, 0xA1); + + write_reg(par, 0xE0,0xD0,0x08,0x0E,0x09,0x09,0x05,0x31,0x33,0x48,0x17,0x14,0x15,0x31,0x34); + + write_reg(par, 0xE1,0xD0,0x08,0x0E,0x09,0x09,0x15,0x31,0x33,0x48,0x17,0x14,0x15,0x31,0x34); + + write_reg(par, MIPI_DCS_ENTER_INVERT_MODE); + + write_reg(par, MIPI_DCS_SET_DISPLAY_ON); + return 0; } @@ -369,7 +406,7 @@ static int blank(struct fbtft_par *par, bool on) static struct fbtft_display display = { .regwidth = 8, .width = 240, - .height = 320, + .height = 280, .gamma_num = 2, .gamma_len = 14, .gamma = HSD20_IPS_GAMMA,
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index 9c4d797e7ae4..f15b8d89afe4 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -16,6 +16,8 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/fb.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/gpio/consumer.h> #include <linux/spi/spi.h> #include <linux/delay.h> @@ -74,15 +76,30 @@ static int fbtft_request_one_gpio(struct fbtft_par *par, const char *name, int index, struct gpio_desc **gpiop) { + int ret, gpio; struct device *dev = par->info->device; + struct device_node *np = dev->of_node; *gpiop = devm_gpiod_get_index_optional(dev, name, index, GPIOD_OUT_LOW); - if (IS_ERR(*gpiop)) - return dev_err_probe(dev, PTR_ERR(*gpiop), Failed to request %s GPIO\n, name); - fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, %s: '%s' GPIO\n, - __func__, name); + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(np, name, 0); + if (gpio < 0) { + dev_err(dev, Failed to retrieve %s from dts.\n, name); + return 0; + return 0; + return 0; + } + + ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, dev->driver->name); + if (ret) { + dev_err(dev, Failed to request %s GPIO%d\n, name, gpio); + return -ENODEV; + } + + *gpiop = gpio_to_desc(gpio); + + fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, %s: '%s' GPIO%d\n, + __func__, name, gpio); return 0; } @@ -92,34 +109,34 @@ static int fbtft_request_gpios(struct fbtft_par *par) int i; int ret; - ret = fbtft_request_one_gpio(par, reset, 0, &par->gpio.reset); + ret = fbtft_request_one_gpio(par, reset-gpios, 0, &par->gpio.reset); if (ret) return ret; - ret = fbtft_request_one_gpio(par, dc, 0, &par->gpio.dc); + ret = fbtft_request_one_gpio(par, dc-gpios, 0, &par->gpio.dc); if (ret) return ret; - ret = fbtft_request_one_gpio(par, rd, 0, &par->gpio.rd); + ret = fbtft_request_one_gpio(par, rd-gpios, 0, &par->gpio.rd); if (ret) return ret; - ret = fbtft_request_one_gpio(par, wr, 0, &par->gpio.wr); + ret = fbtft_request_one_gpio(par, wr-gpios, 0, &par->gpio.wr); if (ret) return ret; - ret = fbtft_request_one_gpio(par, cs, 0, &par->gpio.cs); + ret = fbtft_request_one_gpio(par, cs-gpios, 0, &par->gpio.cs); if (ret) return ret; - ret = fbtft_request_one_gpio(par, latch, 0, &par->gpio.latch); + ret = fbtft_request_one_gpio(par, latch-gpios, 0, &par->gpio.latch); if (ret) return ret; for (i = 0; i < 16; i++) { - ret = fbtft_request_one_gpio(par, db, i, + ret = fbtft_request_one_gpio(par, db-gpios, i, &par->gpio.db[i]); if (ret) return ret; - ret = fbtft_request_one_gpio(par, led, i, + ret = fbtft_request_one_gpio(par, led-gpios, i, &par->gpio.led[i]); if (ret) return ret; - ret = fbtft_request_one_gpio(par, aux, i, + ret = fbtft_request_one_gpio(par, aux-gpios, i, &par->gpio.aux[i]); if (ret) return ret; @@ -203,6 +220,8 @@ EXPORT_SYMBOL(fbtft_register_backlight); static void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) { + xs = xs + 20; + xe = xe + 20; write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF); @@ -221,10 +240,10 @@ static void fbtft_reset(struct fbtft_par *par) gpiod_set_value_cansleep(par->gpio.reset, 1); usleep_range(20, 40); - gpiod_set_value_cansleep(par->gpio.reset, 0); + gpiod_set_value_cansleep(par->gpio.reset, 0); /* Low level reset */ msleep(120); - gpiod_set_value_cansleep(par->gpio.cs, 1); /* Activate chip */ + gpiod_set_value_cansleep(par->gpio.reset, 1); /* Activate chip */ }