staging: fbtft: add tearing signal detect
authorCarlis <zhangxuezhi1@yulong.com>
Sun, 7 Feb 2021 14:01:57 +0000 (22:01 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 12 Feb 2021 08:48:29 +0000 (09:48 +0100)
For st7789v IC, when we need continuous full screen refresh, it is best to
wait for the tearing effect line signal to arrive to avoid screen tearing.

Signed-off-by: Carlis <zhangxuezhi1@yulong.com>
Link: https://lore.kernel.org/r/1612706517-124617-1-git-send-email-zhangxuezhi3@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/fbtft/fb_st7789v.c

index 3a280cc..abe9395 100644 (file)
@@ -7,9 +7,13 @@
 
 #include <linux/bitops.h>
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
 #include <linux/module.h>
+
 #include <video/mipi_display.h>
 
 #include "fbtft.h"
@@ -66,6 +70,62 @@ enum st7789v_command {
 #define MADCTL_MX BIT(6) /* bitmask for column address order */
 #define MADCTL_MY BIT(7) /* bitmask for page address order */
 
+/* 60Hz for 16.6ms, configured as 2*16.6ms */
+#define PANEL_TE_TIMEOUT_MS  33
+
+static struct completion panel_te; /* completion for panel TE line */
+static int irq_te; /* Linux IRQ for LCD TE line */
+
+static irqreturn_t panel_te_handler(int irq, void *data)
+{
+       complete(&panel_te);
+       return IRQ_HANDLED;
+}
+
+/*
+ * init_tearing_effect_line() - init tearing effect line.
+ * @par: FBTFT parameter object.
+ *
+ * Return: 0 on success, or a negative error code otherwise.
+ */
+static int init_tearing_effect_line(struct fbtft_par *par)
+{
+       struct device *dev = par->info->device;
+       struct gpio_desc *te;
+       int rc, irq;
+
+       te = gpiod_get_optional(dev, "te", GPIOD_IN);
+       if (IS_ERR(te))
+               return dev_err_probe(dev, PTR_ERR(te), "Failed to request te GPIO\n");
+
+       /* if te is NULL, indicating no configuration, directly return success */
+       if (!te) {
+               irq_te = 0;
+               return 0;
+       }
+
+       irq = gpiod_to_irq(te);
+
+       /* GPIO is locked as an IRQ, we may drop the reference */
+       gpiod_put(te);
+
+       if (irq < 0)
+               return irq;
+
+       irq_te = irq;
+       init_completion(&panel_te);
+
+       /* The effective state is high and lasts no more than 1000 microseconds */
+       rc = devm_request_irq(dev, irq_te, panel_te_handler,
+                             IRQF_TRIGGER_RISING, "TE_GPIO", par);
+       if (rc)
+               return dev_err_probe(dev, rc, "TE IRQ request failed.\n");
+
+       disable_irq_nosync(irq_te);
+
+       return 0;
+}
+
 /**
  * init_display() - initialize the display controller
  *
@@ -82,6 +142,12 @@ enum st7789v_command {
  */
 static int init_display(struct fbtft_par *par)
 {
+       int rc;
+
+       rc = init_tearing_effect_line(par);
+       if (rc)
+               return rc;
+
        /* turn off sleep mode */
        write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
        mdelay(120);
@@ -137,6 +203,10 @@ static int init_display(struct fbtft_par *par)
         */
        write_reg(par, PWCTRL1, 0xA4, 0xA1);
 
+       /* TE line output is off by default when powering on */
+       if (irq_te)
+               write_reg(par, MIPI_DCS_SET_TEAR_ON, 0x00);
+
        write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
 
        if (HSD20_IPS)
@@ -145,6 +215,50 @@ static int init_display(struct fbtft_par *par)
        return 0;
 }
 
+/*
+ * write_vmem() - write data to display.
+ * @par: FBTFT parameter object.
+ * @offset: offset from screen_buffer.
+ * @len: the length of data to be writte.
+ *
+ * Return: 0 on success, or a negative error code otherwise.
+ */
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+       struct device *dev = par->info->device;
+       int ret;
+
+       if (irq_te) {
+               enable_irq(irq_te);
+               reinit_completion(&panel_te);
+               ret = wait_for_completion_timeout(&panel_te,
+                                                 msecs_to_jiffies(PANEL_TE_TIMEOUT_MS));
+               if (ret == 0)
+                       dev_err(dev, "wait panel TE timeout\n");
+
+               disable_irq(irq_te);
+       }
+
+       switch (par->pdata->display.buswidth) {
+       case 8:
+               ret = fbtft_write_vmem16_bus8(par, offset, len);
+               break;
+       case 9:
+               ret = fbtft_write_vmem16_bus9(par, offset, len);
+               break;
+       case 16:
+               ret = fbtft_write_vmem16_bus16(par, offset, len);
+               break;
+       default:
+               dev_err(dev, "Unsupported buswidth %d\n",
+                       par->pdata->display.buswidth);
+               ret = 0;
+               break;
+       }
+
+       return ret;
+}
+
 /**
  * set_var() - apply LCD properties like rotation and BGR mode
  *
@@ -259,6 +373,7 @@ static struct fbtft_display display = {
        .gamma = HSD20_IPS_GAMMA,
        .fbtftops = {
                .init_display = init_display,
+               .write_vmem = write_vmem,
                .set_var = set_var,
                .set_gamma = set_gamma,
                .blank = blank,