[PATCH v15] staging: fbtft: add tearing signal detect

From: Carlis
Date: Sun Feb 07 2021 - 09:05:49 EST


From: Carlis <zhangxuezhi1@xxxxxxxxxx>

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@xxxxxxxxxx>
---
v15: change ret value return logic in write_vmem.
v14: change to define TE completion and TE irq only in st7789v.
v13: change TE completion to par data struct member and add a new
function to deal te gpio request, add new write_vmem function.
v12: change dev_err to dev_err_probe and add space in comments start, and
delete te_mutex, change te wait logic.
v11: remove devm_gpio_put and change a dev_err to dev_info.
v10: additional notes.
v9: change pr_* to dev_*.
v8: delete a log line.
v7: return error value when request fail.
v6: add te gpio request fail deal logic.
v5: fix log print.
v4: modify some code style and change te irq set function name.
v3: modify author and signed-off-by name.
v2: add release te gpio after irq request fail.
---
drivers/staging/fbtft/fb_st7789v.c | 115 +++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)

diff --git a/drivers/staging/fbtft/fb_st7789v.c b/drivers/staging/fbtft/fb_st7789v.c
index 3a280cc..abe9395 100644
--- a/drivers/staging/fbtft/fb_st7789v.c
+++ b/drivers/staging/fbtft/fb_st7789v.c
@@ -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 int blank(struct fbtft_par *par, bool on)
.gamma = HSD20_IPS_GAMMA,
.fbtftops = {
.init_display = init_display,
+ .write_vmem = write_vmem,
.set_var = set_var,
.set_gamma = set_gamma,
.blank = blank,
--
1.9.1